blob: 1db6b9c4bba7b43a002702cb0d71414ef77c9382 [file] [log] [blame]
/*
* Copyright 2012 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.validation.rebind;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.ext.GeneratorContext;
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.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.google.gwt.validation.client.impl.GwtSpecificValidator;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.validation.Validation;
import javax.validation.ValidationException;
import javax.validation.Validator;
import javax.validation.metadata.BeanDescriptor;
import javax.validation.metadata.PropertyDescriptor;
/**
* A cache and factory for BeanHelpers. There should be one BeanHelperCache per
* compilation run.
* <p>
* (public for tests)
*/
public class BeanHelperCache { // public for testing
private final Map<JClassType, BeanHelper> cache;
private final Validator serverSideValidator;
/**
* Creates a cache. There should be one cache per compiler run.
* (public for tests.)
*/
public BeanHelperCache() {
cache = new HashMap<JClassType, BeanHelper>();
serverSideValidator = Validation.buildDefaultValidatorFactory().getValidator();
}
/**
* Clears the cache.
* (Public for testing.)
*/
public void clear() {
cache.clear();
}
/**
* Creates a BeanHelper and writes an interface containing its instance. Also,
* recursively creates any BeanHelpers on its constrained properties.
* (Public for testing.)
*/
public BeanHelper createHelper(Class<?> clazz, TreeLogger logger,
GeneratorContext context) throws UnableToCompleteException {
JClassType beanType = context.getTypeOracle().findType(clazz.getCanonicalName());
return doCreateHelper(clazz, beanType, logger, context);
}
/**
* Creates a BeanHelper and writes an interface containing its instance. Also,
* recursively creates any BeanHelpers on its constrained properties.
*/
BeanHelper createHelper(JClassType jType, TreeLogger logger, GeneratorContext context)
throws UnableToCompleteException {
JClassType erasedType = jType.getErasedType();
try {
Class<?> clazz = Class.forName(erasedType.getQualifiedBinaryName());
return doCreateHelper(clazz, erasedType, logger, context);
} catch (ClassNotFoundException e) {
logger.log(TreeLogger.ERROR, "Unable to create BeanHelper for "
+ erasedType, e);
throw new UnableToCompleteException();
}
}
List<BeanHelper> getAllBeans() {
return Util.sortMostSpecificFirst(cache.values(), BeanHelper.TO_CLAZZ);
}
BeanHelper getBean(JClassType key) {
return cache.get(key);
}
boolean isClassConstrained(Class<?> clazz) {
return serverSideValidator.getConstraintsForClass(clazz).isBeanConstrained();
}
private BeanHelper doCreateHelper(Class<?> clazz,
JClassType beanType, TreeLogger logger, GeneratorContext context)
throws UnableToCompleteException {
BeanHelper helper = getBean(beanType);
if (helper == null) {
BeanDescriptor bean;
try {
bean = serverSideValidator.getConstraintsForClass(clazz);
} catch (ValidationException e) {
logger.log(TreeLogger.ERROR,
"Unable to create a validator for " + clazz.getCanonicalName()
+ " because " + e.getMessage(), e);
throw new UnableToCompleteException();
}
helper = new BeanHelper(beanType, clazz, bean);
cache.put(helper.getJClass(), helper);
writeInterface(context, logger, helper);
// now recurse on all Cascaded elements
for (PropertyDescriptor p : bean.getConstrainedProperties()) {
// TODO(idol) only bother creating objects for properties that have constrains in the groups
// specified in @GwtValidation, but not others
if (p.isCascaded()) {
doCreateHelperForProp(p, helper, logger, context);
}
}
}
return helper;
}
/**
* Creates the appropriate BeanHelper for a property on a bean.
*/
private void doCreateHelperForProp(PropertyDescriptor p, BeanHelper parent,
TreeLogger logger, GeneratorContext context)
throws UnableToCompleteException {
Class<?> elementClass = p.getElementClass();
if (GwtSpecificValidatorCreator.isIterableOrMap(elementClass)) {
if (parent.hasField(p)) {
JClassType type = parent.getAssociationType(p, true);
createHelper(type.getErasedType(), logger, context);
}
if (parent.hasGetter(p)) {
JClassType type = parent.getAssociationType(p, false);
createHelper(type.getErasedType(), logger, context);
}
} else {
if (serverSideValidator.getConstraintsForClass(elementClass).isBeanConstrained()) {
createHelper(elementClass, logger, context);
}
}
}
/**
* Write an Empty Interface implementing
* {@link com.google.gwt.validation.client.impl.GwtSpecificValidator} with
* Generic parameter of the bean type.
*/
private void writeInterface(GeneratorContext context, TreeLogger logger, BeanHelper bean) {
PrintWriter pw = context.tryCreate(logger, bean.getPackage(),
bean.getValidatorName());
if (pw != null) {
TreeLogger interfaceLogger = logger.branch(TreeLogger.TRACE,
"Creating the interface for " + bean.getFullyQualifiedValidatorName());
ClassSourceFileComposerFactory factory = new ClassSourceFileComposerFactory(
bean.getPackage(), bean.getValidatorName());
factory.addImplementedInterface(GwtSpecificValidator.class.getCanonicalName()
+ " <" + bean.getTypeCanonicalName() + ">");
factory.addImport(GWT.class.getCanonicalName());
factory.makeInterface();
SourceWriter sw = factory.createSourceWriter(context, pw);
// static MyValidator INSTANCE = GWT.create(MyValidator.class);
sw.print("static ");
sw.print(bean.getValidatorName());
sw.print(" INSTANCE = GWT.create(");
sw.print(bean.getValidatorName());
sw.println(".class);");
sw.commit(interfaceLogger);
pw.close();
}
}
}