Revision: 9562
Author: [email protected]
Date: Tue Jan 18 08:32:58 2011
Log: Add support for mapping ConstraintViolation objects into
SimpleBeanEditor.
Patch by: bobv
Review by: rjrjr
Review at http://gwt-code-reviews.appspot.com/1260801
http://code.google.com/p/google-web-toolkit/source/detail?r=9562
Added:
/trunk/user/src/com/google/gwt/editor/client/impl/SimpleViolation.java
Modified:
/trunk/build.xml
/trunk/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiCompatibilityChecker.java
/trunk/user/src/com/google/gwt/editor/Editor.gwt.xml
/trunk/user/src/com/google/gwt/editor/client/SimpleBeanEditorDriver.java
/trunk/user/src/com/google/gwt/editor/client/impl/AbstractSimpleBeanEditorDriver.java
/trunk/user/src/com/google/gwt/editor/client/testing/MockSimpleBeanEditorDriver.java
/trunk/user/src/com/google/gwt/requestfactory/client/RequestFactoryEditorDriver.java
/trunk/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequestFactoryEditorDriver.java
/trunk/user/src/com/google/gwt/requestfactory/client/testing/MockRequestFactoryEditorDriver.java
/trunk/user/test/com/google/gwt/editor/rebind/model/EditorModelTest.java
=======================================
--- /dev/null
+++ /trunk/user/src/com/google/gwt/editor/client/impl/SimpleViolation.java
Tue Jan 18 08:32:58 2011
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2011 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.editor.client.impl;
+
+import java.util.Iterator;
+import java.util.List;
+
+import javax.validation.ConstraintViolation;
+
+/**
+ * Abstraction of a ConstraintViolation or a RequestFactory Violation
object.
+ * Also contains a factory method to create SimpleViolation instances from
+ * {@link ConstraintViolation} objects.
+ */
+public abstract class SimpleViolation {
+ /**
+ * Provides a source of SimpleViolation objects based on
ConstraintViolations.
+ * This is re-used by the RequestFactoryEditorDriver implementation,
which
+ * does not share a type hierarchy with the SimpleBeanEditorDriver.
+ */
+ static class ConstraintViolationIterable implements
Iterable<SimpleViolation> {
+
+ private final Iterable<ConstraintViolation<?>> violations;
+
+ public ConstraintViolationIterable(
+ Iterable<ConstraintViolation<?>> violations) {
+ this.violations = violations;
+ }
+
+ public Iterator<SimpleViolation> iterator() {
+ // Use a fresh source iterator each time
+ final Iterator<ConstraintViolation<?>> source =
violations.iterator();
+ return new Iterator<SimpleViolation>() {
+ public boolean hasNext() {
+ return source.hasNext();
+ }
+
+ public SimpleViolation next() {
+ return new SimpleViolationAdapter(source.next());
+ }
+
+ public void remove() {
+ source.remove();
+ }
+ };
+ }
+ }
+
+ /**
+ * Adapts the ConstraintViolation interface to the SimpleViolation
interface.
+ */
+ static class SimpleViolationAdapter extends SimpleViolation {
+ private final ConstraintViolation<?> v;
+
+ public SimpleViolationAdapter(ConstraintViolation<?> v) {
+ this.v = v;
+ }
+
+ public Object getKey() {
+ return v.getLeafBean();
+ }
+
+ public String getMessage() {
+ return v.getMessage();
+ }
+
+ public String getPath() {
+ /*
+ * TODO(bobv,nchalko): Determine the correct way to extract this
+ * information from the ConstraintViolation.
+ */
+ return v.getPropertyPath().toString();
+ }
+
+ public Object getUserDataObject() {
+ return v;
+ }
+ }
+
+ public static Iterable<SimpleViolation> iterableFromConstrantViolations(
+ Iterable<ConstraintViolation<?>> violations) {
+ return new ConstraintViolationIterable(violations);
+ }
+
+ /**
+ * Maps an abstract representation of a violation into the appropriate
+ * EditorDelegate.
+ */
+ public static void pushViolations(Iterable<SimpleViolation> violations,
+ DelegateMap delegateMap) {
+ // For each violation
+ for (SimpleViolation error : violations) {
+ Object key = error.getKey();
+ List<AbstractEditorDelegate<?, ?>> delegateList =
delegateMap.get(key);
+ if (delegateList != null) {
+
+ // For each delegate editing some record...
+ for (AbstractEditorDelegate<?, ?> baseDelegate : delegateList) {
+
+ // compute its base path in the hierarchy...
+ String basePath = baseDelegate.getPath();
+
+ // and the absolute path of the leaf editor receiving the error.
+ String absolutePath = (basePath.length() > 0 ? basePath
+ "." : "")
+ + error.getPath();
+
+ // Find the leaf editor's delegate.
+ List<AbstractEditorDelegate<?, ?>> leafDelegates =
delegateMap.getPath(absolutePath);
+ if (leafDelegates != null) {
+ // Only attach the error to the first delegate in a co-editor
chain.
+ leafDelegates.get(0).recordError(error.getMessage(), null,
+ error.getUserDataObject());
+ } else {
+ // No EditorDelegate to attach it to, stick it on the base.
+ baseDelegate.recordError(error.getMessage(), null,
+ error.getUserDataObject(), error.getPath());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Typically constructed via factory methods.
+ */
+ protected SimpleViolation() {
+ }
+
+ /**
+ * Return the object that the violation is about.
+ */
+ public abstract Object getKey();
+
+ /**
+ * Return a user-facing message describing the violation.
+ */
+ public abstract String getMessage();
+
+ /**
+ * Return a dotted path describing the property.
+ */
+ public abstract String getPath();
+
+ /**
+ * An object that should be available from
+ * {@link com.google.gwt.editor.client.EditorError#getUserData()}.
+ */
+ public abstract Object getUserDataObject();
+}
=======================================
--- /trunk/build.xml Tue Nov 9 15:11:07 2010
+++ /trunk/build.xml Tue Jan 18 08:32:58 2011
@@ -184,6 +184,9 @@
<arg file="${gwt.apicheck.config}"/>
<arg value="-logLevel"/>
<arg value="ERROR"/>
+ <!-- Needed for checking types that include validation APIs -->
+ <arg value="-validationSourceJars" />
+ <arg
path="${gwt.tools.lib}/javax/validation/validation-api-1.0.0.GA-sources.jar"
/>
</java>
</target>
=======================================
---
/trunk/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiCompatibilityChecker.java
Tue Nov 9 15:11:07 2010
+++
/trunk/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiCompatibilityChecker.java
Tue Jan 18 08:32:58 2011
@@ -67,19 +67,19 @@
* <p>
* An {@code ApiContainer} object is a list of {@link ApiPackage} objects.
* {@code ApiPackage} objects themselves are list of {@link ApiClass}
objects.
- * {@code ApiClass} objects contain list of {@code ApiConstructor}, {@code
- * ApiMethod}, and {@code JField} objects.
+ * {@code ApiClass} objects contain list of {@code ApiConstructor},
+ * {@code ApiMethod}, and {@code JField} objects.
* </p>
*
* <p>
* Each {@code ApiDiffGenerator} object computes the list of intersecting
and
- * missing {@link ApiPackageDiffGenerator} objects. Each {@code
- * ApiPackageDiffGenerator} object in turn computes the list of
intersecting and
- * missing {@link ApiClassDiffGenerator} objects. Each {@code
- * ApiClassDiffGenerator} object in turn computes the list of intersecting
and
- * missing API members. The members are represented by {@link
ApiConstructor}
- * for constructors, {@link ApiMethod} for methods, and {@link ApiField}
for
- * fields.
+ * missing {@link ApiPackageDiffGenerator} objects. Each
+ * {@code ApiPackageDiffGenerator} object in turn computes the list of
+ * intersecting and missing {@link ApiClassDiffGenerator} objects. Each
+ * {@code ApiClassDiffGenerator} object in turn computes the list of
+ * intersecting and missing API members. The members are represented by
+ * {@link ApiConstructor} for constructors, {@link ApiMethod} for methods,
and
+ * {@link ApiField} for fields.
* </p>
*
* <p>
@@ -174,65 +174,6 @@
return false;
}
}
-
- /**
- * Abstract internal class that specifies a set of {@link
CompilationUnit}.
- */
- private abstract static class Resources {
- protected final TreeLogger logger;
-
- Resources(TreeLogger logger) {
- this.logger = logger;
- }
-
- public abstract Set<Resource> getResources() throws NotFoundException,
- IOException, UnableToCompleteException;
-
- // TODO (amitmanjhi): remove this code. use TypeOracle functionality
- // instead.
- protected String extractPackageName(Reader reader) throws IOException {
- BufferedReader br = new BufferedReader(reader);
- String str = null;
- while ((str = br.readLine()) != null) {
- if (str.indexOf("package") != 0) {
- continue;
- }
- String parts[] = str.split("[\b\t\n\r ]+");
- if ((parts.length == 2) && parts[0].equals("package")) {
- return parts[1].substring(0, parts[1].length() - 1); // the ;
char
- }
- }
- return null;
- }
-
- protected File getFileFromName(String tag, String pathName)
- throws FileNotFoundException {
- File file = new File(pathName);
- if (!file.exists()) {
- throw new FileNotFoundException(tag + "file " + pathName + " not
found");
- }
- return file;
- }
-
- // TODO (amitmanjhi): remove this code. use TypeOracle functionality
- // instead.
- protected boolean isValidPackage(String packageName, String filePath) {
- logger.log(TreeLogger.SPAM, "packageName = " + packageName
- + ", filePath = " + filePath, null);
- if (packageName == null) {
- return false;
- }
- int lastSlashPosition = filePath.lastIndexOf("/");
- if (lastSlashPosition == -1) {
- return false;
- }
- String dirPath = filePath.substring(0, lastSlashPosition);
- String packageNameAsPath = packageName.replace('.', '/');
- logger.log(TreeLogger.SPAM, "packageNameAsPath " + packageNameAsPath
- + ", dirPath = " + dirPath, null);
- return dirPath.endsWith(packageNameAsPath);
- }
- }
/**
* Class that specifies a set of {@link CompilationUnit} read from jar
files.
@@ -347,6 +288,65 @@
return false;
}
}
+
+ /**
+ * Abstract internal class that specifies a set of {@link
CompilationUnit}.
+ */
+ private abstract static class Resources {
+ protected final TreeLogger logger;
+
+ Resources(TreeLogger logger) {
+ this.logger = logger;
+ }
+
+ public abstract Set<Resource> getResources() throws NotFoundException,
+ IOException, UnableToCompleteException;
+
+ // TODO (amitmanjhi): remove this code. use TypeOracle functionality
+ // instead.
+ protected String extractPackageName(Reader reader) throws IOException {
+ BufferedReader br = new BufferedReader(reader);
+ String str = null;
+ while ((str = br.readLine()) != null) {
+ if (str.indexOf("package") != 0) {
+ continue;
+ }
+ String parts[] = str.split("[\b\t\n\r ]+");
+ if ((parts.length == 2) && parts[0].equals("package")) {
+ return parts[1].substring(0, parts[1].length() - 1); // the ;
char
+ }
+ }
+ return null;
+ }
+
+ protected File getFileFromName(String tag, String pathName)
+ throws FileNotFoundException {
+ File file = new File(pathName);
+ if (!file.exists()) {
+ throw new FileNotFoundException(tag + "file " + pathName + " not
found");
+ }
+ return file;
+ }
+
+ // TODO (amitmanjhi): remove this code. use TypeOracle functionality
+ // instead.
+ protected boolean isValidPackage(String packageName, String filePath) {
+ logger.log(TreeLogger.SPAM, "packageName = " + packageName
+ + ", filePath = " + filePath, null);
+ if (packageName == null) {
+ return false;
+ }
+ int lastSlashPosition = filePath.lastIndexOf("/");
+ if (lastSlashPosition == -1) {
+ return false;
+ }
+ String dirPath = filePath.substring(0, lastSlashPosition);
+ String packageNameAsPath = packageName.replace('.', '/');
+ logger.log(TreeLogger.SPAM, "packageNameAsPath " + packageNameAsPath
+ + ", dirPath = " + dirPath, null);
+ return dirPath.endsWith(packageNameAsPath);
+ }
+ }
/**
* Class that specifies a set of {@link CompilationUnit} read from the
@@ -438,8 +438,9 @@
fileName.length() - 5);
String pkgName = extractPackageName(new FileReader(file));
if (pkgName == null) {
- logger.log(TreeLogger.WARN, "Not adding file = "
- + file.toString() + ", because packageName = null",
null);
+ logger.log(TreeLogger.WARN,
+ "Not adding file = " + file.toString()
+ + ", because packageName = null", null);
} else {
if (isValidPackage(pkgName,
sourcePathEntry.toURI().toURL().toString())) {
@@ -520,10 +521,12 @@
AbstractTreeLogger logger = new PrintWriterTreeLogger();
logger.setMaxDetail(checker.type);
- logger.log(TreeLogger.INFO, "gwtDevJar = " + checker.gwtDevJar
- + ", userJar = " + checker.gwtUserJar + ", refjars = "
- + Arrays.toString(checker.refJars) + ", logLevel = " +
checker.type
- + ", printAllApi = " + checker.printAllApi, null);
+ logger.log(
+ TreeLogger.INFO,
+ "gwtDevJar = " + checker.gwtDevJar + ", userJar = "
+ + checker.gwtUserJar + ", refjars = "
+ + Arrays.toString(checker.refJars) + ", logLevel = "
+ + checker.type + ", printAllApi = " + checker.printAllApi,
null);
Set<String> excludedPackages =
checker.getSetOfExcludedPackages(checker.configProperties);
if (PROCESS_NEW_API) {
@@ -532,6 +535,7 @@
checker.configProperties.getProperty("dirRoot_new"),
checker.getConfigPropertyAsSet("sourceFiles_new"),
checker.getConfigPropertyAsSet("excludedFiles_new"),
logger).getResources());
+
resources.addAll(checker.getJavaxValidationCompilationUnits(logger));
resources.addAll(checker.getGwtCompilationUnits(logger));
newApi = new ApiContainer(
checker.configProperties.getProperty("name_new"), resources,
@@ -552,6 +556,7 @@
checker.getConfigPropertyAsSet("sourceFiles_old"),
checker.getConfigPropertyAsSet("excludedFiles_old"),
logger).getResources());
}
+
resources.addAll(checker.getJavaxValidationCompilationUnits(logger));
resources.addAll(checker.getGwtCompilationUnits(logger));
existingApi = new ApiContainer(
checker.configProperties.getProperty("name_old"), resources,
@@ -631,7 +636,7 @@
}
private Properties configProperties;
-
+ private JarFile[] extraSourceJars;
private JarFile gwtDevJar;
private JarFile gwtUserJar;
@@ -816,6 +821,35 @@
setPropertiesAndWhitelist(str);
return configProperties != null && whiteList != null;
}
+ });
+
+ registerHandler(new ArgHandlerString() {
+
+ @Override
+ public String getPurpose() {
+ return "The location of the javax.validation sources";
+ }
+
+ @Override
+ public String getTag() {
+ return "-validationSourceJars";
+ }
+
+ @Override
+ public String[] getTagArgs() {
+ return new String[] {"jar1.jar:jar2.jar"};
+ }
+
+ @Override
+ public boolean setString(String str) {
+ boolean success = true;
+ String[] parts = str.split(System.getProperty("path.separator"));
+ extraSourceJars = new JarFile[parts.length];
+ for (int i = 0, j = parts.length; i < j; i++) {
+ extraSourceJars[i] = getJarFromString(parts[i]);
+ }
+ return success;
+ }
});
}
@@ -940,6 +974,25 @@
resources.addAll(cu.getResources());
return resources;
}
+
+ /**
+ * This is a hack to make the ApiChecker able to find the
javax.validation
+ * sources, which we include through an external jar file.
+ */
+ private Set<Resource> getJavaxValidationCompilationUnits(TreeLogger
logger)
+ throws UnableToCompleteException, NotFoundException, IOException {
+ Set<Resource> resources = new HashSet<Resource>();
+ if (extraSourceJars != null) {
+ Resources extra = new JarFileResources(extraSourceJars,
+ Collections.singleton(""), new HashSet<String>(Arrays.asList(
+ "javax/validation/Validation.java",
+ "javax/validation/constraints/Pattern.java")), logger);
+ Set<Resource> loaded = extra.getResources();
+ System.out.println("Found " + loaded.size() + " new resources");
+ resources.addAll(loaded);
+ }
+ return resources;
+ }
/*
* excludedPackages is used in only one place: to determine whether some
class
=======================================
--- /trunk/user/src/com/google/gwt/editor/Editor.gwt.xml Fri Nov 5
04:25:19 2010
+++ /trunk/user/src/com/google/gwt/editor/Editor.gwt.xml Tue Jan 18
08:32:58 2011
@@ -14,6 +14,7 @@
<!-- Editor framework support -->
<module>
<inherits name="com.google.gwt.core.Core" />
+ <inherits name="com.google.gwt.validation.Validation" />
<source path="client"/>
<source path="shared"/>
=======================================
---
/trunk/user/src/com/google/gwt/editor/client/SimpleBeanEditorDriver.java
Thu Oct 14 09:50:44 2010
+++
/trunk/user/src/com/google/gwt/editor/client/SimpleBeanEditorDriver.java
Tue Jan 18 08:32:58 2011
@@ -17,6 +17,8 @@
import java.util.List;
+import javax.validation.ConstraintViolation;
+
/**
* Automates editing of simple bean-like objects. The {@link
EditorDelegate}
* provided from this driver has a no-op implementation of
@@ -34,6 +36,9 @@
* instance.flush();
* }
* </pre>
+ * <p>
+ * Note that this interface is intended to be implemented by generated
code and
+ * is subject to API expansion in the future.
*
* @param <T> the type being edited
* @param <E> the Editor for the type
@@ -77,4 +82,17 @@
* @param editor the Editor to populate
*/
void initialize(E editor);
-}
+
+ /**
+ * Show {@link ConstraintViolation ConstraintViolations} generated
through a
+ * {@link javax.validation.Validator Validator}. The violations will be
+ * converted into {@link EditorError} objects whose
+ * {@link EditorError#getUserData() getUserData()} method can be used to
+ * access the original ConstraintViolation object.
+ *
+ * @param violations an Iterable over {@link ConstraintViolation}
instances
+ * @return <code>true</code> if there were any unconsumed EditorErrors
which
+ * can be retrieved from {@link #getErrors()}
+ */
+ boolean setConstraintViolations(Iterable<ConstraintViolation<?>>
violations);
+}
=======================================
---
/trunk/user/src/com/google/gwt/editor/client/impl/AbstractSimpleBeanEditorDriver.java
Wed Sep 15 02:26:39 2010
+++
/trunk/user/src/com/google/gwt/editor/client/impl/AbstractSimpleBeanEditorDriver.java
Tue Jan 18 08:32:58 2011
@@ -22,6 +22,8 @@
import java.util.ArrayList;
import java.util.List;
+import javax.validation.ConstraintViolation;
+
/**
* A base implementation class for generated SimpleBeanEditorDriver
* implementations.
@@ -64,6 +66,19 @@
public void initialize(E editor) {
this.editor = editor;
}
+
+ public boolean setConstraintViolations(
+ final Iterable<ConstraintViolation<?>> violations) {
+ checkDelegate();
+ SimpleViolation.pushViolations(
+ SimpleViolation.iterableFromConstrantViolations(violations),
+ delegateMap);
+
+ // Flush the errors, which will take care of co-editor chains.
+ errors = new ArrayList<EditorError>();
+ delegate.flushErrors(errors);
+ return hasErrors();
+ }
protected abstract SimpleBeanEditorDelegate<T, E> createDelegate();
=======================================
---
/trunk/user/src/com/google/gwt/editor/client/testing/MockSimpleBeanEditorDriver.java
Mon Sep 13 09:30:34 2010
+++
/trunk/user/src/com/google/gwt/editor/client/testing/MockSimpleBeanEditorDriver.java
Tue Jan 18 08:32:58 2011
@@ -22,6 +22,8 @@
import java.util.Collections;
import java.util.List;
+import javax.validation.ConstraintViolation;
+
/**
* A no-op implementation of {@link SimpleBeanEditorDriver} that records
its
* inputs.
@@ -84,4 +86,12 @@
public void initialize(E editor) {
this.editor = editor;
}
-}
+
+ /**
+ * A no-op method that always returns false.
+ */
+ public boolean setConstraintViolations(
+ Iterable<ConstraintViolation<?>> violations) {
+ return false;
+ }
+}
=======================================
---
/trunk/user/src/com/google/gwt/requestfactory/client/RequestFactoryEditorDriver.java
Thu Oct 14 18:28:29 2010
+++
/trunk/user/src/com/google/gwt/requestfactory/client/RequestFactoryEditorDriver.java
Tue Jan 18 08:32:58 2011
@@ -24,6 +24,8 @@
import java.util.List;
+import javax.validation.ConstraintViolation;
+
/**
* The interface that links RequestFactory and the Editor framework
together.
* Used for configuration and lifecycle control. Expected that this will be
@@ -42,17 +44,18 @@
* instance.flush().fire(new Receiver {...});
* }
* </pre>
- *
- * <p> See {@code
com.google.gwt.requestfactory.client.testing.MockRequestFactoryEditorDriver}
+ *
+ * <p>
*
* @param <P> the type of Proxy being edited
* @param <E> the type of Editor that will edit the Record
+ * @see {@link
com.google.gwt.requestfactory.client.testing.MockRequestFactoryEditorDriver}
*/
public interface RequestFactoryEditorDriver<P, E extends Editor<? super
P>> {
/**
* Start driving the Editor and its sub-editors with data for
display-only
* mode.
- *
+ *
* @param proxy a Proxy of type P
*/
void display(P proxy);
@@ -60,8 +63,8 @@
/**
* Start driving the Editor and its sub-editors with data. A
* {@link RequestContext} is required to provide context for the changes
to
- * the proxy (see {@link RequestContext#edit}. Note that this driver will
- * not fire the request.
+ * the proxy (see {@link RequestContext#edit}. Note that this driver
will not
+ * fire the request.
*
* @param proxy the proxy to be edited
* @param request the request context that will accumulate edits and is
@@ -83,21 +86,21 @@
/**
* Returns any unconsumed {@link EditorError EditorErrors} from the last
call
* to {@link #flush()}.
- *
+ *
* @return a List of {@link EditorError} instances
*/
List<EditorError> getErrors();
/**
* Returns a new array containing the request paths.
- *
+ *
* @return an array of Strings
*/
String[] getPaths();
/**
* Indicates if the last call to {@link #flush()} resulted in any errors.
- *
+ *
* @return {@code} true if errors are present
*/
boolean hasErrors();
@@ -105,7 +108,7 @@
/**
* Overload of {@link #initialize(RequestFactory, Editor)} to allow a
modified
* {@link EventBus} to be monitored for subscription services.
- *
+ *
* @param eventBus the {@link EventBus}
* @param requestFactory a {@link RequestFactory} instance
* @param editor an {@link Editor} of type E
@@ -118,7 +121,7 @@
/**
* Initializes a driver with the editor it will run, and a
RequestFactory to
* use for subscription services.
- *
+ *
* @param requestFactory a {@link RequestFactory} instance
* @param editor an {@link Editor} of type E
*
@@ -130,21 +133,33 @@
* Initializes a driver that will not be able to support subscriptions.
Calls
* to {@link com.google.gwt.editor.client.EditorDelegate#subscribe()}
will do
* nothing.
- *
+ *
* @param editor an {@link Editor} of type E
*/
void initialize(E editor);
+ /**
+ * Show {@link ConstraintViolation ConstraintViolations} generated
through a
+ * JSR 303 Validator. The violations will be converted into
+ * {@link EditorError} objects whose {@link EditorError#getUserData()
+ * getUserData()} method can be used to access the original
+ * ConstraintViolation object.
+ *
+ * @param violations an Iterable over {@link ConstraintViolation}
instances
+ * @return <code>true</code> if there were any unconsumed EditorErrors
which
+ * can be retrieved from {@link #getErrors()}
+ */
+ boolean setConstraintViolations(Iterable<ConstraintViolation<?>>
violations);
+
/**
* Show Violations returned from an attempt to submit a request. The
* violations will be converted into {@link EditorError} objects whose
* {@link EditorError#getUserData() getUserData()} method can be used to
* access the original Violation object.
- *
- * @param errors a Iterable over {@link Violation} instances
*
+ * @param violations an Iterable over {@link Violation} instances
* @return <code>true</code> if there were any unconsumed EditorErrors
which
* can be retrieved from {@link #getErrors()}
*/
- boolean setViolations(Iterable<Violation> errors);
-}
+ boolean setViolations(Iterable<Violation> violations);
+}
=======================================
---
/trunk/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequestFactoryEditorDriver.java
Thu Nov 18 13:15:58 2010
+++
/trunk/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequestFactoryEditorDriver.java
Tue Jan 18 08:32:58 2011
@@ -17,8 +17,8 @@
import com.google.gwt.editor.client.Editor;
import com.google.gwt.editor.client.EditorError;
-import com.google.gwt.editor.client.impl.AbstractEditorDelegate;
import com.google.gwt.editor.client.impl.DelegateMap;
+import com.google.gwt.editor.client.impl.SimpleViolation;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.requestfactory.client.RequestFactoryEditorDriver;
import com.google.gwt.requestfactory.shared.EntityProxy;
@@ -28,8 +28,11 @@
import com.google.gwt.requestfactory.shared.Violation;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
+import javax.validation.ConstraintViolation;
+
/**
* Contains utility methods for top-level driver implementations.
*
@@ -38,6 +41,66 @@
*/
public abstract class AbstractRequestFactoryEditorDriver<R, E extends
Editor<R>>
implements RequestFactoryEditorDriver<R, E> {
+
+ /**
+ * Adapts a RequestFactory Violation object to the SimpleViolation
interface.
+ */
+ static class SimpleViolationAdapter extends SimpleViolation {
+ private final Violation v;
+
+ /**
+ * @param source
+ */
+ private SimpleViolationAdapter(Violation v) {
+ this.v = v;
+ }
+
+ public Object getKey() {
+ return v.getOriginalProxy();
+ }
+
+ public String getMessage() {
+ return v.getMessage();
+ }
+
+ public String getPath() {
+ return v.getPath();
+ }
+
+ public Object getUserDataObject() {
+ return v;
+ }
+ }
+
+ /**
+ * Provides a source of SimpleViolation objects based on RequestFactory's
+ * simplified Violation interface.
+ */
+ static class ViolationIterable implements Iterable<SimpleViolation> {
+
+ private final Iterable<Violation> violations;
+
+ public ViolationIterable(Iterable<Violation> violations) {
+ this.violations = violations;
+ }
+
+ public Iterator<SimpleViolation> iterator() {
+ final Iterator<Violation> source = violations.iterator();
+ return new Iterator<SimpleViolation>() {
+ public boolean hasNext() {
+ return source.hasNext();
+ }
+
+ public SimpleViolation next() {
+ return new SimpleViolationAdapter(source.next());
+ }
+
+ public void remove() {
+ source.remove();
+ }
+ };
+ }
+ }
/**
* Since the ValueProxy is being mutated in-place, we need a way to
stabilize
@@ -130,51 +193,13 @@
initialize(requestFactory.getEventBus(), requestFactory, editor);
}
- public boolean setViolations(Iterable<Violation> violations) {
- checkDelegate();
-
- // For each violation
- for (Violation error : violations) {
-
- /*
- * Find the delegates that are attached to the object. Use getRaw()
here
- * since the violation doesn't include an EntityProxy reference
- */
- Object key = error.getInvalidProxy();
- // Object key = error.getOriginalProxy();
- // if (key == null) {
- // }
- List<AbstractEditorDelegate<?, ?>> delegateList =
delegateMap.get(key);
- if (delegateList != null) {
-
- // For each delegate editing some record...
- for (AbstractEditorDelegate<?, ?> baseDelegate : delegateList) {
-
- // compute its base path in the hierarchy...
- String basePath = baseDelegate.getPath();
-
- // and the absolute path of the leaf editor receiving the error.
- String absolutePath = (basePath.length() > 0 ? basePath
+ "." : "")
- + error.getPath();
-
- // Find the leaf editor's delegate.
- List<AbstractEditorDelegate<?, ?>> leafDelegates =
delegateMap.getPath(absolutePath);
- if (leafDelegates != null) {
- // Only attach the error to the first delegate in a co-editor
chain.
- leafDelegates.get(0).recordError(error.getMessage(), null,
error);
- } else {
- // No EditorDelegate to attach it to, stick it on the base.
- baseDelegate.recordError(error.getMessage(), null, error,
- error.getPath());
- }
- }
- }
- }
-
- // Flush the errors, which will take care of co-editor chains.
- errors = new ArrayList<EditorError>();
- delegate.flushErrors(errors);
- return !errors.isEmpty();
+ public boolean setConstraintViolations(
+ Iterable<ConstraintViolation<?>> violations) {
+ return
doSetViolations(SimpleViolation.iterableFromConstrantViolations(violations));
+ }
+
+ public boolean setViolations(Iterable<Violation> violations) {
+ return doSetViolations(new ViolationIterable(violations));
}
protected abstract RequestFactoryEditorDelegate<R, E> createDelegate();
@@ -211,4 +236,14 @@
traverseEditors(paths);
}
-}
+
+ private boolean doSetViolations(Iterable<SimpleViolation> violations) {
+ checkDelegate();
+ SimpleViolation.pushViolations(violations, delegateMap);
+
+ // Flush the errors, which will take care of co-editor chains.
+ errors = new ArrayList<EditorError>();
+ delegate.flushErrors(errors);
+ return hasErrors();
+ }
+}
=======================================
---
/trunk/user/src/com/google/gwt/requestfactory/client/testing/MockRequestFactoryEditorDriver.java
Tue Oct 5 17:59:14 2010
+++
/trunk/user/src/com/google/gwt/requestfactory/client/testing/MockRequestFactoryEditorDriver.java
Tue Jan 18 08:32:58 2011
@@ -26,6 +26,8 @@
import java.util.Collections;
import java.util.List;
+import javax.validation.ConstraintViolation;
+
/**
* A no-op implementation of {@link RequestFactoryEditorDriver} that
records its
* inputs.
@@ -138,6 +140,14 @@
public void initialize(RequestFactory requestFactory, E editor) {
this.initialize(requestFactory.getEventBus(), requestFactory, editor);
}
+
+ /**
+ * A no-op method that always returns false.
+ */
+ public boolean setConstraintViolations(
+ Iterable<ConstraintViolation<?>> violations) {
+ return false;
+ }
/**
* A no-op method that always returns false.
=======================================
---
/trunk/user/test/com/google/gwt/editor/rebind/model/EditorModelTest.java
Fri Jan 14 08:57:14 2011
+++
/trunk/user/test/com/google/gwt/editor/rebind/model/EditorModelTest.java
Tue Jan 18 08:32:58 2011
@@ -54,6 +54,8 @@
import java.util.HashSet;
import java.util.Set;
+import javax.validation.ConstraintViolation;
+
/**
* Test case for {@link EditorModel} that uses mock CompilationStates.
*/
@@ -881,8 +883,10 @@
Set<Resource> toReturn = new
HashSet<Resource>(Arrays.asList(javaFiles));
toReturn.addAll(Arrays.asList(new Resource[] {
new RealJavaResource(CompositeEditor.class),
+ new EmptyMockJavaResource(ConstraintViolation.class),
new RealJavaResource(Editor.class),
new RealJavaResource(EditorError.class),
+ new EmptyMockJavaResource(EntityProxy.class),
new EmptyMockJavaResource(EventBus.class),
new EmptyMockJavaResource(HasEditorDelegate.class),
new EmptyMockJavaResource(HasEditorErrors.class),
@@ -890,7 +894,6 @@
new RealJavaResource(IsEditor.class),
new EmptyMockJavaResource(Iterable.class),
new RealJavaResource(LeafValueEditor.class),
- new EmptyMockJavaResource(EntityProxy.class),
new EmptyMockJavaResource(RequestFactory.class),
new RealJavaResource(RequestFactoryEditorDriver.class),
new EmptyMockJavaResource(Request.class),
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors