Update from trunk
svn merge https://google-web-toolkit.googlecode.com/svn/trunk -r6101:6142
git-svn-id: https://google-web-toolkit.googlecode.com/svn/branches/farewellSwt@6158 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/branch-info.txt b/branch-info.txt
index 3068726..3e4351b 100644
--- a/branch-info.txt
+++ b/branch-info.txt
@@ -6,3 +6,4 @@
Merged from trunk
================
+svn merge https://google-web-toolkit.googlecode.com/svn/trunk -r6101:6142
diff --git a/dev/core/src/com/google/gwt/dev/CompileTaskRunner.java b/dev/core/src/com/google/gwt/dev/CompileTaskRunner.java
index 5b47004..02f8348 100644
--- a/dev/core/src/com/google/gwt/dev/CompileTaskRunner.java
+++ b/dev/core/src/com/google/gwt/dev/CompileTaskRunner.java
@@ -46,7 +46,8 @@
if (options.isUseGuiLogger()) {
// Initialize a tree logger window.
DetachedTreeLoggerWindow loggerWindow = DetachedTreeLoggerWindow.getInstance(
- "Build Output for " + options.getModuleNames(), 800, 600, true);
+ "Build Output for " + options.getModuleNames(), 800, 600, true,
+ options.getLogLevel());
// Eager AWT initialization for OS X to ensure safe coexistence with SWT.
BootStrapPlatform.initGui();
diff --git a/dev/core/src/com/google/gwt/dev/HostedModeBase.java b/dev/core/src/com/google/gwt/dev/HostedModeBase.java
index 1496a6d..44fd711 100644
--- a/dev/core/src/com/google/gwt/dev/HostedModeBase.java
+++ b/dev/core/src/com/google/gwt/dev/HostedModeBase.java
@@ -86,6 +86,37 @@
}
/**
+ * Handles the -logdir command line option.
+ */
+ protected static class ArgHandlerLogDir extends ArgHandlerString {
+ private final OptionLogDir options;
+
+ public ArgHandlerLogDir(OptionLogDir options) {
+ this.options = options;
+ }
+
+ @Override
+ public String getPurpose() {
+ return "Logs to a file in the given directory, as well as graphically";
+ }
+
+ @Override
+ public String getTag() {
+ return "-logdir";
+ }
+ @Override
+ public String[] getTagArgs() {
+ return new String[] {"directory"};
+ }
+
+ @Override
+ public boolean setString(String value) {
+ options.setLogFile(value);
+ return true;
+ }
+ }
+
+ /**
* Handles the -noserver command line flag.
*/
protected static class ArgHandlerNoServerFlag extends ArgHandlerFlag {
@@ -232,8 +263,9 @@
}
}
- protected interface HostedModeBaseOptions extends JJSOptions, OptionLogLevel,
- OptionGenDir, OptionNoServer, OptionPort, OptionStartupURLs {
+ protected interface HostedModeBaseOptions extends JJSOptions, OptionLogDir,
+ OptionLogLevel, OptionGenDir, OptionNoServer, OptionPort,
+ OptionStartupURLs {
/**
* The base shell work directory.
@@ -248,6 +280,7 @@
PrecompileOptionsImpl implements HostedModeBaseOptions {
private boolean isNoServer;
+ private File logDir;
private int port;
private final List<String> startupURLs = new ArrayList<String>();
@@ -255,6 +288,20 @@
startupURLs.add(url);
}
+ public boolean alsoLogToFile() {
+ return logDir != null;
+ }
+
+ public File getLogDir() {
+ return logDir;
+ }
+ public File getLogFile(String sublog) {
+ if (logDir == null) {
+ return null;
+ }
+ return new File(logDir, sublog);
+ }
+
public int getPort() {
return port;
}
@@ -271,6 +318,10 @@
return isNoServer;
}
+ public void setLogFile(String filename) {
+ logDir = new File(filename);
+ }
+
public void setNoServer(boolean isNoServer) {
this.isNoServer = isNoServer;
}
@@ -281,6 +332,20 @@
}
/**
+ * Controls whether and where to log data to file.
+ *
+ */
+ protected interface OptionLogDir {
+ boolean alsoLogToFile();
+
+ File getLogDir();
+
+ File getLogFile(String subfile);
+
+ void setLogFile(String filename);
+ }
+
+ /**
* Controls whether to run a server or not.
*
*/
@@ -317,6 +382,7 @@
registerHandler(new ArgHandlerPort(options));
registerHandler(new ArgHandlerWhitelist());
registerHandler(new ArgHandlerBlacklist());
+ registerHandler(new ArgHandlerLogDir(options));
registerHandler(new ArgHandlerLogLevel(options));
registerHandler(new ArgHandlerGenDir(options));
registerHandler(new ArgHandlerScriptStyle(options));
@@ -480,7 +546,7 @@
// Initialize the logger.
//
initializeLogger();
-
+
// Check for updates
final TreeLogger logger = getTopLogger();
final CheckForUpdates updateChecker
diff --git a/dev/core/src/com/google/gwt/dev/SwtHostedModeBase.java b/dev/core/src/com/google/gwt/dev/SwtHostedModeBase.java
index 1633bfe..38ee586 100644
--- a/dev/core/src/com/google/gwt/dev/SwtHostedModeBase.java
+++ b/dev/core/src/com/google/gwt/dev/SwtHostedModeBase.java
@@ -285,7 +285,9 @@
shell.setImages(ShellMainWindow.getIcons());
mainWnd = new ShellMainWindow(this, shell, getTitleText(),
- options.isNoServer() ? 0 : getPort());
+ options.isNoServer() ? 0 : getPort(),
+ options.alsoLogToFile() ? options.getLogFile("hosted.log") : null,
+ options.getLogLevel());
shell.setSize(700, 600);
if (!isHeadless()) {
diff --git a/dev/core/src/com/google/gwt/dev/shell/ShellMainWindow.java b/dev/core/src/com/google/gwt/dev/shell/ShellMainWindow.java
index 2af6125..8a3bebc 100644
--- a/dev/core/src/com/google/gwt/dev/shell/ShellMainWindow.java
+++ b/dev/core/src/com/google/gwt/dev/shell/ShellMainWindow.java
@@ -17,6 +17,7 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.dev.shell.BrowserWindowController.WebServerRestart;
import com.google.gwt.dev.shell.log.TreeLoggerWidget;
import com.google.gwt.dev.util.Util;
@@ -38,6 +39,8 @@
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.ToolItem;
+import java.io.File;
+
/**
* Implements the GWTShell's main window control.
*/
@@ -201,7 +204,8 @@
private Toolbar toolbar;
public ShellMainWindow(BrowserWindowController browserWindowController,
- Shell parent, String titleText, int serverPort) {
+ Shell parent, String titleText, int serverPort, File logFile,
+ Type logLevel) {
super(parent, SWT.NONE);
this.browserWindowController = browserWindowController;
@@ -235,7 +239,7 @@
// Create the log pane.
{
- logPane = new TreeLoggerWidget(this);
+ logPane = new TreeLoggerWidget(this, logFile, logLevel);
GridData data = new GridData();
data.grabExcessHorizontalSpace = true;
data.grabExcessVerticalSpace = true;
diff --git a/dev/core/src/com/google/gwt/dev/shell/log/DetachedTreeLoggerWindow.java b/dev/core/src/com/google/gwt/dev/shell/log/DetachedTreeLoggerWindow.java
index 5a44b99..0aaf638 100644
--- a/dev/core/src/com/google/gwt/dev/shell/log/DetachedTreeLoggerWindow.java
+++ b/dev/core/src/com/google/gwt/dev/shell/log/DetachedTreeLoggerWindow.java
@@ -15,6 +15,7 @@
*/
package com.google.gwt.dev.shell.log;
+import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.dev.shell.LowLevel;
import com.google.gwt.dev.util.log.AbstractTreeLogger;
@@ -42,10 +43,10 @@
*/
public static synchronized DetachedTreeLoggerWindow getInstance(
final String caption, final int width, final int height,
- final boolean autoScroll) {
+ final boolean autoScroll, Type logLevel) {
if (singleton == null) {
singleton = new DetachedTreeLoggerWindow(caption, width, height,
- autoScroll);
+ autoScroll, logLevel);
}
return singleton;
}
@@ -55,7 +56,7 @@
private boolean isRunning = false;
private DetachedTreeLoggerWindow(final String caption, final int width,
- final int height, final boolean autoScroll) {
+ final int height, final boolean autoScroll, Type logLevel) {
shell = new Shell(Display.getCurrent());
shell.setText(caption);
@@ -64,7 +65,8 @@
fillLayout.marginHeight = 0;
shell.setLayout(fillLayout);
- final TreeLoggerWidget treeLoggerWidget = new TreeLoggerWidget(shell);
+ final TreeLoggerWidget treeLoggerWidget = new TreeLoggerWidget(shell, null,
+ logLevel);
treeLoggerWidget.setAutoScroll(autoScroll);
logger = treeLoggerWidget.getLogger();
diff --git a/dev/core/src/com/google/gwt/dev/shell/log/TreeLoggerWidget.java b/dev/core/src/com/google/gwt/dev/shell/log/TreeLoggerWidget.java
index 47f80d0..2bfc484 100644
--- a/dev/core/src/com/google/gwt/dev/shell/log/TreeLoggerWidget.java
+++ b/dev/core/src/com/google/gwt/dev/shell/log/TreeLoggerWidget.java
@@ -15,10 +15,14 @@
*/
package com.google.gwt.dev.shell.log;
+import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.TreeLogger.HelpInfo;
+import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.dev.shell.BrowserWidget;
import com.google.gwt.dev.shell.log.TreeItemLogger.LogEvent;
import com.google.gwt.dev.util.log.AbstractTreeLogger;
+import com.google.gwt.dev.util.log.CompositeTreeLogger;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
@@ -43,6 +47,8 @@
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
+import java.io.File;
+import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URL;
@@ -57,11 +63,19 @@
private final Text details;
- private final TreeItemLogger logger;
+ private final AbstractTreeLogger logger;
+
+ private final TreeItemLogger uiLogger;
private final Tree tree;
- public TreeLoggerWidget(Composite parent) {
+ /**
+ * Creates a graphical widget, and optionally logging to file as well.
+ *
+ * @param parent the graphical interface parent
+ * @param logFile the file to log to
+ */
+ public TreeLoggerWidget(Composite parent, File logFile, Type logLevel) {
super(parent, SWT.NONE);
setLayout(new FillLayout());
@@ -97,7 +111,21 @@
}
});
- logger = new TreeItemLogger();
+ uiLogger = new TreeItemLogger();
+ AbstractTreeLogger bestLogger = uiLogger;
+ if (logFile != null) {
+ try {
+ PrintWriterTreeLogger fileLogger = new PrintWriterTreeLogger(logFile);
+ bestLogger = new CompositeTreeLogger(uiLogger, fileLogger);
+ fileLogger.setMaxDetail(logLevel);
+ uiLogger.setMaxDetail(logLevel);
+ } catch (IOException ex) {
+ uiLogger.log(TreeLogger.ERROR, "Can't log to " +
+ logFile.getAbsolutePath(), ex);
+ }
+ }
+ logger = bestLogger;
+ logger.setMaxDetail(logLevel);
// The detail
details = new Text(sash, SWT.MULTI | SWT.WRAP | SWT.READ_ONLY | SWT.BORDER
@@ -238,7 +266,7 @@
return;
}
- if (logger.uiFlush(tree)) {
+ if (uiLogger.uiFlush(tree)) {
// Sync to the end of the tree.
//
if (autoScroll) {
diff --git a/dev/core/src/com/google/gwt/dev/util/log/AbstractTreeLogger.java b/dev/core/src/com/google/gwt/dev/util/log/AbstractTreeLogger.java
index 2c7af4c..d128e8d 100644
--- a/dev/core/src/com/google/gwt/dev/util/log/AbstractTreeLogger.java
+++ b/dev/core/src/com/google/gwt/dev/util/log/AbstractTreeLogger.java
@@ -85,15 +85,15 @@
}
public int indexWithinMyParent;
+
+ protected TreeLogger.Type logLevel = TreeLogger.ALL;
- private TreeLogger.Type logLevel = TreeLogger.ALL;
+ protected AbstractTreeLogger parent;
private int nextChildIndex;
private final Object nextChildIndexLock = new Object();
- private AbstractTreeLogger parent;
-
private UncommittedBranchData uncommitted;
/**
@@ -226,6 +226,38 @@
return getLoggerId();
}
+ protected int allocateNextChildIndex() {
+ synchronized (nextChildIndexLock) {
+ // postincrement because we want indices to start at 0
+ return nextChildIndex++;
+ }
+ }
+
+ /**
+ * Commits the branch after ensuring that the parent logger (if there is one)
+ * has been committed first.
+ */
+ protected synchronized void commitMyBranchEntryInMyParentLogger() {
+ // (Only the root logger doesn't have a parent.)
+ //
+ if (parent != null) {
+ if (uncommitted != null) {
+ // Commit the parent first.
+ //
+ parent.commitMyBranchEntryInMyParentLogger();
+
+ // Let the subclass do its thing to commit this branch.
+ //
+ parent.doCommitBranch(this, uncommitted.type, uncommitted.message,
+ uncommitted.caught, uncommitted.helpInfo);
+
+ // Release the uncommitted state.
+ //
+ uncommitted = null;
+ }
+ }
+ }
+
/**
* Derived classes should override this method to return a branched logger.
*/
@@ -267,13 +299,6 @@
protected abstract void doLog(int indexOfLogEntryWithinParentLogger,
TreeLogger.Type type, String msg, Throwable caught, HelpInfo helpInfo);
- private int allocateNextChildIndex() {
- synchronized (nextChildIndexLock) {
- // postincrement because we want indices to start at 0
- return nextChildIndex++;
- }
- }
-
/**
* Scans <code>t</code> and its causes for {@link OutOfMemoryError}.
*
@@ -293,31 +318,6 @@
return false;
}
- /**
- * Commits the branch after ensuring that the parent logger (if there is one)
- * has been committed first.
- */
- private synchronized void commitMyBranchEntryInMyParentLogger() {
- // (Only the root logger doesn't have a parent.)
- //
- if (parent != null) {
- if (uncommitted != null) {
- // Commit the parent first.
- //
- parent.commitMyBranchEntryInMyParentLogger();
-
- // Let the subclass do its thing to commit this branch.
- //
- parent.doCommitBranch(this, uncommitted.type, uncommitted.message,
- uncommitted.caught, uncommitted.helpInfo);
-
- // Release the uncommitted state.
- //
- uncommitted = null;
- }
- }
- }
-
private String getLoggerId() {
if (parent != null) {
if (parent.parent == null) {
diff --git a/dev/core/src/com/google/gwt/dev/util/log/CompositeTreeLogger.java b/dev/core/src/com/google/gwt/dev/util/log/CompositeTreeLogger.java
new file mode 100644
index 0000000..c88f461
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/log/CompositeTreeLogger.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.util.log;
+
+/**
+ * Forks logging over two child loggers. This provides the graphics + file
+ * logging of HostedModeBase's -logfile option.
+ */
+public class CompositeTreeLogger extends AbstractTreeLogger {
+
+ private AbstractTreeLogger[] loggers;
+
+ public CompositeTreeLogger(AbstractTreeLogger... loggers) {
+ this.loggers = loggers;
+ }
+
+ @Override
+ protected AbstractTreeLogger doBranch() {
+ AbstractTreeLogger children[] = new AbstractTreeLogger[loggers.length];
+ for (int i = 0; i < loggers.length; i++) {
+ children[i] = loggers[i].doBranch();
+ children[i].indexWithinMyParent = loggers[i].allocateNextChildIndex();
+ children[i].parent = loggers[i];
+ children[i].logLevel = loggers[i].logLevel;
+ }
+ return new CompositeTreeLogger(children);
+ }
+
+ @Override
+ protected void doCommitBranch(AbstractTreeLogger childBeingCommitted,
+ Type type, String msg, Throwable caught, HelpInfo helpInfo) {
+ CompositeTreeLogger child = (CompositeTreeLogger) childBeingCommitted;
+ assert loggers.length == child.loggers.length;
+ for (int i = 0; i < loggers.length; i++) {
+ loggers[i].doCommitBranch(child.loggers[i], type, msg, caught, helpInfo);
+ }
+ }
+
+ @Override
+ protected void doLog(int indexOfLogEntryWithinParentLogger, Type type,
+ String msg, Throwable caught, HelpInfo helpInfo) {
+ for (AbstractTreeLogger logger : loggers) {
+ logger.doLog(indexOfLogEntryWithinParentLogger, type, msg, caught,
+ helpInfo);
+ }
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/util/log/PrintWriterTreeLogger.java b/dev/core/src/com/google/gwt/dev/util/log/PrintWriterTreeLogger.java
index cdd7843..6b75ab9 100644
--- a/dev/core/src/com/google/gwt/dev/util/log/PrintWriterTreeLogger.java
+++ b/dev/core/src/com/google/gwt/dev/util/log/PrintWriterTreeLogger.java
@@ -15,6 +15,9 @@
*/
package com.google.gwt.dev.util.log;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
@@ -26,6 +29,8 @@
private final String indent;
private final PrintWriter out;
+
+ private final Object mutex = new Object();
public PrintWriterTreeLogger() {
this(new PrintWriter(System.out, true));
@@ -34,12 +39,22 @@
public PrintWriterTreeLogger(PrintWriter out) {
this(out, "");
}
+
+ public PrintWriterTreeLogger(File logFile) throws IOException {
+ boolean existing = logFile.exists();
+ this.out = new PrintWriter(new FileWriter(logFile, true), true);
+ this.indent = "";
+ if (existing) {
+ out.println(); // blank line to mark relaunch
+ }
+ }
protected PrintWriterTreeLogger(PrintWriter out, String indent) {
this.out = out;
this.indent = indent;
}
+ @Override
protected AbstractTreeLogger doBranch() {
return new PrintWriterTreeLogger(out, indent + " ");
}
@@ -53,23 +68,25 @@
@Override
protected void doLog(int indexOfLogEntryWithinParentLogger, Type type,
String msg, Throwable caught, HelpInfo helpInfo) {
- out.print(indent);
- if (type.needsAttention()) {
- out.print("[");
- out.print(type.getLabel());
- out.print("] ");
- }
-
- out.println(msg);
- if (helpInfo != null) {
- URL url = helpInfo.getURL();
- if (url != null) {
- out.print(indent);
- out.println("For additional info see: " + url.toString());
+ synchronized (mutex) { // ensure thread interleaving...
+ out.print(indent);
+ if (type.needsAttention()) {
+ out.print("[");
+ out.print(type.getLabel());
+ out.print("] ");
}
- }
- if (caught != null) {
- caught.printStackTrace(out);
+
+ out.println(msg);
+ if (helpInfo != null) {
+ URL url = helpInfo.getURL();
+ if (url != null) {
+ out.print(indent);
+ out.println("For additional info see: " + url.toString());
+ }
+ }
+ if (caught != null) {
+ caught.printStackTrace(out);
+ }
}
}
}
diff --git a/dev/core/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java b/dev/core/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java
index fdf6acb..d58e825 100644
--- a/dev/core/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java
+++ b/dev/core/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java
@@ -281,24 +281,36 @@
}
String inputFileName = "roundedCorners.css";
File inputFile = new File(classPath + RESOURCES_PATH + inputFileName);
+ if (!inputFile.exists()) {
+ inputFile = new File(classPath + inputFileName);
+ }
File outputFile = getOutFile("roundedCorners.css");
copyFileOrDirectory(inputFile, outputFile, classPath, RESOURCES_PATH
+ inputFileName, false);
inputFileName = "classLevel.css";
File inputFile2 = new File(classPath + RESOURCES_PATH + inputFileName);
+ if (!inputFile2.exists()) {
+ inputFile2 = new File(classPath + inputFileName);
+ }
File outputFile2 = getOutFile("classLevel.css");
copyFileOrDirectory(inputFile2, outputFile2, classPath, RESOURCES_PATH
+ inputFileName, false);
inputFileName = "common.css";
File inputFile3 = new File(classPath + RESOURCES_PATH + inputFileName);
+ if (!inputFile3.exists()) {
+ inputFile3 = new File(classPath + inputFileName);
+ }
File outputFile3 = getOutFile("common.css");
copyFileOrDirectory(inputFile3, outputFile3, classPath, RESOURCES_PATH
+ inputFileName, false);
inputFileName = "images";
File inputDir = new File(classPath + RESOURCES_PATH + "images");
+ if (!inputDir.exists()) {
+ inputDir = new File(classPath + "images");
+ }
File outputDir = getOutFile("images");
copyFileOrDirectory(inputDir, outputDir, classPath, inputFileName, true);
@@ -856,7 +868,12 @@
outFile.println("<body>");
outFile.println("<div class='abs mainHeader'>");
outFile.println("<h2>Story of Your Compile Dashboard</h2>");
-
+ String permutationInfo = settings.allPermsInfo.get(permutationId);
+ outFile.print("<h3>Permutation " + permutationId);
+ if (permutationInfo.length() > 0) {
+ outFile.println(" (" + permutationInfo + ")");
+ }
+ outFile.println("</h3>");
outFile.println("<hr>");
outFile.println("<center>");
if (globalInformation.getSplitPointToLocation().size() > 1) {
diff --git a/dev/oophm/overlay/com/google/gwt/dev/shell/ShellMainWindow.java b/dev/oophm/overlay/com/google/gwt/dev/shell/ShellMainWindow.java
index 6a94906..a58d5cf 100644
--- a/dev/oophm/overlay/com/google/gwt/dev/shell/ShellMainWindow.java
+++ b/dev/oophm/overlay/com/google/gwt/dev/shell/ShellMainWindow.java
@@ -21,6 +21,7 @@
import java.awt.BorderLayout;
import java.awt.GridLayout;
+import java.io.File;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
@@ -33,7 +34,7 @@
private SwingLoggerPanel logWindow;
- public ShellMainWindow(TreeLogger.Type maxLevel) {
+ public ShellMainWindow(TreeLogger.Type maxLevel, File logFile) {
super(new BorderLayout());
// TODO(jat): add back when we have real options
if (false) {
@@ -49,7 +50,7 @@
panel.add(launchPanel);
add(panel, BorderLayout.NORTH);
}
- logWindow = new SwingLoggerPanel(maxLevel);
+ logWindow = new SwingLoggerPanel(maxLevel, logFile);
add(logWindow);
}
diff --git a/dev/oophm/src/com/google/gwt/dev/ModulePanel.java b/dev/oophm/src/com/google/gwt/dev/ModulePanel.java
index f20ec84..281dd76 100644
--- a/dev/oophm/src/com/google/gwt/dev/ModulePanel.java
+++ b/dev/oophm/src/com/google/gwt/dev/ModulePanel.java
@@ -24,6 +24,7 @@
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import java.io.File;
import javax.swing.JButton;
import javax.swing.JOptionPane;
@@ -41,7 +42,7 @@
private boolean disconnected;
public ModulePanel(Type maxLevel, String moduleName,
- Session session) {
+ Session session, File logFile) {
super(new BorderLayout());
this.session = session;
if (false) {
@@ -57,7 +58,7 @@
topPanel.add(compileButton);
add(topPanel, BorderLayout.NORTH);
}
- loggerPanel = new SwingLoggerPanel(maxLevel);
+ loggerPanel = new SwingLoggerPanel(maxLevel, logFile);
add(loggerPanel);
session.addModule(moduleName, this);
}
diff --git a/dev/oophm/src/com/google/gwt/dev/ModuleTabPanel.java b/dev/oophm/src/com/google/gwt/dev/ModuleTabPanel.java
index e6b3107..196fa1a 100644
--- a/dev/oophm/src/com/google/gwt/dev/ModuleTabPanel.java
+++ b/dev/oophm/src/com/google/gwt/dev/ModuleTabPanel.java
@@ -17,7 +17,7 @@
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.dev.OophmHostedModeBase.TabPanelCollection;
-import com.google.gwt.dev.shell.Icons;
+import com.google.gwt.dev.util.BrowserInfo;
import java.awt.BorderLayout;
import java.awt.CardLayout;
@@ -26,6 +26,7 @@
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DateFormat;
@@ -226,40 +227,6 @@
}
/**
- * Holds information about the browser used in the UI.
- */
- private static class BrowserInfo {
-
- private final ImageIcon icon;
- private final String shortName;
-
- /**
- * Create a BrowserInfo instance.
- *
- * @param icon
- * @param shortName
- */
- public BrowserInfo(ImageIcon icon, String shortName) {
- this.icon = icon;
- this.shortName = shortName;
- }
-
- /**
- * @return the icon used to identify this browser, or null if none.
- */
- public ImageIcon getIcon() {
- return icon;
- }
-
- /**
- * @return the short name used to identify this browser, or null if none.
- */
- public String getShortName() {
- return shortName;
- }
- }
-
- /**
* Renderer used to show entries in the module dropdown box.
*/
private static class SessionModuleRenderer extends BasicComboBoxRenderer {
@@ -384,7 +351,7 @@
cardLayout = new CardLayout();
deckPanel.setLayout(cardLayout);
add(deckPanel);
- BrowserInfo browserInfo = getBrowserInfo(userAgent);
+ BrowserInfo browserInfo = BrowserInfo.getBrowserInfo(userAgent);
// Construct the tab title and tooltip
String tabTitle = url;
@@ -426,10 +393,11 @@
public synchronized ModulePanel addModuleSession(Type maxLevel,
String moduleName,
- String sessionKey) {
+ String sessionKey,
+ File logFile) {
Session session = findOrCreateSession(sessionKey);
- ModulePanel panel = new ModulePanel(maxLevel, moduleName, session);
+ ModulePanel panel = new ModulePanel(maxLevel, moduleName, session, logFile);
return panel;
}
@@ -479,32 +447,6 @@
return session;
}
- /**
- * Choose an icon appropriate for this browser, or null if none.
- *
- * @param userAgent User-Agent string from browser
- * @return icon or null if none
- */
- private BrowserInfo getBrowserInfo(String userAgent) {
- ImageIcon browserIcon = null;
- String shortName = null;
- String lcAgent = userAgent.toLowerCase();
- if (lcAgent.contains("msie")) {
- browserIcon = Icons.getIE24();
- shortName = "IE";
- } else if (lcAgent.contains("chrome")) {
- browserIcon = Icons.getChrome24();
- shortName = "Chrome";
- } else if (lcAgent.contains("webkit") || lcAgent.contains("safari")) {
- browserIcon = Icons.getSafari24();
- shortName = "Safari";
- } else if (lcAgent.contains("firefox")) {
- browserIcon = Icons.getFirefox24();
- shortName = "FF";
- }
- return new BrowserInfo(browserIcon, shortName);
- }
-
private String getTabTitle(URL parsedUrl) {
String tabTitle = parsedUrl.getPath();
if (tabTitle.length() > 0) {
diff --git a/dev/oophm/src/com/google/gwt/dev/OophmHostedModeBase.java b/dev/oophm/src/com/google/gwt/dev/OophmHostedModeBase.java
index bc3f169..908a578 100644
--- a/dev/oophm/src/com/google/gwt/dev/OophmHostedModeBase.java
+++ b/dev/oophm/src/com/google/gwt/dev/OophmHostedModeBase.java
@@ -27,6 +27,7 @@
import com.google.gwt.dev.shell.OophmSessionHandler;
import com.google.gwt.dev.shell.ShellMainWindow;
import com.google.gwt.dev.shell.ShellModuleSpaceHost;
+import com.google.gwt.dev.util.BrowserInfo;
import com.google.gwt.dev.util.collect.HashMap;
import com.google.gwt.dev.util.log.AbstractTreeLogger;
import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
@@ -35,6 +36,7 @@
import java.awt.Cursor;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
+import java.io.File;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
@@ -185,7 +187,10 @@
if (!isHeadless()) {
tabPanel = findModuleTab(userAgent, remoteSocket, url, tabKey,
moduleName);
- tab = tabPanel.addModuleSession(maxLevel, moduleName, sessionKey);
+ String agentTag = BrowserInfo.getShortName(userAgent).toLowerCase();
+ tab = tabPanel.addModuleSession(maxLevel, moduleName, sessionKey,
+ options.getLogFile(String.format("%s-%s-%d.log", moduleName,
+ agentTag, getNextSessionCounter(options.getLogDir()))));
logger = tab.getLogger();
TreeLogger branch = logger.branch(TreeLogger.INFO, "Loading module "
+ moduleName);
@@ -276,6 +281,8 @@
private static final Random RNG = new Random();
+ private static int sessionCounter = 0;
+
/**
* Produce a random string that has low probability of collisions.
*
@@ -448,6 +455,23 @@
return browserHost;
}
+ protected int getNextSessionCounter(File logdir) {
+ if (sessionCounter == 0 && logdir != null) {
+ // first time only, figure out the "last" session count already in use
+ for (String filename : logdir.list()) {
+ if (filename.matches("^[A-Za-z0-9_$]*-[a-z]*-[0-9]*.log$")) {
+ String substring = filename.substring(filename.lastIndexOf('-') + 1,
+ filename.length() - 4);
+ int number = Integer.parseInt(substring);
+ if (number > sessionCounter) {
+ sessionCounter = number;
+ }
+ }
+ }
+ }
+ return ++sessionCounter;
+ }
+
/**
* @return the icon to use for the web server tab
*/
@@ -498,10 +522,15 @@
ImageIcon gwtIcon = loadImageIcon("icon24.png");
frame = new JFrame("GWT Development Mode");
tabs = new JTabbedPane();
- mainWnd = new ShellMainWindow(options.getLogLevel());
+ if (options.alsoLogToFile()) {
+ options.getLogDir().mkdirs();
+ }
+ mainWnd = new ShellMainWindow(options.getLogLevel(),
+ options.getLogFile("main.log"));
tabs.addTab("Development Mode", gwtIcon, mainWnd, "GWT Development mode");
if (!options.isNoServer()) {
webServerLog = new WebServerPanel(getPort(), options.getLogLevel(),
+ options.getLogFile("webserver.log"),
new RestartAction() {
public void restartServer(TreeLogger logger) {
try {
diff --git a/dev/oophm/src/com/google/gwt/dev/WebServerPanel.java b/dev/oophm/src/com/google/gwt/dev/WebServerPanel.java
index 4c45873..4fb3986 100644
--- a/dev/oophm/src/com/google/gwt/dev/WebServerPanel.java
+++ b/dev/oophm/src/com/google/gwt/dev/WebServerPanel.java
@@ -21,6 +21,7 @@
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import java.io.File;
import javax.swing.JButton;
import javax.swing.JPanel;
@@ -38,14 +39,15 @@
private SwingLoggerPanel logWindow;
- public WebServerPanel(int serverPort, TreeLogger.Type maxLevel) {
- this(serverPort, maxLevel, null);
+ public WebServerPanel(int serverPort, TreeLogger.Type maxLevel,
+ File logFile) {
+ this(serverPort, maxLevel, logFile, null);
}
public WebServerPanel(int serverPort, TreeLogger.Type maxLevel,
- final RestartAction restartServerAction) {
+ File logFile, final RestartAction restartServerAction) {
super(new BorderLayout());
- logWindow = new SwingLoggerPanel(maxLevel);
+ logWindow = new SwingLoggerPanel(maxLevel, logFile);
if (restartServerAction != null) {
JPanel panel = new JPanel();
JButton restartButton = new JButton("Restart Server");
diff --git a/dev/oophm/src/com/google/gwt/dev/shell/log/SwingLoggerPanel.java b/dev/oophm/src/com/google/gwt/dev/shell/log/SwingLoggerPanel.java
index c24a316..f530a15 100644
--- a/dev/oophm/src/com/google/gwt/dev/shell/log/SwingLoggerPanel.java
+++ b/dev/oophm/src/com/google/gwt/dev/shell/log/SwingLoggerPanel.java
@@ -21,6 +21,8 @@
import com.google.gwt.dev.shell.CloseButton.Callback;
import com.google.gwt.dev.shell.log.SwingTreeLogger.LogEvent;
import com.google.gwt.dev.util.log.AbstractTreeLogger;
+import com.google.gwt.dev.util.log.CompositeTreeLogger;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
import java.awt.BorderLayout;
import java.awt.Color;
@@ -35,6 +37,8 @@
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
+import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
@@ -264,7 +268,7 @@
private boolean disconnected = false;
- public SwingLoggerPanel(TreeLogger.Type maxLevel) {
+ public SwingLoggerPanel(TreeLogger.Type maxLevel, File logFile) {
super(new BorderLayout());
regexFilter = "";
levelFilter = maxLevel;
@@ -368,8 +372,22 @@
treeView.setMinimumSize(minSize);
splitter.setDividerLocation(0.80);
add(splitter);
- logger = new SwingTreeLogger(this);
- logger.setMaxDetail(maxLevel);
+
+ AbstractTreeLogger uiLogger = new SwingTreeLogger(this);
+ AbstractTreeLogger bestLogger = uiLogger;
+ if (logFile != null) {
+ try {
+ PrintWriterTreeLogger fileLogger = new PrintWriterTreeLogger(logFile);
+ bestLogger = new CompositeTreeLogger(bestLogger, fileLogger);
+ fileLogger.setMaxDetail(maxLevel);
+ uiLogger.setMaxDetail(maxLevel);
+ } catch (IOException ex) {
+ bestLogger.log(TreeLogger.ERROR, "Can't log to file "
+ + logFile.getAbsolutePath(), ex);
+ }
+ }
+ bestLogger.setMaxDetail(maxLevel);
+ logger = bestLogger;
KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_F,
InputEvent.CTRL_DOWN_MASK);
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(key, "find");
diff --git a/dev/oophm/src/com/google/gwt/dev/shell/log/SwingTreeLogger.java b/dev/oophm/src/com/google/gwt/dev/shell/log/SwingTreeLogger.java
index 520769c..c7a4a79 100644
--- a/dev/oophm/src/com/google/gwt/dev/shell/log/SwingTreeLogger.java
+++ b/dev/oophm/src/com/google/gwt/dev/shell/log/SwingTreeLogger.java
@@ -113,7 +113,7 @@
// Show the exception info for anything other than "UnableToComplete".
//
if (exceptionDetail != null) {
- sb.append(htmlEscape(exceptionDetail));
+ sb.append("<pre>" + htmlEscape(exceptionDetail) + "</pre>");
}
if (helpInfo != null) {
URL url = helpInfo.getURL();
diff --git a/dev/oophm/src/com/google/gwt/dev/util/BrowserInfo.java b/dev/oophm/src/com/google/gwt/dev/util/BrowserInfo.java
new file mode 100644
index 0000000..5f40822
--- /dev/null
+++ b/dev/oophm/src/com/google/gwt/dev/util/BrowserInfo.java
@@ -0,0 +1,100 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.util;
+
+import com.google.gwt.dev.shell.Icons;
+
+import javax.swing.ImageIcon;
+
+/**
+ * Holds information about the browser used in the UI.
+ */
+public class BrowserInfo {
+
+ private static final String UNKNOWN = "Unknown";
+ private static final String FIREFOX = "FF";
+ private static final String SAFARI = "Safari";
+ private static final String OPERA = "Opera";
+ private static final String CHROME = "Chrome";
+ private static final String IE = "IE";
+
+ /**
+ * Choose an icon and short name appropriate for this browser. The icon
+ * may be null.
+ *
+ * @param userAgent User-Agent string from browser
+ * @return icon or null if none
+ */
+ public static BrowserInfo getBrowserInfo(String userAgent) {
+ ImageIcon browserIcon = null;
+ String shortName = getShortName(userAgent);
+ if (shortName.equals(IE)) {
+ browserIcon = Icons.getIE24();
+ } else if (shortName.equals(CHROME)) {
+ browserIcon = Icons.getChrome24();
+ } else if (shortName.equals(OPERA)) {
+ // no icon for Opera
+ } else if (shortName.equals(SAFARI)) {
+ browserIcon = Icons.getSafari24();
+ } else if (shortName.equals(FIREFOX)) {
+ browserIcon = Icons.getFirefox24();
+ }
+ return new BrowserInfo(browserIcon, shortName);
+ }
+
+ public static String getShortName(String userAgent) {
+ String lcAgent = userAgent.toLowerCase();
+ if (lcAgent.contains("msie")) {
+ return IE;
+ } else if (lcAgent.contains("chrome")) {
+ return CHROME;
+ } else if (lcAgent.contains("opera")) {
+ return OPERA;
+ } else if (lcAgent.contains("webkit") || lcAgent.contains("safari")) {
+ return SAFARI;
+ } else if (lcAgent.contains("firefox")) {
+ return FIREFOX;
+ }
+ return UNKNOWN;
+ }
+ private final ImageIcon icon;
+ private final String shortName;
+
+ /**
+ * Create a BrowserInfo instance.
+ *
+ * @param icon
+ * @param shortName
+ */
+ private BrowserInfo(ImageIcon icon, String shortName) {
+ this.icon = icon;
+ this.shortName = shortName;
+ }
+
+ /**
+ * @return the icon used to identify this browser, or null if none.
+ */
+ public ImageIcon getIcon() {
+ return icon;
+ }
+
+ /**
+ * @return the short name used to identify this browser, or null if none.
+ */
+ public String getShortName() {
+ return shortName;
+ }
+}
\ No newline at end of file
diff --git a/plugins/ie/oophm/oophm/oophm.vcproj b/plugins/ie/oophm/oophm/oophm.vcproj
index 6e4865c..a0cae8c 100644
--- a/plugins/ie/oophm/oophm/oophm.vcproj
+++ b/plugins/ie/oophm/oophm/oophm.vcproj
@@ -115,102 +115,6 @@
/>
</Configuration>
<Configuration
- Name="Release|Win32"
- OutputDirectory="$(ConfigurationName)32"
- IntermediateDirectory="$(ConfigurationName)32"
- ConfigurationType="2"
- UseOfMFC="1"
- UseOfATL="1"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
- >
- <Tool
- Name="VCPreBuildEventTool"
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- PreprocessorDefinitions="NDEBUG"
- MkTypLibCompatible="false"
- TargetEnvironment="1"
- GenerateStublessProxies="true"
- TypeLibraryName="$(IntDir)/oophm.tlb"
- HeaderFileName="oophm_i.h"
- DLLDataFileName=""
- InterfaceIdentifierFileName="oophm_i.c"
- ProxyFileName="oophm_p.c"
- ValidateParameters="true"
- />
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- AdditionalIncludeDirectories="../../../common"
- PreprocessorDefinitions="_WINDOWS;GWT_DEBUGDISABLE"
- RuntimeLibrary="0"
- UsePrecompiledHeader="0"
- WarningLevel="3"
- DebugInformationFormat="3"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- PreprocessorDefinitions="NDEBUG"
- Culture="1033"
- AdditionalIncludeDirectories="$(IntDir)"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- RegisterOutput="true"
- IgnoreImportLibrary="true"
- AdditionalDependencies="comsuppw.lib ws2_32.lib"
- OutputFile="..\..\prebuilt\$(ProjectName).dll"
- LinkIncremental="1"
- ModuleDefinitionFile=".\oophm.def"
- GenerateDebugInformation="true"
- SubSystem="2"
- OptimizeReferences="2"
- EnableCOMDATFolding="2"
- TargetMachine="1"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCWebDeploymentTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- />
- </Configuration>
- <Configuration
Name="Debug|x64"
OutputDirectory="$(PlatformName)\$(ConfigurationName)"
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
@@ -305,6 +209,102 @@
/>
</Configuration>
<Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)32"
+ IntermediateDirectory="$(ConfigurationName)32"
+ ConfigurationType="2"
+ UseOfMFC="1"
+ UseOfATL="1"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="false"
+ TargetEnvironment="1"
+ GenerateStublessProxies="true"
+ TypeLibraryName="$(IntDir)/oophm.tlb"
+ HeaderFileName="oophm_i.h"
+ DLLDataFileName=""
+ InterfaceIdentifierFileName="oophm_i.c"
+ ProxyFileName="oophm_p.c"
+ ValidateParameters="true"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""$(ProjectDir)";../../../platform/Win;../../../common"
+ PreprocessorDefinitions="_WINDOWS;GWT_DEBUGDISABLE"
+ RuntimeLibrary="0"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(IntDir)"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ RegisterOutput="true"
+ IgnoreImportLibrary="true"
+ AdditionalDependencies="comsuppw.lib ws2_32.lib"
+ OutputFile="..\..\prebuilt\$(ProjectName).dll"
+ LinkIncremental="1"
+ ModuleDefinitionFile=".\oophm.def"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
Name="Release|x64"
OutputDirectory="$(ConfigurationName)64"
IntermediateDirectory="$(ConfigurationName)64"
@@ -342,7 +342,7 @@
Name="VCCLCompilerTool"
Optimization="2"
FavorSizeOrSpeed="1"
- AdditionalIncludeDirectories="../../../common"
+ AdditionalIncludeDirectories="../../../platform/Win;../../../common"
PreprocessorDefinitions="_WINDOWS;GWT_DEBUGDISABLE"
RuntimeLibrary="0"
UsePrecompiledHeader="0"
@@ -410,7 +410,7 @@
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
- RelativePath=".\AllowDialog.cpp"
+ RelativePath="..\..\..\platform\Win\AllowDialog.cpp"
>
</File>
<File
@@ -426,7 +426,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Win32"
+ Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
@@ -435,7 +435,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|x64"
+ Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
@@ -466,7 +466,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Win32"
+ Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
@@ -475,7 +475,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|x64"
+ Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
@@ -522,7 +522,7 @@
>
</File>
<File
- RelativePath=".\Preferences.cpp"
+ RelativePath="..\..\..\platform\Win\Preferences.cpp"
>
</File>
<File
@@ -537,7 +537,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Win32"
+ Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
@@ -545,7 +545,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|x64"
+ Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
@@ -636,7 +636,7 @@
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
- RelativePath=".\AllowDialog.h"
+ RelativePath="..\..\..\platform\Win\AllowDialog.h"
>
</File>
<File
@@ -668,7 +668,7 @@
>
</File>
<File
- RelativePath=".\Preferences.h"
+ RelativePath="..\..\..\platform\Win\Preferences.h"
>
</File>
<File
@@ -831,7 +831,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Win32"
+ Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
@@ -840,7 +840,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|x64"
+ Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
diff --git a/plugins/npapi/LocalObjectTable.cpp b/plugins/npapi/LocalObjectTable.cpp
index e0c381c..a450381 100644
--- a/plugins/npapi/LocalObjectTable.cpp
+++ b/plugins/npapi/LocalObjectTable.cpp
@@ -1,37 +1,8 @@
#include "mozincludes.h"
#include "LocalObjectTable.h"
-// Mirrors of NPObjectWrapper and NPObjectProxy from Chrome
-// TODO(jat): this is very fragile and may break if Chrome changes
-struct ChromeNPObjectProxy {
- // IPC::Channel::Listener and IPC::Message::Sender are pure interfaces, so we don't need to
- // account for any data fields from their inheritance
- void* channel_; // scoped_refptr keeps only a single pointer
- void* unknown; // looks like another pointer before route id
- int route_id_;
- intptr_t npobject_ptr_;
-};
-
-struct ChromeNPObjectWrapper {
- NPObject object;
- ChromeNPObjectProxy* proxy;
-};
-
-NPClass* LocalObjectTable::wrappedObjectClass = 0;
-
LocalObjectTable::~LocalObjectTable() {
if (!dontFree) {
freeAll();
}
}
-
-void* LocalObjectTable::getIdentityFrom(NPObject* obj) {
- void* id = obj;
- if (obj->_class == wrappedObjectClass) {
- ChromeNPObjectWrapper* wrapper = reinterpret_cast<ChromeNPObjectWrapper*>(obj);
- ChromeNPObjectProxy* proxy = wrapper->proxy;
- id = reinterpret_cast<void*>(proxy->npobject_ptr_);
- Debug::log(Debug::Info) << "Mapped obj=" << (void*)obj << " to " << id << Debug::flush;
- }
- return id;
-}
diff --git a/plugins/npapi/LocalObjectTable.h b/plugins/npapi/LocalObjectTable.h
index 4943ff3..9ff809d 100644
--- a/plugins/npapi/LocalObjectTable.h
+++ b/plugins/npapi/LocalObjectTable.h
@@ -18,7 +18,6 @@
#include <vector>
#include <algorithm>
-#include <map>
#include "Debug.h"
@@ -30,9 +29,7 @@
int nextFree;
std::vector<NPObject*> objects;
- std::map<void*, int> objectIndex;
bool dontFree;
- static NPClass* wrappedObjectClass;
bool isFree(int id) {
// low bit is set for free pointers, object pointers can't be odd
@@ -45,15 +42,6 @@
nextFree = id;
}
- int findIndex(void* ptr) {
- std::map<void*, int>::iterator it = objectIndex.find(ptr);
- if (it == objectIndex.end()) {
- return -1;
- }
- return it->second;
- }
-
- static void* getIdentityFrom(NPObject* obj);
public:
LocalObjectTable() {
nextFree = -1;
@@ -63,18 +51,11 @@
virtual ~LocalObjectTable();
- static void setWrappedObjectClass(NPClass* clazz) {
- wrappedObjectClass = clazz;
- }
-
+ /**
+ * Add a new object, which must not be in the table, and return a new id for it.
+ */
int add(NPObject* obj) {
- void* objId = getIdentityFrom(obj);
- int id = findIndex(objId);
- if (id >= 0) {
- Debug::log(Debug::Spam) << "LocalObjectTable::add(obj=" << obj
- << "): returning old id=" << id << Debug::flush;
- return id;
- }
+ int id;
if (nextFree >= 0) {
id = nextFree;
nextFree = int(reinterpret_cast<long long>(objects[nextFree])) >> 1;
@@ -83,7 +64,6 @@
id = static_cast<int>(objects.size());
objects.push_back(obj);
}
- objectIndex[objId] = id;
Debug::log(Debug::Spam) << "LocalObjectTable::add(obj=" << obj << "): id=" << id
<< Debug::flush;
// keep track that we hold a reference in the table
@@ -91,11 +71,6 @@
return id;
}
- int find(NPObject* obj) {
- void* objId = getIdentityFrom(obj);
- return findIndex(objId);
- }
-
void free(int id) {
Debug::log(Debug::Spam) << "LocalObjectTable::free(id=" << id << ")" << Debug::flush;
if (unsigned(id) >= objects.size()) {
diff --git a/plugins/npapi/NPVariantWrapper.h b/plugins/npapi/NPVariantWrapper.h
index 0a469c9..91bcaaf 100644
--- a/plugins/npapi/NPVariantWrapper.h
+++ b/plugins/npapi/NPVariantWrapper.h
@@ -490,6 +490,12 @@
return *this;
}
+ // Convenience method for C++ code
+ NPVariantWrapper& operator=(int intval) {
+ NPVariantProxy::assignFrom(variant, intval);
+ return *this;
+ }
+
void release() {
NPVariantProxy::release(variant);
}
diff --git a/plugins/npapi/ScriptableInstance.cpp b/plugins/npapi/ScriptableInstance.cpp
index b3c5c18..6a137bd 100644
--- a/plugins/npapi/ScriptableInstance.cpp
+++ b/plugins/npapi/ScriptableInstance.cpp
@@ -21,6 +21,9 @@
#include "ReturnMessage.h"
#include "ServerMethods.h"
#include "AllowedConnections.h"
+#include "Preferences.h"
+#include "AllowDialog.h"
+
#include "mozincludes.h"
#include "scoped_ptr/scoped_ptr.h"
#include "NPVariantWrapper.h"
@@ -32,12 +35,6 @@
return string(GetNPStringUTF8Characters(str), GetNPStringUTF8Length(str));
}
-static bool askUserToAllow(const std::string& url, bool* remember) {
- // TODO(jat): implement, for now allow anything but don't remember
- *remember = false;
- return true;
-}
-
string ScriptableInstance::computeTabIdentity() {
return "";
}
@@ -65,7 +62,7 @@
toStringID(NPN_GetStringIdentifier("toString")),
connectedID(NPN_GetStringIdentifier("connected")),
statsID(NPN_GetStringIdentifier("stats")),
- savedID(NPN_GetStringIdentifier("saved")),
+ gwtId(NPN_GetStringIdentifier("__gwt_ObjectId")),
jsInvokeID(NPN_GetStringIdentifier("__gwt_jsInvoke")),
jsResultID(NPN_GetStringIdentifier("__gwt_makeResult")),
jsTearOffID(NPN_GetStringIdentifier("__gwt_makeTearOff")),
@@ -145,7 +142,7 @@
return true;
}
// TODO: special-case toString tear-offs
- return name == statsID || name == connectedID || name == savedID;
+ return name == statsID || name == connectedID;
}
bool ScriptableInstance::getProperty(NPIdentifier name, NPVariant* variant) {
@@ -159,13 +156,6 @@
} else if (name == statsID) {
NPVariantProxy::assignFrom(*variant, "<stats data>");
retVal = true;
- } else if (name == savedID) {
- if (savedValueIdx >= 0) {
- NPObject* npObj = localObjects.get(savedValueIdx);
- OBJECT_TO_NPVARIANT(npObj, *variant);
- NPN_RetainObject(npObj);
- }
- retVal = true;
}
if (retVal) {
// TODO: testing
@@ -179,14 +169,6 @@
Debug::log(Debug::Debugging) << "ScriptableInstance::setProperty(name="
<< NPN_UTF8FromIdentifier(name) << ", value=" << *variant << ")"
<< Debug::flush;
- if (NPN_IdentifierIsString(name)) {
- if (name == savedID && NPVariantProxy::isObject(*variant)) {
- Debug::log(Debug::Debugging) << " set saved value" << Debug::flush;
- savedValueIdx = localObjects.add(NPVariantProxy::getAsObject(*variant));
- return true;
- }
- return false;
- }
return false;
}
@@ -259,7 +241,6 @@
// replace our window object with that passed by the caller
window = NPVariantProxy::getAsObject(args[0]);
NPN_RetainObject(window);
- LocalObjectTable::setWrappedObjectClass(window->_class);
BOOLEAN_TO_NPVARIANT(true, *result);
result->type = NPVariantType_Bool;
}
@@ -289,16 +270,17 @@
<< ",module=" << NPVariantProxy::toString(args[3]) << ",hostedHtmlVers=" << NPVariantProxy::toString(args[4])
<< ")" << Debug::flush;
const std::string urlStr = convertToString(url);
- bool allowed = false;
- // TODO(jat): load access list
- if (!AllowedConnections::matchesRule(urlStr, &allowed)) {
- // If we didn't match an existing rule, prompt the user
- bool remember = false;
- allowed = askUserToAllow(urlStr, &remember);
- if (remember) {
- // TODO(jat): update access list
- }
- }
+
+ Preferences::loadAccessList();
+ bool allowed = false;
+ if (!AllowedConnections::matchesRule(urlStr, &allowed)) {
+ bool remember = false;
+ allowed = AllowDialog::askUserToAllow(&remember);
+ if (remember) {
+ std::string host = AllowedConnections::getHostFromUrl(urlStr);
+ Preferences::addNewRule(host, !allowed);
+ }
+ }
if (!allowed) {
BOOLEAN_TO_NPVARIANT(false, *result);
result->type = NPVariantType_Bool;
@@ -345,6 +327,22 @@
result->type = NPVariantType_Bool;
}
+int ScriptableInstance::getLocalObjectRef(NPObject* obj) {
+ NPVariantWrapper wrappedRetVal(*this);
+ int id;
+ if (NPN_GetProperty(getNPP(), obj, gwtId, wrappedRetVal.addressForReturn())
+ && wrappedRetVal.isInt()) {
+ id = wrappedRetVal.getAsInt();
+ } else {
+ id = localObjects.add(obj);
+ wrappedRetVal = id;
+ if (!NPN_SetProperty(getNPP(), obj, gwtId, wrappedRetVal.address())) {
+ Debug::log(Debug::Error) << "Setting GWT id on object failed" << Debug::flush;
+ }
+ }
+ return id;
+}
+
void ScriptableInstance::fatalError(HostChannel& channel, const std::string& message) {
// TODO(jat): better error handling
Debug::log(Debug::Error) << "Fatal error: " << message << Debug::flush;
@@ -362,7 +360,12 @@
Debug::log(Debug::Debugging) << "freeValue(#ids=" << idCount << ")" << Debug::flush;
for (int i = 0; i < idCount; ++i) {
Debug::log(Debug::Spam) << " id=" << ids[i] << Debug::flush;
- localObjects.free(ids[i]);
+ NPObject* obj = localObjects.get(ids[i]);
+ if (!NPN_RemoveProperty(getNPP(), obj, gwtId)) {
+ Debug::log(Debug::Error) << "Unable to remove GWT ID from object " << ids[i] << Debug::flush;
+ } else {
+ localObjects.free(ids[i]);
+ }
}
}
diff --git a/plugins/npapi/ScriptableInstance.h b/plugins/npapi/ScriptableInstance.h
index 035f823..cdce6f3 100644
--- a/plugins/npapi/ScriptableInstance.h
+++ b/plugins/npapi/ScriptableInstance.h
@@ -63,7 +63,7 @@
void dumpJSresult(const char* js);
- int getLocalObjectRef(NPObject* obj) { return localObjects.add(obj); }
+ int getLocalObjectRef(NPObject* obj);
NPObject* getLocalObject(int refid) { return localObjects.get(refid); }
bool tryGetStringPrimitive(NPObject* obj, NPVariant& result);
@@ -92,7 +92,7 @@
const NPIdentifier connectedID;
const NPIdentifier statsID;
- const NPIdentifier savedID;
+ const NPIdentifier gwtId;
const NPIdentifier jsInvokeID;
const NPIdentifier jsResultID;
diff --git a/plugins/npapi/VisualStudio/npapi-plugin.vcproj b/plugins/npapi/VisualStudio/npapi-plugin.vcproj
index a0e4545..d338c7f 100755
--- a/plugins/npapi/VisualStudio/npapi-plugin.vcproj
+++ b/plugins/npapi/VisualStudio/npapi-plugin.vcproj
@@ -39,7 +39,7 @@
<Tool
Name="VCCLCompilerTool"
Optimization="0"
- AdditionalIncludeDirectories=""$(ProjectDir)\..\..\common""
+ AdditionalIncludeDirectories=""$(ProjectDir)\..\..\platform\Win";"$(ProjectDir)\..";"$(ProjectDir)\..\..\common""
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;FIREFOXPLUGIN_EXPORTS"
MinimalRebuild="true"
BasicRuntimeChecks="3"
@@ -63,7 +63,7 @@
Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib"
ShowProgress="2"
- OutputFile="$(ProjectDir)\..\prebuilt\WINNT_x86-msvc\npOOPHM.dll"
+ OutputFile="$(ProjectDir)\..\prebuilt\gwtdmp\WINNT_x86-msvc\npOOPHM.dll"
LinkIncremental="1"
ModuleDefinitionFile="$(ProjectDir)\..\npOOPHM.def"
GenerateDebugInformation="true"
@@ -122,7 +122,7 @@
Name="VCCLCompilerTool"
Optimization="3"
EnableIntrinsicFunctions="true"
- AdditionalIncludeDirectories=""..\..\common""
+ AdditionalIncludeDirectories=""$(ProjectDir)\..\..\platform\Win";"$(ProjectDir)\..";"$(ProjectDir)\..\..\common""
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;FIREFOXPLUGIN_EXPORTS"
ExceptionHandling="1"
RuntimeLibrary="2"
@@ -144,7 +144,7 @@
Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib"
ShowProgress="2"
- OutputFile="..\extension\platform\WINNT_x86-msvc\plugins\npOOPHM.dll"
+ OutputFile="$(ProjectDir)\..\prebuilt\gwtdmp\WINNT_x86-msvc\npOOPHM.dll"
LinkIncremental="0"
AdditionalLibraryDirectories=""
ModuleDefinitionFile="..\npOOPHM.def"
@@ -191,6 +191,10 @@
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
+ RelativePath="..\..\platform\Win\AllowDialog.h"
+ >
+ </File>
+ <File
RelativePath="..\..\common\AllowedConnections.h"
>
</File>
@@ -291,6 +295,10 @@
>
</File>
<File
+ RelativePath="..\..\platform\Win\Preferences.h"
+ >
+ </File>
+ <File
RelativePath="..\..\common\ProtocolVersionMessage.h"
>
</File>
@@ -351,6 +359,10 @@
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
+ RelativePath="..\..\platform\Win\AllowDialog.cpp"
+ >
+ </File>
+ <File
RelativePath="..\..\common\AllowedConnections.cpp"
>
</File>
@@ -419,6 +431,10 @@
>
</File>
<File
+ RelativePath="..\..\platform\Win\Preferences.cpp"
+ >
+ </File>
+ <File
RelativePath="..\..\common\ProtocolVersionMessage.cpp"
>
</File>
diff --git a/plugins/npapi/extension/platform/WINNT_x86-msvc/plugins/npOOPHM.dll b/plugins/npapi/extension/platform/WINNT_x86-msvc/plugins/npOOPHM.dll
deleted file mode 100755
index dfcdb9b..0000000
--- a/plugins/npapi/extension/platform/WINNT_x86-msvc/plugins/npOOPHM.dll
+++ /dev/null
Binary files differ
diff --git a/plugins/npapi/main.cpp b/plugins/npapi/main.cpp
index 065c0c0..27338ac 100644
--- a/plugins/npapi/main.cpp
+++ b/plugins/npapi/main.cpp
@@ -17,11 +17,13 @@
#include "Plugin.h"
#include "ScriptableInstance.h"
#include "scoped_ptr/scoped_ptr.h"
+#include "AllowDialog.h"
#ifdef _WINDOWS
#include <windows.h>
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ulReasonForCall, LPVOID lpReserved) {
+ AllowDialog::setHInstance(hModule);
switch (ulReasonForCall) {
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
@@ -280,11 +282,11 @@
Debug::log(Debug::Info) << "NP_GetValue(var=" << variable << ")" << Debug::flush;
switch (variable) {
case NPPVpluginNameString:
- *static_cast<const char **>(value) = "GWT Hosted-mode Plugin";
+ *static_cast<const char **>(value) = "GWT Development-Mode Plugin";
break;
case NPPVpluginDescriptionString:
*static_cast<const char **>(value) = "Plugin to enable debugging of Google Web Toolkit "
- "applications in hosted mode.";
+ "applications in development mode.";
break;
default:
Debug::log(Debug::Info) << "NPP_GetValue(var=" << variable
diff --git a/plugins/npapi/npOOPHM.rc b/plugins/npapi/npOOPHM.rc
index 8c0f920..ed2790b 100644
--- a/plugins/npapi/npOOPHM.rc
+++ b/plugins/npapi/npOOPHM.rc
@@ -1,73 +1,111 @@
-#include "resource.h"
-
-#define APSTUDIO_READONLY_SYMBOLS
-#include "windows.h"
-#undef APSTUDIO_READONLY_SYMBOLS
-
-#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
-#ifdef _WIN32
-LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
-#pragma code_page(1252)
-#endif
-
-1 VERSIONINFO
- FILEVERSION 0,1,1,0
- PRODUCTVERSION 0,1,1,0
- FILEFLAGSMASK 0x3fL
-#ifdef _DEBUG
- FILEFLAGS 0x1L
-#else
- FILEFLAGS 0x0L
-#endif
- FILEOS 0x40004L
- FILETYPE 0x2L
- FILESUBTYPE 0x0L
-BEGIN
- BLOCK "StringFileInfo"
- BEGIN
- BLOCK "040904e4"
- BEGIN
- VALUE "CompanyName", "Google Inc"
- VALUE "FileDescription", "GWT OOPHM Plugin"
-#if 0
- VALUE "FileExtents", ""
-#endif
- VALUE "FileOpenName", "Plugin to allow debugging of GWT applications in hosted mode."
- VALUE "FileVersion", "0.1a"
- VALUE "InternalName", "GWT OOPHM Plugin"
- VALUE "LegalCopyright", "Copyright © 2008 Google Inc. All rights reserved."
- VALUE "MIMEType", "application/x-gwt-hosted-mode"
- VALUE "OriginalFilename", "npOOPHM.dll"
- VALUE "ProductName", "GWT OOPHM Plugin"
- VALUE "ProductVersion", "0.1a"
- END
- END
- BLOCK "VarFileInfo"
- BEGIN
- VALUE "Translation", 0x409, 1252
- END
-END
-
-#ifdef APSTUDIO_INVOKED
-1 TEXTINCLUDE
-BEGIN
- "resource.h\0"
-END
-
-2 TEXTINCLUDE
-BEGIN
- "#include ""mfc/afxres.h""\r\n"
- "\0"
-END
-
-3 TEXTINCLUDE
-BEGIN
- "\r\n"
- "\0"
-END
-
-#endif
-
-#else
-
-#endif
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+#include "windows.h"
+#include "winres.h"
+#undef APSTUDIO_READONLY_SYMBOLS
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif
+
+1 VERSIONINFO
+ FILEVERSION 0,9,0,0
+ PRODUCTVERSION 0,9,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "Google Inc"
+ VALUE "FileDescription", "GWT Development Mode Plugin"
+#if 0
+ VALUE "FileExtents", ""
+#endif
+ VALUE "FileOpenName", "Plugin to allow debugging of GWT applications in hosted mode."
+ VALUE "FileVersion", "0.9.0"
+ VALUE "InternalName", "GWT DMP"
+ VALUE "LegalCopyright", "Copyright © 2009 Google Inc. All rights reserved."
+ VALUE "MIMEType", "application/x-gwt-hosted-mode"
+ VALUE "OriginalFilename", "npOOPHM.dll"
+ VALUE "ProductName", "GWT DMP Plugin"
+ VALUE "ProductVersion", "0.9.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
+
+#ifdef APSTUDIO_INVOKED
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""mfc/afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_ALLOW_DIALOG DIALOGEX 0, 0, 188, 73
+STYLE DS_SYSMODAL | DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_CENTER | WS_POPUP | WS_CAPTION
+EXSTYLE WS_EX_TOPMOST
+CAPTION "GWT Plugin Security Alert"
+FONT 10, "Microsoft Sans Serif", 400, 0, 0x0
+BEGIN
+ CONTROL "Remember this decision for this server",IDC_REMEMBER_CHECKBOX,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,30,31,129,10
+ LTEXT "This web server is trying to initiate a GWT Development\r\nMode Conncetion -- should it be allowed?",IDC_STATIC,10,7,167,19
+ PUSHBUTTON "Allow",IDC_ALLOW_BUTTON,37,50,50,14
+ DEFPUSHBUTTON "Deny",IDCANCEL,100,50,50,14
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_ALLOW_DIALOG, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 181
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 66
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#else
+
+#endif
diff --git a/plugins/npapi/prebuilt/extension/chrome.manifest b/plugins/npapi/prebuilt/extension/chrome.manifest
deleted file mode 100644
index 38a0fdd..0000000
--- a/plugins/npapi/prebuilt/extension/chrome.manifest
+++ /dev/null
@@ -1,2 +0,0 @@
-content gwt-oophm content/
-skin gwt-oophm classic/1.0 skin/
diff --git a/plugins/npapi/prebuilt/extension/platform/Darwin_x86-gcc3/plugins/oophm.plugin/Contents/Info.plist b/plugins/npapi/prebuilt/extension/platform/Darwin_x86-gcc3/plugins/oophm.plugin/Contents/Info.plist
deleted file mode 100644
index 234ad6e..0000000
--- a/plugins/npapi/prebuilt/extension/platform/Darwin_x86-gcc3/plugins/oophm.plugin/Contents/Info.plist
+++ /dev/null
@@ -1,20 +0,0 @@
-<plist version="1.0">
-<dict>
- <key>CFBundleDevelopmentRegion</key>
- <string>English</string>
- <key>CFBundleExecutable</key>
- <string>liboophm</string>
- <key>CFBundleIdentifier</key>
- <string>com.google.gwt.oophm</string>
- <key>CFBundleName</key>
- <string>oophm</string>
- <key>CFBundleInfoDictionaryVersion</key>
- <string>6.0</string>
- <key>CFBundlePackageType</key>
- <string>NSPL</string>
- <key>CFBundleSignature</key>
- <string>MOSS</string>
- <key>CFBundleVersion</key>
- <string>1.0</string>
-</dict>
-</plist>
diff --git a/plugins/npapi/prebuilt/extension/platform/Darwin_x86-gcc3/plugins/oophm.plugin/Contents/MacOS/liboophm b/plugins/npapi/prebuilt/extension/platform/Darwin_x86-gcc3/plugins/oophm.plugin/Contents/MacOS/liboophm
deleted file mode 100755
index 49d5a1f..0000000
--- a/plugins/npapi/prebuilt/extension/platform/Darwin_x86-gcc3/plugins/oophm.plugin/Contents/MacOS/liboophm
+++ /dev/null
Binary files differ
diff --git a/plugins/npapi/prebuilt/extension/platform/Darwin_x86-gcc3/plugins/oophm.plugin/Contents/Resources/liboophm.rsrc b/plugins/npapi/prebuilt/extension/platform/Darwin_x86-gcc3/plugins/oophm.plugin/Contents/Resources/liboophm.rsrc
deleted file mode 100644
index c1cb961..0000000
--- a/plugins/npapi/prebuilt/extension/platform/Darwin_x86-gcc3/plugins/oophm.plugin/Contents/Resources/liboophm.rsrc
+++ /dev/null
Binary files differ
diff --git a/plugins/npapi/prebuilt/extension/platform/WINNT_x86-msvc/plugins/npOOPHM.dll b/plugins/npapi/prebuilt/extension/platform/WINNT_x86-msvc/plugins/npOOPHM.dll
deleted file mode 100755
index dfcdb9b..0000000
--- a/plugins/npapi/prebuilt/extension/platform/WINNT_x86-msvc/plugins/npOOPHM.dll
+++ /dev/null
Binary files differ
diff --git a/plugins/npapi/prebuilt/gwtdmp.crx b/plugins/npapi/prebuilt/gwtdmp.crx
new file mode 100644
index 0000000..1538642
--- /dev/null
+++ b/plugins/npapi/prebuilt/gwtdmp.crx
Binary files differ
diff --git a/plugins/npapi/prebuilt/gwtdmp/WINNT_x86-msvc/npOOPHM.dll b/plugins/npapi/prebuilt/gwtdmp/WINNT_x86-msvc/npOOPHM.dll
new file mode 100644
index 0000000..a1dcaa9
--- /dev/null
+++ b/plugins/npapi/prebuilt/gwtdmp/WINNT_x86-msvc/npOOPHM.dll
Binary files differ
diff --git a/plugins/npapi/prebuilt/gwtdmp/gwt128.png b/plugins/npapi/prebuilt/gwtdmp/gwt128.png
new file mode 100644
index 0000000..57e8e01
--- /dev/null
+++ b/plugins/npapi/prebuilt/gwtdmp/gwt128.png
Binary files differ
diff --git a/plugins/npapi/prebuilt/gwtdmp/gwt16.png b/plugins/npapi/prebuilt/gwtdmp/gwt16.png
new file mode 100644
index 0000000..f26096e
--- /dev/null
+++ b/plugins/npapi/prebuilt/gwtdmp/gwt16.png
Binary files differ
diff --git a/plugins/npapi/prebuilt/extension/skin/icon.png b/plugins/npapi/prebuilt/gwtdmp/gwt32.png
similarity index 100%
rename from plugins/npapi/prebuilt/extension/skin/icon.png
rename to plugins/npapi/prebuilt/gwtdmp/gwt32.png
Binary files differ
diff --git a/plugins/npapi/prebuilt/gwtdmp/gwt48.png b/plugins/npapi/prebuilt/gwtdmp/gwt48.png
new file mode 100644
index 0000000..e8a4172
--- /dev/null
+++ b/plugins/npapi/prebuilt/gwtdmp/gwt48.png
Binary files differ
diff --git a/plugins/npapi/prebuilt/gwtdmp/gwt64.png b/plugins/npapi/prebuilt/gwtdmp/gwt64.png
new file mode 100644
index 0000000..922cc88
--- /dev/null
+++ b/plugins/npapi/prebuilt/gwtdmp/gwt64.png
Binary files differ
diff --git a/plugins/npapi/prebuilt/gwtdmp/manifest.json b/plugins/npapi/prebuilt/gwtdmp/manifest.json
new file mode 100644
index 0000000..ad68f94
--- /dev/null
+++ b/plugins/npapi/prebuilt/gwtdmp/manifest.json
@@ -0,0 +1,15 @@
+{
+ "name": "GWT Development Mode Plugin",
+ "version": "0.9.0",
+ "description": "A plugin to enable debugging with GWT's Development Mode",
+ "icons": {
+ "16": "gwt16.png",
+ "32": "gwt32.png",
+ "48": "gwt48.png",
+ "64": "gwt64.png",
+ "128": "gwt128.png"
+ },
+ "plugins": [
+ { "path": "WINNT_x86-msvc/npOOPHM.dll", "public": true }
+ ]
+}
diff --git a/plugins/npapi/prebuilt/oophm.xpi b/plugins/npapi/prebuilt/oophm.xpi
deleted file mode 100644
index 425cdba..0000000
--- a/plugins/npapi/prebuilt/oophm.xpi
+++ /dev/null
Binary files differ
diff --git a/plugins/npapi/prebuilt/winnt_x86-msvc/npOOPHM.dll b/plugins/npapi/prebuilt/winnt_x86-msvc/npOOPHM.dll
deleted file mode 100644
index 8e91518..0000000
--- a/plugins/npapi/prebuilt/winnt_x86-msvc/npOOPHM.dll
+++ /dev/null
Binary files differ
diff --git a/plugins/npapi/resource.h b/plugins/npapi/resource.h
index 359695e..645e017 100644
--- a/plugins/npapi/resource.h
+++ b/plugins/npapi/resource.h
@@ -1,20 +1,23 @@
-//{{NO_DEPENDENCIES}}
-// Microsoft Visual C++ generated include file.
-// Used by npApuProto.rc
-//
-#define IDD_MAIN 101
-#define IDC_BUTTON_GO 1002
-#define IDC_STATIC_UA 1003
-#define IDC_BUTTON1 1005
-#define IDC_BUTTON_DONT 1005
-
-// Next default values for new objects
-//
-#ifdef APSTUDIO_INVOKED
-#ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE 102
-#define _APS_NEXT_COMMAND_VALUE 40001
-#define _APS_NEXT_CONTROL_VALUE 1006
-#define _APS_NEXT_SYMED_VALUE 101
-#endif
-#endif
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by npOOPHM.rc
+//
+#define IDD_MAIN 101
+#define IDC_BUTTON_GO 1002
+#define IDC_STATIC_UA 1003
+#define IDC_BUTTON1 1005
+#define IDC_BUTTON_DONT 1005
+#define IDD_ALLOW_DIALOG 103
+#define IDC_REMEMBER_CHECKBOX 201
+#define IDC_ALLOW_BUTTON 202
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1006
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/plugins/ie/oophm/oophm/AllowDialog.cpp b/plugins/platform/Win/AllowDialog.cpp
similarity index 100%
rename from plugins/ie/oophm/oophm/AllowDialog.cpp
rename to plugins/platform/Win/AllowDialog.cpp
diff --git a/plugins/ie/oophm/oophm/AllowDialog.h b/plugins/platform/Win/AllowDialog.h
similarity index 92%
rename from plugins/ie/oophm/oophm/AllowDialog.h
rename to plugins/platform/Win/AllowDialog.h
index b8d14c8..93f9438 100644
--- a/plugins/ie/oophm/oophm/AllowDialog.h
+++ b/plugins/platform/Win/AllowDialog.h
@@ -16,7 +16,10 @@
* the License.
*/
-#include "stdafx.h"
+#ifdef _WINDOWS
+#include <windows.h>
+#include <winnt.h>
+#include <windef.h>
class AllowDialog {
public:
@@ -34,5 +37,6 @@
private:
static HINSTANCE hInstance;
};
+#endif
#endif
diff --git a/plugins/ie/oophm/oophm/Preferences.cpp b/plugins/platform/Win/Preferences.cpp
similarity index 98%
rename from plugins/ie/oophm/oophm/Preferences.cpp
rename to plugins/platform/Win/Preferences.cpp
index cabe0d5..ee15029 100644
--- a/plugins/ie/oophm/oophm/Preferences.cpp
+++ b/plugins/platform/Win/Preferences.cpp
@@ -14,7 +14,7 @@
* the License.
*/
-#include "stdafx.h"
+#include <windows.h>
#include <winnt.h>
#include <winreg.h>
#include "Debug.h"
diff --git a/plugins/ie/oophm/oophm/Preferences.h b/plugins/platform/Win/Preferences.h
similarity index 100%
rename from plugins/ie/oophm/oophm/Preferences.h
rename to plugins/platform/Win/Preferences.h
diff --git a/user/javadoc/com/google/gwt/examples/SplitLayoutPanelExample.java b/user/javadoc/com/google/gwt/examples/SplitLayoutPanelExample.java
new file mode 100644
index 0000000..e12b1b3
--- /dev/null
+++ b/user/javadoc/com/google/gwt/examples/SplitLayoutPanelExample.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.examples;
+
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.RootLayoutPanel;
+import com.google.gwt.user.client.ui.SplitLayoutPanel;
+import com.google.gwt.user.client.ui.DockLayoutPanel.Direction;
+
+public class SplitLayoutPanelExample implements EntryPoint {
+
+ public void onModuleLoad() {
+ // Create a three-pane layout with splitters.
+ SplitLayoutPanel p = new SplitLayoutPanel();
+ p.add(new HTML("navigation"), Direction.WEST, 128);
+ p.add(new HTML("list"), Direction.NORTH, 384);
+ p.add(new HTML("details"), Direction.CENTER, 0);
+
+ // Note the explicit call to layout(). This is required for the layout to
+ // take effect.
+ p.layout();
+
+ // Attach the LayoutPanel to the RootLayoutPanel. The latter will listen for
+ // resize events on the window to ensure that its children are informed of
+ // possible size changes.
+ RootLayoutPanel rp = RootLayoutPanel.get();
+ rp.add(p);
+
+ // The RootLayoutPanel also requires that its layout() method be explicitly
+ // called for the initial layout to take effect.
+ rp.layout();
+ }
+}
diff --git a/user/javadoc/com/google/gwt/examples/StackLayoutPanelExample.java b/user/javadoc/com/google/gwt/examples/StackLayoutPanelExample.java
new file mode 100644
index 0000000..b493b01
--- /dev/null
+++ b/user/javadoc/com/google/gwt/examples/StackLayoutPanelExample.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.examples;
+
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.RootLayoutPanel;
+import com.google.gwt.user.client.ui.StackLayoutPanel;
+
+public class StackLayoutPanelExample implements EntryPoint {
+
+ public void onModuleLoad() {
+ // Create a three-item stack, with headers sized in EMs.
+ StackLayoutPanel p = new StackLayoutPanel(Unit.EM);
+ p.add(new HTML("this"), new HTML("[this]"), 128);
+ p.add(new HTML("that"), new HTML("[that]"), 384);
+ p.add(new HTML("the other"), new HTML("[the other]"), 0);
+
+ // Note the explicit call to layout(). This is required for the layout to
+ // take effect.
+ p.layout();
+
+ // Attach the LayoutPanel to the RootLayoutPanel. The latter will listen for
+ // resize events on the window to ensure that its children are informed of
+ // possible size changes.
+ RootLayoutPanel rp = RootLayoutPanel.get();
+ rp.add(p);
+
+ // The RootLayoutPanel also requires that its layout() method be explicitly
+ // called for the initial layout to take effect.
+ rp.layout();
+ }
+}
diff --git a/user/src/com/google/gwt/jsonp/client/JsonpRequest.java b/user/src/com/google/gwt/jsonp/client/JsonpRequest.java
index 43699ec..7d64af1 100644
--- a/user/src/com/google/gwt/jsonp/client/JsonpRequest.java
+++ b/user/src/com/google/gwt/jsonp/client/JsonpRequest.java
@@ -142,7 +142,6 @@
script.setType("text/javascript");
script.setId(callbackId);
script.setSrc(uri.toString());
- getHeadElement().appendChild(script);
timer = new Timer() {
@Override
public void run() {
@@ -150,6 +149,7 @@
}
};
timer.schedule(timeout);
+ getHeadElement().appendChild(script);
}
@SuppressWarnings("unused") // used by JSNI
diff --git a/user/src/com/google/gwt/resources/css/ExtractClassNamesVisitor.java b/user/src/com/google/gwt/resources/css/ExtractClassNamesVisitor.java
index c01464f..ca85293 100644
--- a/user/src/com/google/gwt/resources/css/ExtractClassNamesVisitor.java
+++ b/user/src/com/google/gwt/resources/css/ExtractClassNamesVisitor.java
@@ -1,12 +1,12 @@
/*
* Copyright 2009 Google Inc.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -29,6 +29,15 @@
* Collect all CSS class names in a stylesheet.
*/
public class ExtractClassNamesVisitor extends CssVisitor {
+ /**
+ * Extract all CSS class names in the provided stylesheet.
+ */
+ public static Set<String> exec(CssStylesheet sheet) {
+ ExtractClassNamesVisitor v = new ExtractClassNamesVisitor();
+ v.accept(sheet);
+ return v.found;
+ }
+
private final Set<String> found = new HashSet<String>();
/**
@@ -51,17 +60,8 @@
}
/**
- * Extract all CSS class names in the provided stylesheet.
- */
- public Set<String> exec(CssStylesheet sheet) {
- ExtractClassNamesVisitor v = new ExtractClassNamesVisitor();
- v.accept(sheet);
- return v.found;
- }
-
- /**
* Package-protected for testing.
- *
+ *
* @return
*/
Set<String> getFoundClasses() {
diff --git a/user/src/com/google/gwt/resources/rg/Counter.java b/user/src/com/google/gwt/resources/rg/Counter.java
new file mode 100644
index 0000000..6209605
--- /dev/null
+++ b/user/src/com/google/gwt/resources/rg/Counter.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.resources.rg;
+
+/**
+ * Simple serial number dispenser, handy for hanging off
+ * of caches.
+ */
+class Counter {
+ private int i;
+
+ int next() {
+ return i++;
+ }
+}
diff --git a/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java b/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java
index 307fb1c..8295ad6 100644
--- a/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java
+++ b/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java
@@ -1,12 +1,12 @@
/*
* Copyright 2008 Google Inc.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -331,6 +331,7 @@
}
}
+ @SuppressWarnings("serial")
static class JClassOrderComparator implements Comparator<JClassType>,
Serializable {
public int compare(JClassType o1, JClassType o2) {
@@ -1008,6 +1009,8 @@
private static final String KEY_BY_CLASS_AND_METHOD = "classAndMethod";
private static final String KEY_HAS_CACHED_DATA = "hasCachedData";
private static final String KEY_SHARED_METHODS = "sharedMethods";
+ private static final String KEY_CLASS_PREFIX = "prefix";
+ private static final String KEY_CLASS_COUNTER = "counter";
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
@@ -1083,7 +1086,7 @@
* Very large concatenation expressions using '+' cause the GWT compiler to
* overflow the stack due to deep AST nesting. The workaround for now is to
* force it to be more balanced using intermediate concatenation groupings.
- *
+ *
* This variable is used to track the number of subexpressions within the
* current parenthetical expression.
*/
@@ -1156,7 +1159,7 @@
/**
* Check if number of concat expressions currently exceeds limit and either
* append '+' if the limit isn't reached or ') + (' if it is.
- *
+ *
* @return numExpressions + 1 or 0 if limit was exceeded.
*/
private static int concatOp(int numExpressions, StringBuilder b) {
@@ -1239,7 +1242,7 @@
}
}
- private String classPrefix;
+ private Counter classCounter;
private JClassType cssResourceType;
private JClassType elementType;
private boolean enableMerge;
@@ -1323,6 +1326,7 @@
@Override
public void init(TreeLogger logger, ResourceContext context)
throws UnableToCompleteException {
+ String classPrefix;
try {
PropertyOracle propertyOracle = context.getGeneratorContext().getPropertyOracle();
ConfigurationProperty styleProp = propertyOracle.getConfigurationProperty("CssResource.style");
@@ -1336,17 +1340,10 @@
ConfigurationProperty classPrefixProp = propertyOracle.getConfigurationProperty("CssResource.obfuscationPrefix");
classPrefix = classPrefixProp.getValues().get(0);
} catch (BadPropertyValueException e) {
- logger.log(TreeLogger.WARN, "Unable to query module property", e);
+ logger.log(TreeLogger.ERROR, "Unable to query module property", e);
throw new UnableToCompleteException();
}
- if ("default".equals(classPrefix)) {
- // Compute it later in computeObfuscatedNames();
- classPrefix = null;
- } else if ("empty".equals(classPrefix)) {
- classPrefix = "";
- }
-
// Find all of the types that we care about in the type system
TypeOracle typeOracle = context.getGeneratorContext().getTypeOracle();
@@ -1361,7 +1358,7 @@
stylesheetMap = new IdentityHashMap<JMethod, CssStylesheet>();
- initReplacements(logger, context);
+ initReplacements(logger, context, classPrefix);
}
@Override
@@ -1388,17 +1385,19 @@
(new RequirementsCollector(logger, requirements)).accept(sheet);
}
- /**
- * Each distinct type of CssResource has a unique collection of values that it
- * will return, excepting for those methods that are defined within an
- * interface that is tagged with {@code @Shared}.
- */
- private void computeObfuscatedNames(TreeLogger logger) {
- logger = logger.branch(TreeLogger.DEBUG, "Computing CSS class replacements");
-
- SortedSet<JClassType> cssResourceSubtypes = computeOperableTypes(logger);
+ private String computeClassPrefix(String classPrefix,
+ SortedSet<JClassType> cssResourceSubtypes) {
+ if ("default".equals(classPrefix)) {
+ classPrefix = null;
+ } else if ("empty".equals(classPrefix)) {
+ classPrefix = "";
+ }
if (classPrefix == null) {
+ /*
+ * Note that the checksum will miss some or all of the subtypes generated
+ * by other generators.
+ */
Adler32 checksum = new Adler32();
for (JClassType type : cssResourceSubtypes) {
checksum.update(Util.getBytes(type.getQualifiedSourceName()));
@@ -1407,8 +1406,23 @@
+ Long.toString(checksum.getValue(), Character.MAX_RADIX);
}
- int count = 0;
+ return classPrefix;
+ }
+
+ /**
+ * Each distinct type of CssResource has a unique collection of values that it
+ * will return, excepting for those methods that are defined within an
+ * interface that is tagged with {@code @Shared}.
+ */
+ private void computeObfuscatedNames(TreeLogger logger, String classPrefix,
+ Set<JClassType> cssResourceSubtypes) {
+ logger = logger.branch(TreeLogger.DEBUG, "Computing CSS class replacements");
+
for (JClassType type : cssResourceSubtypes) {
+ if (replacementsByClassAndMethod.containsKey(type)) {
+ continue;
+ }
+
Map<JMethod, String> replacements = new IdentityHashMap<JMethod, String>();
replacementsByClassAndMethod.put(type, replacements);
@@ -1425,13 +1439,11 @@
name = classNameOverride.value();
}
- String obfuscatedClassName;
+ String obfuscatedClassName = classPrefix + makeIdent(classCounter.next());
if (prettyOutput) {
- obfuscatedClassName = classPrefix + "-"
+ obfuscatedClassName += "-"
+ type.getQualifiedSourceName().replaceAll("[.$]", "-") + "-"
+ name;
- } else {
- obfuscatedClassName = classPrefix + makeIdent(count++);
}
replacements.put(method, obfuscatedClassName);
@@ -1550,26 +1562,43 @@
* names.
*/
@SuppressWarnings("unchecked")
- private void initReplacements(TreeLogger logger, ResourceContext context) {
- if (context.getCachedData(KEY_HAS_CACHED_DATA, Boolean.class) == Boolean.TRUE) {
- replacementsByClassAndMethod = context.getCachedData(
- KEY_BY_CLASS_AND_METHOD, Map.class);
- replacementsForSharedMethods = context.getCachedData(KEY_SHARED_METHODS,
- Map.class);
- logger.log(TreeLogger.DEBUG, "Using cached replacements maps");
+ private void initReplacements(TreeLogger logger, ResourceContext context,
+ String classPrefix) {
+ /*
+ * This code was originally written to take a snapshot of all the
+ * CssResource descendants in the TypeOracle on its first run and calculate
+ * the obfuscated names in one go, to ensure that the same obfuscation would
+ * result regardless of the order in which the generators fired. (It no
+ * longer behaves that way, as that scheme prevented the generation of new
+ * CssResource interfaces, but the complexity lives on.)
+ *
+ * TODO(rjrjr,bobv) These days scottb tells us we're guaranteed that the
+ * recompiling the same code will fire the generators in a consistent order,
+ * so the old gymnastics aren't really justified anyway. It would probably
+ * be be worth the effort to simplify this.
+ */
- } else {
- context.putCachedData(KEY_HAS_CACHED_DATA, Boolean.TRUE);
+ SortedSet<JClassType> cssResourceSubtypes = computeOperableTypes(logger);
- replacementsByClassAndMethod = new IdentityHashMap<JClassType, Map<JMethod, String>>();
+ if (context.getCachedData(KEY_HAS_CACHED_DATA, Boolean.class) != Boolean.TRUE) {
+ context.putCachedData(KEY_CLASS_COUNTER, new Counter());
context.putCachedData(KEY_BY_CLASS_AND_METHOD,
- replacementsByClassAndMethod);
-
- replacementsForSharedMethods = new IdentityHashMap<JMethod, String>();
- context.putCachedData(KEY_SHARED_METHODS, replacementsForSharedMethods);
-
- computeObfuscatedNames(logger);
+ new IdentityHashMap<JClassType, Map<JMethod, String>>());
+ context.putCachedData(KEY_SHARED_METHODS,
+ new IdentityHashMap<JMethod, String>());
+ context.putCachedData(KEY_CLASS_PREFIX, computeClassPrefix(classPrefix,
+ cssResourceSubtypes));
+ context.putCachedData(KEY_HAS_CACHED_DATA, Boolean.TRUE);
}
+
+ classCounter = context.getCachedData(KEY_CLASS_COUNTER, Counter.class);
+ replacementsByClassAndMethod = context.getCachedData(
+ KEY_BY_CLASS_AND_METHOD, Map.class);
+ replacementsForSharedMethods = context.getCachedData(KEY_SHARED_METHODS,
+ Map.class);
+ classPrefix = context.getCachedData(KEY_CLASS_PREFIX, String.class);
+
+ computeObfuscatedNames(logger, classPrefix, cssResourceSubtypes);
}
private boolean isStrict(TreeLogger logger, ResourceContext context,
@@ -1628,7 +1657,7 @@
/**
* Create a Java expression that evaluates to the string representation of the
* stylesheet resource.
- *
+ *
* @param actualReplacements An out parameter that will be populated by the
* obfuscated class names that should be used for the particular
* instance of the CssResource, based on any substitution
diff --git a/user/src/com/google/gwt/uibinder/parsers/BeanParser.java b/user/src/com/google/gwt/uibinder/parsers/BeanParser.java
index 826edcc..ebb4fce 100644
--- a/user/src/com/google/gwt/uibinder/parsers/BeanParser.java
+++ b/user/src/com/google/gwt/uibinder/parsers/BeanParser.java
@@ -54,7 +54,8 @@
final Map<String, JParameter> unfilledRequiredParams =
new HashMap<String, JParameter>();
- final OwnerFieldClass ownerFieldClass = OwnerFieldClass.getFieldClass(type);
+ final OwnerFieldClass ownerFieldClass = OwnerFieldClass.getFieldClass(type,
+ writer.getLogger());
// See if there's a factory method
JAbstractMethod creator = writer.getOwnerClass().getUiFactoryMethod(type);
@@ -142,7 +143,7 @@
JParameter[] params = setter.getParameters();
AttributeParser parser = writer.getAttributeParser(attribute, params);
-
+
if (parser == null) {
writer.die("In %s, unable to parse %s.", elem, attribute);
}
diff --git a/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java b/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java
index 0596583..291368f 100644
--- a/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java
@@ -15,9 +15,7 @@
*/
package com.google.gwt.uibinder.rebind;
-import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JType;
@@ -38,12 +36,14 @@
private final Set<FieldWriter> needs = new HashSet<FieldWriter>();
private String initializer;
private boolean written;
+ private MortalLogger logger;
- public AbstractFieldWriter(String name) {
+ public AbstractFieldWriter(String name, MortalLogger logger) {
if (name == null) {
throw new RuntimeException("name cannot be null");
}
this.name = name;
+ this.logger = logger;
}
public void needs(FieldWriter f) {
@@ -69,7 +69,7 @@
this.initializer = initializer;
}
- public void write(IndentedWriter w, TreeLogger logger)
+ public void write(IndentedWriter w)
throws UnableToCompleteException {
if (written) {
return;
@@ -80,7 +80,7 @@
// we support more interesting contexts (e.g. the same need being used
// inside two different
// LazyPanels)
- f.write(w, logger);
+ f.write(w);
}
if (initializer == null) {
@@ -88,9 +88,8 @@
if (type != null) {
if ((type.isInterface() == null)
&& (type.findConstructor(new JType[0]) == null)) {
- logger.log(Type.ERROR, String.format(NO_DEFAULT_CTOR_ERROR,
- type.getQualifiedSourceName(), type.getName()));
- throw new UnableToCompleteException();
+ logger.die(NO_DEFAULT_CTOR_ERROR,
+ type.getQualifiedSourceName(), type.getName());
}
}
}
diff --git a/user/src/com/google/gwt/uibinder/rebind/BundleWriter.java b/user/src/com/google/gwt/uibinder/rebind/BundleWriter.java
index a3a6922..5187e6a 100644
--- a/user/src/com/google/gwt/uibinder/rebind/BundleWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/BundleWriter.java
@@ -15,31 +15,47 @@
*/
package com.google.gwt.uibinder.rebind;
+import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource.Strict;
-import com.google.gwt.uibinder.rebind.model.ImplicitBundle;
-import com.google.gwt.uibinder.rebind.model.CssResourceGetter;
-
-import java.io.PrintWriter;
+import com.google.gwt.uibinder.rebind.model.ImplicitClientBundle;
+import com.google.gwt.uibinder.rebind.model.ImplicitCssResource;
/**
- * Writes source implementing a {@link ImplicitBundle} to a {@link PrintWriter}.
+ * Writes source implementing an {@link ImplicitClientBundle}.
*/
public class BundleWriter {
- private final ImplicitBundle bundleClass;
+ private final ImplicitClientBundle bundleClass;
private final IndentedWriter writer;
+ private final PrintWriterManager writerManager;
private final TypeOracle oracle;
+ private final String clientBundleType;
+ private final String strictAnnotationType;
- public BundleWriter(ImplicitBundle bundleClass, PrintWriter printWriter,
- TypeOracle oracle) {
+ public BundleWriter(ImplicitClientBundle bundleClass,
+ PrintWriterManager writerManager, TypeOracle oracle,
+ PrintWriterManager printWriterProvider) {
this.bundleClass = bundleClass;
- this.writer = new IndentedWriter(printWriter);
+ this.writer = new IndentedWriter(
+ writerManager.makePrintWriterFor(bundleClass.getClassName()));
+ this.writerManager = writerManager;
this.oracle = oracle;
+
+ clientBundleType = oracle.findType(ClientBundle.class.getName()).getQualifiedSourceName();
+ strictAnnotationType = oracle.findType(Strict.class.getCanonicalName()).getQualifiedSourceName();
}
- public void write() {
+ public void write() throws UnableToCompleteException {
+ writeBundleClass();
+ for (ImplicitCssResource css : bundleClass.getCssMethods()) {
+ new CssResourceWriter(css, oracle,
+ writerManager.makePrintWriterFor(css.getClassName())).write();
+ }
+ }
+
+ private void writeBundleClass() {
// Package declaration
String packageName = bundleClass.getPackageName();
if (packageName.length() > 0) {
@@ -49,9 +65,8 @@
// Imports
writer.write("import %s;",
- oracle.findType(ClientBundle.class.getName()).getQualifiedSourceName());
- writer.write("import %s;",
- oracle.findType(Strict.class.getCanonicalName()).getQualifiedSourceName());
+ clientBundleType);
+ writer.write("import %s;", strictAnnotationType);
writer.newline();
// Open interface
@@ -60,9 +75,9 @@
writer.indent();
// Write css methods
- for (CssResourceGetter css : bundleClass.getCssMethods()) {
- writer.write("@Strict @Source(\"%s\")",css.getSource());
- writer.write("%s %s();", css.getExtendedInterface().getQualifiedSourceName(), css.getName());
+ for (ImplicitCssResource css : bundleClass.getCssMethods()) {
+ writer.write("@Strict @Source(\"%s\")", css.getSource());
+ writer.write("%s %s();", css.getClassName(), css.getName());
writer.newline();
}
diff --git a/user/src/com/google/gwt/uibinder/rebind/CssResourceWriter.java b/user/src/com/google/gwt/uibinder/rebind/CssResourceWriter.java
new file mode 100644
index 0000000..72d0f0e
--- /dev/null
+++ b/user/src/com/google/gwt/uibinder/rebind/CssResourceWriter.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.uibinder.rebind;
+
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.resources.client.CssResource;
+import com.google.gwt.uibinder.rebind.model.ImplicitCssResource;
+
+import java.io.PrintWriter;
+import java.util.Set;
+
+/**
+ * Writes the source to implement an {@link ImplicitCssResource} interface.
+ */
+public class CssResourceWriter {
+ /**
+ *
+ */
+ private static final JType[] NO_PARAMS = new JType[0];
+ private final ImplicitCssResource css;
+ private final IndentedWriter writer;
+ private final JClassType cssResourceType;
+ private final JClassType stringType;
+
+ public CssResourceWriter(ImplicitCssResource css, TypeOracle oracle,
+ PrintWriter writer) {
+ this.css = css;
+ this.writer = new IndentedWriter(writer);
+ this.cssResourceType = oracle.findType(CssResource.class.getName());
+ this.stringType = oracle.findType(String.class.getName());
+ }
+
+ public void write() throws UnableToCompleteException {
+ // Package declaration
+ String packageName = css.getPackageName();
+ if (packageName.length() > 0) {
+ writer.write("package %1$s;", packageName);
+ writer.newline();
+ }
+
+ JClassType superType = css.getExtendedInterface();
+ if (superType == null) {
+ superType = cssResourceType;
+ }
+
+ writer.write("import %s;", superType.getQualifiedSourceName());
+ writer.newline();
+
+ // Open interface
+ writer.write("public interface %s extends %s {", css.getClassName(),
+ superType.getSimpleSourceName());
+ writer.indent();
+
+ writeCssMethods(superType);
+
+ // Close interface.
+ writer.outdent();
+ writer.write("}");
+ }
+
+ private void writeCssMethods(JClassType superType) throws UnableToCompleteException {
+ Set<String> classNames = css.getCssClassNames();
+
+ /*
+ * Only write names that we are not overriding from super, or else we'll
+ * re-obfuscate any @Shared ones
+ */
+ for (String className : classNames) {
+ JMethod method = superType.findMethod(className, NO_PARAMS);
+ if (method == null || !stringType.equals(method.getReturnType())) {
+ writer.write("String %s();", className);
+ }
+ }
+ }
+}
diff --git a/user/src/com/google/gwt/uibinder/rebind/FieldManager.java b/user/src/com/google/gwt/uibinder/rebind/FieldManager.java
index 0624bc5..67418ea 100644
--- a/user/src/com/google/gwt/uibinder/rebind/FieldManager.java
+++ b/user/src/com/google/gwt/uibinder/rebind/FieldManager.java
@@ -15,7 +15,6 @@
*/
package com.google.gwt.uibinder.rebind;
-import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
@@ -42,13 +41,13 @@
*/
private final LinkedList<FieldWriter> parsedFieldStack = new LinkedList<FieldWriter>();
- private TreeLogger logger;
+ private MortalLogger logger;
/**
* Basic constructor just injects an oracle instance.
*/
- public FieldManager(TreeLogger logger) {
- this.logger = logger;
+ public FieldManager(MortalLogger logger2) {
+ this.logger = logger2;
}
/**
@@ -116,7 +115,7 @@
public FieldWriter registerFieldOfGeneratedType(String typePackage,
String typeName, String fieldName) throws UnableToCompleteException {
FieldWriter field = new FieldWriterOfGeneratedType(typePackage, typeName,
- fieldName);
+ fieldName, logger);
return registerField(fieldName, field);
}
@@ -130,7 +129,7 @@
String ownerTypeName) throws UnableToCompleteException {
Collection<FieldWriter> fields = fieldsMap.values();
for (FieldWriter field : fields) {
- field.write(writer, logger);
+ field.write(writer);
}
}
@@ -148,9 +147,7 @@
private void requireUnique(String fieldName) throws UnableToCompleteException {
if (fieldsMap.containsKey(fieldName)) {
- Object[] params = {fieldName};
- logger.log(TreeLogger.ERROR, String.format(DUPLICATE_FIELD_ERROR, params));
- throw new UnableToCompleteException();
+ logger.die(DUPLICATE_FIELD_ERROR, fieldName);
}
}
}
diff --git a/user/src/com/google/gwt/uibinder/rebind/FieldWriter.java b/user/src/com/google/gwt/uibinder/rebind/FieldWriter.java
index 6daf715..a4c2beb 100644
--- a/user/src/com/google/gwt/uibinder/rebind/FieldWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/FieldWriter.java
@@ -15,7 +15,6 @@
*/
package com.google.gwt.uibinder.rebind;
-import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
@@ -73,9 +72,7 @@
/**
* Write the field delcaration.
- *
- * @param logger
*/
- void write(IndentedWriter w, TreeLogger logger)
+ void write(IndentedWriter w)
throws UnableToCompleteException;
}
\ No newline at end of file
diff --git a/user/src/com/google/gwt/uibinder/rebind/FieldWriterOfExistingType.java b/user/src/com/google/gwt/uibinder/rebind/FieldWriterOfExistingType.java
index 3c32b9b..035b01c 100644
--- a/user/src/com/google/gwt/uibinder/rebind/FieldWriterOfExistingType.java
+++ b/user/src/com/google/gwt/uibinder/rebind/FieldWriterOfExistingType.java
@@ -15,7 +15,6 @@
*/
package com.google.gwt.uibinder.rebind;
-import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.typeinfo.JClassType;
/**
@@ -24,10 +23,10 @@
*/
class FieldWriterOfExistingType extends AbstractFieldWriter {
final JClassType type;
- final TreeLogger logger;
+ final MortalLogger logger;
- FieldWriterOfExistingType(JClassType type, String name, TreeLogger logger) {
- super(name);
+ FieldWriterOfExistingType(JClassType type, String name, MortalLogger logger) {
+ super(name, logger);
this.logger = logger;
if (type == null) {
throw new RuntimeException("type cannot be null");
diff --git a/user/src/com/google/gwt/uibinder/rebind/FieldWriterOfGeneratedType.java b/user/src/com/google/gwt/uibinder/rebind/FieldWriterOfGeneratedType.java
index ddefbfb..4204f77 100644
--- a/user/src/com/google/gwt/uibinder/rebind/FieldWriterOfGeneratedType.java
+++ b/user/src/com/google/gwt/uibinder/rebind/FieldWriterOfGeneratedType.java
@@ -27,11 +27,8 @@
private final String typePackage;
private final String typeName;
- /**
- * @param name
- */
- public FieldWriterOfGeneratedType(String typePackage, String typeName, String name) {
- super(name);
+ public FieldWriterOfGeneratedType(String typePackage, String typeName, String name, MortalLogger logger) {
+ super(name, logger);
if (typeName == null) {
throw new RuntimeException("typeName must not be null");
}
diff --git a/user/src/com/google/gwt/uibinder/rebind/HandlerEvaluator.java b/user/src/com/google/gwt/uibinder/rebind/HandlerEvaluator.java
index 6c10827..bdea59a 100644
--- a/user/src/com/google/gwt/uibinder/rebind/HandlerEvaluator.java
+++ b/user/src/com/google/gwt/uibinder/rebind/HandlerEvaluator.java
@@ -15,7 +15,6 @@
*/
package com.google.gwt.uibinder.rebind;
-import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
@@ -33,7 +32,9 @@
* annotated with {@link com.google.gwt.uibinder.client.UiHandler} so that the
* user doesn't need to worry about writing code to implement these bindings.
*
- * <p>For instance, the class defined below:
+ * <p>
+ * For instance, the class defined below:
+ *
* <pre>
* public class MyClass {
* @UiField Label label;
@@ -46,6 +47,7 @@
* </pre>
*
* will generate a piece of code like:
+ *
* <pre>
* ClickHandler handler0 = new ClickHandler() {
* @Override
@@ -63,12 +65,10 @@
*/
class HandlerEvaluator {
- private static final String HANDLER_BASE_NAME =
- "handlerMethodWithNameVeryUnlikelyToCollideWithUserFieldNames";
+ private static final String HANDLER_BASE_NAME = "handlerMethodWithNameVeryUnlikelyToCollideWithUserFieldNames";
/*
* TODO(rjrjr) The correct fix is to put the handlers in a locally defined
- * class, making the generated code look like this:
- *
+ * class, making the generated code look like this
*
* http://docs.google.com/Doc?docid=0AQfnKgX9tAdgZGZ2cTM5YjdfMmQ4OTk0eGhz&hl=en
*
@@ -77,7 +77,7 @@
*/
private int varCounter = 0;
- private final TreeLogger logger;
+ private final MortalLogger logger;
private final JClassType handlerRegistrationJClass;
private final JClassType eventHandlerJClass;
@@ -90,7 +90,7 @@
* @param logger the logger for warnings and errors
* @param oracle the type oracle
*/
- HandlerEvaluator(OwnerClass ownerClass, TreeLogger logger, TypeOracle oracle) {
+ HandlerEvaluator(OwnerClass ownerClass, MortalLogger logger, TypeOracle oracle) {
this.ownerClass = ownerClass;
this.logger = logger;
@@ -99,17 +99,6 @@
}
/**
- * Post an error message and throws an {@link UnableToCompleteException}.
- *
- * @param message the error message
- * @param params the format params
- */
- public void die(String message, Object... params) throws UnableToCompleteException {
- logger.log(TreeLogger.ERROR, String.format(message, params));
- throw new UnableToCompleteException();
- }
-
- /**
* Runs the evaluator in the given class according to the valid fields
* extracted from the template (via attribute ui:field).
*
@@ -125,24 +114,27 @@
// Evaluate the method.
String boundMethod = method.getName();
if (method.isPrivate()) {
- die("Method '%s' cannot be private.", boundMethod);
+ logger.die("Method '%s' cannot be private.", boundMethod);
}
// Retrieves both event and handler types.
JParameter[] parameters = method.getParameters();
if (parameters.length != 1) {
- die("Method '%s' must have a single event parameter defined.", boundMethod);
+ logger.die("Method '%s' must have a single event parameter defined.",
+ boundMethod);
}
JClassType eventType = parameters[0].getType().isClass();
JClassType handlerType = getHandlerForEvent(eventType);
if (handlerType == null) {
- die("Parameter '%s' is not an event (subclass of GwtEvent).", eventType.getName());
+ logger.die("Parameter '%s' is not an event (subclass of GwtEvent).",
+ eventType.getName());
}
// Cool to add the handler in the output.
String handlerVarName = HANDLER_BASE_NAME + (++varCounter);
- writeHandler(writer, uiOwner, handlerVarName, handlerType, eventType, boundMethod);
+ writeHandler(writer, uiOwner, handlerVarName, handlerType, eventType,
+ boundMethod);
// Adds the handler created above.
UiHandler annotation = method.getAnnotation(UiHandler.class);
@@ -150,36 +142,27 @@
// Is the field object valid?
FieldWriter fieldWriter = fieldManager.lookup(objectName);
if (fieldWriter == null) {
- die("Method '%s' can not be bound. You probably missed ui:field='%s' "
- + "in the template.", boundMethod, objectName);
+ logger.die(
+ ("Method '%s' can not be bound. You probably missed ui:field='%s' "
+ + "in the template."), boundMethod, objectName);
}
// Retrieves the "add handler" method in the object.
JMethod addHandlerMethodType = getAddHandlerMethodForObject(
fieldWriter.getType(), handlerType);
if (addHandlerMethodType == null) {
- die("Field '%s' does not have an 'add%s' method associated.",
+ logger.die("Field '%s' does not have an 'add%s' method associated.",
objectName, handlerType.getName());
}
// Cool to tie the handler into the object.
- writeAddHandler(writer, handlerVarName,
- addHandlerMethodType.getName(), objectName);
+ writeAddHandler(writer, handlerVarName, addHandlerMethodType.getName(),
+ objectName);
}
}
}
/**
- * Post a warning message.
- *
- * @param message the error message
- * @param params the format params
- */
- public void warn(String message, Object... params) {
- logger.log(TreeLogger.WARN, String.format(message, params));
- }
-
- /**
* Writes a handler entry using the given writer.
*
* @param writer the writer used to output the results
@@ -189,30 +172,32 @@
* @param eventType the event associated with the handler
* @param boundMethod the method bound in the handler
*/
- protected void writeHandler(IndentedWriter writer, String uiOwner, String handlerVarName,
- JClassType handlerType, JClassType eventType, String boundMethod)
- throws UnableToCompleteException {
+ protected void writeHandler(IndentedWriter writer, String uiOwner,
+ String handlerVarName, JClassType handlerType, JClassType eventType,
+ String boundMethod) throws UnableToCompleteException {
// Retrieves the single method (usually 'onSomething') related to all
// handlers. Ex: onClick in ClickHandler, onBlur in BlurHandler ...
JMethod[] methods = handlerType.getMethods();
if (methods.length != 1) {
- die("'%s' has more than one method defined.", handlerType.getName());
+ logger.die("'%s' has more than one method defined.",
+ handlerType.getName());
}
// Checks if the method has an Event as parameter. Ex: ClickEvent in
// onClick, BlurEvent in onBlur ...
JParameter[] parameters = methods[0].getParameters();
if (parameters.length != 1 || parameters[0].getType() != eventType) {
- die("Method '%s' needs '%s' as parameter", methods[0].getName(), eventType.getName());
+ logger.die("Method '%s' needs '%s' as parameter", methods[0].getName(),
+ eventType.getName());
}
writer.newline();
writer.write("final %1$s %2$s = new %1$s() {",
handlerType.getParameterizedQualifiedSourceName(), handlerVarName);
writer.indent();
- writer.write("public void %1$s(%2$s event) {",
- methods[0].getName(), eventType.getParameterizedQualifiedSourceName());
+ writer.write("public void %1$s(%2$s event) {", methods[0].getName(),
+ eventType.getParameterizedQualifiedSourceName());
writer.indent();
writer.write("%1$s.%2$s(event);", uiOwner, boundMethod);
writer.outdent();
@@ -226,14 +211,14 @@
*
* @param writer the writer used to output the results
* @param handlerVarName the name of the handler variable
- * @param addHandlerMethodName the "add handler" method name associated
- * with the object
+ * @param addHandlerMethodName the "add handler" method name associated with
+ * the object
* @param objectName the name of the object we want to tie the handler
*/
void writeAddHandler(IndentedWriter writer, String handlerVarName,
String addHandlerMethodName, String objectName) {
- writer.write("%1$s.%2$s(%3$s);", objectName,
- addHandlerMethodName, handlerVarName);
+ writer.write("%1$s.%2$s(%3$s);", objectName, addHandlerMethodName,
+ handlerVarName);
}
/**
@@ -242,8 +227,10 @@
* {@link com.google.gwt.event.shared.HandlerRegistration} and receives a
* single input parameter of the same type of handlerType.
*
- * <p>Output an error in case more than one method match the conditions
- * described above.</p>
+ * <p>
+ * Output an error in case more than one method match the conditions described
+ * above.
+ * </p>
*
* <pre>
* <b>Examples:</b>
@@ -256,11 +243,11 @@
* @param objectType the object type we want to check
* @param handlerType the handler type we want to check in the object
*
- * @return the method that adds handlerType into objectType, or <b>null</b>
- * if no method was found
+ * @return the method that adds handlerType into objectType, or <b>null</b> if
+ * no method was found
*/
private JMethod getAddHandlerMethodForObject(JClassType objectType,
- JClassType handlerType) throws UnableToCompleteException {
+ JClassType handlerType) throws UnableToCompleteException {
JMethod handlerMethod = null;
for (JMethod method : objectType.getOverridableMethods()) {
@@ -269,12 +256,14 @@
// Condition 2: single parameter of the same type of handlerType?
JParameter[] parameters = method.getParameters();
- if ((parameters.length == 1) && handlerType.equals(parameters[0].getType())) {
+ if ((parameters.length == 1)
+ && handlerType.equals(parameters[0].getType())) {
// Condition 3: does more than one method match the condition?
if (handlerMethod != null) {
- die("This handler cannot be generated. Methods '%s' and '%s' are "
- + "ambiguous. Which one to pick?", method, handlerMethod);
+ logger.die(
+ ("This handler cannot be generated. Methods '%s' and '%s' are "
+ + "ambiguous. Which one to pick?"), method, handlerMethod);
}
handlerMethod = method;
@@ -295,11 +284,11 @@
// All handlers event must have an overrided method getAssociatedType().
// We take advantage of this information to get the associated handler.
// Ex:
- // com.google.gwt.event.dom.client.ClickEvent
- // ---> com.google.gwt.event.dom.client.ClickHandler
+ // com.google.gwt.event.dom.client.ClickEvent
+ // ---> com.google.gwt.event.dom.client.ClickHandler
//
- // com.google.gwt.event.dom.client.BlurEvent
- // ---> com.google.gwt.event.dom.client.BlurHandler
+ // com.google.gwt.event.dom.client.BlurEvent
+ // ---> com.google.gwt.event.dom.client.BlurHandler
if (eventType == null) {
return null;
@@ -307,29 +296,34 @@
JMethod method = eventType.findMethod("getAssociatedType", new JType[0]);
if (method == null) {
- warn("Method 'getAssociatedType()' could not be found in the event '%s'.",
+ logger.warn(
+ "Method 'getAssociatedType()' could not be found in the event '%s'.",
eventType.getName());
return null;
}
JType returnType = method.getReturnType();
if (returnType == null) {
- warn("The method 'getAssociatedType()' in the event '%s' returns void.",
+ logger.warn(
+ "The method 'getAssociatedType()' in the event '%s' returns void.",
eventType.getName());
return null;
}
JParameterizedType isParameterized = returnType.isParameterized();
if (isParameterized == null) {
- warn("The method 'getAssociatedType()' in '%s' does not return Type<? extends EventHandler>.",
- eventType.getName());
+ logger.warn(
+ "The method 'getAssociatedType()' in '%s' does not return Type<? extends EventHandler>.",
+ eventType.getName());
return null;
}
JClassType[] argTypes = isParameterized.getTypeArgs();
- if ((argTypes.length != 1) && !argTypes[0].isAssignableTo(eventHandlerJClass)) {
- warn("The method 'getAssociatedType()' in '%s' does not return Type<? extends EventHandler>.",
- eventType.getName());
+ if ((argTypes.length != 1)
+ && !argTypes[0].isAssignableTo(eventHandlerJClass)) {
+ logger.warn(
+ "The method 'getAssociatedType()' in '%s' does not return Type<? extends EventHandler>.",
+ eventType.getName());
return null;
}
diff --git a/user/src/com/google/gwt/uibinder/rebind/MortalLogger.java b/user/src/com/google/gwt/uibinder/rebind/MortalLogger.java
new file mode 100644
index 0000000..80964ad
--- /dev/null
+++ b/user/src/com/google/gwt/uibinder/rebind/MortalLogger.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.uibinder.rebind;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+
+/**
+ * Wraps a {@link TreeLogger} with handy {@link String#format} style methods and
+ * can be told to die. Perhaps we should instead add die(), warn(), etc. to
+ * Treelogger.
+ */
+public class MortalLogger {
+ private final TreeLogger logger;
+
+ MortalLogger(TreeLogger logger) {
+ this.logger = logger;
+ }
+
+ /**
+ * Post an error message and halt processing. This method always throws an
+ * {@link UnableToCompleteException}.
+ */
+ public void die(String message, Object... params)
+ throws UnableToCompleteException {
+ logger.log(TreeLogger.ERROR, String.format(message, params));
+ throw new UnableToCompleteException();
+ }
+
+ public TreeLogger getTreeLogger() {
+ return logger;
+ }
+
+ /**
+ * Post a warning message.
+ */
+ public void warn(String message, Object... params) {
+ logger.log(TreeLogger.WARN, String.format(message, params));
+ }
+}
diff --git a/user/src/com/google/gwt/uibinder/rebind/PrintWriterManager.java b/user/src/com/google/gwt/uibinder/rebind/PrintWriterManager.java
new file mode 100644
index 0000000..1b79e6d
--- /dev/null
+++ b/user/src/com/google/gwt/uibinder/rebind/PrintWriterManager.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.uibinder.rebind;
+
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Factory for printwriters creating source files in a particular package.
+ */
+class PrintWriterManager {
+ private final GeneratorContext genCtx;
+ private final String packageName;
+ private final TreeLogger logger;
+ private final Set<PrintWriter> writers = new HashSet<PrintWriter>();
+
+ PrintWriterManager(GeneratorContext genCtx, TreeLogger logger,
+ String packageName) {
+ this.genCtx = genCtx;
+ this.packageName = packageName;
+ this.logger = logger;
+ }
+
+ /**
+ * Commit all writers we have vended.
+ */
+ void commit() {
+ for (PrintWriter writer : writers) {
+ genCtx.commit(logger, writer);
+ }
+ }
+
+ /**
+ * @param name classname
+ * @return the printwriter
+ * @throws RuntimeException if this class has already been written
+ */
+ PrintWriter makePrintWriterFor(String name) {
+ PrintWriter writer = tryToMakePrintWriterFor(name);
+ if (writer == null) {
+ throw new RuntimeException(String.format("Tried to write %s.%s twice.",
+ packageName, name));
+ }
+ return writer;
+ }
+
+ /**
+ * @param name classname
+ * @return the printwriter, or null if this class has already been written
+ */
+ PrintWriter tryToMakePrintWriterFor(String name) {
+ PrintWriter writer = genCtx.tryCreate(logger, packageName, name);
+ if (writer != null) {
+ writers.add(writer);
+ }
+ return writer;
+ }
+}
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java
index 59cfa4c..7f4045b 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java
@@ -24,22 +24,9 @@
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.uibinder.client.UiTemplate;
import com.google.gwt.uibinder.rebind.messages.MessagesWriter;
-import com.google.gwt.uibinder.rebind.model.ImplicitBundle;
+import com.google.gwt.uibinder.rebind.model.ImplicitClientBundle;
-import org.w3c.dom.Document;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
-
-import java.io.IOException;
-import java.io.InputStream;
import java.io.PrintWriter;
-import java.net.URL;
-import java.util.Enumeration;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
/**
* Generator for implementations of
@@ -49,77 +36,12 @@
private static final String TEMPLATE_SUFFIX = ".ui.xml";
- private static void commit(TreeLogger logger, GeneratorContext genCtx,
- String packageName, PrintWriter pw, UiBinderWriter uiWriter) {
- uiWriter.write(pw);
- genCtx.commit(logger, pw);
-
- MessagesWriter messages = uiWriter.getMessages();
- if (messages.hasMessages()) {
- PrintWriter messagesPrintWriter = genCtx.tryCreate(logger, packageName,
- messages.getMessagesClassName());
- if (messagesPrintWriter == null) {
- throw new RuntimeException("Tried to gen messages twice.");
- }
-
- messages.write(messagesPrintWriter);
- genCtx.commit(logger, messagesPrintWriter);
- }
-
- ImplicitBundle bundleClass = uiWriter.getBundleClass();
- PrintWriter bundlePrintWriter = genCtx.tryCreate(logger, packageName,
- bundleClass.getClassName());
- if (bundlePrintWriter == null) {
- throw new RuntimeException("Tried to gen bundle twice.");
- }
-
- new BundleWriter(bundleClass, bundlePrintWriter, genCtx.getTypeOracle()).write();
- genCtx.commit(logger, bundlePrintWriter);
- }
-
- @Override
- public String generate(TreeLogger logger, GeneratorContext genCtx,
- String fqInterfaceName) throws UnableToCompleteException {
- TypeOracle oracle = genCtx.getTypeOracle();
- JClassType interfaceType;
- try {
- interfaceType = oracle.getType(fqInterfaceName);
- } catch (NotFoundException e) {
- throw new RuntimeException(e);
- }
-
- String implName = interfaceType.getName();
- implName = implName.replace('.', '_') + "Impl";
-
- String packageName = interfaceType.getPackage().getName();
- PrintWriter printWriter = genCtx.tryCreate(logger, packageName, implName);
-
- if (printWriter != null) {
- String templateName = deduceTemplateFile(logger, interfaceType);
-
- Document document;
- try {
- document = parseXmlResource(logger, templateName);
- } catch (SAXParseException e) {
- logger.log(TreeLogger.ERROR, "Error parsing XML (line "
- + e.getLineNumber() + "): " + e.getMessage(), e);
- throw new UnableToCompleteException();
- }
-
- UiBinderWriter uiBinderWriter = new UiBinderWriter(interfaceType,
- implName, templateName, oracle, logger);
- uiBinderWriter.parseDocument(document);
- commit(logger, genCtx, packageName, printWriter, uiBinderWriter);
- }
- return packageName + "." + implName;
- }
-
/**
* Given a UiBinder interface, return the path to its ui.xml file, suitable
* for any classloader to find it as a resource.
*/
- private String deduceTemplateFile(TreeLogger logger, JClassType interfaceType)
- throws UnableToCompleteException {
+ private static String deduceTemplateFile(MortalLogger logger,
+ JClassType interfaceType) throws UnableToCompleteException {
String templateName = null;
UiTemplate annotation = interfaceType.getAnnotation(UiTemplate.class);
if (annotation == null) {
@@ -132,9 +54,8 @@
} else {
templateName = annotation.value();
if (!templateName.endsWith(TEMPLATE_SUFFIX)) {
- logger.log(TreeLogger.ERROR, "Template file name must end with "
+ logger.die("Template file name must end with "
+ TEMPLATE_SUFFIX);
- throw new UnableToCompleteException();
}
/*
@@ -153,48 +74,54 @@
return templateName;
}
- private Document parseXmlResource(TreeLogger logger, final String resourcePath)
- throws SAXParseException, UnableToCompleteException {
- // Get the document builder. We need namespaces, and automatic expanding
- // of entity references (the latter of which makes life somewhat easier
- // for XMLElement).
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- factory.setNamespaceAware(true);
- factory.setExpandEntityReferences(true);
- DocumentBuilder builder;
- try {
- builder = factory.newDocumentBuilder();
- } catch (ParserConfigurationException e) {
- throw new RuntimeException(e);
- }
-
- try {
- ClassLoader classLoader = UiBinderGenerator.class.getClassLoader();
- Enumeration<URL> urls = classLoader.getResources(resourcePath);
- if (!urls.hasMoreElements()) {
- logger.log(TreeLogger.ERROR, "Unable to find resource: " + resourcePath);
- throw new UnableToCompleteException();
- }
- URL url = urls.nextElement();
-
- InputStream stream = url.openStream();
- InputSource input = new InputSource(stream);
- input.setSystemId(url.toExternalForm());
-
- builder.setEntityResolver(new GwtResourceEntityResolver());
-
- return builder.parse(input);
- } catch (SAXParseException e) {
- // Let SAXParseExceptions through.
- throw e;
- } catch (SAXException e) {
- throw new RuntimeException(e);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
+ private static String slashify(String s) {
+ return s.replace(".", "/");
}
- private String slashify(String s) {
- return s.replace(".", "/");
+ @Override
+ public String generate(TreeLogger logger, GeneratorContext genCtx,
+ String fqInterfaceName) throws UnableToCompleteException {
+ TypeOracle oracle = genCtx.getTypeOracle();
+ JClassType interfaceType;
+ try {
+ interfaceType = oracle.getType(fqInterfaceName);
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ String implName = interfaceType.getName().replace('.', '_') + "Impl";
+ String packageName = interfaceType.getPackage().getName();
+ PrintWriterManager writers = new PrintWriterManager(genCtx, logger,
+ packageName);
+ PrintWriter printWriter = writers.tryToMakePrintWriterFor(implName);
+
+ if (printWriter != null) {
+ generateOnce(interfaceType, implName, packageName, printWriter, logger,
+ oracle, writers);
+ }
+ return packageName + "." + implName;
+ }
+
+ private void generateOnce(JClassType interfaceType, String implName,
+ String packageName, PrintWriter binderPrintWrier, TreeLogger treeLogger,
+ TypeOracle oracle, PrintWriterManager writerManager)
+ throws UnableToCompleteException {
+
+ MortalLogger logger = new MortalLogger(treeLogger);
+ String templatePath = deduceTemplateFile(logger, interfaceType);
+
+ UiBinderWriter uiBinderWriter = new UiBinderWriter(interfaceType, implName,
+ templatePath, oracle, logger);
+ uiBinderWriter.parseDocument(binderPrintWrier);
+
+ MessagesWriter messages = uiBinderWriter.getMessages();
+ if (messages.hasMessages()) {
+ messages.write(writerManager.makePrintWriterFor(messages.getMessagesClassName()));
+ }
+
+ ImplicitClientBundle bundleClass = uiBinderWriter.getBundleClass();
+ new BundleWriter(bundleClass, writerManager, oracle, writerManager).write();
+
+ writerManager.commit();
}
}
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java
index 100fd0b..f26d9ca 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java
@@ -20,9 +20,10 @@
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.resources.client.CssResource;
+import com.google.gwt.uibinder.parsers.NullInterpreter;
import com.google.gwt.uibinder.rebind.messages.MessagesWriter;
-import com.google.gwt.uibinder.rebind.model.CssResourceGetter;
-import com.google.gwt.uibinder.rebind.model.ImplicitBundle;
+import com.google.gwt.uibinder.rebind.model.ImplicitClientBundle;
+import com.google.gwt.uibinder.rebind.model.ImplicitCssResource;
import com.google.gwt.uibinder.rebind.model.OwnerField;
/**
@@ -38,16 +39,19 @@
private final TypeOracle oracle;
private final MessagesWriter messagesWriter;
private final FieldManager fieldManager;
- private final ImplicitBundle bundleClass;
+ private final ImplicitClientBundle bundleClass;
+ private final JClassType cssResourceType;
public UiBinderParser(UiBinderWriter writer, MessagesWriter messagesWriter,
- FieldManager fieldManager, TypeOracle oracle, ImplicitBundle bundleClass) {
+ FieldManager fieldManager, TypeOracle oracle,
+ ImplicitClientBundle bundleClass) {
this.writer = writer;
this.oracle = oracle;
this.messagesWriter = messagesWriter;
this.fieldManager = fieldManager;
this.bundleClass = bundleClass;
- }
+ this.cssResourceType = oracle.findType(CssResource.class.getCanonicalName());
+ }
/**
* Parses the root UiBinder element, and kicks off the parsing of the rest of
@@ -55,7 +59,7 @@
*/
public String parse(XMLElement elem) throws UnableToCompleteException {
// TODO(rjrjr) Clearly need to break these find* methods out into their own
- // parsers, need registration scheme for ui binder's own parsers
+ // parsers, an so need a registration scheme for uibinder-specific parsers
findStyles(elem);
findResources(elem);
messagesWriter.findMessagesConfig(elem);
@@ -65,10 +69,19 @@
private JClassType consumeCssResourceType(XMLElement elem)
throws UnableToCompleteException {
- JClassType publicType = consumeTypeAttribute(elem);
- JClassType cssResourceType = oracle.findType(CssResource.class.getCanonicalName());
+ String typeName = elem.consumeAttribute("type", null);
+ if (typeName == null) {
+ return cssResourceType;
+ }
+
+ JClassType publicType = oracle.findType(typeName);
+ if (publicType == null) {
+ writer.die("In %s, no such type %s", elem, typeName);
+ }
+
if (!publicType.isAssignableTo(cssResourceType)) {
- writer.die("In %s, type %s does not extend %s", elem, publicType.getQualifiedSourceName(),
+ writer.die("In %s, type %s does not extend %s", elem,
+ publicType.getQualifiedSourceName(),
cssResourceType.getQualifiedSourceName());
}
return publicType;
@@ -129,14 +142,20 @@
}
private void createStyle(XMLElement elem) throws UnableToCompleteException {
- // Won't be required for long
- String source = elem.consumeRequiredAttribute("source");
+ String body = elem.consumeInnerText(new NullInterpreter<String>());
+ if (body.length() > 0 && elem.hasAttribute("source")) {
+ writer.die("In %s, cannot use both a source attribute and inline css text.", elem);
+ }
+
+ String source = elem.consumeAttribute("source");
String name = elem.consumeAttribute("field", "style");
JClassType publicType = consumeCssResourceType(elem);
- CssResourceGetter cssMethod = bundleClass.createCssResource(name, source,
- publicType);
- FieldWriter field = fieldManager.registerField(publicType,
+ ImplicitCssResource cssMethod = bundleClass.createCssResource(name, source,
+ publicType, body);
+
+ FieldWriter field = fieldManager.registerFieldOfGeneratedType(
+ cssMethod.getPackageName(), cssMethod.getClassName(),
cssMethod.getName());
field.setInitializer(String.format("%s.%s()", bundleClass.getFieldName(),
cssMethod.getName()));
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
index 9482309..edec5f7 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
@@ -15,7 +15,6 @@
*/
package com.google.gwt.uibinder.rebind;
-import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
@@ -31,16 +30,22 @@
import com.google.gwt.uibinder.parsers.ElementParser;
import com.google.gwt.uibinder.parsers.StrictAttributeParser;
import com.google.gwt.uibinder.rebind.messages.MessagesWriter;
-import com.google.gwt.uibinder.rebind.model.CssResourceGetter;
-import com.google.gwt.uibinder.rebind.model.ImplicitBundle;
+import com.google.gwt.uibinder.rebind.model.ImplicitClientBundle;
+import com.google.gwt.uibinder.rebind.model.ImplicitCssResource;
import com.google.gwt.uibinder.rebind.model.OwnerClass;
import com.google.gwt.uibinder.rebind.model.OwnerField;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -48,6 +53,10 @@
import java.util.List;
import java.util.Map;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
/**
* Writer for UiBinder generated classes.
*
@@ -178,6 +187,8 @@
return list;
}
+ private final MortalLogger logger;
+
/**
* Class names of parsers for values of attributes with no namespace prefix,
* keyed by method parameter signatures.
@@ -203,7 +214,6 @@
private final MessagesWriter messages;
private final Tokenator tokenator = new Tokenator();
- private final TreeLogger logger;
private final String templatePath;
private final TypeOracle oracle;
/**
@@ -223,7 +233,7 @@
private final FieldManager fieldManager;
- private final ImplicitBundle bundleClass;
+ private final ImplicitClientBundle bundleClass;
private int fieldIndex;
@@ -232,13 +242,14 @@
private String rendered;
UiBinderWriter(JClassType baseClass, String implClassName,
- String templatePath, TypeOracle oracle, TreeLogger logger)
+ String templatePath, TypeOracle oracle, MortalLogger logger)
throws UnableToCompleteException {
this.baseClass = baseClass;
this.implClassName = implClassName;
this.oracle = oracle;
this.logger = logger;
this.templatePath = templatePath;
+
this.messages = new MessagesWriter(BINDER_URI, logger, templatePath,
baseClass.getPackage().getName(), this.implClassName);
@@ -247,30 +258,24 @@
uiRootType = typeArgs[0];
uiOwnerType = typeArgs[1];
- ownerClass = new OwnerClass(uiOwnerType);
- bundleClass = new ImplicitBundle(baseClass.getPackage().getName(),
- this.implClassName, CLIENT_BUNDLE_FIELD);
+ ownerClass = new OwnerClass(uiOwnerType, logger);
+ bundleClass = new ImplicitClientBundle(baseClass.getPackage().getName(),
+ this.implClassName, CLIENT_BUNDLE_FIELD, logger);
handlerEvaluator = new HandlerEvaluator(ownerClass, logger, oracle);
fieldManager = new FieldManager(logger);
}
/**
- * Add a statement to be run after everything has been instantiated.
+ * Add a statement to be run after everything has been instantiated, in the
+ * style of {@link String#format}
*/
public void addInitStatement(String format, Object... params) {
initStatements.add(String.format(format, params));
}
/**
- * Adds a statement to the block run after fields are declared.
- */
- public void addStatement(String statement) {
- statements.add(statement);
- }
-
- /**
- * Adds a statement to the block run after fields are declared, using the Java
- * message format.
+ * Adds a statement to the block run after fields are declared, in the style
+ * of {@link String#format}
*/
public void addStatement(String format, Object... args) {
statements.add(String.format(format, args));
@@ -372,8 +377,7 @@
* {@link UnableToCompleteException}
*/
public void die(String message) throws UnableToCompleteException {
- logger.log(TreeLogger.ERROR, message);
- throw new UnableToCompleteException();
+ logger.die(message);
}
/**
@@ -382,8 +386,7 @@
*/
public void die(String message, Object... params)
throws UnableToCompleteException {
- logger.log(TreeLogger.ERROR, String.format(message, params));
- throw new UnableToCompleteException();
+ logger.die(message, params);
}
/**
@@ -505,11 +508,19 @@
return parser;
}
- public ImplicitBundle getBundleClass() {
+ public ImplicitClientBundle getBundleClass() {
return bundleClass;
}
/**
+ * @return The logger, at least until we get get it handed off to parsers via
+ * constructor args.
+ */
+ public MortalLogger getLogger() {
+ return logger;
+ }
+
+ /**
* Get the {@link MessagesWriter} for this UI, generating it if necessary.
*/
public MessagesWriter getMessages() {
@@ -615,14 +626,14 @@
* Post a warning message.
*/
public void warn(String message) {
- logger.log(TreeLogger.WARN, message);
+ logger.warn(message);
}
/**
* Post a warning message.
*/
public void warn(String message, Object... params) {
- logger.log(TreeLogger.WARN, String.format(message, params));
+ logger.warn(message, params);
}
/**
@@ -630,7 +641,15 @@
* implementation's superstructure, and parses the root widget (leading to all
* of its children being parsed as well).
*/
- void parseDocument(Document doc) throws UnableToCompleteException {
+ void parseDocument(PrintWriter printWriter) throws UnableToCompleteException {
+ Document doc = null;
+ try {
+ doc = parseXmlResource(templatePath);
+ } catch (SAXParseException e) {
+ die("Error parsing XML (line " + e.getLineNumber() + "): "
+ + e.getMessage(), e);
+ }
+
JClassType uiBinderClass = getOracle().findType(UiBinder.class.getName());
if (!baseClass.isAssignableTo(uiBinderClass)) {
die(baseClass.getName() + " must implement UiBinder");
@@ -641,9 +660,6 @@
XMLElement elem = new XMLElement(documentElement, this);
this.rendered = tokenator.detokenate(parseDocumentElement(elem));
- }
-
- void write(PrintWriter printWriter) {
printWriter.print(rendered);
}
@@ -668,12 +684,11 @@
XMLAttribute attribute) throws UnableToCompleteException {
final String templateResourceName = attribute.getName().split(":")[0];
- logger.log(TreeLogger.WARN, String.format(
- "The %1$s mechanism is deprecated. Instead, declare the following "
- + "%2$s:with element as a child of your %2$s:UiBinder element: "
- + "<%2$s:with name='%3$s' type='%4$s.%5$s' />", BUNDLE_URI_SCHEME,
+ warn("The %1$s mechanism is deprecated. Instead, declare the following "
+ + "%2$s:with element as a child of your %2$s:UiBinder element: "
+ + "<%2$s:with name='%3$s' type='%4$s.%5$s' />", BUNDLE_URI_SCHEME,
gwtPrefix, templateResourceName, bundleClass.getPackage().getName(),
- bundleClass.getName()));
+ bundleClass.getName());
// Try to find any bundle instance created with UiField.
OwnerField field = getOwnerClass().getUiFieldForType(bundleClass);
@@ -755,10 +770,9 @@
hasOldSchoolId = true;
// If an id is specified on the element, use that.
fieldName = elem.consumeAttribute("id");
- logger.log(TreeLogger.WARN, String.format(
- "Deprecated use of id=\"%1$s\" for field name. "
- + "Please switch to gwt:field=\"%1$s\" instead. "
- + "This will soon be a compile error!", fieldName));
+ warn("Deprecated use of id=\"%1$s\" for field name. "
+ + "Please switch to gwt:field=\"%1$s\" instead. "
+ + "This will soon be a compile error!", fieldName);
}
if (elem.hasAttribute(getUiFieldAttributeName())) {
if (hasOldSchoolId) {
@@ -903,6 +917,45 @@
return null;
}
+ private Document parseXmlResource(final String resourcePath)
+ throws SAXParseException, UnableToCompleteException {
+ // Get the document builder. We need namespaces, and automatic expanding
+ // of entity references (the latter of which makes life somewhat easier
+ // for XMLElement).
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ factory.setExpandEntityReferences(true);
+ DocumentBuilder builder;
+ try {
+ builder = factory.newDocumentBuilder();
+ } catch (ParserConfigurationException e) {
+ throw new RuntimeException(e);
+ }
+
+ try {
+ ClassLoader classLoader = UiBinderGenerator.class.getClassLoader();
+ URL url = classLoader.getResource(resourcePath);
+ if (null == url) {
+ die("Unable to find resource: " + resourcePath);
+ }
+
+ InputStream stream = url.openStream();
+ InputSource input = new InputSource(stream);
+ input.setSystemId(url.toExternalForm());
+
+ builder.setEntityResolver(new GwtResourceEntityResolver());
+
+ return builder.parse(input);
+ } catch (SAXParseException e) {
+ // Let SAXParseExceptions through.
+ throw e;
+ } catch (SAXException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
private void registerParsers() {
// TODO(rjrjr): Allow third-party parsers to register themselves
// automagically, according to http://b/issue?id=1867118
@@ -943,9 +996,9 @@
}
/**
- * Write statements that parsers created via calls to
- * {@link #addInitStatement}. Such statements will assume that
- * {@link #writeGwtFields} has already been called.
+ * Write statements that parsers created via calls to {@link #addStatement}.
+ * Such statements will assume that {@link #writeGwtFields} has already been
+ * called.
*/
private void writeAddedStatements(IndentedWriter niceWriter) {
for (String s : statements) {
@@ -985,7 +1038,7 @@
writeHandlers(w);
w.newline();
- writeFieldSetters(w);
+ writeOwnerFieldSetters(w);
writeCssInjectors(w);
@@ -1006,7 +1059,7 @@
}
private void writeCssInjectors(IndentedWriter w) {
- for (CssResourceGetter css : bundleClass.getCssMethods()) {
+ for (ImplicitCssResource css : bundleClass.getCssMethods()) {
w.write("ensureCssInjected(%s.%s());", bundleClass.getFieldName(),
css.getName());
}
@@ -1014,44 +1067,6 @@
}
/**
- * Write the statements to fill in the fields of the UI owner.
- */
- private void writeFieldSetters(IndentedWriter niceWriter)
- throws UnableToCompleteException {
- for (OwnerField ownerField : getOwnerClass().getUiFields()) {
- String fieldName = ownerField.getName();
- FieldWriter fieldWriter = fieldManager.lookup(fieldName);
-
- BundleAttributeParser bundleParser = bundleParsers.get(ownerField.getType().getRawType().getQualifiedSourceName());
-
- if (bundleParser != null) {
- // ownerField is a bundle resource.
- maybeWriteFieldSetter(niceWriter, ownerField,
- bundleParser.bundleClass(), bundleParser.bundleInstance());
-
- } else if (fieldWriter != null) {
- // ownerField is a widget.
- JClassType type = fieldWriter.getType();
- if (type != null) {
- maybeWriteFieldSetter(niceWriter, ownerField, fieldWriter.getType(),
- fieldName);
- } else {
- // Must be a generated type
- if (!ownerField.isProvided()) {
- niceWriter.write("owner.%1$s = %1$s;", fieldName);
- }
- }
-
- } else {
- // ownerField was not found as bundle resource or widget, must die.
- die("Template %s has no %s attribute for %s.%s#%s", templatePath,
- getUiFieldAttributeName(), uiOwnerType.getPackage().getName(),
- uiOwnerType.getName(), fieldName);
- }
- }
- }
-
- /**
* Write declarations for variables or fields to hold elements declared with
* gwt:field in the template. For those that have not had constructor
* generation suppressed, emit GWT.create() calls instantiating them (or die
@@ -1096,7 +1111,7 @@
/**
* Write statements created by {@link #addInitStatement}. This code must be
- * placed after all instnatiation code.
+ * placed after all instantiation code.
*/
private void writeInitStatements(IndentedWriter niceWriter) {
for (String s : initStatements) {
@@ -1104,6 +1119,44 @@
}
}
+ /**
+ * Write the statements to fill in the fields of the UI owner.
+ */
+ private void writeOwnerFieldSetters(IndentedWriter niceWriter)
+ throws UnableToCompleteException {
+ for (OwnerField ownerField : getOwnerClass().getUiFields()) {
+ String fieldName = ownerField.getName();
+ FieldWriter fieldWriter = fieldManager.lookup(fieldName);
+
+ BundleAttributeParser bundleParser = bundleParsers.get(ownerField.getType().getRawType().getQualifiedSourceName());
+
+ if (bundleParser != null) {
+ // ownerField is a bundle resource.
+ maybeWriteFieldSetter(niceWriter, ownerField,
+ bundleParser.bundleClass(), bundleParser.bundleInstance());
+
+ } else if (fieldWriter != null) {
+ // ownerField is a widget.
+ JClassType type = fieldWriter.getType();
+ if (type != null) {
+ maybeWriteFieldSetter(niceWriter, ownerField, fieldWriter.getType(),
+ fieldName);
+ } else {
+ // Must be a generated type
+ if (!ownerField.isProvided()) {
+ niceWriter.write("owner.%1$s = %1$s;", fieldName);
+ }
+ }
+
+ } else {
+ // ownerField was not found as bundle resource or widget, must die.
+ die("Template %s has no %s attribute for %s.%s#%s", templatePath,
+ getUiFieldAttributeName(), uiOwnerType.getPackage().getName(),
+ uiOwnerType.getName(), fieldName);
+ }
+ }
+ }
+
private void writePackage(IndentedWriter w) {
String packageName = baseClass.getPackage().getName();
if (packageName.length() > 0) {
@@ -1139,4 +1192,5 @@
writeStaticMessagesInstance(w);
writeStaticBundleInstances(w);
}
+
}
diff --git a/user/src/com/google/gwt/uibinder/rebind/XMLElement.java b/user/src/com/google/gwt/uibinder/rebind/XMLElement.java
index 2f6d347..0ff9000 100644
--- a/user/src/com/google/gwt/uibinder/rebind/XMLElement.java
+++ b/user/src/com/google/gwt/uibinder/rebind/XMLElement.java
@@ -1,12 +1,12 @@
/*
* Copyright 2008 Google Inc.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -47,7 +47,7 @@
public interface Interpreter<T> {
/**
* Given an XMLElement, return its filtered value.
- *
+ *
* @throws UnableToCompleteException on error
*/
T interpretElement(XMLElement elem) throws UnableToCompleteException;
@@ -115,7 +115,7 @@
/**
* Consumes the given attribute and returns its trimmed value, or null if it
* was unset. The returned string is not escaped.
- *
+ *
* @param name the attribute's full name (including prefix)
* @return the attribute's value, or null
*/
@@ -128,7 +128,7 @@
/**
* Consumes the given attribute and returns its trimmed value, or the given
* default value if it was unset. The returned string is not escaped.
- *
+ *
* @param name the attribute's full name (including prefix)
* @param defaultValue the value to return if the attribute was unset
* @return the attribute's value, or defaultValue
@@ -143,7 +143,7 @@
/**
* Consumes the given attribute as a boolean value.
- *
+ *
* @throws UnableToCompleteException
*/
public boolean consumeBooleanAttribute(String attr)
@@ -177,9 +177,9 @@
* Consumes and returns all child elements selected by the interpreter. Note
* that text nodes are not elements, and so are not presented for
* interpretation, and are not consumed.
- *
+ *
* @param interpreter Should return true for any child that should be consumed
- * and returned.
+ * and returned by the consumeChildElements call
* @throws UnableToCompleteException
*/
public Collection<XMLElement> consumeChildElements(
@@ -217,7 +217,7 @@
* The odds are you want to use
* {@link com.google.gwt.templates.parsers.HtmlInterpreter} for an HTML value,
* or {@link com.google.gwt.templates.parsers.TextInterpreter} for text.
- *
+ *
* @param interpreter Called for each element, expected to return a string
* replacement for it, or null if it should be left as is
*/
@@ -250,7 +250,7 @@
* This call requires an interpreter to make sense of any special children.
* The odds are you want to use
* {@link com.google.gwt.templates.parsers.TextInterpreter}
- *
+ *
* @throws UnableToCompleteException If any elements present are not consumed
* by the interpreter
*/
@@ -292,7 +292,7 @@
*/
public String consumeOpeningTag() {
String rtn = getOpeningTag();
-
+
for (int i = getAttributeCount() - 1; i >= 0; i--) {
getAttribute(i).consumeValue();
}
@@ -306,7 +306,7 @@
throws UnableToCompleteException {
String value = consumeAttribute(name);
if ("".equals(value)) {
- writer.die("In %s, missing required attribute name\"%s\"", this);
+ writer.die("In %s, missing required attribute name\"%s\"", this, name);
}
return value;
}
@@ -319,8 +319,9 @@
XMLElement ret = null;
for (XMLElement child : consumeChildElements()) {
if (ret != null) {
- throw new RuntimeException(getLocalName()
- + " may only contain a single child element.");
+ throw new RuntimeException(String.format(
+ "%s may only contain a single child element, but found"
+ + "%s and %s.", getLocalName(), ret, child));
}
ret = child;
diff --git a/user/src/com/google/gwt/uibinder/rebind/messages/MessagesWriter.java b/user/src/com/google/gwt/uibinder/rebind/messages/MessagesWriter.java
index 6e4fe34..58e43cb 100644
--- a/user/src/com/google/gwt/uibinder/rebind/messages/MessagesWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/messages/MessagesWriter.java
@@ -15,9 +15,9 @@
*/
package com.google.gwt.uibinder.rebind.messages;
-import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.uibinder.rebind.IndentedWriter;
+import com.google.gwt.uibinder.rebind.MortalLogger;
import com.google.gwt.uibinder.rebind.UiBinderWriter;
import com.google.gwt.uibinder.rebind.XMLElement;
@@ -44,7 +44,7 @@
private final String messagesNamespaceURI;
private final String packageName;
private final String messagesClassName;
- private final TreeLogger logger;
+ private final MortalLogger logger;
private final List<MessageWriter> messages = new ArrayList<MessageWriter>();
private final String generatedFrom;
@@ -56,7 +56,7 @@
private Map<XMLElement, Collection<AttributeMessage>> elemToAttributeMessages =
new HashMap<XMLElement, Collection<AttributeMessage>>();
- public MessagesWriter(String nameSpaceUri, TreeLogger logger, String generatedFrom,
+ public MessagesWriter(String nameSpaceUri, MortalLogger mortalLogger, String generatedFrom,
String packageName, String uiBinderImplClassName) {
this.messagesNamespaceURI = nameSpaceUri;
this.generatedFrom = generatedFrom;
@@ -65,7 +65,7 @@
// Localizable classes cannot have underscores in their names.
this.messagesClassName = uiBinderImplClassName.replaceAll("_", "") + "GenMessages";
- this.logger = logger;
+ this.logger = mortalLogger;
}
/**
@@ -108,10 +108,10 @@
for (XMLElement child : messageChildren) {
String attributeName = consumeMessageElementAttribute(NAME, child);
if (attributeName.length() == 0) {
- die(String.format("Missing name attribute in %s", child));
+ logger.die("Missing name attribute in %s", child);
}
if (!elem.hasAttribute(attributeName)) {
- die(String.format("%s has no attribute matching %s", elem, child));
+ logger.die("%s has no attribute matching %s", elem, child);
}
String defaultMessage =
@@ -183,7 +183,7 @@
/**
* Confirm existence of an m:blah attribute on a non-message element, e.g.
- * {@code <span m:ph="fnord"/>}
+ * {@code <span ui:ph="fnord"/>}
*/
public boolean hasMessageAttribute(String attName, XMLElement elem) {
String fullAttName = getMessagesPrefix() + ":" + attName;
@@ -268,9 +268,9 @@
String fullAttName = getMessagesPrefix() + ":" + attName;
if (elem.hasAttribute(fullAttName)) {
String value = elem.consumeAttribute(fullAttName);
- logger.log(TreeLogger.WARN, String.format(
+ logger.warn(
"In %s, deprecated prefix \"%s:\" on \"%s\". Use \"%s\" instead.",
- elem, getMessagesPrefix(), fullAttName, attName));
+ elem, getMessagesPrefix(), fullAttName, attName);
return value;
}
@@ -281,7 +281,7 @@
XMLElement elem) throws UnableToCompleteException {
String value = consumeMessageElementAttribute(attName, elem);
if ("".equals(value)) {
- die("%s does not have required attribute %s", elem, attName);
+ logger.die("%s does not have required attribute %s", elem, attName);
}
return value;
}
@@ -302,25 +302,6 @@
return declareMessage(newMessage);
}
- private void die(String message) throws UnableToCompleteException {
- // TODO(rjrjr) copied from TemplateWriter. Move to common superclass or
- // something
- logger.log(TreeLogger.ERROR, message);
- throw new UnableToCompleteException();
- }
-
- /**
- * Post an error message and halt processing. This method always throws an
- * {@link UnableToCompleteException}
- */
- private void die(String message, Object... params)
- throws UnableToCompleteException {
- // TODO(rjrjr) copied from TemplateWriter. Move to common superclass or
- // something
- logger.log(TreeLogger.ERROR, String.format(message, params));
- throw new UnableToCompleteException();
- }
-
private void genInterfaceAnnotations(IndentedWriter pw) {
pw.write("@GeneratedFrom(\"%s\")", generatedFrom);
if (defaultLocale.length() > 0) {
@@ -339,7 +320,7 @@
throws UnableToCompleteException {
if (isAttributeMessage(child)) {
if (child.hasChildNodes()) {
- die(String.format("Illegal body for %s in %s.", child, elem));
+ logger.die("Illegal body for %s in %s.", child, elem);
}
return true;
}
diff --git a/user/src/com/google/gwt/uibinder/rebind/model/CssResourceGetter.java b/user/src/com/google/gwt/uibinder/rebind/model/CssResourceGetter.java
deleted file mode 100644
index ae0017a..0000000
--- a/user/src/com/google/gwt/uibinder/rebind/model/CssResourceGetter.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2009 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.uibinder.rebind.model;
-
-import com.google.gwt.core.ext.typeinfo.JClassType;
-
-/**
- * Models a method returning a CssResource on a generated ClientBundle. At the
- * moment, they must implement a public interface and be tied to an external
- * .css file. That should improve in the next day or so.
- */
-public class CssResourceGetter {
- private final String name;
- private final String source;
- private final JClassType extendedInterface;
-
- public CssResourceGetter(String name, String source,
- JClassType extendedInterface) {
- this.name = name;
- this.source = source;
- this.extendedInterface = extendedInterface;
- }
-
- /**
- * @return the public interface that this CssResource implements
- */
- public JClassType getExtendedInterface() {
- return extendedInterface;
- }
-
- /**
- * @return the name of this resource. This is both its method name in the
- * owning {@link ImplicitBundle} and its ui:field name
- */
- public String getName() {
- return name;
- }
-
- /**
- * @return the path to the associated .css file resource
- */
- public String getSource() {
- return source;
- }
-}
diff --git a/user/src/com/google/gwt/uibinder/rebind/model/ImplicitBundle.java b/user/src/com/google/gwt/uibinder/rebind/model/ImplicitClientBundle.java
similarity index 66%
rename from user/src/com/google/gwt/uibinder/rebind/model/ImplicitBundle.java
rename to user/src/com/google/gwt/uibinder/rebind/model/ImplicitClientBundle.java
index abee4d3..3f17973 100644
--- a/user/src/com/google/gwt/uibinder/rebind/model/ImplicitBundle.java
+++ b/user/src/com/google/gwt/uibinder/rebind/model/ImplicitClientBundle.java
@@ -16,6 +16,7 @@
package com.google.gwt.uibinder.rebind.model;
import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.uibinder.rebind.MortalLogger;
import java.util.Collections;
import java.util.HashSet;
@@ -24,24 +25,29 @@
/**
* Models the ClientBundle to be generated from a ui.xml.
*/
-public class ImplicitBundle {
+public class ImplicitClientBundle {
- private final Set<CssResourceGetter> cssMethods = new HashSet<CssResourceGetter>();
+ private final Set<ImplicitCssResource> cssMethods = new HashSet<ImplicitCssResource>();
private final String packageName;
private final String className;
private final String fieldName;
+ private final String cssBaseName;
+ private final MortalLogger logger;
/**
* @param packageName Where the bundle should live
* @param uiBinderImplClassName The name of the generated ui binder
* implementation that owns the bundle
* @param fieldName The bundle's field name
+ * @param logger TODO
*/
- public ImplicitBundle(String packageName, String uiBinderImplClassName,
- String fieldName) {
+ public ImplicitClientBundle(String packageName, String uiBinderImplClassName,
+ String fieldName, MortalLogger logger) {
this.packageName = packageName;
- this.className = uiBinderImplClassName + "GenBundle";
+ this.className = uiBinderImplClassName + "_GenBundle";
+ this.cssBaseName = uiBinderImplClassName + "_GenCss";
this.fieldName = fieldName;
+ this.logger = logger;
}
/**
@@ -51,11 +57,13 @@
* @param source path to the .css file resource
* @param extendedInterface the public interface implemented by this
* CssResource, or null
+ * @param body the inline css text
* @return
*/
- public CssResourceGetter createCssResource(String name, String source,
- JClassType extendedInterface) {
- CssResourceGetter css = new CssResourceGetter(name, source, extendedInterface);
+ public ImplicitCssResource createCssResource(String name, String source,
+ JClassType extendedInterface, String body) {
+ ImplicitCssResource css = new ImplicitCssResource(packageName, cssBaseName
+ + name, name, source, extendedInterface, body, logger);
cssMethods.add(css);
return css;
}
@@ -64,7 +72,7 @@
return className;
}
- public Set<CssResourceGetter> getCssMethods() {
+ public Set<ImplicitCssResource> getCssMethods() {
return Collections.unmodifiableSet(cssMethods);
}
diff --git a/user/src/com/google/gwt/uibinder/rebind/model/ImplicitCssResource.java b/user/src/com/google/gwt/uibinder/rebind/model/ImplicitCssResource.java
new file mode 100644
index 0000000..5742cec
--- /dev/null
+++ b/user/src/com/google/gwt/uibinder/rebind/model/ImplicitCssResource.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.uibinder.rebind.model;
+
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.resources.css.ExtractClassNamesVisitor;
+import com.google.gwt.resources.css.GenerateCssAst;
+import com.google.gwt.resources.css.ast.CssStylesheet;
+import com.google.gwt.resources.ext.ResourceGeneratorUtil;
+import com.google.gwt.uibinder.rebind.MortalLogger;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Set;
+
+/**
+ * Models a method returning a CssResource on a generated ClientBundle. At the
+ * moment, they must be tied to an external .css file. That should improve in
+ * the next day or so.
+ */
+public class ImplicitCssResource {
+ private final String packageName;
+ private final String className;
+ private final String name;
+ private final String source;
+ private final JClassType extendedInterface;
+ private final String body;
+ private final MortalLogger logger;
+ private File generatedFile;
+
+ public ImplicitCssResource(String packageName, String className, String name,
+ String source, JClassType extendedInterface, String body,
+ MortalLogger logger) {
+ this.packageName = packageName;
+ this.className = className;
+ this.name = name;
+ this.extendedInterface = extendedInterface;
+ this.body = body;
+ this.logger = logger;
+
+ if (body.length() > 0) {
+ assert "".equals(source); // Enforced for real by the parser
+
+ /*
+ * We're going to write the inline body to a temporary File and register
+ * it with the CssResource world under the name in this.source, via
+ * ResourceGeneratorUtil.addNamedFile(). When CssResourceGenerator sees
+ * this name in an @Source annotation it will know to use the registered
+ * file rather than load a resource.
+ */
+ source = String.format("uibinder:%s.%s.css", packageName, className);
+ }
+ this.source = source;
+ }
+
+ /**
+ * @return the name of the CssResource interface
+ */
+ public String getClassName() {
+ return className;
+ }
+
+ /**
+ * @return the set of CSS classnames in the underlying .css files
+ *
+ * @throws UnableToCompleteException if the user has called for a .css file we
+ * can't find.
+ */
+ public Set<String> getCssClassNames() throws UnableToCompleteException {
+ URL[] urls;
+
+ if (body.length() == 0) {
+ urls = getExternalCss();
+ } else {
+ try {
+ urls = new URL[] {getGeneratedFile().toURL()};
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ CssStylesheet sheet = GenerateCssAst.exec(logger.getTreeLogger(), urls);
+ return ExtractClassNamesVisitor.exec(sheet);
+ }
+
+ /**
+ * @return the public interface that this CssResource implements
+ */
+ public JClassType getExtendedInterface() {
+ return extendedInterface;
+ }
+
+ /**
+ * @return the name of this resource. This is both its method name in the
+ * owning {@link ImplicitClientBundle} and its ui:field name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return the package in which the generated CssResource interface should
+ * reside
+ */
+ public String getPackageName() {
+ return packageName;
+ }
+
+ /**
+ * @return the name of the .css file(s), separate by white space
+ */
+ public String getSource() {
+ return source;
+ }
+
+ private URL[] getExternalCss() throws UnableToCompleteException {
+ /*
+ * TODO(rjrjr,bobv) refactor ResourceGeneratorUtil.findResources so we can
+ * find them the same way ClientBundle does. For now, just look relative to
+ * this package
+ */
+
+ ClassLoader classLoader = ImplicitCssResource.class.getClassLoader();
+ String path = packageName.replace(".", "/");
+
+ String[] sources = source.split(" ");
+ URL[] urls = new URL[sources.length];
+ int i = 0;
+
+ for (String s : sources) {
+ String resourcePath = path + '/' + s;
+ URL found = classLoader.getResource(resourcePath);
+ if (null == found) {
+ logger.die("Unable to find resource: " + resourcePath);
+ }
+ urls[i++] = found;
+ }
+ return urls;
+ }
+
+ private File getGeneratedFile() {
+ if (generatedFile == null) {
+ try {
+ File f = File.createTempFile(String.format("uiBinder_%s_%s",
+ packageName, className), ".css");
+
+ BufferedWriter out = new BufferedWriter(new FileWriter(f));
+ out.write(body);
+ out.close();
+ generatedFile = f;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ ResourceGeneratorUtil.addNamedFile(getSource(), generatedFile);
+ }
+ return generatedFile;
+ }
+}
diff --git a/user/src/com/google/gwt/uibinder/rebind/model/OwnerClass.java b/user/src/com/google/gwt/uibinder/rebind/model/OwnerClass.java
index 5250c8c..c7ee4f0 100644
--- a/user/src/com/google/gwt/uibinder/rebind/model/OwnerClass.java
+++ b/user/src/com/google/gwt/uibinder/rebind/model/OwnerClass.java
@@ -22,6 +22,7 @@
import com.google.gwt.uibinder.client.UiFactory;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.uibinder.rebind.MortalLogger;
import java.util.ArrayList;
import java.util.Collection;
@@ -61,12 +62,16 @@
*/
private final List<JMethod> uiHandlers = new ArrayList<JMethod>();
+ private final MortalLogger logger;
+
/**
* Constructor.
*
* @param ownerType the type of the owner class
+ * @param logger
*/
- public OwnerClass(JClassType ownerType) throws UnableToCompleteException {
+ public OwnerClass(JClassType ownerType, MortalLogger logger) throws UnableToCompleteException {
+ this.logger = logger;
findUiFields(ownerType);
findUiFactories(ownerType);
findUiHandlers(ownerType);
@@ -105,7 +110,7 @@
* @return the field descriptor
* @deprecated This will die with {@link com.google.gwt.uibinder.parsers.BundleAttributeParser}
*/
- @Deprecated
+ @Deprecated
public OwnerField getUiFieldForType(JClassType type) {
return uiFieldTypes.get(type);
}
@@ -127,9 +132,9 @@
/**
* Scans the owner class to find all methods annotated with @UiFactory, and
* puts them in {@link #uiFactories}.
- *
+ *
* @param ownerType the type of the owner class
- * @throws UnableToCompleteException
+ * @throws UnableToCompleteException
*/
private void findUiFactories(JClassType ownerType)
throws UnableToCompleteException {
@@ -139,18 +144,14 @@
JClassType factoryType = method.getReturnType().isClassOrInterface();
if (factoryType == null) {
- // TODO(rdamazio): proper logging
- System.out.println("Factory return type is not a class in method "
+ logger.die("Factory return type is not a class in method "
+ method.getName());
- throw new UnableToCompleteException();
}
if (uiFactories.containsKey(factoryType)) {
- // TODO(rdamazio): proper logging
- System.out.println("Duplicate factory in class "
+ logger.die("Duplicate factory in class "
+ method.getEnclosingType().getName() + " for type "
+ factoryType.getName());
- throw new UnableToCompleteException();
}
uiFactories.put(factoryType, method);
@@ -167,7 +168,7 @@
/**
* Scans the owner class to find all fields annotated with @UiField, and puts
* them in {@link #uiFields} and {@link #uiFieldTypes}.
- *
+ *
* @param ownerType the type of the owner class
*/
private void findUiFields(JClassType ownerType)
@@ -178,13 +179,11 @@
JClassType ownerFieldType = field.getType().isClassOrInterface();
if (ownerFieldType == null) {
- // TODO(rdamazio): proper logging
- System.out.println("Field type is not a class in field "
+ logger.die("Field type is not a class in field "
+ field.getName());
- throw new UnableToCompleteException();
}
- OwnerField ownerField = new OwnerField(field);
+ OwnerField ownerField = new OwnerField(field, logger);
String ownerFieldName = field.getName();
uiFields.put(ownerFieldName, ownerField);
diff --git a/user/src/com/google/gwt/uibinder/rebind/model/OwnerField.java b/user/src/com/google/gwt/uibinder/rebind/model/OwnerField.java
index e319827..d257000 100644
--- a/user/src/com/google/gwt/uibinder/rebind/model/OwnerField.java
+++ b/user/src/com/google/gwt/uibinder/rebind/model/OwnerField.java
@@ -19,6 +19,7 @@
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JField;
import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.rebind.MortalLogger;
/**
* Descriptor for a field of the owner class.
@@ -38,29 +39,26 @@
* Constructor.
*
* @param field the field of the owner class
+ * @param logger
*/
- public OwnerField(JField field) throws UnableToCompleteException {
+ public OwnerField(JField field, MortalLogger logger) throws UnableToCompleteException {
this.name = field.getName();
// Get the field type and ensure it's a class or interface
JClassType fieldClassType = field.getType().isClassOrInterface();
if (fieldClassType == null) {
- // TODO(rdamazio): proper logging
- System.out.println("Type for field " + name + " is not a class: "
+ logger.die("Type for field " + name + " is not a class: "
+ field.getType().getSimpleSourceName());
- throw new UnableToCompleteException();
}
- this.fieldType = OwnerFieldClass.getFieldClass(fieldClassType);
+ this.fieldType = OwnerFieldClass.getFieldClass(fieldClassType, logger);
// Get the UiField annotation and process it
UiField annotation = field.getAnnotation(UiField.class);
if (annotation == null) {
- // TODO(rdamazio): proper logging
- System.out.println("Field " + name + " is not annotated with @UiField");
- throw new UnableToCompleteException();
+ logger.die("Field " + name + " is not annotated with @UiField");
}
isProvided = annotation.provided();
diff --git a/user/src/com/google/gwt/uibinder/rebind/model/OwnerFieldClass.java b/user/src/com/google/gwt/uibinder/rebind/model/OwnerFieldClass.java
index 772b959..9622998 100644
--- a/user/src/com/google/gwt/uibinder/rebind/model/OwnerFieldClass.java
+++ b/user/src/com/google/gwt/uibinder/rebind/model/OwnerFieldClass.java
@@ -23,6 +23,7 @@
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.uibinder.client.UiConstructor;
+import com.google.gwt.uibinder.rebind.MortalLogger;
import java.util.ArrayList;
import java.util.Collection;
@@ -32,31 +33,31 @@
import java.util.Set;
/**
- * Descriptor for a class which can be used as a @UiField.
- * This is usually a widget, but can also be a resource (such as Messages or
- * an ImageBundle). Also notice that the existence of an OwnerFieldClass doesn't
- * mean the class is actually present as a field in the owner.
+ * Descriptor for a class which can be used as a @UiField. This is usually a
+ * widget, but can also be a resource (such as Messages or an ImageBundle). Also
+ * notice that the existence of an OwnerFieldClass doesn't mean the class is
+ * actually present as a field in the owner.
*/
public class OwnerFieldClass {
/**
- * Global map of field classes.
- * This serves as a cache so each class is only processed once.
+ * Global map of field classes. This serves as a cache so each class is only
+ * processed once.
*/
- private static final Map<JClassType, OwnerFieldClass> FIELD_CLASSES =
- new HashMap<JClassType, OwnerFieldClass>();
+ private static final Map<JClassType, OwnerFieldClass> FIELD_CLASSES = new HashMap<JClassType, OwnerFieldClass>();
/**
* Gets or creates the descriptor for the given field class.
*
* @param forType the field type to get a descriptor for
+ * @param logger TODO
* @return the descriptor
*/
- public static OwnerFieldClass getFieldClass(JClassType forType)
- throws UnableToCompleteException {
+ public static OwnerFieldClass getFieldClass(JClassType forType,
+ MortalLogger logger) throws UnableToCompleteException {
OwnerFieldClass clazz = FIELD_CLASSES.get(forType);
if (clazz == null) {
- clazz = new OwnerFieldClass(forType);
+ clazz = new OwnerFieldClass(forType, logger);
FIELD_CLASSES.put(forType, clazz);
}
return clazz;
@@ -66,16 +67,19 @@
private final Map<String, JMethod> setters = new HashMap<String, JMethod>();
private Set<String> ambiguousSetters;
private JConstructor uiConstructor;
+ private final MortalLogger logger;
/**
- * Default constructor.
- * This is package-visible for testing only.
+ * Default constructor. This is package-visible for testing only.
*
* @param forType the type of the field class
+ * @param logger
* @throws UnableToCompleteException if the class is not valid
*/
- OwnerFieldClass(JClassType forType) throws UnableToCompleteException {
+ OwnerFieldClass(JClassType forType, MortalLogger logger)
+ throws UnableToCompleteException {
this.rawType = forType;
+ this.logger = logger;
findUiConstructor(forType);
findSetters(forType);
@@ -101,10 +105,8 @@
// when CheckBox#setChecked will go away and CheckBox#setValue must be used
if (ambiguousSetters != null && ambiguousSetters.contains(propertyName)) {
- // TODO(rdamazio): proper logging
- System.out.println("Ambiguous setter requested: " + rawType.getName()
- + "." + propertyName);
- throw new UnableToCompleteException();
+ logger.die("Ambiguous setter requested: " + rawType.getName() + "."
+ + propertyName);
}
return setters.get(propertyName);
@@ -120,9 +122,8 @@
/**
* Given a collection of setters for the same property, picks which one to
- * use.
- * Not having a proper setter is not an error unless of course the user tries
- * to use it.
+ * use. Not having a proper setter is not an error unless of course the user
+ * tries to use it.
*
* @param propertySetters the collection of setters
* @return the setter to use, or null if none is good enough
@@ -167,8 +168,7 @@
* @param fieldType the leaf type to look at
* @return a multimap of property name to the setter methods
*/
- private Map<String, Collection<JMethod>> findAllSetters(
- JClassType fieldType) {
+ private Map<String, Collection<JMethod>> findAllSetters(JClassType fieldType) {
Map<String, Collection<JMethod>> allSetters;
// First, get all setters from the parent class, recursively.
@@ -246,10 +246,8 @@
for (JConstructor ctor : fieldType.getConstructors()) {
if (ctor.getAnnotation(UiConstructor.class) != null) {
if (uiConstructor != null) {
- // TODO(rdamazio): proper logging
- System.out.println(fieldType.getName()
+ logger.die(fieldType.getName()
+ " has more than one constructor annotated with @UiConstructor");
- throw new UnableToCompleteException();
}
uiConstructor = ctor;
}
@@ -257,9 +255,8 @@
}
/**
- * Checks whether the given method qualifies as a setter.
- * This looks at the method qualifiers, name and return type, but not at the
- * parameter types.
+ * Checks whether the given method qualifies as a setter. This looks at the
+ * method qualifiers, name and return type, but not at the parameter types.
*
* @param method the method to look at
* @return whether it's a setter
diff --git a/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.css b/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.css
index 9bd75be..c712178 100644
--- a/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.css
+++ b/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.css
@@ -2,3 +2,11 @@
border:solid;
background-color:white;
}
+
+/*
+ * Demonstrates that the ui.xml has access to styles that
+ * do not back any declared CssResource api
+ */
+.privateColor {
+ color: SteelBlue;
+}
diff --git a/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.java b/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.java
index 1fde1b3..edb568f 100644
--- a/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.java
+++ b/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.java
@@ -25,6 +25,7 @@
import com.google.gwt.dom.client.StyleInjector;
import com.google.gwt.dom.client.TableElement;
import com.google.gwt.resources.client.CssResource;
+import com.google.gwt.resources.client.CssResource.Shared;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiFactory;
import com.google.gwt.uibinder.client.UiField;
@@ -50,6 +51,7 @@
* This CssResource is a requirement of the WidgetBasedUi, to be provided by
* its ui.xml template.
*/
+ @Shared
public interface Style extends CssResource {
String menuBar();
}
@@ -108,6 +110,9 @@
@UiField OListElement widgetCrazyOrderedList;
@UiField DListElement widgetCrazyDefinitionList;
@UiField HTMLPanel customTagHtmlPanel;
+ @UiField ParagraphElement privateStyleParagraph;
+ @UiField ParagraphElement reallyPrivateStyleParagraph;
+ @UiField SpanElement totallyPrivateStyleSpan;
public WidgetBasedUi() {
this.bundledLabel = new Label();
diff --git a/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.ui.xml b/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.ui.xml
index 0d2db28..33ba429 100644
--- a/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.ui.xml
+++ b/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.ui.xml
@@ -69,8 +69,30 @@
<!--
Tests creating a CssResource from an external file.
-->
-<!--<ui:style field='myStyle' source='WidgetBasedUi.css' type='com.google.gwt.uibinder.sample.client.WidgetBasedUi.Style'/>-->
-<ui:style field='myStyle' source='WidgetBasedUi.css' type='com.google.gwt.uibinder.sample.client.WidgetBasedUi.Style'/>
+<ui:style field='myStyle' source='WidgetBasedUi.css'
+ type='com.google.gwt.uibinder.sample.client.WidgetBasedUi.Style'/>
+
+<ui:style field='myOtherStyle' type='com.google.gwt.uibinder.sample.client.WidgetBasedUi.Style'>
+ /* because we extend WidgetBasedUi.Style and it's tagged @Shared, we can refine the menubar
+ style defined in other files */
+
+ .menuBar.psychedelic {
+ background-color: Yellow;
+ }
+
+ /* Visible only to this template */
+ .privateColor {
+ color: Purple;
+ }
+</ui:style>
+
+<ui:style field='myTotallyPrivateStyle'>
+ /* An inline style tied to no public type */
+
+ .superPrivateColor {
+ background-color: Gold;
+ }
+</ui:style>
<gwt:DockPanel ui:field="root" width="100%">
<gwt:Dock direction='NORTH'>
@@ -150,7 +172,7 @@
Piggledy
</ui:msg>
</div>
- <gwt:MenuBar vertical="true" styleName="{myStyle.menuBar}">
+ <gwt:MenuBar vertical="true" styleName="{myStyle.menuBar} {myOtherStyle.psychedelic}">
<gwt:MenuItem>delta</gwt:MenuItem>
<gwt:MenuItem>echo</gwt:MenuItem>
<gwt:MenuItem>foxtrot</gwt:MenuItem>
@@ -256,13 +278,23 @@
<h2>Stylish</h2>
<p>
- Templates work with what is now called ImmutableResourceBundle, but
- which you will soon come to know and love as ClientBundle. For example,
- this label gets its text from somplace tricky and its style from a resource:</p>
+ Templates work with ClientBundle. For example,
+ this label gets its text from somplace tricky and its style from a ClientBundle
+ defined outside of this UI:</p>
<gwt:Label ui:field='bundledLabel' text="{values.helloText}" styleName="{external.style.prettyText}"/>
+ <!-- Note use of id rather than ui:field, to test that ids work on dom elements -->
<p class="{external.style.prettyText}" id='prettyPara'>
- This stylish paragraph also gets its good looks from a
- resource.
+ This stylish paragraph also gets its good looks from the
+ external ClientBundle.
+ </p>
+ <p ui:field='privateStyleParagraph' class='{myStyle.privateColor}'>
+ This one is has a private style, defined out in WidgetBaseUi.css and used only by this ui.xml file.
+ </p>
+ <p ui:field='reallyPrivateStyleParagraph' class='{myOtherStyle.privateColor}'>
+ And this style is defined right here inside the ui.xml file.
+ <span ui:field='totallyPrivateStyleSpan' class='{myTotallyPrivateStyle.superPrivateColor}'>
+ (This one too, but even more so.)
+ </span>
</p>
<h2>Evolving</h2>
<p>
diff --git a/user/src/com/google/gwt/user/client/ui/DockLayoutPanel.java b/user/src/com/google/gwt/user/client/ui/DockLayoutPanel.java
index 4281dde..58e15d3 100644
--- a/user/src/com/google/gwt/user/client/ui/DockLayoutPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/DockLayoutPanel.java
@@ -101,6 +101,7 @@
*
* @param widget the widget to be added
* @param direction the widget's direction in the dock
+ * @param size the child widget's size
*
* @throws IllegalArgumentException when adding to the {@link #CENTER} and
* there is already a different widget there
@@ -114,7 +115,7 @@
*/
public Element getContainerElementFor(Widget widget) {
assertIsChild(widget);
- return ((LayoutData)widget.getLayoutData()).layer.getContainerElement();
+ return ((LayoutData) widget.getLayoutData()).layer.getContainerElement();
}
/**
diff --git a/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java b/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java
new file mode 100644
index 0000000..46a43e8
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.client.ui;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
+import com.google.gwt.user.client.Event;
+
+/**
+ * A panel that adds user-positioned splitters between each of its child
+ * widgets.
+ *
+ * <p>
+ * This panel is used in the same way as {@link DockLayoutPanel}, except that
+ * its children's sizes are always specified in {@link Unit#PX} units, and each
+ * pair of child widgets has a splitter between them that the user can drag.
+ * </p>
+ *
+ * <p>
+ * This widget will <em>only</em> work in standards mode, which requires
+ * that the HTML page in which it is run have an explicit <!DOCTYPE>
+ * declaration.
+ * </p>
+ *
+ * <p>
+ * NOTE: This class is still very new, and its interface may change without
+ * warning. Use at your own risk.
+ * </p>
+ *
+ * <p>
+ * <h3>Example</h3>
+ * {@example com.google.gwt.examples.SplitLayoutPanelExample}
+ * </p>
+ *
+ * TODO(jgw):
+ * - RTL Support.
+ * - implement insert().
+ * - Come up with a decent way to specify splitter style and size.
+ */
+public class SplitLayoutPanel extends DockLayoutPanel {
+
+ private class HSplitter extends Splitter {
+ public HSplitter(Widget target, boolean reverse) {
+ super(target, reverse);
+ getElement().getStyle().setPropertyPx("width", 4);
+ setStyleName("LayoutPanel-HDragger");
+ }
+
+ @Override
+ protected int getAbsolutePosition() {
+ return getAbsoluteLeft();
+ }
+
+ @Override
+ protected int getEventPosition(Event event) {
+ return event.getClientX();
+ }
+
+ @Override
+ protected int getTargetPosition() {
+ return target.getAbsoluteLeft();
+ }
+
+ @Override
+ protected int getTargetSize() {
+ return target.getOffsetWidth();
+ }
+ }
+
+ private abstract class Splitter extends Widget {
+ protected final Widget target;
+
+ private int offset;
+ private boolean mouseDown;
+ private Command layoutCommand;
+
+ private final boolean reverse;
+ private int minSize;
+
+ public Splitter(Widget target, boolean reverse) {
+ this.target = target;
+ this.reverse = reverse;
+
+ setElement(Document.get().createDivElement());
+ sinkEvents(Event.ONMOUSEDOWN | Event.ONMOUSEUP | Event.ONMOUSEMOVE
+ | Event.ONDBLCLICK);
+ }
+
+ @Override
+ public void onBrowserEvent(Event event) {
+ switch (event.getTypeInt()) {
+ case Event.ONMOUSEDOWN:
+ mouseDown = true;
+ offset = getEventPosition(event) - getAbsolutePosition();
+ Event.setCapture(getElement());
+ event.preventDefault();
+ break;
+
+ case Event.ONMOUSEUP:
+ mouseDown = false;
+ Event.releaseCapture(getElement());
+ event.preventDefault();
+ break;
+
+ case Event.ONMOUSEMOVE:
+ if (mouseDown) {
+ int size;
+ if (reverse) {
+ size = getTargetPosition() + getTargetSize()
+ - getEventPosition(event) - offset;
+ } else {
+ size = getEventPosition(event) - getTargetPosition() - offset;
+ }
+
+ setAssociatedWidgetSize(size);
+ event.preventDefault();
+ }
+ break;
+ }
+ }
+
+ public void setMinSize(int minSize) {
+ this.minSize = minSize;
+ LayoutData layout = (LayoutData) target.getLayoutData();
+
+ // Try resetting the associated widget's size, which will enforce the new
+ // minSize value.
+ setAssociatedWidgetSize((int) layout.size);
+ }
+
+ protected abstract int getAbsolutePosition();
+
+ protected abstract int getEventPosition(Event event);
+
+ protected abstract int getTargetPosition();
+
+ protected abstract int getTargetSize();
+
+ private void setAssociatedWidgetSize(int size) {
+ if (size < minSize) {
+ size = minSize;
+ }
+
+ LayoutData layout = (LayoutData) target.getLayoutData();
+ if (size == layout.size) {
+ return;
+ }
+
+ layout.size = size;
+
+ // Defer actually updating the layout, so that if we receive many
+ // mouse events before layout/paint occurs, we'll only update once.
+ if (layoutCommand == null) {
+ layoutCommand = new Command() {
+ public void execute() {
+ layoutCommand = null;
+ layout();
+ }
+ };
+ DeferredCommand.addCommand(layoutCommand);
+ }
+ }
+ }
+
+ private class VSplitter extends Splitter {
+ public VSplitter(Widget target, boolean reverse) {
+ super(target, reverse);
+ getElement().getStyle().setPropertyPx("height", 4);
+ setStyleName("LayoutPanel-VDragger");
+ }
+
+ @Override
+ protected int getAbsolutePosition() {
+ return getAbsoluteTop();
+ }
+
+ @Override
+ protected int getEventPosition(Event event) {
+ return event.getClientY();
+ }
+
+ @Override
+ protected int getTargetPosition() {
+ return target.getAbsoluteTop();
+ }
+
+ @Override
+ protected int getTargetSize() {
+ return target.getOffsetHeight();
+ }
+ }
+
+ private static final int SPLITTER_SIZE = 4;
+
+ public SplitLayoutPanel() {
+ super(Unit.PX);
+ }
+
+ @Override
+ public void add(Widget child, Direction direction, double size) {
+ super.add(child, direction, size);
+ if (direction != Direction.CENTER) {
+ addSplitter();
+ }
+ }
+
+ @Override
+ public boolean remove(Widget child) {
+ assert !(child instanceof Splitter) : "Splitters may not be directly removed";
+
+ if (super.remove(child)) {
+ // Remove the associated splitter, if any.
+ int idx = getWidgetIndex(child);
+ if (idx < getWidgetCount() - 1) {
+ remove(idx + 1);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Sets the minimum allowable size for the given widget.
+ *
+ * <p>
+ * Its assocated splitter cannot be dragged to a position that would make it
+ * smaller than this size. This method has no effect for the
+ * {@link Direction#CENTER} widget.
+ * </p>
+ *
+ * @param child the child whose minimum size will be set
+ * @param minSize the minimum size for this widget
+ */
+ public void setWidgetMinSize(Widget child, int minSize) {
+ Splitter splitter = getAssociatedSplitter(child);
+ splitter.setMinSize(minSize);
+ }
+
+ private void addSplitter() {
+ assert getChildren().size() > 0 : "Can't add a splitter before any children";
+ assert getCenter() == null : "Can't add a splitter after the CENTER widget";
+
+ Widget lastChild = getChildren().get(getChildren().size() - 1);
+ LayoutData lastChildLayout = (LayoutData) lastChild.getLayoutData();
+ Splitter splitter;
+ switch (lastChildLayout.direction) {
+ case WEST:
+ splitter = new HSplitter(lastChild, false);
+ break;
+ case EAST:
+ splitter = new HSplitter(lastChild, true);
+ break;
+ case NORTH:
+ splitter = new VSplitter(lastChild, false);
+ break;
+ case SOUTH:
+ splitter = new VSplitter(lastChild, true);
+ break;
+ default:
+ assert false : "Unexpected direction";
+ return;
+ }
+
+ super.add(splitter, lastChildLayout.direction, SPLITTER_SIZE);
+ }
+
+ private Splitter getAssociatedSplitter(Widget child) {
+ // If a widget has a next sibling, it must be a splitter, because the only
+ // widget that *isn't* followed by a splitter must be the CENTER, which has
+ // no associated splitter.
+ int idx = getWidgetIndex(child);
+ if (idx < getWidgetCount() - 2) {
+ Widget splitter = getWidget(idx + 1);
+ assert splitter instanceof Splitter : "Expected child widget to be splitter";
+ return (Splitter) splitter;
+ }
+ return null;
+ }
+}
diff --git a/user/src/com/google/gwt/user/client/ui/StackLayoutPanel.java b/user/src/com/google/gwt/user/client/ui/StackLayoutPanel.java
new file mode 100644
index 0000000..a280d32
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/StackLayoutPanel.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.client.ui;
+
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.layout.client.Layout.AnimationCallback;
+import com.google.gwt.layout.client.Layout.Layer;
+import com.google.gwt.user.client.Event;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * A panel that stacks its children vertically, displaying only one at a time,
+ * with a header for each child which the user can click to display.
+ *
+ * <p>
+ * This widget will <em>only</em> work in standards mode, which requires
+ * that the HTML page in which it is run have an explicit <!DOCTYPE>
+ * declaration.
+ * </p>
+ *
+ * <p>
+ * NOTE: This class is still very new, and its interface may change without
+ * warning. Use at your own risk.
+ * </p>
+ *
+ * <p>
+ * <h3>Example</h3>
+ * {@example com.google.gwt.examples.StackLayoutPanelExample}
+ * </p>
+ *
+ * TODO(jgw):
+ * - implement insert().
+ * - add() methods with default widgets for headers.
+ * - some way to get the header widget associated with a child.
+ * - make animation configurable (with {@link HasAnimation}).
+ * - default style.
+ */
+public class StackLayoutPanel extends Composite implements HasWidgets,
+ RequiresLayout, RequiresResize, ProvidesResize {
+
+ private class ClickWrapper extends Composite {
+ private Widget target;
+
+ public ClickWrapper(Widget target, Widget wrappee) {
+ this.target = target;
+ initWidget(wrappee);
+ sinkEvents(Event.ONCLICK);
+ }
+
+ @Override
+ public void onBrowserEvent(Event event) {
+ if (event.getTypeInt() == Event.ONCLICK) {
+ showWidget(target);
+ }
+ }
+ }
+
+ private static class LayoutData {
+ public double headerSize;
+ public Widget header;
+ public Widget widget;
+ public Layer widgetLayer;
+ public Layer headerLayer;
+
+ public LayoutData(Widget widget, Widget header, double headerSize,
+ Layer widgetLayer, Layer headerLayer) {
+ this.widget = widget;
+ this.header = header;
+ this.headerSize = headerSize;
+ this.widgetLayer = widgetLayer;
+ this.headerLayer = headerLayer;
+ }
+ }
+
+ private static final int ANIMATION_TIME = 250;
+
+ private LayoutPanel layoutPanel;
+ private Unit unit;
+ private ArrayList<LayoutData> layoutData = new ArrayList<LayoutData>();
+ private Widget visibleWidget;
+
+ /**
+ * Creates an empty stack panel.
+ *
+ * @param unit the unit to be used for layout
+ */
+ public StackLayoutPanel(Unit unit) {
+ this.unit = unit;
+ initWidget(layoutPanel = new LayoutPanel());
+ }
+
+ public void add(Widget w) {
+ assert false : "Single-argument add() is not supported for this widget";
+ }
+
+ /**
+ * Adds a child widget to this stack, along with a widget representing the
+ * stack header.
+ *
+ * @param widget the child widget to be added
+ * @param header the header widget
+ * @param headerSize the size of the header widget
+ */
+ public void add(Widget widget, Widget header, double headerSize) {
+ ClickWrapper wrapper = new ClickWrapper(widget, header);
+ layoutPanel.add(wrapper);
+ layoutPanel.add(widget);
+
+ Layer headerLayer = layoutPanel.getLayer(wrapper);
+ headerLayer.setLeftRight(0, Unit.PX, 0, Unit.PX);
+
+ Layer widgetLayer = layoutPanel.getLayer(widget);
+ widgetLayer.setLeftRight(0, Unit.PX, 0, Unit.PX);
+
+ LayoutData data = new LayoutData(widget, wrapper, headerSize, widgetLayer,
+ headerLayer);
+ layoutData.add(data);
+
+ if (visibleWidget == null) {
+ visibleWidget = widget;
+ }
+ }
+
+ public void clear() {
+ layoutPanel.clear();
+ visibleWidget = null;
+ }
+
+ public Iterator<Widget> iterator() {
+ return new Iterator<Widget>() {
+ int i = 0, last = -1;
+
+ public boolean hasNext() {
+ return i < layoutData.size();
+ }
+
+ public Widget next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ return layoutData.get(last = i++).widget;
+ }
+
+ public void remove() {
+ if (last < 0) {
+ throw new IllegalStateException();
+ }
+
+ StackLayoutPanel.this.remove(layoutData.get(last).widget);
+ i = last;
+ last = -1;
+ }
+ };
+ }
+
+ public void layout() {
+ layout(0);
+ }
+
+ public void layout(int duration) {
+ layout(duration, null);
+ }
+
+ public void layout(int duration, AnimationCallback callback) {
+ int top = 0, bottom = 0;
+ int i = 0, visibleIndex = -1;
+ for (; i < layoutData.size(); ++i) {
+ LayoutData data = layoutData.get(i);
+ data.headerLayer.setTopHeight(top, unit, data.headerSize, unit);
+
+ top += data.headerSize;
+
+ data.widgetLayer.setTopHeight(top, unit, 0, unit);
+
+ if (data.widget == visibleWidget) {
+ visibleIndex = i;
+ break;
+ }
+ }
+
+ assert visibleIndex != -1;
+
+ for (int j = layoutData.size() - 1; j > i; --j) {
+ LayoutData data = layoutData.get(j);
+ data.headerLayer.setBottomHeight(bottom, unit, data.headerSize, unit);
+ data.widgetLayer.setBottomHeight(bottom, unit, 0, unit);
+ bottom += data.headerSize;
+ }
+
+ LayoutData data = layoutData.get(visibleIndex);
+ data.widgetLayer.setTopBottom(top, unit, bottom, unit);
+
+ layoutPanel.layout(duration, callback);
+ }
+
+ public void onResize() {
+ layoutPanel.onResize();
+ }
+
+ public boolean remove(Widget child) {
+ if (child.getParent() != this) {
+ return false;
+ }
+
+ LayoutData data = (LayoutData) child.getLayoutData();
+ layoutPanel.remove(data.header);
+ layoutPanel.remove(child);
+ return true;
+ }
+
+ /**
+ * Shows the specified widget.
+ *
+ * @param widget the child widget to be shown.
+ */
+ public void showWidget(Widget widget) {
+ visibleWidget = widget;
+ layout(ANIMATION_TIME);
+ }
+}
diff --git a/user/test/com/google/gwt/uibinder/sample/client/UiBinderTest.java b/user/test/com/google/gwt/uibinder/sample/client/UiBinderTest.java
index 82a842b..e2ba40c 100644
--- a/user/test/com/google/gwt/uibinder/sample/client/UiBinderTest.java
+++ b/user/test/com/google/gwt/uibinder/sample/client/UiBinderTest.java
@@ -24,6 +24,7 @@
import com.google.gwt.junit.DoNotRunWith;
import com.google.gwt.junit.Platform;
import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.DockPanel;
@@ -235,6 +236,19 @@
domUi.root.getClassName());
}
+ interface Bundle extends ClientBundle {
+ @Source("WidgetBasedUi.css")
+ public WidgetBasedUi.Style style();
+ }
+
+ public void testNoOverrideInheritedSharedCssClasses() {
+ Bundle bundle = GWT.create(Bundle.class);
+ WidgetBasedUi ui = GWT.create(WidgetBasedUi.class);
+ String publicStyle = bundle.style().menuBar();
+ String privateStyle = ui.myStyle.menuBar();
+ assertEquals(publicStyle, privateStyle);
+ }
+
public void suppressedForIEfail_testNonXmlEntities() {
// This fragment includes both translated and non-translated strings
ParagraphElement mainParagraph = widgetUi.main;
@@ -251,6 +265,22 @@
assertTrue(((HTML) north).getHTML().contains("Title area"));
}
+ public void testPrivateStyleFromExternalCss() {
+ ParagraphElement p = widgetUi.privateStyleParagraph;
+ assertTrue("Some kind of class should be set", p.getClassName().length() > 0);
+ }
+
+ public void testPrivateStylesFromInlineCss() {
+ ParagraphElement p = widgetUi.reallyPrivateStyleParagraph;
+ assertTrue("Some kind of class should be set",
+ p.getClassName().length() > 0);
+ assertFalse("Should be a different style than privateStyleParagraph's",
+ widgetUi.privateStyleParagraph.getClassName().equals(p.getClassName()));
+
+ assertTrue("Some kind of class should be set",
+ widgetUi.totallyPrivateStyleSpan.getClassName().length() > 0);
+ }
+
@DoNotRunWith(Platform.Htmlunit)
public void testRadioButton() {
RadioButton able = widgetUi.myRadioAble;
diff --git a/user/test/com/google/gwt/user/client/ui/HTMLPanelTest.java b/user/test/com/google/gwt/user/client/ui/HTMLPanelTest.java
index 9409b7d..94fa845 100644
--- a/user/test/com/google/gwt/user/client/ui/HTMLPanelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/HTMLPanelTest.java
@@ -124,7 +124,7 @@
*/
public void testCustomRootTagAsTable() {
HTMLPanel hp = new HTMLPanel("table",
- "<tr><td>Hello <span id='labelHere'></span></td></tr></table>");
+ "<tr><td>Hello <span id='labelHere'></span></td></tr>");
InlineLabel label = new InlineLabel("World");
hp.addAndReplaceElement(label, "labelHere");