Package org.slim3.gen.desc

Source Code of org.slim3.gen.desc.ModelMetaDescFactory

/*
* Copyright 2004-2010 the Seasar Foundation and the Others.
*
* 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 org.slim3.gen.desc;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.slim3.gen.AnnotationConstants;
import org.slim3.gen.datastore.PrimitiveBooleanType;
import org.slim3.gen.message.MessageCode;
import org.slim3.gen.processor.AptException;
import org.slim3.gen.processor.Options;
import org.slim3.gen.processor.UnknownDeclarationException;
import org.slim3.gen.processor.ValidationException;
import org.slim3.gen.util.AnnotationMirrorUtil;
import org.slim3.gen.util.DeclarationUtil;
import org.slim3.gen.util.StringUtil;
import org.slim3.gen.util.TypeUtil;

import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.declaration.AnnotationMirror;
import com.sun.mirror.declaration.ClassDeclaration;
import com.sun.mirror.declaration.FieldDeclaration;
import com.sun.mirror.declaration.InterfaceDeclaration;
import com.sun.mirror.declaration.MethodDeclaration;
import com.sun.mirror.declaration.Modifier;
import com.sun.mirror.type.ClassType;
import com.sun.mirror.type.InterfaceType;
import com.sun.mirror.type.TypeMirror;

/**
* Creates a model meta description.
*
* @author taedium
* @since 1.0.0
*
*/
public class ModelMetaDescFactory {

    /** the environment */
    protected final AnnotationProcessorEnvironment env;

    /** the attribute meta description factory */
    protected final AttributeMetaDescFactory attributeMetaDescFactory;

    /**
     * Creates a new {@link ModelMetaDescFactory}.
     *
     * @param env
     *            the environment
     * @param attributeMetaDescFactory
     *            the attribute meta description factory
     */
    public ModelMetaDescFactory(AnnotationProcessorEnvironment env,
            AttributeMetaDescFactory attributeMetaDescFactory) {
        if (env == null) {
            throw new NullPointerException("The env parameter is null.");
        }
        if (attributeMetaDescFactory == null) {
            throw new NullPointerException(
                "The attributeMetaDescFactory parameter is null.");
        }
        this.env = env;
        this.attributeMetaDescFactory = attributeMetaDescFactory;
    }

    /**
     * Creates a model meta description.
     *
     * @param classDeclaration
     *            the model declaration.
     * @return a model description
     */
    public ModelMetaDesc createModelMetaDesc(ClassDeclaration classDeclaration) {
        if (classDeclaration == null) {
            throw new NullPointerException(
                "The classDeclaration parameter is null.");
        }
        validateTopLevel(classDeclaration);
        validatePublicModifier(classDeclaration);
        validateNonGenericType(classDeclaration);
        validateDefaultConstructor(classDeclaration);

        AnnotationMirror model =
            DeclarationUtil.getAnnotationMirror(
                env,
                classDeclaration,
                AnnotationConstants.Model);
        if (model == null) {
            throw new IllegalStateException(AnnotationConstants.Model
                + " not found.");
        }

        String modelClassName = classDeclaration.getQualifiedName().toString();
        ModelMetaClassName modelMetaClassName =
            createModelMetaClassName(modelClassName);

        String kind = null;
        List<String> classHierarchyList = new ArrayList<String>();
        PolyModelDesc polyModelDesc = createPolyModelDesc(classDeclaration);
        if (polyModelDesc == null) {
            kind = getKind(model, modelMetaClassName.getKind());
        } else {
            kind = polyModelDesc.getKind();
            classHierarchyList = polyModelDesc.getClassHierarchyList();
            validateKind(classDeclaration);
        }

        String schemaVersionName =
            AnnotationMirrorUtil.getElementValueWithDefault(
                model,
                AnnotationConstants.schemaVersionName);
        validateSchemaVersionName(classDeclaration, schemaVersionName);

        Integer schemaVersion =
            AnnotationMirrorUtil.getElementValueWithDefault(
                model,
                AnnotationConstants.schemaVersion);

        String classHierarchyListName =
            AnnotationMirrorUtil.getElementValueWithDefault(
                model,
                AnnotationConstants.classHierarchyListName);
        validateClassHierarchyListName(classDeclaration, classHierarchyListName);

        ModelMetaDesc modelMetaDesc =
            new ModelMetaDesc(
                modelMetaClassName.getPackageName(),
                modelMetaClassName.getSimpleName(),
                classDeclaration.getModifiers().contains(Modifier.ABSTRACT),
                modelClassName,
                kind,
                schemaVersionName,
                schemaVersion.intValue(),
                classHierarchyListName,
                classHierarchyList);
        handleModelListener(modelMetaDesc, classDeclaration, model);
        handleAttributes(classDeclaration, modelMetaDesc);
        return modelMetaDesc;
    }

    /**
     * Creates the poly model description.
     *
     * @param classDeclaration
     *            the model declaration.
     * @return the poly model description.
     */
    protected PolyModelDesc createPolyModelDesc(
            ClassDeclaration classDeclaration) {
        String kind = null;
        LinkedList<String> classHierarchyList = new LinkedList<String>();
        for (ClassDeclaration c = classDeclaration; c != null
            && !c.getQualifiedName().equals(Object.class.getName()); c =
            c.getSuperclass().getDeclaration()) {
            AnnotationMirror anno =
                DeclarationUtil.getAnnotationMirror(
                    env,
                    c,
                    AnnotationConstants.Model);
            if (anno != null) {
                ModelMetaClassName modelMetaClassName =
                    createModelMetaClassName(c.getQualifiedName().toString());
                kind = getKind(anno, modelMetaClassName.getKind());
                classHierarchyList.addFirst(c.getQualifiedName());
            }
        }
        if (classHierarchyList.size() <= 1) {
            return null;
        }
        classHierarchyList.removeFirst();
        return new PolyModelDesc(kind, classHierarchyList);
    }

    /**
     * Returns the kind.
     *
     * @param anno
     *            the model annotation mirror.
     * @param defaultKind
     *            the default kind.
     * @return the kind
     */
    protected String getKind(AnnotationMirror anno, String defaultKind) {
        String value =
            AnnotationMirrorUtil
                .getElementValue(anno, AnnotationConstants.kind);
        if (value != null && value.length() > 0) {
            return value;
        }
        return defaultKind;
    }

    /**
     * Validates that nested level is top level.
     *
     * @param classDeclaration
     *            the class declaration
     */
    protected void validateTopLevel(ClassDeclaration classDeclaration) {
        if (classDeclaration.getDeclaringType() != null) {
            throw new ValidationException(
                MessageCode.SLIM3GEN1019,
                env,
                classDeclaration.getPosition());
        }
    }

    /**
     * Validates that modifier is public.
     *
     * @param classDeclaration
     *            the class declaration
     */
    protected void validatePublicModifier(ClassDeclaration classDeclaration) {
        if (!classDeclaration.getModifiers().contains(Modifier.PUBLIC)) {
            throw new ValidationException(
                MessageCode.SLIM3GEN1017,
                env,
                classDeclaration.getPosition());
        }
    }

    /**
     * Validates that the class is not generic type.
     *
     * @param classDeclaration
     */
    protected void validateNonGenericType(ClassDeclaration classDeclaration) {
        if (!classDeclaration.getFormalTypeParameters().isEmpty()) {
            throw new ValidationException(
                MessageCode.SLIM3GEN1020,
                env,
                classDeclaration.getPosition());
        }
    }

    /**
     * Validates that the default constructor is existent.
     *
     * @param classDeclaration
     *            the class declaration
     */
    protected void validateDefaultConstructor(ClassDeclaration classDeclaration) {
        if (!DeclarationUtil.hasPublicDefaultConstructor(classDeclaration)) {
            throw new ValidationException(
                MessageCode.SLIM3GEN1018,
                env,
                classDeclaration.getPosition());
        }
    }

    /**
     * Validates that the kind is unspecified.
     *
     * @param classDeclaration
     *            the class declaration
     */
    protected void validateKind(ClassDeclaration classDeclaration) {
        AnnotationMirror anno =
            DeclarationUtil.getAnnotationMirror(
                env,
                classDeclaration,
                AnnotationConstants.Model);
        if (anno == null) {
            throw new IllegalStateException(AnnotationConstants.Model
                + " not found.");
        }
        String value =
            AnnotationMirrorUtil
                .getElementValue(anno, AnnotationConstants.kind);
        if (value != null && value.length() > 0) {
            throw new ValidationException(MessageCode.SLIM3GEN1022, env, anno
                .getPosition());
        }
    }

    /**
     * Validates that the schemaVersionName is not empty.
     *
     * @param classDeclaration
     * @param schemaVersionName
     */
    protected void validateSchemaVersionName(ClassDeclaration classDeclaration,
            String schemaVersionName) {
        if (StringUtil.isEmpty(schemaVersionName)) {
            throw new ValidationException(
                MessageCode.SLIM3GEN1023,
                env,
                classDeclaration.getPosition());
        }
    }

    /**
     * Validates that the classHierarchyListName is not empty.
     *
     * @param classDeclaration
     * @param classHierarchyListName
     */
    protected void validateClassHierarchyListName(
            ClassDeclaration classDeclaration, String classHierarchyListName) {
        if (StringUtil.isEmpty(classHierarchyListName)) {
            throw new ValidationException(
                MessageCode.SLIM3GEN1049,
                env,
                classDeclaration.getPosition());
        }
    }

    /**
     * Creates a model meta class name.
     *
     * @param modelClassName
     *            a model class name
     * @return a model meta class name
     */
    protected ModelMetaClassName createModelMetaClassName(String modelClassName) {
        return new ModelMetaClassName(modelClassName, Options
            .getModelPackage(env), Options.getMetaPackage(env), Options
            .getSharedPackage(env), Options.getServerPackage(env));
    }

    /**
     * Handles attributes.
     *
     * @param classDeclaration
     *            the model declaration.
     * @param modelMetaDesc
     *            the model meta description
     */
    protected void handleAttributes(ClassDeclaration classDeclaration,
            ModelMetaDesc modelMetaDesc) {
        List<MethodDeclaration> methodDeclarations =
            getMethodDeclarations(classDeclaration);
        Set<String> propertyNames = createPropertyNames(modelMetaDesc);
        Set<String> booleanAttributeNames = new HashSet<String>();
        for (FieldDeclaration fieldDeclaration : getFieldDeclarations(classDeclaration)) {
            AttributeMetaDesc attributeMetaDesc =
                createAttributeMetaDesc(
                    classDeclaration,
                    fieldDeclaration,
                    methodDeclarations);
            if (attributeMetaDesc == null) {
                modelMetaDesc.setError(true);
                continue;
            }
            if (attributeMetaDesc.isPersistent()) {
                validatePrimaryKeyUniqueness(
                    attributeMetaDesc,
                    classDeclaration,
                    modelMetaDesc);
                validateVersionUniqueness(
                    attributeMetaDesc,
                    classDeclaration,
                    modelMetaDesc);
                validatePropertyNameUniqueness(
                    propertyNames,
                    attributeMetaDesc,
                    classDeclaration,
                    fieldDeclaration);
                validateBooleanAttributeNameUniqueness(
                    booleanAttributeNames,
                    attributeMetaDesc,
                    classDeclaration,
                    fieldDeclaration);
            }
            modelMetaDesc.addAttributeMetaDesc(attributeMetaDesc);
        }
        if (!modelMetaDesc.isError()
            && modelMetaDesc.getKeyAttributeMetaDesc() == null) {
            throw new ValidationException(
                MessageCode.SLIM3GEN1015,
                env,
                classDeclaration.getPosition());
        }
    }
   
    /**
     * Handles the model listener.
     *
     * @param modelMetaDesc
     *            the model meta description
     * @param classDeclaration
     *            the model class declaration
     * @param model
     *            the annotation mirror for Model
     */
    protected void handleModelListener(ModelMetaDesc modelMetaDesc,
            ClassDeclaration classDeclaration, AnnotationMirror model) {
        Object listener =
            AnnotationMirrorUtil.getElementValue(
                model,
                AnnotationConstants.listener);
        if (listener == null) {
            return;
        }
        if (listener instanceof InterfaceType) {
            throw new ValidationException(
                MessageCode.SLIM3GEN1052,
                env,
                classDeclaration.getPosition());
        }
        ClassType listenerClassType =
            TypeUtil.toClassType((TypeMirror) listener);
        if (listenerClassType == null) {
            return;
        }
        ClassDeclaration listenerClassDeclaration =
            listenerClassType.getDeclaration();
        if (listenerClassDeclaration == null) {
            throw new UnknownDeclarationException(
                env,
                listenerClassDeclaration,
                listenerClassType);
        }
        if (!DeclarationUtil
            .hasPublicDefaultConstructor(listenerClassDeclaration)) {
                throw new ValidationException(
                    MessageCode.SLIM3GEN1050,
                    env,
                    classDeclaration.getPosition(),
                    listenerClassDeclaration.getQualifiedName());
        }
        modelMetaDesc
            .setModelListenerClassName(listenerClassDeclaration
                .getQualifiedName());
    }
   
    /**
     * Creates property name set which contains reserved property names.
     *
     * @param modelMetaDesc
     * @return property name set
     */
    protected Set<String> createPropertyNames(ModelMetaDesc modelMetaDesc) {
        Set<String> results = new HashSet<String>();
        results.add(modelMetaDesc.getClassHierarchyListName());
        results.add(modelMetaDesc.getSchemaVersionName());
        return results;
    }

    /**
     * Validates primary key uniqueness.
     *
     * @param attributeMetaDesc
     * @param classDeclaration
     * @param modelMetaDesc
     */
    protected void validatePrimaryKeyUniqueness(
            AttributeMetaDesc attributeMetaDesc,
            ClassDeclaration classDeclaration, ModelMetaDesc modelMetaDesc) {
        if (attributeMetaDesc.isPrimaryKey()
            && modelMetaDesc.getKeyAttributeMetaDesc() != null) {
            throw new ValidationException(
                MessageCode.SLIM3GEN1013,
                env,
                classDeclaration.getPosition());
        }
    }

    /**
     * Validates version uniqueness.
     *
     * @param attributeMetaDesc
     * @param classDeclaration
     * @param modelMetaDesc
     */
    protected void validateVersionUniqueness(
            AttributeMetaDesc attributeMetaDesc,
            ClassDeclaration classDeclaration, ModelMetaDesc modelMetaDesc) {
        if (attributeMetaDesc.isVersion()
            && modelMetaDesc.getVersionAttributeMetaDesc() != null) {
            throw new ValidationException(
                MessageCode.SLIM3GEN1014,
                env,
                classDeclaration.getPosition());
        }
    }

    /**
     * Validates property name uniqueness.
     *
     * @param propertyNames
     * @param attributeMetaDesc
     * @param classDeclaration
     * @param fieldDeclaration
     */
    protected void validatePropertyNameUniqueness(Set<String> propertyNames,
            AttributeMetaDesc attributeMetaDesc,
            ClassDeclaration classDeclaration, FieldDeclaration fieldDeclaration) {
        String propertyName = attributeMetaDesc.getName();
        if (propertyNames.contains(propertyName)) {
            if (classDeclaration.equals(fieldDeclaration.getDeclaringType())) {
                throw new ValidationException(
                    MessageCode.SLIM3GEN1047,
                    env,
                    fieldDeclaration.getPosition(),
                    propertyName);
            }
            throw new ValidationException(
                MessageCode.SLIM3GEN1048,
                env,
                classDeclaration.getPosition(),
                propertyName,
                fieldDeclaration.getSimpleName(),
                fieldDeclaration.getDeclaringType().getQualifiedName());
        }
        propertyNames.add(propertyName);
    }

    /**
     * Validates boolean attribute name uniqueness.
     *
     * @param booleanAttributeNames
     * @param attributeMetaDesc
     * @param classDeclaration
     * @param fieldDeclaration
     */
    protected void validateBooleanAttributeNameUniqueness(
            Set<String> booleanAttributeNames,
            AttributeMetaDesc attributeMetaDesc,
            ClassDeclaration classDeclaration, FieldDeclaration fieldDeclaration) {
        if (attributeMetaDesc.getDataType() instanceof PrimitiveBooleanType) {
            String attributeName = attributeMetaDesc.getAttributeName();
            if (booleanAttributeNames.contains(attributeName)) {
                if (classDeclaration
                    .equals(fieldDeclaration.getDeclaringType())) {
                    throw new ValidationException(
                        MessageCode.SLIM3GEN1043,
                        env,
                        fieldDeclaration.getPosition(),
                        fieldDeclaration.getSimpleName());
                }
                throw new ValidationException(
                    MessageCode.SLIM3GEN1044,
                    env,
                    classDeclaration.getPosition(),
                    fieldDeclaration.getSimpleName(),
                    fieldDeclaration.getDeclaringType().getQualifiedName());
            }
            booleanAttributeNames.add(attributeName);
        }
    }

    /**
     * Creates a attribute meta description.
     *
     * @param classDeclaration
     *            the model declaration
     * @param fieldDeclaration
     * @param methodDeclarations
     * @return a attribute meta description or {@code null} if error occured.
     */
    protected AttributeMetaDesc createAttributeMetaDesc(
            ClassDeclaration classDeclaration,
            FieldDeclaration fieldDeclaration,
            List<MethodDeclaration> methodDeclarations) {
        try {
            return attributeMetaDescFactory.createAttributeMetaDesc(
                classDeclaration,
                fieldDeclaration,
                methodDeclarations);
        } catch (AptException e) {
            e.sendError();
        }
        return null;
    }

    /**
     * Returns field declarations.
     *
     * @param classDeclaration
     *            the class declaration
     * @return field declarations
     */
    protected List<FieldDeclaration> getFieldDeclarations(
            ClassDeclaration classDeclaration) {
        List<FieldDeclaration> results = new LinkedList<FieldDeclaration>();
        for (ClassDeclaration c = classDeclaration; c != null
            && !c.getQualifiedName().equals(Object.class.getName()); c =
            c.getSuperclass().getDeclaration()) {
            for (FieldDeclaration field : c.getFields()) {
                Collection<Modifier> modifiers = field.getModifiers();
                if (!modifiers.contains(Modifier.STATIC)) {
                    results.add(field);
                }
            }
        }

        List<FieldDeclaration> hiderFieldDeclarations =
            new LinkedList<FieldDeclaration>();
        for (Iterator<FieldDeclaration> it = results.iterator(); it.hasNext();) {
            FieldDeclaration hidden = it.next();
            for (FieldDeclaration hider : hiderFieldDeclarations) {
                if (env.getDeclarationUtils().hides(hider, hidden)) {
                    it.remove();
                }
            }
        }
        return results;
    }

    /**
     * Returns method declarations.
     *
     * @param classDeclaration
     *            the class declaration
     * @return method declarations
     */
    protected List<MethodDeclaration> getMethodDeclarations(
            ClassDeclaration classDeclaration) {
        List<MethodDeclaration> results = new LinkedList<MethodDeclaration>();
        for (ClassDeclaration c = classDeclaration; c != null
            && !c.getQualifiedName().equals(Object.class.getName()); c =
            c.getSuperclass().getDeclaration()) {
            gatherClassMethodDeclarations(c, results);
            for (InterfaceType superinterfaceType : c.getSuperinterfaces()) {
                InterfaceDeclaration superinterfaceDeclaration =
                    superinterfaceType.getDeclaration();
                if (superinterfaceDeclaration == null) {
                    throw new UnknownDeclarationException(
                        env,
                        classDeclaration,
                        superinterfaceType);
                }
                gatherInterfaceMethodDeclarations(
                    superinterfaceDeclaration,
                    results);
            }
        }

        List<MethodDeclaration> overriderMethodDeclarations =
            new LinkedList<MethodDeclaration>();
        for (Iterator<MethodDeclaration> it = results.iterator(); it.hasNext();) {
            MethodDeclaration overriden = it.next();
            for (MethodDeclaration overrider : overriderMethodDeclarations) {
                if (env.getDeclarationUtils().overrides(overrider, overriden)) {
                    it.remove();
                }
            }
        }
        return results;
    }

    /**
     * Gather class method declarations.
     *
     * @param classDeclaration
     *            the class declaration
     * @param methodDeclarations
     *            the list of method declarations
     */
    protected void gatherClassMethodDeclarations(
            ClassDeclaration classDeclaration,
            List<MethodDeclaration> methodDeclarations) {
        for (MethodDeclaration method : classDeclaration.getMethods()) {
            Collection<Modifier> modifiers = method.getModifiers();
            if (modifiers.contains(Modifier.PUBLIC)
                && !modifiers.contains(Modifier.STATIC)) {
                methodDeclarations.add(method);
            }
        }
    }

    /**
     * Gather interface method declarations.
     *
     * @param interfaceDeclaration
     *            the interface declaration
     * @param methodDeclarations
     *            the list of method declarations
     */
    protected void gatherInterfaceMethodDeclarations(
            InterfaceDeclaration interfaceDeclaration,
            List<MethodDeclaration> methodDeclarations) {
        for (MethodDeclaration method : interfaceDeclaration.getMethods()) {
            methodDeclarations.add(method);
        }
        for (InterfaceType superinterfaceType : interfaceDeclaration
            .getSuperinterfaces()) {
            InterfaceDeclaration superInterfaceDeclaration =
                superinterfaceType.getDeclaration();
            if (superInterfaceDeclaration == null) {
                throw new UnknownDeclarationException(
                    env,
                    interfaceDeclaration,
                    superinterfaceType);
            }
            gatherInterfaceMethodDeclarations(
                superInterfaceDeclaration,
                methodDeclarations);
        }
    }

    /**
     * The poly model description.
     *
     * @author taedium
     *
     */
    public static class PolyModelDesc {

        /** the kind */
        protected final String kind;

        /** the class hierarchy list */
        protected final List<String> classHierarchyList;

        /**
         * Creates a new {@link PolyModelDesc}.
         *
         * @param kind
         *            the kind
         * @param classHierarchyList
         *            the class hierarchy list
         */
        public PolyModelDesc(String kind, List<String> classHierarchyList) {
            if (kind == null) {
                throw new NullPointerException("The kind parameter is null.");
            }
            if (classHierarchyList == null) {
                throw new NullPointerException(
                    "The classHierarchyList parameter is null.");
            }
            this.kind = kind;
            this.classHierarchyList = classHierarchyList;
        }

        /**
         * Returns the kind.
         *
         * @return the kind
         */
        public String getKind() {
            return kind;
        }

        /**
         * Returns the class hierarchy list.
         *
         * @return the class hierarchy list
         */
        public List<String> getClassHierarchyList() {
            return classHierarchyList;
        }

    }

}
TOP

Related Classes of org.slim3.gen.desc.ModelMetaDescFactory

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.