Revision: 6821 Author: [email protected] Date: Tue Nov 10 14:44:37 2009 Log: Merges tr...@6810,6813 into releases/2.0 keep Ie7 users from paying the AlphaImageLoader penalty proper behavior of g:header and g:customHeader in StackLayoutPanelparser layout panel units default to PX (instead of NPE, ahem) svn merge --ignore-ancestry -c 6810 https://google-web-toolkit.googlecode.com/svn/trunk . svn merge --ignore-ancestry -c 6813 https://google-web-toolkit.googlecode.com/svn/trunk .
http://code.google.com/p/google-web-toolkit/source/detail?r=6821 Added: /releases/2.0/user/src/com/google/gwt/uibinder/elementparsers/IsEmptyParser.java /releases/2.0/user/test/com/google/gwt/uibinder/elementparsers/IsEmptyParserTest.java /releases/2.0/user/test/com/google/gwt/uibinder/elementparsers/StackLayoutPanelParserTest.java /releases/2.0/user/test/com/google/gwt/uibinder/elementparsers/TabLayoutPanelParserTest.java /releases/2.0/user/test/com/google/gwt/uibinder/elementparsers/UIObjectParserTest.java Modified: /releases/2.0/branch-info.txt /releases/2.0/samples/mail/src/com/google/gwt/sample/mail/client/Shortcuts.ui.xml /releases/2.0/user/src/com/google/gwt/uibinder/elementparsers/DockLayoutPanelParser.java /releases/2.0/user/src/com/google/gwt/uibinder/elementparsers/StackLayoutPanelParser.java /releases/2.0/user/src/com/google/gwt/uibinder/elementparsers/TabLayoutPanelParser.java /releases/2.0/user/src/com/google/gwt/uibinder/elementparsers/UIObjectParser.java /releases/2.0/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java /releases/2.0/user/src/com/google/gwt/uibinder/rebind/XMLElement.java /releases/2.0/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImplIE6.java /releases/2.0/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java /releases/2.0/user/test/com/google/gwt/uibinder/elementparsers/DockLayoutPanelParserTest.java /releases/2.0/user/test/com/google/gwt/uibinder/elementparsers/ElementParserTester.java /releases/2.0/user/test/com/google/gwt/uibinder/elementparsers/UiJavaResources.java /releases/2.0/user/test/com/google/gwt/uibinder/rebind/XMLElementTest.java ======================================= --- /dev/null +++ /releases/2.0/user/src/com/google/gwt/uibinder/elementparsers/IsEmptyParser.java Tue Nov 10 14:44:37 2009 @@ -0,0 +1,34 @@ +/* + * 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.uibinder.elementparsers; + +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.uibinder.rebind.UiBinderWriter; +import com.google.gwt.uibinder.rebind.XMLElement; + +/** + * The last parser, asserts that everything has been consumed + * and so the template has nothing unexpected. + */ +public class IsEmptyParser implements ElementParser { + + public void parse(XMLElement elem, String fieldName, JClassType type, + UiBinderWriter writer) throws UnableToCompleteException { + elem.assertNoAttributes(); + elem.assertNoBody(); + } +} ======================================= --- /dev/null +++ /releases/2.0/user/test/com/google/gwt/uibinder/elementparsers/IsEmptyParserTest.java Tue Nov 10 14:44:37 2009 @@ -0,0 +1,83 @@ +/* + * 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.elementparsers; + +import com.google.gwt.core.ext.UnableToCompleteException; + +import junit.framework.TestCase; + +import org.xml.sax.SAXException; + +import java.io.IOException; + +/** + * A unit test. Guess what of. + */ +public class IsEmptyParserTest extends TestCase { + private static final String PARSED_TYPE = "com.google.gwt.user.client.ui.UIObject"; + + private ElementParserTester tester; + + @Override + public void setUp() throws Exception { + super.setUp(); + tester = new ElementParserTester(PARSED_TYPE, new IsEmptyParser()); + } + + public void testExtraText() throws SAXException, IOException { + StringBuffer b = new StringBuffer(); + b.append("<g:UIObject>"); + b.append(" I have some extra"); + b.append("</g:UIObject>"); + + try { + tester.parse(b.toString()); + fail(); + } catch (UnableToCompleteException e) { + assertTrue("Expect extra text echo", + tester.logger.died.contains("I have some extra")); + } + } + + public void testExtraChildren() throws SAXException, IOException { + StringBuffer b = new StringBuffer(); + b.append("<g:UIObject>"); + b.append(" <blorp />"); + b.append("</g:UIObject>"); + + try { + tester.parse(b.toString()); + fail(); + } catch (UnableToCompleteException e) { + assertTrue("Expect extra text child", + tester.logger.died.contains("<blorp>")); + } + } + + public void testExtraAttributes() throws SAXException, IOException { + StringBuffer b = new StringBuffer(); + b.append("<g:UIObject blip='blap' blorp='bloop'>"); + b.append("</g:UIObject>"); + + try { + tester.parse(b.toString()); + fail(); + } catch (UnableToCompleteException e) { + assertTrue("Expect extra attributes list", + tester.logger.died.contains("\"blip\", \"blorp\"")); + } + } +} ======================================= --- /dev/null +++ /releases/2.0/user/test/com/google/gwt/uibinder/elementparsers/StackLayoutPanelParserTest.java Tue Nov 10 14:44:37 2009 @@ -0,0 +1,104 @@ +/* + * 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.elementparsers; + +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.uibinder.rebind.FieldWriter; + +import junit.framework.TestCase; + +import org.xml.sax.SAXException; + +import java.io.IOException; +import java.util.Iterator; + +/** + * A unit test. Guess what of. + */ +public class StackLayoutPanelParserTest extends TestCase { + + private static final String PARSED_TYPE = "com.google.gwt.user.client.ui.StackLayoutPanel"; + + private ElementParserTester tester; + + @Override + public void setUp() throws Exception { + super.setUp(); + tester = new ElementParserTester(PARSED_TYPE, new StackLayoutPanelParser()); + } + + public void testBadChild() throws SAXException, IOException { + StringBuffer b = new StringBuffer(); + b.append("<g:StackLayoutPanel unit='EM'>"); + b.append(" <g:west><foo/></g:west>"); + b.append("</g:StackLayoutPanel>"); + + try { + tester.parse(b.toString()); + fail(); + } catch (UnableToCompleteException e) { + assertTrue("expect \"only g:stack\" error", + tester.logger.died.contains("only <g:stack> children")); + } + } + + public void testHappy() throws UnableToCompleteException, SAXException, + IOException { + StringBuffer b = new StringBuffer(); + b.append("<g:StackLayoutPanel unit='PX'>"); + b.append(" <g:stack>"); + b.append(" <g:header size='3'>Re<b>mark</b>able</g:header>"); + b.append(" <g:Label id='able'>able</g:Label>"); + b.append(" </g:stack>"); + b.append(" <g:stack>"); + b.append(" <g:customHeader size='3'>"); + b.append(" <g:Label id='custom'>Custom</g:Label>"); + b.append(" </g:customHeader>"); + b.append(" <g:Label id='baker'>baker</g:Label>"); + b.append(" </g:stack>"); + b.append("</g:StackLayoutPanel>"); + + String[] expected = { + "fieldName.add(<g:Label id='able'>, " + + "new com.google.gwt.user.client.ui.HTML(\"Re<b>mark</b>able\"), 3);", + "fieldName.add(<g:Label id='baker'>, " + "<g:Label id='custom'>, 3);",}; + + FieldWriter w = tester.parse(b.toString()); + assertEquals("new " + PARSED_TYPE + + "(com.google.gwt.dom.client.Style.Unit.PX)", w.getInitializer()); + + Iterator<String> i = tester.writer.statements.iterator(); + for (String e : expected) { + assertEquals(e, i.next()); + } + assertFalse(i.hasNext()); + assertNull(tester.logger.died); + } + + public void testNoUnits() throws SAXException, IOException, + UnableToCompleteException { + StringBuffer b = new StringBuffer(); + b.append(" <g:StackLayoutPanel>"); + b.append(" </g:StackLayoutPanel>"); + + FieldWriter w = tester.parse(b.toString()); + assertEquals("new " + PARSED_TYPE + + "(com.google.gwt.dom.client.Style.Unit.PX)", w.getInitializer()); + + Iterator<String> i = tester.writer.statements.iterator(); + assertFalse(i.hasNext()); + } +} ======================================= --- /dev/null +++ /releases/2.0/user/test/com/google/gwt/uibinder/elementparsers/TabLayoutPanelParserTest.java Tue Nov 10 14:44:37 2009 @@ -0,0 +1,102 @@ +/* + * 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.elementparsers; + +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.uibinder.rebind.FieldWriter; + +import junit.framework.TestCase; + +import org.xml.sax.SAXException; + +import java.io.IOException; +import java.util.Iterator; + +/** + * A unit test. Guess what of. + */ +public class TabLayoutPanelParserTest extends TestCase { + + private static final String PARSED_TYPE = "com.google.gwt.user.client.ui.TabLayoutPanel"; + + private ElementParserTester tester; + + @Override + public void setUp() throws Exception { + super.setUp(); + tester = new ElementParserTester(PARSED_TYPE, new TabLayoutPanelParser()); + } + + public void testBadChild() throws SAXException, IOException { + StringBuffer b = new StringBuffer(); + b.append("<g:TabLayoutPanel unit='EM'>"); + b.append(" <g:west><foo/></g:west>"); + b.append("</g:TabLayoutPanel>"); + + try { + tester.parse(b.toString()); + fail(); + } catch (UnableToCompleteException e) { + assertNotNull(tester.logger.died); + } + } + + public void testHappy() throws UnableToCompleteException, SAXException, + IOException { + StringBuffer b = new StringBuffer(); + b.append("<g:TabLayoutPanel barUnit='PX' barHeight='30'>"); + b.append(" <g:tab>"); + b.append(" <g:header size='3'>Re<b>mark</b>able</g:header>"); + b.append(" <g:Label id='able'>able</g:Label>"); + b.append(" </g:tab>"); + b.append(" <g:tab>"); + b.append(" <g:customHeader size='3'>"); + b.append(" <g:Label id='custom'>Custom</g:Label>"); + b.append(" </g:customHeader>"); + b.append(" <g:Label id='baker'>baker</g:Label>"); + b.append(" </g:tab>"); + b.append("</g:TabLayoutPanel>"); + + String[] expected = { + "fieldName.add(<g:Label id='able'>, \"Re<b>mark</b>able\", true);", + "fieldName.add(<g:Label id='baker'>, " + "<g:Label id='custom'>);",}; + + FieldWriter w = tester.parse(b.toString()); + assertEquals("new " + PARSED_TYPE + + "(30, com.google.gwt.dom.client.Style.Unit.PX)", w.getInitializer()); + + Iterator<String> i = tester.writer.statements.iterator(); + for (String e : expected) { + assertEquals(e, i.next()); + } + assertFalse(i.hasNext()); + assertNull(tester.logger.died); + } + + public void testNoUnits() throws SAXException, IOException, + UnableToCompleteException { + StringBuffer b = new StringBuffer(); + b.append("<g:TabLayoutPanel barHeight='3'>"); + b.append(" </g:TabLayoutPanel>"); + + FieldWriter w = tester.parse(b.toString()); + assertEquals("new " + PARSED_TYPE + + "(3, com.google.gwt.dom.client.Style.Unit.PX)", w.getInitializer()); + + Iterator<String> i = tester.writer.statements.iterator(); + assertFalse(i.hasNext()); + } +} ======================================= --- /dev/null +++ /releases/2.0/user/test/com/google/gwt/uibinder/elementparsers/UIObjectParserTest.java Tue Nov 10 14:44:37 2009 @@ -0,0 +1,65 @@ +/* + * 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.elementparsers; + +import com.google.gwt.core.ext.UnableToCompleteException; + +import junit.framework.TestCase; + +import org.xml.sax.SAXException; + +import java.io.IOException; +import java.util.Iterator; + +/** + * A unit test. Guess what of. + */ +public class UIObjectParserTest extends TestCase { + private static final String PARSED_TYPE = "com.google.gwt.user.client.ui.UIObject"; + + private ElementParserTester tester; + + @Override + public void setUp() throws Exception { + super.setUp(); + tester = new ElementParserTester(PARSED_TYPE, new UIObjectParser()); + } + + public void testHappy() throws UnableToCompleteException, SAXException, + IOException { + StringBuffer b = new StringBuffer(); + b.append("<g:UIObject debugId='blat' addStyleNames='foo, bar baz'"); + b.append(" addStyleDependentNames='able, baker charlie' >"); + b.append("</g:UIObject>"); + + String[] expected = { + "fieldName.ensureDebugId(\"blat\");", + "fieldName.addStyleName(\"foo\");", "fieldName.addStyleName(\"bar\");", + "fieldName.addStyleName(\"baz\");", + "fieldName.addStyleDependentName(\"able\");", + "fieldName.addStyleDependentName(\"baker\");", + "fieldName.addStyleDependentName(\"charlie\");",}; + + tester.parse(b.toString()); + + Iterator<String> i = tester.writer.statements.iterator(); + for (String e : expected) { + assertEquals(e, i.next()); + } + assertFalse(i.hasNext()); + assertNull(tester.logger.died); + } +} ======================================= --- /releases/2.0/branch-info.txt Tue Nov 10 13:15:16 2009 +++ /releases/2.0/branch-info.txt Tue Nov 10 14:44:37 2009 @@ -523,3 +523,9 @@ Fix null.nullMethod() in DynaTable sample app due to null-tightened field svn merge --ignore-ancestry -c6814,6815 https://google-web-toolkit.googlecode.com/svn/trunk/ . +tr...@6810,6813 were merged into this branch + keep Ie7 users from paying the AlphaImageLoader penalty + proper behavior of g:header and g:customHeader in StackLayoutPanelparser + layout panel units default to PX (instead of NPE, ahem) + svn merge --ignore-ancestry -c 6810 https://google-web-toolkit.googlecode.com/svn/trunk . + svn merge --ignore-ancestry -c 6813 https://google-web-toolkit.googlecode.com/svn/trunk . ======================================= --- /releases/2.0/samples/mail/src/com/google/gwt/sample/mail/client/Shortcuts.ui.xml Mon Nov 9 11:00:52 2009 +++ /releases/2.0/samples/mail/src/com/google/gwt/sample/mail/client/Shortcuts.ui.xml Tue Nov 10 14:44:37 2009 @@ -45,29 +45,17 @@ <g:StackLayoutPanel styleName='{style.shortcuts}' unit='EM'> <g:stack> - <g:header size='3'> - <g:HTMLPanel styleName='{style.stackHeader}'> - <div class='{style.mailboxesIcon}'/> Mailboxes - </g:HTMLPanel> - </g:header> + <g:header size='3'><div class='{style.mailboxesIcon}'/> Mailboxes</g:header> <mail:Mailboxes ui:field='mailboxes'/> </g:stack> <g:stack> - <g:header size='3'> - <g:HTMLPanel styleName='{style.stackHeader}'> - <div class='{style.tasksIcon}'/> Tasks - </g:HTMLPanel> - </g:header> + <g:header size='3'><div class='{style.tasksIcon}'/> Tasks</g:header> <mail:Tasks ui:field='tasks'/> </g:stack> <g:stack> - <g:header size='3'> - <g:HTMLPanel styleName='{style.stackHeader}'> - <div class='{style.contactsIcon}'/> Contacts - </g:HTMLPanel> - </g:header> + <g:header size='3'><div class='{style.contactsIcon}'/> Contacts</g:header> <mail:Contacts ui:field='contacts'/> </g:stack> </g:StackLayoutPanel> ======================================= --- /releases/2.0/user/src/com/google/gwt/uibinder/elementparsers/DockLayoutPanelParser.java Fri Nov 6 17:41:50 2009 +++ /releases/2.0/user/src/com/google/gwt/uibinder/elementparsers/DockLayoutPanelParser.java Tue Nov 10 14:44:37 2009 @@ -62,11 +62,13 @@ public void parse(XMLElement elem, String fieldName, JClassType type, UiBinderWriter writer) throws UnableToCompleteException { // Generate instantiation (requires a 'unit' ctor param). - // (Don't generate a ctor for the SplitLayoutPanel; it's implicitly PX). + // (Don't generate a ctor for the SplitLayoutPanel, it has its own parser). if (type != getSplitLayoutPanelType(writer)) { JEnumType unitEnumType = writer.getOracle().findType( Unit.class.getCanonicalName()).isEnum(); - String unit = elem.consumeAttribute("unit", unitEnumType); + String unit = elem.consumeAttributeWithDefault("unit", + String.format("%s.%s", unitEnumType.getQualifiedSourceName(), "PX"), + unitEnumType); writer.setFieldInitializerAsConstructor(fieldName, writer.getOracle().findType(DockLayoutPanel.class.getName()), unit); } @@ -123,11 +125,7 @@ } private boolean isValidChildElement(XMLElement parent, XMLElement child) { - String childNsUri = child.getNamespaceUri(); - if (childNsUri == null) { - return false; - } - if (!childNsUri.equals(parent.getNamespaceUri())) { + if (!parent.getNamespaceUri().equals(child.getNamespaceUri())) { return false; } if (!DOCK_NAMES.containsKey(child.getLocalName())) { ======================================= --- /releases/2.0/user/src/com/google/gwt/uibinder/elementparsers/StackLayoutPanelParser.java Fri Nov 6 17:41:50 2009 +++ /releases/2.0/user/src/com/google/gwt/uibinder/elementparsers/StackLayoutPanelParser.java Tue Nov 10 14:44:37 2009 @@ -27,56 +27,135 @@ * Parses {...@link StackLayoutPanel} widgets. */ public class StackLayoutPanelParser implements ElementParser { - - private static final String HEADER_ELEM = "header"; - private static final String STACK_ELEM = "stack"; - - public void parse(XMLElement elem, String fieldName, JClassType type, + private static class Children { + XMLElement body; + XMLElement header; + XMLElement customHeader; + } + + private static final String CUSTOM = "customHeader"; + private static final String HEADER = "header"; + private static final String STACK = "stack"; + + public void parse(XMLElement panelElem, String fieldName, JClassType type, UiBinderWriter writer) throws UnableToCompleteException { + JEnumType unitEnumType = writer.getOracle().findType( Unit.class.getCanonicalName()).isEnum(); - String unit = elem.consumeAttribute("unit", unitEnumType); + String unit = panelElem.consumeAttributeWithDefault("unit", + String.format("%s.%s", unitEnumType.getQualifiedSourceName(), "PX"), + unitEnumType); + writer.setFieldInitializerAsConstructor(fieldName, writer.getOracle().findType(StackLayoutPanel.class.getName()), unit); // Parse children. - for (XMLElement child : elem.consumeChildElements()) { + for (XMLElement stackElem : panelElem.consumeChildElements()) { // Get the stack element. - if (!isElementType(elem, child, STACK_ELEM)) { - writer.die("In %s, Only <stack> children are allowed.", elem); - } - - XMLElement headerElem = null, widgetElem = null; - for (XMLElement stackChild : child.consumeChildElements()) { - // Get the header. - if (isElementType(elem, stackChild, HEADER_ELEM)) { - if (headerElem != null) { - writer.die("In %s, Only one <header> allowed per <stack>", elem); - } - headerElem = stackChild; - continue; - } - - // Get the widget. - if (widgetElem != null) { - writer.die("In %s, Only one child widget allowed per <stack>", elem); - } - widgetElem = stackChild; - } - - String size = headerElem.consumeDoubleAttribute("size"); - XMLElement headerWidgetElem = headerElem.consumeSingleChildElement(); - String headerFieldName = writer.parseElementToField(headerWidgetElem); - String childFieldName = writer.parseElementToField(widgetElem); - - writer.addStatement("%s.add(%s, %s, %s);", fieldName, childFieldName, - headerFieldName, size); - } - } + if (!isElementType(panelElem, stackElem, STACK)) { + writer.die("In %s, only <%s:%s> children are allowed.", panelElem, + panelElem.getPrefix(), STACK); + } + + // Find all the children of the <stack>. + Children children = findChildren(stackElem, writer); + + // Parse the child widget. + if (children.body == null) { + writer.die("In %s, %s must have a child widget", panelElem, stackElem); + } + if (!writer.isWidgetElement(children.body)) { + writer.die("In %s, %s must be a widget", stackElem, children.body); + } + String childFieldName = writer.parseElementToField(children.body); + + // Parse the header. + if (children.header != null) { + HtmlInterpreter htmlInt = HtmlInterpreter.newInterpreterForUiObject( + writer, fieldName); + String size = children.header.consumeDoubleAttribute("size"); + if ("".equals(size)) { + writer.die("In %s, %s must have a size", panelElem, stackElem); + } + String html = children.header.consumeInnerHtml(htmlInt); + writer.addStatement("%s.add(%s, " + + "new com.google.gwt.user.client.ui.HTML(\"%s\"), " + + "%s);", fieldName, + childFieldName, html, size); + } else if (children.customHeader != null) { + XMLElement headerElement = + children.customHeader.consumeSingleChildElement(); + String size = children.customHeader.consumeDoubleAttribute("size"); + if ("".equals(size)) { + writer.die("In %s, %s must have a size", panelElem, stackElem); + } + if (!writer.isWidgetElement(headerElement)) { + writer.die("In %s of %s, %s is not a widget", children.customHeader, + stackElem, headerElement); + } + + String headerField = writer.parseElementToField(headerElement); + writer.addStatement("%s.add(%s, %s, %s);", fieldName, childFieldName, + headerField, size); + } else { + // Neither a header or customHeader. + writer.die("In %1$s, %2$s requires either a <%3$s:%4$s> or <%3$s:%5$s>", + panelElem, stackElem, stackElem.getPrefix(), HEADER, CUSTOM); + } + } + } + +private Children findChildren(final XMLElement elem, + final UiBinderWriter writer) throws UnableToCompleteException { + final Children children = new Children(); + + elem.consumeChildElements(new XMLElement.Interpreter<Boolean>() { + public Boolean interpretElement(XMLElement child) + throws UnableToCompleteException { + + if (hasTag(child, HEADER)) { + assertFirstHeader(); + children.header = child; + return true; + } + + if (hasTag(child, CUSTOM)) { + assertFirstHeader(); + children.customHeader = child; + return true; + } + + // Must be the body, then + if (null != children.body) { + writer.die("In %s, may have only one body element", elem); + } + + children.body = child; + return true; + } + + void assertFirstHeader() throws UnableToCompleteException { + if ((null != children.header) && (null != children.customHeader)) { + writer.die("In %1$s, may have only one %2$s:header " + + "or %2$s:customHeader", elem, elem.getPrefix()); + } + } + + private boolean hasTag(XMLElement child, final String attribute) { + return rightNamespace(child) && child.getLocalName().equals(attribute); + } + + private boolean rightNamespace(XMLElement child) { + return child.getNamespaceUri().equals(elem.getNamespaceUri()); + } + }); + + return children; +} private boolean isElementType(XMLElement parent, XMLElement child, String type) { - return child.getNamespaceUri().equals(parent.getNamespaceUri()) + return parent.getNamespaceUri().equals(child.getNamespaceUri()) && type.equals(child.getLocalName()); } } ======================================= --- /releases/2.0/user/src/com/google/gwt/uibinder/elementparsers/TabLayoutPanelParser.java Fri Nov 6 17:41:50 2009 +++ /releases/2.0/user/src/com/google/gwt/uibinder/elementparsers/TabLayoutPanelParser.java Tue Nov 10 14:44:37 2009 @@ -40,17 +40,21 @@ public void parse(XMLElement panelElem, String fieldName, JClassType type, UiBinderWriter writer) throws UnableToCompleteException { - JEnumType unitEnumType = writer.getOracle().findType( - Unit.class.getCanonicalName()).isEnum(); - // TabLayoutPanel requires tabBar size and unit ctor args. + String size = panelElem.consumeDoubleAttribute("barHeight"); - String unit = panelElem.consumeAttribute("barUnit", unitEnumType); - - JClassType tlpType = writer.getOracle().findType( - TabLayoutPanel.class.getName()); - writer.setFieldInitializerAsConstructor(fieldName, tlpType, - size, unit); + if ("".equals(size)) { + writer.die("In %s, barHeight attribute is required", panelElem); + } + + JEnumType unitEnumType = writer.getOracle().findType( + Unit.class.getCanonicalName()).isEnum(); + String unit = panelElem.consumeAttributeWithDefault("barUnit", + String.format("%s.%s", unitEnumType.getQualifiedSourceName(), "PX"), + unitEnumType); + + writer.setFieldInitializerAsConstructor(fieldName, + writer.getOracle().findType(TabLayoutPanel.class.getName()), size, unit); // Parse children. for (XMLElement tabElem : panelElem.consumeChildElements()) { @@ -65,7 +69,7 @@ // Parse the child widget. if (children.body == null) { - writer.die("%s must have a child widget", tabElem); + writer.die("In %s, %s must have a child widget", panelElem, tabElem); } if (!writer.isWidgetElement(children.body)) { writer.die("In %s, %s must be a widget", tabElem, children.body); @@ -93,8 +97,8 @@ headerField); } else { // Neither a header or customHeader. - writer.die("%1$s requires either a <%2$s:%3$s> or <%2$s:%4$s>", - tabElem, tabElem.getPrefix(), HEADER, CUSTOM); + writer.die("In %1$s, %2$s requires either a <%3$s:%4$s> or <%3$s:%5$s>", + panelElem, tabElem, tabElem.getPrefix(), HEADER, CUSTOM); } } } @@ -148,7 +152,7 @@ } private boolean isElementType(XMLElement parent, XMLElement child, String type) { - return child.getNamespaceUri().equals(parent.getNamespaceUri()) + return parent.getNamespaceUri().equals(child.getNamespaceUri()) && type.equals(child.getLocalName()); } } ======================================= --- /releases/2.0/user/src/com/google/gwt/uibinder/elementparsers/UIObjectParser.java Fri Nov 6 17:41:50 2009 +++ /releases/2.0/user/src/com/google/gwt/uibinder/elementparsers/UIObjectParser.java Tue Nov 10 14:44:37 2009 @@ -43,14 +43,5 @@ for (String s : styleNames) { writer.addStatement("%s.addStyleDependentName(%s);", fieldName, s); } - - HtmlInterpreter interpreter = HtmlInterpreter.newInterpreterForUiObject( - writer, fieldName); - - String html = elem.consumeInnerHtml(interpreter); - if (html.trim().length() > 0) { - writer.setFieldInitializer(fieldName, String.format( - "new DomHolder(UiBinderUtil.fromHtml(\"%s\"))", html)); - } } } ======================================= --- /releases/2.0/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java Mon Nov 9 13:21:34 2009 +++ /releases/2.0/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java Tue Nov 10 14:44:37 2009 @@ -28,6 +28,7 @@ import com.google.gwt.uibinder.elementparsers.AttributeMessageParser; import com.google.gwt.uibinder.elementparsers.BeanParser; import com.google.gwt.uibinder.elementparsers.ElementParser; +import com.google.gwt.uibinder.elementparsers.IsEmptyParser; import com.google.gwt.uibinder.rebind.messages.MessagesWriter; import com.google.gwt.uibinder.rebind.model.ImplicitClientBundle; import com.google.gwt.uibinder.rebind.model.ImplicitCssResource; @@ -833,7 +834,8 @@ } parsers.add(new BeanParser()); - + parsers.add(new IsEmptyParser()); + return parsers; } ======================================= --- /releases/2.0/user/src/com/google/gwt/uibinder/rebind/XMLElement.java Mon Nov 9 13:21:34 2009 +++ /releases/2.0/user/src/com/google/gwt/uibinder/rebind/XMLElement.java Tue Nov 10 14:44:37 2009 @@ -86,7 +86,7 @@ private static void clearChildren(Element elem) { // TODO(rjrjr) I'm nearly positive that anywhere this is called - // we should instead be calling assertEmpty + // we should instead be calling assertNoBody Node child; while ((child = elem.getFirstChild()) != null) { elem.removeChild(child); @@ -145,6 +145,57 @@ this.debugString = getOpeningTag(); } + + /** + * Ensure that the receiver has no attributes left + * + * @throws UnableToCompleteException if it does + */ + public void assertNoAttributes() throws UnableToCompleteException { + int numAtts = getAttributeCount(); + if (numAtts == 0) { + return; + } + + StringBuilder b = new StringBuilder(); + for (int i = 0; i < numAtts; i++) { + if (i > 0) { + b.append(", "); + } + b.append('"').append(getAttribute(i).getName()).append('"'); + } + logger.die("Unexpected attributes in %s: %s", this, b); + } + + /** + * Require that the receiver's body is empty of text and has no child nodes + * + * @throws UnableToCompleteException if it isn't + */ + public void assertNoBody() throws UnableToCompleteException { + consumeChildElements(new Interpreter<Boolean>() { + public Boolean interpretElement(XMLElement elem) + throws UnableToCompleteException { + logger.die("In %s, found unexpected child \"%s\"", this, elem); + return false; // unreachable + } + }); + assertNoText(); + } + + /** + * Require that the receiver's body is empty of text + * + * @throws UnableToCompleteException if it isn't + */ + public void assertNoText() throws UnableToCompleteException { + NoBrainInterpeter<String> nullInterpreter = new NoBrainInterpeter<String>( + null); + String s = consumeInnerTextEscapedAsHtmlStringLiteral(nullInterpreter); + if (!"".equals(s)) { + logger.die("Unexpected text in %s: \"%s\"", this, s); + } + } /** * Consumes the given attribute as a literal or field reference. The optional @@ -259,7 +310,7 @@ public Iterable<XMLElement> consumeChildElements() throws UnableToCompleteException { Iterable<XMLElement> rtn = consumeChildElementsNoEmptyCheck(); - assertEmpty(); + assertNoText(); return rtn; } @@ -651,15 +702,6 @@ public String toString() { return debugString; } - - private void assertEmpty() throws UnableToCompleteException { - NoBrainInterpeter<String> nullInterpreter = new NoBrainInterpeter<String>( - null); - String s = consumeInnerTextEscapedAsHtmlStringLiteral(nullInterpreter); - if (!"".equals(s)) { - logger.die("Unexpected text in %s: \"%s\"", this, s); - } - } private Iterable<XMLElement> consumeChildElementsNoEmptyCheck() { try { ======================================= --- /releases/2.0/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImplIE6.java Fri Feb 6 13:06:24 2009 +++ /releases/2.0/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImplIE6.java Tue Nov 10 14:44:37 2009 @@ -36,6 +36,8 @@ private static String moduleBaseUrlProtocol = GWT.getHostPageBaseURL().startsWith("https") ? "https://" : "http://"; + private static boolean isIE6 = isIE6(); + private static native void injectGlobalHandler() /*-{ $wnd.__gwt_transparentImgHandler = function (elem) { elem.onerror = null; @@ -43,6 +45,28 @@ }; }-*/; + // Stolen and modified from UserAgent.gwt.xml. + // TODO(jgw): Get rid of this method, and switch to using soft permutations + // once they land in trunk. + private static native boolean isIE6() /*-{ + function makeVersion(result) { + return (parseInt(result[1]) * 1000) + parseInt(result[2]); + } + + var ua = navigator.userAgent.toLowerCase(); + if (ua.indexOf("msie") != -1) { + var result = /msie ([0-9]+)\.([0-9]+)/.exec(ua); + if (result && result.length == 3) { + var v = makeVersion(result); + if (v < 7000) { + return true; + } + } + } + + return false; + }-*/; + public ClippedImageImplIE6() { injectGlobalHandler(); } @@ -50,7 +74,11 @@ @Override public void adjust(Element clipper, String url, int left, int top, int width, int height) { - + if (!isIE6) { + super.adjust(clipper, url, left, top, width, height); + return; + } + clipper.getStyle().setPropertyPx("width", width); clipper.getStyle().setPropertyPx("height", height); @@ -73,6 +101,10 @@ @Override public Element createStructure(String url, int left, int top, int width, int height) { + if (!isIE6) { + return super.createStructure(url, left, top, width, height); + } + // We need to explicitly sink ONLOAD on the child image element, because it // can't be fired on the clipper. Element clipper = super.createStructure(url, left, top, width, height); @@ -82,6 +114,11 @@ } public void fireSyntheticLoadEvent(final Image image) { + if (!isIE6) { + super.fireSyntheticLoadEvent(image); + return; + } + // This is the same as the superclass' implementation, except that it // explicitly checks for the 'clipper' element, and dispatches the event // on the img (you can't dispatch events on the clipper). @@ -97,6 +134,10 @@ @Override public String getHTML(String url, int left, int top, int width, int height) { + if (!isIE6) { + return super.getHTML(url, left, top, width, height); + } + String clipperStyle = "overflow: hidden; width: " + width + "px; height: " + height + "px; padding: 0px; zoom: 1"; ======================================= --- /releases/2.0/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java Mon Nov 9 13:21:34 2009 +++ /releases/2.0/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java Tue Nov 10 14:44:37 2009 @@ -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 @@ -21,6 +21,10 @@ import com.google.gwt.uibinder.attributeparsers.StringAttributeParserTest; import com.google.gwt.uibinder.elementparsers.DialogBoxParserTest; import com.google.gwt.uibinder.elementparsers.DockLayoutPanelParserTest; +import com.google.gwt.uibinder.elementparsers.IsEmptyParserTest; +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.GwtResourceEntityResolverTest; import com.google.gwt.uibinder.rebind.HandlerEvaluatorTest; import com.google.gwt.uibinder.rebind.TokenatorTest; @@ -50,14 +54,20 @@ suite.addTestSuite(OwnerFieldClassTest.class); suite.addTestSuite(OwnerFieldTest.class); - // parsers - suite.addTestSuite(DialogBoxParserTest.class); - suite.addTestSuite(DockLayoutPanelParserTest.class); - suite.addTestSuite(FieldReferenceConverterTest.class); + // attributeparsers suite.addTestSuite(IntAttributeParserTest.class); + suite.addTestSuite(FieldReferenceConverterTest.class); suite.addTestSuite(StrictAttributeParserTest.class); suite.addTestSuite(StringAttributeParserTest.class); + // elementparsers + suite.addTestSuite(DialogBoxParserTest.class); + suite.addTestSuite(DockLayoutPanelParserTest.class); + suite.addTestSuite(IsEmptyParserTest.class); + suite.addTestSuite(StackLayoutPanelParserTest.class); + suite.addTestSuite(TabLayoutPanelParserTest.class); + suite.addTestSuite(UIObjectParserTest.class); + return suite; } ======================================= --- /releases/2.0/user/test/com/google/gwt/uibinder/elementparsers/DockLayoutPanelParserTest.java Mon Nov 9 13:21:34 2009 +++ /releases/2.0/user/test/com/google/gwt/uibinder/elementparsers/DockLayoutPanelParserTest.java Tue Nov 10 14:44:37 2009 @@ -50,7 +50,8 @@ tester.parse(b.toString()); fail(); } catch (UnableToCompleteException e) { - assertNotNull(tester.logger.died); + assertTrue("expect \"must contain a widget\" error", + tester.logger.died.contains("must contain a widget")); } } @@ -127,4 +128,17 @@ assertNotNull(tester.logger.died); } } -} + + public void testNoUnits() throws SAXException, IOException, UnableToCompleteException { + StringBuffer b = new StringBuffer(); + b.append("<g:DockLayoutPanel>"); + b.append("</g:DockLayoutPanel>"); + + FieldWriter w = tester.parse(b.toString()); + assertEquals("new " + PARSED_TYPE + + "(com.google.gwt.dom.client.Style.Unit.PX)", w.getInitializer()); + + Iterator<String> i = tester.writer.statements.iterator(); + assertFalse(i.hasNext()); + } +} ======================================= --- /releases/2.0/user/test/com/google/gwt/uibinder/elementparsers/ElementParserTester.java Tue Nov 10 11:37:16 2009 +++ /releases/2.0/user/test/com/google/gwt/uibinder/elementparsers/ElementParserTester.java Tue Nov 10 14:44:37 2009 @@ -23,6 +23,7 @@ import com.google.gwt.dev.javac.CompilationStateBuilder; import com.google.gwt.dev.javac.impl.MockJavaResource; import com.google.gwt.dev.resource.Resource; +import com.google.gwt.dev.util.log.PrintWriterTreeLogger; import com.google.gwt.uibinder.attributeparsers.AttributeParsers; import com.google.gwt.uibinder.rebind.FieldManager; import com.google.gwt.uibinder.rebind.FieldWriter; @@ -33,13 +34,21 @@ import com.google.gwt.uibinder.rebind.XMLElementProviderImpl; import com.google.gwt.uibinder.rebind.messages.MessagesWriter; +import junit.framework.Assert; + import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.SAXException; import java.io.IOException; +import java.io.PrintWriter; import java.util.Set; +/** + * Utility for testing {...@link ElementParser} implementations in + * isolation. For a new test you'll probably need to extend the + * mock class hierarchy in {...@link UiJavaResources}. + */ class ElementParserTester { static final MockJavaResource BINDER_OWNER_JAVA = new MockJavaResource( "my.Ui") { @@ -59,14 +68,21 @@ static final String FIELD_NAME = "fieldName"; static final String BINDER_URI = "binderUri"; + private static TreeLogger createLogger() { + PrintWriterTreeLogger logger = new PrintWriterTreeLogger(new PrintWriter( + System.err, true)); + logger.setMaxDetail(TreeLogger.ERROR); + return logger; + } final JClassType parsedType; final MockMortalLogger logger = new MockMortalLogger(); - final W3cDomHelper docHelper = new W3cDomHelper(TreeLogger.NULL); - + + final W3cDomHelper docHelper = new W3cDomHelper(createLogger()); final TypeOracle types; final XMLElementProvider elemProvider; final MockUiBinderWriter writer; final FieldManager fieldManager; + final ElementParser parser; ElementParserTester(String parsedTypeName, ElementParser parser) @@ -74,7 +90,7 @@ this.parser = parser; String templatePath = "TemplatePath.ui.xml"; String implName = "ImplClass"; - CompilationState state = CompilationStateBuilder.buildFrom(TreeLogger.NULL, + CompilationState state = CompilationStateBuilder.buildFrom(createLogger(), getUiResources()); types = state.getTypeOracle(); @@ -107,8 +123,11 @@ private XMLElement getElem(String string) throws SAXException, IOException { Document doc = docHelper.documentFor(string); + String tag = "g:" + parsedType.getName(); Element w3cElem = (Element) doc.getDocumentElement().getElementsByTagName( - "g:" + parsedType.getName()).item(0); + tag).item(0); + Assert.assertNotNull(String.format("Expected to find <%s> element in test DOM", tag), + w3cElem); XMLElement elem = elemProvider.get(w3cElem); return elem; } ======================================= --- /releases/2.0/user/test/com/google/gwt/uibinder/elementparsers/UiJavaResources.java Tue Nov 10 11:37:16 2009 +++ /releases/2.0/user/test/com/google/gwt/uibinder/elementparsers/UiJavaResources.java Tue Nov 10 14:44:37 2009 @@ -72,6 +72,17 @@ code.append("}\n"); return code; } + }; + public static final MockJavaResource STACK_LAYOUT_PANEL = new MockJavaResource( + "com.google.gwt.user.client.ui.StackLayoutPanel") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package com.google.gwt.user.client.ui;\n"); + code.append("public class StackLayoutPanel extends Widget {\n"); + code.append("}\n"); + return code; + } }; public static final MockJavaResource STYLE = new MockJavaResource( "com.google.gwt.dom.client.Style") { @@ -84,6 +95,17 @@ code.append("}\n"); return code; } + }; + public static final MockJavaResource TAB_LAYOUT_PANEL = new MockJavaResource( + "com.google.gwt.user.client.ui.TabLayoutPanel") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package com.google.gwt.user.client.ui;\n"); + code.append("public class TabLayoutPanel extends Widget {\n"); + code.append("}\n"); + return code; + } }; public static final MockJavaResource UI_BINDER = new MockJavaResource( "com.google.gwt.uibinder.client.UiBinder") { @@ -95,6 +117,17 @@ code.append("}\n"); return code; } + }; + public static final MockJavaResource UI_OBJECT = new MockJavaResource( + "com.google.gwt.user.client.ui.UIObject") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package com.google.gwt.user.client.ui;\n"); + code.append("public class UIObject {\n"); + code.append("}\n"); + return code; + } }; public static final MockJavaResource WIDGET = new MockJavaResource( "com.google.gwt.user.client.ui.Widget") { @@ -102,7 +135,7 @@ 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("public class Widget extends UIObject {\n"); code.append("}\n"); return code; } @@ -119,7 +152,10 @@ rtn.add(DOCK_LAYOUT_PANEL); rtn.add(LABEL); rtn.add(SPLIT_LAYOUT_PANEL); + rtn.add(STACK_LAYOUT_PANEL); rtn.add(STYLE); + rtn.add(TAB_LAYOUT_PANEL); + rtn.add(UI_OBJECT); rtn.add(UI_BINDER); rtn.add(WIDGET); return rtn; ======================================= --- /releases/2.0/user/test/com/google/gwt/uibinder/rebind/XMLElementTest.java Tue Nov 10 11:37:16 2009 +++ /releases/2.0/user/test/com/google/gwt/uibinder/rebind/XMLElementTest.java Tue Nov 10 14:44:37 2009 @@ -64,6 +64,40 @@ init("<doc><elm attr1=\"attr1Value\" attr2=\"attr2Value\"/></doc>"); } + + public void testAssertNoAttributes() throws SAXException, IOException { + init("<doc><elm yes='true' no='false'>Blah <blah/> blah</elm></doc>"); + try { + elm.assertNoAttributes(); + fail(); + } catch (UnableToCompleteException e) { + assertTrue("Expect extra attributes list", + logger.died.contains("\"yes\"")); + assertTrue("Expect extra attributes list", + logger.died.contains("\"no\"")); + } + } + + public void testAssertNoBody() throws SAXException, IOException { + init("<doc><elm yes='true' no='false'>Blah <blah/> blah</elm></doc>"); + try { + elm.assertNoBody(); + fail(); + } catch (UnableToCompleteException e) { + assertTrue("Expect extra child", logger.died.contains("<blah>")); + } + } + + public void testAssertNoText() throws SAXException, IOException { + init("<doc><elm yes='true' no='false'>Blah <blah/> blah</elm></doc>"); + try { + elm.assertNoText(); + fail(); + } catch (UnableToCompleteException e) { + assertTrue("Expect extra text", logger.died.contains("Blah")); + assertTrue("Expect extra text", logger.died.contains("blah")); + } + } public void testConsumeBoolean() throws SAXException, IOException, UnableToCompleteException { --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---
