Fixes bad code gen on non-widget children in DockLayoutPanelParser, and allows
<g:center> to appear anywhere.
To allow real unit testing, refactored UiBinderWriter to be a bit more DI. This
included moving w3c parsing knowledge outside of the writer. In the process made
the w3c dom test helper into the actual production code--it was nearly identical
anyway, and now test and prod won't drift.
This took a little while. But the next one won't.
Review by jgw, scottb
http://gwt-code-reviews.appspot.com/93806
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6714 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/uibinder/parsers/DockLayoutPanelParser.java b/user/src/com/google/gwt/uibinder/parsers/DockLayoutPanelParser.java
index 0bf38f5..0ec22f9 100644
--- a/user/src/com/google/gwt/uibinder/parsers/DockLayoutPanelParser.java
+++ b/user/src/com/google/gwt/uibinder/parsers/DockLayoutPanelParser.java
@@ -39,6 +39,16 @@
*/
public class DockLayoutPanelParser implements ElementParser {
+ private static class CenterChild {
+ final String widgetName;
+ final XMLElement child;
+
+ public CenterChild(XMLElement child, String widgetName) {
+ this.widgetName = widgetName;
+ this.child = child;
+ }
+ }
+
private static final Map<String, String> DOCK_NAMES = new HashMap<String, String>();
static {
@@ -61,30 +71,43 @@
writer.getOracle().findType(DockLayoutPanel.class.getName()), unit);
}
+ CenterChild center = null;
+
// Parse children.
for (XMLElement child : elem.consumeChildElements()) {
// Make sure the element is one of the fixed set of valid directions.
if (!isValidChildElement(elem, child)) {
writer.die(
- "In %1$s, child must be one of " +
- "<%2$s:north>, <%2$s:south>, <%2$s:east>, <%2$s:west> or <%2$s:center>, " +
- "but found %3$s",
- elem, elem.getPrefix(), child);
+ "In %1$s, child must be one of "
+ + "<%2$s:north>, <%2$s:south>, <%2$s:east>, <%2$s:west> or <%2$s:center>, "
+ + "but found %3$s", elem, elem.getPrefix(), child);
}
// Consume the single widget element.
XMLElement widget = child.consumeSingleChildElement();
- String childFieldName = writer.parseElementToField(widget);
+ if (!writer.isWidgetElement(widget)) {
+ writer.die("In %s, %s must contain a widget, but found %s", elem, child,
+ widget);
+ }
+ String widgetName = writer.parseElementToField(widget);
- if (requiresSize(child)) {
+ if (child.getLocalName().equals("center")) {
+ if (center != null) {
+ writer.die("In %s, only one <%s:center> is allowed", elem,
+ elem.getPrefix());
+ }
+ center = new CenterChild(child, widgetName);
+ } else {
String size = child.consumeDoubleAttribute("size");
writer.addStatement("%s.%s(%s, %s);", fieldName, addMethodName(child),
- childFieldName, size);
- } else {
- writer.addStatement("%s.%s(%s);", fieldName, addMethodName(child),
- childFieldName);
+ widgetName, size);
}
}
+
+ if (center != null) {
+ writer.addStatement("%s.%s(%s);", fieldName, addMethodName(center.child),
+ center.widgetName);
+ }
}
private String addMethodName(XMLElement elem) {
@@ -102,8 +125,8 @@
private boolean isValidChildElement(XMLElement parent, XMLElement child) {
String childNsUri = child.getNamespaceUri();
if (childNsUri == null) {
- return false;
- }
+ return false;
+ }
if (!childNsUri.equals(parent.getNamespaceUri())) {
return false;
}
@@ -113,8 +136,4 @@
// Made it through the gauntlet.
return true;
}
-
- private boolean requiresSize(XMLElement elem) {
- return !elem.getLocalName().equals("center");
- }
}
diff --git a/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java b/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java
index 24052c5..72b9044 100644
--- a/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java
@@ -45,6 +45,10 @@
this.name = name;
this.logger = logger;
}
+
+ public String getInitializer() {
+ return initializer;
+ }
public void needs(FieldWriter f) {
needs.add(f);
diff --git a/user/src/com/google/gwt/uibinder/rebind/AttributeParsers.java b/user/src/com/google/gwt/uibinder/rebind/AttributeParsers.java
index 0e5a47c..46d2288 100644
--- a/user/src/com/google/gwt/uibinder/rebind/AttributeParsers.java
+++ b/user/src/com/google/gwt/uibinder/rebind/AttributeParsers.java
@@ -24,7 +24,10 @@
import java.util.HashMap;
import java.util.Map;
-class AttributeParsers {
+/**
+ * Managers access to all implementations of {@link AttributeParser}
+ */
+public class AttributeParsers {
private static final String DOUBLE = "double";
private static final String BOOLEAN = "boolean";
diff --git a/user/src/com/google/gwt/uibinder/rebind/FieldManager.java b/user/src/com/google/gwt/uibinder/rebind/FieldManager.java
index 67418ea..9b7a225 100644
--- a/user/src/com/google/gwt/uibinder/rebind/FieldManager.java
+++ b/user/src/com/google/gwt/uibinder/rebind/FieldManager.java
@@ -26,7 +26,7 @@
* This class handles all {@link FieldWriter} instances created for the current
* template.
*/
-class FieldManager {
+public class FieldManager {
private static final String DUPLICATE_FIELD_ERROR = "Duplicate declaration of field %1$s.";
@@ -46,8 +46,8 @@
/**
* Basic constructor just injects an oracle instance.
*/
- public FieldManager(MortalLogger logger2) {
- this.logger = logger2;
+ public FieldManager(MortalLogger logger) {
+ this.logger = logger;
}
/**
diff --git a/user/src/com/google/gwt/uibinder/rebind/FieldWriter.java b/user/src/com/google/gwt/uibinder/rebind/FieldWriter.java
index a4c2beb..cfab907 100644
--- a/user/src/com/google/gwt/uibinder/rebind/FieldWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/FieldWriter.java
@@ -36,6 +36,8 @@
String getQualifiedSourceName();
+ String getInitializer();
+
/**
* @return the type of this field, or null if this field is of a type that has
* not yet been generated
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java
index 5bd718a..7c6ebc7 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java
@@ -26,7 +26,11 @@
import com.google.gwt.uibinder.rebind.messages.MessagesWriter;
import com.google.gwt.uibinder.rebind.model.ImplicitClientBundle;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXParseException;
+
import java.io.PrintWriter;
+import java.net.URL;
/**
* Generator for implementations of
@@ -36,6 +40,8 @@
private static final String TEMPLATE_SUFFIX = ".ui.xml";
+ static final String BINDER_URI = "urn:ui:com.google.gwt.uibinder";
+
/**
* Given a UiBinder interface, return the path to its ui.xml file, suitable
* for any classloader to find it as a resource.
@@ -103,18 +109,22 @@
}
private void generateOnce(JClassType interfaceType, String implName,
- PrintWriter binderPrintWrier, TreeLogger treeLogger, TypeOracle oracle,
+ PrintWriter binderPrintWriter, TreeLogger treeLogger, TypeOracle oracle,
PrintWriterManager writerManager)
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
MortalLogger logger = new MortalLogger(treeLogger);
String templatePath = deduceTemplateFile(logger, interfaceType);
+ MessagesWriter messages = new MessagesWriter(BINDER_URI, logger,
+ templatePath, interfaceType.getPackage().getName(), implName);
UiBinderWriter uiBinderWriter = new UiBinderWriter(interfaceType, implName,
- templatePath, oracle, logger);
- uiBinderWriter.parseDocument(binderPrintWrier);
+ templatePath, oracle, logger, new FieldManager(logger), messages);
- MessagesWriter messages = uiBinderWriter.getMessages();
+ Document doc = getW3cDoc(logger, templatePath);
+
+ uiBinderWriter.parseDocument(doc, binderPrintWriter);
+
if (messages.hasMessages()) {
messages.write(writerManager.makePrintWriterFor(messages.getMessagesClassName()));
}
@@ -124,4 +134,21 @@
writerManager.commit();
}
+
+ private Document getW3cDoc(MortalLogger logger, String templatePath)
+ throws UnableToCompleteException {
+ URL url = UiBinderGenerator.class.getClassLoader().getResource(templatePath);
+ if (null == url) {
+ logger.die("Unable to find resource: " + templatePath);
+ }
+
+ Document doc = null;
+ try {
+ doc = new W3cDomHelper().documentFor(url);
+ } catch (SAXParseException e) {
+ logger.die("Error parsing XML (line " + e.getLineNumber() + "): "
+ + e.getMessage(), e);
+ }
+ return doc;
+ }
}
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java
index b3391f1..fc67fe6 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java
@@ -110,7 +110,7 @@
writer.die("Bad prefix on <%s:%s>? The root element must be in "
+ "xml namespace \"%s\" (usually with prefix \"ui:\"), "
+ "but this has prefix \"%s\"", elem.getPrefix(),
- elem.getLocalName(), UiBinderWriter.BINDER_URI, elem.getPrefix());
+ elem.getLocalName(), UiBinderGenerator.BINDER_URI, elem.getPrefix());
}
if (!TAG.equals(elem.getLocalName())) {
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
index 5e281fe..9a6e5ae 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
@@ -33,15 +33,9 @@
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;
@@ -50,10 +44,6 @@
import java.util.Locale;
import java.util.Map;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-
/**
* Writer for UiBinder generated classes.
*
@@ -64,7 +54,6 @@
*/
@SuppressWarnings("deprecation")
public class UiBinderWriter {
- static final String BINDER_URI = "urn:ui:com.google.gwt.uibinder";
private static final String PACKAGE_URI_SCHEME = "urn:import:";
// TODO(rjrjr) Another place that we need a general anonymous field
@@ -226,17 +215,17 @@
private final AttributeParsers attributeParsers;
private final BundleAttributeParsers bundleParsers;
- UiBinderWriter(JClassType baseClass, String implClassName,
- String templatePath, TypeOracle oracle, MortalLogger logger)
+ public UiBinderWriter(JClassType baseClass, String implClassName,
+ String templatePath, TypeOracle oracle, MortalLogger logger,
+ FieldManager fieldManager, MessagesWriter messagesWriter)
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);
+ this.fieldManager = fieldManager;
+ this.messages = messagesWriter;
// Check for possible misuse 'GWT.create(UiBinder.class)'
JClassType uibinderItself =
@@ -267,7 +256,6 @@
bundleClass = new ImplicitClientBundle(baseClass.getPackage().getName(),
this.implClassName, CLIENT_BUNDLE_FIELD, logger);
handlerEvaluator = new HandlerEvaluator(ownerClass, logger, oracle);
- fieldManager = new FieldManager(logger);
attributeParsers = new AttributeParsers();
bundleParsers = new BundleAttributeParsers(oracle, gwtPrefix, logger,
@@ -563,7 +551,7 @@
public boolean isBinderElement(XMLElement elem) {
String uri = elem.getNamespaceUri();
- return uri != null && BINDER_URI.equals(uri);
+ return uri != null && UiBinderGenerator.BINDER_URI.equals(uri);
}
public boolean isWidgetElement(XMLElement elem) {
@@ -659,23 +647,16 @@
* Entry point for the code generation logic. It generates the
* implementation's superstructure, and parses the root widget (leading to all
* of its children being parsed as well).
+ * @param doc TODO
*/
- 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);
- }
-
+ void parseDocument(Document doc, PrintWriter printWriter) throws UnableToCompleteException {
JClassType uiBinderClass = getOracle().findType(UiBinder.class.getName());
if (!baseClass.isAssignableTo(uiBinderClass)) {
die(baseClass.getName() + " must implement UiBinder");
}
Element documentElement = doc.getDocumentElement();
- gwtPrefix = documentElement.lookupPrefix(BINDER_URI);
+ gwtPrefix = documentElement.lookupPrefix(UiBinderGenerator.BINDER_URI);
XMLElement elem = new XMLElementProviderImpl(attributeParsers,
bundleParsers, oracle, logger).get(documentElement);
@@ -911,45 +892,6 @@
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
diff --git a/user/src/com/google/gwt/uibinder/rebind/W3cDomHelper.java b/user/src/com/google/gwt/uibinder/rebind/W3cDomHelper.java
new file mode 100644
index 0000000..b275f16
--- /dev/null
+++ b/user/src/com/google/gwt/uibinder/rebind/W3cDomHelper.java
@@ -0,0 +1,78 @@
+/*
+ * 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 org.w3c.dom.Document;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+/**
+ * Simplifies instantiation of the w3c XML parser, in just the style
+ * that UiBinder likes it. Used by both prod and test.
+ */
+public class W3cDomHelper {
+ private final DocumentBuilderFactory factory;
+ private final DocumentBuilder builder;
+
+ public W3cDomHelper() {
+ factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ factory.setExpandEntityReferences(true);
+ try {
+ builder = factory.newDocumentBuilder();
+ } catch (ParserConfigurationException e) {
+ throw new RuntimeException(e);
+ }
+ builder.setEntityResolver(new GwtResourceEntityResolver());
+ }
+
+ /**
+ * Creates an XML document model with the given contents. Nice for testing.
+ *
+ * @param string the document contents
+ */
+ public Document documentFor(String string) throws SAXException,
+ IOException {
+ return builder.parse(new ByteArrayInputStream(string.getBytes()));
+ }
+
+ public Document documentFor(URL url) throws SAXParseException {
+ try {
+ InputStream stream = url.openStream();
+ InputSource input = new InputSource(stream);
+ input.setSystemId(url.toExternalForm());
+
+ 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);
+ }
+ }
+}
diff --git a/user/src/com/google/gwt/uibinder/rebind/XMLElementProvider.java b/user/src/com/google/gwt/uibinder/rebind/XMLElementProvider.java
index 7e47451..0c6b5e0 100644
--- a/user/src/com/google/gwt/uibinder/rebind/XMLElementProvider.java
+++ b/user/src/com/google/gwt/uibinder/rebind/XMLElementProvider.java
@@ -17,6 +17,9 @@
import org.w3c.dom.Element;
-interface XMLElementProvider {
+/**
+ * Implemented by objects that instantiate XMLElement.
+ */
+public interface XMLElementProvider {
XMLElement get(Element e);
}
\ No newline at end of file
diff --git a/user/src/com/google/gwt/uibinder/rebind/XMLElementProviderImpl.java b/user/src/com/google/gwt/uibinder/rebind/XMLElementProviderImpl.java
index 2a6c713..e9c98cf 100644
--- a/user/src/com/google/gwt/uibinder/rebind/XMLElementProviderImpl.java
+++ b/user/src/com/google/gwt/uibinder/rebind/XMLElementProviderImpl.java
@@ -19,7 +19,10 @@
import org.w3c.dom.Element;
-class XMLElementProviderImpl implements XMLElementProvider {
+/**
+ * The default implemenatation of {@link XMLElementProvider}.
+ */
+public class XMLElementProviderImpl implements XMLElementProvider {
private final AttributeParsers attributeParsers;
@SuppressWarnings("deprecation")
// bundleParsers for legacy templates
diff --git a/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java b/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java
index a59d7e2..b56ba29 100644
--- a/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java
+++ b/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java
@@ -15,6 +15,7 @@
*/
package com.google.gwt.uibinder;
+import com.google.gwt.uibinder.parsers.DockLayoutPanelParserTest;
import com.google.gwt.uibinder.parsers.FieldReferenceConverterTest;
import com.google.gwt.uibinder.parsers.IntAttributeParserTest;
import com.google.gwt.uibinder.parsers.StrictAttributeParserTest;
@@ -49,6 +50,7 @@
suite.addTestSuite(OwnerFieldTest.class);
// parsers
+ suite.addTestSuite(DockLayoutPanelParserTest.class);
suite.addTestSuite(FieldReferenceConverterTest.class);
suite.addTestSuite(IntAttributeParserTest.class);
suite.addTestSuite(StrictAttributeParserTest.class);
diff --git a/user/test/com/google/gwt/uibinder/parsers/DockLayoutPanelParserTest.java b/user/test/com/google/gwt/uibinder/parsers/DockLayoutPanelParserTest.java
new file mode 100644
index 0000000..027dd80
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/parsers/DockLayoutPanelParserTest.java
@@ -0,0 +1,285 @@
+/*
+ * 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.parsers;
+
+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.TypeOracle;
+import com.google.gwt.dev.javac.CompilationState;
+import com.google.gwt.dev.javac.impl.MockJavaResource;
+import com.google.gwt.dev.javac.impl.MockResourceOracle;
+import com.google.gwt.dev.util.collect.Lists;
+import com.google.gwt.uibinder.rebind.AttributeParsers;
+import com.google.gwt.uibinder.rebind.FieldManager;
+import com.google.gwt.uibinder.rebind.FieldWriter;
+import com.google.gwt.uibinder.rebind.MortalLogger;
+import com.google.gwt.uibinder.rebind.UiBinderWriter;
+import com.google.gwt.uibinder.rebind.W3cDomHelper;
+import com.google.gwt.uibinder.rebind.XMLElement;
+import com.google.gwt.uibinder.rebind.XMLElementProvider;
+import com.google.gwt.uibinder.rebind.XMLElementProviderImpl;
+import com.google.gwt.uibinder.rebind.messages.MessagesWriter;
+import com.google.gwt.uibinder.test.UiJavaResources;
+import com.google.gwt.user.client.ui.DockLayoutPanel;
+
+import junit.framework.TestCase;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A unit test. Guess what of.
+ */
+public class DockLayoutPanelParserTest extends TestCase {
+ private static class MyUiBinderWriter extends UiBinderWriter {
+ final List<String> statements = new ArrayList<String>();
+ String died;
+
+ public MyUiBinderWriter(JClassType baseClass, String implClassName,
+ String templatePath, TypeOracle oracle, MortalLogger logger,
+ FieldManager fieldManager, MessagesWriter messagesWriter)
+ throws UnableToCompleteException {
+ super(baseClass, implClassName, templatePath, oracle, logger,
+ fieldManager, messagesWriter);
+ }
+
+ @Override
+ public void addStatement(String format, Object... args) {
+ statements.add(String.format(format, args));
+ }
+
+ @Override
+ public String parseElementToField(XMLElement elem)
+ throws UnableToCompleteException {
+ return elem.consumeOpeningTag();
+ }
+
+ @Override
+ public void die(String message, Object... params)
+ throws UnableToCompleteException {
+ noteDeath(String.format(message, params));
+ super.die(message, params);
+ }
+
+ @Override
+ public void die(String message) throws UnableToCompleteException {
+ noteDeath(message);
+ super.die(message);
+ }
+
+ /** Handy place to set a break point and inspect suicide notes. */
+ void noteDeath(String s) {
+ died = s;
+ }
+ }
+
+ private static final W3cDomHelper docHelper = new W3cDomHelper();
+ private static final String BINDER_URI = "binderUri";
+
+ private static final String fieldName = "fieldName";
+
+ // TODO(rjrjr) Move this to JavaResourceBase. Have to do it atomically for
+ // all other tests that define their own Enum.
+ private static final MockJavaResource ENUM = new MockJavaResource(
+ "java.lang.Enum") {
+ @Override
+ protected CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package java.lang;\n");
+ code.append("public abstract class Enum<E extends Enum<E>> {\n");
+ code.append(" protected Enum(String name, int ordinal) {}\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+ private static final MockJavaResource MY_UI_JAVA = new MockJavaResource(
+ "my.Ui") {
+ @Override
+ protected CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package my;\n");
+ code.append("import com.google.gwt.user.client.ui.Widget;\n");
+ code.append("public class Ui {\n");
+ code.append(" public interface BaseClass extends "
+ + "com.google.gwt.uibinder.client.UiBinder<Widget, BaseClass> {}\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+
+ private TypeOracle types;
+ private XMLElementProvider elemProvider;
+ private Document doc;
+ private MyUiBinderWriter writer;
+ private DockLayoutPanelParser parser;
+ private JClassType dockLayoutPanelType;
+ private FieldManager fieldManager;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ MockResourceOracle resources = new MockResourceOracle(getUiResources());
+ CompilationState state = new CompilationState(TreeLogger.NULL, resources);
+ types = state.getTypeOracle();
+ dockLayoutPanelType = types.findType("com.google.gwt.user.client.ui.DockLayoutPanel");
+
+ elemProvider = new XMLElementProviderImpl(new AttributeParsers(), null,
+ types, MortalLogger.NULL);
+
+ fieldManager = new FieldManager(MortalLogger.NULL);
+ fieldManager.registerField(
+ types.findType(DockLayoutPanel.class.getCanonicalName()), fieldName);
+
+ String templatePath = "TemplatePath.ui.xml";
+ String implName = "ImplClass";
+ JClassType baseType = types.findType("my.Ui.BaseClass");
+ MessagesWriter messages = new MessagesWriter(BINDER_URI, MortalLogger.NULL,
+ templatePath, baseType.getPackage().getName(), implName);
+
+ writer = new MyUiBinderWriter(baseType, implName, templatePath, types,
+ MortalLogger.NULL, fieldManager, messages);
+ parser = new DockLayoutPanelParser();
+ }
+
+ public void testHappy() throws UnableToCompleteException, SAXException,
+ IOException {
+ StringBuffer b = new StringBuffer();
+ b.append("<ui:UiBinder xmlns:ui='" + BINDER_URI + "'");
+ b.append(" xmlns:g='urn:import:com.google.gwt.user.client.ui'>");
+ b.append(" <g:DockLayoutPanel unit='EM'>");
+ b.append(" <g:north size='5'>");
+ b.append(" <g:Label id='north'>north</g:Label>");
+ b.append(" </g:north>");
+ b.append(" <g:center>");
+ b.append(" <g:Label id='center'>center</g:Label>");
+ b.append(" </g:center>");
+ b.append(" </g:DockLayoutPanel>");
+ b.append("</ui:UiBinder>");
+
+ String[] expected = {
+ "fieldName.addNorth(<g:Label id='north'>, 5);",
+ "fieldName.add(<g:Label id='center'>);",};
+
+ parser.parse(getElem(b.toString()), fieldName, dockLayoutPanelType, writer);
+ FieldWriter w = fieldManager.lookup(fieldName);
+ assertEquals(
+ "new com.google.gwt.user.client.ui.DockLayoutPanel(com.google.gwt.dom.client.Style.Unit.EM)",
+ w.getInitializer());
+
+ Iterator<String> i = writer.statements.iterator();
+ for (String e : expected) {
+ assertEquals(e, i.next());
+ }
+ assertFalse(i.hasNext());
+ assertNull(writer.died);
+ }
+
+ public void testNiceCenter() throws UnableToCompleteException, SAXException,
+ IOException {
+ StringBuffer b = new StringBuffer();
+ b.append("<ui:UiBinder xmlns:ui='" + BINDER_URI + "'");
+ b.append(" xmlns:g='urn:import:com.google.gwt.user.client.ui'>");
+ b.append(" <g:DockLayoutPanel unit='EM'>");
+ b.append(" <g:center>");
+ b.append(" <g:Label id='center'>center</g:Label>");
+ b.append(" </g:center>");
+ b.append(" <g:north size='5'>");
+ b.append(" <g:Label id='north'>north</g:Label>");
+ b.append(" </g:north>");
+ b.append(" </g:DockLayoutPanel>");
+ b.append("</ui:UiBinder>");
+
+ String[] expected = {
+ "fieldName.addNorth(<g:Label id='north'>, 5);",
+ "fieldName.add(<g:Label id='center'>);",};
+
+ parser.parse(getElem(b.toString()), fieldName, dockLayoutPanelType, writer);
+ FieldWriter w = fieldManager.lookup(fieldName);
+ assertEquals(
+ "new com.google.gwt.user.client.ui.DockLayoutPanel(com.google.gwt.dom.client.Style.Unit.EM)",
+ w.getInitializer());
+
+ Iterator<String> i = writer.statements.iterator();
+ for (String e : expected) {
+ assertEquals(e, i.next());
+ }
+ assertFalse(i.hasNext());
+ }
+
+ public void testTooManyCenters() throws SAXException, IOException {
+ StringBuffer b = new StringBuffer();
+ b.append("<ui:UiBinder xmlns:ui='" + BINDER_URI + "'");
+ b.append(" xmlns:g='urn:import:com.google.gwt.user.client.ui'>");
+ b.append(" <g:DockLayoutPanel unit='EM'>");
+ b.append(" <g:center>");
+ b.append(" <g:Label id='center'>center</g:Label>");
+ b.append(" </g:center>");
+ b.append(" <g:center>");
+ b.append(" <g:Label id='centerAlso'>centaur</g:Label>");
+ b.append(" </g:center>");
+ b.append(" </g:DockLayoutPanel>");
+ b.append("</ui:UiBinder>");
+
+ try {
+ parser.parse(getElem(b.toString()), fieldName, dockLayoutPanelType,
+ writer);
+ fail();
+ } catch (UnableToCompleteException e) {
+ assertNotNull(writer.died);
+ }
+ }
+
+ public void testBadChild() throws SAXException, IOException {
+ StringBuffer b = new StringBuffer();
+ b.append("<ui:UiBinder xmlns:ui='" + BINDER_URI + "'");
+ b.append(" xmlns:g='urn:import:com.google.gwt.user.client.ui'>");
+ b.append(" <g:DockLayoutPanel unit='EM'>");
+ b.append(" <g:west><foo/></g:west>");
+ b.append(" </g:DockLayoutPanel>");
+ b.append("</ui:UiBinder>");
+
+ try {
+ parser.parse(getElem(b.toString()), fieldName, dockLayoutPanelType,
+ writer);
+ fail();
+ } catch (UnableToCompleteException e) {
+ assertNotNull(writer.died);
+ }
+ }
+
+ MockJavaResource[] getUiResources() {
+ List<MockJavaResource> rtn = Lists.create(UiJavaResources.getUiResources());
+ rtn.add(MY_UI_JAVA);
+ rtn.add(ENUM);
+ return rtn.toArray(new MockJavaResource[rtn.size()]);
+ }
+
+ private XMLElement getElem(String string) throws SAXException, IOException {
+ doc = docHelper.documentFor(string);
+ Element w3cElem = (Element) doc.getDocumentElement().getElementsByTagName(
+ "g:DockLayoutPanel").item(0);
+ XMLElement elem = elemProvider.get(w3cElem);
+ return elem;
+ }
+}
diff --git a/user/test/com/google/gwt/uibinder/rebind/DocumentTestHelp.java b/user/test/com/google/gwt/uibinder/rebind/DocumentTestHelp.java
deleted file mode 100644
index f82bfac..0000000
--- a/user/test/com/google/gwt/uibinder/rebind/DocumentTestHelp.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2007 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 org.w3c.dom.Document;
-import org.xml.sax.SAXException;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-
-/**
- * Support methods for testing uibinder.
- */
-class DocumentTestHelp {
- /**
- * Creates an XML document model with the given contents.
- *
- * @param string the document contents
- */
- public static Document documentForString(String string)
- throws ParserConfigurationException, SAXException, IOException {
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- factory.setNamespaceAware(true);
- factory.setExpandEntityReferences(true);
- DocumentBuilder builder = factory.newDocumentBuilder();
- Document doc = builder.parse(new ByteArrayInputStream(string.getBytes()));
- return doc;
- }
-
- /**
- * Not instantiable.
- */
- private DocumentTestHelp() {
- }
-}
diff --git a/user/test/com/google/gwt/uibinder/rebind/XMLElementTest.java b/user/test/com/google/gwt/uibinder/rebind/XMLElementTest.java
index f47e96e..e448c81 100644
--- a/user/test/com/google/gwt/uibinder/rebind/XMLElementTest.java
+++ b/user/test/com/google/gwt/uibinder/rebind/XMLElementTest.java
@@ -34,35 +34,35 @@
import java.util.HashSet;
import java.util.Set;
-import javax.xml.parsers.ParserConfigurationException;
-
/**
* Tests XMLElement.
*/
public class XMLElementTest extends TestCase {
private static final String STRING_WITH_DOUBLEQUOTE = "I have a \" quote in me";
+ private static final W3cDomHelper docHelper = new W3cDomHelper();
private Document doc;
private Element item;
private XMLElement elm;
private XMLElementProvider elemProvider;
-
- TypeOracle oracle;
-
+ private TypeOracle oracle;
+
@Override
public void setUp() throws Exception {
super.setUp();
- init("<doc><elm attr1=\"attr1Value\" attr2=\"attr2Value\"/></doc>");
-
MockResourceOracle resourceOracle = new MockResourceOracle(
JavaResourceBase.getStandardResources());
-
CompilationState state = new CompilationState(TreeLogger.NULL,
resourceOracle);
oracle = state.getTypeOracle();
+
+ elemProvider = new XMLElementProviderImpl(new AttributeParsers(), null,
+ oracle, MortalLogger.NULL);
+
+ init("<doc><elm attr1=\"attr1Value\" attr2=\"attr2Value\"/></doc>");
}
- public void testConsumeBoolean() throws ParserConfigurationException,
- SAXException, IOException, UnableToCompleteException {
+ public void testConsumeBoolean() throws SAXException, IOException,
+ UnableToCompleteException {
init("<doc><elm yes='true' no='false' "
+ "fnord='fnord' ref='{foo.bar.baz}'/></doc>");
@@ -84,8 +84,8 @@
}
}
- public void testConsumeBooleanConstant() throws ParserConfigurationException,
- SAXException, IOException, UnableToCompleteException {
+ public void testConsumeBooleanConstant() throws SAXException, IOException,
+ UnableToCompleteException {
init("<doc><elm yes='true' no='false' "
+ "fnord='fnord' ref='{foo.bar.baz}'/></doc>");
@@ -113,7 +113,7 @@
}
public void testConsumeDouble() throws UnableToCompleteException,
- ParserConfigurationException, SAXException, IOException {
+ SAXException, IOException {
init("<doc><elm minus='-123.45' plus='123.45' minus-one='-1' "
+ "plus-one='1' fnord='fnord' ref='{foo.bar.baz}'/></doc>");
assertEquals("1", elm.consumeDoubleAttribute("plus-one"));
@@ -167,9 +167,8 @@
}
}
- public void testConsumeSingleChildElementEmpty()
- throws ParserConfigurationException, SAXException, IOException,
- UnableToCompleteException {
+ public void testConsumeSingleChildElementEmpty() throws SAXException,
+ IOException, UnableToCompleteException {
try {
elm.consumeSingleChildElement();
fail("Should throw on single child element");
@@ -220,10 +219,9 @@
}
public void testNoEndTags() throws Exception {
- Document doc = DocumentTestHelp.documentForString("<doc><br/></doc>");
-
- Element item = (Element) doc.getDocumentElement().getElementsByTagName("br").item(
- 0);
+ doc = docHelper.documentFor("<doc><br/></doc>");
+ Element documentElement = doc.getDocumentElement();
+ Element item = (Element) documentElement.getElementsByTagName("br").item(0);
XMLElement elm = elemProvider.get(item);
assertEquals("br", item.getTagName());
assertEquals("", elm.getClosingTag());
@@ -234,14 +232,10 @@
item.appendChild(t);
}
- private void init(final String domString)
- throws ParserConfigurationException, SAXException, IOException {
- doc = DocumentTestHelp.documentForString(domString);
+ private void init(final String domString) throws SAXException, IOException {
+ doc = docHelper.documentFor(domString);
item = (Element) doc.getDocumentElement().getElementsByTagName("elm").item(
0);
-
- elemProvider = new XMLElementProviderImpl(new AttributeParsers(), null,
- oracle, MortalLogger.NULL);
elm = elemProvider.get(item);
}
}
diff --git a/user/test/com/google/gwt/uibinder/test/UiJavaResources.java b/user/test/com/google/gwt/uibinder/test/UiJavaResources.java
new file mode 100644
index 0000000..53e847e
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/UiJavaResources.java
@@ -0,0 +1,112 @@
+/*
+ * 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.test;
+
+import com.google.gwt.dev.javac.impl.JavaResourceBase;
+import com.google.gwt.dev.javac.impl.MockJavaResource;
+import com.google.gwt.dev.util.collect.Lists;
+
+import java.util.List;
+
+/**
+ * A paired down set of GWT widget Java source files for code generator testing.
+ */
+public class UiJavaResources {
+
+ public static final MockJavaResource WIDGET = new MockJavaResource(
+ "com.google.gwt.user.client.ui.Widget") {
+ @Override
+ protected CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package com.google.gwt.user.client.ui;\n");
+ code.append("public class Widget {\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+ public static final MockJavaResource DOCK_LAYOUT_PANEL = new MockJavaResource(
+ "com.google.gwt.user.client.ui.DockLayoutPanel") {
+ @Override
+ protected CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package com.google.gwt.user.client.ui;\n");
+ code.append("public class DockLayoutPanel extends Widget {\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+ public static final MockJavaResource SPLIT_LAYOUT_PANEL = new MockJavaResource(
+ "com.google.gwt.user.client.ui.SplitLayoutPanel") {
+ @Override
+ protected CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package com.google.gwt.user.client.ui;\n");
+ code.append("public class SplitLayoutPanel extends DockLayoutPanel {\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+ public static final MockJavaResource LABEL = new MockJavaResource(
+ "com.google.gwt.user.client.ui.Label") {
+ @Override
+ protected CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package com.google.gwt.user.client.ui;\n");
+ code.append("public class Label extends Widget {\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+ public static final MockJavaResource UI_BINDER = new MockJavaResource(
+ "com.google.gwt.uibinder.client.UiBinder") {
+ @Override
+ protected CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package com.google.gwt.uibinder.client;\n");
+ code.append("public interface UiBinder<U, O> {\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+ public static final MockJavaResource STYLE = new MockJavaResource(
+ "com.google.gwt.dom.client.Style") {
+ @Override
+ protected CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package com.google.gwt.dom.client;\n");
+ code.append("public class Style {\n");
+ code.append(" public enum Unit { PX, PT, EM };\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+
+ /**
+ * @return a pale reflection of com.google.gwt.user.ui, plus
+ * {@link JavaResourceBase#getStandardResources}
+ */
+ public static MockJavaResource[] getUiResources() {
+ MockJavaResource[] base = JavaResourceBase.getStandardResources();
+ List<MockJavaResource> rtn = Lists.create(base);
+ rtn.add(WIDGET);
+ rtn.add(DOCK_LAYOUT_PANEL);
+ rtn.add(SPLIT_LAYOUT_PANEL);
+ rtn.add(LABEL);
+ rtn.add(UI_BINDER);
+ rtn.add(STYLE);
+ return rtn.toArray(new MockJavaResource[rtn.size()]);
+ }
+}
\ No newline at end of file