Package org.gwtoolbox.bean.rebind.validation

Source Code of org.gwtoolbox.bean.rebind.validation.BeanValidatorGenerator

package org.gwtoolbox.bean.rebind.validation;

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.JType;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import org.gwtoolbox.bean.client.BeanInfo;
import org.gwtoolbox.bean.client.BeanInfoRegistry;
import org.gwtoolbox.bean.client.PropertyDescriptor;
import org.gwtoolbox.bean.client.validation.AbstractBeanValidator;
import org.gwtoolbox.bean.client.validation.BeanValidator;
import org.gwtoolbox.bean.client.validation.PathImpl;
import org.gwtoolbox.bean.client.validation.ViolationRegistry;
import org.gwtoolbox.bean.rebind.BeanGeneratorUtils;
import org.gwtoolbox.bean.rebind.BeanOracle;
import org.gwtoolbox.bean.rebind.BeanOracleBuilder;
import org.gwtoolbox.bean.rebind.JProperty;
import org.gwtoolbox.bean.rebind.validation.config.BeanValidationConfig;
import org.gwtoolbox.bean.rebind.validation.config.BeanValidationConfigHolder;
import org.gwtoolbox.commons.collections.client.Pair;
import org.gwtoolbox.commons.collections.client.attributes.Attributes;
import org.gwtoolbox.commons.generator.rebind.EasyTreeLogger;
import org.gwtoolbox.commons.generator.rebind.GeneratorUtils;

import javax.validation.ConstraintValidator;
import javax.validation.groups.Default;
import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.util.*;

import static org.gwtoolbox.commons.generator.rebind.GeneratorUtils.escapeForWriter;

/**
* @author Uri Boness
*/
public class BeanValidatorGenerator {

    private static int varNameCounter = 0;

    public static String generate(EasyTreeLogger logger, GeneratorContext context, JClassType beanType) throws UnableToCompleteException {
        String packageName = beanType.getPackage().getName();

        String validatorClassName = "gtx__" + beanType.getSimpleSourceName() + "Validator";
        String qualifiedValidatorClassName = packageName + "." + validatorClassName;
        SourceWriter sourceWriter = getSourceWriter(logger, context, packageName, validatorClassName, beanType.getQualifiedSourceName());
        if (sourceWriter == null) {
            return qualifiedValidatorClassName;
        }

        BeanOracle beanOracle = new BeanOracleBuilder(context.getTypeOracle()).build(logger, beanType);

        BeanValidationConfig config = BeanValidationConfigHolder.getConfig();
        ValidationOracle validationOracle = new ValidationOracle(beanOracle);

        write(logger, context, sourceWriter, validationOracle);

        sourceWriter.commit(logger);

        return qualifiedValidatorClassName;
    }


    private static SourceWriter getSourceWriter(TreeLogger logger, GeneratorContext context, String packageName, String validatorClassName,  String beanClassName) {

        PrintWriter printWriter = context.tryCreate(logger, packageName, validatorClassName);

        if (printWriter == null) {
            return null;
        }

        ClassSourceFileComposerFactory composerFactory = new ClassSourceFileComposerFactory(packageName, validatorClassName);

        //TODO add imports here
        composerFactory.addImport(BeanValidator.class.getName());
        composerFactory.addImport(AbstractBeanValidator.class.getName());
        composerFactory.addImport(ViolationRegistry.class.getName());
        composerFactory.addImport(ConstraintValidator.class.getName());
        composerFactory.addImport(PropertyDescriptor.class.getName());
        composerFactory.addImport(BeanInfo.class.getName());
        composerFactory.addImport(BeanInfoRegistry.class.getName());
        composerFactory.addImport(ViolationRegistry.class.getName());
        composerFactory.addImport(Set.class.getName());
        composerFactory.addImport(HashSet.class.getName());
        composerFactory.addImport(Map.class.getName());
        composerFactory.addImport(Map.class.getName() + ".Entry");
        composerFactory.addImport(List.class.getName());
        composerFactory.addImport(ArrayList.class.getName());
        composerFactory.addImport(HashMap.class.getName());
        composerFactory.addImport(Default.class.getName());
        composerFactory.addImport(PathImpl.class.getName());
        composerFactory.addImport(PathImpl.class.getName() + ".NodeImpl");
        composerFactory.addImport(Collection.class.getName());

        composerFactory.setSuperclass(AbstractBeanValidator.class.getName() + "<" + beanClassName + ">");


        return composerFactory.createSourceWriter(context, printWriter);
    }

    private static void write(EasyTreeLogger logger, GeneratorContext context, SourceWriter writer, ValidationOracle oracle) throws UnableToCompleteException {

        writeAbstractMethodsImplementation(logger, context, writer, oracle);

        for (JProperty property : oracle.getBeanOracle().getProperties()) {
            if (oracle.isValidated(property.getName())) {
                writePropertyValidateMethod(logger, context, writer, oracle, property);
            }
        }
    }

    private static void writeAbstractMethodsImplementation(EasyTreeLogger logger, GeneratorContext context, SourceWriter writer, ValidationOracle oracle) throws UnableToCompleteException {
        String beanClassName = oracle.getBeanOracle().getType().getQualifiedSourceName();

        writer.println("public void validate(ViolationRegistry registry, " + beanClassName + " bean, Set<Class> groups) {");
        for (JProperty property : oracle.getBeanOracle().getProperties()) {
            if (oracle.isValidated(property.getName())) {
                writer.println("    validate_" + property.getName() + "(registry, bean, groups);");
            }
        }
        //TODO currently we only validate the class level constraint iff there are no violations on the properties level.
        if (oracle.hasClassLevelConstraints()) {
            writer.println("if (!registry.hasViolations()) {");
            writer.println("    validateAtClassLevel(registry, bean, groups);");
            writer.println("}");
        }
        writer.println("}");

        if (oracle.hasClassLevelConstraints()) {
            writeClassLevelValidationMethod(logger, context, writer, oracle);
        }

        writer.println("public void validateProperty(ViolationRegistry registry, " + beanClassName + " bean, String propertyName, Set<Class> groups) {");
        if (oracle.hasValidatedProperties()) {
            for (JProperty property : oracle.getBeanOracle().getProperties()) {
                if (oracle.isValidated(property.getName())) {
                    writer.println("    if (propertyName.equals(\"" + property.getName() + "\")) {");
                    writer.println("        validate_" + property.getName() + "(registry, bean, groups);");
                    writer.println("        return;");
                    writer.println("    }");
                }
            }
        }
        writer.println("}");

        writer.println("public List<Set<Class>> computeGroupSequence(Set<Class> groups) {");
        writer.println("    Set<Class> expandedGroups = new HashSet<Class>();");
        writer.println("    for (Class group : groups) {");
        writer.println("        Set<Class> groupSet = getGroupSet(group);");
        writer.println("        expandedGroups.addAll(groupSet);");
        writer.println("    }");
        writer.println("    List<Set<Class>> sequence = new ArrayList<Set<Class>>();");
        writer.println("    sequence.add(expandedGroups);");
        writer.println("    return sequence;");
        writer.println("}");

        writer.println("private Set<Class> getGroupSet(Class group) {");
        writer.println("    Set<Class> groupSet = new HashSet<Class>();");
        writer.println("    groupSet.add(group);");
        for (Class superGroup : GroupUtils.getSuperGroups()) {
            Set<Class> groupSet = GroupUtils.getGroupSet(superGroup);
            writer.println("    if (group == " + superGroup.getName() + ".class) {");
            for (Class subGroup : groupSet) {
                writer.println("        groupSet.add(" + subGroup.getName() + ".class);");
            }
            writer.println("        return groupSet;");
            writer.println("    }");
        }
        writer.println("    return groupSet;");
        writer.println("}");

        writer.println();
        writer.println("public static String replaceParameters(String template, Map<String, Object> attributes) {");
        writer.println("    for (Map.Entry<String, Object> entry : attributes.entrySet()) {");
        writer.println("        template = template.replace(\"{\" + entry.getKey() + \"}\", String.valueOf(entry.getValue()));");
        writer.println("    }");
        writer.println("    return template;");
        writer.println("}");

    }

    private static void writeClassLevelValidationMethod(EasyTreeLogger logger, GeneratorContext context, SourceWriter writer, ValidationOracle oracle) throws UnableToCompleteException {
        String beanTypeName = oracle.getBeanOracle().getType().getQualifiedSourceName();
        writer.println("private void validateAtClassLevel(ViolationRegistry registry, " + beanTypeName + " bean, Set<Class> groups) {");

        List<ConstraintDefinition> definitions = oracle.getClassLevelConstraints();

        int i = 0;
        for (ConstraintDefinition definition : definitions) {
            writer.print("    if (");
            boolean first = true;
            for (Class group : definition.getGroups()) {
                if (!first) {
                    writer.print(" || ");
                }
                writer.print("groups.contains(" + group.getName() + ".class)");
                first = false;
            }
            writer.println(") {");



            Class clazz = definition.getValidatorClass();
            writer.println("        Map<String, Object> attributes" + i + " = new HashMap<String, Object>();");
            Attributes attributes = definition.getAttributes();
            for (String name : attributes.getNames()) {
                Object value = attributes.get(name);
                indent(writer, 3);
                String varName = writeAnnotationValue(writer, context, value);
                outdent(writer, 3);
                writer.println("        attributes" + i + ".put(\"" + name + "\", " + varName + ");");
            }
            writer.println("        ConstraintValidator validator" + i + " = new " + clazz.getName() + "();");
            writer.println("        validator" + i + ".initialize(attributes" + i + ");");
            writer.println("        if (!validator" + i + ".isValid(bean, null)) {");

            Message message = definition.getMessage();
            writer.println("            String messageTemplate = \"" + message.getTemplate() + "\";");
            if (message.getMessagesMethod() != null) {
                String holderClassName = BeanValidationConfigHolder.getConfig().getMessagesClassHolderClassName();
                writer.println("            String message = " + holderClassName + ".get()." + message.getMessagesMethod().getName() + "();");
            } else {
                writer.println("            String message = messageTemplate;");
            }
            writer.println("            message = replaceParameters(message, attributes" + i + ");");
            writer.println("            registry.register(bean, null, message, messageTemplate, bean);");
            writer.println("        }");
            writer.println("    }");

        }

        writer.println("}");
    }


    private static void writePropertyValidateMethod(EasyTreeLogger logger, GeneratorContext context, SourceWriter writer, ValidationOracle oracle, JProperty property) throws UnableToCompleteException {

        String beanTypeName = oracle.getBeanOracle().getType().getQualifiedSourceName();
        String propertyTypeName = GeneratorUtils.getBoxedTypeName(property.getType());

        writer.println("private void validate_" + property.getName() + "(ViolationRegistry registry, " + beanTypeName + " bean, Set<Class> groups) {");

        List<ConstraintDefinition> definitions = oracle.getConstraintDefinitions(property.getName());

        writer.println("    " + propertyTypeName + " propVal = bean." + property.getGetter().getName() + "();");

        int i = 0;
        for (ConstraintDefinition definition : definitions) {
            writer.print("    if (");
            boolean first = true;
            for (Class group : definition.getGroups()) {
                if (!first) {
                    writer.print(" || ");
                }
                writer.print("groups.contains(" + group.getName() + ".class)");
                first = false;
            }
            writer.println(") {");



            Class clazz = definition.getValidatorClass();
            writer.println("        Map<String, Object> attributes" + i + " = new HashMap<String, Object>();");
            Attributes attributes = definition.getAttributes();
            for (String name : attributes.getNames()) {
                Object value = attributes.get(name);
                indent(writer, 3);
                String varName = writeAnnotationValue(writer, context, value);
                outdent(writer, 3);
                writer.println("        attributes" + i + ".put(\"" + name + "\", " + varName + ");");
            }
            writer.println("        ConstraintValidator validator" + i + " = new " + clazz.getName() + "();");
            writer.println("        validator" + i + ".initialize(attributes" + i + ");");
            writer.println("        if (!validator" + i + ".isValid(propVal, null)) {");

            Message message = definition.getMessage();
            writer.println("            String messageTemplate = \"" + message.getTemplate() + "\";");
            if (message.getMessagesMethod() != null) {
                String holderClassName = BeanValidationConfigHolder.getConfig().getMessagesClassHolderClassName();
                writer.println("            String message = " + holderClassName + ".get()." + message.getMessagesMethod().getName() + "();");
            } else {
                writer.println("            String message = messageTemplate;");
            }
            writer.println("            message = replaceParameters(message, attributes" + i + ");");
            writer.println("            registry.register(bean, \"" + property.getName() + "\", message, messageTemplate, propVal);");
            writer.println("        }");
            writer.println("    }");

        }

        if (oracle.isCascade(property.getName())) {

            if (property.isBean()) {
                writer.println("    if (propVal != null) {");
                writer.println("        BeanInfo subBeanInfo = BeanInfoRegistry.get().getBeanInfo(" + property.getType().getQualifiedSourceName() + ".class);");
                writer.println("        registry.push(new PathImpl.NodeImpl(\"" + property.getName() + "\"));");
                writer.println("        AbstractBeanValidator subValidator = (AbstractBeanValidator) subBeanInfo.getValidator();");
                writer.println("        subValidator.validate(registry, propVal, groups);");
                writer.println("        registry.pop();");
                writer.println("    }");

            } else if (property.isCollection()) {
                JClassType elementType = BeanGeneratorUtils.findCollectionElementType(property.getType(), context);
                if (BeanGeneratorUtils.isBean(elementType)) {
                    String elementTypeName = elementType.getQualifiedSourceName();
                    writer.println("    if (propVal != null) {");
                    writer.println("        int i = 0;");
                    writer.println("        for (" + elementTypeName + " element : ((Collection<" + elementTypeName + ">) propVal)) {");
                    writer.println("            BeanInfo subBeanInfo = BeanInfoRegistry.get().getBeanInfo(" + elementTypeName + ".class);");
                    writer.println("            registry.push(new PathImpl.NodeImpl(\"" + property.getName() + "\", i++));");
                    writer.println("            AbstractBeanValidator subValidator = (AbstractBeanValidator) subBeanInfo.getValidator();");
                    writer.println("            subValidator.validate(registry, element, groups);");
                    writer.println("            registry.pop();");
                    writer.println("        }");
                    writer.println("    }");
                }

            } else if (property.isMap()) {
                Pair<JClassType, JClassType> keyValueTypes = BeanGeneratorUtils.findMapKeyValueTypes(property.getType(), context);
                if (BeanGeneratorUtils.isBean(keyValueTypes.getValue2())) {
                    String keyTypeName = keyValueTypes.getValue1().getQualifiedSourceName();
                    String valueTypeName = keyValueTypes.getValue2().getQualifiedSourceName();
                    writer.println("    if (propVal != null) {");
                    writer.println("        for (Map.Entry<" + keyTypeName + ", " + valueTypeName + "> entry : ((Map<" + keyTypeName + ", " + valueTypeName + ">) propVal).entrySet()) {");
                    writer.println("            BeanInfo subBeanInfo = BeanInfoRegistry.get().getBeanInfo(" + valueTypeName + ".class);");
                    writer.println("            registry.push(new PathImpl.NodeImpl(\"" + property.getName() + "\", entry.getKey()));");
                    writer.println("            AbstractBeanValidator subValidator = (AbstractBeanValidator) subBeanInfo.getValidator();");
                    writer.println("            subValidator.validate(registry, entry.getValue(), groups);");
                    writer.println("            registry.pop();");
                    writer.println("        }");
                    writer.println("    }");
                }

            } else if (property.isArray() && BeanGeneratorUtils.isBean(property.getType().isArray().getComponentType())) {
                JArrayType arrayType = property.getType().isArray();
                String componentTypeName = arrayType.getComponentType().getQualifiedSourceName();
                writer.println("    if (propVal != null) {");
                String arrayVar = nextVarName();
                writer.println("        " + componentTypeName + "[] " + arrayVar + " = (" + componentTypeName + "[]) propVal;");
                writer.println("        for (int i = 0; i < " + arrayVar + ".length; i++) {");
                writer.println("            BeanInfo subBeanInfo = BeanInfoRegistry.get().getBeanInfo(" + componentTypeName + ".class);");
                writer.println("            registry.push(new PathImpl.NodeImpl(\"" + property.getName() + "\", i++));");
                writer.println("            AbstractBeanValidator subValidator = (AbstractBeanValidator) subBeanInfo.getValidator();");
                writer.println("            subValidator.validate(registry, " + arrayVar + "[i], groups);");
                writer.println("            registry.pop();");
                writer.println("        }");
                writer.println("    }");
            }

        }

        writer.println("}");
    }

    private static String writeAnnotationValue(SourceWriter writer, GeneratorContext context, Object value) {
        String varName = nextVarName();

        if (String.class.isInstance(value)) {
            writer.println("String " + varName + " = \"" + escapeForWriter(String.valueOf(value)) + "\";");
            return varName;
        }

        if (Enum.class.isInstance(value)) {
            String typeName = value.getClass().getCanonicalName();
            writer.println(typeName + " " + varName + " = " + typeName + "." + ((Enum) value).name() + ";");
            return varName;
        }

        if (Class.class.isInstance(value)) {
            writer.println("Class " + varName + " = " + ((Class) value).getName() + ".class;");
            return varName;
        }

        if (value.getClass().isArray()) {
            int length = Array.getLength(value);
            StringBuilder vars = new StringBuilder();
            for (int i = 0; i < length; i++) {
                Object item = Array.get(value, i);
                if (vars.length() != 0) {
                    vars.append(", ");
                }
                vars.append(writeAnnotationValue(writer, context, item));
            }

            String componentyTypeName = value.getClass().getComponentType().getCanonicalName();
            writer.println(componentyTypeName + "[] " + varName + " = new " + componentyTypeName + "[] { " + vars.toString() + " };");
            return varName;
        }

        if (Number.class.isInstance(value)) {
            writer.println(value.getClass().getSimpleName() + " " + varName + " = new " + value.getClass().getSimpleName() + "(" + String.valueOf(value) + ");");
            return varName;
        }

        writer.println(value.getClass().getName() + " " + varName + " = " + String.valueOf(value) + ";");
        return varName;
    }

    private static String nextVarName() {
        return "var" + varNameCounter++;
    }

    private static void indent(SourceWriter writer, int indent) {
        for (int i = 0; i < indent; i++) {
            writer.indent();
        }
    }

    private static void outdent(SourceWriter writer, int outdent) {
        for (int i = 0; i < outdent; i++) {
            writer.outdent();
        }
    }
}
TOP

Related Classes of org.gwtoolbox.bean.rebind.validation.BeanValidatorGenerator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.