/* * 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(); } } }