/*
* Copyright 2010 Google Inc. Copyright 2016 Manfred Tremmel
*
* 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 de.knightsoftnet.validators.rebind;
import de.knightsoftnet.validators.client.GwtValidation;
import de.knightsoftnet.validators.client.impl.GwtSpecificValidator;
import com.google.gwt.core.ext.Generator;
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.core.ext.typeinfo.JParameterizedType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import javax.validation.Validator;
/**
* Generates subclasses of {@link Validator} and {@link GwtSpecificValidator}. The generic validator
* only handles the classes listed in the {@link de.knightsoftnet.validators.client.GwtValidation}
* annotation. See {@link de.knightsoftnet.validators.client.GwtValidation} for usage.
*/
public final class ValidatorGenerator extends Generator {
private final BeanHelperCache cache;
private Class<?>[] validGroups;
/**
* constructor called by the compiler via reflection.
*/
public ValidatorGenerator() {
super();
this.cache = new BeanHelperCache();
this.validGroups = new Class<?>[] {};
}
/**
* constructor called from tests.
*
* @param cache bean helper cache
* @param validGroups validation groups
*/
public ValidatorGenerator(final BeanHelperCache cache, final Class<?>[] validGroups) {
super();
this.cache = cache;
this.validGroups = validGroups;
}
@Override
public String generate(final TreeLogger logger, final GeneratorContext context,
final String typeName) throws UnableToCompleteException {
final TypeOracle typeOracle = context.getTypeOracle();
assert typeOracle != null;
final JClassType validatorType = this.findType(logger, typeOracle, typeName);
final JClassType genericType = this.findType(logger, typeOracle, Validator.class.getName());
final JClassType gwtSpecificType =
this.findType(logger, typeOracle, GwtSpecificValidator.class.getName());
if (validatorType.isAssignableTo(genericType)) {
return this.generateGenericValidator(logger, context, validatorType);
} else if (validatorType.isAssignableTo(gwtSpecificType)) {
return this.generateGwtSpecificValidator(logger, context, validatorType);
} else {
logger.log(TreeLogger.ERROR,
"type is not a ValidatorGenerator or GwtSpecificValidatorGenerator: '" + typeName + "'",
null);
throw new UnableToCompleteException();
}
}
private JClassType findType(final TreeLogger logger, final TypeOracle typeOracle,
final String typeName) throws UnableToCompleteException {
final JClassType result = typeOracle.findType(typeName);
if (result == null) {
logger.log(TreeLogger.ERROR, "Unable to find metadata for type '" + typeName + "'", null);
throw new UnableToCompleteException();
}
return result;
}
private String generateGenericValidator(final TreeLogger logger, final GeneratorContext context,
final JClassType validatorType) throws UnableToCompleteException {
final String typeName = validatorType.getName();
final GwtValidation gwtValidation =
validatorType.findAnnotationInTypeHierarchy(GwtValidation.class);
if (gwtValidation == null) {
logger.log(TreeLogger.ERROR,
typeName + " must be anntotated with " + GwtValidation.class.getCanonicalName(), null);
throw new UnableToCompleteException();
}
if (gwtValidation.value().length == 0) {
logger.log(TreeLogger.ERROR, "The @" + GwtValidation.class.getSimpleName() + " of "
+ typeName + "must specify at least one bean type to validate.", null);
throw new UnableToCompleteException();
}
if (gwtValidation.groups().length == 0) {
logger.log(TreeLogger.ERROR, "The @" + GwtValidation.class.getSimpleName() + " of "
+ typeName + "must specify at least one validation group.", null);
throw new UnableToCompleteException();
}
this.validGroups = gwtValidation.groups();
final TreeLogger validatorLogger = logger.branch(TreeLogger.DEBUG,
"Generating Validator for '" + validatorType.getQualifiedSourceName() + "'", null);
final AbstractCreator creator =
new ValidatorCreator(validatorType, gwtValidation, validatorLogger, context, this.cache);
return creator.create();
}
private String generateGwtSpecificValidator(final TreeLogger logger,
final GeneratorContext context, final JClassType validatorType)
throws UnableToCompleteException {
final JClassType gwtSpecificInterface = this.getGwtSpecificValidator(logger, validatorType);
final JClassType beanType = this.getBeanType(logger, validatorType, gwtSpecificInterface);
final BeanHelper beanHelper = this.cache.createHelper(beanType, logger, context);
if (beanHelper == null) {
logger.log(TreeLogger.ERROR, "Unable to create BeanHelper for " + beanType + " "
+ GwtSpecificValidator.class.getSimpleName() + ".", null);
throw new UnableToCompleteException();
}
final AbstractCreator creator = new GwtSpecificValidatorCreator(validatorType, beanType,
beanHelper, logger, context, this.cache, this.validGroups);
return creator.create();
}
private JClassType getBeanType(final TreeLogger logger, final JClassType validator,
final JClassType gwtSpecificInterface) throws UnableToCompleteException {
if (gwtSpecificInterface instanceof JParameterizedType) {
final JParameterizedType paramType = (JParameterizedType) gwtSpecificInterface;
return paramType.getTypeArgs()[0];
}
logger.log(TreeLogger.ERROR,
validator.getQualifiedSourceName() + " must implement "
+ GwtSpecificValidator.class.getCanonicalName() + " with a one generic parameter.",
null);
throw new UnableToCompleteException();
}
private JClassType getGwtSpecificValidator(final TreeLogger logger, final JClassType validator)
throws UnableToCompleteException {
for (final JClassType interfaceType : validator.getImplementedInterfaces()) {
if (interfaceType.getQualifiedSourceName()
.endsWith(GwtSpecificValidator.class.getCanonicalName())) {
return interfaceType;
}
}
logger.log(TreeLogger.ERROR, validator.getQualifiedSourceName() + " must implement "
+ GwtSpecificValidator.class.getCanonicalName(), null);
throw new UnableToCompleteException();
}
}