Merging releases/1.5@r3125:r3170 into trunk. svn merge -r3125:3170 https://google-web-toolkit.googlecode.com/svn/releases/1.5 . git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@3173 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/DelegateMembers.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/DelegateMembers.java index d595bd5..3ae97b1 100644 --- a/dev/core/src/com/google/gwt/core/ext/typeinfo/DelegateMembers.java +++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/DelegateMembers.java
@@ -65,8 +65,7 @@ protected Map<String, JField> doGetFields() { if (lazyFields != null) { /* - * Return if the fields are being initialized or have been - * initialized. + * Return if the fields are being initialized or have been initialized. */ return lazyFields; } @@ -86,8 +85,7 @@ protected Map<String, List<JMethod>> doGetMethods() { if (lazyMethods != null) { /* - * Return if the methods are being initialized or have been - * initialized. + * Return if the methods are being initialized or have been initialized. */ return lazyMethods; } @@ -98,12 +96,21 @@ JMethod newMethod = new JMethod(getParentType(), baseMethod); initializeParams(baseMethod, newMethod); newMethod.setReturnType(substitution.getSubstitution(baseMethod.getReturnType())); + initializeExceptions(baseMethod, newMethod); addMethod(newMethod); } return lazyMethods; } + private void initializeExceptions(JAbstractMethod srcMethod, + JAbstractMethod newMethod) { + for (JType thrown : srcMethod.getThrows()) { + // exceptions cannot be parameterized; just copy them over + newMethod.addThrows(thrown); + } + } + private void initializeParams(JAbstractMethod srcMethod, JAbstractMethod newMethod) { for (JParameter srcParam : srcMethod.getParameters()) {
diff --git a/dev/core/src/com/google/gwt/core/linker/IFrameTemplate.js b/dev/core/src/com/google/gwt/core/linker/IFrameTemplate.js index 177eaa9..f304cff 100644 --- a/dev/core/src/com/google/gwt/core/linker/IFrameTemplate.js +++ b/dev/core/src/com/google/gwt/core/linker/IFrameTemplate.js
@@ -413,6 +413,9 @@ // Script resources are injected here // __MODULE_SCRIPTS_END__ + // The 'defer' attribute here is a workaround for strange IE behavior where + // <script> tags that are doc.writ()en execute *immediately*, rather than + // in document-order, as they should. It has no effect on other browsers. $doc.write('<script defer="defer">__MODULE_FUNC__.onInjectionDone(\'__MODULE_NAME__\')</script>'); }
diff --git a/dev/core/src/com/google/gwt/core/linker/XSTemplate.js b/dev/core/src/com/google/gwt/core/linker/XSTemplate.js index 826d553..2d5153f 100644 --- a/dev/core/src/com/google/gwt/core/linker/XSTemplate.js +++ b/dev/core/src/com/google/gwt/core/linker/XSTemplate.js
@@ -353,17 +353,26 @@ // Script resources are injected here // __MODULE_SCRIPTS_END__ - $doc.write('<script defer="defer">' + // This is a bit ugly, but serves a purpose. We need to ensure that the stats + // script runs before the compiled script. If they are both doc.write()n in + // sequence, that should be the effect. Except on IE it turns out that a + // script injected via doc.write() can execute immediately! Adding 'defer' + // attributes to both seemed to fix this, but caused startup problems for + // some apps. The final solution was simply to inject the compiled script + // from *within* the stats script, guaranteeing order at the expense of near + // total inscrutability :( + var compiledScriptTag = '"<script src=\\"' + base + strongName + '\\"></scr" + "ipt>"'; + $doc.write('<script><!--\n' + 'window.__gwtStatsEvent && window.__gwtStatsEvent({' - + 'moduleName:\'__MODULE_NAME__\', subSystem:\'startup\',' - + 'evtGroup: \'loadExternalRefs\', millis:(new Date()).getTime(),' - + 'type: \'end\'});' + + 'moduleName:"__MODULE_NAME__", subSystem:"startup",' + + 'evtGroup: "loadExternalRefs", millis:(new Date()).getTime(),' + + 'type: "end"});' + 'window.__gwtStatsEvent && window.__gwtStatsEvent({' - + 'moduleName:\'__MODULE_NAME__\', subSystem:\'startup\',' - + 'evtGroup: \'moduleStartup\', millis:(new Date()).getTime(),' - + 'type: \'moduleRequested\'});' - + '</script>' - + '<script defer="defer" src="' + base + strongName + '"></script>'); + + 'moduleName:"__MODULE_NAME__", subSystem:"startup",' + + 'evtGroup: "moduleStartup", millis:(new Date()).getTime(),' + + 'type: "moduleRequested"});' + + 'document.write(' + compiledScriptTag + ');' + + '\n--></script>'); } // Called from compiled code to hook the window's resize & load events (the
diff --git a/dev/core/src/com/google/gwt/dev/jdt/TypeRefVisitor.java b/dev/core/src/com/google/gwt/dev/jdt/TypeRefVisitor.java index df9510e..d72f031 100644 --- a/dev/core/src/com/google/gwt/dev/jdt/TypeRefVisitor.java +++ b/dev/core/src/com/google/gwt/dev/jdt/TypeRefVisitor.java
@@ -66,6 +66,16 @@ @Override public void endVisit(MessageSend messageSend, BlockScope scope) { + /* + * Counterintuitive: there's a reason we only need to record a reference for + * static method calls. For any non-static method call, there must be a + * qualifying instance expression. When that instance expression is visited, + * the type reference to its declared type will be recorded. Thus, recording + * for each instance call is unnecessary. + * + * Note: when we tried recording for instance calls, we would get a null + * scope in some cases, which would cause compiler errors. + */ if (messageSend.binding != null && messageSend.binding.isStatic()) { maybeDispatch(scope, messageSend, messageSend.actualReceiverType); }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CompoundAssignmentNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CompoundAssignmentNormalizer.java index 4ec5616..5b18dcb 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/CompoundAssignmentNormalizer.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CompoundAssignmentNormalizer.java
@@ -36,7 +36,9 @@ import com.google.gwt.dev.jjs.ast.js.JMultiExpression; import java.util.ArrayList; +import java.util.IdentityHashMap; import java.util.List; +import java.util.Map; import java.util.Stack; /** @@ -57,12 +59,12 @@ * * <p> * If the <code>reuseTemps</code> constructor parameter is set to - * <code>false</code>, then temps will be reused aggressively, even when - * their types do not match the values assigned to them. To determine when a - * temp can be reused, the current implementation uses a notion of "temp usage - * scopes". Every time a temporary variable is allocated, it is recorded in the - * current temp usage scope. Once the current temp usage scope is exited, all of - * its temps become available for use for other purposes. + * <code>true</code>, then temps of the correct type will be reused once they + * become available. To determine when a temp can be reused, the current + * implementation uses a notion of "temp usage scopes". Every time a temporary + * variable is allocated, it is recorded in the current temp usage scope. Once + * the current temp usage scope is exited, all of its temps become available for + * use for other purposes. * </p> */ public abstract class CompoundAssignmentNormalizer { @@ -290,16 +292,87 @@ } } - private static int localCounter; + /** + * For some particular type, tracks all usage of temporary local variables of + * that type within the current method. + */ + private static class TempLocalTracker { + /** + * Temps previously created in nested usage scopes that are now available + * for reuse by subsequent code. + */ + private List<JLocal> reusable = new ArrayList<JLocal>(); + + /** + * Temps in use in active scopes. They cannot currently be reused; however, + * any time a scope is exited, the set of locals at the top of stack is + * popped off and all contained locals become reusable. The top of stack + * correlates to the current scope. + */ + private Stack<List<JLocal>> usageStack = new Stack<List<JLocal>>(); + + public TempLocalTracker() { + /* + * Due to the lazy-creation nature, we must assume upon creation that + * we're already in a valid scope (and thus create the initial empty scope + * ourselves). + */ + enterScope(); + } + + public void enterScope() { + usageStack.push(new ArrayList<JLocal>()); + } + + public void exitScope() { + /* + * Due to the lazy-creation nature, the program flow might be several + * levels deep already when this object is created. Since we don't know + * how many exitScope() calls to accept, we must be empty-tolerant. In + * other words, this isn't naively defensive programming. :) + */ + if (!usageStack.isEmpty()) { + List<JLocal> freed = usageStack.pop(); + reusable.addAll(freed); + } + } + + public JLocal tryGetReusableLocal() { + if (!reusable.isEmpty()) { + return reusable.remove(reusable.size() - 1); + } + return null; + } + + public void useLocal(JLocal local) { + usageStack.peek().add(local); + } + } + protected final JProgram program; private final CloneExpressionVisitor cloner; private JMethodBody currentMethodBody; + /** + * Counter to generate a unique name for each temporary within the current + * method. + */ + private int localCounter; + + /** + * Map of type onto lazily-created local tracker. + */ + private Map<JType, TempLocalTracker> localTrackers = new IdentityHashMap<JType, TempLocalTracker>(); + + /** + * If <code>true</code>, reuse temps. The pre-optimization subclass does + * not reuse temps because doing so can defeat optimizations because different + * uses impact each other and we do nothing to disambiguate usage. After + * optimizations, it makes sense to reuse temps to reduce code size and memory + * consumption of the output. + */ private final boolean reuseTemps; - private List<JLocal> tempLocals; - private int tempLocalsIndex; - private Stack<Integer> usageScopeStarts; protected CompoundAssignmentNormalizer(JProgram program, boolean reuseTemps) { this.program = program; @@ -322,6 +395,8 @@ return lhs; } + protected abstract String getTempPrefix(); + protected abstract boolean shouldBreakUp(JBinaryOperation x); protected abstract boolean shouldBreakUp(JPostfixOperation x); @@ -329,32 +404,47 @@ protected abstract boolean shouldBreakUp(JPrefixOperation x); private void clearLocals() { - tempLocals = new ArrayList<JLocal>(); - tempLocalsIndex = 0; - usageScopeStarts = new Stack<Integer>(); + localCounter = 0; + localTrackers.clear(); } private void enterTempUsageScope() { - usageScopeStarts.push(tempLocalsIndex); + for (TempLocalTracker tracker : localTrackers.values()) { + tracker.enterScope(); + } } private void exitTempUsageScope() { - tempLocalsIndex = usageScopeStarts.pop(); + for (TempLocalTracker tracker : localTrackers.values()) { + tracker.exitScope(); + } } /** * Allocate a temporary local variable. */ private JLocal getTempLocal(JType type) { - if (reuseTemps) { - if (tempLocalsIndex < tempLocals.size()) { - return tempLocals.get(tempLocalsIndex++); - } + TempLocalTracker tracker = localTrackers.get(type); + if (tracker == null) { + tracker = new TempLocalTracker(); + localTrackers.put(type, tracker); } - JLocal newTemp = program.createLocal(null, - ("$t" + localCounter++).toCharArray(), type, false, currentMethodBody); - tempLocals.add(newTemp); - return newTemp; + JLocal temp = null; + if (reuseTemps) { + /* + * If the return is non-null, we now "own" the returned JLocal; it's + * important to call tracker.useLocal() on the returned value (below). + */ + temp = tracker.tryGetReusableLocal(); + } + + if (temp == null) { + temp = program.createLocal(null, + (getTempPrefix() + localCounter++).toCharArray(), type, false, + currentMethodBody); + } + tracker.useLocal(temp); + return temp; } }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java index b179a14..5de3ae9 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
@@ -993,6 +993,23 @@ } } + /** + * If the effect of <code>statement</code> is to immediately do a break, + * then return the {@link JBreakStatement} corresponding to that break. + */ + private JBreakStatement findUnconditionalBreak(JStatement statement) { + if (statement instanceof JBreakStatement) { + return (JBreakStatement) statement; + } else if (statement instanceof JBlock) { + JBlock block = (JBlock) statement; + List<JStatement> blockStmts = block.statements; + if (blockStmts.size() > 0 && isUnconditionalBreak(blockStmts.get(0))) { + return (JBreakStatement) blockStmts.get(0); + } + } + return null; + } + private boolean hasNoDefaultCase(JSwitchStatement x) { JBlock body = x.getBody(); boolean inDefault = false; @@ -1002,7 +1019,7 @@ if (caseStmt.getExpr() == null) { inDefault = true; } - } else if (isUnconditionalBreak(statement)) { + } else if (isUnconditionalUnlabeledBreak(statement)) { inDefault = false; } else { // We have some code to execute other than a break. @@ -1134,15 +1151,16 @@ } private boolean isUnconditionalBreak(JStatement statement) { - if (statement instanceof JBreakStatement) { - return true; - } else if (statement instanceof JBlock) { - JBlock block = (JBlock) statement; - List<JStatement> blockStmts = block.statements; - return blockStmts.size() > 0 && isUnconditionalBreak(blockStmts.get(0)); - } else { - return false; - } + return findUnconditionalBreak(statement) != null; + } + + private boolean isUnconditionalUnlabeledBreak(JStatement statement) { + JBreakStatement breakStat = findUnconditionalBreak(statement); + return (breakStat != null) && (breakStat.getLabel() == null); + } + + private <T> T last(List<T> statements) { + return statements.get(statements.size() - 1); } private Class<?> mapType(JType type) { @@ -1192,7 +1210,8 @@ } // Remove a trailing break statement from a case block - if (lastWasBreak && body.statements.size() > 0) { + if (body.statements.size() > 0 + && isUnconditionalUnlabeledBreak(last(body.statements))) { body.statements.remove(body.statements.size() - 1); didChange = true; }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/FixAssignmentToUnbox.java b/dev/core/src/com/google/gwt/dev/jjs/impl/FixAssignmentToUnbox.java index a5f9d9d..130d511 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/FixAssignmentToUnbox.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/FixAssignmentToUnbox.java
@@ -62,6 +62,11 @@ } @Override + protected String getTempPrefix() { + return "$tunbox"; + } + + @Override protected boolean shouldBreakUp(JBinaryOperation x) { return isUnboxExpression(x.getLhs()); }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/PostOptimizationCompoundAssignmentNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/PostOptimizationCompoundAssignmentNormalizer.java index e67dd23..271f9f5 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/PostOptimizationCompoundAssignmentNormalizer.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/PostOptimizationCompoundAssignmentNormalizer.java
@@ -36,6 +36,11 @@ } @Override + protected String getTempPrefix() { + return "$t"; + } + + @Override protected boolean shouldBreakUp(JBinaryOperation x) { if (x.getType() == program.getTypePrimitiveLong()) { return true;
diff --git a/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java b/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java index b663f76..c381889 100644 --- a/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java +++ b/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java
@@ -298,12 +298,12 @@ if (onModuleLoad == null) { module = rebindAndCreate(entryPointTypeName); onModuleLoad = module.getClass().getMethod("onModuleLoad"); + // Record the rebound name of the class for stats (below). + entryPointTypeName = module.getClass().getName().replace('$', '.'); } onModuleLoad.setAccessible(true); - String reboundSourceName = module.getClass().getName().replace('$', - '.'); invokeNativeVoid("fireOnModuleLoadStart", null, - new Class[] {String.class}, new Object[] {reboundSourceName}); + new Class[] {String.class}, new Object[] {entryPointTypeName}); onModuleLoad.invoke(module); } } else {
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/JDelegatingClassTypeTestBase.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/JDelegatingClassTypeTestBase.java index 2c3e989..da56a75 100644 --- a/dev/core/test/com/google/gwt/core/ext/typeinfo/JDelegatingClassTypeTestBase.java +++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/JDelegatingClassTypeTestBase.java
@@ -68,6 +68,10 @@ assertEquals(substitution.getSubstitution(preSubParam.getType()), postSubParam.getType()); } + + JType[] preSubThrows = preSubMethod.getThrows(); + JType[] postSubThrows = postSubMethod.getThrows(); + assertArraysEqual(preSubThrows, postSubThrows); } protected static void validateAnnotations(Annotation[] expected,
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/GenericClass.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/GenericClass.java index f328c00..b9b39bb 100644 --- a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/GenericClass.java +++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/GenericClass.java
@@ -29,7 +29,8 @@ * definition of GenericClass is as follows: class GenericClass<T extends * Serializable & Comparable<T>> implements Comparable<T> { ... } */ -public class GenericClass<T extends Serializable> implements Comparable<T>, Serializable { +public class GenericClass<T extends Serializable> implements Comparable<T>, + Serializable { /** * Non-static, generic inner class. * @@ -63,8 +64,7 @@ * NOTE: The following is disabled because it violates an assumption in TOB * line 1228. */ -// GenericClass.NonGenericInnerClass rawNonGenericInnerClassField; - + // GenericClass.NonGenericInnerClass rawNonGenericInnerClassField; Class rawClazzField; /** @@ -112,7 +112,7 @@ return collection.iterator().next(); } - public void setT(T t) { + public void setT(T t) throws Exception { this.typeParameterField = t; } } \ No newline at end of file
diff --git a/servlet/build.xml b/servlet/build.xml index 0a830ff..2b8cb39 100755 --- a/servlet/build.xml +++ b/servlet/build.xml
@@ -15,11 +15,10 @@ <exclude name="**/tools/**" /> <exclude name="com/google/gwt/json/**" /> <exclude name="com/google/gwt/junit/*" /> - <exclude name="com/google/gwt/junit/benchmarks/**" /> - <exclude name="com/google/gwt/junit/client/Benchmark.*" /> <exclude name="com/google/gwt/junit/client/GWTTestCase.*" /> <exclude name="com/google/gwt/junit/remote/**" /> <exclude name="com/google/gwt/junit/server/**" /> + <exclude name="com/google/gwt/benchmarks/*" /> </fileset> </gwt.jar> </target>
diff --git a/user/src/com/google/gwt/dom/client/DOMImpl.java b/user/src/com/google/gwt/dom/client/DOMImpl.java index 3369ea2..2c0cb1e 100644 --- a/user/src/com/google/gwt/dom/client/DOMImpl.java +++ b/user/src/com/google/gwt/dom/client/DOMImpl.java
@@ -166,6 +166,10 @@ select.add(option, before); }-*/; + public native void selectClear(SelectElement select) /*-{ + select.options.length = 0; + }-*/; + public native int selectGetLength(SelectElement select) /*-{ return select.options.length; }-*/;
diff --git a/user/src/com/google/gwt/dom/client/DOMImplSafari.java b/user/src/com/google/gwt/dom/client/DOMImplSafari.java index 098d70c..1a5d930 100644 --- a/user/src/com/google/gwt/dom/client/DOMImplSafari.java +++ b/user/src/com/google/gwt/dom/client/DOMImplSafari.java
@@ -124,6 +124,10 @@ * not work. However, this bug does not cause problems in the case of <SELECT> * elements, because their descendent elements are only one level deep. */ + @Override + public native void selectClear(SelectElement select) /*-{ + select.innerText = ''; + }-*/; @Override public native int selectGetLength(SelectElement select) /*-{
diff --git a/user/src/com/google/gwt/dom/client/SelectElement.java b/user/src/com/google/gwt/dom/client/SelectElement.java index 7ca4952..0755722 100644 --- a/user/src/com/google/gwt/dom/client/SelectElement.java +++ b/user/src/com/google/gwt/dom/client/SelectElement.java
@@ -60,6 +60,13 @@ }-*/; /** + * Removes all OPTION elements from this SELECT. + */ + public final void clear() { + DOMImpl.impl.selectClear(this); + } + + /** * Gives keyboard focus to this element. */ public final native void focus() /*-{
diff --git a/user/src/com/google/gwt/dom/client/TableCellElement.java b/user/src/com/google/gwt/dom/client/TableCellElement.java index bc5c3c8..b78851b 100644 --- a/user/src/com/google/gwt/dom/client/TableCellElement.java +++ b/user/src/com/google/gwt/dom/client/TableCellElement.java
@@ -27,7 +27,8 @@ * automatically typecast it. */ public static TableCellElement as(Element elem) { - assert elem.getTagName().equalsIgnoreCase("td"); + assert elem.getTagName().equalsIgnoreCase("td") + || elem.getTagName().equalsIgnoreCase("th"); return (TableCellElement) elem; }
diff --git a/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableImplCreator.java b/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableImplCreator.java index 03036b4..71b44e2 100644 --- a/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableImplCreator.java +++ b/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableImplCreator.java
@@ -24,6 +24,7 @@ import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.i18n.client.LocalizableResource.Generate; import com.google.gwt.i18n.client.LocalizableResource.Key; +import com.google.gwt.i18n.rebind.AbstractResource.ResourceList; import com.google.gwt.i18n.rebind.AnnotationsResource.AnnotationsError; import com.google.gwt.i18n.rebind.format.MessageCatalogFormat; import com.google.gwt.i18n.rebind.keygen.KeyGenerator; @@ -86,14 +87,14 @@ throw error(logger, name + " must be an interface"); } - AbstractResource resource = null; + ResourceList resourceList = null; try { - resource = ResourceFactory.getBundle(logger, targetClass, locale, + resourceList = ResourceFactory.getBundle(logger, targetClass, locale, assignableToConstants); } catch (MissingResourceException e) { throw error( logger, - "Localization failed; there must be at least one properties file accessible through" + "Localization failed; there must be at least one resource accessible through" + " the classpath in package '" + packageName + "' whose base name is '" @@ -121,17 +122,17 @@ // Now that we have all the information set up, process the class if (constantsWithLookupClass.isAssignableFrom(targetClass)) { ConstantsWithLookupImplCreator c = new ConstantsWithLookupImplCreator( - logger, deprecatedLogger, writer, targetClass, resource, + logger, deprecatedLogger, writer, targetClass, resourceList, context.getTypeOracle()); c.emitClass(logger, locale); } else if (constantsClass.isAssignableFrom(targetClass)) { ConstantsImplCreator c = new ConstantsImplCreator(logger, - deprecatedLogger, writer, targetClass, resource, + deprecatedLogger, writer, targetClass, resourceList, context.getTypeOracle()); c.emitClass(logger, locale); } else { MessagesImplCreator messages = new MessagesImplCreator(logger, - deprecatedLogger, writer, targetClass, resource, + deprecatedLogger, writer, targetClass, resourceList, context.getTypeOracle()); messages.emitClass(logger, locale); } @@ -207,7 +208,7 @@ throw error(logger, e.getMessage()); } try { - msgWriter.write(branch, resource, out, targetClass); + msgWriter.write(branch, locale, resourceList, out, targetClass); out.flush(); context.commitResource(logger, outStr).setPrivate(true); } catch (UnableToCompleteException e) { @@ -234,7 +235,7 @@ /** * The Dictionary/value bindings used to determine message contents. */ - private AbstractResource messageBindings; + private ResourceList resourceList; /** * Logger to use for deprecated warnings. @@ -252,14 +253,14 @@ * @param deprecatedLogger logger to use for deprecated warnings. * @param writer writer * @param targetClass current target - * @param messageBindings backing resource + * @param resourceList backing resource */ public AbstractLocalizableImplCreator(TreeLogger logger, TreeLogger deprecatedLogger, SourceWriter writer, JClassType targetClass, - AbstractResource messageBindings, boolean isConstants) { + ResourceList resourceList, boolean isConstants) { super(writer, targetClass); this.deprecatedLogger = deprecatedLogger; - this.messageBindings = messageBindings; + this.resourceList = resourceList; this.isConstants = isConstants; try { keyGenerator = AnnotationsResource.getKeyGenerator(targetClass); @@ -274,8 +275,8 @@ * * @return the resource */ - public AbstractResource getResourceBundle() { - return messageBindings; + public ResourceList getResourceBundle() { + return resourceList; } @Override @@ -301,7 +302,7 @@ + method.getName(), null); throw new UnableToCompleteException(); } - methodCreator.createMethodFor(logger, method, key, messageBindings, locale); + methodCreator.createMethodFor(logger, method, key, resourceList, locale); } /**
diff --git a/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableMethodCreator.java b/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableMethodCreator.java index 260cd82..a572764 100644 --- a/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableMethodCreator.java +++ b/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableMethodCreator.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -15,6 +15,7 @@ */ package com.google.gwt.i18n.rebind; +import com.google.gwt.i18n.rebind.AbstractResource.ResourceList; import com.google.gwt.user.rebind.AbstractGeneratorClassCreator; import com.google.gwt.user.rebind.AbstractMethodCreator; @@ -41,20 +42,11 @@ } /** - * Gets the associated locale. - * - * @return the locale - */ - protected String getLocaleName() { - return getResources().getLocaleName(); - } - - /** * Get the resources associated with this class. * * @return associated resources. */ - protected AbstractResource getResources() { + protected ResourceList getResources() { return ((ConstantsImplCreator) currentCreator).getResourceBundle(); } }
diff --git a/user/src/com/google/gwt/i18n/rebind/AbstractResource.java b/user/src/com/google/gwt/i18n/rebind/AbstractResource.java index bd3f0fc..dea4cb7 100644 --- a/user/src/com/google/gwt/i18n/rebind/AbstractResource.java +++ b/user/src/com/google/gwt/i18n/rebind/AbstractResource.java
@@ -18,10 +18,12 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.i18n.client.PluralRule.PluralForm; +import java.util.AbstractList; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -43,8 +45,9 @@ * parent is associated with a separate resource tree. */ public abstract class AbstractResource { + /** - * Exception indicating a require resource was not found. + * Exception indicating a required resource was not found. */ public static class MissingResourceException extends RuntimeException { private String key; @@ -85,6 +88,211 @@ } /** + * Encapsulates an ordered set of resources to search for translations. + */ + public static class ResourceList extends AbstractList<AbstractResource> + implements List<AbstractResource>, Set<AbstractResource> { + + private List<AbstractResource> list = new ArrayList<AbstractResource>(); + + private Set<AbstractResource> set = new HashSet<AbstractResource>(); + + private Map<String, PluralForm[]> pluralForms = new HashMap<String, PluralForm[]>(); + + @Override + public boolean add(AbstractResource element) { + if (set.contains(element)) { + return false; + } + set.add(element); + return list.add(element); + } + + @Override + public void add(int index, AbstractResource element) { + if (set.contains(element)) { + throw new IllegalArgumentException("Duplicate element"); + } + set.add(element); + list.add(index, element); + } + + /** + * Add all keys known by this ResourceList to the specified set. + * + * @param s set to add keys to + */ + public void addToKeySet(Set<String> s) { + for (AbstractResource resource : list) { + resource.addToKeySet(s); + } + } + + @Override + public AbstractResource get(int index) { + return list.get(index); + } + + /** + * Return the first AnnotationsResource containing a specified key. + * + * @param logger + * @param key + * @return first AnnotationsResource containing key, or null if none + */ + public AnnotationsResource getAnnotationsResource(TreeLogger logger, + String key) { + for (AbstractResource resource : list) { + if (resource instanceof AnnotationsResource + && resource.keySet.contains(key)) { + return (AnnotationsResource) resource; + } + } + return null; + } + + /** + * Return the list of extensions available for a given key. + * + * @param key + * @return collection of extensions for the given key + */ + public Collection<String> getExtension(String key) { + Set<String> extensions = new HashSet<String>(); + for (AbstractResource resource : list) { + extensions.addAll(resource.getExtensions(key)); + } + return extensions; + } + + /** + * Return the list of plural forms for a given key. + * + * @param key + * @return array of plural forms. + */ + public PluralForm[] getPluralForms(String key) { + return pluralForms.get(key); + } + + /** + * Return a translation for a key, or throw an exception. + * + * @param key + * @return translated string for key + * @throws MissingResourceException + */ + public String getRequiredString(String key) + throws MissingResourceException { + String val = getString(key); + if (val == null) { + throw new MissingResourceException(key, list); + } + return val; + } + + /** + * Return a translation for a key/extension, or throw an exception. + * + * @param key + * @param ext key extension, null if none + * @return translated string for key + * @throws MissingResourceException + */ + public String getRequiredStringExt(String key, String ext) + throws MissingResourceException { + String val = getStringExt(key, ext); + if (val == null) { + throw new MissingResourceException(getExtendedKey(key, ext), list); + } + return val; + } + + /** + * Return a translation for a key, or null if not found. + * + * @param key + * @return translated string for key + */ + public String getString(String key) { + for (AbstractResource resource : list) { + String s = resource.getStringExt(key, null); + if (s != null) { + return s; + } + } + return null; + } + + /** + * Return a translation for a key/extension, or null if not found. + * + * @param key + * @param extension key extension, null if none + * @return translated string for key + */ + public String getStringExt(String key, String extension) { + for (AbstractResource resource : list) { + String s = resource.getStringExt(key, extension); + if (s != null) { + return s; + } + } + return null; + } + + @Override + public int indexOf(Object o) { + return list.indexOf(o); + } + + @Override + public Iterator<AbstractResource> iterator() { + return list.iterator(); + } + + /** + * @return set of keys present across all resources + */ + public Set<String> keySet() { + Set<String> keySet = new HashSet<String>(); + for (AbstractResource resource : list) { + keySet.addAll(resource.keySet()); + } + return keySet; + } + + @Override + public int lastIndexOf(Object o) { + return list.lastIndexOf(o); + } + + @Override + public AbstractResource remove(int index) { + AbstractResource element = list.remove(index); + set.remove(element); + return element; + } + + /** + * Set the plural forms associated with a given message. + * + * @param key + * @param forms + */ + public void setPluralForms(String key, PluralForm[] forms) { + if (!pluralForms.containsKey(key)) { + pluralForms.put(key, forms); + } + } + + @Override + public int size() { + return list.size(); + } + } + + /** * Error messages concerning missing keys should include the defined keys if * the number of keys is below this threshold. */ @@ -97,82 +305,46 @@ return key; } - private final List<AbstractResource> alternativeParents = new ArrayList<AbstractResource>(); - private Set<String> keySet; - private Map<String, PluralForm[]> pluralFormMap = new HashMap<String, PluralForm[]>(); - - private String localeName; - private String path; - private AbstractResource primaryParent; - - /** - * Walk up the resource inheritance tree until we find one which is an - * instance of AnnotationsResource. - * - * This is needed so the original Annotations metadata can be used even in - * inherited resources, such as from a properties file for a specific locale. - * - * TODO(jat): really bad code smell -- the superclass should not know about a - * particular implementation, but I couldn't think of a decent way around it. - * Long term, this whole structure needs to be be redone to make use of - * resources that fundamentally don't look like property files to be used - * effectively. I don't think it is feasible to do that in time for 1.5, so I - * am continuing with this hacky solution but we need to look at this after - * 1.5 is done. - */ - public AnnotationsResource getAnnotationsResource() { - AbstractResource resource = this; - while (resource != null && !(resource instanceof AnnotationsResource)) { - resource = resource.primaryParent; - } - return (AnnotationsResource) resource; - } - public Collection<String> getExtensions(String key) { return new ArrayList<String>(); } /** - * @see java.util.ResourceBundle#getLocale() + * Get a string and fail if not present. + * + * @param key + * @return the requested string */ - public String getLocaleName() { - return localeName; - } - - /** - * @see java.util.ResourceBundle#getObject(java.lang.String) - */ - public final Object getObject(String key) { - Object s = getObjectAux(key, true, true); - return s; - } - - public PluralForm[] getPluralForms(String key) { - return pluralFormMap.get(key); + public final String getRequiredString(String key) { + return getRequiredStringExt(key, null); } /** * Get a string (with optional extension) and fail if not present. * - * @param logger * @param key * @param extension * @return the requested string */ - public final String getRequiredStringExt(TreeLogger logger, String key, - String extension) { - return extension == null ? (String) getObjectAux(key, true, true) : null; + public final String getRequiredStringExt(String key, String extension) { + String s = getStringExt(key, extension); + if (s == null) { + ArrayList<AbstractResource> list = new ArrayList<AbstractResource>(); + list.add(this); + throw new MissingResourceException(key, list); + } + return s; } /** * @see java.util.ResourceBundle#getString(java.lang.String) */ public final String getString(String key) { - return (String) getObjectAux(key, true); + return getStringExt(key, null); } /** @@ -182,11 +354,7 @@ * @param extension extension of the key, nullable * @return string or null */ - public String getStringExt(String key, String extension) { - return extension == null ? getString(key) : null; - } - - public abstract Object handleGetObject(String key); + public abstract String getStringExt(String key, String extension); /** * Keys associated with this resource. @@ -197,19 +365,15 @@ if (keySet == null) { keySet = new HashSet<String>(); addToKeySet(keySet); - if (primaryParent != null) { - primaryParent.addToKeySet(keySet); - } - for (int i = 0; i < alternativeParents.size(); i++) { - AbstractResource element = alternativeParents.get(i); - keySet.addAll(element.keySet()); - } } return keySet; } - public void setPluralForms(String key, PluralForm[] pluralForms) { - pluralFormMap.put(key, pluralForms); + /** + * @return true if this resource has any keys + */ + public boolean notEmpty() { + return !keySet.isEmpty(); } @Override @@ -228,98 +392,16 @@ return b.toString(); } - void addAlternativeParent(AbstractResource parent) { - if (parent != null) { - alternativeParents.add(parent); - } - } - abstract void addToKeySet(Set<String> s); - void checkKeys() { - // If I don't have a parent, then I am a default node so do not need to - // conform - if (primaryParent == null) { - return; - } - for (String key : keySet()) { - if (primaryParent.getObjectAux(key, true) == null) { - for (int i = 0; i < alternativeParents.size(); i++) { - AbstractResource alt = alternativeParents.get(i); - if (alt.getObjectAux(key, true) != null) { - break; - } - } - - throw new IllegalArgumentException( - key - + " is not a valid resource key as it does not occur in the default version of " - + this + " nor in any of " + alternativeParents); - } - } - } - - final Object getObjectAux(String key, boolean useAlternativeParents) { - try { - return getObjectAux(key, useAlternativeParents, false); - } catch (MissingResourceException e) { - // Can't happen since we pass required=false - throw new RuntimeException("Unexpected MissingResourceException", e); - } - } - - final Object getObjectAux(String key, boolean useAlternativeParents, - boolean required) throws MissingResourceException { - ArrayList<AbstractResource> searched = new ArrayList<AbstractResource>(); - searched.add(this); - Object s = handleGetObject(key); - if (s != null) { - return s; - } - AbstractResource parent = this.getPrimaryParent(); - if (parent != null) { - // Primary parents should not look at their alternative parents - searched.add(parent); - s = parent.getObjectAux(key, false); - } - if ((s == null) && (alternativeParents.size() > 0) - && (useAlternativeParents)) { - for (int i = 0; (i < alternativeParents.size()) && (s == null); i++) { - // Alternate parents may look at their alternative parents. - AbstractResource altParent = alternativeParents.get(i); - searched.add(altParent); - s = altParent.getObjectAux(key, true); - } - } - if (s == null && required) { - throw new MissingResourceException(key, searched); - } - return s; - } - String getPath() { return path; } - AbstractResource getPrimaryParent() { - return primaryParent; - } - - void setLocaleName(String locale) { - this.localeName = locale; - } - void setPath(String path) { this.path = path; } - void setPrimaryParent(AbstractResource primaryParent) { - if (primaryParent == null) { - return; - } - this.primaryParent = primaryParent; - } - private void newLine(int indent, StringBuffer buf) { buf.append("\n"); for (int i = 0; i < indent; i++) { @@ -330,16 +412,5 @@ private void toVerboseStringAux(int indent, StringBuffer buf) { newLine(indent, buf); buf.append(toString()); - if (primaryParent != null) { - newLine(indent, buf); - buf.append("Primary Parent: "); - primaryParent.toVerboseStringAux(indent + 1, buf); - } - for (int i = 0; i < alternativeParents.size(); i++) { - newLine(indent, buf); - buf.append("Alternate Parent: "); - AbstractResource element = alternativeParents.get(i); - element.toVerboseStringAux(indent + 1, buf); - } } }
diff --git a/user/src/com/google/gwt/i18n/rebind/AnnotationsResource.java b/user/src/com/google/gwt/i18n/rebind/AnnotationsResource.java index 9c21deb..87b9d05 100644 --- a/user/src/com/google/gwt/i18n/rebind/AnnotationsResource.java +++ b/user/src/com/google/gwt/i18n/rebind/AnnotationsResource.java
@@ -25,6 +25,7 @@ import com.google.gwt.core.ext.typeinfo.JPrimitiveType; import com.google.gwt.core.ext.typeinfo.JRawType; import com.google.gwt.core.ext.typeinfo.JType; +import com.google.gwt.i18n.client.PluralRule; import com.google.gwt.i18n.client.Constants.DefaultBooleanValue; import com.google.gwt.i18n.client.Constants.DefaultDoubleValue; import com.google.gwt.i18n.client.Constants.DefaultFloatValue; @@ -32,6 +33,7 @@ import com.google.gwt.i18n.client.Constants.DefaultStringArrayValue; import com.google.gwt.i18n.client.Constants.DefaultStringMapValue; import com.google.gwt.i18n.client.Constants.DefaultStringValue; +import com.google.gwt.i18n.client.LocalizableResource.DefaultLocale; import com.google.gwt.i18n.client.LocalizableResource.Description; import com.google.gwt.i18n.client.LocalizableResource.GenerateKeys; import com.google.gwt.i18n.client.LocalizableResource.Key; @@ -76,6 +78,7 @@ public boolean isPluralCount; public String name; public boolean optional; + public Class<? extends PluralRule> pluralRuleClass; public ArgumentInfo(String name) { this.name = name; @@ -364,14 +367,23 @@ * * @param logger * @param clazz + * @param locale * @param isConstants * @throws AnnotationsError if there is a fatal error while processing * annotations */ public AnnotationsResource(TreeLogger logger, JClassType clazz, - boolean isConstants) throws AnnotationsError { + String locale, boolean isConstants) throws AnnotationsError { KeyGenerator keyGenerator = getKeyGenerator(clazz); map = new HashMap<String, MethodEntry>(); + setPath(clazz.getQualifiedSourceName()); + DefaultLocale defLocale = clazz.getAnnotation(DefaultLocale.class); + if (defLocale != null && !ResourceFactory.DEFAULT_TOKEN.equals(locale) + && !locale.equalsIgnoreCase(defLocale.value())) { + logger.log(TreeLogger.WARN, "@DefaultLocale on " + + clazz.getQualifiedSourceName() + " doesn't match " + locale); + return; + } for (JMethod method : clazz.getMethods()) { String meaningString = null; Meaning meaning = method.getAnnotation(Meaning.class); @@ -409,10 +421,10 @@ if ((pluralForms.length & 1) != 0) { throw new AnnotationsError( "Odd number of strings supplied to @PluralText: must be" - + " pairs of form names and strings"); + + " pairs of form names and strings"); } for (int i = 0; i + 1 < pluralForms.length; i += 2) { - entry.pluralText.put(pluralForms[i], pluralForms[i + 1]); + entry.addPluralText(pluralForms[i], pluralForms[i + 1]); } } for (JParameter param : method.getParameters()) { @@ -431,7 +443,6 @@ } } } - setPath(clazz.getQualifiedSourceName()); } @Override @@ -462,27 +473,22 @@ @Override public String getStringExt(String key, String extension) { - if (extension == null) { - return getString(key); - } MethodEntry entry = map.get(key); - return entry == null ? null : entry.pluralText.get(extension); + if (entry == null) { + return null; + } + if (extension != null) { + return entry.pluralText.get(extension); + } else { + return entry.text; + } } @Override - public Object handleGetObject(String key) { - MethodEntry entry = map.get(key); - return entry == null ? null : entry.text; - } - public boolean notEmpty() { return !map.isEmpty(); } - public void setParentResource(AnnotationsResource parent) { - setPrimaryParent(parent); - } - @Override public String toString() { return "Annotations from class " + getPath();
diff --git a/user/src/com/google/gwt/i18n/rebind/ConstantsImplCreator.java b/user/src/com/google/gwt/i18n/rebind/ConstantsImplCreator.java index 7d601f6..f189751 100644 --- a/user/src/com/google/gwt/i18n/rebind/ConstantsImplCreator.java +++ b/user/src/com/google/gwt/i18n/rebind/ConstantsImplCreator.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -26,6 +26,7 @@ import com.google.gwt.core.ext.typeinfo.NotFoundException; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.core.ext.typeinfo.TypeOracleException; +import com.google.gwt.i18n.rebind.AbstractResource.ResourceList; import com.google.gwt.user.rebind.SourceWriter; import java.util.Map; @@ -47,14 +48,14 @@ * @param deprecatedLogger logger to use for deprecated warnings * @param writer <code>Writer</code> to print to * @param localizableClass class/interface to conform to - * @param messageBindings resource bundle used to generate the class + * @param resourceList resource bundle used to generate the class * @param oracle types * @throws UnableToCompleteException */ public ConstantsImplCreator(TreeLogger logger, TreeLogger deprecatedLogger, SourceWriter writer, - JClassType localizableClass, AbstractResource messageBindings, + JClassType localizableClass, ResourceList resourceList, TypeOracle oracle) throws UnableToCompleteException { - super(logger, deprecatedLogger, writer, localizableClass, messageBindings, true); + super(logger, deprecatedLogger, writer, localizableClass, resourceList, true); try { JClassType stringClass = oracle.getType(String.class.getName()); JClassType mapClass = oracle.getType(Map.class.getName());
diff --git a/user/src/com/google/gwt/i18n/rebind/ConstantsMapMethodCreator.java b/user/src/com/google/gwt/i18n/rebind/ConstantsMapMethodCreator.java index 2176733..e18625c 100644 --- a/user/src/com/google/gwt/i18n/rebind/ConstantsMapMethodCreator.java +++ b/user/src/com/google/gwt/i18n/rebind/ConstantsMapMethodCreator.java
@@ -19,6 +19,7 @@ import com.google.gwt.core.ext.typeinfo.JMethod; import com.google.gwt.i18n.client.impl.ConstantMap; import com.google.gwt.i18n.rebind.AbstractResource.MissingResourceException; +import com.google.gwt.i18n.rebind.AbstractResource.ResourceList; import com.google.gwt.user.rebind.AbstractGeneratorClassCreator; /** @@ -40,12 +41,12 @@ * @param logger TreeLogger instance for logging * @param method method body to create * @param key value to create map from - * @param resource AbstractResource for key lookup + * @param resourceList AbstractResource for key lookup * @param locale locale to use for localized string lookup */ @Override public void createMethodFor(TreeLogger logger, JMethod method, String key, - AbstractResource resource, String locale) { + ResourceList resourceList, String locale) { String methodName = method.getName(); if (method.getParameters().length > 0) { error( @@ -66,7 +67,7 @@ println("args = new " + constantMapClassName + "();"); String value; try { - value = resource.getRequiredStringExt(logger, key, null); + value = resourceList.getRequiredStringExt(key, null); } catch (MissingResourceException e) { e.setDuring("getting key list"); throw e;
diff --git a/user/src/com/google/gwt/i18n/rebind/ConstantsStringArrayMethodCreator.java b/user/src/com/google/gwt/i18n/rebind/ConstantsStringArrayMethodCreator.java index 1cd2e33..358010e 100644 --- a/user/src/com/google/gwt/i18n/rebind/ConstantsStringArrayMethodCreator.java +++ b/user/src/com/google/gwt/i18n/rebind/ConstantsStringArrayMethodCreator.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -17,6 +17,7 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.typeinfo.JMethod; +import com.google.gwt.i18n.rebind.AbstractResource.ResourceList; import com.google.gwt.user.rebind.AbstractGeneratorClassCreator; /** @@ -60,7 +61,7 @@ @Override public void createMethodFor(TreeLogger logger, JMethod method, String key, - AbstractResource resource, String locale) { + ResourceList resource, String locale) { String methodName = method.getName(); // Make sure cache exists. enableCache(); @@ -71,7 +72,7 @@ indent(); println("String [] writer= {"); indent(); - String template = resource.getRequiredStringExt(logger, key, null); + String template = resource.getRequiredStringExt(key, null); String[] args = split(template); for (int i = 0; i < args.length; i++) { String toPrint = args[i].replaceAll("\\,", ",");
diff --git a/user/src/com/google/gwt/i18n/rebind/ConstantsWithLookupImplCreator.java b/user/src/com/google/gwt/i18n/rebind/ConstantsWithLookupImplCreator.java index 73958ba..433f698 100644 --- a/user/src/com/google/gwt/i18n/rebind/ConstantsWithLookupImplCreator.java +++ b/user/src/com/google/gwt/i18n/rebind/ConstantsWithLookupImplCreator.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -24,6 +24,7 @@ import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.core.ext.typeinfo.TypeOracleException; import com.google.gwt.i18n.client.impl.ConstantMap; +import com.google.gwt.i18n.rebind.AbstractResource.ResourceList; import com.google.gwt.user.rebind.AbstractMethodCreator; import com.google.gwt.user.rebind.SourceWriter; @@ -43,15 +44,15 @@ * @param deprecatedLogger logger to use for deprecated warnings * @param writer <code>Writer</code> to print to * @param localizableClass class/interface to conform to - * @param messageBindings resource bundle used to generate the class + * @param resourceList resource bundle used to generate the class * @param oracle types * @throws UnableToCompleteException */ ConstantsWithLookupImplCreator(TreeLogger logger, TreeLogger deprecatedLogger, SourceWriter writer, - JClassType localizableClass, AbstractResource messageBindings, + JClassType localizableClass, ResourceList resourceList, TypeOracle oracle) throws UnableToCompleteException { - super(logger, deprecatedLogger, writer, localizableClass, messageBindings, + super(logger, deprecatedLogger, writer, localizableClass, resourceList, oracle); try {
diff --git a/user/src/com/google/gwt/i18n/rebind/LocalizedPropertiesResource.java b/user/src/com/google/gwt/i18n/rebind/LocalizedPropertiesResource.java index 8ccd20a..22d9f17 100644 --- a/user/src/com/google/gwt/i18n/rebind/LocalizedPropertiesResource.java +++ b/user/src/com/google/gwt/i18n/rebind/LocalizedPropertiesResource.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -69,23 +69,17 @@ @Override public String getStringExt(String key, String extension) { if (extension != null) { - key += '[' + extension + ']'; - /* - * Do not look up key extensions in other files, as they may not be - * relevant. For example, if you are looking up the "one" French - * plural form and it isn't present, using the default fallback (which - * will typically be English) will result in an incorrect translation. - * Better to give a warning about a missing plural form and use the - * default rather than silently use an incorrect one. - */ - return (String) getObjectAux(key, false); + String s = getStringExt(getExtendedKey(key, extension), null); + if (s != null) { + return s; + } } - return getString(key); + return props.getProperty(key); } @Override - public Object handleGetObject(String key) { - return props.getProperty(key); + public boolean notEmpty() { + return props != null && !props.getPropertyMap().isEmpty(); } @Override
diff --git a/user/src/com/google/gwt/i18n/rebind/LookupMethodCreator.java b/user/src/com/google/gwt/i18n/rebind/LookupMethodCreator.java index 9f6d2bf..cb59124 100644 --- a/user/src/com/google/gwt/i18n/rebind/LookupMethodCreator.java +++ b/user/src/com/google/gwt/i18n/rebind/LookupMethodCreator.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -19,6 +19,7 @@ import com.google.gwt.core.ext.typeinfo.JMethod; import com.google.gwt.core.ext.typeinfo.JPrimitiveType; import com.google.gwt.core.ext.typeinfo.JType; +import com.google.gwt.i18n.rebind.AbstractResource.ResourceList; import com.google.gwt.user.rebind.AbstractGeneratorClassCreator; import com.google.gwt.user.rebind.AbstractMethodCreator; import com.google.gwt.user.rebind.AbstractSourceCreator; @@ -45,7 +46,7 @@ @Override public void createMethodFor(TreeLogger logger, JMethod targetMethod, - String key, AbstractResource resource, String locale) { + String key, ResourceList resourceList, String locale) { createMethodFor(targetMethod); }
diff --git a/user/src/com/google/gwt/i18n/rebind/MessagesImplCreator.java b/user/src/com/google/gwt/i18n/rebind/MessagesImplCreator.java index 883df33..3332695 100644 --- a/user/src/com/google/gwt/i18n/rebind/MessagesImplCreator.java +++ b/user/src/com/google/gwt/i18n/rebind/MessagesImplCreator.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -21,6 +21,7 @@ import com.google.gwt.core.ext.typeinfo.JMethod; import com.google.gwt.core.ext.typeinfo.NotFoundException; import com.google.gwt.core.ext.typeinfo.TypeOracle; +import com.google.gwt.i18n.rebind.AbstractResource.ResourceList; import com.google.gwt.user.rebind.SourceWriter; /** @@ -34,16 +35,16 @@ * * @param writer <code>Writer</code> to print to * @param localizableClass Class/Interface to conform to - * @param messageBindings resource bundle used to generate the class + * @param resourceList resource bundle used to generate the class * @param oracle types * @param logger logger to print errors * @param deprecatedLogger logger for deprecated metadata warnings * @throws UnableToCompleteException */ public MessagesImplCreator(TreeLogger logger, TreeLogger deprecatedLogger, SourceWriter writer, - JClassType localizableClass, AbstractResource messageBindings, + JClassType localizableClass, ResourceList resourceList, TypeOracle oracle) throws UnableToCompleteException { - super(logger, deprecatedLogger, writer, localizableClass, messageBindings, false); + super(logger, deprecatedLogger, writer, localizableClass, resourceList, false); try { JClassType stringClass = oracle.getType(String.class.getName()); register(stringClass, new MessagesMethodCreator(this));
diff --git a/user/src/com/google/gwt/i18n/rebind/MessagesMethodCreator.java b/user/src/com/google/gwt/i18n/rebind/MessagesMethodCreator.java index ff59e9f..25b7dbc 100644 --- a/user/src/com/google/gwt/i18n/rebind/MessagesMethodCreator.java +++ b/user/src/com/google/gwt/i18n/rebind/MessagesMethodCreator.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -26,11 +26,12 @@ import com.google.gwt.i18n.client.DateTimeFormat; import com.google.gwt.i18n.client.NumberFormat; import com.google.gwt.i18n.client.PluralRule; -import com.google.gwt.i18n.client.PluralRule.PluralForm; import com.google.gwt.i18n.client.Messages.Optional; import com.google.gwt.i18n.client.Messages.PluralCount; import com.google.gwt.i18n.client.Messages.PluralText; +import com.google.gwt.i18n.client.PluralRule.PluralForm; import com.google.gwt.i18n.client.impl.plurals.DefaultRule; +import com.google.gwt.i18n.rebind.AbstractResource.ResourceList; import com.google.gwt.user.rebind.AbstractGeneratorClassCreator; import com.google.gwt.user.rebind.AbstractMethodCreator; @@ -289,7 +290,6 @@ * Constructor for <code>MessagesMethodCreator</code>. * * @param classCreator associated class creator - * @param localizableClass class we are generating methods for */ public MessagesMethodCreator(AbstractGeneratorClassCreator classCreator) { super(classCreator); @@ -297,7 +297,7 @@ @Override public void createMethodFor(TreeLogger logger, JMethod m, String key, - AbstractResource resource, String locale) + ResourceList resourceList, String locale) throws UnableToCompleteException { JParameter[] params = m.getParameters(); int pluralParamIndex = -1; @@ -343,12 +343,15 @@ + "();\n"); generated.append("switch (rule.select(arg" + pluralParamIndex + ")) {\n"); PluralForm[] pluralForms = rule.pluralForms(); - resource.setPluralForms(key, pluralForms); + resourceList.setPluralForms(key, pluralForms); // Skip default plural form (index 0); the fall-through case will handle // it. for (int i = 1; i < pluralForms.length; ++i) { - String template = resource.getStringExt(key, pluralForms[i].getName()); + String template = resourceList.getStringExt(key, + pluralForms[i].getName()); if (template != null) { + generated.append(" // " + pluralForms[i].getName() + " - " + + pluralForms[i].getDescription() + "\n"); generated.append(" case " + i + ": return "); generateString(logger, template, params, seenFlags, generated); generated.append(";\n"); @@ -362,7 +365,7 @@ generated.append("}\n"); } generated.append("return "); - String template = resource.getRequiredStringExt(logger, key, null); + String template = resourceList.getRequiredStringExt(key, null); generateString(logger, template, params, seenFlags, generated); // Generate an error if any required parameter was not used somewhere. @@ -447,6 +450,7 @@ * @param outputBuf * @throws UnableToCompleteException */ + @SuppressWarnings("fallthrough") private void generateString(TreeLogger logger, String template, JParameter[] params, boolean[] seenFlag, StringBuffer outputBuf) throws UnableToCompleteException {
diff --git a/user/src/com/google/gwt/i18n/rebind/ResourceFactory.java b/user/src/com/google/gwt/i18n/rebind/ResourceFactory.java index c249d3a..ee5f501 100644 --- a/user/src/com/google/gwt/i18n/rebind/ResourceFactory.java +++ b/user/src/com/google/gwt/i18n/rebind/ResourceFactory.java
@@ -18,14 +18,15 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.i18n.rebind.AbstractResource.ResourceList; import com.google.gwt.i18n.rebind.AnnotationsResource.AnnotationsError; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.MissingResourceException; import java.util.Set; /** @@ -135,20 +136,7 @@ public static final String DEFAULT_TOKEN = "default"; public static final char LOCALE_SEPARATOR = '_'; - public static final AbstractResource NOT_FOUND = new AbstractResource() { - - @Override - public Object handleGetObject(String key) { - throw new IllegalStateException("Not found resource"); - } - - @Override - void addToKeySet(Set<String> s) { - throw new IllegalStateException("Not found resource"); - } - }; - - private static Map<String, AbstractResource> cache = new HashMap<String, AbstractResource>(); + private static Map<String, ResourceList> cache = new HashMap<String, ResourceList>(); private static List<ResourceFactory> loaders = new ArrayList<ResourceFactory>(); static { @@ -162,48 +150,11 @@ cache.clear(); } - public static AbstractResource getAnnotations(TreeLogger logger, JClassType targetClass, - String locale, boolean isConstants) throws UnableToCompleteException { - Map<String, JClassType> matchingClasses - = LocalizableLinkageCreator.findDerivedClasses(logger, targetClass); - matchingClasses.put(ResourceFactory.DEFAULT_TOKEN, targetClass); - String localeSuffix = locale; - JClassType currentClass = null; - AnnotationsResource previous = null; - AbstractResource result = null; - while (true) { - currentClass = matchingClasses.get(localeSuffix); - if (currentClass != null) { - AnnotationsResource resource; - try { - resource = new AnnotationsResource(logger, currentClass, isConstants); - } catch (AnnotationsError e) { - logger.log(TreeLogger.ERROR, e.getMessage(), e); - throw new UnableToCompleteException(); - } - if (resource.notEmpty()) { - if (result == null) { - result = resource; - } - if (previous != null) { - previous.setParentResource(resource); - } - previous = resource; - } - } - if (localeSuffix.equals(ResourceFactory.DEFAULT_TOKEN)) { - return result; - } - - localeSuffix = ResourceFactory.getParentLocaleName(localeSuffix); - } - } - - public static AbstractResource getBundle(Class<?> clazz, String locale, boolean isConstants) { + public static ResourceList getBundle(Class<?> clazz, String locale, boolean isConstants) { return getBundle(TreeLogger.NULL, clazz, locale, isConstants); } - public static AbstractResource getBundle(String path, String locale, boolean isConstants) { + public static ResourceList getBundle(String path, String locale, boolean isConstants) { return getBundle(TreeLogger.NULL, path, locale, isConstants); } @@ -214,7 +165,7 @@ * @param locale locale name * @return the resource */ - public static AbstractResource getBundle(TreeLogger logger, Class<?> javaInterface, + public static ResourceList getBundle(TreeLogger logger, Class<?> javaInterface, String locale, boolean isConstants) { if (javaInterface.isInterface() == false) { throw new IllegalArgumentException(javaInterface @@ -231,7 +182,7 @@ * @param locale locale name * @return the resource */ - public static AbstractResource getBundle(TreeLogger logger, JClassType javaInterface, + public static ResourceList getBundle(TreeLogger logger, JClassType javaInterface, String locale, boolean isConstants) { return getBundleAux(logger, new JClassTypePathTree(javaInterface), javaInterface, locale, true, isConstants); @@ -244,7 +195,7 @@ * @param locale locale name * @return the resource */ - public static AbstractResource getBundle(TreeLogger logger, String path, String locale, + public static ResourceList getBundle(TreeLogger logger, String path, String locale, boolean isConstants) { return getBundleAux(logger, new SimplePathTree(path), null, locale, true, isConstants); } @@ -281,44 +232,49 @@ return name; } - private static List<AbstractResource> findAlternativeParents(TreeLogger logger, + private static void addAlternativeParents(TreeLogger logger, ResourceFactory.AbstractPathTree tree, JClassType clazz, String locale, - boolean isConstants) { - List<AbstractResource> altParents = null; + boolean useAlternativeParents, boolean isConstants, + ResourceList resources, Set<String> seenPaths) { if (tree != null) { - altParents = new ArrayList<AbstractResource>(); for (int i = 0; i < tree.numChildren(); i++) { ResourceFactory.AbstractPathTree child = tree.getChild(i); - AbstractResource altParent = getBundleAux(logger, child, child.getJClassType(clazz), - locale, false, isConstants); - if (altParent != null) { - altParents.add(altParent); - } + addResources(logger, child, child.getJClassType(clazz), + locale, useAlternativeParents, isConstants, resources, seenPaths); } } - return altParents; } - private static AbstractResource findPrimaryParent(TreeLogger logger, + private static void addPrimaryParent(TreeLogger logger, ResourceFactory.AbstractPathTree tree, JClassType clazz, String locale, - boolean isConstants) { + boolean isConstants, ResourceList resources, Set<String> seenPaths) { // If we are not in the default case, calculate parent if (!DEFAULT_TOKEN.equals(locale)) { - return getBundleAux(logger, tree, clazz, getParentLocaleName(locale), false, isConstants); + addResources(logger, tree, clazz, getParentLocaleName(locale), + false, isConstants, resources, seenPaths); } - return null; } - private static AbstractResource getBundleAux(TreeLogger logger, - ResourceFactory.AbstractPathTree tree, JClassType clazz, String locale, boolean required, - boolean isConstants) { + private static void addResources(TreeLogger logger, + ResourceFactory.AbstractPathTree tree, JClassType clazz, String locale, + boolean useAlternateParents, boolean isConstants, + ResourceList resources, Set<String> seenPaths) { String targetPath = tree.getPath(); + String localizedPath = targetPath; + if (!DEFAULT_TOKEN.equals(locale)) { + localizedPath = targetPath + LOCALE_SEPARATOR + locale; + } + if (seenPaths.contains(localizedPath)) { + return; + } + seenPaths.add(localizedPath); ClassLoader loader = AbstractResource.class.getClassLoader(); Map<String, JClassType> matchingClasses = null; if (clazz != null) { try { - matchingClasses = LocalizableLinkageCreator.findDerivedClasses(logger, clazz); + matchingClasses = LocalizableLinkageCreator.findDerivedClasses(logger, + clazz); /* * In this case, we specifically want to be able to look at the interface * instead of just implementations. @@ -344,76 +300,68 @@ locale = DEFAULT_TOKEN; } - // Calculate baseName - String localizedPath = targetPath; - if (!DEFAULT_TOKEN.equals(locale)) { - localizedPath = targetPath + LOCALE_SEPARATOR + locale; - } - AbstractResource result = cache.get(localizedPath); - if (result != null) { - if (result == NOT_FOUND) { - return null; - } else { - return result; + // Check for file-based resources. + String partialPath = localizedPath.replace('.', '/'); + for (int i = 0; i < loaders.size(); i++) { + ResourceFactory element = loaders.get(i); + String path = partialPath + "." + element.getExt(); + InputStream m = loader.getResourceAsStream(path); + if (m != null) { + AbstractResource found = element.load(m); + found.setPath(path); + resources.add(found); } } - String partialPath = localizedPath.replace('.', '/'); - AbstractResource parent = findPrimaryParent(logger, tree, clazz, locale, isConstants); - List<AbstractResource> altParents = findAlternativeParents(logger, tree, clazz, locale, isConstants); - AbstractResource found = null; + // Check for annotations JClassType currentClass = matchingClasses.get(locale); if (currentClass != null) { AnnotationsResource resource; try { - resource = new AnnotationsResource(logger, currentClass, isConstants); + resource = new AnnotationsResource(logger, currentClass, locale, + isConstants); if (resource.notEmpty()) { - found = resource; - found.setPath(currentClass.getQualifiedSourceName()); + resource.setPath(currentClass.getQualifiedSourceName()); + resources.add(resource); } } catch (AnnotationsError e) { logger.log(TreeLogger.ERROR, e.getMessage(), e); } } - for (int i = 0; found == null && i < loaders.size(); i++) { - ResourceFactory element = loaders.get(i); - String path = partialPath + "." + element.getExt(); - InputStream m = loader.getResourceAsStream(path); - if (m != null) { - found = element.load(m); - found.setPath(path); - } - } - if (found == null) { - if (parent != null) { - found = parent; - } else { - found = NOT_FOUND; - } - } else { - found.setPrimaryParent(parent); - found.setLocaleName(locale); - for (int j = 0; j < altParents.size(); j++) { - AbstractResource altParent = altParents.get(j); - found.addAlternativeParent(altParent); - } - found.checkKeys(); - } + + // Add our parent, if any + addPrimaryParent(logger, tree, clazz, locale, isConstants, resources, + seenPaths); - cache.put(localizedPath, found); - - if (found == NOT_FOUND) { - if (required) { - throw new MissingResourceException( - "Could not find any resource associated with " + tree.getPath(), - null, null); - } else { - return null; - } + // Add our alternate parents + if (useAlternateParents) { + addAlternativeParents(logger, tree, clazz, locale, useAlternateParents, + isConstants, resources, seenPaths); } + } - // At this point, found cannot be equal to null or NOT_FOUND - return found; + private static ResourceList getBundleAux(TreeLogger logger, + ResourceFactory.AbstractPathTree tree, JClassType clazz, String locale, + boolean required, boolean isConstants) { + String cacheKey = tree.getPath() + "_" + locale; + if (cache.containsKey(cacheKey)) { + return cache.get(cacheKey); + } + Set<String> seenPaths = new HashSet<String>(); + final ResourceList resources = new ResourceList(); + addResources(logger, tree, clazz, locale, true, isConstants, resources, + seenPaths); + String className = tree.getPath(); + if (clazz != null) { + className = clazz.getQualifiedSourceName(); + } + TreeLogger branch = logger.branch(TreeLogger.SPAM, "Resource search order for " + + className + ", locale " + locale); + for (AbstractResource resource : resources) { + branch.log(TreeLogger.SPAM, resource.toString()); + } + cache.put(cacheKey, resources); + return resources; } abstract String getExt();
diff --git a/user/src/com/google/gwt/i18n/rebind/SimpleValueMethodCreator.java b/user/src/com/google/gwt/i18n/rebind/SimpleValueMethodCreator.java index 00db1d4..b28b2a5 100644 --- a/user/src/com/google/gwt/i18n/rebind/SimpleValueMethodCreator.java +++ b/user/src/com/google/gwt/i18n/rebind/SimpleValueMethodCreator.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -18,6 +18,7 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JMethod; +import com.google.gwt.i18n.rebind.AbstractResource.ResourceList; import com.google.gwt.user.rebind.AbstractGeneratorClassCreator; /** @@ -88,8 +89,8 @@ @Override public void createMethodFor(TreeLogger logger, JMethod targetMethod, - String key, AbstractResource resource, String locale) throws UnableToCompleteException { - String value = resource.getRequiredStringExt(logger, key, null); + String key, ResourceList resource, String locale) throws UnableToCompleteException { + String value = resource.getRequiredStringExt(key, null); try { String translatedValue = valueCreator.getValue(value); println("return " + translatedValue + ";");
diff --git a/user/src/com/google/gwt/i18n/rebind/format/MessageCatalogFormat.java b/user/src/com/google/gwt/i18n/rebind/format/MessageCatalogFormat.java index c61ac17..5693c39 100644 --- a/user/src/com/google/gwt/i18n/rebind/format/MessageCatalogFormat.java +++ b/user/src/com/google/gwt/i18n/rebind/format/MessageCatalogFormat.java
@@ -18,7 +18,7 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JClassType; -import com.google.gwt.i18n.rebind.AbstractResource; +import com.google.gwt.i18n.rebind.AbstractResource.ResourceList; import java.io.PrintWriter; @@ -40,7 +40,8 @@ * Write a message catalog file. * * @param logger TreeLogger for logging errors/etc - * @param resource the contents to write + * @param locale locale of this output file + * @param resourceList the contents to write * @param out the PrintWriter to generate output on * @param messageInterface the interface to create (so additional * annotations may be accessed) @@ -48,8 +49,9 @@ * the output file. In this case, the implementation must have already * logged an appropriate ERROR message to the logger. */ - void write(TreeLogger logger, AbstractResource resource, PrintWriter out, - JClassType messageInterface) throws UnableToCompleteException; + void write(TreeLogger logger, String locale, ResourceList resourceList, + PrintWriter out, JClassType messageInterface) + throws UnableToCompleteException; /** * @return the extension to use for this file type, including the dot
diff --git a/user/src/com/google/gwt/i18n/rebind/format/PropertiesFormat.java b/user/src/com/google/gwt/i18n/rebind/format/PropertiesFormat.java index 88add3c..043b659 100644 --- a/user/src/com/google/gwt/i18n/rebind/format/PropertiesFormat.java +++ b/user/src/com/google/gwt/i18n/rebind/format/PropertiesFormat.java
@@ -18,8 +18,8 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.i18n.client.PluralRule.PluralForm; -import com.google.gwt.i18n.rebind.AbstractResource; import com.google.gwt.i18n.rebind.AnnotationsResource; +import com.google.gwt.i18n.rebind.AbstractResource.ResourceList; import com.google.gwt.i18n.rebind.AnnotationsResource.ArgumentInfo; import java.io.PrintWriter; @@ -46,42 +46,44 @@ * @see com.google.gwt.i18n.rebind.format.MessageCatalogFormat#write(com.google.gwt.i18n.rebind.util.AbstractResource, * java.io.File, com.google.gwt.core.ext.typeinfo.JClassType) */ - public void write(TreeLogger logger, AbstractResource resource, PrintWriter out, - JClassType messageInterface) { - AnnotationsResource annotResource = resource.getAnnotationsResource(); + public void write(TreeLogger logger, String locale, + ResourceList resourceList, PrintWriter out, JClassType messageInterface) { writeComment(out, "Generated from " + messageInterface.getQualifiedSourceName()); - String localeName = resource.getLocaleName(); - if (localeName != null) { - writeComment(out, "for locale " + localeName); + if (locale != null) { + writeComment(out, "for locale " + locale); } // Sort keys for deterministic output. - Set<String> keySet = resource.keySet(); + Set<String> keySet = resourceList.keySet(); String[] sortedKeys = keySet.toArray(new String[keySet.size()]); Arrays.sort(sortedKeys); for (String key : sortedKeys) { out.println(); + AnnotationsResource annotResource = resourceList.getAnnotationsResource( + logger, key); if (annotResource != null) { // Write comments from the annotations. writeAnnotComments(out, annotResource, key); } // Collect plural forms for this locale. - PluralForm[] pluralForms = resource.getPluralForms(key); + PluralForm[] pluralForms = resourceList.getPluralForms(key); if (pluralForms != null) { for (PluralForm form : pluralForms) { String name = form.getName(); if ("other".equals(name)) { // write the "other" description here, and the default message writeComment(out, "- " + form.getDescription()); - write(out, key, resource.getString(key)); + write(out, key, resourceList.getString(key)); } else { - String comment = "- plural form '" + form.getName() + "': " + form.getDescription(); + String comment = "- plural form '" + form.getName() + "': " + + form.getDescription(); if (!form.getWarnIfMissing()) { comment += " (optional)"; } writeComment(out, comment); - String translated = resource.getStringExt(key, form.getName()); + String translated = resourceList.getStringExt(key, + form.getName()); if (translated == null) { translated = ""; } @@ -89,7 +91,7 @@ } } } else { - write(out, key, resource.getString(key)); + write(out, key, resourceList.getString(key)); } } }
diff --git a/user/src/com/google/gwt/junit/GWTMockUtilities.java b/user/src/com/google/gwt/junit/GWTMockUtilities.java index b9fe106..906aea4 100644 --- a/user/src/com/google/gwt/junit/GWTMockUtilities.java +++ b/user/src/com/google/gwt/junit/GWTMockUtilities.java
@@ -54,9 +54,9 @@ * } * * public void testSomething() { - * MyWidget mock = EasyMock.createMock(MyWidget.class); - * mock.setText("expected text"); - * mock.replay(); + * MyStatusWidget mock = EasyMock.createMock(MyStatusWidget.class); + * EasyMock.expect(mock.setText("expected text")); + * EasyMock.replay(mock); * * StatusController controller = new StatusController(mock); * controller.setStatus("expected text"); @@ -65,7 +65,7 @@ * } * </pre> */ - public static void disarm(/* TearDownAccepter test */) { + public static void disarm() { GWTBridge bridge = new GWTDummyBridge(); setGwtBridge(bridge); }
diff --git a/user/src/com/google/gwt/user/ListBox.gwt.xml b/user/src/com/google/gwt/user/ListBox.gwt.xml deleted file mode 100644 index f245f28..0000000 --- a/user/src/com/google/gwt/user/ListBox.gwt.xml +++ /dev/null
@@ -1,34 +0,0 @@ -<!-- --> -<!-- Copyright 2007 Google Inc. --> -<!-- Licensed under the Apache License, Version 2.0 (the "License"); you --> -<!-- may not use this file except in compliance with the License. You may --> -<!-- 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. License for the specific language governing permissions and --> -<!-- limitations under the License. --> - -<!-- Deferred binding rules for ListBox's implementation. --> -<!-- --> -<!-- This module is typically inherited via com.google.gwt.user.User --> -<!-- --> -<module> - <inherits name="com.google.gwt.core.Core"/> - <inherits name="com.google.gwt.user.UserAgent"/> - <inherits name="com.google.gwt.user.DOM"/> - - <!-- Fall through to this rule if the browser isn't Safari --> - <replace-with class="com.google.gwt.user.client.ui.ListBox.Impl"> - <when-type-is class="com.google.gwt.user.client.ui.ListBox.Impl"/> - </replace-with> - - <!-- Safari requires a different ListBox implementation --> - <replace-with class="com.google.gwt.user.client.ui.ListBox.ImplSafari"> - <when-type-is class="com.google.gwt.user.client.ui.ListBox.Impl"/> - <when-property-is name="user.agent" value="safari"/> - </replace-with> -</module>
diff --git a/user/src/com/google/gwt/user/User.gwt.xml b/user/src/com/google/gwt/user/User.gwt.xml index 707e33c..99baf1c 100644 --- a/user/src/com/google/gwt/user/User.gwt.xml +++ b/user/src/com/google/gwt/user/User.gwt.xml
@@ -35,7 +35,6 @@ <inherits name="com.google.gwt.user.ClippedImage"/> <inherits name="com.google.gwt.user.RichText"/> <inherits name="com.google.gwt.user.SplitPanel"/> - <inherits name="com.google.gwt.user.ListBox" /> <inherits name="com.google.gwt.user.CaptionPanel" /> <inherits name="com.google.gwt.user.Window" /> <inherits name="com.google.gwt.user.Accessibility"/>
diff --git a/user/src/com/google/gwt/user/client/ui/Anchor.java b/user/src/com/google/gwt/user/client/ui/Anchor.java index f7669f5..8910305 100644 --- a/user/src/com/google/gwt/user/client/ui/Anchor.java +++ b/user/src/com/google/gwt/user/client/ui/Anchor.java
@@ -33,16 +33,16 @@ HasName, HasText, HasHTML, HasWordWrap, HasDirection { /** - * Creates an Anchor widget that wraps an existing <div> or <span> - * element. + * Creates an Anchor widget that wraps an existing <a> element. * - * This element must already be attached to the document. + * 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 */ public static Anchor wrap(Element element) { - // Assert that the element is of the correct type and is attached. - AnchorElement.as(element); + // Assert that the element is attached. assert Document.get().getBody().isOrHasChild(element); Anchor anchor = new Anchor(element); @@ -147,7 +147,14 @@ this(text, false, href, target); } - private Anchor(Element element) { + /** + * This constructor may be used by subclasses to explicitly use an existing + * element. This element must be an <a> element. + * + * @param element the element to be used + */ + protected Anchor(Element element) { + AnchorElement.as(element); setElement(element); }
diff --git a/user/src/com/google/gwt/user/client/ui/Button.java b/user/src/com/google/gwt/user/client/ui/Button.java index a29a69b..bce6eac 100644 --- a/user/src/com/google/gwt/user/client/ui/Button.java +++ b/user/src/com/google/gwt/user/client/ui/Button.java
@@ -17,8 +17,7 @@ import com.google.gwt.dom.client.ButtonElement; import com.google.gwt.dom.client.Document; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; +import com.google.gwt.dom.client.Element; /** * A standard push-button widget. @@ -33,7 +32,8 @@ * </ul> * * <p> - * <h3>Example</h3> {@example com.google.gwt.examples.ButtonExample} + * <h3>Example</h3> + * {@example com.google.gwt.examples.ButtonExample} * </p> */ public class Button extends ButtonBase { @@ -41,16 +41,17 @@ /** * Creates a Button widget that wraps an existing <button> element. * - * This element must already be attached to the document. + * 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 */ public static Button wrap(com.google.gwt.dom.client.Element element) { - // Assert that the element is of the correct type and is attached. - ButtonElement.as(element); + // Assert that the element is attached. assert Document.get().getBody().isOrHasChild(element); - Button button = new Button((Element) element); + Button button = new Button(element); // Mark it attached and remember it for cleanup. button.onAttach(); @@ -77,7 +78,7 @@ * Creates a button with no caption. */ public Button() { - super(DOM.createButton()); + super(Document.get().createButtonElement()); adjustType(getElement()); setStyleName("gwt-Button"); } @@ -103,8 +104,15 @@ addClickListener(listener); } - private Button(Element element) { - super(element); + /** + * This constructor may be used by subclasses to explicitly use an existing + * element. This element must be a <button> element. + * + * @param element the element to be used + */ + protected Button(com.google.gwt.dom.client.Element element) { + super(element.<Element>cast()); + ButtonElement.as(element); } /**
diff --git a/user/src/com/google/gwt/user/client/ui/ButtonBase.java b/user/src/com/google/gwt/user/client/ui/ButtonBase.java index 7aaf631..1e6d99d 100644 --- a/user/src/com/google/gwt/user/client/ui/ButtonBase.java +++ b/user/src/com/google/gwt/user/client/ui/ButtonBase.java
@@ -15,8 +15,7 @@ */ package com.google.gwt.user.client.ui; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; +import com.google.gwt.dom.client.Element; /** * Abstract base class for {@link com.google.gwt.user.client.ui.Button}, @@ -35,18 +34,18 @@ } public String getHTML() { - return DOM.getInnerHTML(getElement()); + return getElement().getInnerHTML(); } public String getText() { - return DOM.getInnerText(getElement()); + return getElement().getInnerText(); } public void setHTML(String html) { - DOM.setInnerHTML(getElement(), html); + getElement().setInnerHTML(html); } public void setText(String text) { - DOM.setInnerText(getElement(), text); + getElement().setInnerText(text); } }
diff --git a/user/src/com/google/gwt/user/client/ui/CustomButton.java b/user/src/com/google/gwt/user/client/ui/CustomButton.java index d08c817..270387f 100644 --- a/user/src/com/google/gwt/user/client/ui/CustomButton.java +++ b/user/src/com/google/gwt/user/client/ui/CustomButton.java
@@ -583,26 +583,28 @@ super.onBrowserEvent(event); // Synthesize clicks based on keyboard events AFTER the normal key handling. - char keyCode = (char) DOM.eventGetKeyCode(event); - switch (type) { - case Event.ONKEYDOWN: - if (keyCode == ' ') { - isFocusing = true; - onClickStart(); - } - break; - case Event.ONKEYUP: - if (isFocusing && keyCode == ' ') { - isFocusing = false; - onClick(); - } - break; - case Event.ONKEYPRESS: - if (keyCode == '\n' || keyCode == '\r') { - onClickStart(); - onClick(); - } - break; + if ((event.getTypeInt() & Event.KEYEVENTS) != 0) { + char keyCode = (char) DOM.eventGetKeyCode(event); + switch (type) { + case Event.ONKEYDOWN: + if (keyCode == ' ') { + isFocusing = true; + onClickStart(); + } + break; + case Event.ONKEYUP: + if (isFocusing && keyCode == ' ') { + isFocusing = false; + onClick(); + } + break; + case Event.ONKEYPRESS: + if (keyCode == '\n' || keyCode == '\r') { + onClickStart(); + onClick(); + } + break; + } } }
diff --git a/user/src/com/google/gwt/user/client/ui/FileUpload.java b/user/src/com/google/gwt/user/client/ui/FileUpload.java index 966fbf3..f1bbf02 100644 --- a/user/src/com/google/gwt/user/client/ui/FileUpload.java +++ b/user/src/com/google/gwt/user/client/ui/FileUpload.java
@@ -1,5 +1,5 @@ /* - * Copyright 2006 Google Inc. + * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -16,9 +16,8 @@ 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.dom.client.InputElement; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; /** * A widget that wraps the HTML <input type='file'> element. This widget @@ -36,16 +35,17 @@ * Creates a FileUpload widget that wraps an existing <input * type='file'> element. * - * This element must already be attached to the document. + * 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 */ - public static FileUpload wrap(com.google.gwt.dom.client.Element element) { - // Assert that the element is of the right type and is attached. - assert InputElement.as(element).getType().equalsIgnoreCase("file"); + public static FileUpload wrap(Element element) { + // Assert that the element is attached. assert Document.get().getBody().isOrHasChild(element); - FileUpload fileUpload = new FileUpload((Element) element); + FileUpload fileUpload = new FileUpload(element); // Mark it attached and remember it for cleanup. fileUpload.onAttach(); @@ -58,12 +58,19 @@ * Constructs a new file upload widget. */ public FileUpload() { - setElement(DOM.createElement("input")); - DOM.setElementProperty(getElement(), "type", "file"); + setElement(Document.get().createFileInputElement()); setStyleName("gwt-FileUpload"); } - private FileUpload(Element element) { + /** + * This constructor may be used by subclasses to explicitly use an existing + * element. This element must be an <input> element whose type is + * 'file'. + * + * @param element the element to be used + */ + protected FileUpload(Element element) { + assert InputElement.as(element).getType().equalsIgnoreCase("file"); setElement(element); } @@ -74,14 +81,18 @@ * @return the widget's filename */ public String getFilename() { - return DOM.getElementProperty(getElement(), "value"); + return getInputElement().getValue(); } public String getName() { - return DOM.getElementProperty(getElement(), "name"); + return getInputElement().getName(); } public void setName(String name) { - DOM.setElementProperty(getElement(), "name", name); + getInputElement().setName(name); + } + + private InputElement getInputElement() { + return getElement().cast(); } }
diff --git a/user/src/com/google/gwt/user/client/ui/FocusWidget.java b/user/src/com/google/gwt/user/client/ui/FocusWidget.java index e1ea806..cb09062 100644 --- a/user/src/com/google/gwt/user/client/ui/FocusWidget.java +++ b/user/src/com/google/gwt/user/client/ui/FocusWidget.java
@@ -15,8 +15,8 @@ */ package com.google.gwt.user.client.ui; +import com.google.gwt.dom.client.Element; import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.impl.FocusImpl; @@ -165,7 +165,7 @@ } @Override - protected void setElement(Element elem) { + protected void setElement(com.google.gwt.user.client.Element elem) { super.setElement(elem); // Accessibility: setting tab index to be 0 by default, ensuring element
diff --git a/user/src/com/google/gwt/user/client/ui/FormPanel.java b/user/src/com/google/gwt/user/client/ui/FormPanel.java index d873e14..85ab71a 100644 --- a/user/src/com/google/gwt/user/client/ui/FormPanel.java +++ b/user/src/com/google/gwt/user/client/ui/FormPanel.java
@@ -18,11 +18,10 @@ import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.GWT.UncaughtExceptionHandler; import com.google.gwt.dom.client.Document; +import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.FormElement; import com.google.gwt.user.client.Command; -import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.DeferredCommand; -import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.impl.FormPanelImpl; import com.google.gwt.user.client.ui.impl.FormPanelImplHost; @@ -39,7 +38,9 @@ * <li>{@link com.google.gwt.user.client.ui.TextBox}</li> * <li>{@link com.google.gwt.user.client.ui.PasswordTextBox}</li> * <li>{@link com.google.gwt.user.client.ui.RadioButton}</li> + * <li>{@link com.google.gwt.user.client.ui.SimpleRadioButton}</li> * <li>{@link com.google.gwt.user.client.ui.CheckBox}</li> + * <li>{@link com.google.gwt.user.client.ui.SimpleCheckBox}</li> * <li>{@link com.google.gwt.user.client.ui.TextArea}</li> * <li>{@link com.google.gwt.user.client.ui.ListBox}</li> * <li>{@link com.google.gwt.user.client.ui.FileUpload}</li> @@ -90,16 +91,17 @@ /** * Creates a FormPanel that wraps an existing <form> element. * - * This element must already be attached to the document. + * 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 */ - public static FormPanel wrap(com.google.gwt.dom.client.Element element) { - // Assert that the element is of the correct type and is attached. - FormElement.as(element); + public static FormPanel wrap(Element element) { + // Assert that the element is attached. assert Document.get().getBody().isOrHasChild(element); - FormPanel formPanel = new FormPanel((Element) element); + FormPanel formPanel = new FormPanel(element); // Mark it attached and remember it for cleanup. formPanel.onAttach(); @@ -110,7 +112,7 @@ private FormHandlerCollection formHandlers; private String frameName; - private Element iframe; + private Element synthesizedFrame; /** * Creates a new FormPanel. When created using this constructor, it will be @@ -132,7 +134,7 @@ * cannot be made to work properly on all browsers. */ public FormPanel() { - super(DOM.createForm()); + super(Document.get().createFormElement()); frameName = "FormPanel_" + (++formId); setTarget(frameName); @@ -171,12 +173,19 @@ * page be replaced */ public FormPanel(String target) { - super(DOM.createForm()); + super(Document.get().createFormElement()); setTarget(target); } - private FormPanel(Element elem) { - super(elem); + /** + * This constructor may be used by subclasses to explicitly use an existing + * element. This element must be a <form> element. + * + * @param element the element to be used + */ + protected FormPanel(Element element) { + super(element); + FormElement.as(element); } public void addFormHandler(FormHandler handler) { @@ -193,7 +202,7 @@ * @return the form's action */ public String getAction() { - return DOM.getElementProperty(getElement(), "action"); + return getFormElement().getAction(); } /** @@ -213,7 +222,7 @@ * @return the form's method */ public String getMethod() { - return DOM.getElementProperty(getElement(), "method"); + return getFormElement().getMethod(); } /** @@ -224,7 +233,7 @@ * @return the form's target. */ public String getTarget() { - return DOM.getElementProperty(getElement(), "target"); + return getFormElement().getTarget(); } public boolean onFormSubmit() { @@ -258,7 +267,7 @@ * @param url the form's action */ public void setAction(String url) { - DOM.setElementProperty(getElement(), "action", url); + getFormElement().setAction(url); } /** @@ -278,7 +287,7 @@ * @param method the form's method */ public void setMethod(String method) { - DOM.setElementProperty(getElement(), "method", method); + getFormElement().setMethod(method); } /** @@ -299,34 +308,44 @@ } } - impl.submit(getElement(), iframe); + impl.submit(getElement(), synthesizedFrame); } @Override protected void onAttach() { super.onAttach(); - // Create and attach a hidden iframe to the body element. - createFrame(); - DOM.appendChild(RootPanel.getBodyElement(), iframe); + if (frameName != null) { + // Create and attach a hidden iframe to the body element. + createFrame(); + Document.get().getBody().appendChild(synthesizedFrame); - // Hook up the underlying iframe's onLoad event when attached to the DOM. - // Making this connection only when attached avoids memory-leak issues. - // The FormPanel cannot use the built-in GWT event-handling mechanism - // because there is no standard onLoad event on iframes that works across - // browsers. - impl.hookEvents(iframe, getElement(), this); + // Hook up the underlying iframe's onLoad event when attached to the DOM. + // Making this connection only when attached avoids memory-leak issues. + // The FormPanel cannot use the built-in GWT event-handling mechanism + // because there is no standard onLoad event on iframes that works across + // browsers. + impl.hookEvents(synthesizedFrame, getElement(), this); + } } @Override protected void onDetach() { super.onDetach(); - // Unhook the iframe's onLoad when detached. - impl.unhookEvents(iframe, getElement()); + if (synthesizedFrame != null) { + // Unhook the iframe's onLoad when detached. + impl.unhookEvents(synthesizedFrame, getElement()); - DOM.removeChild(RootPanel.getBodyElement(), iframe); - iframe = null; + // And remove it from the document. + Document.get().getBody().removeChild(synthesizedFrame); + synthesizedFrame = null; + } + } + + // For unit-tests. + Element getSynthesizedIFrame() { + return synthesizedFrame; } private void createFrame() { @@ -334,11 +353,15 @@ // the form will be submitted. We have to create the iframe using innerHTML, // because setting an iframe's 'name' property dynamically doesn't work on // most browsers. - Element dummy = DOM.createDiv(); - DOM.setInnerHTML(dummy, "<iframe src=\"javascript:''\" name='" + frameName + Element dummy = Document.get().createDivElement(); + dummy.setInnerHTML("<iframe src=\"javascript:''\" name='" + frameName + "' style='position:absolute;width:0;height:0;border:0'>"); - iframe = DOM.getFirstChild(dummy); + synthesizedFrame = dummy.getFirstChildElement(); + } + + private FormElement getFormElement() { + return getElement().cast(); } private boolean onFormSubmitAndCatch(UncaughtExceptionHandler handler) { @@ -375,13 +398,13 @@ // 'infinite loading' state. See issue 916. DeferredCommand.addCommand(new Command() { public void execute() { - formHandlers.fireOnComplete(FormPanel.this, impl.getContents(iframe)); + formHandlers.fireOnComplete(FormPanel.this, impl.getContents(synthesizedFrame)); } }); } } private void setTarget(String target) { - DOM.setElementProperty(getElement(), "target", target); + getFormElement().setTarget(target); } }
diff --git a/user/src/com/google/gwt/user/client/ui/Frame.java b/user/src/com/google/gwt/user/client/ui/Frame.java index c8a6069..c6b09da 100644 --- a/user/src/com/google/gwt/user/client/ui/Frame.java +++ b/user/src/com/google/gwt/user/client/ui/Frame.java
@@ -16,9 +16,9 @@ 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.dom.client.FrameElement; import com.google.gwt.dom.client.IFrameElement; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; /** * A widget that wraps an IFRAME element, which can contain an arbitrary web @@ -44,16 +44,17 @@ /** * Creates a Frame widget that wraps an existing <frame> element. * - * This element must already be attached to the document. + * 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 */ - public static Frame wrap(com.google.gwt.dom.client.Element element) { - // Assert that the element is of the correct type and is attached. - IFrameElement.as(element); + public static Frame wrap(Element element) { + // Assert that the element is attached. assert Document.get().getBody().isOrHasChild(element); - Frame frame = new Frame((Element) element); + Frame frame = new Frame(element); // Mark it attached and remember it for cleanup. frame.onAttach(); @@ -66,7 +67,7 @@ * Creates an empty frame. */ public Frame() { - setElement(DOM.createIFrame()); + setElement(Document.get().createIFrameElement()); setStyleName(DEFAULT_STYLENAME); } @@ -80,7 +81,14 @@ setUrl(url); } - private Frame(Element element) { + /** + * This constructor may be used by subclasses to explicitly use an existing + * element. This element must be an <iframe> element. + * + * @param element the element to be used + */ + protected Frame(Element element) { + IFrameElement.as(element); setElement(element); } @@ -90,7 +98,7 @@ * @return the frame's URL */ public String getUrl() { - return DOM.getElementProperty(getElement(), "src"); + return getFrameElement().getSrc(); } /** @@ -99,6 +107,10 @@ * @param url the frame's new URL */ public void setUrl(String url) { - DOM.setElementProperty(getElement(), "src", url); + getFrameElement().setSrc(url); + } + + private FrameElement getFrameElement() { + return getElement().cast(); } }
diff --git a/user/src/com/google/gwt/user/client/ui/HTML.java b/user/src/com/google/gwt/user/client/ui/HTML.java index 5cebe0a..d6959a0 100644 --- a/user/src/com/google/gwt/user/client/ui/HTML.java +++ b/user/src/com/google/gwt/user/client/ui/HTML.java
@@ -16,8 +16,7 @@ package com.google.gwt.user.client.ui; import com.google.gwt.dom.client.Document; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; +import com.google.gwt.dom.client.Element; /** * A widget that can contain arbitrary HTML. @@ -48,17 +47,17 @@ * Creates an HTML widget that wraps an existing <div> or <span> * element. * - * This element must already be attached to the document. + * 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 */ - public static HTML wrap(com.google.gwt.dom.client.Element element) { - // Assert that the element is of the correct type and is attached. - assert element.getTagName().equalsIgnoreCase("div") - || element.getTagName().equalsIgnoreCase("span"); + public static HTML wrap(Element element) { + // Assert that the element is attached. assert Document.get().getBody().isOrHasChild(element); - HTML html = new HTML((Element) element); + HTML html = new HTML(element); // Mark it attached and remember it for cleanup. html.onAttach(); @@ -71,7 +70,7 @@ * Creates an empty HTML widget. */ public HTML() { - super(DOM.createDiv()); + super(Document.get().createDivElement()); setStyleName("gwt-HTML"); } @@ -97,15 +96,23 @@ setWordWrap(wordWrap); } - HTML(Element element) { + /** + * This constructor may be used by subclasses to explicitly use an existing + * element. This element must be either a <div> or <span> element. + * + * @param element the element to be used + */ + protected HTML(Element element) { super(element); + assert element.getTagName().equalsIgnoreCase("div") + || element.getTagName().equalsIgnoreCase("span"); } public String getHTML() { - return DOM.getInnerHTML(getElement()); + return getElement().getInnerHTML(); } public void setHTML(String html) { - DOM.setInnerHTML(getElement(), html); + getElement().setInnerHTML(html); } }
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 b667197..fcd978b 100644 --- a/user/src/com/google/gwt/user/client/ui/Hidden.java +++ b/user/src/com/google/gwt/user/client/ui/Hidden.java
@@ -1,5 +1,5 @@ /* - * Copyright 2006 Google Inc. + * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -13,13 +13,11 @@ * 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.dom.client.InputElement; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; /** * Represents a hidden field in an HTML form. @@ -30,16 +28,17 @@ * Creates a Hidden widget that wraps an existing <input type='hidden'> * element. * - * This element must already be attached to the document. + * 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 */ - public static Hidden wrap(com.google.gwt.dom.client.Element element) { - // Assert that the element is of the correct type and is attached. - assert InputElement.as(element).getType().equalsIgnoreCase("hidden"); + public static Hidden wrap(Element element) { + // Assert that the element is attached. assert Document.get().getBody().isOrHasChild(element); - Hidden hidden = new Hidden((Element) element); + Hidden hidden = new Hidden(element); // Mark it attached and remember it for cleanup. hidden.onAttach(); @@ -52,9 +51,7 @@ * Constructor for <code>Hidden</code>. */ public Hidden() { - Element e = DOM.createElement("input"); - setElement(e); - DOM.setElementProperty(e, "type", "hidden"); + setElement(Document.get().createHiddenInputElement()); } /** @@ -78,7 +75,15 @@ setValue(value); } - private Hidden(Element element) { + /** + * This constructor may be used by subclasses to explicitly use an existing + * element. This element must be an <input> element whose type is + * 'hidden'. + * + * @param element the element to be used + */ + protected Hidden(Element element) { + assert InputElement.as(element).getType().equalsIgnoreCase("hidden"); setElement(element); } @@ -88,7 +93,7 @@ * @return the default value */ public String getDefaultValue() { - return DOM.getElementProperty(getElement(), "defaultValue"); + return getInputElement().getDefaultValue(); } /** @@ -97,7 +102,7 @@ * @return the id */ public String getID() { - return DOM.getElementProperty(getElement(), "id"); + return getElement().getId(); } /** @@ -107,7 +112,7 @@ */ public String getName() { - return DOM.getElementProperty(getElement(), "name"); + return getInputElement().getName(); } /** @@ -116,7 +121,7 @@ * @return the value */ public String getValue() { - return DOM.getElementProperty(getElement(), "value"); + return getInputElement().getValue(); } /** @@ -125,7 +130,7 @@ * @param defaultValue default value to set */ public void setDefaultValue(String defaultValue) { - DOM.setElementProperty(getElement(), "defaultValue", defaultValue); + getInputElement().setDefaultValue(defaultValue); } /** @@ -134,7 +139,7 @@ * @param id id to set */ public void setID(String id) { - DOM.setElementProperty(getElement(), "id", id); + getElement().setId(id); } /** @@ -148,7 +153,8 @@ } else if (name.equals("")) { throw new IllegalArgumentException("Name cannot be an empty string."); } - DOM.setElementProperty(getElement(), "name", name); + + getInputElement().setName(name); } /** @@ -157,6 +163,10 @@ * @param value value to set */ public void setValue(String value) { - DOM.setElementProperty(getElement(), "value", value); + getInputElement().setValue(value); + } + + private InputElement getInputElement() { + return getElement().cast(); } }
diff --git a/user/src/com/google/gwt/user/client/ui/Image.java b/user/src/com/google/gwt/user/client/ui/Image.java index a6feb7e..8d0e256 100644 --- a/user/src/com/google/gwt/user/client/ui/Image.java +++ b/user/src/com/google/gwt/user/client/ui/Image.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -17,11 +17,10 @@ import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Document; +import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.ImageElement; import com.google.gwt.user.client.Command; -import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.DeferredCommand; -import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.impl.ClippedImageImpl; @@ -224,12 +223,12 @@ private static class UnclippedState extends State { UnclippedState(Element element) { - DOM.sinkEvents(element, Event.ONCLICK | Event.MOUSEEVENTS | Event.ONLOAD - | Event.ONERROR | Event.ONMOUSEWHEEL); + Event.sinkEvents(element, Event.ONCLICK | Event.MOUSEEVENTS + | Event.ONLOAD | Event.ONERROR | Event.ONMOUSEWHEEL); } UnclippedState(Image image) { - image.replaceElement(DOM.createImg()); + image.replaceElement(Document.get().createImageElement()); image.sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS | Event.ONLOAD | Event.ONERROR | Event.ONMOUSEWHEEL); } @@ -241,7 +240,7 @@ @Override public int getHeight(Image image) { - return DOM.getElementPropertyInt(image.getElement(), "height"); + return image.getImageElement().getHeight(); } @Override @@ -256,17 +255,17 @@ @Override public String getUrl(Image image) { - return DOM.getImgSrc(image.getElement()); + return image.getImageElement().getSrc(); } @Override public int getWidth(Image image) { - return DOM.getElementPropertyInt(image.getElement(), "width"); + return image.getImageElement().getWidth(); } @Override public void setUrl(Image image, String url) { - DOM.setImgSrc(image.getElement(), url); + image.getImageElement().setSrc(url); } @Override @@ -302,25 +301,26 @@ * @param url the URL of the image to be prefetched */ public static void prefetch(String url) { - Element img = DOM.createImg(); - DOM.setImgSrc(img, url); + ImageElement img = Document.get().createImageElement(); + img.setSrc(url); prefetchImages.put(url, img); } /** * Creates a Image widget that wraps an existing <img> element. * - * This element must already be attached to the document. + * 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 */ - public static Image wrap(com.google.gwt.dom.client.Element element) { - // Assert that the element is of the correct type and is attached. - ImageElement.as(element); + public static Image wrap(Element element) { + // Assert that the element is attached. assert Document.get().getBody().isOrHasChild(element); - Image image = new Image((Element) element); - image.changeState(new UnclippedState((Element) element)); + Image image = new Image(element); + image.changeState(new UnclippedState(element)); // Mark it attached and remember it for cleanup. image.onAttach(); @@ -378,7 +378,14 @@ setStyleName("gwt-Image"); } - private Image(Element element) { + /** + * This constructor may be used by subclasses to explicitly use an existing + * element. This element must be an <img> element. + * + * @param element the element to be used + */ + protected Image(Element element) { + ImageElement.as(element); setElement(element); } @@ -471,7 +478,7 @@ @Override public void onBrowserEvent(Event event) { - switch (DOM.eventGetType(event)) { + switch (event.getTypeInt()) { case Event.ONCLICK: { if (clickListeners != null) { clickListeners.fireClick(this); @@ -589,4 +596,8 @@ private void changeState(State newState) { state = newState; } + + private ImageElement getImageElement() { + return getElement().cast(); + } }
diff --git a/user/src/com/google/gwt/user/client/ui/InlineHTML.java b/user/src/com/google/gwt/user/client/ui/InlineHTML.java index 36c885a..c870c4b 100644 --- a/user/src/com/google/gwt/user/client/ui/InlineHTML.java +++ b/user/src/com/google/gwt/user/client/ui/InlineHTML.java
@@ -16,8 +16,7 @@ package com.google.gwt.user.client.ui; import com.google.gwt.dom.client.Document; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; +import com.google.gwt.dom.client.Element; /** * A widget that can contain arbitrary HTML. @@ -43,17 +42,17 @@ * Creates an InlineHTML widget that wraps an existing <div> or * <span> element. * - * This element must already be attached to the document. + * 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 */ - public static InlineHTML wrap(com.google.gwt.dom.client.Element element) { - // Assert that the element is of the correct type and is attached. - assert element.getTagName().equalsIgnoreCase("div") - || element.getTagName().equalsIgnoreCase("span"); + public static InlineHTML wrap(Element element) { + // Assert that the element is attached. assert Document.get().getBody().isOrHasChild(element); - InlineHTML html = new InlineHTML((Element) element); + InlineHTML html = new InlineHTML(element); // Mark it attached and remember it for cleanup. html.onAttach(); @@ -66,7 +65,7 @@ * Creates an empty HTML widget. */ public InlineHTML() { - super(DOM.createSpan()); + super(Document.get().createSpanElement()); setStyleName("gwt-InlineHTML"); } @@ -80,7 +79,15 @@ setHTML(html); } - private InlineHTML(Element element) { + /** + * This constructor may be used by subclasses to explicitly use an existing + * element. This element must be either a <div> <span> element. + * + * @param element the element to be used + */ + protected InlineHTML(Element element) { super(element); + assert element.getTagName().equalsIgnoreCase("div") + || element.getTagName().equalsIgnoreCase("span"); } }
diff --git a/user/src/com/google/gwt/user/client/ui/InlineLabel.java b/user/src/com/google/gwt/user/client/ui/InlineLabel.java index d69affa..08f3583 100644 --- a/user/src/com/google/gwt/user/client/ui/InlineLabel.java +++ b/user/src/com/google/gwt/user/client/ui/InlineLabel.java
@@ -16,8 +16,7 @@ package com.google.gwt.user.client.ui; import com.google.gwt.dom.client.Document; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; +import com.google.gwt.dom.client.Element; /** * A widget that contains arbitrary text, <i>not</i> interpreted as HTML. @@ -36,17 +35,17 @@ * Creates a InlineLabel widget that wraps an existing <div> or * <span> element. * - * This element must already be attached to the document. + * 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 */ - public static InlineLabel wrap(com.google.gwt.dom.client.Element element) { - // Assert that the element is of the correct type and is attached. - assert element.getTagName().equalsIgnoreCase("div") - || element.getTagName().equalsIgnoreCase("span"); + public static InlineLabel wrap(Element element) { + // Assert that the element is attached. assert Document.get().getBody().isOrHasChild(element); - InlineLabel label = new InlineLabel((Element) element); + InlineLabel label = new InlineLabel(element); // Mark it attached and remember it for cleanup. label.onAttach(); @@ -59,7 +58,7 @@ * Creates an empty label. */ public InlineLabel() { - super(DOM.createSpan()); + super(Document.get().createSpanElement()); setStyleName("gwt-InlineLabel"); } @@ -73,7 +72,15 @@ setText(text); } - private InlineLabel(Element element) { + /** + * This constructor may be used by subclasses to explicitly use an existing + * element. This element must be either a <div> <span> element. + * + * @param element the element to be used + */ + protected InlineLabel(Element element) { super(element); + assert element.getTagName().equalsIgnoreCase("div") + || element.getTagName().equalsIgnoreCase("span"); } }
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 c985697..07faf0b 100644 --- a/user/src/com/google/gwt/user/client/ui/Label.java +++ b/user/src/com/google/gwt/user/client/ui/Label.java
@@ -16,17 +16,16 @@ 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.client.BidiUtils; import com.google.gwt.i18n.client.HasDirection; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; /** * A widget that contains arbitrary text, <i>not</i> interpreted as HTML. * - * This widget uses a <div> element, causing it to be displayed with - * block layout. + * This widget uses a <div> element, causing it to be displayed with block + * layout. * * <h3>CSS Style Rules</h3> * <ul class='css'> @@ -46,17 +45,17 @@ * Creates a Label widget that wraps an existing <div> or <span> * element. * - * This element must already be attached to the document. + * 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 */ - public static Label wrap(com.google.gwt.dom.client.Element element) { - // Assert that the element is of the correct type and is attached. - assert element.getTagName().equalsIgnoreCase("div") - || element.getTagName().equalsIgnoreCase("span"); + public static Label wrap(Element element) { + // Assert that the element is attached. assert Document.get().getBody().isOrHasChild(element); - Label label = new Label((Element) element); + Label label = new Label(element); // Mark it attached and remember it for cleanup. label.onAttach(); @@ -74,7 +73,7 @@ * Creates an empty label. */ public Label() { - setElement(DOM.createDiv()); + setElement(Document.get().createDivElement()); setStyleName("gwt-Label"); } @@ -100,12 +99,15 @@ } /** - * This constructor is used to let the HTML constructors avoid work. + * This constructor may be used by subclasses to explicitly use an existing + * element. This element must be either a <div> or <span> element. * - * @param element element + * @param element the element to be used */ - Label(Element element) { + protected Label(Element element) { setElement(element); + assert element.getTagName().equalsIgnoreCase("div") + || element.getTagName().equalsIgnoreCase("span"); } public void addClickListener(ClickListener listener) { @@ -135,22 +137,22 @@ public Direction getDirection() { return BidiUtils.getDirectionOnElement(getElement()); } - + public HorizontalAlignmentConstant getHorizontalAlignment() { return horzAlign; } public String getText() { - return DOM.getInnerText(getElement()); + return getElement().getInnerText(); } public boolean getWordWrap() { - return !DOM.getStyleAttribute(getElement(), "whiteSpace").equals("nowrap"); + return getElement().getStyle().getProperty("whiteSpace").equals("nowrap"); } @Override public void onBrowserEvent(Event event) { - switch (DOM.eventGetType(event)) { + switch (event.getTypeInt()) { case Event.ONCLICK: if (clickListeners != null) { clickListeners.fireClick(this); @@ -192,22 +194,22 @@ mouseWheelListeners.remove(listener); } } - + public void setDirection(Direction direction) { BidiUtils.setDirectionOnElement(getElement(), direction); } - + public void setHorizontalAlignment(HorizontalAlignmentConstant align) { horzAlign = align; - DOM.setStyleAttribute(getElement(), "textAlign", align.getTextAlignString()); + getElement().getStyle().setProperty("textAlign", align.getTextAlignString()); } public void setText(String text) { - DOM.setInnerText(getElement(), text); + getElement().setInnerText(text); } public void setWordWrap(boolean wrap) { - DOM.setStyleAttribute(getElement(), "whiteSpace", wrap ? "normal" - : "nowrap"); + getElement().getStyle().setProperty("whiteSpace", + wrap ? "normal" : "nowrap"); } }
diff --git a/user/src/com/google/gwt/user/client/ui/ListBox.java b/user/src/com/google/gwt/user/client/ui/ListBox.java index a403499..ed82a9b 100644 --- a/user/src/com/google/gwt/user/client/ui/ListBox.java +++ b/user/src/com/google/gwt/user/client/ui/ListBox.java
@@ -15,11 +15,10 @@ */ package com.google.gwt.user.client.ui; -import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Document; +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.OptionElement; import com.google.gwt.dom.client.SelectElement; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; /** @@ -43,127 +42,22 @@ public class ListBox extends FocusWidget implements SourcesChangeEvents, HasName { - /** - * ListBox implementation for all browsers except Safari. This implementation - * relies on the JavaScript Select object and its 'options' array. - */ - private static class Impl { - - public native void clear(Element select) /*-{ - select.options.length = 0; - }-*/; - - public native int getItemCount(Element select) /*-{ - return select.options.length; - }-*/; - - public native String getItemText(Element select, int index) /*-{ - return select.options[index].text; - }-*/; - - public native String getItemValue(Element select, int index) /*-{ - return select.options[index].value; - }-*/; - - public native boolean isItemSelected(Element select, int index) /*-{ - return select.options[index].selected; - }-*/; - - public native void removeItem(Element select, int index) /*-{ - select.options[index] = null; - }-*/; - - public native void setItemSelected(Element select, int index, - boolean selected) /*-{ - select.options[index].selected = selected; - }-*/; - - public native void setValue(Element select, int index, String value) /*-{ - select.options[index].value = value; - }-*/; - - protected native Element getItemElement(Element select, int index) /*-{ - return select.options[index]; - }-*/; - } - - /** - * ListBox implementation for Safari. The 'options' array cannot be used - * due to a bug in the version of WebKit that ships with GWT - * (http://bugs.webkit.org/show_bug.cgi?id=10472). - * The 'children' array, which is common for all DOM elements in Safari, - * does not suffer from the same problem. Ideally, the 'children' - * array should be used in all of the traversal methods in the DOM classes. - * Unfortunately, due to a bug in Safari 2 - * (http://bugs.webkit.org/show_bug.cgi?id=3330), this will not work. - * However, this bug does not cause problems in the case of <SELECT> - * elements, because their descendent elements are only one level deep. - */ - private static class ImplSafari extends Impl { - - @Override - public native void clear(Element select) /*-{ - select.innerText = ''; - }-*/; - - @Override - public native int getItemCount(Element select) /*-{ - return select.children.length; - }-*/; - - @Override - public native String getItemText(Element select, int index) /*-{ - return select.children[index].text; - }-*/; - - @Override - public native String getItemValue(Element select, int index) /*-{ - return select.children[index].value; - }-*/; - - @Override - public native boolean isItemSelected(Element select, int index) /*-{ - return select.children[index].selected; - }-*/; - - @Override - public native void removeItem(Element select, int index) /*-{ - select.removeChild(select.children[index]); - }-*/; - - @Override - public native void setItemSelected(Element select, int index, - boolean selected) /*-{ - select.children[index].selected = selected; - }-*/; - - @Override - public native void setValue(Element select, int index, String value) /*-{ - select.children[index].value = value; - }-*/; - - @Override - protected native Element getItemElement(Element select, int index) /*-{ - return select.children[index]; - }-*/; - } - private static final int INSERT_AT_END = -1; - private static final Impl impl = GWT.create(Impl.class); /** * Creates a ListBox widget that wraps an existing <select> element. * - * This element must already be attached to the document. + * 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 */ - public static ListBox wrap(com.google.gwt.dom.client.Element element) { - // Assert that the element is of the correct type and is attached. - SelectElement.as(element); + public static ListBox wrap(Element element) { + // Assert that the element is attached. assert Document.get().getBody().isOrHasChild(element); - ListBox listBox = new ListBox((Element) element); + ListBox listBox = new ListBox(element); // Mark it attached and remember it for cleanup. listBox.onAttach(); @@ -188,12 +82,19 @@ * @param isMultipleSelect specifies if multiple selection is enabled */ public ListBox(boolean isMultipleSelect) { - super(DOM.createSelect(isMultipleSelect)); + super(Document.get().createSelectElement(isMultipleSelect)); setStyleName("gwt-ListBox"); } - private ListBox(Element element) { + /** + * This constructor may be used by subclasses to explicitly use an existing + * element. This element must be a <select> element. + * + * @param element the element to be used + */ + protected ListBox(Element element) { super(element); + SelectElement.as(element); } public void addChangeListener(ChangeListener listener) { @@ -232,7 +133,7 @@ * Removes all items from the list box. */ public void clear() { - impl.clear(getElement()); + getSelectElement().clear(); } /** @@ -241,7 +142,7 @@ * @return the number of items */ public int getItemCount() { - return impl.getItemCount(getElement()); + return getSelectElement().getOptions().getLength(); } /** @@ -253,11 +154,11 @@ */ public String getItemText(int index) { checkIndex(index); - return impl.getItemText(getElement(), index); + return getSelectElement().getOptions().getItem(index).getText(); } public String getName() { - return DOM.getElementProperty(getElement(), "name"); + return getSelectElement().getName(); } /** @@ -268,7 +169,7 @@ * @return the selected index, or <code>-1</code> if none is selected */ public int getSelectedIndex() { - return DOM.getElementPropertyInt(getElement(), "selectedIndex"); + return getSelectElement().getSelectedIndex(); } /** @@ -280,7 +181,7 @@ */ public String getValue(int index) { checkIndex(index); - return impl.getItemValue(getElement(), index); + return getSelectElement().getOptions().getItem(index).getValue(); } /** @@ -290,7 +191,7 @@ * @return the visible item count */ public int getVisibleItemCount() { - return DOM.getElementPropertyInt(getElement(), "size"); + return getSelectElement().getSize(); } /** @@ -319,7 +220,17 @@ * @param index the index at which to insert it */ public void insertItem(String item, String value, int index) { - DOM.insertListItem(getElement(), item, value, index); + SelectElement select = getSelectElement(); + OptionElement option = Document.get().createOptionElement(); + option.setText(item); + option.setValue(value); + + if ((index == -1) || (index == select.getLength())) { + select.add(option, null); + } else { + OptionElement before = select.getOptions().getItem(index); + select.add(option, before); + } } /** @@ -331,7 +242,7 @@ */ public boolean isItemSelected(int index) { checkIndex(index); - return impl.isItemSelected(getElement(), index); + return getSelectElement().getOptions().getItem(index).isSelected(); } /** @@ -340,12 +251,12 @@ * @return <code>true</code> if multiple selection is allowed */ public boolean isMultipleSelect() { - return DOM.getElementPropertyBoolean(getElement(), "multiple"); + return getSelectElement().isMultiple(); } @Override public void onBrowserEvent(Event event) { - if (DOM.eventGetType(event) == Event.ONCHANGE) { + if (event.getTypeInt() == Event.ONCHANGE) { if (changeListeners != null) { changeListeners.fireChange(this); } @@ -368,7 +279,7 @@ */ public void removeItem(int index) { checkIndex(index); - impl.removeItem(getElement(), index); + getSelectElement().remove(index); } /** @@ -385,7 +296,7 @@ */ public void setItemSelected(int index, boolean selected) { checkIndex(index); - impl.setItemSelected(getElement(), index, selected); + getSelectElement().getOptions().getItem(index).setSelected(selected); } /** @@ -400,7 +311,7 @@ if (text == null) { throw new NullPointerException("Cannot set an option to have null text"); } - DOM.setOptionText(getElement(), text, index); + getSelectElement().getOptions().getItem(index).setText(text); } /** @@ -413,11 +324,11 @@ */ public void setMultipleSelect(boolean multiple) { // TODO: we can remove the above doc admonition once we address issue 1007 - DOM.setElementPropertyBoolean(getElement(), "multiple", multiple); + getSelectElement().setMultiple(multiple); } public void setName(String name) { - DOM.setElementProperty(getElement(), "name", name); + getSelectElement().setName(name); } /** @@ -435,7 +346,7 @@ * @param index the index of the item to be selected */ public void setSelectedIndex(int index) { - DOM.setElementPropertyInt(getElement(), "selectedIndex", index); + getSelectElement().setSelectedIndex(index); } /** @@ -449,7 +360,7 @@ */ public void setValue(int index, String value) { checkIndex(index); - impl.setValue(getElement(), index, value); + getSelectElement().getOptions().getItem(index).setValue(value); } /** @@ -459,7 +370,7 @@ * @param visibleItems the visible item count */ public void setVisibleItemCount(int visibleItems) { - DOM.setElementPropertyInt(getElement(), "size", visibleItems); + getSelectElement().setSize(visibleItems); } /** @@ -475,10 +386,9 @@ super.onEnsureDebugId(baseID); // Set the id of each option - Element selectElem = getElement(); int numItems = getItemCount(); for (int i = 0; i < numItems; i++) { - ensureDebugId(impl.getItemElement(selectElem, i), baseID, "item" + i); + ensureDebugId(getSelectElement().getOptions().getItem(i), baseID, "item" + i); } } @@ -487,4 +397,8 @@ throw new IndexOutOfBoundsException(); } } + + private SelectElement getSelectElement() { + return getElement().cast(); + } }
diff --git a/user/src/com/google/gwt/user/client/ui/PasswordTextBox.java b/user/src/com/google/gwt/user/client/ui/PasswordTextBox.java index de63755..f2a80b1 100644 --- a/user/src/com/google/gwt/user/client/ui/PasswordTextBox.java +++ b/user/src/com/google/gwt/user/client/ui/PasswordTextBox.java
@@ -16,9 +16,8 @@ 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.dom.client.InputElement; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; /** * A text box that visually masks its input to prevent eavesdropping. @@ -30,11 +29,13 @@ * <h3>CSS Style Rules</h3> * <ul class='css'> * <li>.gwt-PasswordTextBox { primary style }</li> - * <li>.gwt-PasswordTextBox-readonly { dependent style set when the password text box is read-only }</li> + * <li>.gwt-PasswordTextBox-readonly { dependent style set when the password + * text box is read-only }</li> * </ul> * * <p> - * <h3>Example</h3> {@example com.google.gwt.examples.TextBoxExample} + * <h3>Example</h3> + * {@example com.google.gwt.examples.TextBoxExample} * </p> */ public class PasswordTextBox extends TextBox { @@ -43,16 +44,17 @@ * Creates a PasswordTextBox widget that wraps an existing <input * type='password'> element. * - * This element must already be attached to the document. + * 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 */ - public static PasswordTextBox wrap(com.google.gwt.dom.client.Element element) { - // Assert that the element is of the correct type and is attached. - assert InputElement.as(element).getType().equalsIgnoreCase("password"); + public static PasswordTextBox wrap(Element element) { + // Assert that the element is attached. assert Document.get().getBody().isOrHasChild(element); - PasswordTextBox textBox = new PasswordTextBox((Element) element); + PasswordTextBox textBox = new PasswordTextBox(element); // Mark it attached and remember it for cleanup. textBox.onAttach(); @@ -65,11 +67,18 @@ * Creates an empty password text box. */ public PasswordTextBox() { - super(DOM.createInputPassword()); - setStyleName("gwt-PasswordTextBox"); + super(Document.get().createPasswordInputElement(), "gwt-PasswordTextBox"); } - private PasswordTextBox(Element element) { - super(element); + /** + * This constructor may be used by subclasses to explicitly use an existing + * element. This element must be an <input> element whose type is + * 'password'. + * + * @param element the element to be used + */ + protected PasswordTextBox(Element element) { + super(element, null); + assert InputElement.as(element).getType().equalsIgnoreCase("password"); } }
diff --git a/user/src/com/google/gwt/user/client/ui/PopupPanel.java b/user/src/com/google/gwt/user/client/ui/PopupPanel.java index cc64a7a..b928838 100644 --- a/user/src/com/google/gwt/user/client/ui/PopupPanel.java +++ b/user/src/com/google/gwt/user/client/ui/PopupPanel.java
@@ -71,7 +71,7 @@ /** * An {@link Animation} used to enlarge the popup into view. */ - private static class ResizeAnimation extends Animation { + static class ResizeAnimation extends Animation { /** * The offset height and width of the current {@link PopupPanel}. */ @@ -695,7 +695,18 @@ } } } - + + /** + * Sets the animation used to animate this popup. Used by gwt-incubator to + * allow DropDownPanel to override the default popup animation. Not protected + * because the exact API may change in gwt 1.6. + * + * @param animation the animation to use for this popup + */ + void setAnimation(ResizeAnimation animation) { + resizeAnimation = animation; + } + /** * Enable or disable animation of the {@link PopupPanel}. *
diff --git a/user/src/com/google/gwt/user/client/ui/RootPanel.java b/user/src/com/google/gwt/user/client/ui/RootPanel.java index ea02702..62af479 100644 --- a/user/src/com/google/gwt/user/client/ui/RootPanel.java +++ b/user/src/com/google/gwt/user/client/ui/RootPanel.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -15,17 +15,18 @@ */ package com.google.gwt.user.client.ui; +import com.google.gwt.i18n.client.BidiUtils; +import com.google.gwt.i18n.client.HasDirection; +import com.google.gwt.i18n.client.LocaleInfo; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.WindowCloseListener; -import com.google.gwt.i18n.client.LocaleInfo; -import com.google.gwt.i18n.client.BidiUtils; -import com.google.gwt.i18n.client.HasDirection; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; /** * The panel to which all other widgets must ultimately be added. RootPanels are @@ -38,24 +39,59 @@ */ public class RootPanel extends AbsolutePanel { - private static HashMap<String, RootPanel> rootPanels = new HashMap<String, RootPanel>(); - private static List<Widget> widgetsToDetach = new ArrayList<Widget>(); + private static Map<String, RootPanel> rootPanels = new HashMap<String, RootPanel>(); + private static Set<Widget> widgetsToDetach = new HashSet<Widget>(); /** - * Adds a widget to the list of widgets to be detached when the page unloads. + * Adds a widget to the detach list. This is the list of widgets to be + * detached when the page unloads. * * This method must be called for all widgets that have no parent widgets. * These are most commonly {@link RootPanel RootPanels}, but can also be any - * widget used to wrap an existing element on the page. Failing to do this - * may cause these widgets to leak memory. + * widget used to wrap an existing element on the page. Failing to do this may + * cause these widgets to leak memory. This method is called automatically by + * widgets' wrap methods (e.g. + * {@link Button#wrap(com.google.gwt.dom.client.Element)}). * * @param widget the widget to be cleaned up when the page closes + * @see #detachNow(Widget) */ public static void detachOnWindowClose(Widget widget) { + assert !widgetsToDetach.contains(widget) : "detachOnUnload() called twice " + + "for the same widget"; + widgetsToDetach.add(widget); } /** + * Marks a widget as detached and removes it from the detach list. + * + * If an element belonging to a widget originally passed to + * {@link #detachOnWindowClose(Widget)} has been removed from the document, calling + * this method will cause it to be marked as detached immediately. Failure to + * do so will keep the widget from being garbage collected until the page is + * unloaded. + * + * This method may only be called per widget, and only for widgets that were + * originally passed to {@link #detachOnWindowClose(Widget)}. Any widget in the + * detach list, whose element is no longer in the document when the page + * unloads, will cause an assertion error. + * + * @param widget the widget that no longer needs to be cleaned up when the + * page closes + * @see #detachOnWindowClose(Widget) + */ + public static void detachNow(Widget widget) { + assert !getBodyElement().isOrHasChild(widget.getElement()) : "detachNow() " + + "called on a widget whose element is still attached to the document"; + assert widgetsToDetach.contains(widget) : "detachNow() called on a widget " + + "not currently in the detach list"; + + widget.onDetach(); + widgetsToDetach.remove(widget); + } + + /** * Gets the default root panel. This panel wraps body of the browser's * document. This root panel can contain any number of widgets, which will be * laid out in their natural HTML ordering. Many applications, however, will @@ -90,18 +126,18 @@ } } - // Note that the code in this if block only happens once - - // on the first RootPanel.get(String) or RootPanel.get() + // Note that the code in this if block only happens once - + // on the first RootPanel.get(String) or RootPanel.get() // call. - if (rootPanels.size() == 0) { hookWindowClosing(); - + // If we're in a RTL locale, set the RTL directionality // on the entire document. if (LocaleInfo.getCurrentLocale().isRTL()) { - BidiUtils.setDirectionOnElement(getRootElement(), HasDirection.Direction.RTL); - } + BidiUtils.setDirectionOnElement(getRootElement(), + HasDirection.Direction.RTL); + } } // Create the panel and put it in the map. @@ -110,7 +146,7 @@ elem = getBodyElement(); } rootPanels.put(id, rp = new RootPanel(elem)); - widgetsToDetach.add(rp); + detachOnWindowClose(rp); return rp; } @@ -123,10 +159,30 @@ return $doc.body; }-*/; + // Package-protected for use by unit tests. Do not call this method directly. + static void detachWidgets() { + // When the window is closing, detach all widgets that need to be + // cleaned up. This will cause all of their event listeners + // to be unhooked, which will avoid potential memory leaks. + for (Widget widget : widgetsToDetach) { + if (widget.isAttached()) { + widget.onDetach(); + } + + // Assert that each widget's element is actually attached to the + // document. If not, then it was probably wrapped and removed, but not + // properly detached. + assert getBodyElement().isOrHasChild(widget.getElement()) : "A " + + "widget in the detach list was found not attached to the " + + "document. The is likely caused by wrapping an existing " + + "element and removing it from the document without calling " + + "RootPanel.detachNow()."; + } + } + /** - * Convenience method for getting the document's root - * (<html>) element. - * + * Convenience method for getting the document's root (<html>) element. + * * @return the document's root element */ private static native Element getRootElement() /*-{ @@ -137,15 +193,7 @@ // Catch the window closing event. Window.addWindowCloseListener(new WindowCloseListener() { public void onWindowClosed() { - // When the window is closing, detach all widgets that need to be - // cleaned up. This will cause all of their event listeners - // to be unhooked, which will avoid potential memory leaks. - for (int i = 0; i < widgetsToDetach.size(); ++i) { - Widget widget = widgetsToDetach.get(i); - if (widget.isAttached()) { - widget.onDetach(); - } - } + detachWidgets(); } public String onWindowClosing() {
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 d241e1115..602ffe5 100644 --- a/user/src/com/google/gwt/user/client/ui/SimpleCheckBox.java +++ b/user/src/com/google/gwt/user/client/ui/SimpleCheckBox.java
@@ -34,13 +34,14 @@ * Creates a SimpleCheckBox widget that wraps an existing <input * type='checkbox'> element. * - * This element must already be attached to the document. + * 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 */ public static SimpleCheckBox wrap(Element element) { - // Assert that the element is of the correct type and is attached. - assert InputElement.as(element).getType().equalsIgnoreCase("checkbox"); + // Assert that the element is attached. assert Document.get().getBody().isOrHasChild(element); SimpleCheckBox checkBox = new SimpleCheckBox(element); @@ -56,14 +57,28 @@ * Creates a new simple checkbox. */ public SimpleCheckBox() { - setElement(Document.get().createCheckInputElement()); - setStyleName("gwt-SimpleCheckBox"); + this(Document.get().createCheckInputElement(), "gwt-SimpleCheckBox"); } - SimpleCheckBox(Element element) { + /** + * This constructor may be used by subclasses to explicitly use an existing + * element. This element must be an <input> element whose type is either + * 'checkbox'. + * + * @param element the element to be used + */ + protected SimpleCheckBox(Element element) { + assert InputElement.as(element).getType().equalsIgnoreCase("checkbox"); setElement(element); } + SimpleCheckBox(Element element, String styleName) { + setElement(element); + if (styleName != null) { + setStyleName(styleName); + } + } + public String getName() { return getInputElement().getName(); }
diff --git a/user/src/com/google/gwt/user/client/ui/SimpleRadioButton.java b/user/src/com/google/gwt/user/client/ui/SimpleRadioButton.java index aac907b..5d415fe 100644 --- a/user/src/com/google/gwt/user/client/ui/SimpleRadioButton.java +++ b/user/src/com/google/gwt/user/client/ui/SimpleRadioButton.java
@@ -34,13 +34,14 @@ * Creates a SimpleRadioButton widget that wraps an existing <input * type='radio'> element. * - * This element must already be attached to the document. + * 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 */ public static SimpleRadioButton wrap(Element element) { - // Assert that the element is of the correct type and is attached. - assert InputElement.as(element).getType().equalsIgnoreCase("radio"); + // Assert that the element is attached. assert Document.get().getBody().isOrHasChild(element); SimpleRadioButton radioButton = new SimpleRadioButton(element); @@ -63,11 +64,18 @@ * @param name the group name with which to associate the radio button */ public SimpleRadioButton(String name) { - this(Document.get().createRadioInputElement(name)); - setStyleName("gwt-SimpleRadioButton"); + super(Document.get().createRadioInputElement(name), "gwt-SimpleRadioButton"); } - private SimpleRadioButton(Element element) { - super(element); + /** + * This constructor may be used by subclasses to explicitly use an existing + * element. This element must be an <input> element whose type is + * 'radio'. + * + * @param element the element to be used + */ + protected SimpleRadioButton(Element element) { + super(element, null); + assert InputElement.as(element).getType().equalsIgnoreCase("radio"); } }
diff --git a/user/src/com/google/gwt/user/client/ui/TextBox.java b/user/src/com/google/gwt/user/client/ui/TextBox.java index a3ff602..ea77484 100644 --- a/user/src/com/google/gwt/user/client/ui/TextBox.java +++ b/user/src/com/google/gwt/user/client/ui/TextBox.java
@@ -16,11 +16,10 @@ 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.dom.client.InputElement; import com.google.gwt.i18n.client.BidiUtils; import com.google.gwt.i18n.client.HasDirection; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; /** * A standard single-line text box. @@ -32,7 +31,8 @@ * <h3>CSS Style Rules</h3> * <ul class='css'> * <li>.gwt-TextBox { primary style }</li> - * <li>.gwt-TextBox-readonly { dependent style set when the text box is read-only }</li> + * <li>.gwt-TextBox-readonly { dependent style set when the text box is + * read-only }</li> * </ul> * * <p> @@ -46,16 +46,17 @@ * Creates a TextBox widget that wraps an existing <input type='text'> * element. * - * This element must already be attached to the document. + * 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 */ - public static TextBox wrap(com.google.gwt.dom.client.Element element) { - // Assert that the element is of the correct type and is attached. - assert InputElement.as(element).getType().equalsIgnoreCase("text"); + public static TextBox wrap(Element element) { + // Assert that the element is attached. assert Document.get().getBody().isOrHasChild(element); - TextBox textBox = new TextBox((Element) element); + TextBox textBox = new TextBox(element); // Mark it attached and remember it for cleanup. textBox.onAttach(); @@ -68,29 +69,39 @@ * Creates an empty text box. */ public TextBox() { - super(DOM.createInputText()); - setStyleName("gwt-TextBox"); + this(Document.get().createTextInputElement(), "gwt-TextBox"); } /** - * Protected constructor for use by subclasses. - * @param element element + * This constructor may be used by subclasses to explicitly use an existing + * element. This element must be an <input> element whose type is + * 'text'. + * + * @param element the element to be used */ - TextBox(Element element) { - super(element); + protected TextBox(Element element) { + super(element); + assert InputElement.as(element).getType().equalsIgnoreCase("text"); + } + + TextBox(Element element, String styleName) { + super(element); + if (styleName != null) { + setStyleName(styleName); + } } public Direction getDirection() { - return BidiUtils.getDirectionOnElement(getElement()); + return BidiUtils.getDirectionOnElement(getElement()); } - + /** * Gets the maximum allowable length of the text box. * * @return the maximum length, in characters */ public int getMaxLength() { - return DOM.getElementPropertyInt(getElement(), "maxLength"); + return getInputElement().getMaxLength(); } /** @@ -99,20 +110,20 @@ * @return the number of visible characters */ public int getVisibleLength() { - return DOM.getElementPropertyInt(getElement(), "size"); + return getInputElement().getSize(); } public void setDirection(Direction direction) { BidiUtils.setDirectionOnElement(getElement(), direction); } - + /** * Sets the maximum allowable length of the text box. * * @param length the maximum length, in characters */ public void setMaxLength(int length) { - DOM.setElementPropertyInt(getElement(), "maxLength", length); + getInputElement().setMaxLength(length); } /** @@ -121,6 +132,10 @@ * @param length the number of visible characters */ public void setVisibleLength(int length) { - DOM.setElementPropertyInt(getElement(), "size", length); + getInputElement().setSize(length); + } + + private InputElement getInputElement() { + return getElement().cast(); } }
diff --git a/user/src/com/google/gwt/user/client/ui/TextBoxBase.java b/user/src/com/google/gwt/user/client/ui/TextBoxBase.java index 665e8f9..5011cac 100644 --- a/user/src/com/google/gwt/user/client/ui/TextBoxBase.java +++ b/user/src/com/google/gwt/user/client/ui/TextBoxBase.java
@@ -16,8 +16,8 @@ package com.google.gwt.user.client.ui; import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.Element; import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.impl.TextBoxImpl;
diff --git a/user/src/com/google/gwt/user/client/ui/impl/FormPanelImpl.java b/user/src/com/google/gwt/user/client/ui/impl/FormPanelImpl.java index 959c66e..27ecc4d 100644 --- a/user/src/com/google/gwt/user/client/ui/impl/FormPanelImpl.java +++ b/user/src/com/google/gwt/user/client/ui/impl/FormPanelImpl.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -15,7 +15,7 @@ */ package com.google.gwt.user.client.ui.impl; -import com.google.gwt.user.client.Element; +import com.google.gwt.dom.client.Element; /** * Implementation class used by {@link com.google.gwt.user.client.ui.FormPanel}.
diff --git a/user/src/com/google/gwt/user/client/ui/impl/FormPanelImplIE6.java b/user/src/com/google/gwt/user/client/ui/impl/FormPanelImplIE6.java index ecb0fb9..bb52b3e 100644 --- a/user/src/com/google/gwt/user/client/ui/impl/FormPanelImplIE6.java +++ b/user/src/com/google/gwt/user/client/ui/impl/FormPanelImplIE6.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -15,7 +15,7 @@ */ package com.google.gwt.user.client.ui.impl; -import com.google.gwt.user.client.Element; +import com.google.gwt.dom.client.Element; /** * IE6 implementation of {@link com.google.gwt.user.client.ui.impl.FormPanelImpl}.
diff --git a/user/src/com/google/gwt/user/rebind/AbstractMethodCreator.java b/user/src/com/google/gwt/user/rebind/AbstractMethodCreator.java index e116c6c..b81e7eb 100644 --- a/user/src/com/google/gwt/user/rebind/AbstractMethodCreator.java +++ b/user/src/com/google/gwt/user/rebind/AbstractMethodCreator.java
@@ -1,5 +1,5 @@ /* - * Copyright 2006 Google Inc. + * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -18,7 +18,7 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JMethod; -import com.google.gwt.i18n.rebind.AbstractResource; +import com.google.gwt.i18n.rebind.AbstractResource.ResourceList; /** * Creates method factories depending upon the method type. Includes the core @@ -47,11 +47,11 @@ * * @param logger TreeLogger for logging * @param targetMethod Method - * @param resource base resource to use for lookup + * @param resourceList base resource to use for lookup * @throws UnableToCompleteException */ public abstract void createMethodFor(TreeLogger logger, JMethod targetMethod, - String key, AbstractResource resource, String locale) throws UnableToCompleteException; + String key, ResourceList resourceList, String locale) throws UnableToCompleteException; /** * Prints to the current <code>AbstractGeneratorClassCreator</code>.
diff --git a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java index f7c24ee..f1a9e0b 100644 --- a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java +++ b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
@@ -32,6 +32,7 @@ import com.google.gwt.dev.util.log.PrintWriterTreeLogger; import com.google.gwt.user.client.rpc.IsSerializable; import com.google.gwt.user.rebind.rpc.TypeParameterExposureComputer.TypeParameterFlowInfo; +import com.google.gwt.user.rebind.rpc.TypePaths.TypePath; import java.io.OutputStream; import java.io.PrintWriter; @@ -82,12 +83,6 @@ */ public class SerializableTypeOracleBuilder { - interface Path { - Path getParent(); - - String toString(); - } - private class TypeInfoComputed { /** @@ -127,7 +122,7 @@ /** * Path used to discover this type. */ - private final Path path; + private final TypePath path; /** * The state that this type is currently in. @@ -139,7 +134,7 @@ */ private final JClassType type; - public TypeInfoComputed(JClassType type, Path path) { + public TypeInfoComputed(JClassType type, TypePath path) { this.type = type; this.path = path; autoSerializable = SerializableTypeOracleBuilder.isAutoSerializable(type); @@ -151,7 +146,7 @@ return manualSerializer; } - public Path getPath() { + public TypePath getPath() { return path; } @@ -471,100 +466,6 @@ return true; } - private static Path createArrayComponentPath(final JArrayType arrayType, - final Path parent) { - return new Path() { - public Path getParent() { - return parent; - } - - @Override - public String toString() { - return "Type '" - + arrayType.getComponentType().getParameterizedQualifiedSourceName() - + "' is reachable from array type '" - + arrayType.getParameterizedQualifiedSourceName() + "'"; - } - }; - } - - private static Path createFieldPath(final Path parent, final JField field) { - return new Path() { - public Path getParent() { - return parent; - } - - @Override - public String toString() { - JType type = field.getType(); - JClassType enclosingType = field.getEnclosingType(); - return "'" + type.getParameterizedQualifiedSourceName() - + "' is reachable from field '" + field.getName() + "' of type '" - + enclosingType.getParameterizedQualifiedSourceName() + "'"; - } - }; - } - - private static Path createRootPath(final JType type) { - return new Path() { - public Path getParent() { - return null; - } - - @Override - public String toString() { - return "Started from '" + type.getParameterizedQualifiedSourceName() - + "'"; - } - }; - } - - private static Path createSubtypePath(final Path parent, final JType type, - final JClassType supertype) { - return new Path() { - public Path getParent() { - return parent; - } - - @Override - public String toString() { - return "'" + type.getParameterizedQualifiedSourceName() - + "' is reachable as a subtype of type '" + supertype + "'"; - } - }; - } - - private static Path createSupertypePath(final Path parent, final JType type, - final JClassType subtype) { - return new Path() { - public Path getParent() { - return parent; - } - - @Override - public String toString() { - return "'" + type.getParameterizedQualifiedSourceName() - + "' is reachable as a supertype of type '" + subtype + "'"; - } - }; - } - - private static Path createTypeArgumentPath(final Path parent, - final JClassType type, final int typeArgIndex, final JClassType typeArg) { - return new Path() { - public Path getParent() { - return parent; - } - - @Override - public String toString() { - return "'" + typeArg.getParameterizedQualifiedSourceName() - + "' is reachable from type argument " + typeArgIndex - + " of type '" + type.getParameterizedQualifiedSourceName() + "'"; - } - }; - } - private static boolean directlyImplementsMarkerInterface(JClassType type) { try { return TypeHierarchyUtils.directlyImplementsInterface(type, @@ -754,7 +655,7 @@ boolean allSucceeded = true; for (Entry<JClassType, TreeLogger> entry : rootTypes.entrySet()) { allSucceeded &= checkTypeInstantiable(entry.getValue(), entry.getKey(), - false, createRootPath(entry.getKey())); + false, TypePaths.createRootPath(entry.getKey())); } if (!allSucceeded) { @@ -827,18 +728,18 @@ * The method is exposed using default access to enable testing. */ final boolean checkTypeInstantiable(TreeLogger logger, JType type, - boolean isSpeculative, Path path) { + boolean isSpeculative, TypePath path) { return checkTypeInstantiable(logger, type, isSpeculative, path, new HashSet<JClassType>()); } /** * Same as - * {@link #checkTypeInstantiable(TreeLogger, JType, boolean, com.google.gwt.user.rebind.rpc.SerializableTypeOracleBuilder.Path)}, + * {@link #checkTypeInstantiable(TreeLogger, JType, boolean, com.google.gwt.user.rebind.rpc.SerializableTypeOracleBuilder.TypePath)}, * except that returns the set of instantiable subtypes. */ boolean checkTypeInstantiable(TreeLogger logger, JType type, - boolean isSpeculative, Path path, Set<JClassType> instSubtypes) { + boolean isSpeculative, TypePath path, Set<JClassType> instSubtypes) { assert (type != null); if (type.isPrimitive() != null) { return true; @@ -856,8 +757,7 @@ if (typeParametersInRootTypes.contains(isTypeParameter)) { return checkTypeInstantiable(localLogger, isTypeParameter.getFirstBound(), isSpeculative, - createTypeArgumentPath(path, isTypeParameter.getDeclaringClass(), - isTypeParameter.getOrdinal(), isTypeParameter.getFirstBound()), + TypePaths.createTypeParameterInRootPath(path, isTypeParameter), instSubtypes); } @@ -948,7 +848,7 @@ * * @param logger */ - private void checkAllSubtypesOfObject(TreeLogger logger, Path parent) { + private void checkAllSubtypesOfObject(TreeLogger logger, TypePath parent) { if (alreadyCheckedObject) { return; } @@ -965,14 +865,15 @@ JClassType[] allTypes = typeOracle.getJavaLangObject().getSubtypes(); for (JClassType cls : allTypes) { if (isDeclaredSerializable(cls)) { - checkTypeInstantiable(localLogger, cls, true, createSubtypePath(parent, - cls, typeOracle.getJavaLangObject())); + checkTypeInstantiable(localLogger, cls, true, + TypePaths.createSubtypePath(parent, cls, + typeOracle.getJavaLangObject())); } } } private boolean checkArrayInstantiable(TreeLogger logger, JArrayType array, - boolean isSpeculative, Path path) { + boolean isSpeculative, TypePath path) { JType leafType = array.getLeafType(); JWildcardType leafWild = leafType.isWildcard(); @@ -1007,7 +908,8 @@ Set<JClassType> instantiableTypes = new HashSet<JClassType>(); boolean succeeded = checkTypeInstantiable(branch, array.getComponentType(), - isSpeculative, createArrayComponentPath(array, path), instantiableTypes); + isSpeculative, TypePaths.createArrayComponentPath(array, path), + instantiableTypes); if (succeeded && leafClass != null) { TreeLogger covariantArrayLogger = logger.branch(TreeLogger.DEBUG, "Covariant array types"); @@ -1047,7 +949,7 @@ * necessary types. */ private boolean checkDeclaredFields(TreeLogger logger, - TypeInfoComputed typeInfo, boolean isSpeculative, Path parent) { + TypeInfoComputed typeInfo, boolean isSpeculative, TypePath parent) { JClassType classOrInterface = typeInfo.getType(); if (classOrInterface.isEnum() != null) { @@ -1079,7 +981,7 @@ field.toString(), null); JType fieldType = field.getType(); - Path path = createFieldPath(parent, field); + TypePath path = TypePaths.createFieldPath(parent, field); if (typeInfo.isManuallySerializable() && fieldType.getLeafType() == typeOracle.getJavaLangObject()) { checkAllSubtypesOfObject(fieldLogger.branch(TreeLogger.WARN, @@ -1101,7 +1003,7 @@ } private boolean checkSubtype(TreeLogger logger, JClassType classOrInterface, - JClassType originalType, boolean isSpeculative, Path parent) { + JClassType originalType, boolean isSpeculative, TypePath parent) { if (classOrInterface.isEnum() != null) { // The fields of an enum are never serialized; they are always okay. return true; @@ -1145,7 +1047,7 @@ boolean superTypeOk = false; if (superType != null) { superTypeOk = checkSubtype(logger, superType, originalType, - isSpeculative, createSupertypePath(parent, superType, + isSpeculative, TypePaths.createSupertypePath(parent, superType, classOrInterface)); } @@ -1169,7 +1071,7 @@ * instantiable relative to a known base type. */ private boolean checkSubtypes(TreeLogger logger, JClassType originalType, - Set<JClassType> instSubtypes, Path path) { + Set<JClassType> instSubtypes, TypePath path) { JClassType baseType = getBaseType(originalType); TreeLogger computationLogger = logger.branch(TreeLogger.DEBUG, "Finding possibly instantiable subtypes"); @@ -1195,7 +1097,8 @@ continue; } - Path subtypePath = createSubtypePath(path, candidate, originalType); + TypePath subtypePath = TypePaths.createSubtypePath(path, candidate, + originalType); TypeInfoComputed tic = getTypeInfoComputed(candidate, subtypePath); if (tic.isDone()) { anySubtypes |= tic.isInstantiable(); @@ -1247,7 +1150,7 @@ * <code>typeArg</code>. */ private boolean checkTypeArgument(TreeLogger logger, JGenericType baseType, - int paramIndex, JClassType typeArg, boolean isSpeculative, Path parent) { + int paramIndex, JClassType typeArg, boolean isSpeculative, TypePath parent) { JWildcardType isWildcard = typeArg.isWildcard(); if (isWildcard != null) { return checkTypeArgument(logger, baseType, paramIndex, @@ -1279,7 +1182,8 @@ } } - Path path = createTypeArgumentPath(parent, baseType, paramIndex, typeArg); + TypePath path = TypePaths.createTypeArgumentPath(parent, baseType, paramIndex, + typeArg); int exposure = getTypeParameterExposure(baseType, paramIndex); switch (exposure) { case TypeParameterExposureComputer.EXPOSURE_DIRECT: { @@ -1370,7 +1274,7 @@ return possiblyInstantiableTypes; } - private TypeInfoComputed getTypeInfoComputed(JClassType type, Path path) { + private TypeInfoComputed getTypeInfoComputed(JClassType type, TypePath path) { TypeInfoComputed tic = typeToTypeInfoComputed.get(type); if (tic == null) { tic = new TypeInfoComputed(type, path); @@ -1400,7 +1304,7 @@ return false; } - private void logPath(TreeLogger logger, Path path) { + private void logPath(TreeLogger logger, TypePath path) { if (path == null) { return; }
diff --git a/user/src/com/google/gwt/user/rebind/rpc/TypePaths.java b/user/src/com/google/gwt/user/rebind/rpc/TypePaths.java new file mode 100644 index 0000000..ede05da --- /dev/null +++ b/user/src/com/google/gwt/user/rebind/rpc/TypePaths.java
@@ -0,0 +1,179 @@ +/* + * Copyright 2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.user.rebind.rpc; + +import com.google.gwt.core.ext.typeinfo.JArrayType; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.JField; +import com.google.gwt.core.ext.typeinfo.JGenericType; +import com.google.gwt.core.ext.typeinfo.JType; +import com.google.gwt.core.ext.typeinfo.JTypeParameter; + +/** + * Paths of types and utility methods for creating them. These are used by + * {@link SerializableTypeOracleBuilder} to record why it visits the types it + * does. + */ +class TypePaths { + /** + * A path of types. This interface does not currently expose the type itself, + * because these are currently only used for logging. + */ + interface TypePath { + /** + * Get the previous element on this type path, or <code>null</code> if + * this is a one-element path. + */ + TypePath getParent(); + + String toString(); + } + + static TypePaths.TypePath createArrayComponentPath( + final JArrayType arrayType, final TypePaths.TypePath parent) { + assert (arrayType != null); + + return new TypePaths.TypePath() { + public TypePaths.TypePath getParent() { + return parent; + } + + @Override + public String toString() { + return "Type '" + + arrayType.getComponentType().getParameterizedQualifiedSourceName() + + "' is reachable from array type '" + + arrayType.getParameterizedQualifiedSourceName() + "'"; + } + }; + } + + static TypePaths.TypePath createFieldPath(final TypePaths.TypePath parent, + final JField field) { + return new TypePaths.TypePath() { + public TypePaths.TypePath getParent() { + return parent; + } + + @Override + public String toString() { + JType type = field.getType(); + JClassType enclosingType = field.getEnclosingType(); + return "'" + type.getParameterizedQualifiedSourceName() + + "' is reachable from field '" + field.getName() + "' of type '" + + enclosingType.getParameterizedQualifiedSourceName() + "'"; + } + }; + } + + static TypePaths.TypePath createRootPath(final JType type) { + assert (type != null); + + return new TypePaths.TypePath() { + public TypePaths.TypePath getParent() { + return null; + } + + @Override + public String toString() { + return "Started from '" + type.getParameterizedQualifiedSourceName() + + "'"; + } + }; + } + + static TypePaths.TypePath createSubtypePath(final TypePaths.TypePath parent, + final JType type, final JClassType supertype) { + assert (type != null); + assert (supertype != null); + + return new TypePaths.TypePath() { + public TypePaths.TypePath getParent() { + return parent; + } + + @Override + public String toString() { + return "'" + type.getParameterizedQualifiedSourceName() + + "' is reachable as a subtype of type '" + supertype + "'"; + } + }; + } + + static TypePaths.TypePath createSupertypePath( + final TypePaths.TypePath parent, final JType type, + final JClassType subtype) { + assert (type != null); + assert (subtype != null); + + return new TypePaths.TypePath() { + public TypePaths.TypePath getParent() { + return parent; + } + + @Override + public String toString() { + return "'" + type.getParameterizedQualifiedSourceName() + + "' is reachable as a supertype of type '" + subtype + "'"; + } + }; + } + + static TypePaths.TypePath createTypeArgumentPath( + final TypePaths.TypePath parent, final JGenericType baseType, + final int typeArgIndex, final JClassType typeArg) { + assert (baseType != null); + assert (typeArg != null); + + return new TypePaths.TypePath() { + public TypePaths.TypePath getParent() { + return parent; + } + + @Override + public String toString() { + return "'" + typeArg.getParameterizedQualifiedSourceName() + + "' is reachable from type argument " + typeArgIndex + + " of type '" + baseType.getParameterizedQualifiedSourceName() + + "'"; + } + }; + } + + static TypePaths.TypePath createTypeParameterInRootPath( + final TypePaths.TypePath parent, final JTypeParameter typeParameter) { + assert (typeParameter != null); + + return new TypePaths.TypePath() { + public TypePaths.TypePath getParent() { + return parent; + } + + @Override + public String toString() { + String parameterString = typeParameter.getName(); + if (typeParameter.getDeclaringClass() != null) { + parameterString += " of class " + + typeParameter.getDeclaringClass().getQualifiedSourceName(); + } + return "'" + + typeParameter.getFirstBound().getParameterizedQualifiedSourceName() + + "' is reachable as an upper bound of type parameter " + + parameterString + ", which appears in a root type"; + } + }; + } +}
diff --git a/user/super/com/google/gwt/emul/java/util/Arrays.java b/user/super/com/google/gwt/emul/java/util/Arrays.java index 97a1ce1..a12e24e 100644 --- a/user/super/com/google/gwt/emul/java/util/Arrays.java +++ b/user/super/com/google/gwt/emul/java/util/Arrays.java
@@ -882,8 +882,8 @@ return 0; } int hashCode = 1; - for (int i = 0, n = a.length; i < n; ++i) { - hashCode = (31 * hashCode + a[i].hashCode()) | 0; + for (Object e : a) { + hashCode = (31 * hashCode + (e == null ? 0 : e.hashCode())) | 0; } return hashCode;
diff --git a/user/super/com/google/gwt/emul/java/util/TreeMap.java b/user/super/com/google/gwt/emul/java/util/TreeMap.java index abaf942..fa3cb12 100644 --- a/user/super/com/google/gwt/emul/java/util/TreeMap.java +++ b/user/super/com/google/gwt/emul/java/util/TreeMap.java
@@ -159,7 +159,7 @@ public int size() { return TreeMap.this.size(); } - }; + } /** * Tree node. @@ -506,13 +506,16 @@ // The number of nodes in the tree. private int size = 0; - @SuppressWarnings("unchecked") public TreeMap() { - this((Comparator<? super K>) DEFAULT_COMPARATOR); + this((Comparator<? super K>) null); } + @SuppressWarnings("unchecked") public TreeMap(Comparator<? super K> c) { root = null; + if (c == null) { + c = (Comparator<? super K>) DEFAULT_COMPARATOR; + } cmp = c; } @@ -523,10 +526,7 @@ @SuppressWarnings("unchecked") public TreeMap(SortedMap<K, ? extends V> map) { - cmp = map.comparator(); - if (cmp == null) { - cmp = (Comparator<? super K>) DEFAULT_COMPARATOR; - } + this(map.comparator()); putAll(map); // TODO(jat): more efficient init from sorted map }
diff --git a/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java b/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java index 27e904e..74a017b 100644 --- a/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java +++ b/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
@@ -143,6 +143,8 @@ private static int sideEffectChecker; + private static volatile int THREE = 3; + private static volatile boolean TRUE = true; private static volatile int volatileInt; @@ -557,6 +559,21 @@ } assertEquals(1, i); assertEquals(1, j); + + /* + * Issue 2069: a default with a break should not be stripped unless the + * break has no label. + */ + outer : while (true) { + switch (THREE) { + case 0: + case 1: + break; + default: + break outer; + } + fail("should not be reached"); + } } public void testLocalClasses() {
diff --git a/user/test/com/google/gwt/dev/jjs/test/NativeLongTest.java b/user/test/com/google/gwt/dev/jjs/test/NativeLongTest.java index a01dc69..ab2868e 100644 --- a/user/test/com/google/gwt/dev/jjs/test/NativeLongTest.java +++ b/user/test/com/google/gwt/dev/jjs/test/NativeLongTest.java
@@ -23,6 +23,20 @@ * LongLibTest. */ public class NativeLongTest extends GWTTestCase { + private static class RequestIdFactory { + static RequestIdFactory instance = new RequestIdFactory(); + + static RequestIdFactory getInstance() { + return instance; + } + + long nextId; + + long getNextId() { + return nextId++; + } + } + /* * These constants are done as volatile fields so that the compiler will not * constant fold them. The problem is that if you write assertEquals(2L, @@ -30,6 +44,7 @@ */ private static volatile byte BYTE_FOUR = (byte) 4; private static volatile char CHAR_FOUR = (char) 4; + private static volatile boolean FALSE = false; private static volatile int INT_FOUR = 4; private static volatile long LONG_100 = 100L; private static volatile long LONG_1234 = 0x1234123412341234L; @@ -177,4 +192,57 @@ public void testToString() { assertEquals("1234123412341234", "" + LONG_1234_DECIMAL); } + + /** + * It's important when allocating a new temporary that it is marked as in use. + */ + public void testVariableReuseInCompoundAssignmentNormalizer1() { + if (FALSE) { + // Prevent inlining, so that CAN allocates temporaries predictably. + testVariableReuseInCompoundAssignmentNormalizer1(); + } + + assertEquals("0", + Long.toHexString(RequestIdFactory.getInstance().getNextId())); + } + + /** + * Using differently typed temp variables can cause JArrayType to throw a + * class cast exception. + */ + public void testVariableReuseInCompoundAssignmentNormalizer2() { + if (FALSE) { + // Prevent inlining, so that CAN allocates temporaries predictably. + testVariableReuseInCompoundAssignmentNormalizer2(); + } + long ary[][] = new long[10][10]; + long i = 3, j = 3; + assertEquals(0L, ary[(int) i++][(int) j++]++); + assertEquals(4L, i); + assertEquals(4L, j); + assertEquals(1L, ary[3][3]); + } + + /** + * Using differently typed temp variables can cause LongEmulationNormalizer to + * fail an assertion. + */ + public void testVariableReuseInCompoundAssignmentNormalizer3() { + if (FALSE) { + // Prevent inlining, so that CAN allocates temporaries predictably. + testVariableReuseInCompoundAssignmentNormalizer3(); + } + + long ary[] = new long[10]; + int i = 3; + long j = 5; + + assertEquals(0L, ary[i++]++); + assertEquals(4, i); + assertEquals(1L, ary[3]); + + assertEquals(0L, ary[(int) j++]++); + assertEquals(6L, j); + assertEquals(1L, ary[5]); + } }
diff --git a/user/test/com/google/gwt/emultest/java/util/ArraysTest.java b/user/test/com/google/gwt/emultest/java/util/ArraysTest.java index 3592513..9a0f70f 100644 --- a/user/test/com/google/gwt/emultest/java/util/ArraysTest.java +++ b/user/test/com/google/gwt/emultest/java/util/ArraysTest.java
@@ -62,6 +62,16 @@ } /** + * Verifies that calling Arrays.hashCode(Object[]) with an array with + * embedded null references works properly (and most importantly doesn't + * throw an NPE). + */ + public void testArraysHashCodeWithNullElements() { + String[] a = new String[] { "foo", null, "bar", "baz" }; + Arrays.hashCode(a); + } + + /** * Tests {@link Arrays#asList(Object[])}. */ @SuppressWarnings("unchecked")
diff --git a/user/test/com/google/gwt/i18n/I18NSuite.java b/user/test/com/google/gwt/i18n/I18NSuite.java index 77155de..d8ebab6 100644 --- a/user/test/com/google/gwt/i18n/I18NSuite.java +++ b/user/test/com/google/gwt/i18n/I18NSuite.java
@@ -15,6 +15,7 @@ */ package com.google.gwt.i18n; +import com.google.gwt.i18n.client.AnnotationsTest; import com.google.gwt.i18n.client.ArabicPluralsTest; import com.google.gwt.i18n.client.DateTimeFormat_de_Test; import com.google.gwt.i18n.client.DateTimeParse_en_Test; @@ -42,6 +43,7 @@ // $JUnit-BEGIN$ suite.addTestSuite(AbstractResourceTest.class); suite.addTestSuite(ArabicPluralsTest.class); + suite.addTestSuite(AnnotationsTest.class); suite.addTestSuite(ConstantMapTest.class); suite.addTestSuite(DateTimeFormat_de_Test.class); suite.addTestSuite(DateTimeParse_en_Test.class);
diff --git a/user/test/com/google/gwt/i18n/client/AnnotationsTest.java b/user/test/com/google/gwt/i18n/client/AnnotationsTest.java new file mode 100644 index 0000000..a26792a --- /dev/null +++ b/user/test/com/google/gwt/i18n/client/AnnotationsTest.java
@@ -0,0 +1,143 @@ +/* + * Copyright 2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.i18n.client; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.i18n.client.LocalizableResource.DefaultLocale; +import com.google.gwt.junit.client.GWTTestCase; + +/** + * Tests annotations not covered elsewhere. + */ +public class AnnotationsTest extends GWTTestCase { + + /** + * First grandparent for test. + */ + public interface GP1 extends TestConstants { + @DefaultStringValue("gp1 annot") + String gp1(); + + @DefaultStringValue("gp1 shared annot") + String shared(); + } + + /** + * Second grandparent for test. + */ + public interface GP2 extends TestConstants { + @DefaultStringValue("gp2 annot") + String gp2(); + + @DefaultStringValue("gp2 shared annot") + String shared(); + } + + /** + * Test interface for P1 before P2. + */ + public interface Inherit1 extends P1, P2 { + } + + /** + * Test interface for P2 before P1. + */ + public interface Inherit2 extends P2, P1 { + @DefaultStringValue("def") + String def(); + } + + /** + * Used to verify that we can explicitly localize messages in annotations. + */ + @DefaultLocale("en") + public interface Inherit2_en extends Inherit2 { + @DefaultStringValue("en def") + String def(); + } + + /** + * First parent interface for test. + */ + public interface P1 extends TestConstants { + @DefaultStringValue("p1 annot") + String p1(); + + @DefaultStringValue("p1 shared annot") + String shared(); + } + + /** + * Second parent interface for test. + */ + public interface P2 extends GP1, GP2 { + @DefaultStringValue("p2 annot") + String p2(); + + String shared(); + } + + /** + * Basic test message. + */ + public interface Msg1 extends Messages { + @DefaultMessage("Test {0}") + String getTest(String testName); + } + + /** + * Plural form test message. + */ + public interface Msg2 extends Messages { + @DefaultMessage("You have {0} widgets.") + @PluralText({"one", "You have a widget."}) + String getWidgetCount(@PluralCount int count); + } + + /** + * Aggregate messages into one interface. + */ + public interface AllMessages extends Msg1, Msg2 { + } + + @Override + public String getModuleName() { + return "com.google.gwt.i18n.I18NTest_en"; + } + + public void testInheritance() { + Inherit1 i1 = GWT.create(Inherit1.class); + assertEquals("p1 annot", i1.p1()); + assertEquals("gp2 annot", i1.gp2()); + assertEquals("p1 shared annot", i1.shared()); + Inherit2 i2 = GWT.create(Inherit2.class); + assertEquals("p1 annot", i2.p1()); + assertEquals("gp2 annot", i2.gp2()); + assertEquals("gp1 shared annot", i2.shared()); + + // TODO(jat): this doesn't work because findDerivedClasses only + // looks for concrete classes, not other interfaces -- commenting + // out for now, revisit later. + // assertEquals("en def", i2.def()); + } + + public void testIssue2359() { + AllMessages m = GWT.create(AllMessages.class); + assertEquals("Test foo", m.getTest("foo")); + assertEquals("You have 2 widgets.", m.getWidgetCount(2)); + assertEquals("You have a widget.", m.getWidgetCount(1)); + } +}
diff --git a/user/test/com/google/gwt/i18n/client/ArabicPluralsTest.java b/user/test/com/google/gwt/i18n/client/ArabicPluralsTest.java index 4144016..dadb7d2 100644 --- a/user/test/com/google/gwt/i18n/client/ArabicPluralsTest.java +++ b/user/test/com/google/gwt/i18n/client/ArabicPluralsTest.java
@@ -23,6 +23,7 @@ */ public class ArabicPluralsTest extends GWTTestCase { + @Override public String getModuleName() { return "com.google.gwt.i18n.I18NTest_ar"; }
diff --git a/user/test/com/google/gwt/i18n/client/I18N2Test.java b/user/test/com/google/gwt/i18n/client/I18N2Test.java index 1c73374..3cc843b 100644 --- a/user/test/com/google/gwt/i18n/client/I18N2Test.java +++ b/user/test/com/google/gwt/i18n/client/I18N2Test.java
@@ -113,8 +113,16 @@ public void testCheckColorsAndShapes() { ColorsAndShapes s = (ColorsAndShapes) GWT.create(ColorsAndShapes.class); - // should not have changed, because we included no shapesAndColor info. - assertEquals("ýéļļöŵ", s.yellow()); + // Red comes from Colors_b_C_d + assertEquals("red_b_C_d", s.red()); + // Blue comes from Colors_b_C + assertEquals("blue_b_C", s.blue()); + // Yellow comes from Colors_b + assertEquals("yellow_b", s.yellow()); + // RedSquare comes from ColorsAndShapes + assertEquals("red square", s.redSquare()); + // Circle comes from Shapes + assertEquals("a circle", s.circle()); } public void testWalkUpColorTree() {
diff --git a/user/test/com/google/gwt/i18n/client/I18NTest.java b/user/test/com/google/gwt/i18n/client/I18NTest.java index bf5e6b1..1e5e2a7 100644 --- a/user/test/com/google/gwt/i18n/client/I18NTest.java +++ b/user/test/com/google/gwt/i18n/client/I18NTest.java
@@ -45,77 +45,23 @@ * Tests Internationalization. Assumes locale is set to piglatin_UK */ public class I18NTest extends GWTTestCase { + @Override public String getModuleName() { return "com.google.gwt.i18n.I18NTest"; } - public void testLocalizableInner() { - // Check simple inner - LocalizableSimpleInner s = (LocalizableSimpleInner) GWT.create(Inners.LocalizableSimpleInner.class); - assertEquals("getLocalizableInner", s.getLocalizableInner()); - - LocalizableInnerInner localizableInnerInner = (LocalizableInnerInner) GWT.create(Inners.InnerClass.LocalizableInnerInner.class); - assertEquals("localizableInnerInner", localizableInnerInner.string()); - - // Check success of finding embedded - OuterLoc lock = (OuterLoc) GWT.create(OuterLoc.class); - assertEquals("piglatin", lock.string()); - - assertEquals("InnerLoc", Inners.testInnerLoc()); - } - - public void testLocalizableInterfaceInner() { - Inners inner = new Inners(); - - // Simple Inner - SimpleInner simpleInner = (SimpleInner) GWT.create(Inners.SimpleInner.class); - assertEquals(0, simpleInner.intZero()); - assertEquals("Simple Inner", simpleInner.simpleInner()); - assertTrue(inner.testProtectedInner()); - - // Has Inner - HasInner hasInner = (HasInner) GWT.create(Inners.HasInner.class); - assertEquals("Has Inner", hasInner.hasInner()); - assertEquals(0, hasInner.floatZero(), .0001); - - // Is Inner - IsInner isInner = (IsInner) GWT.create(IsInner.class); - assertEquals(2, isInner.isInner()); - - // Inner Inner - InnerInner innerInner = (InnerInner) GWT.create(InnerInner.class); - assertEquals(4.321, innerInner.innerInner(), .0001); - assertEquals("outer", innerInner.outer()); - - // Inner Inner Message - InnerInnerMessages innerInnerMessages = (InnerInnerMessages) GWT.create(InnerInnerMessages.class); - assertEquals("I am a person", - innerInnerMessages.innerClassMessages("person")); - - // Extends Inner Inner - ExtendsInnerInner extendsInnerInner = (ExtendsInnerInner) GWT.create(ExtendsInnerInner.class); - assertEquals("Extends Inner Inner", extendsInnerInner.extendsInnerInner()); - - // Protected InnerClass - InnerClass innerClass = new Inners.InnerClass(); - String extendsAnotherInner = innerClass.testExtendsAnotherInner(); - assertEquals("{innerInner=4.321, outer=outer}", extendsAnotherInner); - - // ExtendProtectedInner - String extendProtectedInner = innerClass.testExtendsProtectedInner(); - assertEquals("Extend Protected Inner", extendProtectedInner); - } - public void testAnnotatedConstants() { TestAnnotatedConstants c = GWT.create(TestAnnotatedConstants.class); assertEquals(14, c.fourteen()); assertFalse(c.isFalse()); assertTrue(c.isTrue()); - assertArrayEquals(new String[] {"String array with one string"}, c.singleString()); - assertArrayEquals(new String[] {"One", "Two", "Three,Comma"}, c.threeStrings()); + assertArrayEquals(new String[] {"String array with one string"}, + c.singleString()); + assertArrayEquals(new String[] {"One", "Two", "Three,Comma"}, + c.threeStrings()); assertEquals("Properties value #s need quoting!", c.propertiesQuoting()); - Map<String,String> stringMap = c.stringMap(); + Map<String, String> stringMap = c.stringMap(); assertTrue(stringMap.containsKey("key1")); assertTrue(stringMap.containsKey("key2")); assertEquals("value1", stringMap.get("key1")); @@ -132,15 +78,16 @@ assertEquals(3.14, c.threePointOneFour()); assertEquals("Once more, with meaning", c.withMeaning()); } - + public void testAnnotatedConstantsGenMD5() { TestAnnotatedConstantsGenMD5 c = GWT.create(TestAnnotatedConstantsGenMD5.class); assertEquals(14, c.fourteen()); assertFalse(c.isFalse()); assertTrue(c.isTrue()); - assertArrayEquals(new String[] {"String array with one string"}, c.singleString()); + assertArrayEquals(new String[] {"String array with one string"}, + c.singleString()); assertArrayEquals(new String[] {"One", "Two"}, c.twoStrings()); - Map<String,String> stringMap = c.stringMap(); + Map<String, String> stringMap = c.stringMap(); assertTrue(stringMap.containsKey("key1")); assertTrue(stringMap.containsKey("key2")); assertEquals("value1", stringMap.get("key1")); @@ -151,7 +98,7 @@ assertEquals(3.14, c.threePointOneFour()); assertEquals("Once more, with meaning", c.withMeaning()); } - + public void testAnnotatedMessages() { TestAnnotatedMessages m = GWT.create(TestAnnotatedMessages.class); assertEquals("Estay emay", m.basicText()); @@ -161,16 +108,20 @@ m.optionalArgument("where am I?")); assertEquals("Two arguments, second and first, inverted", m.invertedArguments("first", "second")); // from default locale - assertEquals("PL: Don't tell me I can't {quote things in braces}", m.quotedText()); - assertEquals("PL: This {0} would be an argument if not quoted", m.quotedArg()); + assertEquals("PL: Don't tell me I can't {quote things in braces}", + m.quotedText()); + assertEquals("PL: This {0} would be an argument if not quoted", + m.quotedArg()); assertEquals("PL: Total is US$11,305.01", m.currencyFormat(11305.01)); - assertEquals("PL: Default number format is 1,017.1", m.defaultNumberFormat(1017.1)); + assertEquals("PL: Default number format is 1,017.1", + m.defaultNumberFormat(1017.1)); assertEquals("PL: It is 12:01 PM on Saturday, December 1, 2007", m.getTimeDate(new Date(107, 11, 1, 12, 1, 2))); assertEquals("PL: 13 widgets", m.pluralWidgetsOther(13)); - assertEquals("Too many widgets to count (150) in pig-latin", m.pluralWidgetsOther(150)); + assertEquals("Too many widgets to count (150) in pig-latin", + m.pluralWidgetsOther(150)); } - + public void testBindings() { TestBinding b = (TestBinding) GWT.create(TestBinding.class); assertEquals("default", b.a()); @@ -453,28 +404,6 @@ } } - // Uncomment for desk tests - // /** - // * Tests focus on correctness of entries, since ABCD exercises the map. - // */ - // public void testConstantMapEmpty() { - // TestConstants types = (TestConstants) GWT.create(TestConstants.class); - // - // ConstantMap map = (ConstantMap) types.mapEmpty(); - // - // assertEquals(0, map.size()); - // - // Set keys = map.keySet(); - // assertEquals(0, keys.size()); - // Iterator keyIter = keys.iterator(); - // assertFalse(keyIter.hasNext()); - // - // Collection values = map.values(); - // assertEquals(0, values.size()); - // Iterator valueIter = values.iterator(); - // assertFalse(valueIter.hasNext()); - // } - public void testDictionary() { createDummyDictionaries(); Dictionary d = Dictionary.getDictionary("testDic"); @@ -511,6 +440,85 @@ assertEquals(Integer.MIN_VALUE, types.intMin()); } + // Uncomment for desk tests + // /** + // * Tests focus on correctness of entries, since ABCD exercises the map. + // */ + // public void testConstantMapEmpty() { + // TestConstants types = (TestConstants) GWT.create(TestConstants.class); + // + // ConstantMap map = (ConstantMap) types.mapEmpty(); + // + // assertEquals(0, map.size()); + // + // Set keys = map.keySet(); + // assertEquals(0, keys.size()); + // Iterator keyIter = keys.iterator(); + // assertFalse(keyIter.hasNext()); + // + // Collection values = map.values(); + // assertEquals(0, values.size()); + // Iterator valueIter = values.iterator(); + // assertFalse(valueIter.hasNext()); + // } + + public void testLocalizableInner() { + // Check simple inner + LocalizableSimpleInner s = (LocalizableSimpleInner) GWT.create(Inners.LocalizableSimpleInner.class); + assertEquals("getLocalizableInner", s.getLocalizableInner()); + + LocalizableInnerInner localizableInnerInner = (LocalizableInnerInner) GWT.create(Inners.InnerClass.LocalizableInnerInner.class); + assertEquals("localizableInnerInner", localizableInnerInner.string()); + + // Check success of finding embedded + OuterLoc lock = (OuterLoc) GWT.create(OuterLoc.class); + assertEquals("piglatin", lock.string()); + + assertEquals("InnerLoc", Inners.testInnerLoc()); + } + + public void testLocalizableInterfaceInner() { + Inners inner = new Inners(); + + // Simple Inner + SimpleInner simpleInner = (SimpleInner) GWT.create(Inners.SimpleInner.class); + assertEquals(0, simpleInner.intZero()); + assertEquals("Simple Inner", simpleInner.simpleInner()); + assertTrue(inner.testProtectedInner()); + + // Has Inner + HasInner hasInner = (HasInner) GWT.create(Inners.HasInner.class); + assertEquals("Has Inner", hasInner.hasInner()); + assertEquals(0, hasInner.floatZero(), .0001); + + // Is Inner + IsInner isInner = (IsInner) GWT.create(IsInner.class); + assertEquals(2, isInner.isInner()); + + // Inner Inner + InnerInner innerInner = (InnerInner) GWT.create(InnerInner.class); + assertEquals(4.321, innerInner.innerInner(), .0001); + assertEquals("outer", innerInner.outer()); + + // Inner Inner Message + InnerInnerMessages innerInnerMessages = (InnerInnerMessages) GWT.create(InnerInnerMessages.class); + assertEquals("I am a person", + innerInnerMessages.innerClassMessages("person")); + + // Extends Inner Inner + ExtendsInnerInner extendsInnerInner = (ExtendsInnerInner) GWT.create(ExtendsInnerInner.class); + assertEquals("Extends Inner Inner", extendsInnerInner.extendsInnerInner()); + + // Protected InnerClass + InnerClass innerClass = new Inners.InnerClass(); + String extendsAnotherInner = innerClass.testExtendsAnotherInner(); + assertEquals("{innerInner=4.321, outer=outer}", extendsAnotherInner); + + // ExtendProtectedInner + String extendProtectedInner = innerClass.testExtendsProtectedInner(); + assertEquals("Extend Protected Inner", extendProtectedInner); + } + public void testShapesFamily() { Shapes shapes = (Shapes) GWT.create(Shapes.class); // test overload @@ -540,8 +548,8 @@ public void testTypedMessages() { TestTypedMessages typed = (TestTypedMessages) GWT.create(TestTypedMessages.class); String expected = "int(0) float(1.2), long(0), boolean(true), Object([], char(a), byte(127), short(-32768);"; - assertEquals(expected, typed.testAllTypes(0, (float) 1.2, 0, true, new ArrayList<String>(), - 'a', Byte.MAX_VALUE, Short.MIN_VALUE)); + assertEquals(expected, typed.testAllTypes(0, (float) 1.2, 0, true, + new ArrayList<String>(), 'a', Byte.MAX_VALUE, Short.MIN_VALUE)); String lotsOfInts = typed.testLotsOfInts(1, 2, 3, 4); assertEquals("1, 2,3,4 ", lotsOfInts); String oneFloat = typed.simpleMessageTest((float) 2.3); @@ -552,7 +560,7 @@ new StringBuffer("hello"), new Integer("34"), null); assertEquals( "this(null(com.google.gwt.i18n.client.I18NTest)), StringBuffer(hello), Integer(34), " - + "null(null);", testSomeObjectTypes); + + "null(null);", testSomeObjectTypes); } private void assertArrayEquals(String[] shouldBe, String[] test) {
diff --git a/user/test/com/google/gwt/i18n/rebind/AbstractResourceTest.java b/user/test/com/google/gwt/i18n/rebind/AbstractResourceTest.java index 572d6ca..4645e10 100644 --- a/user/test/com/google/gwt/i18n/rebind/AbstractResourceTest.java +++ b/user/test/com/google/gwt/i18n/rebind/AbstractResourceTest.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -18,6 +18,7 @@ import com.google.gwt.i18n.client.ColorsAndShapes; import com.google.gwt.i18n.client.ColorsAndShapesAndConcepts; import com.google.gwt.i18n.client.gen.Colors; +import com.google.gwt.i18n.rebind.AbstractResource.ResourceList; import junit.framework.TestCase; @@ -38,23 +39,21 @@ public void testBundle() { // simple test String s = Colors.class.getName(); - AbstractResource resource = ResourceFactory.getBundle(s, null, true); - assertNotNull(resource); - AbstractResource pigLatinResource = + ResourceList resourceList = ResourceFactory.getBundle(s, null, true); + assertNotNull(resourceList); + ResourceList pigLatinResourceList = ResourceFactory.getBundle(s, LOCALE_NAME_PIGLATIN, true); - assertEquals(LOCALE_NAME_PIGLATIN, pigLatinResource.getLocaleName()); - assertNotNull(pigLatinResource); - assertEquals("ueblay", pigLatinResource.getString("blue")); - assertEquals("ĝréý", pigLatinResource.getString("grey")); + assertNotNull(pigLatinResourceList); + assertEquals("ueblay", pigLatinResourceList.getString("blue")); + assertEquals("ĝréý", pigLatinResourceList.getString("grey")); } public void testInheritence() { ResourceFactory.clearCache(); - AbstractResource resource = ResourceFactory.getBundle( + ResourceList resourceList = ResourceFactory.getBundle( ColorsAndShapes.class, LOCALE_NAME_PIGLATIN, true); - assertEquals(LOCALE_NAME_PIGLATIN, resource.getLocaleName()); - assertEquals("ueblay", resource.getString("blue")); - assertEquals("ĝréý", resource.getString("grey")); + assertEquals("ueblay", resourceList.getString("blue")); + assertEquals("ĝréý", resourceList.getString("grey")); } public void testByteStreamBehavior() throws UnsupportedEncodingException { @@ -68,11 +67,11 @@ } public void testDoubleInherits() { - AbstractResource resource = ResourceFactory.getBundle( + ResourceList resourceList = ResourceFactory.getBundle( ColorsAndShapesAndConcepts.class, LOCALE_NAME_PIGLATIN_UK, true); - String s = resource.getString("internationalization"); + String s = resourceList.getString("internationalization"); assertEquals("Îñţérñåţîöñåļîžåţîöñ", s); - assertTrue(resource.keySet().size() > 5); + assertTrue(resourceList.keySet().size() > 5); } public void testCharArrayBehavior() {
diff --git a/user/test/com/google/gwt/user/UISuite.java b/user/test/com/google/gwt/user/UISuite.java index b34b49c..fe04a70 100644 --- a/user/test/com/google/gwt/user/UISuite.java +++ b/user/test/com/google/gwt/user/UISuite.java
@@ -70,6 +70,7 @@ import com.google.gwt.user.client.ui.WidgetCollectionTest; import com.google.gwt.user.client.ui.WidgetIteratorsTest; import com.google.gwt.user.client.ui.WidgetOnLoadTest; +import com.google.gwt.user.client.ui.WidgetSubclassingTest; import com.google.gwt.user.client.ui.impl.ClippedImagePrototypeTest; import com.google.gwt.user.rebind.ui.ImageBundleGeneratorTest; import com.google.gwt.xml.client.XMLTest; @@ -141,6 +142,7 @@ suite.addTestSuite(WidgetCollectionTest.class); suite.addTestSuite(WidgetIteratorsTest.class); suite.addTestSuite(WidgetOnLoadTest.class); + suite.addTestSuite(WidgetSubclassingTest.class); suite.addTestSuite(WindowTest.class); suite.addTestSuite(XMLTest.class); suite.addTestSuite(ClassInitTest.class);
diff --git a/user/test/com/google/gwt/user/client/ui/ElementWrappingTest.java b/user/test/com/google/gwt/user/client/ui/ElementWrappingTest.java index 7c927ef..fd3a5d9 100644 --- a/user/test/com/google/gwt/user/client/ui/ElementWrappingTest.java +++ b/user/test/com/google/gwt/user/client/ui/ElementWrappingTest.java
@@ -15,6 +15,8 @@ */ package com.google.gwt.user.client.ui; +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.AnchorElement; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.junit.client.GWTTestCase; @@ -126,21 +128,24 @@ public void testPasswordTextBox() { ensureDiv().setInnerHTML("<input type='password' id='foo'></input>"); - PasswordTextBox textBox = PasswordTextBox.wrap(Document.get().getElementById("foo")); + PasswordTextBox textBox = PasswordTextBox.wrap(Document.get().getElementById( + "foo")); assertExistsAndAttached(textBox); } public void testSimpleCheckBox() { ensureDiv().setInnerHTML("<input type='checkbox' id='foo'></input>"); - SimpleCheckBox checkBox = SimpleCheckBox.wrap(Document.get().getElementById("foo")); + SimpleCheckBox checkBox = SimpleCheckBox.wrap(Document.get().getElementById( + "foo")); assertExistsAndAttached(checkBox); } public void testSimpleRadioButton() { ensureDiv().setInnerHTML("<input type='radio' id='foo'></input>"); - SimpleRadioButton radio = SimpleRadioButton.wrap(Document.get().getElementById("foo")); + SimpleRadioButton radio = SimpleRadioButton.wrap(Document.get().getElementById( + "foo")); assertExistsAndAttached(radio); } @@ -152,6 +157,78 @@ assertExistsAndAttached(textBox); } + public void testDetachOnUnloadTwiceFails() { + // Testing hosted-mode-only assertion. + if (!GWT.isScript()) { + try { + // Trying to pass the same widget to RootPanel.detachOnUnload() twice + // should fail an assertion (the first call is implicit through + // Anchor.wrap()). + ensureDiv().setInnerHTML( + "<a id='foo' href='" + TEST_URL + "'>myAnchor</a>"); + Anchor a = Anchor.wrap(Document.get().getElementById("foo")); // pass + RootPanel.detachOnWindowClose(a); // fail + fail("Expected assertion failure calling detachOnLoad() twice"); + } catch (AssertionError e) { + } + } + } + + public void testDetachNowTwiceFails() { + // Testing hosted-mode-only assertion. + if (!GWT.isScript()) { + try { + // Trying to pass the same widget to RootPanel.detachNow() twice + // should fail an assertion. + ensureDiv().setInnerHTML( + "<a id='foo' href='" + TEST_URL + "'>myAnchor</a>"); + Anchor a = Anchor.wrap(Document.get().getElementById("foo")); + RootPanel.detachNow(a); // pass + RootPanel.detachNow(a); // fail + fail("Expected assertion failure calling detachNow() twice"); + } catch (AssertionError e) { + } + } + } + + public void testWrapUnattachedFails() { + // Testing hosted-mode-only assertion. + if (!GWT.isScript()) { + try { + // Trying to wrap an unattached element should fail an assertion. + // We only test this for one element/widget type, because they + // all call RootPanel.detachOnUnload(), where the actual assertion + // occurs. + AnchorElement aElem = Document.get().createAnchorElement(); + Anchor.wrap(aElem); + fail("Expected assertion failure wrapping unattached element"); + } catch (AssertionError e) { + } + } + } + + public void testOnUnloadAssertions() { + // Testing hosted-mode-only assertion. + if (!GWT.isScript()) { + try { + // When a wrap()ed element is detached from the document without being + // properly unwrapped, there will be an assertion to catch this run on + // unload. + ensureDiv().setInnerHTML( + "<a id='foo' href='" + TEST_URL + "'>myAnchor</a>"); + Element aElem = Document.get().getElementById("foo"); + Anchor.wrap(aElem); + aElem.getParentElement().removeChild(aElem); + + // Fake an unload by telling the RootPanel to go ahead and detach all + // of its widgets. + RootPanel.detachWidgets(); + fail("Assertion expected for orphaned wrap()ed widgets"); + } catch (AssertionError e) { + } + } + } + private void assertExistsAndAttached(Widget widget) { assertNotNull(widget); assertTrue(widget.isAttached());
diff --git a/user/test/com/google/gwt/user/client/ui/FormPanelTest.java b/user/test/com/google/gwt/user/client/ui/FormPanelTest.java index 850d90b..2ab69df 100644 --- a/user/test/com/google/gwt/user/client/ui/FormPanelTest.java +++ b/user/test/com/google/gwt/user/client/ui/FormPanelTest.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -16,10 +16,12 @@ package com.google.gwt.user.client.ui; import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.Document; +import com.google.gwt.dom.client.Element; import com.google.gwt.junit.client.GWTTestCase; -import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.HasWidgetsTester.WidgetAdder; +import com.google.gwt.user.server.ui.FormPanelTestServlet; /** * Tests the FormPanel. @@ -29,6 +31,10 @@ public class FormPanelTest extends GWTTestCase { public static boolean clicked = false; + private native boolean isHappyDivPresent(Element iframe) /*-{ + return !!iframe.contentWindow.document.getElementById(':)'); + }-*/; + public String getModuleName() { return "com.google.gwt.user.FormPanelTest"; } @@ -213,14 +219,42 @@ assertTrue(isHappyDivPresent(frame.getElement())); finishTest(); } - - private native boolean isHappyDivPresent(Element iframe) /*-{ - return !!iframe.contentWindow.document.getElementById(':)'); - }-*/; }; // Wait 5 seconds before checking the results. t.schedule(5000); form.submit(); } + + public void testWrappedForm() { + // Create a form and frame in the document we can wrap. + HTML formAndFrame = new HTML( + "<form id='wrapMe' method='post' target='targetFrame' action='" + + GWT.getModuleBaseURL() + "formHandler?sendHappyHtml'>" + + "<input type='hidden' name='foo' value='bar'></input></form>" + + "<iframe src='javascript:\'\'' id='targetMe' name='targetFrame'></iframe>"); + RootPanel.get().add(formAndFrame); + + // Wrap the form and make sure its target frame is intact. + FormPanel form = FormPanel.wrap(Document.get().getElementById("wrapMe")); + assertEquals("targetFrame", form.getTarget()); + + // Ensure that no synthesized iframe was created. + assertNull(form.getSynthesizedIFrame()); + + // Submit the form and make sure the target frame comes back with the right + // stuff (give it 2.5 s to complete). + delayTestFinish(5000); + new Timer() { + @Override + public void run() { + // Get the targeted frame and make sure it contains the requested + // 'happyElem'. + Frame frame = Frame.wrap(Document.get().getElementById("targetMe")); + assertTrue(isHappyDivPresent(frame.getElement())); + finishTest(); + } + }.schedule(2500); + form.submit(); + } }
diff --git a/user/test/com/google/gwt/user/client/ui/HiddenTest.java b/user/test/com/google/gwt/user/client/ui/HiddenTest.java index 7c1a814..378fe15 100644 --- a/user/test/com/google/gwt/user/client/ui/HiddenTest.java +++ b/user/test/com/google/gwt/user/client/ui/HiddenTest.java
@@ -34,7 +34,7 @@ // Expected } try { - Hidden d = new Hidden(null); + Hidden d = new Hidden((String)null); fail("expected null pointer exception"); } catch (NullPointerException e) { // Expected
diff --git a/user/test/com/google/gwt/user/client/ui/ImageTest.java b/user/test/com/google/gwt/user/client/ui/ImageTest.java index 7083f4e..2874c8f 100644 --- a/user/test/com/google/gwt/user/client/ui/ImageTest.java +++ b/user/test/com/google/gwt/user/client/ui/ImageTest.java
@@ -15,6 +15,7 @@ */ package com.google.gwt.user.client.ui; +import com.google.gwt.dom.client.Document; import com.google.gwt.junit.client.GWTTestCase; import com.google.gwt.user.client.Timer; @@ -90,7 +91,7 @@ * Tests the transition from the clipped state to the unclipped state. */ public void testChangeClippedImageToUnclipped() { - final ArrayList onloadEventFireCounter = new ArrayList(); + final ArrayList<Object> onloadEventFireCounter = new ArrayList<Object>(); final Image image = new Image("counting-forwards.png", 12, 13, 8, 8); assertEquals("clipped", getCurrentImageStateName(image)); @@ -167,7 +168,7 @@ * Tests the creation of an image in clipped mode. */ public void testCreateClippedImage() { - final ArrayList onloadEventFireCounter = new ArrayList(); + final ArrayList<Object> onloadEventFireCounter = new ArrayList<Object>(); final Image image = new Image("counting-forwards.png", 16, 16, 16, 16); @@ -246,7 +247,7 @@ * on a clipped image. */ public void testSetUrlAndVisibleRectOnClippedImage() { - final ArrayList onloadEventFireCounter = new ArrayList(); + final ArrayList<Object> onloadEventFireCounter = new ArrayList<Object>(); final Image image = new Image("counting-backwards.png", 12, 12, 12, 12); @@ -335,7 +336,7 @@ * on a clipped image. */ public void testSetVisibleRectAndLoadEventsOnClippedImage() { - final ArrayList onloadEventFireCounter = new ArrayList(); + final ArrayList<Object> onloadEventFireCounter = new ArrayList<Object>(); final Image image = new Image("counting-backwards.png", 16, 16, 16, 16); @@ -366,4 +367,37 @@ t.schedule(1000); } + + /** + * Tests that wrapping an existing DOM element works if you call + * setUrlAndVisibleRect() on it. + */ + public void testWrapThenSetUrlAndVisibleRect() { + String uid = Document.get().createUniqueId(); + HTML html = new HTML("<img id='" + uid + "' src='counting-backwards.png' width='16' height='16'>"); + RootPanel.get().add(html); + final Image image = Image.wrap(Document.get().getElementById(uid)); + + delayTestFinish(20000); + + assertEquals(0, image.getOriginLeft()); + assertEquals(0, image.getOriginTop()); + assertEquals(16, image.getWidth()); + assertEquals(16, image.getHeight()); + assertEquals("unclipped", getCurrentImageStateName(image)); + + image.setUrlAndVisibleRect("counting-forwards.png", 0, 16, 16, 16); + Timer t = new Timer() { + public void run() { + assertEquals(0, image.getOriginLeft()); + assertEquals(16, image.getOriginTop()); + assertEquals(16, image.getWidth()); + assertEquals(16, image.getHeight()); + assertEquals("clipped", getCurrentImageStateName(image)); + finishTest(); + } + }; + + t.schedule(1000); + } }
diff --git a/user/test/com/google/gwt/user/client/ui/WidgetSubclassingTest.java b/user/test/com/google/gwt/user/client/ui/WidgetSubclassingTest.java new file mode 100644 index 0000000..9432fdb --- /dev/null +++ b/user/test/com/google/gwt/user/client/ui/WidgetSubclassingTest.java
@@ -0,0 +1,452 @@ +/* + * Copyright 2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.user.client.ui; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.Document; +import com.google.gwt.junit.client.GWTTestCase; + +/** + * A series of tests to ensure that widgets with a wrap() method properly assert + * their element types. + */ +public class WidgetSubclassingTest extends GWTTestCase { + + private static final String ASSERTION_ERROR = "Should have received an assertion error trying to use the wrong element type"; + + @Override + public String getModuleName() { + return "com.google.gwt.user.UserTest"; + } + + // Correct subclasses. + public static class TestAnchor extends Anchor { + public TestAnchor() { + super(Document.get().createAnchorElement()); + } + } + + public static class TestButton extends Button { + public TestButton() { + super(Document.get().createButtonElement()); + } + } + + public static class TestFileUpload extends FileUpload { + public TestFileUpload() { + super(Document.get().createFileInputElement()); + } + } + + public static class TestFormPanel extends FormPanel { + public TestFormPanel() { + super(Document.get().createFormElement()); + } + } + + public static class TestFrame extends Frame { + public TestFrame() { + super(Document.get().createIFrameElement()); + } + } + + public static class TestHidden extends Hidden { + public TestHidden() { + super(Document.get().createHiddenInputElement()); + } + } + + public static class TestHTML extends HTML { + public TestHTML() { + super(Document.get().createDivElement()); + } + } + + public static class TestImage extends Image { + public TestImage() { + super(Document.get().createImageElement()); + } + } + + public static class TestInlineHTML extends InlineHTML { + public TestInlineHTML() { + super(Document.get().createSpanElement()); + } + } + + public static class TestInlineLabel extends InlineLabel { + public TestInlineLabel() { + super(Document.get().createSpanElement()); + } + } + + public static class TestLabel extends Label { + public TestLabel() { + super(Document.get().createSpanElement()); + } + } + + public static class TestListBox extends ListBox { + public TestListBox() { + super(Document.get().createSelectElement()); + } + } + + public static class TestPasswordTextBox extends PasswordTextBox { + public TestPasswordTextBox() { + super(Document.get().createPasswordInputElement()); + } + } + + public static class TestSimpleCheckBox extends SimpleCheckBox { + public TestSimpleCheckBox() { + super(Document.get().createCheckInputElement()); + } + } + + public static class TestSimpleRadioButton extends SimpleRadioButton { + public TestSimpleRadioButton() { + super(Document.get().createRadioInputElement("group")); + } + } + + public static class TestTextBox extends TextBox { + public TestTextBox() { + super(Document.get().createTextInputElement()); + } + } + + // Broken subclasses. + public static class BrokenAnchor extends Anchor { + public BrokenAnchor() { + super(Document.get().createBRElement()); + } + } + + public static class BrokenButton extends Button { + public BrokenButton() { + super(Document.get().createBRElement()); + } + } + + public static class BrokenFileUpload extends FileUpload { + public BrokenFileUpload() { + super(Document.get().createBRElement()); + } + } + + public static class BrokenFormPanel extends FormPanel { + public BrokenFormPanel() { + super(Document.get().createBRElement()); + } + } + + public static class BrokenFrame extends Frame { + public BrokenFrame() { + super(Document.get().createBRElement()); + } + } + + public static class BrokenHidden extends Hidden { + public BrokenHidden() { + super(Document.get().createBRElement()); + } + } + + public static class BrokenHTML extends HTML { + public BrokenHTML() { + super(Document.get().createBRElement()); + } + } + + public static class BrokenImage extends Image { + public BrokenImage() { + super(Document.get().createBRElement()); + } + } + + public static class BrokenInlineHTML extends InlineHTML { + public BrokenInlineHTML() { + super(Document.get().createBRElement()); + } + } + + public static class BrokenInlineLabel extends InlineLabel { + public BrokenInlineLabel() { + super(Document.get().createBRElement()); + } + } + + public static class BrokenLabel extends Label { + public BrokenLabel() { + super(Document.get().createBRElement()); + } + } + + public static class BrokenListBox extends ListBox { + public BrokenListBox() { + super(Document.get().createBRElement()); + } + } + + public static class BrokenPasswordTextBox extends PasswordTextBox { + public BrokenPasswordTextBox() { + super(Document.get().createBRElement()); + } + } + + public static class BrokenSimpleCheckBox extends SimpleCheckBox { + public BrokenSimpleCheckBox() { + super(Document.get().createBRElement()); + } + } + + public static class BrokenSimpleRadioButton extends SimpleRadioButton { + public BrokenSimpleRadioButton() { + super(Document.get().createBRElement()); + } + } + + public static class BrokenTextBox extends TextBox { + public BrokenTextBox() { + super(Document.get().createBRElement()); + } + } + + public void testAnchor() { + // Make sure the normal case works. + new TestAnchor(); + + // And the wrong element type doesn't. + if (!GWT.isScript()) { + try { + new BrokenAnchor(); + fail(ASSERTION_ERROR); + } catch (AssertionError e) { + } + } + } + + public void testButton() { + // Make sure the normal case works. + new TestButton(); + + // And the wrong element type doesn't. + if (!GWT.isScript()) { + try { + new BrokenButton(); + fail(ASSERTION_ERROR); + } catch (AssertionError e) { + } + } + } + + public void testFileUpload() { + // Make sure the normal case works. + new TestFileUpload(); + + // And the wrong element type doesn't. + if (!GWT.isScript()) { + try { + new BrokenFileUpload(); + fail(ASSERTION_ERROR); + } catch (AssertionError e) { + } + } + } + + public void testFormPanel() { + // Make sure the normal case works. + new TestFormPanel(); + + // And the wrong element type doesn't. + if (!GWT.isScript()) { + try { + new BrokenFormPanel(); + fail(ASSERTION_ERROR); + } catch (AssertionError e) { + } + } + } + + public void testFrame() { + // Make sure the normal case works. + new TestFrame(); + + // And the wrong element type doesn't. + if (!GWT.isScript()) { + try { + new BrokenFrame(); + fail(ASSERTION_ERROR); + } catch (AssertionError e) { + } + } + } + + public void testHidden() { + // Make sure the normal case works. + new TestHidden(); + + // And the wrong element type doesn't. + if (!GWT.isScript()) { + try { + new BrokenHidden(); + fail(ASSERTION_ERROR); + } catch (AssertionError e) { + } + } + } + + public void testHTML() { + // Make sure the normal case works. + new TestHTML(); + + // And the wrong element type doesn't. + if (!GWT.isScript()) { + try { + new BrokenHTML(); + fail(ASSERTION_ERROR); + } catch (AssertionError e) { + } + } + } + + public void testImage() { + // Make sure the normal case works. + new TestImage(); + + // And the wrong element type doesn't. + if (!GWT.isScript()) { + try { + new BrokenImage(); + fail(ASSERTION_ERROR); + } catch (AssertionError e) { + } + } + } + + public void testInlineHTML() { + // Make sure the normal case works. + new TestInlineHTML(); + + // And the wrong element type doesn't. + if (!GWT.isScript()) { + try { + new BrokenInlineHTML(); + fail(ASSERTION_ERROR); + } catch (AssertionError e) { + } + } + } + + public void testInlineLabel() { + // Make sure the normal case works. + new TestInlineLabel(); + + // And the wrong element type doesn't. + if (!GWT.isScript()) { + try { + new BrokenInlineLabel(); + fail(ASSERTION_ERROR); + } catch (AssertionError e) { + } + } + } + + public void testLabel() { + // Make sure the normal case works. + new TestLabel(); + + // And the wrong element type doesn't. + if (!GWT.isScript()) { + try { + new BrokenLabel(); + fail(ASSERTION_ERROR); + } catch (AssertionError e) { + } + } + } + + public void testListBox() { + // Make sure the normal case works. + new TestListBox(); + + // And the wrong element type doesn't. + if (!GWT.isScript()) { + try { + new BrokenListBox(); + fail(ASSERTION_ERROR); + } catch (AssertionError e) { + } + } + } + + public void testPasswordTextBox() { + // Make sure the normal case works. + new TestPasswordTextBox(); + + // And the wrong element type doesn't. + if (!GWT.isScript()) { + try { + new BrokenPasswordTextBox(); + fail(ASSERTION_ERROR); + } catch (AssertionError e) { + } + } + } + + public void testSimpleCheckBox() { + // Make sure the normal case works. + new TestSimpleCheckBox(); + + // And the wrong element type doesn't. + if (!GWT.isScript()) { + try { + new BrokenSimpleCheckBox(); + fail(ASSERTION_ERROR); + } catch (AssertionError e) { + } + } + } + + public void testSimpleRadioButton() { + // Make sure the normal case works. + new TestSimpleRadioButton(); + + // And the wrong element type doesn't. + if (!GWT.isScript()) { + try { + new BrokenSimpleRadioButton(); + fail(ASSERTION_ERROR); + } catch (AssertionError e) { + } + } + } + + public void testTextBox() { + // Make sure the normal case works. + new TestTextBox(); + + // And the wrong element type doesn't. + if (!GWT.isScript()) { + try { + new BrokenTextBox(); + fail(ASSERTION_ERROR); + } catch (AssertionError e) { + } + } + } +}
diff --git a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java index 5915a67..972c2b1 100644 --- a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java +++ b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
@@ -2113,8 +2113,8 @@ * @throws UnableToCompleteException * @throws NotFoundException */ - public void testTypeParametersInRootTypes() throws UnableToCompleteException, - NotFoundException { + public void testTypeParametersInRootTypes1() + throws UnableToCompleteException, NotFoundException { Set<CompilationUnit> units = new HashSet<CompilationUnit>(); addStandardClasses(units); @@ -2160,6 +2160,63 @@ assertSerializableTypes(so, rawA, b); } + /** + * Tests root types that <em>are</em> type parameters. + * + * @throws UnableToCompleteException + * @throws NotFoundException + */ + public void testTypeParametersInRootTypes2() + throws UnableToCompleteException, NotFoundException { + Set<CompilationUnit> units = new HashSet<CompilationUnit>(); + addStandardClasses(units); + + { + StringBuilder code = new StringBuilder(); + code.append("import java.io.Serializable;\n"); + code.append("public abstract class A<U extends B> implements Serializable {\n"); + code.append(" <V extends C> V getFoo() { return null; }\n"); + code.append("}\n"); + units.add(createMockCompilationUnit("A", code)); + } + + { + StringBuilder code = new StringBuilder(); + code.append("import java.io.Serializable;\n"); + code.append("public class B implements Serializable {\n"); + code.append("}\n"); + units.add(createMockCompilationUnit("B", code)); + } + + { + StringBuilder code = new StringBuilder(); + code.append("import java.io.Serializable;\n"); + code.append("public class C implements Serializable {\n"); + code.append("}\n"); + units.add(createMockCompilationUnit("C", code)); + } + + TreeLogger logger = createLogger(); + TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, units); + + JGenericType a = to.getType("A").isGenericType(); + JClassType b = to.getType("B"); + JClassType c = to.getType("C"); + JTypeParameter u = a.getTypeParameters()[0]; + JTypeParameter v = a.getMethod("getFoo", makeArray()).getReturnType().isTypeParameter(); + + SerializableTypeOracleBuilder sob = new SerializableTypeOracleBuilder( + logger, to); + sob.addRootType(logger, u); + sob.addRootType(logger, v); + SerializableTypeOracle so = sob.build(logger); + + assertSerializableTypes(so, b, c); + assertInstantiable(so, b); + assertInstantiable(so, c); + assertNotInstantiableOrFieldSerializable(so, a.getRawType()); + } + private JClassType[] makeArray(JClassType... elements) { return elements; }