Revision: 6337 Author: rj...@google.com Date: Fri Oct 9 11:55:53 2009 Log: Introduces ui:image--ImageResource in UiBinder
Review by: bobv http://code.google.com/p/google-web-toolkit/source/detail?r=6337 Added: /trunk/user/src/com/google/gwt/uibinder/rebind/GetEscapedInnerTextVisitor.java /trunk/user/src/com/google/gwt/uibinder/rebind/model/ImplicitImageResource.java /trunk/user/src/com/google/gwt/uibinder/sample/client/prettyImage.png Deleted: /trunk/user/src/com/google/gwt/uibinder/rebind/GetInnerTextVisitor.java Modified: /trunk/user/src/com/google/gwt/uibinder/parsers/HasTextParser.java /trunk/user/src/com/google/gwt/uibinder/parsers/TextPlaceholderInterpreter.java /trunk/user/src/com/google/gwt/uibinder/rebind/BundleWriter.java /trunk/user/src/com/google/gwt/uibinder/rebind/GetInnerHtmlVisitor.java /trunk/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java /trunk/user/src/com/google/gwt/uibinder/rebind/XMLElement.java /trunk/user/src/com/google/gwt/uibinder/rebind/model/ImplicitClientBundle.java /trunk/user/src/com/google/gwt/uibinder/rebind/model/ImplicitCssResource.java /trunk/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.java /trunk/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.ui.xml /trunk/user/test/com/google/gwt/uibinder/rebind/XMLElementTest.java /trunk/user/test/com/google/gwt/uibinder/sample/client/UiBinderTest.java ======================================= --- /dev/null +++ /trunk/user/src/com/google/gwt/uibinder/rebind/GetEscapedInnerTextVisitor.java Fri Oct 9 11:55:53 2009 @@ -0,0 +1,79 @@ +/* + * 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.rebind; + +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.uibinder.rebind.XMLElement.Interpreter; + +import org.w3c.dom.CDATASection; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.Text; + +class GetEscapedInnerTextVisitor implements NodeVisitor { + + /** + * Gathers a text representation of the children of the given Elem, and stuffs + * it into the given StringBuffer. Applies the interpreter to each descendant, + * and uses the writer to report errors. + */ + public static void getEscapedInnerText(Element elem, StringBuffer buffer, + Interpreter<String> interpreter, UiBinderWriter writer) + throws UnableToCompleteException { + new ChildWalker().accept(elem, new GetEscapedInnerTextVisitor(buffer, interpreter, + writer)); + } + + protected final StringBuffer buffer; + protected final Interpreter<String> interpreter; + protected final UiBinderWriter writer; + + protected GetEscapedInnerTextVisitor(StringBuffer buffer, + Interpreter<String> interpreter, UiBinderWriter writer) { + this.buffer = buffer; + this.interpreter = interpreter; + this.writer = writer; + } + + public void visitCData(CDATASection d) { + // TODO(jgw): write this back just as it came in. + } + + public void visitElement(Element e) throws UnableToCompleteException { + String replacement = + interpreter.interpretElement(new XMLElement(e, writer)); + + if (replacement != null) { + buffer.append(replacement); + } + } + + public void visitText(Text t) { + String escaped = + UiBinderWriter.escapeText(t.getTextContent(), preserveWhiteSpace(t)); + buffer.append(escaped); + } + + private boolean preserveWhiteSpace(Text t) { + Element parent = Node.ELEMENT_NODE == t.getParentNode().getNodeType() + ? (Element) t.getParentNode() : null; + + boolean preserveWhitespace = + parent != null && "pre".equals(parent.getTagName()); + // TODO(rjrjr) What about script blocks? + return preserveWhitespace; + } +} ======================================= --- /dev/null +++ /trunk/user/src/com/google/gwt/uibinder/rebind/model/ImplicitImageResource.java Fri Oct 9 11:55:53 2009 @@ -0,0 +1,52 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.uibinder.rebind.model; + +import com.google.gwt.resources.client.ImageResource.RepeatStyle; + +/** + * Models a method returning an ImageResource on a generated ClientBundle. + */ +public class ImplicitImageResource { + private final String name; + private final String source; + private final Boolean flipRtl; + private final RepeatStyle repeatStyle; + + public ImplicitImageResource( + String name, String source, Boolean flipRtl, RepeatStyle repeatStyle) { + this.name = name; + this.source = source; + this.flipRtl = flipRtl; + this.repeatStyle = repeatStyle; + } + + public Boolean getFlipRtl() { + return flipRtl; + } + + public String getName() { + return name; + } + + public RepeatStyle getRepeatStyle() { + return repeatStyle; + } + + public String getSource() { + return source; + } +} ======================================= --- /dev/null +++ /trunk/user/src/com/google/gwt/uibinder/sample/client/prettyImage.png Fri Oct 9 11:55:53 2009 Binary file, no diff available. ======================================= --- /trunk/user/src/com/google/gwt/uibinder/rebind/GetInnerTextVisitor.java Wed Aug 5 20:27:52 2009 +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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.rebind; - -import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.uibinder.rebind.XMLElement.Interpreter; - -import org.w3c.dom.CDATASection; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.Text; - -class GetInnerTextVisitor implements NodeVisitor { - - /** - * Gathers a text representation of the children of the given Elem, and stuffs - * it into the given StringBuffer. Applies the interpreter to each descendant, - * and uses the writer to report errors. - */ - public static void getEscapedInnerText(Element elem, StringBuffer buffer, - Interpreter<String> interpreter, UiBinderWriter writer) - throws UnableToCompleteException { - new ChildWalker().accept(elem, new GetInnerTextVisitor(buffer, interpreter, - writer)); - } - - protected final StringBuffer buffer; - protected final Interpreter<String> interpreter; - protected final UiBinderWriter writer; - - protected GetInnerTextVisitor(StringBuffer buffer, - Interpreter<String> interpreter, UiBinderWriter writer) { - this.buffer = buffer; - this.interpreter = interpreter; - this.writer = writer; - } - - public void visitCData(CDATASection d) { - // TODO(jgw): write this back just as it came in. - } - - public void visitElement(Element e) throws UnableToCompleteException { - String replacement = - interpreter.interpretElement(new XMLElement(e, writer)); - - if (replacement != null) { - buffer.append(replacement); - } - } - - public void visitText(Text t) { - String escaped = - UiBinderWriter.escapeText(t.getTextContent(), preserveWhiteSpace(t)); - buffer.append(escaped); - } - - private boolean preserveWhiteSpace(Text t) { - Element parent = Node.ELEMENT_NODE == t.getParentNode().getNodeType() - ? (Element) t.getParentNode() : null; - - boolean preserveWhitespace = - parent != null && "pre".equals(parent.getTagName()); - return preserveWhitespace; - } -} ======================================= --- /trunk/user/src/com/google/gwt/uibinder/parsers/HasTextParser.java Wed Aug 5 20:27:52 2009 +++ /trunk/user/src/com/google/gwt/uibinder/parsers/HasTextParser.java Fri Oct 9 11:55:53 2009 @@ -28,7 +28,7 @@ public void parse(XMLElement elem, String fieldName, JClassType type, UiBinderWriter writer) throws UnableToCompleteException { // Widgets that implement HasText will use their elements' inner text. - String text = elem.consumeInnerText(new TextInterpreter(writer)); + String text = elem.consumeInnerTextEscapedAsHtmlStringLiteral(new TextInterpreter(writer)); if (text.trim().length() > 0) { writer.genStringPropertySet(fieldName, "text", text); } ======================================= --- /trunk/user/src/com/google/gwt/uibinder/parsers/TextPlaceholderInterpreter.java Wed Aug 5 20:27:52 2009 +++ /trunk/user/src/com/google/gwt/uibinder/parsers/TextPlaceholderInterpreter.java Fri Oct 9 11:55:53 2009 @@ -34,6 +34,6 @@ @Override protected String consumePlaceholderInnards(XMLElement elem) throws UnableToCompleteException { - return elem.consumeInnerText(new NullInterpreter<String>()); + return elem.consumeInnerTextEscapedAsHtmlStringLiteral(new NullInterpreter<String>()); } } ======================================= --- /trunk/user/src/com/google/gwt/uibinder/rebind/BundleWriter.java Wed Sep 9 10:35:52 2009 +++ /trunk/user/src/com/google/gwt/uibinder/rebind/BundleWriter.java Fri Oct 9 11:55:53 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 @@ -16,11 +16,16 @@ package com.google.gwt.uibinder.rebind; import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.resources.client.ClientBundle; +import com.google.gwt.resources.client.ImageResource; import com.google.gwt.resources.client.CssResource.Strict; +import com.google.gwt.resources.client.ImageResource.ImageOptions; +import com.google.gwt.resources.client.ImageResource.RepeatStyle; import com.google.gwt.uibinder.rebind.model.ImplicitClientBundle; import com.google.gwt.uibinder.rebind.model.ImplicitCssResource; +import com.google.gwt.uibinder.rebind.model.ImplicitImageResource; /** * Writes source implementing an {...@link ImplicitClientBundle}. @@ -31,8 +36,12 @@ private final IndentedWriter writer; private final PrintWriterManager writerManager; private final TypeOracle oracle; - private final String clientBundleType; - private final String strictAnnotationType; + + private final JClassType clientBundleType; + private final JClassType strictAnnotationType; + private final JClassType imageOptionType; + private final JClassType imageResourceType; + private final JClassType repeatStyleType; public BundleWriter(ImplicitClientBundle bundleClass, PrintWriterManager writerManager, TypeOracle oracle, @@ -43,8 +52,11 @@ this.writerManager = writerManager; this.oracle = oracle; - clientBundleType = oracle.findType(ClientBundle.class.getName()).getQualifiedSourceName(); - strictAnnotationType = oracle.findType(Strict.class.getCanonicalName()).getQualifiedSourceName(); + clientBundleType = oracle.findType(ClientBundle.class.getName()); + strictAnnotationType = oracle.findType(Strict.class.getCanonicalName()); + imageOptionType = oracle.findType(ImageOptions.class.getCanonicalName()); + imageResourceType = oracle.findType(ImageResource.class.getCanonicalName()); + repeatStyleType = oracle.findType(RepeatStyle.class.getCanonicalName()); } public void write() throws UnableToCompleteException { @@ -64,9 +76,10 @@ } // Imports - writer.write("import %s;", - clientBundleType); - writer.write("import %s;", strictAnnotationType); + writer.write("import %s;", imageResourceType.getQualifiedSourceName()); + writer.write("import %s;", imageOptionType.getQualifiedSourceName()); + writer.write("import %s;", clientBundleType.getQualifiedSourceName()); + writer.write("import %s;", strictAnnotationType.getQualifiedSourceName()); writer.newline(); // Open interface @@ -80,9 +93,42 @@ writer.write("%s %s();", css.getClassName(), css.getName()); writer.newline(); } + + writer.newline(); + writeImageMethods(); // Close interface. writer.outdent(); writer.write("}"); } -} + + private void writeImageMethods() { + for (ImplicitImageResource image : bundleClass.getImageMethods()) { + if (null != image.getSource()) { + writer.write("@Source(\"%s\")", image.getSource()); + } + + writeImageOptionsAnnotation(image.getFlipRtl(), image.getRepeatStyle()); + writer.write("%s %s();", imageResourceType.getName(), image.getName()); + } + } + + private void writeImageOptionsAnnotation(Boolean flipRtl, + RepeatStyle repeatStyle) { + if (flipRtl != null || repeatStyle != null) { + StringBuilder b = new StringBuilder("@ImageOptions("); + if (null != flipRtl) { + b.append("flipRtl=").append(flipRtl); + if (repeatStyle != null) { + b.append(", "); + } + } + if (repeatStyle != null) { + b.append(String.format("repeatStyle=%s.%s", repeatStyleType.getName(), + repeatStyle.toString())); + } + b.append(")"); + writer.write(b.toString()); + } + } +} ======================================= --- /trunk/user/src/com/google/gwt/uibinder/rebind/GetInnerHtmlVisitor.java Wed Aug 5 20:27:52 2009 +++ /trunk/user/src/com/google/gwt/uibinder/rebind/GetInnerHtmlVisitor.java Fri Oct 9 11:55:53 2009 @@ -20,7 +20,7 @@ import org.w3c.dom.Element; -class GetInnerHtmlVisitor extends GetInnerTextVisitor { +class GetInnerHtmlVisitor extends GetEscapedInnerTextVisitor { /** * Recursively gathers an HTML representation of the children of the given ======================================= --- /trunk/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java Tue Sep 22 11:05:07 2009 +++ /trunk/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java Fri Oct 9 11:55:53 2009 @@ -20,10 +20,12 @@ import com.google.gwt.core.ext.typeinfo.JMethod; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.resources.client.CssResource; -import com.google.gwt.uibinder.parsers.NullInterpreter; +import com.google.gwt.resources.client.ImageResource; +import com.google.gwt.resources.client.ImageResource.RepeatStyle; import com.google.gwt.uibinder.rebind.messages.MessagesWriter; import com.google.gwt.uibinder.rebind.model.ImplicitClientBundle; import com.google.gwt.uibinder.rebind.model.ImplicitCssResource; +import com.google.gwt.uibinder.rebind.model.ImplicitImageResource; import com.google.gwt.uibinder.rebind.model.OwnerField; /** @@ -31,8 +33,11 @@ * document. */ public class UiBinderParser { + + private static final String FLIP_RTL_ATTRIBUTE = "flipRtl"; private static final String FIELD_ATTRIBUTE = "field"; private static final String SOURCE_ATTRIBUTE = "src"; + private static final String REPEAT_STYLE_ATTRIBUTE = "repeatStyle"; // TODO(rjrjr) Make all the ElementParsers receive their dependencies via // constructor like this one does, and make this an ElementParser. I want @@ -44,6 +49,7 @@ private final FieldManager fieldManager; private final ImplicitClientBundle bundleClass; private final JClassType cssResourceType; + private final JClassType imageResourceType; public UiBinderParser(UiBinderWriter writer, MessagesWriter messagesWriter, FieldManager fieldManager, TypeOracle oracle, @@ -54,7 +60,8 @@ this.fieldManager = fieldManager; this.bundleClass = bundleClass; this.cssResourceType = oracle.findType(CssResource.class.getCanonicalName()); - } + this.imageResourceType = oracle.findType(ImageResource.class.getCanonicalName()); + } /** * Parses the root UiBinder element, and kicks off the parsing of the rest of @@ -65,6 +72,7 @@ // parsers, an so need a registration scheme for uibinder-specific parsers findStyles(elem); findResources(elem); + findImages(elem); messagesWriter.findMessagesConfig(elem); XMLElement uiRoot = elem.consumeSingleChildElement(); return writer.parseElementToField(uiRoot); @@ -101,6 +109,36 @@ return resourceType; } + + /** + * Interprets <ui:image> elements + */ + private void createImage(XMLElement elem) throws UnableToCompleteException { + String name = elem.consumeRequiredAttribute(FIELD_ATTRIBUTE); + String source = elem.consumeAttribute(SOURCE_ATTRIBUTE, null); // @source is optional on ImageResource + + Boolean flipRtl = null; + if (elem.hasAttribute(FLIP_RTL_ATTRIBUTE)) { + flipRtl = elem.consumeBooleanAttribute(FLIP_RTL_ATTRIBUTE); + } + + RepeatStyle repeatStyle = null; + if (elem.hasAttribute(REPEAT_STYLE_ATTRIBUTE)) { + String value = elem.consumeAttribute(REPEAT_STYLE_ATTRIBUTE); + try { + repeatStyle = RepeatStyle.valueOf(value); + } catch(IllegalArgumentException e) { + writer.die("In %s, bad repeatStyle value %s", elem, value); + } + } + + ImplicitImageResource imageMethod = bundleClass.createImageResource(name, source, + flipRtl, repeatStyle); + + FieldWriter field = fieldManager.registerField(imageResourceType, imageMethod.getName()); + field.setInitializer(String.format("%s.%s()", bundleClass.getFieldName(), + imageMethod.getName())); + } /** * Interprets <ui:with> elements. @@ -109,7 +147,8 @@ String resourceName = elem.consumeRequiredAttribute(FIELD_ATTRIBUTE); JClassType resourceType = consumeTypeAttribute(elem); if (elem.getAttributeCount() > 0) { - writer.die("In %s, should only find attributes \"field\" and \"type\"", elem); + writer.die("In %s, should only find attributes \"field\" and \"type\"", + elem); } FieldWriter fieldWriter = fieldManager.registerField(resourceType, @@ -145,9 +184,11 @@ } private void createStyle(XMLElement elem) throws UnableToCompleteException { - String body = elem.consumeInnerText(new NullInterpreter<String>()); + String body = elem.consumeUnescapedInnerText(); if (body.length() > 0 && elem.hasAttribute(SOURCE_ATTRIBUTE)) { - writer.die("In %s, cannot use both a source attribute and inline css text.", elem); + writer.die( + "In %s, cannot use both a source attribute and inline css text.", + elem); } String source = elem.consumeAttribute(SOURCE_ATTRIBUTE); @@ -163,6 +204,22 @@ field.setInitializer(String.format("%s.%s()", bundleClass.getFieldName(), cssMethod.getName())); } + + private void findImages(XMLElement binderElement) + throws UnableToCompleteException { + binderElement.consumeChildElements(new XMLElement.Interpreter<Boolean>() { + public Boolean interpretElement(XMLElement elem) + throws UnableToCompleteException { + if (!(writer.isBinderElement(elem) && "image".equals(elem.getLocalName()))) { + return false; // Not of interest, do not consume + } + + createImage(elem); + + return true; // Yum + } + }); + } private void findResources(XMLElement binderElement) throws UnableToCompleteException { ======================================= --- /trunk/user/src/com/google/gwt/uibinder/rebind/XMLElement.java Tue Sep 22 14:34:43 2009 +++ /trunk/user/src/com/google/gwt/uibinder/rebind/XMLElement.java Fri Oct 9 11:55:53 2009 @@ -1,12 +1,12 @@ /* * Copyright 2008 Google Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -22,6 +22,7 @@ import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import org.w3c.dom.Text; import java.util.ArrayList; import java.util.Collection; @@ -47,7 +48,7 @@ public interface Interpreter<T> { /** * Given an XMLElement, return its filtered value. - * + * * @throws UnableToCompleteException on error */ T interpretElement(XMLElement elem) throws UnableToCompleteException; @@ -115,7 +116,7 @@ /** * Consumes the given attribute and returns its trimmed value, or null if it * was unset. The returned string is not escaped. - * + * * @param name the attribute's full name (including prefix) * @return the attribute's value, or null */ @@ -128,7 +129,7 @@ /** * Consumes the given attribute and returns its trimmed value, or the given * default value if it was unset. The returned string is not escaped. - * + * * @param name the attribute's full name (including prefix) * @param defaultValue the value to return if the attribute was unset * @return the attribute's value, or defaultValue @@ -143,7 +144,7 @@ /** * Consumes the given attribute as a boolean value. - * + * * @throws UnableToCompleteException */ public boolean consumeBooleanAttribute(String attr) @@ -177,7 +178,7 @@ * Consumes and returns all child elements selected by the interpreter. Note * that text nodes are not elements, and so are not presented for * interpretation, and are not consumed. - * + * * @param interpreter Should return true for any child that should be consumed * and returned by the consumeChildElements call * @throws UnableToCompleteException @@ -265,7 +266,7 @@ * The odds are you want to use * {...@link com.google.gwt.templates.parsers.HtmlInterpreter} for an HTML value, * or {...@link com.google.gwt.templates.parsers.TextInterpreter} for text. - * + * * @param interpreter Called for each element, expected to return a string * replacement for it, or null if it should be left as is */ @@ -293,23 +294,26 @@ /** * Consumes all child text nodes, and asserts that this element held only - * text. Trailing and leading whitespace is trimmed. + * text. Trailing and leading whitespace is trimmed, and escaped for use as a + * string literal. Notice that HTML entities in the text are also escaped--is + * this a source of errors? * <p> * This call requires an interpreter to make sense of any special children. * The odds are you want to use * {...@link com.google.gwt.templates.parsers.TextInterpreter} - * + * * @throws UnableToCompleteException If any elements present are not consumed * by the interpreter */ - public String consumeInnerText(Interpreter<String> interpreter) - throws UnableToCompleteException { + public String consumeInnerTextEscapedAsHtmlStringLiteral( + Interpreter<String> interpreter) throws UnableToCompleteException { if (interpreter == null) { throw new NullPointerException("interpreter must not be null"); } StringBuffer buf = new StringBuffer(); - GetInnerTextVisitor.getEscapedInnerText(elem, buf, interpreter, writer); + GetEscapedInnerTextVisitor.getEscapedInnerText(elem, buf, interpreter, + writer); // Make sure there are no children left but empty husks for (XMLElement child : consumeChildElements()) { @@ -325,12 +329,34 @@ } /** - * Refines {...@link #consumeInnerText(Interpreter)} to handle - * PostProcessingInterpreter. + * Returns the unprocessed, unescaped, raw inner text of the receiver. Dies if + * the receiver has non-text children. + * <p> + * You probably want to use + * {...@link #consumeInnerTextEscapedAsHtmlStringLiteral} instead. + * + * @return the text + * @throws UnableToCompleteException if it held anything other than text nodes + */ + public String consumeUnescapedInnerText() throws UnableToCompleteException { + final NodeList children = elem.getChildNodes(); + if (children.getLength() < 1) { + return ""; + } + if (children.getLength() > 1 || Node.TEXT_NODE != children.item(0).getNodeType()) { + writer.die("%s must contain only text", this); + } + Text t = (Text) children.item(0); + return t.getTextContent(); + } + + /** + * Refines {...@link #consumeInnerTextEscapedAsHtmlStringLiteral(Interpreter)} to + * handle PostProcessingInterpreter. */ public String consumeInnerText(PostProcessingInterpreter<String> interpreter) throws UnableToCompleteException { - String text = consumeInnerText((Interpreter<String>) interpreter); + String text = consumeInnerTextEscapedAsHtmlStringLiteral((Interpreter<String>) interpreter); return interpreter.postProcess(text); } @@ -379,14 +405,16 @@ /** * Consumes a single child element, ignoring any text nodes and throwing an * exception if more than one child element is found. + * + * @throws UnableToCompleteException */ - public XMLElement consumeSingleChildElement() { + public XMLElement consumeSingleChildElement() + throws UnableToCompleteException { XMLElement ret = null; for (XMLElement child : consumeChildElements()) { if (ret != null) { - throw new RuntimeException(String.format( - "%s may only contain a single child element, but found" - + "%s and %s.", getLocalName(), ret, child)); + writer.die("%s may only contain a single child element, but found" + + "%s and %s.", getLocalName(), ret, child); } ret = child; ======================================= --- /trunk/user/src/com/google/gwt/uibinder/rebind/model/ImplicitClientBundle.java Thu Sep 10 13:35:35 2009 +++ /trunk/user/src/com/google/gwt/uibinder/rebind/model/ImplicitClientBundle.java Fri Oct 9 11:55:53 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 @@ -16,6 +16,7 @@ package com.google.gwt.uibinder.rebind.model; import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.resources.client.ImageResource.RepeatStyle; import com.google.gwt.uibinder.rebind.MortalLogger; import java.util.Collections; @@ -28,6 +29,7 @@ public class ImplicitClientBundle { private final Set<ImplicitCssResource> cssMethods = new HashSet<ImplicitCssResource>(); + private final Set<ImplicitImageResource> imageMethods = new HashSet<ImplicitImageResource>(); private final String packageName; private final String className; private final String fieldName; @@ -39,20 +41,19 @@ * @param uiBinderImplClassName The name of the generated ui binder * implementation that owns the bundle * @param fieldName The bundle's field name - * @param logger TODO */ public ImplicitClientBundle(String packageName, String uiBinderImplClassName, String fieldName, MortalLogger logger) { this.packageName = packageName; this.className = uiBinderImplClassName + "_GenBundle"; - this.cssBaseName = uiBinderImplClassName + "_GenCss"; + this.cssBaseName = uiBinderImplClassName + "_GenCss_"; this.fieldName = fieldName; this.logger = logger; } /** * Called to declare a new CssResource accessor on this bundle. - * + * * @param name the method name * @param source path to the .css file resource * @param extendedInterface the public interface implemented by this @@ -67,6 +68,24 @@ cssMethods.add(css); return css; } + + /** + * Called to declare a new ImageResource accessor on this bundle. + * + * @param name the method name + * @param source path the image resource, or null if none was specified + * @param flipRtl value for the flipRtl ImageOption, or null if none was + * specified + * @param repeatStyle value of the RepeatStyle ImageOption, or null if none + * was specified + * @return + */ + public ImplicitImageResource createImageResource(String name, String source, + Boolean flipRtl, RepeatStyle repeatStyle) { + ImplicitImageResource image = new ImplicitImageResource(name, source, flipRtl, repeatStyle); + imageMethods.add(image); + return image; + } public String getClassName() { return className; @@ -75,6 +94,10 @@ public Set<ImplicitCssResource> getCssMethods() { return Collections.unmodifiableSet(cssMethods); } + + public Set<ImplicitImageResource> getImageMethods() { + return Collections.unmodifiableSet(imageMethods); + } public String getFieldName() { return fieldName; ======================================= --- /trunk/user/src/com/google/gwt/uibinder/rebind/model/ImplicitCssResource.java Thu Sep 10 13:35:35 2009 +++ /trunk/user/src/com/google/gwt/uibinder/rebind/model/ImplicitCssResource.java Fri Oct 9 11:55:53 2009 @@ -32,9 +32,7 @@ import java.util.Set; /** - * Models a method returning a CssResource on a generated ClientBundle. At the - * moment, they must be tied to an external .css file. That should improve in - * the next day or so. + * Models a method returning a CssResource on a generated ClientBundle. */ public class ImplicitCssResource { private final String packageName; ======================================= --- /trunk/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.java Thu Sep 10 13:35:35 2009 +++ /trunk/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.java Fri Oct 9 11:55:53 2009 @@ -25,6 +25,7 @@ import com.google.gwt.dom.client.StyleInjector; import com.google.gwt.dom.client.TableElement; import com.google.gwt.resources.client.CssResource; +import com.google.gwt.resources.client.ImageResource; import com.google.gwt.resources.client.CssResource.Shared; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiFactory; @@ -34,6 +35,7 @@ import com.google.gwt.user.client.ui.DisclosurePanel; import com.google.gwt.user.client.ui.DockPanel; import com.google.gwt.user.client.ui.HTMLPanel; +import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.MenuBar; import com.google.gwt.user.client.ui.MenuItem; @@ -113,6 +115,10 @@ @UiField ParagraphElement privateStyleParagraph; @UiField ParagraphElement reallyPrivateStyleParagraph; @UiField SpanElement totallyPrivateStyleSpan; + @UiField ImageResource prettyImage; + @UiField ImageResource prettyTilingImage; + @UiField Image babyWidget; + @UiField ParagraphElement simpleSpriteParagraph; public WidgetBasedUi() { this.bundledLabel = new Label(); ======================================= --- /trunk/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.ui.xml Fri Sep 25 13:56:23 2009 +++ /trunk/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.ui.xml Fri Oct 9 11:55:53 2009 @@ -96,6 +96,29 @@ } </ui:style> +<ui:style field='mySpritelyStyle'> + @sprite .simpleSprite { + gwt-image: "prettyImage"; + } + + @sprite .tilingSprite { + gwt-image: "prettyTilingImage"; + } + + .garish { + color: Purple; + font-weight: bold; + font-family: sans-serif; + text-shadow: white 1px 1px 1px; + } + + .garish.tilingSprite { + font-size: 1.5em; + } +</ui:style> +<ui:image field='prettyImage' /> +<ui:image field='prettyTilingImage' src='prettyImage.png' flipRtl='true' repeatStyle='Both'/> + <gwt:DockPanel ui:field="root" width="100%"> <gwt:Dock direction='NORTH'> <gwt:HTML> @@ -149,6 +172,21 @@ <demo:ClickyLink text="hyperlink based on a custom widget" ui:field="customLinkWidget" popupText="That tickles! "/>. </p> + + <p>I bet you like babies in your Image widgets.</p> + <gwt:Image ui:field='babyWidget' resource='{prettyImage}'/> + + <p ui:field='simpleSpriteParagraph' + class='{mySpritelyStyle.simpleSprite} {mySpritelyStyle.garish}' > + And sprites too + </p> + + <p class='{mySpritelyStyle.tilingSprite} {mySpritelyStyle.garish}'> + Well how do you like <br/> + tiled sprited images...of babies!! <br/> + Well of course you do. Who wouldn't? + </p> + <p> <ui:msg description=""> Of course, it could just as easily be a Tree under a MenuBar... @@ -389,17 +427,24 @@ xmlns:gwt='urn:import:com.google.gwt.user.client.ui'> <gwt:FlowPanel> - <gwt:Label ui:field="lblDebugId" debugId="joe" addStyleNames="newStyle, anotherStyle" addStyleDependentNames="dependentStyle, anotherDependentStyle" text="a label with debug id" /> + <gwt:Label ui:field="lblDebugId" debugId="joe" + addStyleNames="newStyle, anotherStyle" + addStyleDependentNames="dependentStyle, anotherDependentStyle" + text="a label with debug id" /> <!-- A button that only adds a single style name, no comma's needed --> - <gwt:Button ui:field="btnGo" debugId="myButton" addStyleNames="buttonStyle" text="a button with extra attributes" /> + <gwt:Button ui:field="btnGo" debugId="myButton" + addStyleNames="buttonStyle" text="a button with extra attributes" /> </gwt:FlowPanel> </ui:UiBinder></pre> <b>HTML:</b> <pre style="border: 1px dashed #666; padding: 5px 0;"> - <div id="gwt-debug-joe" class="gwt-Label newStyle anotherStyle gwt-Label-dependentStyle gwt-Label-anotherDependentStyle">A label with a debug id</div> + <div id="gwt-debug-joe" + class="gwt-Label newStyle anotherStyle gwt-Label-dependentStyle gwt-Label-anotherDependentStyle"> + A label with a debug id + </div> <button id="gwt-debug-myButton" class="gwt-Button buttonStyle" tabindex="0" type="button">Go</button></pre> <gwt:FlowPanel> @@ -505,6 +550,7 @@ <tr><td>Even HTMLPanel gets in on the game</td></tr> <tr><td>Lately, anyway.</td></tr> </gwt:HTMLPanel> + </gwt:HTMLPanel> </gwt:Dock> </gwt:DockPanel> ======================================= --- /trunk/user/test/com/google/gwt/uibinder/rebind/XMLElementTest.java Tue Sep 1 11:53:48 2009 +++ /trunk/user/test/com/google/gwt/uibinder/rebind/XMLElementTest.java Fri Oct 9 11:55:53 2009 @@ -16,12 +16,14 @@ package com.google.gwt.uibinder.rebind; import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.uibinder.parsers.NullInterpreter; import com.google.gwt.uibinder.testing.UiBinderTesting; import junit.framework.TestCase; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.Text; import org.xml.sax.SAXException; import java.io.IOException; @@ -34,6 +36,10 @@ * Tests XMLElement. */ public class XMLElementTest extends TestCase { + /** + * + */ + private static final String STRING_WITH_DOUBLEQUOTE = "I have a \" quote in me"; private static final String dom = "<doc><elm attr1=\"attr1Value\" attr2=\"attr2Value\"/></doc>"; private Document doc; @@ -47,6 +53,7 @@ item = (Element) doc.getDocumentElement().getElementsByTagName( "elm").item(0); elm = new XMLElement(item, null); + } public void testConsumeAttribute() { @@ -63,7 +70,7 @@ public void testConsumeRequired() throws UnableToCompleteException { assertEquals("attr1Value", elm.consumeRequiredAttribute("attr1")); - // TODO(rjrjr) Can't test this until die() is factored out of UiBinderWriter + // TODO(rjrjr) Can't test this until UiBinderWriter can be mocked // try { // elm.consumeRequiredAttribute("unsetthing"); // fail("Should have thrown UnableToCompleteException"); @@ -71,6 +78,31 @@ // /* pass */ // } } + + public void testConsumeInnerTextEscapedAsHtmlStringLiteral() throws UnableToCompleteException { + appendText(STRING_WITH_DOUBLEQUOTE); + assertEquals(UiBinderWriter.escapeTextForJavaStringLiteral(STRING_WITH_DOUBLEQUOTE), + elm.consumeInnerTextEscapedAsHtmlStringLiteral(new NullInterpreter<String>())); + } + + public void testConsumeInnerTextEscapedAsHtmlStringLiteralEmpty() throws UnableToCompleteException { + assertEquals("", + elm.consumeInnerTextEscapedAsHtmlStringLiteral(new NullInterpreter<String>())); + } + + private void appendText(final String text) { + Text t = doc.createTextNode(STRING_WITH_DOUBLEQUOTE); + item.appendChild(t); + } + + public void testConsumeUnescapedInnerText() throws UnableToCompleteException { + appendText(STRING_WITH_DOUBLEQUOTE); + assertEquals(STRING_WITH_DOUBLEQUOTE, elm.consumeUnescapedInnerText()); + } + + public void testConsumeUnescapedInnerTextEmpty() throws UnableToCompleteException { + assertEquals("", elm.consumeUnescapedInnerText()); + } public void testEmptyStringOnMissingAttribute() throws ParserConfigurationException, SAXException, IOException { ======================================= --- /trunk/user/test/com/google/gwt/uibinder/sample/client/UiBinderTest.java Thu Sep 10 13:35:35 2009 +++ /trunk/user/test/com/google/gwt/uibinder/sample/client/UiBinderTest.java Fri Oct 9 11:55:53 2009 @@ -344,6 +344,18 @@ String t = clicky.getPopupText(); assertEquals("funny characters \" ' ' & < > > { }", t); } + + public void testImageResourceInImageWidget() { + assertEquals(widgetUi.prettyImage.getWidth(), widgetUi.babyWidget.getOffsetWidth()); + assertEquals(widgetUi.prettyImage.getHeight(), widgetUi.babyWidget.getOffsetHeight()); + assertEquals(widgetUi.prettyImage.getTop(), widgetUi.babyWidget.getOriginTop()); + assertEquals(widgetUi.prettyImage.getLeft(), widgetUi.babyWidget.getOriginLeft()); + } + + public void testSpritedElement() { + assertEquals(widgetUi.prettyImage.getWidth(), widgetUi.simpleSpriteParagraph.getOffsetWidth()); + assertEquals(widgetUi.prettyImage.getHeight(), widgetUi.simpleSpriteParagraph.getOffsetHeight()); + } public void suppressForIEfail_testBizarrelyElementedWidgets() { assertInOrder(widgetUi.widgetCrazyTable.getInnerHTML().toLowerCase(), --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---