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");