Adds design time hooks to UiBinder for GWT Designer, or any other tool that can find a use for them. See discussion in http://groups.google.com/group/google-web-toolkit-contributors/browse_thread/thread/3e9b2193531fb0b4/69829d19f0573be1 Patch by konstantin.scheglov@gmail.com Review by rjrjr@google.com http://gwt-code-reviews.appspot.com/693801/show Review by: robertvawter@google.com git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8384 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/BeanParser.java b/user/src/com/google/gwt/uibinder/elementparsers/BeanParser.java index e73eff3..025cea0 100644 --- a/user/src/com/google/gwt/uibinder/elementparsers/BeanParser.java +++ b/user/src/com/google/gwt/uibinder/elementparsers/BeanParser.java
@@ -21,6 +21,7 @@ import com.google.gwt.core.ext.typeinfo.JMethod; import com.google.gwt.core.ext.typeinfo.JParameter; import com.google.gwt.core.ext.typeinfo.JType; +import com.google.gwt.uibinder.rebind.DesignTimeUtils; import com.google.gwt.uibinder.rebind.UiBinderWriter; import com.google.gwt.uibinder.rebind.XMLAttribute; import com.google.gwt.uibinder.rebind.XMLElement; @@ -105,8 +106,7 @@ if (setter == null || !(params.length == 1) || !isString(writer, params[0].getType())) { - writer.die(elem, "No method found to apply message attribute %s", - key); + writer.die(elem, "No method found to apply message attribute %s", key); } else { setterValues.put(key, value); } @@ -178,9 +178,12 @@ } } - for (String propertyName : setterValues.keySet()) { + for (Map.Entry<String, String> entry : setterValues.entrySet()) { + String propertyName = entry.getKey(); + String value = entry.getValue(); writer.addStatement("%s.set%s(%s);", fieldName, initialCap(propertyName), - setterValues.get(propertyName)); + value); + DesignTimeUtils.putAttribute(writer, elem, propertyName, value); } }
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/CellPanelParser.java b/user/src/com/google/gwt/uibinder/elementparsers/CellPanelParser.java index 21d0eb0..75b06d8 100644 --- a/user/src/com/google/gwt/uibinder/elementparsers/CellPanelParser.java +++ b/user/src/com/google/gwt/uibinder/elementparsers/CellPanelParser.java
@@ -17,6 +17,7 @@ import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.uibinder.rebind.DesignTimeUtils; import com.google.gwt.uibinder.rebind.UiBinderWriter; import com.google.gwt.uibinder.rebind.XMLElement; import com.google.gwt.user.client.ui.HasHorizontalAlignment.HorizontalAlignmentConstant; @@ -54,12 +55,16 @@ String value = cellElem.consumeAttribute(HALIGN_ATTR, hAlignConstantType); writer.addStatement("%1$s.setCellHorizontalAlignment(%2$s, %3$s);", fieldName, childFieldName, value); + DesignTimeUtils.putAttribute(writer, cellElem, "*Cell." + HALIGN_ATTR, + value); } if (cellElem.hasAttribute(VALIGN_ATTR)) { String value = cellElem.consumeAttribute(VALIGN_ATTR, vAlignConstantType); writer.addStatement("%1$s.setCellVerticalAlignment(%2$s, %3$s);", fieldName, childFieldName, value); + DesignTimeUtils.putAttribute(writer, cellElem, "*Cell." + VALIGN_ATTR, + value); } // Parse width and height attributes. @@ -67,12 +72,16 @@ String value = cellElem.consumeStringAttribute(WIDTH_ATTR); writer.addStatement("%1$s.setCellWidth(%2$s, %3$s);", fieldName, childFieldName, value); + DesignTimeUtils.putAttribute(writer, cellElem, "*Cell." + WIDTH_ATTR, + value); } if (cellElem.hasAttribute(HEIGHT_ATTR)) { String value = cellElem.consumeStringAttribute(HEIGHT_ATTR); writer.addStatement("%1$s.setCellHeight(%2$s, %3$s);", fieldName, childFieldName, value); + DesignTimeUtils.putAttribute(writer, cellElem, "*Cell." + HEIGHT_ATTR, + value); } }
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/UIObjectParser.java b/user/src/com/google/gwt/uibinder/elementparsers/UIObjectParser.java index c60f92b..fea1a32 100644 --- a/user/src/com/google/gwt/uibinder/elementparsers/UIObjectParser.java +++ b/user/src/com/google/gwt/uibinder/elementparsers/UIObjectParser.java
@@ -17,38 +17,42 @@ import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.uibinder.rebind.DesignTimeUtils; import com.google.gwt.uibinder.rebind.UiBinderWriter; import com.google.gwt.uibinder.rebind.XMLElement; /** - * Parser of all UIObject types. Provides parsing of debugId, - * addStyleNames, addStyleDependentNames. Also handles - * other setStyle* calls to ensure they happen before the addStyle* - * calls. + * Parser of all UIObject types. Provides parsing of debugId, addStyleNames, + * addStyleDependentNames. Also handles other setStyle* calls to ensure they + * happen before the addStyle* calls. */ public class UIObjectParser implements ElementParser { public void parse(XMLElement elem, String fieldName, JClassType type, UiBinderWriter writer) throws UnableToCompleteException { + DesignTimeUtils.handleUIObject(writer, elem, fieldName); + String debugId = elem.consumeStringAttribute("debugId", null); if (null != debugId) { writer.addStatement("%s.ensureDebugId(%s);", fieldName, debugId); } - + String styleName = elem.consumeStringAttribute("styleName", null); - String stylePrimaryName = elem.consumeStringAttribute("stylePrimaryName", null); + String stylePrimaryName = elem.consumeStringAttribute("stylePrimaryName", + null); if (null != styleName && null != stylePrimaryName) { writer.die(elem, "Cannot set both \"styleName\" " + "and \"stylePrimaryName\""); } - + if (null != styleName) { writer.addStatement("%s.setStyleName(%s);", fieldName, styleName); } if (null != stylePrimaryName) { - writer.addStatement("%s.setStylePrimaryName(%s);", fieldName, stylePrimaryName); + writer.addStatement("%s.setStylePrimaryName(%s);", fieldName, + stylePrimaryName); } String[] extraStyleNames = elem.consumeStringArrayAttribute("addStyleNames");
diff --git a/user/src/com/google/gwt/uibinder/rebind/DesignTimeUtils.java b/user/src/com/google/gwt/uibinder/rebind/DesignTimeUtils.java new file mode 100644 index 0000000..00668a5 --- /dev/null +++ b/user/src/com/google/gwt/uibinder/rebind/DesignTimeUtils.java
@@ -0,0 +1,133 @@ +/* + * Copyright 2010 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.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.beans.Beans; +import java.util.WeakHashMap; + +/** + * Utilities used for implementing design time support of UiBinder. + */ +public class DesignTimeUtils { + private static final WeakHashMap<Element, String> elementPaths = new WeakHashMap<Element, String>(); + + /** + * Adds declarations for design time artifacts. + */ + public static void addDeclarations(IndentedWriter w) { + if (!isDesignTime()) { + return; + } + w.write("public static interface DTObjectHandler {void handle(String path, Object object);}"); + w.write("public DTObjectHandler dtObjectHandler;"); + w.write("public final java.util.Map dtAttributes = new java.util.HashMap();"); + } + + /** + * @return the name of "Impl", unique each time if it is design time. + */ + public static String getImplName(String implName) { + if (isDesignTime()) { + implName += "_designTime" + System.currentTimeMillis(); + } + return implName; + } + + /** + * @return the path of given {@link Element}. + */ + public static String getPath(Element element) { + return elementPaths.get(element); + } + + /** + * @return the design time content of <code>*.ui.xml</code> template to parse, + * or <code>null</code> if not design time, or this template is not + * under design. + */ + public static String getTemplateContent(String path) { + if (DesignTimeUtils.isDesignTime()) { + return System.getProperty("gwt.UiBinder.designTime " + path); + } + return null; + } + + /** + * Notifies tool about <code>UIObject</code> creation. + */ + public static void handleUIObject(Statements writer, + XMLElement elem, String fieldName) { + if (!isDesignTime()) { + return; + } + writer.addStatement( + "if (dtObjectHandler != null) dtObjectHandler.handle(\"%s\", %s);", + elem.getDesignTimePath(), fieldName); + } + + /** + * @return <code>true</code> if UiBinder works now in design time, so + * additional information should be provided in generated classes. + */ + public static boolean isDesignTime() { + return Beans.isDesignTime(); + } + + /** + * Remembers value of attribute, for given {@link XMLElement}. + */ + public static void putAttribute(Statements writer, + XMLElement elem, String name, String value) { + if (!isDesignTime()) { + return; + } + String path = elem.getDesignTimePath(); + String key = path + " " + name; + writer.addStatement("dtAttributes.put(\"%s\", %s);", key, value); + } + + /** + * Fills {@value #elementPaths} with paths for given and child {@link Element} + * s. + */ + public static void rememberPathForElements(Document doc) { + if (!isDesignTime()) { + return; + } + rememberPathForElements(doc.getDocumentElement(), "0"); + } + + /** + * Recursive implementation of {@link #rememberPathForElements(Document)}. + */ + private static void rememberPathForElements(Element element, String path) { + elementPaths.put(element, path); + NodeList childNodes = element.getChildNodes(); + int elementIndex = 0; + for (int i = 0; i < childNodes.getLength(); ++i) { + Node childNode = childNodes.item(i); + if (childNode.getNodeType() == Node.ELEMENT_NODE) { + Element childElement = (Element) childNode; + rememberPathForElements(childElement, path + "/" + elementIndex++); + } + } + } +} \ No newline at end of file
diff --git a/user/src/com/google/gwt/uibinder/rebind/Statements.java b/user/src/com/google/gwt/uibinder/rebind/Statements.java new file mode 100644 index 0000000..acc1b8f --- /dev/null +++ b/user/src/com/google/gwt/uibinder/rebind/Statements.java
@@ -0,0 +1,57 @@ +/* + * Copyright 2010 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; + +/** + * Accepts Java statements to be included in generated UiBinder implementations. + */ +public interface Statements { + + Statements EMPTY = new Statements() { + + public void addDetachStatement(String format, Object... args) { + } + + public void addInitStatement(String format, Object... params) { + } + + public void addStatement(String format, Object... args) { + } + }; + + /** + * Add a statement to be executed right after the current attached element is + * detached. This is useful for doing things that might be expensive while the + * element is attached to the DOM. + * + * @param format + * @param args + * @see #beginAttachedSection(String) + */ + void addDetachStatement(String format, Object... args); + + /** + * Add a statement to be run after everything has been instantiated, in the + * style of {@link String#format}. + */ + void addInitStatement(String format, Object... params); + + /** + * Adds a statement to the block run after fields are declared, in the style + * of {@link String#format}. + */ + void addStatement(String format, Object... args); +}
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java index 49658b9..27b05a6 100644 --- a/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java +++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java
@@ -99,6 +99,8 @@ } String implName = interfaceType.getName().replace('.', '_') + "Impl"; + implName = DesignTimeUtils.getImplName(implName); + String packageName = interfaceType.getPackage().getName(); PrintWriterManager writers = new PrintWriterManager(genCtx, logger, packageName); @@ -126,6 +128,7 @@ messages); Document doc = getW3cDoc(logger, resourceOracle, templatePath); + DesignTimeUtils.rememberPathForElements(doc); uiBinderWriter.parseDocument(doc, binderPrintWriter); @@ -144,19 +147,22 @@ throws UnableToCompleteException { Resource resource = resourceOracle.getResourceMap().get(templatePath); - if (null == resource) { logger.die("Unable to find resource: " + templatePath); } Document doc = null; try { - String content = Util.readStreamAsString(resource.openContents()); - doc = new W3cDomHelper(logger.getTreeLogger(), resourceOracle).documentFor(content, - resource.getPath()); + String content = DesignTimeUtils.getTemplateContent(templatePath); + if (content == null) { + content = Util.readStreamAsString(resource.openContents()); + } + doc = new W3cDomHelper(logger.getTreeLogger(), resourceOracle).documentFor( + content, resource.getPath()); } catch (SAXParseException e) { - logger.die("Error parsing XML (line " + e.getLineNumber() + "): " - + e.getMessage(), e); + logger.die( + "Error parsing XML (line " + e.getLineNumber() + "): " + + e.getMessage(), e); } return doc; }
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java index 54a2b59..592913a 100644 --- a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java +++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
@@ -58,7 +58,7 @@ * TODO(rjrjr): Line numbers in error messages. */ @SuppressWarnings("deprecation") -public class UiBinderWriter { +public class UiBinderWriter implements Statements { private static final String PACKAGE_URI_SCHEME = "urn:import:"; // TODO(rjrjr) Another place that we need a general anonymous field @@ -1180,5 +1180,6 @@ private void writeStatics(IndentedWriter w) { writeStaticMessagesInstance(w); writeStaticBundleInstances(w); + DesignTimeUtils.addDeclarations(w); } }
diff --git a/user/src/com/google/gwt/uibinder/rebind/XMLElement.java b/user/src/com/google/gwt/uibinder/rebind/XMLElement.java index 5f4bb91..7420c3b 100644 --- a/user/src/com/google/gwt/uibinder/rebind/XMLElement.java +++ b/user/src/com/google/gwt/uibinder/rebind/XMLElement.java
@@ -115,7 +115,7 @@ private static final Set<String> NO_END_TAG = new HashSet<String>(); - private static final String[] EMPTY = new String[] {}; + private static final String[] EMPTY = new String[]{}; private static void clearChildren(Element elem) { // TODO(rjrjr) I'm nearly positive that anywhere this is called @@ -257,7 +257,7 @@ */ public String consumeAttributeWithDefault(String name, String defaultValue, JType type) throws UnableToCompleteException { - return consumeAttributeWithDefault(name, defaultValue, new JType[] {type}); + return consumeAttributeWithDefault(name, defaultValue, new JType[]{type}); } /** @@ -494,7 +494,7 @@ */ public String consumeLengthAttribute(String name) throws UnableToCompleteException { - return consumeAttributeWithDefault(name, null, new JType[] { + return consumeAttributeWithDefault(name, null, new JType[]{ getDoubleType(), getUnitType()}); } @@ -755,6 +755,14 @@ } /** + * @return the design time path of this element, in form of indexes from root, + * such as "0/0/1/0". + */ + public String getDesignTimePath() { + return DesignTimeUtils.getPath(elem); + } + + /** * Gets this element's local name (sans namespace prefix). */ public String getLocalName() {
diff --git a/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java b/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java index 38e8cfd..9198de6 100644 --- a/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java +++ b/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java
@@ -34,6 +34,7 @@ import com.google.gwt.uibinder.elementparsers.StackLayoutPanelParserTest; import com.google.gwt.uibinder.elementparsers.TabLayoutPanelParserTest; import com.google.gwt.uibinder.elementparsers.UIObjectParserTest; +import com.google.gwt.uibinder.rebind.DesignTimeUtilsTest; import com.google.gwt.uibinder.rebind.FieldWriterOfGeneratedCssResourceTest; import com.google.gwt.uibinder.rebind.GwtResourceEntityResolverTest; import com.google.gwt.uibinder.rebind.HandlerEvaluatorTest; @@ -59,6 +60,7 @@ suite.addTestSuite(HandlerEvaluatorTest.class); suite.addTestSuite(TokenatorTest.class); suite.addTestSuite(XMLElementTest.class); + suite.addTestSuite(DesignTimeUtilsTest.class); // model suite.addTestSuite(OwnerClassTest.class);
diff --git a/user/test/com/google/gwt/uibinder/rebind/DesignTimeUtilsTest.java b/user/test/com/google/gwt/uibinder/rebind/DesignTimeUtilsTest.java new file mode 100644 index 0000000..02a8402 --- /dev/null +++ b/user/test/com/google/gwt/uibinder/rebind/DesignTimeUtilsTest.java
@@ -0,0 +1,326 @@ +/* + * Copyright 2010 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.dev.javac.impl.MockResourceOracle; + +import junit.framework.TestCase; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXParseException; + +import java.beans.Beans; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; + +/** + * Tests for {@link DesignTimeUtils}. + */ +public class DesignTimeUtilsTest extends TestCase { + private static final W3cDomHelper docHelper = new W3cDomHelper( + TreeLogger.NULL, new MockResourceOracle()); + + //////////////////////////////////////////////////////////////////////////// + // + // Life cycle + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected void tearDown() throws Exception { + Beans.setDesignTime(false); + super.tearDown(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // isDesignTime() + // + /////////////////////////////////////////////////////////////////////////// + /** + * Test for {@link DesignTimeUtils#isDesignTime()}. + */ + public void test_isDesignTime_default() { + assertFalse(DesignTimeUtils.isDesignTime()); + } + + /** + * Test for {@link DesignTimeUtils#isDesignTime()}. + */ + public void test_isDesignTime_designTime() { + Beans.setDesignTime(true); + assertTrue(DesignTimeUtils.isDesignTime()); + } + + //////////////////////////////////////////////////////////////////////////// + // + // addDeclarations() + // + /////////////////////////////////////////////////////////////////////////// + /** + * Test for {@link DesignTimeUtils#addDeclarations(IndentedWriter)}. + */ + public void test_addDeclarations_default() { + String result = call_addDeclarations(); + assertEquals("", result); + } + + /** + * Test for {@link DesignTimeUtils#addDeclarations(IndentedWriter)}. + */ + public void test_addDeclarations_designTime() { + Beans.setDesignTime(true); + String result = call_addDeclarations(); + String lineSeparator = System.getProperty("line.separator"); + assertEquals( + "public static interface DTObjectHandler {void handle(String path, Object object);}" + + lineSeparator + + "public DTObjectHandler dtObjectHandler;" + + lineSeparator + + "public final java.util.Map dtAttributes = new java.util.HashMap();" + + lineSeparator, result); + } + + private static String call_addDeclarations() { + StringWriter sw = new StringWriter(); + IndentedWriter indentedWriter = new IndentedWriter(new PrintWriter(sw)); + DesignTimeUtils.addDeclarations(indentedWriter); + return sw.toString(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // getImplName() + // + /////////////////////////////////////////////////////////////////////////// + /** + * Test for {@link DesignTimeUtils#getImplName(String)}. + */ + public void test_getImplName_default() { + String basicName = "MyBinderImpl"; + String result = DesignTimeUtils.getImplName(basicName); + assertEquals(basicName, result); + } + + /** + * Test for {@link DesignTimeUtils#getImplName(String)}. + */ + public void test_getImplName_designTime() { + Beans.setDesignTime(true); + String basicName = "MyBinderImpl"; + String result = DesignTimeUtils.getImplName(basicName); + // has "_designTime" substring + String prefix = basicName + "_designTime"; + assertTrue(result.startsWith(prefix)); + // suffix is current time plus/minus 1 hour, so generates unique names + long suffix = Long.parseLong(result.substring(prefix.length())); + long delta = System.currentTimeMillis() - suffix; + assertTrue(Math.abs(delta) < 1000 * 3600); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Path + // + /////////////////////////////////////////////////////////////////////////// + /** + * Test for {@link DesignTimeUtils#getPath(Element)} and related methods. + */ + public void test_path_default() throws SAXParseException { + Document doc = docHelper.documentFor("<root><first/><second/></root>", null); + DesignTimeUtils.rememberPathForElements(doc); + Element first = getChildElement(doc.getDocumentElement(), "first"); + Element second = getChildElement(doc.getDocumentElement(), "second"); + assertEquals(null, DesignTimeUtils.getPath(first)); + assertEquals(null, DesignTimeUtils.getPath(second)); + } + + /** + * Test for {@link DesignTimeUtils#getPath(Element)} and related methods. + */ + public void test_path_designTime() throws SAXParseException { + Beans.setDesignTime(true); + Document doc = docHelper.documentFor( + "<root><first/><second><subSecond/></second></root>", null); + DesignTimeUtils.rememberPathForElements(doc); + Element first = getChildElement(doc.getDocumentElement(), "first"); + Element second = getChildElement(doc.getDocumentElement(), "second"); + Element subSecond = getChildElement(second, "subSecond"); + assertEquals("0/0", DesignTimeUtils.getPath(first)); + assertEquals("0/1", DesignTimeUtils.getPath(second)); + assertEquals("0/1/0", DesignTimeUtils.getPath(subSecond)); + } + + //////////////////////////////////////////////////////////////////////////// + // + // getTemplateContent() + // + /////////////////////////////////////////////////////////////////////////// + /** + * Test for {@link DesignTimeUtils#getTemplateContent(String)}. + */ + public void test_getTemplateContent_default() { + String path = "the/path"; + assertEquals(null, DesignTimeUtils.getTemplateContent(path)); + } + + /** + * Test for {@link DesignTimeUtils#getTemplateContent(String)}. + */ + public void test_getTemplateContent_designTime() { + Beans.setDesignTime(true); + String path = "the/path"; + String key = "gwt.UiBinder.designTime " + path; + try { + String content = "myContent"; + System.setProperty(key, content); + assertEquals(content, DesignTimeUtils.getTemplateContent(path)); + } finally { + System.clearProperty(key); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // handleUIObject() + // + /////////////////////////////////////////////////////////////////////////// + /** + * Test for + * {@link DesignTimeUtils#handleUIObject(Statements, XMLElement, String)} + * . + */ + public void test_handleUIObject_default() { + WriterStatements writer = new WriterStatements(); + DesignTimeUtils.handleUIObject(writer, null, "myField"); + assertEquals(0, writer.statements.size()); + } + + /** + * Test for + * {@link DesignTimeUtils#handleUIObject(Statements, XMLElement, String)} + * . + */ + public void test_handleUIObject_designTime() throws SAXParseException { + Beans.setDesignTime(true); + // prepare XMLElement + XMLElement element; + { + Document doc = docHelper.documentFor("<root><first/></root>", null); + DesignTimeUtils.rememberPathForElements(doc); + Element first = getChildElement(doc.getDocumentElement(), "first"); + element = createXMLElement(first); + } + // prepare statements + List<String> statements; + { + WriterStatements writer = new WriterStatements(); + DesignTimeUtils.handleUIObject(writer, element, "myField"); + statements = writer.statements; + } + // validate + assertEquals(1, statements.size()); + assertEquals( + "if (dtObjectHandler != null) dtObjectHandler.handle(\"0/0\", myField);", + statements.get(0)); + } + + //////////////////////////////////////////////////////////////////////////// + // + // putAttribute() + // + /////////////////////////////////////////////////////////////////////////// + /** + * Test for + * {@link DesignTimeUtils#putAttribute(Statements, XMLElement, String, String)} + * . + */ + public void test_putAttribute_default() { + WriterStatements writer = new WriterStatements(); + DesignTimeUtils.putAttribute(writer, null, "name", "value"); + assertEquals(0, writer.statements.size()); + } + + /** + * Test for + * {@link DesignTimeUtils#putAttribute(Statements, XMLElement, String, String)} + * . + */ + public void test_putAttribute_designTime() throws SAXParseException { + Beans.setDesignTime(true); + // prepare XMLElement + XMLElement element; + { + Document doc = docHelper.documentFor("<root><first/></root>", null); + DesignTimeUtils.rememberPathForElements(doc); + Element first = getChildElement(doc.getDocumentElement(), "first"); + element = createXMLElement(first); + } + // prepare statements + List<String> statements; + { + WriterStatements writer = new WriterStatements(); + DesignTimeUtils.putAttribute(writer, element, "name", "value"); + statements = writer.statements; + } + // validate + assertEquals(1, statements.size()); + assertEquals("dtAttributes.put(\"0/0 name\", value);", statements.get(0)); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Utilities + // + /////////////////////////////////////////////////////////////////////////// + + /** + * @return the only child {@link Element} with given tag name. + */ + private static Element getChildElement(Element parent, String name) { + NodeList elements = parent.getElementsByTagName(name); + assertEquals(1, elements.getLength()); + return (Element) elements.item(0); + } + + /** + * @return the {@link XMLElement} wrapper for given {@link Element}. + */ + private static XMLElement createXMLElement(Element elem) { + return new XMLElement(elem, null, null, null, null, null); + } + + /** + * Implementation of {@link Statements} for simple statements. + */ + private static class WriterStatements implements Statements { + List<String> statements = new ArrayList<String>(); + + public void addDetachStatement(String format, Object... args) { + } + + public void addInitStatement(String format, Object... params) { + } + + public void addStatement(String format, Object... args) { + statements.add(String.format(format, args)); + } + }; +}