Merge trunk r9297 into the 2.1 branch. Original public review at: http://gwt-code-reviews.appspot.com/1099801/show Patch by: jat Review by: pdr git-svn-id: https://google-web-toolkit.googlecode.com/svn/releases/2.1@9315 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/i18n/client/DateTimeFormatInfo.java b/user/src/com/google/gwt/i18n/client/DateTimeFormatInfo.java index a4d3fb2..6672ac8 100644 --- a/user/src/com/google/gwt/i18n/client/DateTimeFormatInfo.java +++ b/user/src/com/google/gwt/i18n/client/DateTimeFormatInfo.java
@@ -98,7 +98,7 @@ * @param timePattern the time pattern String * @param datePattern the data pattern String */ - String dateTimeShort(String datePattern, String timePattern); + String dateTimeShort(String timePattern, String datePattern); /** * Returns an array of the full era names.
diff --git a/user/src/com/google/gwt/text/client/DateTimeFormatRenderer.java b/user/src/com/google/gwt/text/client/DateTimeFormatRenderer.java index 9a8699b..78af84d 100644 --- a/user/src/com/google/gwt/text/client/DateTimeFormatRenderer.java +++ b/user/src/com/google/gwt/text/client/DateTimeFormatRenderer.java
@@ -47,6 +47,7 @@ * Create an instance with the given format and time zone. */ public DateTimeFormatRenderer(DateTimeFormat format, TimeZone timeZone) { + assert format != null; this.format = format; this.timeZone = timeZone; }
diff --git a/user/src/com/google/gwt/text/client/NumberFormatRenderer.java b/user/src/com/google/gwt/text/client/NumberFormatRenderer.java new file mode 100644 index 0000000..b6e1e88 --- /dev/null +++ b/user/src/com/google/gwt/text/client/NumberFormatRenderer.java
@@ -0,0 +1,47 @@ +/* + * 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.text.client; + +import com.google.gwt.i18n.client.NumberFormat; +import com.google.gwt.text.shared.AbstractRenderer; + +/** + * Renders {@link Number} objects with a {@link NumberFormat}. + */ +public class NumberFormatRenderer extends AbstractRenderer<Number> { + private final NumberFormat format; + + /** + * Create an instance using {@link NumberFormat#getDecimalFormat()}. + */ + public NumberFormatRenderer() { + this(NumberFormat.getDecimalFormat()); + } + + /** + * Create an instance with the given format. + */ + public NumberFormatRenderer(NumberFormat format) { + this.format = format; + } + + public String render(Number object) { + if (object == null) { + return ""; + } + return format.format(object); + } +}
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/DateLabelParser.java b/user/src/com/google/gwt/uibinder/elementparsers/DateLabelParser.java new file mode 100644 index 0000000..1359664 --- /dev/null +++ b/user/src/com/google/gwt/uibinder/elementparsers/DateLabelParser.java
@@ -0,0 +1,139 @@ +/* + * 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.elementparsers; + +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.JType; +import com.google.gwt.core.ext.typeinfo.TypeOracle; +import com.google.gwt.core.ext.typeinfo.TypeOracleException; +import com.google.gwt.i18n.client.DateTimeFormat; +import com.google.gwt.i18n.client.TimeZone; +import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat; +import com.google.gwt.uibinder.rebind.UiBinderWriter; +import com.google.gwt.uibinder.rebind.XMLElement; + +/** + * Parses {@link com.google.gwt.user.client.ui.DateLabel} widgets. + */ +public class DateLabelParser implements ElementParser { + + static final String AT_MOST_ONE_SPECIFIED_FORMAT = "May have at most one of format, predefinedFormat and customFormat."; + static final String AT_MOST_ONE_SPECIFIED_TIME_ZONE = "May have at most one of timezone and timezoneOffset."; + static final String NO_TIMEZONE_WITHOUT_SPECIFIED_FORMAT = "May not specify a time zone if no format is given."; + + public void parse(XMLElement elem, String fieldName, JClassType type, + UiBinderWriter writer) throws UnableToCompleteException { + boolean supportsTimeZone = hasDateTimeFormatAndTimeZoneConstructor( + writer.getOracle(), type); + if (hasDateTimeFormatConstructor(writer.getOracle(), type) + || supportsTimeZone) { + String format = consumeFormat(elem, writer); + + if (format != null) { + String timeZone = (supportsTimeZone ? consumeTimeZone(elem, writer) + : null); + + writer.setFieldInitializerAsConstructor(fieldName, type, makeArgs( + format, timeZone)); + } else if (supportsTimeZone && hasTimeZone(elem)) { + writer.die(elem, NO_TIMEZONE_WITHOUT_SPECIFIED_FORMAT); + } + } + } + + private String consumeFormat(XMLElement elem, UiBinderWriter writer) + throws UnableToCompleteException { + String format = elem.consumeAttribute("format", + writer.getOracle().findType(DateTimeFormat.class.getCanonicalName())); + String predefinedFormat = elem.consumeAttribute("predefinedFormat", + writer.getOracle().findType(PredefinedFormat.class.getCanonicalName())); + String customFormat = elem.consumeStringAttribute("customFormat"); + + if (format != null) { + if (predefinedFormat != null || customFormat != null) { + writer.die(elem, AT_MOST_ONE_SPECIFIED_FORMAT); + } + return format; + } + if (predefinedFormat != null) { + if (customFormat != null) { + writer.die(elem, AT_MOST_ONE_SPECIFIED_FORMAT); + } + return makeGetFormat(predefinedFormat); + } + if (customFormat != null) { + return makeGetFormat(customFormat); + } + return null; + } + + private String consumeTimeZone(XMLElement elem, UiBinderWriter writer) + throws UnableToCompleteException { + String timeZone = elem.consumeAttribute("timezone", + writer.getOracle().findType(TimeZone.class.getCanonicalName())); + String timeZoneOffset = elem.consumeAttribute("timezoneOffset", + getIntType(writer.getOracle())); + if (timeZone != null && timeZoneOffset != null) { + writer.die(elem, AT_MOST_ONE_SPECIFIED_TIME_ZONE); + } + if (timeZone != null) { + return timeZone; + } + if (timeZoneOffset != null) { + return TimeZone.class.getCanonicalName() + ".createTimeZone(" + + timeZoneOffset + ")"; + } + return null; + } + + private JType getIntType(TypeOracle oracle) { + try { + return oracle.parse("int"); + } catch (TypeOracleException e) { + throw new RuntimeException(e); + } + } + + private boolean hasDateTimeFormatAndTimeZoneConstructor( + TypeOracle typeOracle, JClassType type) { + JType dateTimeFormatType = typeOracle.findType(DateTimeFormat.class.getName()); + JType timeZoneType = typeOracle.findType(TimeZone.class.getName()); + return type.findConstructor(new JType[] {dateTimeFormatType, timeZoneType}) != null; + } + + private boolean hasDateTimeFormatConstructor(TypeOracle typeOracle, + JClassType type) { + JType dateTimeFormatType = typeOracle.findType(DateTimeFormat.class.getName()); + return type.findConstructor(new JType[] {dateTimeFormatType}) != null; + } + + private boolean hasTimeZone(XMLElement elem) { + return elem.hasAttribute("timezone") || elem.hasAttribute("timezoneOffset"); + } + + private String[] makeArgs(String format, String timeZone) { + if (timeZone == null) { + return new String[] {format}; + } + return new String[] {format, timeZone}; + } + + private String makeGetFormat(String format) { + return DateTimeFormat.class.getCanonicalName() + ".getFormat(" + format + + ")"; + } +}
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/NumberLabelParser.java b/user/src/com/google/gwt/uibinder/elementparsers/NumberLabelParser.java new file mode 100644 index 0000000..5ff6656 --- /dev/null +++ b/user/src/com/google/gwt/uibinder/elementparsers/NumberLabelParser.java
@@ -0,0 +1,134 @@ +/* + * 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.elementparsers; + +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.JType; +import com.google.gwt.core.ext.typeinfo.TypeOracle; +import com.google.gwt.i18n.client.CurrencyData; +import com.google.gwt.i18n.client.DateTimeFormat; +import com.google.gwt.i18n.client.NumberFormat; +import com.google.gwt.uibinder.rebind.UiBinderWriter; +import com.google.gwt.uibinder.rebind.XMLElement; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Parses {@link com.google.gwt.user.client.ui.DateLabel} widgets. + */ +public class NumberLabelParser implements ElementParser { + + static final String AT_MOST_ONE_SPECIFIED_FORMAT = "May have only one of format, predefinedFormat and customFormat."; + static final String AT_MOST_ONE_SPECIFIED_CURRENCY = "May have only one of currencyData and customCode."; + static final String NO_CURRENCY_WITH_FORMAT = "May not specify both a NumberFormat and a currency code."; + static final String NO_CURRENCY_WITHOUT_SPECIFIED_FORMAT = "May not specify a currency code if no format is given."; + static final String NO_CURRENCY_WITH_PREDEFINED_FORMAT = "May not specify a currency code with a predefined format (except the CURRENCY format)"; + static final String UNKNOWN_PREDEFINED_FORMAT = "Unknown predefined format: %s"; + + private static final Map<String, String> predefinedFormats; + + static { + String prefix = NumberFormat.class.getCanonicalName(); + Map<String, String> formats = new HashMap<String, String>(4); + formats.put("DECIMAL", prefix + ".getDecimalFormat()"); + formats.put("PERCENT", prefix + ".getPercentFormat()"); + formats.put("SCIENTIFIC", prefix + ".getScientificFormat()"); + // CURRENCY is special-cased in consumeFormat. + predefinedFormats = Collections.unmodifiableMap(formats); + } + + public void parse(XMLElement elem, String fieldName, JClassType type, + UiBinderWriter writer) throws UnableToCompleteException { + if (hasNumberFormatConstructor(writer.getOracle(), type)) { + String format = consumeFormat(elem, writer); + + if (format != null) { + writer.setFieldInitializerAsConstructor(fieldName, type, format); + } + } + } + + private String consumeCurrency(XMLElement elem, UiBinderWriter writer) + throws UnableToCompleteException { + String currencyData = elem.consumeAttribute("currencyData", + writer.getOracle().findType(CurrencyData.class.getCanonicalName())); + String currencyCode = elem.consumeStringAttribute("currencyCode"); + + if (currencyData != null && currencyCode != null) { + writer.die(elem, AT_MOST_ONE_SPECIFIED_CURRENCY); + } + return currencyData != null ? currencyData : currencyCode; + } + + private String consumeFormat(XMLElement elem, UiBinderWriter writer) + throws UnableToCompleteException { + String format = elem.consumeAttribute("format", + writer.getOracle().findType(DateTimeFormat.class.getCanonicalName())); + String predefinedFormat = elem.consumeRawAttribute("predefinedFormat"); + String customFormat = elem.consumeStringAttribute("customFormat"); + + if (format != null) { + if (predefinedFormat != null || customFormat != null) { + writer.die(elem, AT_MOST_ONE_SPECIFIED_FORMAT); + } + if (hasCurrency(elem)) { + writer.die(elem, NO_CURRENCY_WITH_FORMAT); + } + return format; + } + if (predefinedFormat != null) { + if (customFormat != null) { + writer.die(elem, AT_MOST_ONE_SPECIFIED_FORMAT); + } + if ("CURRENCY".equals(predefinedFormat)) { + String currency = consumeCurrency(elem, writer); + return NumberFormat.class.getCanonicalName() + ".getCurrencyFormat(" + + (currency != null ? currency : "") + ")"; + } + if (hasCurrency(elem)) { + writer.die(elem, NO_CURRENCY_WITH_PREDEFINED_FORMAT); + } + String f = predefinedFormats.get(predefinedFormat); + if (f == null) { + writer.die(elem, UNKNOWN_PREDEFINED_FORMAT, predefinedFormat); + } + return f; + } + if (customFormat != null) { + String currency = consumeCurrency(elem, writer); + return NumberFormat.class.getCanonicalName() + ".getFormat(" + customFormat + + (currency != null ? ", " + currency : "") + ")"; + } + if (hasCurrency(elem)) { + writer.die(elem, NO_CURRENCY_WITHOUT_SPECIFIED_FORMAT); + } + return null; + } + + private boolean hasCurrency(XMLElement elem) { + return elem.hasAttribute("currencyData") + || elem.hasAttribute("currencyCode"); + } + + private boolean hasNumberFormatConstructor(TypeOracle typeOracle, + JClassType type) { + JType numberFormatType = typeOracle.findType(NumberFormat.class.getName()); + return type.findConstructor(new JType[] {numberFormatType}) != null; + } +}
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java index bc5c3a3..4297a32 100644 --- a/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java +++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java
@@ -264,10 +264,10 @@ /* Perhaps it is provided via @UiField */ if (ownerField != null) { - if (!resourceType.equals(ownerField.getType().getRawType())) { + if (!resourceType.getErasedType().equals(ownerField.getType().getRawType() + .getErasedType())) { writer.die(elem, "Type must match %s", ownerField); } - if (ownerField.isProvided()) { String initializer; if (writer.getDesignTime().isDesignTime()) {
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java index e807ac6..a7589b5 100644 --- a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java +++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
@@ -998,6 +998,8 @@ addWidgetParser("ListBox"); addWidgetParser("Grid"); addWidgetParser("HasAlignment"); + addWidgetParser("DateLabel"); + addWidgetParser("NumberLabel"); } /**
diff --git a/user/src/com/google/gwt/user/client/ui/CheckBox.java b/user/src/com/google/gwt/user/client/ui/CheckBox.java index 75635ee..3549e0d 100644 --- a/user/src/com/google/gwt/user/client/ui/CheckBox.java +++ b/user/src/com/google/gwt/user/client/ui/CheckBox.java
@@ -59,6 +59,7 @@ HasWordWrap, IsEditor<LeafValueEditor<Boolean>> { InputElement inputElem; LabelElement labelElem; + private LeafValueEditor<Boolean> editor; private boolean valueChangeHandlerInitialized; /** @@ -134,7 +135,10 @@ } public LeafValueEditor<Boolean> asEditor() { - return TakesValueEditor.of(this); + if (editor == null) { + editor = TakesValueEditor.of(this); + } + return editor; } /** @@ -172,7 +176,7 @@ /** * Determines whether this check box is currently checked. * <p> - * Note that this <em>is not</em> return the value property of the checkbox + * Note that this <em>does not</em> return the value property of the checkbox * input element wrapped by this widget. For access to that property, see * {@link #getFormValue()} *
diff --git a/user/src/com/google/gwt/user/client/ui/DateLabel.java b/user/src/com/google/gwt/user/client/ui/DateLabel.java new file mode 100644 index 0000000..0c925a8 --- /dev/null +++ b/user/src/com/google/gwt/user/client/ui/DateLabel.java
@@ -0,0 +1,60 @@ +/* + * 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.user.client.ui; + +import com.google.gwt.i18n.client.DateTimeFormat; +import com.google.gwt.i18n.client.TimeZone; +import com.google.gwt.text.client.DateTimeFormatRenderer; + +import java.util.Date; + +/** + * Extends {@link ValueLabel} for convenience when dealing with dates and + * {@link DateTimeFormat}, especially in + * {@link com.google.gwt.uibinder.client.UiBinder UiBinder} templates. (Note + * that this class does not accept renderers. To do so use {@link ValueLabel} + * directly.) + * + * <h3>Use in UiBinder Templates</h3> + * In {@link com.google.gwt.uibinder.client.UiBinder UiBinder} templates, both the format and time zone can be configured. + * <p> + * The format can be given with one of these attributes: + * <dl> + * <dt>format</dt><dd>a reference to a {@link DateTimeFormat} instance.</dd> + * <dt>predefinedFormat</dt><dd>a {@link DateTimeFormat.PredefinedFormat}.</dd> + * <dt>customFormat</dt><dd>a date time pattern that can be passed to {@link DateTimeFormat#getFormat(String)}.</dd> + * </dl> + * <p> + * The time zone can be specified with either of these attributes: + * <dl> + * <dt>timezone</dt><dd>a reference to a {@link TimeZone} instance.</dd> + * <dt>timezoneOffset</dt><dd>the time zone offset in minutes.</dd> + * </dl> + */ +public class DateLabel extends ValueLabel<Date> { + + public DateLabel() { + super(new DateTimeFormatRenderer()); + } + + public DateLabel(DateTimeFormat format) { + super(new DateTimeFormatRenderer(format)); + } + + public DateLabel(DateTimeFormat format, TimeZone timeZone) { + super(new DateTimeFormatRenderer(format, timeZone)); + } +}
diff --git a/user/src/com/google/gwt/user/client/ui/Hidden.java b/user/src/com/google/gwt/user/client/ui/Hidden.java index fcd978b..d9f1466 100644 --- a/user/src/com/google/gwt/user/client/ui/Hidden.java +++ b/user/src/com/google/gwt/user/client/ui/Hidden.java
@@ -18,11 +18,15 @@ import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.InputElement; +import com.google.gwt.editor.client.IsEditor; +import com.google.gwt.editor.client.LeafValueEditor; +import com.google.gwt.editor.client.adapters.TakesValueEditor; +import com.google.gwt.user.client.TakesValue; /** * Represents a hidden field in an HTML form. */ -public class Hidden extends Widget implements HasName { +public class Hidden extends Widget implements HasName, TakesValue<String>, IsEditor<LeafValueEditor<String>> { /** * Creates a Hidden widget that wraps an existing <input type='hidden'> @@ -47,6 +51,8 @@ return hidden; } + private LeafValueEditor<String> editor; + /** * Constructor for <code>Hidden</code>. */ @@ -87,6 +93,13 @@ setElement(element); } + public LeafValueEditor<String> asEditor() { + if (editor == null) { + editor = TakesValueEditor.of(this); + } + return editor; + } + /** * Gets the default value of the hidden field. *
diff --git a/user/src/com/google/gwt/user/client/ui/Label.java b/user/src/com/google/gwt/user/client/ui/Label.java index 9e13446..8d94dde 100644 --- a/user/src/com/google/gwt/user/client/ui/Label.java +++ b/user/src/com/google/gwt/user/client/ui/Label.java
@@ -43,8 +43,6 @@ import com.google.gwt.i18n.client.BidiUtils; import com.google.gwt.i18n.client.HasDirection; import com.google.gwt.i18n.shared.DirectionEstimator; -import com.google.gwt.i18n.shared.HasDirectionEstimator; -import com.google.gwt.safehtml.shared.SafeHtml; /** * A widget that contains arbitrary text, <i>not</i> interpreted as HTML. @@ -71,10 +69,9 @@ * </p> */ @SuppressWarnings("deprecation") -public class Label extends Widget implements HasDirectionalText, HasWordWrap, +public class Label extends LabelBase<String> implements HasDirectionalText, HasDirection, HasClickHandlers, HasDoubleClickHandlers, SourcesClickEvents, - SourcesMouseEvents, HasAllMouseHandlers, HasDirectionEstimator, - HasAutoHorizontalAlignment, IsEditor<LeafValueEditor<String>> { + SourcesMouseEvents, HasAllMouseHandlers, IsEditor<LeafValueEditor<String>> { public static final DirectionEstimator DEFAULT_DIRECTION_ESTIMATOR = DirectionalTextHelper.DEFAULT_DIRECTION_ESTIMATOR; @@ -102,29 +99,14 @@ return label; } - /** - * The widget's DirectionalTextHelper object. - */ - final DirectionalTextHelper directionalTextHelper; - - /** - * The widget's auto horizontal alignment policy. - * @see HasAutoHorizontalAlignment - */ - private AutoHorizontalAlignmentConstant autoHorizontalAlignment; - - /** - * The widget's horizontal alignment. - */ - private HorizontalAlignmentConstant horzAlign; + private LeafValueEditor<String> editor; /** * Creates an empty label. */ public Label() { - setElement(Document.get().createDivElement()); + super(false); setStyleName("gwt-Label"); - directionalTextHelper = new DirectionalTextHelper(getElement(), false); } /** @@ -181,12 +163,7 @@ * @param element the element to be used */ protected Label(Element element) { - setElement(element); - String tagName = element.getTagName(); - boolean isElementInline = tagName.equalsIgnoreCase("span"); - assert isElementInline || tagName.equalsIgnoreCase("div"); - directionalTextHelper = new DirectionalTextHelper(getElement(), - isElementInline); + super(element); } public HandlerRegistration addClickHandler(ClickHandler handler) { @@ -248,14 +225,10 @@ } public LeafValueEditor<String> asEditor() { - return HasTextEditor.of(this); - } - - /** - * {@inheritDoc} - */ - public AutoHorizontalAlignmentConstant getAutoHorizontalAlignment() { - return autoHorizontalAlignment; + if (editor == null) { + editor = HasTextEditor.of(this); + } + return editor; } /** @@ -267,17 +240,6 @@ return BidiUtils.getDirectionOnElement(getElement()); } - public DirectionEstimator getDirectionEstimator() { - return directionalTextHelper.getDirectionEstimator(); - } - - /** - * {@inheritDoc} - */ - public HorizontalAlignmentConstant getHorizontalAlignment() { - return horzAlign; - } - public String getText() { return directionalTextHelper.getTextOrHtml(false); } @@ -286,10 +248,6 @@ return directionalTextHelper.getTextDirection(); } - public boolean getWordWrap() { - return !getElement().getStyle().getProperty("whiteSpace").equals("nowrap"); - } - /** * @deprecated Use the {@link HandlerRegistration#removeHandler} method on * the object returned by {@link #addClickHandler} instead @@ -318,15 +276,6 @@ } /** - * {@inheritDoc} - */ - public void setAutoHorizontalAlignment(AutoHorizontalAlignmentConstant - autoAlignment) { - autoHorizontalAlignment = autoAlignment; - updateHorizontalAlignment(); - } - - /** * Sets the widget element's direction. * @deprecated Use {@link #setDirectionEstimator} and / or pass explicit * direction to {@link #setText} instead @@ -338,44 +287,6 @@ } /** - * {@inheritDoc} - * <p> - * See note at {@link #setDirectionEstimator(DirectionEstimator)}. - */ - public void setDirectionEstimator(boolean enabled) { - directionalTextHelper.setDirectionEstimator(enabled); - updateHorizontalAlignment(); - } - - /** - * {@inheritDoc} - * <p> - * Note: DirectionEstimator should be set before the widget has any content; - * it's highly recommended to set it using a constructor. Reason: if the - * widget already has non-empty content, this will update its direction - * according to the new estimator's result. This may cause flicker, and thus - * should be avoided. - */ - public void setDirectionEstimator(DirectionEstimator directionEstimator) { - directionalTextHelper.setDirectionEstimator(directionEstimator); - updateHorizontalAlignment(); - } - - /** - * {@inheritDoc} - * - * <p> Note: A subsequent call to {@link #setAutoHorizontalAlignment} may - * override the horizontal alignment set by this method. - * <p> Note: For {@code null}, the horizontal alignment is cleared, allowing - * it to be determined by the standard HTML mechanisms such as inheritance and - * CSS rules. - * @see #setAutoHorizontalAlignment - */ - public void setHorizontalAlignment(HorizontalAlignmentConstant align) { - setAutoHorizontalAlignment(align); - } - - /** * Sets the label's content to the given text. * <p> * Doesn't change the widget's direction or horizontal alignment if {@code @@ -414,104 +325,4 @@ directionalTextHelper.setTextOrHtml(text, dir, false); updateHorizontalAlignment(); } - - public void setWordWrap(boolean wrap) { - getElement().getStyle().setProperty("whiteSpace", - wrap ? "normal" : "nowrap"); - } - - protected String getTextOrHtml(boolean isHtml) { - return directionalTextHelper.getTextOrHtml(isHtml); - } - - /** - * Sets the label's content to the given safe html. See - * {@link #setText(String)} for details on potential effects on direction and - * alignment. - * - * @param html the widget's new safe html - */ - protected void setHTML(SafeHtml html) { - setTextOrHtml(html.asString(), true); - } - - /** - * Sets the label's content to the given safe html. See - * {@link #setText(String)} for details on potential effects on direction and - * alignment. - * - * @param html the widget's new safe html - * @param dir the content's direction - */ - protected void setHTML(SafeHtml html, Direction dir) { - setTextOrHtml(html.asString(), dir, true); - } - - /** - * Sets the label's content to the given value (either plain text or HTML). - * See {@link #setText(String)} for details on potential effects on direction - * and alignment. - * - * @param content the widget's new content - * @param isHtml whether the content is HTML - */ - protected void setTextOrHtml(String content, boolean isHtml) { - directionalTextHelper.setTextOrHtml(content, isHtml); - updateHorizontalAlignment(); - } - - /** - * Sets the label's content to the given value (either plain text or HTML), - * applying the given direction. See - * {@link #setText(String, com.google.gwt.i18n.client.HasDirection.Direction) - * setText(String, Direction)} for details on potential effects on alignment. - * <p> - * Implementation details: - * <ul> - * <li>If the widget's element is a <div>, sets its dir attribute - * according to the given direction. - * <li>Otherwise (i.e. the widget's element is a <span>), the direction - * is set using a nested <span dir=...> element which holds the content - * of the widget. This nested span may be followed by a zero-width Unicode - * direction character (LRM or RLM). This manipulation is necessary to prevent - * garbling in case the direction of the widget is opposite to the direction - * of its context. See {@link com.google.gwt.i18n.shared.BidiFormatter} for - * more details. - * </ul> - * - * @param content the widget's new content - * @param dir the content's direction - * @param isHtml whether the content is HTML - */ - protected void setTextOrHtml(String content, Direction dir, boolean isHtml) { - directionalTextHelper.setTextOrHtml(content, dir, isHtml); - updateHorizontalAlignment(); - } - - /** - * Sets the horizontal alignment of the widget according to the current - * AutoHorizontalAlignment setting. Should be invoked whenever the horizontal - * alignment may be affected, i.e. on every modification of the content or its - * direction. - */ - protected void updateHorizontalAlignment() { - HorizontalAlignmentConstant align; - if (autoHorizontalAlignment == null) { - align = null; - } else if (autoHorizontalAlignment instanceof HorizontalAlignmentConstant) { - align = (HorizontalAlignmentConstant) autoHorizontalAlignment; - } else { - /* autoHorizontalAlignment is a truly automatic policy, i.e. either - ALIGN_CONTENT_START or ALIGN_CONTENT_END */ - align = autoHorizontalAlignment == ALIGN_CONTENT_START ? - HorizontalAlignmentConstant.startOf(getTextDirection()) : - HorizontalAlignmentConstant.endOf(getTextDirection()); - } - - if (align != horzAlign) { - horzAlign = align; - getElement().getStyle().setProperty("textAlign", horzAlign == null ? "" - : horzAlign.getTextAlignString()); - } - } }
diff --git a/user/src/com/google/gwt/user/client/ui/LabelBase.java b/user/src/com/google/gwt/user/client/ui/LabelBase.java new file mode 100644 index 0000000..4f15d3f --- /dev/null +++ b/user/src/com/google/gwt/user/client/ui/LabelBase.java
@@ -0,0 +1,162 @@ +/* + * 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.user.client.ui; + +import com.google.gwt.dom.client.Document; +import com.google.gwt.dom.client.Element; +import com.google.gwt.i18n.shared.DirectionEstimator; +import com.google.gwt.i18n.shared.HasDirectionEstimator; + +/** + * Abstract base class for all text display widgets. + * + * <h3>Use in UiBinder Templates</h3> + * + * @param <T> the value type + */ +public class LabelBase<T> extends Widget implements HasWordWrap, + HasDirectionEstimator, HasAutoHorizontalAlignment { + + /** + * The widget's DirectionalTextHelper object. + */ + final DirectionalTextHelper directionalTextHelper; + + /** + * The widget's auto horizontal alignment policy. + * + * @see HasAutoHorizontalAlignment + */ + private AutoHorizontalAlignmentConstant autoHorizontalAlignment; + + /** + * The widget's horizontal alignment. + */ + private HorizontalAlignmentConstant horzAlign; + + protected LabelBase(boolean inline) { + this(inline ? Document.get().createSpanElement() + : Document.get().createDivElement(), inline); + } + + protected LabelBase(Element element) { + this(element, "span".equalsIgnoreCase(element.getTagName())); + } + + private LabelBase(Element element, boolean isElementInline) { + assert (isElementInline ? "span" : "div").equalsIgnoreCase(element.getTagName()); + setElement(element); + directionalTextHelper = new DirectionalTextHelper(getElement(), + isElementInline); + } + + public AutoHorizontalAlignmentConstant getAutoHorizontalAlignment() { + return autoHorizontalAlignment; + } + + public DirectionEstimator getDirectionEstimator() { + return directionalTextHelper.getDirectionEstimator(); + } + + public HorizontalAlignmentConstant getHorizontalAlignment() { + return horzAlign; + } + + public boolean getWordWrap() { + return !getElement().getStyle().getProperty("whiteSpace").equals("nowrap"); + } + + public void setAutoHorizontalAlignment( + AutoHorizontalAlignmentConstant autoAlignment) { + autoHorizontalAlignment = autoAlignment; + updateHorizontalAlignment(); + } + + /** + * {@inheritDoc} + * <p> + * See note at {@link #setDirectionEstimator(DirectionEstimator)}. + */ + public void setDirectionEstimator(boolean enabled) { + directionalTextHelper.setDirectionEstimator(enabled); + updateHorizontalAlignment(); + } + + /** + * {@inheritDoc} + * <p> + * Note: DirectionEstimator should be set before the widget has any content; + * it's highly recommended to set it using a constructor. Reason: if the + * widget already has non-empty content, this will update its direction + * according to the new estimator's result. This may cause flicker, and thus + * should be avoided. + */ + public void setDirectionEstimator(DirectionEstimator directionEstimator) { + directionalTextHelper.setDirectionEstimator(directionEstimator); + updateHorizontalAlignment(); + } + + /** + * {@inheritDoc} + * + * <p> + * Note: A subsequent call to {@link #setAutoHorizontalAlignment} may override + * the horizontal alignment set by this method. + * <p> + * Note: For {@code null}, the horizontal alignment is cleared, allowing it to + * be determined by the standard HTML mechanisms such as inheritance and CSS + * rules. + * + * @see #setAutoHorizontalAlignment + */ + public void setHorizontalAlignment(HorizontalAlignmentConstant align) { + setAutoHorizontalAlignment(align); + } + + public void setWordWrap(boolean wrap) { + getElement().getStyle().setProperty("whiteSpace", + wrap ? "normal" : "nowrap"); + } + + /** + * Sets the horizontal alignment of the widget according to the current + * AutoHorizontalAlignment setting. Should be invoked whenever the horizontal + * alignment may be affected, i.e. on every modification of the content or its + * direction. + */ + protected void updateHorizontalAlignment() { + HorizontalAlignmentConstant align; + if (autoHorizontalAlignment == null) { + align = null; + } else if (autoHorizontalAlignment instanceof HorizontalAlignmentConstant) { + align = (HorizontalAlignmentConstant) autoHorizontalAlignment; + } else { + /* + * autoHorizontalAlignment is a truly automatic policy, i.e. either + * ALIGN_CONTENT_START or ALIGN_CONTENT_END + */ + align = autoHorizontalAlignment == ALIGN_CONTENT_START + ? HorizontalAlignmentConstant.startOf(directionalTextHelper.getTextDirection()) + : HorizontalAlignmentConstant.endOf(directionalTextHelper.getTextDirection()); + } + + if (align != horzAlign) { + horzAlign = align; + getElement().getStyle().setProperty("textAlign", + horzAlign == null ? "" : horzAlign.getTextAlignString()); + } + } +}
diff --git a/user/src/com/google/gwt/user/client/ui/NumberLabel.java b/user/src/com/google/gwt/user/client/ui/NumberLabel.java new file mode 100644 index 0000000..ad398ca --- /dev/null +++ b/user/src/com/google/gwt/user/client/ui/NumberLabel.java
@@ -0,0 +1,78 @@ +/* + * 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.user.client.ui; + +import com.google.gwt.i18n.client.NumberFormat; +import com.google.gwt.text.client.NumberFormatRenderer; + +/** + * Extends {@link ValueLabel} for convenience when dealing with numbers and + * {@link NumberFormat}, especially in + * {@link com.google.gwt.uibinder.client.UiBinder UiBinder} templates. (Note + * that this class does not accept renderers. To do so use {@link ValueLabel} + * directly.) + * + * <h3>Use in UiBinder Templates</h3> In + * {@link com.google.gwt.uibinder.client.UiBinder UiBinder} templates, the + * {@link NumberFormat} can be specified with one of these attributes: + * <dl> + * <dt>format</dt> + * <dd>a reference to a {@link NumberFormat} instance.</dd> + * <dt>predefinedFormat</dt> + * <dd>a predefined format (see below for the list of acceptable values).</dd> + * <dt>customFormat</dt> + * <dd>a number format pattern that can be passed to + * {@link NumberFormat#getFormat(String)}. See below for a way of specifying a + * currency code.</dd> + * </dl> + * The valid values for the {@code predefinedFormat} attributes are: + * <dl> + * <dt>DECIMAL</dt> + * <dd>the standard decimal format for the current locale, as given by + * {@link NumberFormat#getDecimalFormat()}.</dd> + * <dt>CURRENCY</dt> + * <dd>the standard currency format for the current locale, as given by + * {@link NumberFormat#getCurrencyFormat()}. See below for a way of specifying a + * currency code.</dd> + * <dt>PERCENT</dt> + * <dd>the standard percent format for the current locale, as given by + * {@link NumberFormat#getPercentFormat()}.</dd> + * <dt>SCIENTIFIC</dt> + * <dd>the standard scientific format for the current locale, as given by + * {@link NumberFormat#getScientificFormat()}.</dd> + * </dl> + * When using {@code predefinedFormat="CURRENCY"} or a {@code customFormat}, you + * can specify a currency code using either of the following attributes: + * <dl> + * <dt>currencyData</dt> + * <dd>a reference to a {@link com.google.gwt.i18n.client.CurrencyData + * CurrencyData} instance.</dd> + * <dt>currencyCode</dt> + * <dd>an ISO4217 currency code.</dd> + * </dl> + * + * @param <T> The exact type of number + */ +public class NumberLabel<T extends Number> extends ValueLabel<T> { + + public NumberLabel() { + super(new NumberFormatRenderer()); + } + + public NumberLabel(NumberFormat format) { + super(new NumberFormatRenderer(format)); + } +}
diff --git a/user/src/com/google/gwt/user/client/ui/SimpleCheckBox.java b/user/src/com/google/gwt/user/client/ui/SimpleCheckBox.java index 4bc465c..4975554 100644 --- a/user/src/com/google/gwt/user/client/ui/SimpleCheckBox.java +++ b/user/src/com/google/gwt/user/client/ui/SimpleCheckBox.java
@@ -18,6 +18,10 @@ import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.InputElement; +import com.google.gwt.editor.client.IsEditor; +import com.google.gwt.editor.client.LeafValueEditor; +import com.google.gwt.editor.client.adapters.TakesValueEditor; +import com.google.gwt.user.client.TakesValue; /** * A simple checkbox widget, with no label. @@ -28,7 +32,8 @@ * <li>.gwt-SimpleCheckBox-disabled { Applied when checkbox is disabled }</li> * </ul> */ -public class SimpleCheckBox extends FocusWidget implements HasName { +public class SimpleCheckBox extends FocusWidget implements HasName, + TakesValue<Boolean>, IsEditor<LeafValueEditor<Boolean>> { /** * Creates a SimpleCheckBox widget that wraps an existing <input @@ -53,6 +58,8 @@ return checkBox; } + private LeafValueEditor<Boolean> editor; + /** * Creates a new simple checkbox. */ @@ -79,28 +86,66 @@ } } + public LeafValueEditor<Boolean> asEditor() { + if (editor == null) { + editor = TakesValueEditor.of(this); + } + return editor; + } + + /** + * Returns the value property of the input element that backs this widget. + * This is the value that will be associated with the check box name and + * submitted to the server if a {@link FormPanel} that holds it is submitted + * and the box is checked. + * <p> + * Don't confuse this with {@link #getValue}, which returns true or false if + * the widget is checked. + */ + public String getFormValue() { + return getInputElement().getValue(); + } + public String getName() { return getInputElement().getName(); } /** * Determines whether this check box is currently checked. + * <p> + * Note that this <em>does not</em> return the value property of the checkbox + * input element wrapped by this widget. For access to that property, see + * {@link #getFormValue()} * - * @return <code>true</code> if the check box is checked + * @return <code>true</code> if the check box is checked, false otherwise. + * Will not return null */ - public boolean isChecked() { + public Boolean getValue() { String propName = isAttached() ? "checked" : "defaultChecked"; return getInputElement().getPropertyBoolean(propName); } /** + * Determines whether this check box is currently checked. + * + * @return <code>true</code> if the check box is checked + * @deprecated Use {@link #getValue} instead + */ + @Deprecated + public boolean isChecked() { + // Funny comparison b/c getValue could in theory return null + return getValue() == true; + } + + /** * Checks or unchecks this check box. * * @param checked <code>true</code> to check the check box + * @deprecated Use {@link #setValue(Boolean)} instead */ + @Deprecated public void setChecked(boolean checked) { - getInputElement().setChecked(checked); - getInputElement().setDefaultChecked(checked); + setValue(checked); } @Override @@ -113,18 +158,50 @@ } } + /** + * Set the value property on the input element that backs this widget. This is + * the value that will be associated with the check box's name and submitted + * to the server if a {@link FormPanel} that holds it is submitted and the box + * is checked. + * <p> + * Don't confuse this with {@link #setValue}, which actually checks and + * unchecks the box. + * + * @param value + */ + public void setFormValue(String value) { + getInputElement().setAttribute("value", value); + } + public void setName(String name) { getInputElement().setName(name); } /** + * Checks or unchecks the check box. + * <p> + * Note that this <em>does not</em> set the value property of the checkbox + * input element wrapped by this widget. For access to that property, see + * {@link #setFormValue(String)} + * + * @param value true to check, false to uncheck; null value implies false + */ + public void setValue(Boolean value) { + if (value == null) { + value = Boolean.FALSE; + } + + getInputElement().setChecked(value); + getInputElement().setDefaultChecked(value); + } + + /** * This method is called when a widget is detached from the browser's - * document. Overridden because of IE bug that throws away checked state and - * in order to clear the event listener off of the <code>inputElem</code>. + * document. Overridden because of IE bug that throws away checked state. */ @Override protected void onUnload() { - setChecked(isChecked()); + setValue(getValue()); } private InputElement getInputElement() {
diff --git a/user/src/com/google/gwt/user/client/ui/SuggestBox.java b/user/src/com/google/gwt/user/client/ui/SuggestBox.java index a291aec..919a5f5 100644 --- a/user/src/com/google/gwt/user/client/ui/SuggestBox.java +++ b/user/src/com/google/gwt/user/client/ui/SuggestBox.java
@@ -657,6 +657,7 @@ private boolean selectsFirstItem = true; private SuggestOracle oracle; private String currentText; + private LeafValueEditor<String> editor; private final SuggestionDisplay display; private final TextBoxBase box; private final Callback callback = new Callback() { @@ -814,7 +815,10 @@ * Returns a {@link TakesValueEditor} backed by the DateBox. */ public LeafValueEditor<String> asEditor() { - return TakesValueEditor.of(this); + if (editor == null) { + editor = TakesValueEditor.of(this); + } + return editor; } /**
diff --git a/user/src/com/google/gwt/user/client/ui/ToggleButton.java b/user/src/com/google/gwt/user/client/ui/ToggleButton.java index 3ce977b..b337f3f 100644 --- a/user/src/com/google/gwt/user/client/ui/ToggleButton.java +++ b/user/src/com/google/gwt/user/client/ui/ToggleButton.java
@@ -16,6 +16,9 @@ package com.google.gwt.user.client.ui; +import com.google.gwt.editor.client.IsEditor; +import com.google.gwt.editor.client.LeafValueEditor; +import com.google.gwt.editor.client.adapters.TakesValueEditor; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; @@ -40,9 +43,11 @@ * <h3>Example</h3> {@example com.google.gwt.examples.ToggleButtonExample} * </p> */ -public class ToggleButton extends CustomButton implements HasValue<Boolean> { +public class ToggleButton extends CustomButton implements HasValue<Boolean>, IsEditor<LeafValueEditor<Boolean>> { private static String STYLENAME_DEFAULT = "gwt-ToggleButton"; + private LeafValueEditor<Boolean> editor; + { setStyleName(STYLENAME_DEFAULT); } @@ -182,6 +187,13 @@ return addHandler(handler, ValueChangeEvent.getType()); } + public LeafValueEditor<Boolean> asEditor() { + if (editor == null) { + editor = TakesValueEditor.of(this); + } + return editor; + } + /** * Determines whether this button is currently down. *
diff --git a/user/src/com/google/gwt/user/client/ui/ValueBoxBase.java b/user/src/com/google/gwt/user/client/ui/ValueBoxBase.java index d24f2a3..05d416e 100644 --- a/user/src/com/google/gwt/user/client/ui/ValueBoxBase.java +++ b/user/src/com/google/gwt/user/client/ui/ValueBoxBase.java
@@ -45,7 +45,6 @@ * * @param <T> the value type */ -@SuppressWarnings("deprecation") public class ValueBoxBase<T> extends FocusWidget implements HasChangeHandlers, HasName, HasDirectionEstimator, HasValue<T>, AutoDirectionHandler.Target, IsEditor<ValueBoxEditor<T>> { @@ -83,6 +82,7 @@ private final Parser<T> parser; private final Renderer<T> renderer; + private ValueBoxEditor<T> editor; private Event currentEvent; private boolean valueChangeHandlerInitialized; @@ -125,7 +125,10 @@ * Editor framework. */ public ValueBoxEditor<T> asEditor() { - return ValueBoxEditor.of(this); + if (editor == null) { + editor = ValueBoxEditor.of(this); + } + return editor; } /**
diff --git a/user/src/com/google/gwt/user/client/ui/ValueLabel.java b/user/src/com/google/gwt/user/client/ui/ValueLabel.java new file mode 100644 index 0000000..92e29db --- /dev/null +++ b/user/src/com/google/gwt/user/client/ui/ValueLabel.java
@@ -0,0 +1,139 @@ +/* + * 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.user.client.ui; + +import com.google.gwt.dom.client.Document; +import com.google.gwt.dom.client.Element; +import com.google.gwt.editor.client.IsEditor; +import com.google.gwt.editor.client.LeafValueEditor; +import com.google.gwt.editor.client.adapters.TakesValueEditor; +import com.google.gwt.text.shared.Parser; +import com.google.gwt.text.shared.Renderer; +import com.google.gwt.uibinder.client.UiConstructor; +import com.google.gwt.user.client.TakesValue; + +import java.text.ParseException; + +/** + * A label displaying its value through a renderer. + * + * @param <T> the value type. + */ +public class ValueLabel<T> extends LabelBase<T> implements TakesValue<T>, + IsEditor<LeafValueEditor<T>> { + + /** + * Creates a ValueLabel widget that wraps an existing <span> element. + * <p> + * The ValueLabel's value will be <code>null</code>, whether the element being + * wrapped has content or not. Use {@link #wrap(Element, Renderer, Parser)} to + * parse the initial element's content to initialize the ValueLabel's value. + * <p> + * This element must already be attached to the document. If the element is + * removed from the document, you must call + * {@link RootPanel#detachNow(Widget)}. + * + * @param element the element to be wrapped + * @param renderer the renderer used to render values into the element + */ + public static <T> ValueLabel<T> wrap(Element element, + Renderer<? super T> renderer) { + // Assert that the element is attached. + assert Document.get().getBody().isOrHasChild(element); + + ValueLabel<T> label = new ValueLabel<T>(element, renderer); + + // Mark it attached and remember it for cleanup. + label.onAttach(); + RootPanel.detachOnWindowClose(label); + + return label; + } + + /** + * Creates a ValueLabel widget that wraps an existing <span> element. + * <p> + * The ValueLabel's value will be initialized with the element's content, + * passed through the <code>parser</code>. + * <p> + * This element must already be attached to the document. If the element is + * removed from the document, you must call + * {@link RootPanel#detachNow(Widget)}. + * + * @param element the element to be wrapped + * @param renderer the renderer used to render values into the element + * @param parser the parser used to initialize the ValueLabel's value from the + * element's content + */ + public static <T> ValueLabel<T> wrap(Element element, + Renderer<? super T> renderer, Parser<? extends T> parser) + throws ParseException { + ValueLabel<T> label = wrap(element, renderer); + + label.setValue(parser.parse(element.getInnerText())); + + // Mark it attached and remember it for cleanup. + label.onAttach(); + RootPanel.detachOnWindowClose(label); + + return label; + } + + private final Renderer<? super T> renderer; + private T value; + private LeafValueEditor<T> editor; + + /** + * Creates an empty value label. + * + * @param inline + * @param renderer + */ + @UiConstructor + public ValueLabel(Renderer<? super T> renderer) { + super(true); + this.renderer = renderer; + } + + /** + * This constructor may be used by subclasses to explicitly use an existing + * element. This element must be either a <span> or a <div> + * element. + * + * @param element the element to be used + */ + protected ValueLabel(Element element, Renderer<? super T> renderer) { + super(element); + this.renderer = renderer; + } + + public LeafValueEditor<T> asEditor() { + if (editor == null) { + editor = TakesValueEditor.of(this); + } + return editor; + } + + public T getValue() { + return value; + } + + public void setValue(T value) { + this.value = value; + directionalTextHelper.setTextOrHtml(renderer.render(value), false); + updateHorizontalAlignment(); + } +} \ No newline at end of file
diff --git a/user/src/com/google/gwt/user/client/ui/ValueListBox.java b/user/src/com/google/gwt/user/client/ui/ValueListBox.java index 24b8b93..4fffa9a 100644 --- a/user/src/com/google/gwt/user/client/ui/ValueListBox.java +++ b/user/src/com/google/gwt/user/client/ui/ValueListBox.java
@@ -49,6 +49,7 @@ private final Renderer<T> renderer; private final ProvidesKey<T> keyProvider; + private TakesValueEditor<T> editor; private T value; public ValueListBox(Renderer<T> renderer) { @@ -81,7 +82,10 @@ * Returns a {@link TakesValueEditor} backed by the ValueListBox. */ public TakesValueEditor<T> asEditor() { - return TakesValueEditor.of(this); + if (editor == null) { + editor = TakesValueEditor.of(this); + } + return editor; } public T getValue() {
diff --git a/user/src/com/google/gwt/user/client/ui/ValuePicker.java b/user/src/com/google/gwt/user/client/ui/ValuePicker.java index 8003cec..7ec9259 100644 --- a/user/src/com/google/gwt/user/client/ui/ValuePicker.java +++ b/user/src/com/google/gwt/user/client/ui/ValuePicker.java
@@ -56,6 +56,7 @@ private final CellList<T> cellList; private SingleSelectionModel<T> smodel = new SingleSelectionModel<T>(); + private LeafValueEditor<T> editor; public ValuePicker(CellList<T> cellList) { this.cellList = cellList; @@ -80,7 +81,10 @@ * Returns a {@link TakesValueEditor} backed by the ValuePicker. */ public LeafValueEditor<T> asEditor() { - return TakesValueEditor.of(this); + if (editor == null) { + editor = TakesValueEditor.of(this); + } + return editor; } /**
diff --git a/user/src/com/google/gwt/user/datepicker/client/DateBox.java b/user/src/com/google/gwt/user/datepicker/client/DateBox.java index c9ebf25..3d6b25b 100644 --- a/user/src/com/google/gwt/user/datepicker/client/DateBox.java +++ b/user/src/com/google/gwt/user/datepicker/client/DateBox.java
@@ -249,6 +249,7 @@ private final PopupPanel popup; private final TextBox box = new TextBox(); private final DatePicker picker; + private LeafValueEditor<Date> editor; private Format format; private boolean allowDPShow = true; @@ -298,7 +299,10 @@ * Returns a {@link TakesValueEditor} backed by the DateBox. */ public LeafValueEditor<Date> asEditor() { - return TakesValueEditor.of(this); + if (editor == null) { + editor = TakesValueEditor.of(this); + } + return editor; } /**
diff --git a/user/src/com/google/gwt/user/datepicker/client/DatePicker.java b/user/src/com/google/gwt/user/datepicker/client/DatePicker.java index ddb55a4..439875a 100644 --- a/user/src/com/google/gwt/user/datepicker/client/DatePicker.java +++ b/user/src/com/google/gwt/user/datepicker/client/DatePicker.java
@@ -253,6 +253,7 @@ private Date value; private Date highlighted; private StandardCss css = StandardCss.DEFAULT; + private LeafValueEditor<Date> editor; /** * Create a new date picker. @@ -381,7 +382,10 @@ * Returns a {@link TakesValueEditor} backed by the DatePicker. */ public LeafValueEditor<Date> asEditor() { - return TakesValueEditor.of(this); + if (editor == null) { + editor = TakesValueEditor.of(this); + } + return editor; } /**
diff --git a/user/test/com/google/gwt/text/TextSuite.java b/user/test/com/google/gwt/text/TextSuite.java index cfe30d3..d14d55f 100644 --- a/user/test/com/google/gwt/text/TextSuite.java +++ b/user/test/com/google/gwt/text/TextSuite.java
@@ -20,6 +20,7 @@ import com.google.gwt.text.client.DoubleParserTest; import com.google.gwt.text.client.IntegerParserTest; import com.google.gwt.text.client.LongParserTest; +import com.google.gwt.text.client.NumberFormatRendererTest; import junit.framework.Test; @@ -34,6 +35,7 @@ suite.addTestSuite(DoubleParserTest.class); suite.addTestSuite(IntegerParserTest.class); suite.addTestSuite(LongParserTest.class); + suite.addTestSuite(NumberFormatRendererTest.class); return suite; } }
diff --git a/user/test/com/google/gwt/text/client/NumberFormatRendererTest.java b/user/test/com/google/gwt/text/client/NumberFormatRendererTest.java new file mode 100644 index 0000000..f57c016 --- /dev/null +++ b/user/test/com/google/gwt/text/client/NumberFormatRendererTest.java
@@ -0,0 +1,45 @@ +/* + * 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.text.client; + +import com.google.gwt.i18n.client.NumberFormat; +import com.google.gwt.junit.client.GWTTestCase; + +/** + * Eponymous unit test. + */ +public class NumberFormatRendererTest extends GWTTestCase { + + @Override + public String getModuleName() { + return "com.google.gwt.text.TextSuite"; + } + + public void testDefault() { + assertEquals(NumberFormat.getDecimalFormat().format(Math.PI), + new NumberFormatRenderer().render(Math.PI)); + } + + public void testScientific() { + assertEquals( + NumberFormat.getScientificFormat().format(Math.PI), + new NumberFormatRenderer(NumberFormat.getScientificFormat()).render(Math.PI)); + } + + public void testNull() { + assertEquals("", new NumberFormatRenderer().render(null)); + } +}
diff --git a/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java b/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java index bdceca6..5436f36 100644 --- a/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java +++ b/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java
@@ -26,6 +26,7 @@ import com.google.gwt.uibinder.attributeparsers.TextAlignConstantParserTest; import com.google.gwt.uibinder.attributeparsers.VerticalAlignmentConstantParserTest; import com.google.gwt.uibinder.elementparsers.AbsolutePanelParserTest; +import com.google.gwt.uibinder.elementparsers.DateLabelParserTest; import com.google.gwt.uibinder.elementparsers.DialogBoxParserTest; import com.google.gwt.uibinder.elementparsers.DisclosurePanelParserTest; import com.google.gwt.uibinder.elementparsers.DockLayoutPanelParserTest; @@ -36,6 +37,7 @@ import com.google.gwt.uibinder.elementparsers.ListBoxParserTest; import com.google.gwt.uibinder.elementparsers.MenuBarParserTest; import com.google.gwt.uibinder.elementparsers.MenuItemParserTest; +import com.google.gwt.uibinder.elementparsers.NumberLabelParserTest; import com.google.gwt.uibinder.elementparsers.StackLayoutPanelParserTest; import com.google.gwt.uibinder.elementparsers.StackPanelParserTest; import com.google.gwt.uibinder.elementparsers.TabLayoutPanelParserTest; @@ -88,6 +90,7 @@ // elementparsers suite.addTestSuite(AbsolutePanelParserTest.class); + suite.addTestSuite(DateLabelParserTest.class); suite.addTestSuite(DialogBoxParserTest.class); suite.addTestSuite(DisclosurePanelParserTest.class); suite.addTestSuite(DockLayoutPanelParserTest.class); @@ -98,6 +101,7 @@ suite.addTestSuite(ListBoxParserTest.class); suite.addTestSuite(MenuBarParserTest.class); suite.addTestSuite(MenuItemParserTest.class); + suite.addTestSuite(NumberLabelParserTest.class); suite.addTestSuite(StackLayoutPanelParserTest.class); suite.addTestSuite(StackPanelParserTest.class); suite.addTestSuite(TabLayoutPanelParserTest.class);
diff --git a/user/test/com/google/gwt/uibinder/elementparsers/DateLabelParserTest.java b/user/test/com/google/gwt/uibinder/elementparsers/DateLabelParserTest.java new file mode 100644 index 0000000..131f614 --- /dev/null +++ b/user/test/com/google/gwt/uibinder/elementparsers/DateLabelParserTest.java
@@ -0,0 +1,324 @@ +/* + * 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.elementparsers; + +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.dev.javac.impl.MockJavaResource; +import com.google.gwt.i18n.client.DateTimeFormat; +import com.google.gwt.i18n.client.TimeZone; +import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat; +import com.google.gwt.uibinder.rebind.FieldWriter; +import com.google.gwt.user.client.ui.DateLabel; + +import junit.framework.TestCase; + +import org.xml.sax.SAXException; + +/** + * Eponymous unit test. + */ +public class DateLabelParserTest extends TestCase { + private static final String PARSED_TYPE = "com.google.gwt.user.client.ui.DateLabel"; + + private static final MockJavaResource DATELABEL_SUBCLASS_NO_CONSTRUCTOR = new MockJavaResource( + "my.MyDateLabel") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package my;\n"); + code.append("import com.google.gwt.user.client.ui.DateLabel;\n"); + code.append("public class MyDateLabel extends DateLabel {\n"); + code.append("}\n"); + return code; + } + }; + private static final MockJavaResource DATELABEL_SUBCLASS_FORMAT_CONSTRUCTOR = new MockJavaResource( + "my.MyConstructedDateLabel") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package my;\n"); + code.append("import com.google.gwt.user.client.ui.DateLabel;\n"); + code.append("import com.google.gwt.i18n.client.DateTimeFormat;\n"); + code.append("public class MyConstructedDateLabel extends DateLabel {\n"); + code.append(" public MyConstructedDateLabel(DateTimeFormat f) { super(f); }"); + code.append("}\n"); + return code; + } + }; + private static final MockJavaResource DATELABEL_SUBCLASS_FORMAT_AND_TZ_CONSTRUCTOR = new MockJavaResource( + "my.MyConstructedDateLabel2") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package my;\n"); + code.append("import com.google.gwt.user.client.ui.DateLabel;\n"); + code.append("import com.google.gwt.i18n.client.DateTimeFormat;\n"); + code.append("import com.google.gwt.i18n.client.TimeZone;\n"); + code.append("public class MyConstructedDateLabel2 extends DateLabel {\n"); + code.append(" public MyConstructedDateLabel2(DateTimeFormat f, TimeZone tz) { super(f, tz); }"); + code.append("}\n"); + return code; + } + }; + private static final MockJavaResource DATELABEL_SUBCLASS_TZ_CONSTRUCTOR = new MockJavaResource( + "my.MyConstructedDateLabel3") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package my;\n"); + code.append("import com.google.gwt.user.client.ui.DateLabel;\n"); + code.append("import com.google.gwt.i18n.client.TimeZone;\n"); + code.append("public class MyConstructedDateLabel3 extends DateLabel {\n"); + code.append(" public MyConstructedDateLabel3(TimeZone tz) { super(); }"); + code.append("}\n"); + return code; + } + }; + private ElementParserTester tester; + + @Override + public void setUp() throws Exception { + super.setUp(); + tester = new ElementParserTester(PARSED_TYPE, new DateLabelParser()); + } + + public void testHappyWithDefaultInstantiableSubclass() + throws UnableToCompleteException, SAXException { + tester = new ElementParserTester("my.MyDateLabel", new DateLabelParser(), + DATELABEL_SUBCLASS_NO_CONSTRUCTOR); + DateLabelParser parser = new DateLabelParser(); + StringBuffer b = new StringBuffer(); + + b.append("<ui:UiBinder xmlns:ui='" + ElementParserTester.BINDER_URI + "'"); + b.append(" xmlns:my='urn:import:my'"); + b.append(" xmlns:g='urn:import:com.google.gwt.user.client.ui'>"); + b.append(" <my:MyDateLabel format='{someDateTimeFormat}' timezone='{someTimeZone}' /> "); + b.append("</ui:UiBinder>"); + + parser.parse(tester.getElem(b.toString(), "my:MyDateLabel"), "fieldName", + tester.parsedType, tester.writer); + FieldWriter w = tester.fieldManager.lookup("fieldName"); + assertNull(w.getInitializer()); + + assertTrue(tester.writer.statements.isEmpty()); + assertNull(tester.logger.died); + } + + public void testHappyWithSubclassWithDateTimeFormatConstructor() + throws UnableToCompleteException, SAXException { + DateLabelParser parser = new DateLabelParser(); + tester = new ElementParserTester("my.MyConstructedDateLabel", + new DateLabelParser(), DATELABEL_SUBCLASS_FORMAT_CONSTRUCTOR); + + StringBuffer b = new StringBuffer(); + + b.append("<ui:UiBinder xmlns:ui='" + ElementParserTester.BINDER_URI + "'"); + b.append(" xmlns:my='urn:import:my'"); + b.append(" xmlns:g='urn:import:com.google.gwt.user.client.ui'>"); + b.append(" <my:MyConstructedDateLabel format='{someDateTimeFormat}' timezone='{someTimeZone}' /> "); + b.append("</ui:UiBinder>"); + + parser.parse(tester.getElem(b.toString(), "my:MyConstructedDateLabel"), + "fieldName", tester.parsedType, tester.writer); + FieldWriter w = tester.fieldManager.lookup("fieldName"); + assertEquals("new my.MyConstructedDateLabel(someDateTimeFormat)", + w.getInitializer()); + + assertTrue(tester.writer.statements.isEmpty()); + assertNull(tester.logger.died); + } + + public void testHappyWithSubclassWithDateTimeFormatAndTimeZoneConstructor() + throws UnableToCompleteException, SAXException { + DateLabelParser parser = new DateLabelParser(); + tester = new ElementParserTester("my.MyConstructedDateLabel2", + new DateLabelParser(), DATELABEL_SUBCLASS_FORMAT_AND_TZ_CONSTRUCTOR); + + StringBuffer b = new StringBuffer(); + + b.append("<ui:UiBinder xmlns:ui='" + ElementParserTester.BINDER_URI + "'"); + b.append(" xmlns:my='urn:import:my'"); + b.append(" xmlns:g='urn:import:com.google.gwt.user.client.ui'>"); + b.append(" <my:MyConstructedDateLabel2 format='{someDateTimeFormat}' timezone='{someTimeZone}' /> "); + b.append("</ui:UiBinder>"); + + parser.parse(tester.getElem(b.toString(), "my:MyConstructedDateLabel2"), + "fieldName", tester.parsedType, tester.writer); + FieldWriter w = tester.fieldManager.lookup("fieldName"); + assertEquals( + "new my.MyConstructedDateLabel2(someDateTimeFormat, someTimeZone)", + w.getInitializer()); + + assertTrue(tester.writer.statements.isEmpty()); + assertNull(tester.logger.died); + } + + public void testHappyWithSubclassWithTimeZoneFormatConstructor() + throws UnableToCompleteException, SAXException { + DateLabelParser parser = new DateLabelParser(); + tester = new ElementParserTester("my.MyConstructedDateLabel3", + new DateLabelParser(), DATELABEL_SUBCLASS_TZ_CONSTRUCTOR); + + StringBuffer b = new StringBuffer(); + + b.append("<ui:UiBinder xmlns:ui='" + ElementParserTester.BINDER_URI + "'"); + b.append(" xmlns:my='urn:import:my'"); + b.append(" xmlns:g='urn:import:com.google.gwt.user.client.ui'>"); + b.append(" <my:MyConstructedDateLabel3 format='{someDateTimeFormat}' timezone='{someTimeZone}' /> "); + b.append("</ui:UiBinder>"); + + parser.parse(tester.getElem(b.toString(), "my:MyConstructedDateLabel3"), + "fieldName", tester.parsedType, tester.writer); + FieldWriter w = tester.fieldManager.lookup("fieldName"); + assertNull(w.getInitializer()); + + assertTrue(tester.writer.statements.isEmpty()); + assertNull(tester.logger.died); + } + + public void testHappyWithNoFormat() throws UnableToCompleteException, + SAXException { + StringBuffer b = new StringBuffer(); + b.append("<g:DateLabel>"); + b.append("</g:DateLabel>"); + + FieldWriter w = tester.parse(b.toString()); + assertNull(w.getInitializer()); + + assertTrue(tester.writer.statements.isEmpty()); + assertNull(tester.logger.died); + } + + public void testHappyWithPredefinedFormat() throws UnableToCompleteException, + SAXException { + StringBuffer b = new StringBuffer(); + b.append("<g:DateLabel predefinedFormat='DATE_MEDIUM'>"); + b.append("</g:DateLabel>"); + + FieldWriter w = tester.parse(b.toString()); + assertEquals("new " + DateLabel.class.getCanonicalName() + "(" + + DateTimeFormat.class.getCanonicalName() + ".getFormat(" + + PredefinedFormat.class.getCanonicalName() + ".DATE_MEDIUM))", + w.getInitializer()); + + assertTrue(tester.writer.statements.isEmpty()); + assertNull(tester.logger.died); + } + + public void testHappyWithTimezoneOffset() throws UnableToCompleteException, + SAXException { + StringBuffer b = new StringBuffer(); + b.append("<g:DateLabel customFormat='zzzz' timezoneOffset='-7200'>"); + b.append("</g:DateLabel>"); + + FieldWriter w = tester.parse(b.toString()); + assertEquals("new " + DateLabel.class.getCanonicalName() + "(" + + DateTimeFormat.class.getCanonicalName() + ".getFormat(\"zzzz\"), " + + TimeZone.class.getCanonicalName() + ".createTimeZone(-7200))", + w.getInitializer()); + + assertTrue(tester.writer.statements.isEmpty()); + assertNull(tester.logger.died); + } + + public void testChokeOnNonDateTimeFormat() throws SAXException { + StringBuffer b = new StringBuffer(); + b.append("<g:DateLabel format='someString' >"); + b.append("</g:DateLabel>"); + + try { + tester.parse(b.toString()); + fail(); + } catch (UnableToCompleteException e) { + assertTrue("Expect to hear about DateTimeFormat", + tester.logger.died.contains("DateTimeFormat")); + } + } + + public void testChokeOnNonTimeZone() throws SAXException { + StringBuffer b = new StringBuffer(); + b.append("<g:DateLabel format='{someDateTimeFormat}' timezone='someString' >"); + b.append("</g:DateLabel>"); + + try { + tester.parse(b.toString()); + fail(); + } catch (UnableToCompleteException e) { + assertTrue("Expect to hear about TimeZone", + tester.logger.died.contains("TimeZone")); + } + } + + public void testChokeOnUnknownPredefinedFormat() throws SAXException { + StringBuffer b = new StringBuffer(); + b.append("<g:DateLabel predefinedFormat='someString' >"); + b.append("</g:DateLabel>"); + + try { + tester.parse(b.toString()); + fail(); + } catch (UnableToCompleteException e) { + assertTrue("Expect to hear about PredefinedFormat", + tester.logger.died.contains("PredefinedFormat")); + } + } + + public void testChokeOnMultipleFormats() throws SAXException { + StringBuffer b = new StringBuffer(); + b.append("<g:DateLabel predefinedFormat='DATE_FULL' customFormat='MM/dd'>"); + b.append("</g:DateLabel>"); + + try { + tester.parse(b.toString()); + fail(); + } catch (UnableToCompleteException e) { + assertTrue( + tester.logger.died, + tester.logger.died.contains(DateLabelParser.AT_MOST_ONE_SPECIFIED_FORMAT)); + } + } + + public void testChokeOnMultipleTimeZones() throws SAXException { + StringBuffer b = new StringBuffer(); + b.append("<g:DateLabel format='{someDateTimeFormat}' timezone='{someTimeZone}' timezoneOffset='-7200'>"); + b.append("</g:DateLabel>"); + + try { + tester.parse(b.toString()); + fail(); + } catch (UnableToCompleteException e) { + assertTrue( + tester.logger.died, + tester.logger.died.contains(DateLabelParser.AT_MOST_ONE_SPECIFIED_TIME_ZONE)); + } + } + + public void testChokeOnTimeZoneWithoutSpecifiedFormat() throws SAXException { + StringBuffer b = new StringBuffer(); + b.append("<g:DateLabel timezoneOffset='-7200'>"); + b.append("</g:DateLabel>"); + + try { + tester.parse(b.toString()); + fail(); + } catch (UnableToCompleteException e) { + assertTrue( + tester.logger.died, + tester.logger.died.contains(DateLabelParser.NO_TIMEZONE_WITHOUT_SPECIFIED_FORMAT)); + } + } +}
diff --git a/user/test/com/google/gwt/uibinder/elementparsers/NumberLabelParserTest.java b/user/test/com/google/gwt/uibinder/elementparsers/NumberLabelParserTest.java new file mode 100644 index 0000000..41fff15 --- /dev/null +++ b/user/test/com/google/gwt/uibinder/elementparsers/NumberLabelParserTest.java
@@ -0,0 +1,273 @@ +/* + * 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.elementparsers; + +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.dev.javac.impl.MockJavaResource; +import com.google.gwt.i18n.client.NumberFormat; +import com.google.gwt.uibinder.rebind.FieldWriter; +import com.google.gwt.user.client.ui.NumberLabel; + +import junit.framework.TestCase; + +import org.xml.sax.SAXException; + +/** + * Eponymous unit test. + */ +public class NumberLabelParserTest extends TestCase { + private static final String PARSED_TYPE = "com.google.gwt.user.client.ui.NumberLabel"; + + private static final MockJavaResource NUMBERLABEL_SUBCLASS_NO_CONSTRUCTOR = new MockJavaResource( + "my.MyNumberLabel") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package my;\n"); + code.append("import com.google.gwt.user.client.ui.NumberLabel;\n"); + code.append("public class MyNumberLabel extends NumberLabel {\n"); + code.append("}\n"); + return code; + } + }; + private static final MockJavaResource NUMBERLABEL_SUBCLASS_FORMAT_CONSTRUCTOR = new MockJavaResource( + "my.MyConstructedNumberLabel") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package my;\n"); + code.append("import com.google.gwt.user.client.ui.NumberLabel;\n"); + code.append("import com.google.gwt.i18n.client.NumberFormat;\n"); + code.append("public class MyConstructedNumberLabel extends NumberLabel {\n"); + code.append(" public MyConstructedNumberLabel(NumberFormat f) { super(f); }"); + code.append("}\n"); + return code; + } + }; + private ElementParserTester tester; + + @Override + public void setUp() throws Exception { + super.setUp(); + tester = new ElementParserTester(PARSED_TYPE, new NumberLabelParser()); + } + + public void testHappyWithDefaultInstantiableSubclass() + throws UnableToCompleteException, SAXException { + tester = new ElementParserTester("my.MyNumberLabel", + new NumberLabelParser(), NUMBERLABEL_SUBCLASS_NO_CONSTRUCTOR); + NumberLabelParser parser = new NumberLabelParser(); + StringBuffer b = new StringBuffer(); + + b.append("<ui:UiBinder xmlns:ui='" + ElementParserTester.BINDER_URI + "'"); + b.append(" xmlns:my='urn:import:my'"); + b.append(" xmlns:g='urn:import:com.google.gwt.user.client.ui'>"); + b.append(" <my:MyNumberLabel format='{someDateTimeFormat}' currencyData='{someCurrencyData}' /> "); + b.append("</ui:UiBinder>"); + + parser.parse(tester.getElem(b.toString(), "my:MyNumberLabel"), "fieldName", + tester.parsedType, tester.writer); + FieldWriter w = tester.fieldManager.lookup("fieldName"); + assertNull(w.getInitializer()); + + assertTrue(tester.writer.statements.isEmpty()); + assertNull(tester.logger.died); + } + + public void testHappyWithSubclassWithNumberFormatConstructor() + throws UnableToCompleteException, SAXException { + NumberLabelParser parser = new NumberLabelParser(); + tester = new ElementParserTester("my.MyConstructedNumberLabel", + new NumberLabelParser(), NUMBERLABEL_SUBCLASS_FORMAT_CONSTRUCTOR); + + StringBuffer b = new StringBuffer(); + + b.append("<ui:UiBinder xmlns:ui='" + ElementParserTester.BINDER_URI + "'"); + b.append(" xmlns:my='urn:import:my'"); + b.append(" xmlns:g='urn:import:com.google.gwt.user.client.ui'>"); + b.append(" <my:MyConstructedNumberLabel format='{someDateTimeFormat}' /> "); + b.append("</ui:UiBinder>"); + + parser.parse(tester.getElem(b.toString(), "my:MyConstructedNumberLabel"), + "fieldName", tester.parsedType, tester.writer); + FieldWriter w = tester.fieldManager.lookup("fieldName"); + assertEquals("new my.MyConstructedNumberLabel(someDateTimeFormat)", + w.getInitializer()); + + assertTrue(tester.writer.statements.isEmpty()); + assertNull(tester.logger.died); + } + + public void testHappyWithNoFormat() throws UnableToCompleteException, + SAXException { + StringBuffer b = new StringBuffer(); + b.append("<g:NumberLabel>"); + b.append("</g:NumberLabel>"); + + FieldWriter w = tester.parse(b.toString()); + assertNull(w.getInitializer()); + + assertTrue(tester.writer.statements.isEmpty()); + assertNull(tester.logger.died); + } + + public void testHappyWithCustomFormatAndCurrency() + throws UnableToCompleteException, SAXException { + StringBuffer b = new StringBuffer(); + b.append("<g:NumberLabel customFormat='#\u00A0\u00A4' currencyCode='EUR'>"); + b.append("</g:NumberLabel>"); + + FieldWriter w = tester.parse(b.toString()); + assertEquals("new " + NumberLabel.class.getCanonicalName() + "(" + + NumberFormat.class.getCanonicalName() + ".getFormat(\"#\u00A0\u00A4\", \"EUR\"))", + w.getInitializer()); + + assertTrue(tester.writer.statements.isEmpty()); + assertNull(tester.logger.died); + } + + public void testHappyWithCurrencyPredefinedFormat() + throws UnableToCompleteException, SAXException { + StringBuffer b = new StringBuffer(); + b.append("<g:NumberLabel predefinedFormat='CURRENCY'>"); + b.append("</g:NumberLabel>"); + + FieldWriter w = tester.parse(b.toString()); + assertEquals("new " + NumberLabel.class.getCanonicalName() + "(" + + NumberFormat.class.getCanonicalName() + ".getCurrencyFormat())", + w.getInitializer()); + + assertTrue(tester.writer.statements.isEmpty()); + assertNull(tester.logger.died); + } + + public void testHappyWithCurrencyPredefinedFormatAndCurrencCode() + throws UnableToCompleteException, SAXException { + StringBuffer b = new StringBuffer(); + b.append("<g:NumberLabel predefinedFormat='CURRENCY' currencyCode='EUR'>"); + b.append("</g:NumberLabel>"); + + FieldWriter w = tester.parse(b.toString()); + assertEquals("new " + NumberLabel.class.getCanonicalName() + "(" + + NumberFormat.class.getCanonicalName() + ".getCurrencyFormat(\"EUR\"))", + w.getInitializer()); + + assertTrue(tester.writer.statements.isEmpty()); + assertNull(tester.logger.died); + } + + public void testChokeOnNonDateTimeFormat() throws SAXException { + StringBuffer b = new StringBuffer(); + b.append("<g:NumberLabel format='someString' >"); + b.append("</g:NumberLabel>"); + + try { + tester.parse(b.toString()); + fail(); + } catch (UnableToCompleteException e) { + assertTrue("Expect to hear about DateTimeFormat", + tester.logger.died.contains("DateTimeFormat")); + } + } + + public void testChokeOnNonCurrencyData() throws SAXException { + StringBuffer b = new StringBuffer(); + b.append("<g:NumberLabel predefinedFormat='CURRENCY' currencyData='someString' >"); + b.append("</g:NumberLabel>"); + + try { + tester.parse(b.toString()); + fail(); + } catch (UnableToCompleteException e) { + assertTrue("Expect to hear about CurrencyData", + tester.logger.died.contains("CurrencyData")); + } + } + + public void testChokeOnUnknownPredefinedFormat() throws SAXException { + StringBuffer b = new StringBuffer(); + b.append("<g:NumberLabel predefinedFormat='someString' >"); + b.append("</g:NumberLabel>"); + + try { + tester.parse(b.toString()); + fail(); + } catch (UnableToCompleteException e) { + assertTrue(tester.logger.died, tester.logger.died.contains(String.format( + NumberLabelParser.UNKNOWN_PREDEFINED_FORMAT, "someString"))); + } + } + + public void testChokeOnMultipleFormats() throws SAXException { + StringBuffer b = new StringBuffer(); + b.append("<g:NumberLabel predefinedFormat='SCIENTIFIC' customFormat='#'>"); + b.append("</g:NumberLabel>"); + + try { + tester.parse(b.toString()); + fail(); + } catch (UnableToCompleteException e) { + assertTrue( + tester.logger.died, + tester.logger.died.contains(NumberLabelParser.AT_MOST_ONE_SPECIFIED_FORMAT)); + } + } + + public void testChokeOnMultipleCurrencies() throws SAXException { + StringBuffer b = new StringBuffer(); + b.append("<g:NumberLabel predefinedFormat='CURRENCY' currencyData='{someCurrencyData}' currencyCode='EUR'>"); + b.append("</g:NumberLabel>"); + + try { + tester.parse(b.toString()); + fail(); + } catch (UnableToCompleteException e) { + assertTrue( + tester.logger.died, + tester.logger.died.contains(NumberLabelParser.AT_MOST_ONE_SPECIFIED_CURRENCY)); + } + } + + public void testChokeOnCurrencyWithoutSpecifiedFormat() throws SAXException { + StringBuffer b = new StringBuffer(); + b.append("<g:NumberLabel currencyCode='EUR'>"); + b.append("</g:NumberLabel>"); + + try { + tester.parse(b.toString()); + fail(); + } catch (UnableToCompleteException e) { + assertTrue( + tester.logger.died, + tester.logger.died.contains(NumberLabelParser.NO_CURRENCY_WITHOUT_SPECIFIED_FORMAT)); + } + } + + public void testChokeOnCurrencyWithPredefinedFormat() throws SAXException { + StringBuffer b = new StringBuffer(); + b.append("<g:NumberLabel predefinedFormat='PERCENT' currencyCode='EUR'>"); + b.append("</g:NumberLabel>"); + + try { + tester.parse(b.toString()); + fail(); + } catch (UnableToCompleteException e) { + assertTrue( + tester.logger.died, + tester.logger.died.contains(NumberLabelParser.NO_CURRENCY_WITH_PREDEFINED_FORMAT)); + } + } +}
diff --git a/user/test/com/google/gwt/uibinder/test/UiJavaResources.java b/user/test/com/google/gwt/uibinder/test/UiJavaResources.java index 91852ee..ad1280a 100644 --- a/user/test/com/google/gwt/uibinder/test/UiJavaResources.java +++ b/user/test/com/google/gwt/uibinder/test/UiJavaResources.java
@@ -1,12 +1,12 @@ /* * Copyright 2009 Google Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -19,6 +19,7 @@ import com.google.gwt.dev.javac.impl.MockJavaResource; import com.google.gwt.dev.resource.Resource; import com.google.gwt.dev.util.collect.HashSet; +import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat; import java.util.Arrays; import java.util.Set; @@ -95,6 +96,53 @@ return code; } }; + public static final MockJavaResource CURRENCY_DATA = new MockJavaResource( + "com.google.gwt.i18n.client.CurrencyData") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package com.google.gwt.i18n.client;\n"); + code.append("public class CurrencyData {\n"); + code.append("}\n"); + return code; + } + }; + public static final MockJavaResource DATE_LABEL = new MockJavaResource( + "com.google.gwt.user.client.ui.DateLabel") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package com.google.gwt.user.client.ui;\n"); + code.append("import com.google.gwt.i18n.client.DateTimeFormat;\n"); + code.append("import com.google.gwt.i18n.client.TimeZone;\n"); + code.append("public class DateLabel extends ValueLabel {\n"); + code.append(" public DateLabel() { super(null); } "); + code.append(" public DateLabel(DateTimeFormat format) { super(null); } "); + code.append(" public DateLabel(DateTimeFormat format, TimeZone timeZone) { super(null); } "); + code.append("}\n"); + return code; + } + }; + public static final MockJavaResource DATE_TIME_FORMAT = new MockJavaResource( + "com.google.gwt.i18n.client.DateTimeFormat") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package com.google.gwt.i18n.client;\n"); + code.append("public class DateTimeFormat {\n"); + code.append(" public static enum PredefinedFormat {\n"); + PredefinedFormat[] values = PredefinedFormat.values(); + for (int i = 0; i < values.length; i++) { + code.append(" ").append(values[i].name()); + if (i < values.length - 1) { + code.append(",\n"); + } + } + code.append(" }\n"); + code.append("}\n"); + return code; + } + }; public static final MockJavaResource DIALOG_BOX = new MockJavaResource( "com.google.gwt.user.client.ui.DialogBox") { @Override @@ -352,6 +400,42 @@ return code; } }; + public static final MockJavaResource NUMBER_LABEL = new MockJavaResource( + "com.google.gwt.user.client.ui.NumberLabel") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package com.google.gwt.user.client.ui;\n"); + code.append("import com.google.gwt.i18n.client.NumberFormat;\n"); + code.append("public class NumberLabel extends ValueLabel {\n"); + code.append(" public NumberLabel() { super(null); } "); + code.append(" public NumberLabel(NumberFormat format) { super(null); } "); + code.append("}\n"); + return code; + } + }; + public static final MockJavaResource NUMBER_FORMAT = new MockJavaResource( + "com.google.gwt.i18n.client.NumberFormat") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package com.google.gwt.i18n.client;\n"); + code.append("public class NumberFormat {\n"); + code.append("}\n"); + return code; + } + }; + public static final MockJavaResource RENDERER = new MockJavaResource( + "com.google.gwt.text.shared.Renderer") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package com.google.gwt.text.shared;\n"); + code.append("public class Renderer<T> {\n"); + code.append("}\n"); + return code; + } + }; public static final MockJavaResource SPLIT_LAYOUT_PANEL = new MockJavaResource( "com.google.gwt.user.client.ui.SplitLayoutPanel") { @Override @@ -432,6 +516,17 @@ return code; } }; + public static final MockJavaResource TIME_ZONE = new MockJavaResource( + "com.google.gwt.i18n.client.TimeZone") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package com.google.gwt.i18n.client;\n"); + code.append("public class TimeZone {\n"); + code.append("}\n"); + return code; + } + }; public static final MockJavaResource UI_BINDER = new MockJavaResource( "com.google.gwt.uibinder.client.UiBinder") { @Override @@ -467,6 +562,19 @@ return code; } }; + public static final MockJavaResource VALUE_LABEL = new MockJavaResource( + "com.google.gwt.user.client.ui.ValueLabel") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package com.google.gwt.user.client.ui;\n"); + code.append("import com.google.gwt.text.shared.Renderer;\n"); + code.append("public class ValueLabel extends Widget {\n"); + code.append(" public ValueLabel(Renderer renderer) {} "); + code.append("}\n"); + return code; + } + }; public static final MockJavaResource WIDGET = new MockJavaResource( "com.google.gwt.user.client.ui.Widget") { @Override @@ -491,6 +599,9 @@ rtn.add(CLICK_EVENT); rtn.add(CLICK_HANDLER); rtn.add(COMMAND); + rtn.add(CURRENCY_DATA); + rtn.add(DATE_LABEL); + rtn.add(DATE_TIME_FORMAT); rtn.add(DIALOG_BOX); rtn.add(DISCLOSURE_PANEL); rtn.add(DOCK_LAYOUT_PANEL); @@ -512,6 +623,9 @@ rtn.add(MENU_ITEM_SEPARATOR); rtn.add(MOUSE_OVER_EVENT); rtn.add(MOUSE_OVER_HANDLER); + rtn.add(NUMBER_LABEL); + rtn.add(NUMBER_FORMAT); + rtn.add(RENDERER); rtn.add(SPLIT_LAYOUT_PANEL); rtn.add(STACK_LAYOUT_PANEL); rtn.add(STACK_PANEL); @@ -519,9 +633,11 @@ rtn.add(TAB_LAYOUT_PANEL); rtn.add(TAB_PANEL); rtn.add(TEXT_BOX_BASE); + rtn.add(TIME_ZONE); rtn.add(UI_OBJECT); rtn.add(UI_BINDER); rtn.add(UI_FACTORY); + rtn.add(VALUE_LABEL); rtn.add(WIDGET); return rtn; }
diff --git a/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java b/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java index 7201ea0..74426a9 100644 --- a/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java +++ b/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java
@@ -28,14 +28,17 @@ import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.ui.AbsolutePanel; import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.DateLabel; import com.google.gwt.user.client.ui.DisclosurePanel; import com.google.gwt.user.client.ui.HTML; 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.NamedFrame; +import com.google.gwt.user.client.ui.NumberLabel; import com.google.gwt.user.client.ui.RadioButton; import com.google.gwt.user.client.ui.StackPanel; +import com.google.gwt.user.client.ui.ValueLabel; import com.google.gwt.user.client.ui.Widget; /** @@ -469,6 +472,21 @@ assertNotNull("NamedFrame exists", p); } + public void testDateLabel() { + DateLabel p = widgetUi.myDateLabel; + assertNotNull("DateLabel exists", p); + } + + public void testNumberLabel() { + NumberLabel<Float> p = widgetUi.myNumberLabel; + assertNotNull("NumberLabel exists", p); + } + + public void testValueLabel() { + ValueLabel<Double> p = widgetUi.myValueLabel; + assertNotNull("ValueLabel exists", p); + } + public void testStringAttributeIgnoresStaticSetter() { // Assumes setPopupText() is overloaded such that there is a static // setPopupText(Foo, String) method.
diff --git a/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.java b/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.java index 766ce98..9cd127e 100644 --- a/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.java +++ b/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.java
@@ -27,12 +27,15 @@ import com.google.gwt.resources.client.DataResource; import com.google.gwt.resources.client.ImageResource; import com.google.gwt.resources.client.CssResource.Shared; +import com.google.gwt.text.client.DoubleRenderer; +import com.google.gwt.text.shared.Renderer; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiFactory; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.ui.AbsolutePanel; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.DateLabel; import com.google.gwt.user.client.ui.DisclosurePanel; import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.HTML; @@ -45,11 +48,13 @@ import com.google.gwt.user.client.ui.MenuBar; import com.google.gwt.user.client.ui.MenuItem; import com.google.gwt.user.client.ui.NamedFrame; +import com.google.gwt.user.client.ui.NumberLabel; import com.google.gwt.user.client.ui.PushButton; import com.google.gwt.user.client.ui.RadioButton; import com.google.gwt.user.client.ui.StackPanel; import com.google.gwt.user.client.ui.ToggleButton; import com.google.gwt.user.client.ui.Tree; +import com.google.gwt.user.client.ui.ValueLabel; import com.google.gwt.user.client.ui.Widget; /** @@ -171,6 +176,11 @@ @UiField Widget myAbsolutePanelItemB; @UiField Widget myAbsolutePanelItemC; @UiField NamedFrame myNamedFrame; + @UiField DateLabel myDateLabel; + @UiField NumberLabel<Float> myNumberLabel; + @UiField(provided = true) @SuppressWarnings("unchecked") + Renderer doubleRenderer = DoubleRenderer.instance(); + @UiField ValueLabel<Double> myValueLabel; public WidgetBasedUi() { external.style().ensureInjected();
diff --git a/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.ui.xml b/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.ui.xml index b4d3710..08a6b83 100644 --- a/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.ui.xml +++ b/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.ui.xml
@@ -72,6 +72,10 @@ for a resource to provide arbitrary objects to arbitrary attributes (look for FooLabel) </ui:with> +<ui:with field='doubleRenderer' type='com.google.gwt.text.shared.Renderer'> + Used to test ValueLabel's @UiConstructor +</ui:with> + <ui:import field='com.google.gwt.uibinder.test.client.Constants.CONST_FOO'> Tests the static import of a single constant into the local namespace. </ui:import> @@ -83,6 +87,7 @@ <ui:import field='com.google.gwt.uibinder.test.client.Constants.MyEnum.*'> Tests the static import of an enum into the local namespace. </ui:import> + <!-- Tests creating a CssResource from an external file. --> @@ -303,7 +308,8 @@ <ui:attribute name="text" description="radio button name"/> </demo:PointlessRadioButtonSubclass> - <gwt:HorizontalPanel ui:field="myHorizontalPanel" horizontalAlignment="ALIGN_LEFT" verticalAlignment="ALIGN_MIDDLE"> + <gwt:HorizontalPanel ui:field="myHorizontalPanel" + horizontalAlignment="ALIGN_LEFT" verticalAlignment="ALIGN_MIDDLE"> <gwt:Cell><gwt:HTMLPanel> <p> ... a StackPanel ... </p> @@ -640,6 +646,12 @@ <gwt:NamedFrame ui:field='myNamedFrame' name='myName'/> + <gwt:DateLabel ui:field='myDateLabel' predefinedFormat='DATE_FULL' /> + + <gwt:NumberLabel ui:field='myNumberLabel' predefinedFormat='SCIENTIFIC' /> + + <gwt:ValueLabel ui:field='myValueLabel' renderer='{doubleRenderer}' /> + </gwt:HTMLPanel> </gwt:Dock> </gwt:DockPanel>
diff --git a/user/test/com/google/gwt/user/client/ui/LabelTest.java b/user/test/com/google/gwt/user/client/ui/LabelTest.java index e697ea8..d43d87e 100644 --- a/user/test/com/google/gwt/user/client/ui/LabelTest.java +++ b/user/test/com/google/gwt/user/client/ui/LabelTest.java
@@ -22,7 +22,6 @@ import com.google.gwt.i18n.client.BidiUtils; import com.google.gwt.i18n.client.HasDirection.Direction; import com.google.gwt.junit.client.GWTTestCase; -import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.google.gwt.user.client.ui.HasHorizontalAlignment.AutoHorizontalAlignmentConstant; import com.google.gwt.user.client.ui.HasHorizontalAlignment.HorizontalAlignmentConstant; @@ -143,19 +142,6 @@ assertEquals(Direction.LTR, label.getDirection()); } - @SuppressWarnings("deprecation") - public void testSetSafeHtml() { - Label label = new Label("foo"); - label.setHTML(SafeHtmlUtils.fromSafeConstant(html1)); - - assertEquals(html1, label.getTextOrHtml(true).toLowerCase()); - - label.setHTML(SafeHtmlUtils.fromSafeConstant(html2), Direction.LTR); - - assertEquals(html2, label.getTextOrHtml(true).toLowerCase()); - assertEquals(Direction.LTR, label.getDirection()); - } - /** * Create a div and attach it to the {@link RootPanel}. *