com.google.gwt.validation.rebind.BeanHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gwt.validation.rebind.BeanHelper.java

Source

/*
 * Copyright 2010 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.JArrayType;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JField;
import com.google.gwt.core.ext.typeinfo.JParameterizedType;
import com.google.gwt.core.ext.typeinfo.JRawType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.thirdparty.guava.common.base.Function;
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.Collections;
import java.util.HashMap;
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;

/**
 * <strong>EXPERIMENTAL</strong> and subject to change. Do not use this in
 * production code.
 * <p>
 * A simple struct for the various values associated with a Bean that can be
 * validated.
 */
public final class BeanHelper {

    public static final Function<BeanHelper, Class<?>> TO_CLAZZ = new Function<BeanHelper, Class<?>>() {
        public Class<?> apply(BeanHelper helper) {
            return helper.getClazz();
        }
    };

    private static final Validator serverSideValidator = Validation.buildDefaultValidatorFactory().getValidator();

    // stash the map in a ThreadLocal, since each GWT module lives in its own
    // thread in DevMode
    private static final ThreadLocal<Map<JClassType, BeanHelper>> threadLocalHelperMap = new ThreadLocal<Map<JClassType, BeanHelper>>() {
        @Override
        protected synchronized Map<JClassType, BeanHelper> initialValue() {
            return new HashMap<JClassType, BeanHelper>();
        }
    };

    /**
     * Visible for testing.
     */
    public static void clearBeanHelpersForTests() {
        threadLocalHelperMap.get().clear();
    }

    public static BeanHelper createBeanHelper(Class<?> clazz, TreeLogger logger, GeneratorContext context)
            throws UnableToCompleteException {
        JClassType beanType = context.getTypeOracle().findType(clazz.getCanonicalName());
        return createBeanHelper(clazz, beanType, logger, context);
    }

    public static Map<JClassType, BeanHelper> getBeanHelpers() {
        return Collections.unmodifiableMap(threadLocalHelperMap.get());
    }

    protected static BeanHelper createBeanHelper(JClassType jType, TreeLogger logger, GeneratorContext context)
            throws UnableToCompleteException {
        JClassType erasedType = jType.getErasedType();
        try {
            Class<?> clazz = Class.forName(erasedType.getQualifiedBinaryName());
            return createBeanHelper(clazz, erasedType, logger, context);
        } catch (ClassNotFoundException e) {
            logger.log(TreeLogger.ERROR, "Unable to create BeanHelper for " + erasedType, e);
            throw new UnableToCompleteException();
        }
    }

    protected static boolean isClassConstrained(Class<?> clazz) {
        return serverSideValidator.getConstraintsForClass(clazz).isBeanConstrained();
    }

    static BeanHelper getBeanHelper(JClassType beanType) {
        return getBeanHelpers().get(beanType.getErasedType());
    }

    /**
     * Write an Empty Interface implementing {@link GwtSpecificValidator} with
     * Generic parameter of the bean type.
     */
    static 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();
        }
    }

    private static synchronized void addBeanHelper(BeanHelper helper) {
        threadLocalHelperMap.get().put(helper.getJClass(), helper);
    }

    private static BeanHelper createBeanHelper(Class<?> clazz, JClassType beanType, TreeLogger logger,
            GeneratorContext context) throws UnableToCompleteException {
        BeanHelper helper = getBeanHelper(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);
            addBeanHelper(helper);
            writeInterface(context, logger, helper);

            // now recurse on all Cascaded elements
            for (PropertyDescriptor p : bean.getConstrainedProperties()) {
                if (p.isCascaded()) {
                    createBeanHelper(p, helper, logger, context);
                }
            }
        }
        return helper;
    }

    private static void createBeanHelper(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);

                createBeanHelper(type.getErasedType(), logger, context);
            }
            if (parent.hasGetter(p)) {
                JClassType type = parent.getAssociationType(p, false);

                createBeanHelper(type.getErasedType(), logger, context);
            }
        } else {
            if (serverSideValidator.getConstraintsForClass(elementClass).isBeanConstrained()) {
                createBeanHelper(elementClass, logger, context);
            }
        }
    }

    private final BeanDescriptor beanDescriptor;

    private final JClassType jClass;

    private Class<?> clazz;

    private BeanHelper(JClassType jClass, Class<?> clazz, BeanDescriptor beanDescriptor) {
        super();
        this.beanDescriptor = beanDescriptor;
        this.jClass = jClass;
        this.clazz = clazz;
    }

    public JClassType getAssociationType(PropertyDescriptor p, boolean useField) {
        JType type = this.getElementType(p, useField);
        JArrayType jArray = type.isArray();
        if (jArray != null) {
            return jArray.getComponentType().isClassOrInterface();
        }
        JParameterizedType pType = type.isParameterized();
        JClassType[] typeArgs;
        if (pType == null) {
            JRawType rType = type.isRawType();
            typeArgs = rType.getGenericType().getTypeParameters();
        } else {
            typeArgs = pType.getTypeArgs();
        }
        // it is either a Iterable or a Map use the last type arg.
        return typeArgs[typeArgs.length - 1].isClassOrInterface();
    }

    public BeanDescriptor getBeanDescriptor() {
        return beanDescriptor;
    }

    /*
     * The server side validator needs an actual class.
     */
    public Class<?> getClazz() {
        return clazz;
    }

    public String getDescriptorName() {

        return jClass.getName() + "Descriptor";
    }

    public String getFullyQualifiedValidatorName() {
        return getPackage() + "." + getValidatorName();
    }

    public JClassType getJClass() {
        return jClass;
    }

    public String getPackage() {
        return jClass.getPackage().getName();
    }

    public String getTypeCanonicalName() {
        return jClass.getQualifiedSourceName();
    }

    public String getValidatorInstanceName() {
        return getFullyQualifiedValidatorName() + ".INSTANCE";
    }

    public String getValidatorName() {
        return makeJavaSafe("_" + jClass.getName() + "Validator");
    }

    @Override
    public String toString() {
        return getTypeCanonicalName();
    }

    JType getElementType(PropertyDescriptor p, boolean useField) {
        if (useField) {
            return jClass.findField(p.getPropertyName()).getType();
        } else {
            return jClass.findMethod(GwtSpecificValidatorCreator.asGetter(p), GwtSpecificValidatorCreator.NO_ARGS)
                    .getReturnType();
        }
    }

    boolean hasField(PropertyDescriptor p) {
        JField field = jClass.findField(p.getPropertyName());
        return field != null;
    }

    boolean hasGetter(PropertyDescriptor p) {
        JType[] paramTypes = new JType[] {};
        try {
            jClass.getMethod(GwtSpecificValidatorCreator.asGetter(p), paramTypes);
            return true;
        } catch (NotFoundException e) {
            return false;
        }
    }

    private String makeJavaSafe(String in) {
        return in.replaceAll("\\.", "_");
    }

}