All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.requery.processor.AttributeMember Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017 requery.io
 *
 * 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 io.requery.processor;

import io.requery.CascadeAction;
import io.requery.Column;
import io.requery.Convert;
import io.requery.Embedded;
import io.requery.ForeignKey;
import io.requery.Generated;
import io.requery.Index;
import io.requery.JunctionTable;
import io.requery.Key;
import io.requery.Lazy;
import io.requery.ManyToMany;
import io.requery.ManyToOne;
import io.requery.Naming;
import io.requery.Nullable;
import io.requery.OneToMany;
import io.requery.OneToOne;
import io.requery.OrderBy;
import io.requery.PropertyNameStyle;
import io.requery.ReadOnly;
import io.requery.ReferentialAction;
import io.requery.Transient;
import io.requery.Version;
import io.requery.converter.EnumOrdinalConverter;
import io.requery.meta.AttributeBuilder;
import io.requery.meta.Cardinality;
import io.requery.meta.ListAttributeBuilder;
import io.requery.meta.MapAttributeBuilder;
import io.requery.meta.ResultAttributeBuilder;
import io.requery.meta.SetAttributeBuilder;
import io.requery.query.Order;

import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.ConstraintMode;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.JoinColumn;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;

/**
 * Processes field level annotations on an abstract entity type where the annotated element can be
 * either a field in class or a method.
 *
 * @author Nikhil Purushe
 */
class AttributeMember extends BaseProcessableElement implements AttributeDescriptor {

    private final EntityDescriptor entity;
    private String name;
    private boolean isBoolean;
    private boolean isEmbedded;
    private boolean isForeignKey;
    private boolean isGenerated;
    private boolean isIndexed;
    private boolean isIterable;
    private boolean isKey;
    private boolean isLazy;
    private boolean isMap;
    private boolean isNullable;
    private boolean isOptional;
    private boolean isReadOnly;
    private boolean isTransient;
    private boolean isUnique;
    private boolean isVersion;
    private Type type;
    private Class builderClass;
    private Integer length;
    private Set indexNames;
    private Cardinality cardinality;
    private String converterType;
    private CascadeAction[] cascadeActions;
    private ReferentialAction deleteAction;
    private ReferentialAction updateAction;
    private String referencedColumn;
    private String referencedType;
    private String referencedTable;
    private String mappedBy;
    private String defaultValue;
    private String definition;
    private String collate;
    private String optionalClassName;
    private String orderByColumn;
    private Order orderByDirection;
    private AssociativeEntityDescriptor associativeDescriptor;

    AttributeMember(Element element, EntityDescriptor entity) {
        super(element);
        if (!element.getKind().isField() && element.getKind() != ElementKind.METHOD) {
            throw new IllegalStateException();
        }
        this.entity = entity;
        this.indexNames = new LinkedHashSet<>();
        this.type = Type.DEFAULT;
    }

    @Override
    public Set process(ProcessingEnvironment processingEnvironment) {
        Set validators = new LinkedHashSet<>();
        ElementValidator validator = new ElementValidator(element(), processingEnvironment);
        validators.add(validator);
        validateField(validator);
        processFieldAccessAnnotations(validator);
        processBasicColumnAnnotations(validator);
        processAssociativeAnnotations(processingEnvironment, validator);
        processConverterAnnotation(validator);
        checkMemberType(processingEnvironment, validators);
        if (cardinality() != null && entity.isImmutable()) {
            validator.error("Immutable value type cannot contain relational references");
        }
        if (!isTransient) {
            checkReserved(name(), validator);
        }
        isEmbedded = annotationOf(Embedded.class).isPresent() ||
            annotationOf(javax.persistence.Embedded.class).isPresent();
        indexNames.forEach(name -> checkReserved(name, validator));
        if (isReadOnly) {
            checkForInvalidSetter(validator);
        }
        return validators;
    }

    private void validateField(ElementValidator validator) {
        if (element().getKind().isField()) {
            Set modifiers = element().getModifiers();
            if (!entity.isUnimplementable() && modifiers.contains(Modifier.PRIVATE)) {
                validator.error("Entity field cannot be private");
            }
            if (modifiers.contains(Modifier.STATIC)) {
                validator.error("Entity field cannot be static");
            }
            if (modifiers.contains(Modifier.FINAL)) {
                validator.error("Entity field cannot be final");
            }
        }
    }

    private void checkMemberType(ProcessingEnvironment processingEnvironment,
                                 Set validators) {
        builderClass = AttributeBuilder.class;
        Types types = processingEnvironment.getTypeUtils();
        TypeMirror mirror = typeMirror();
        isBoolean = mirror.getKind() == TypeKind.BOOLEAN;
        if (mirror.getKind() == TypeKind.DECLARED) {
            TypeElement element = (TypeElement) types.asElement(mirror);
            if (element != null) {
                // only set if the attribute is relational
                if (cardinality != null) {
                    isIterable = Mirrors.isInstance(types, element, Iterable.class);
                }
                isMap = Mirrors.isInstance(types, element, Map.class);
                if (isMap && cardinality != null) {
                    builderClass = MapAttributeBuilder.class;
                }
                // check for optional compatible types
                String[] names = {
                        Optional.class.getName(),
                        "com.google.common.base.Optional",
                        "java8.util.Optional"
                };
                for (String name : names) {
                    if (Mirrors.isInstance(types, element, name)) {
                        isOptional = true;
                        optionalClassName = name;
                        break;
                    }
                }
                isBoolean = Mirrors.isInstance(types, element, Boolean.class);
            }
        }
        if (isIterable) {
            ElementValidator validator = validateCollectionType(processingEnvironment);
            if (validator != null) {
                validators.add(validator);
            }
        } else {
            TypeElement element = null;
            if (mirror.getKind().isPrimitive()) {
                element = types.boxedClass((PrimitiveType) mirror);
            } else if (mirror.getKind() == TypeKind.DECLARED) {
                element = (TypeElement) types.asElement(mirror);
            }

            if (element != null) {
                if (Mirrors.isInstance(types, element, Number.class)
                    || Mirrors.isInstance(types, element, java.util.Date.class)
                    || Mirrors.isInstance(types, element, java.time.temporal.Temporal.class)) {
                    type = Type.NUMERIC;
                } else if (Mirrors.isInstance(types, element, String.class)) {
                    type = Type.STRING;
                }
            }
        }
    }

    private ElementValidator validateCollectionType(ProcessingEnvironment processingEnvironment) {
        Types types = processingEnvironment.getTypeUtils();
        TypeElement collectionElement = (TypeElement) types.asElement(typeMirror());
        if (collectionElement != null) {
            ElementValidator validator = new ElementValidator(collectionElement, processingEnvironment);
            if (Mirrors.isInstance(types, collectionElement, List.class)) {
                builderClass = ListAttributeBuilder.class;
            } else if (Mirrors.isInstance(types, collectionElement, Set.class)) {
                builderClass = SetAttributeBuilder.class;
            } else if (Mirrors.isInstance(types, collectionElement, Iterable.class)) {
                builderClass = ResultAttributeBuilder.class;
            } else {
                validator.error("Invalid collection type, must be Set, List or Iterable");
            }
            return validator;
        }
        return null;
    }

    private void processFieldAccessAnnotations(ElementValidator validator) {
        if (annotationOf(Transient.class).isPresent() ||
            annotationOf(java.beans.Transient.class).isPresent() ||
            annotationOf(javax.persistence.Transient.class).isPresent() ||
                element().getModifiers().contains(Modifier.TRANSIENT)) {
            isTransient = true;
        }
        isReadOnly = annotationOf(ReadOnly.class).isPresent();
        if (!SourceVersion.isIdentifier(getterName())) {
            validator.error("Invalid getter name " + getterName(), Naming.class);
        }
        if (!SourceVersion.isIdentifier(setterName())) {
            validator.error("Invalid setter name " + setterName(), Naming.class);
        }
    }

    private void processBasicColumnAnnotations(ElementValidator validator) {
        if (annotationOf(Key.class).isPresent() ||
            annotationOf(javax.persistence.Id.class).isPresent()) {
            isKey = true;
            if (isTransient) {
                validator.error("Key field cannot be transient");
            }
        }
        // generated keys can't be set through a setter
        if (annotationOf(Generated.class).isPresent() ||
            annotationOf(GeneratedValue.class).isPresent()) {
            isGenerated = true;
            isReadOnly = true;

            // check generation strategy
            annotationOf(GeneratedValue.class).ifPresent(generatedValue -> {
                if (generatedValue.strategy() != GenerationType.IDENTITY  &&
                    generatedValue.strategy() != GenerationType.AUTO) {
                    validator.warning("GeneratedValue.strategy() " +
                        generatedValue.strategy() + " not supported", generatedValue.getClass());
                }
            });
        }
        if (annotationOf(Lazy.class).isPresent()) {
            if (isKey) {
                cannotCombine(validator, Key.class, Lazy.class);
            }
            isLazy = true;
        }
        if (annotationOf(Nullable.class).isPresent() || isOptional ||
            Mirrors.findAnnotationMirror(element(), "javax.annotation.Nullable").isPresent()) {
            isNullable = true;
        } else {
            // if not a primitive type the value assumed nullable
            if (element().getKind().isField()) {
                isNullable = !element().asType().getKind().isPrimitive();
            } else if(element().getKind() == ElementKind.METHOD) {
                ExecutableElement executableElement = (ExecutableElement) element();
                isNullable = !executableElement.getReturnType().getKind().isPrimitive();
            }
        }
        if (annotationOf(Version.class).isPresent() ||
            annotationOf(javax.persistence.Version.class).isPresent()) {
            isVersion = true;
            if (isKey) {
                cannotCombine(validator, Key.class, Version.class);
            }
        }

        Column column = annotationOf(Column.class).orElse(null);
        ForeignKey foreignKey = null;
        boolean foreignKeySetFromColumn = false;
        if (column != null) {
            name = "".equals(column.name()) ? null : column.name();
            isUnique = column.unique();
            isNullable = column.nullable();
            defaultValue = column.value();
            collate = column.collate();
            definition = column.definition();
            if (column.length() > 0) {
                length = column.length();
            }
            if (column.foreignKey().length > 0) {
                foreignKey = column.foreignKey()[0];
                foreignKeySetFromColumn = true;
            }
        }
        if (!foreignKeySetFromColumn) {
            foreignKey = annotationOf(ForeignKey.class).orElse(null);
        }
        if (foreignKey != null) {
            this.isForeignKey = true;
            deleteAction = foreignKey.delete();
            updateAction = foreignKey.update();
            referencedColumn = foreignKey.referencedColumn();
        }
        annotationOf(Index.class).ifPresent(index -> {
            isIndexed = true;
            Collections.addAll(indexNames, index.value());
        });

        // JPA specific
        annotationOf(Basic.class).ifPresent(basic -> {
            isNullable = basic.optional();
            isLazy = basic.fetch() == FetchType.LAZY;
        });

        annotationOf(javax.persistence.Index.class).ifPresent(index -> {
            isIndexed = true;
            Collections.addAll(indexNames, index.name());
        });

        annotationOf(JoinColumn.class).ifPresent(joinColumn -> {
            javax.persistence.ForeignKey joinForeignKey = joinColumn.foreignKey();
            this.isForeignKey = true;
            ConstraintMode constraintMode = joinForeignKey.value();
            switch (constraintMode) {
                default:
                case PROVIDER_DEFAULT:
                case CONSTRAINT:
                    deleteAction = ReferentialAction.CASCADE;
                    updateAction = ReferentialAction.CASCADE;
                    break;
                case NO_CONSTRAINT:
                    deleteAction = ReferentialAction.NO_ACTION;
                    updateAction = ReferentialAction.NO_ACTION;
                    break;
            }
            this.referencedTable = joinColumn.table();
            this.referencedColumn = joinColumn.referencedColumnName();
        });

        annotationOf(javax.persistence.Column.class).ifPresent(persistenceColumn -> {
            name = "".equals(persistenceColumn.name()) ? null : persistenceColumn.name();
            isUnique = persistenceColumn.unique();
            isNullable = persistenceColumn.nullable();
            length = persistenceColumn.length();
            isReadOnly = !persistenceColumn.updatable();
            definition = persistenceColumn.columnDefinition();
        });

        annotationOf(Enumerated.class).ifPresent(enumerated -> {
            EnumType enumType = enumerated.value();
            if (enumType == EnumType.ORDINAL) {
                converterType = EnumOrdinalConverter.class.getCanonicalName();
            }
        });
    }

    private void processAssociativeAnnotations(ProcessingEnvironment processingEnvironment,
                                               ElementValidator validator) {
        Optional oneToOne = annotationOf(OneToOne.class);
        Optional oneToMany = annotationOf(OneToMany.class);
        Optional manyToOne = annotationOf(ManyToOne.class);
        Optional manyToMany = annotationOf(ManyToMany.class);

        oneToOne = oneToOne.isPresent() ? oneToOne :
            annotationOf(javax.persistence.OneToOne.class);
        oneToMany = oneToMany.isPresent() ? oneToMany :
            annotationOf(javax.persistence.OneToMany.class);
        manyToOne = manyToOne.isPresent() ? manyToOne :
            annotationOf(javax.persistence.ManyToOne.class);
        manyToMany = manyToMany.isPresent() ? manyToMany :
            annotationOf(javax.persistence.ManyToMany.class);

        if (Stream.of(oneToOne, oneToMany, manyToOne, manyToMany)
            .filter(Optional::isPresent).count() > 1) {
            validator.error("Cannot have more than one associative annotation per field");
        }
        if (oneToOne.isPresent()) {
            cardinality = Cardinality.ONE_TO_ONE;
            ReflectiveAssociation reflect = new ReflectiveAssociation(oneToOne.get());
            mappedBy = reflect.mappedBy();
            cascadeActions = reflect.cascade();
            if (!isForeignKey()) {
                isReadOnly = true;
                if (!isKey()) {
                    isUnique = true;
                }
            }
        }
        if (oneToMany.isPresent()) {
            isIterable = true;
            cardinality = Cardinality.ONE_TO_MANY;
            isReadOnly = true;
            ReflectiveAssociation reflect = new ReflectiveAssociation(oneToMany.get());
            mappedBy = reflect.mappedBy();
            cascadeActions = reflect.cascade();
            checkIterable(validator);
            processOrderBy();
        }
        if (manyToOne.isPresent()) {
            cardinality = Cardinality.MANY_TO_ONE;
            isForeignKey = true;
            ReflectiveAssociation reflect = new ReflectiveAssociation(manyToOne.get());
            cascadeActions = reflect.cascade();
            if (deleteAction == null) {
                deleteAction = ReferentialAction.CASCADE;
            }
            if (updateAction == null) {
                updateAction = ReferentialAction.CASCADE;
            }
        }
        if (manyToMany.isPresent()) {
            isIterable = true;
            cardinality = Cardinality.MANY_TO_MANY;
            ReflectiveAssociation reflect = new ReflectiveAssociation(manyToMany.get());
            mappedBy = reflect.mappedBy();
            cascadeActions = reflect.cascade();
            Optional junctionTable = annotationOf(JunctionTable.class);
            Optional joinTable =
                annotationOf(javax.persistence.JoinTable.class);

            if (junctionTable.isPresent()) {
                Elements elements = processingEnvironment.getElementUtils();
                associativeDescriptor =
                    new JunctionTableAssociation(elements, this, junctionTable.get());

            } else if(joinTable.isPresent()) {
                associativeDescriptor = new JoinTableAssociation(joinTable.get());
            }
            isReadOnly = true;
            checkIterable(validator);
            processOrderBy();
        }
        if (isForeignKey()) {
            if (deleteAction == ReferentialAction.SET_NULL && !isNullable()) {
                validator.error("Cannot SET_NULL on optional attribute", ForeignKey.class);
            }
            // user mirror so generated type can be referenced
            Optional mirror =
                    Mirrors.findAnnotationMirror(element(), ForeignKey.class);
            if (mirror.isPresent()) {
                referencedType = mirror.flatMap(m -> Mirrors.findAnnotationValue(m, "references"))
                        .map(value -> value.getValue().toString())
                        .orElse(null);
            } else if (!typeMirror().getKind().isPrimitive()) {
                referencedType = typeMirror().toString();
            }
        }
    }

    private void checkReserved(String name, ElementValidator validator) {
        if (Stream.of(ReservedKeyword.values())
            .anyMatch(keyword -> keyword.toString().equalsIgnoreCase(name))) {
            validator.warning("Column or index name " + name + " may need to be escaped");
        }
    }

    private void checkIterable(ElementValidator validator) {
        if (!isIterable()) {
            validator.error("Many relation must be stored in an iterable type");
        }
    }

    private void processConverterAnnotation(ElementValidator validator) {
        if (annotationOf(Convert.class).isPresent()) {
            Optional mirror =
                Mirrors.findAnnotationMirror(element(), Convert.class);
            converterType = mirror.map(Mirrors::findAnnotationValue)
                .filter(Optional::isPresent)
                .map(Optional::get)
                .map(value -> value.getValue().toString()).orElse(null);

        } else if (annotationOf(javax.persistence.Convert.class).isPresent()) {

            Optional mirror =
                Mirrors.findAnnotationMirror(element(), javax.persistence.Convert.class);
            converterType = mirror.map(m -> Mirrors.findAnnotationValue(m, "converter"))
                .filter(Optional::isPresent)
                .map(Optional::get)
                .map(value -> value.getValue().toString()).orElse(null);
        }

        if (converterType != null && cardinality != null) {
            validator.warning("Cannot specify converter on association field", Convert.class);
        }
    }

    private void processOrderBy() {
        annotationOf(OrderBy.class).ifPresent(orderBy -> {
            orderByColumn = orderBy.value();
            orderByDirection = orderBy.order();
        });
        annotationOf(javax.persistence.OrderBy.class).ifPresent(orderBy -> {
            String value = orderBy.value();
            String[] parts = value.split(" ");
            if (parts.length > 0) {
                orderByColumn = parts[0].trim();
                if (parts.length > 1) {
                    String direction = parts[1].toUpperCase().trim();
                    try {
                        orderByDirection = Order.valueOf(direction);
                    } catch (IllegalArgumentException e) {
                        orderByDirection = Order.ASC;
                    }
                }
            }
        });
    }

    private void cannotCombine(ElementValidator validator,
                               Class annotation1,
                               Class annotation2) {
        String first = annotation1.getSimpleName();
        String second = annotation2.getSimpleName();
        validator.error("The " + first +
                " annotation cannot be combined with annotation " + second, annotation2);
    }

    private String getMethodName(String override, String prefix) {
        override = override.replace("\"", "");
        if (Names.isEmpty(override)) {
            CharSequence simpleName = Names.removeMemberPrefixes(element().getSimpleName());
            return Names.isEmpty(prefix) ?
                Names.lowerCaseFirst(simpleName) :
                prefix + Names.upperCaseFirst(simpleName);
        } else {
            return override;
        }
    }

    @Override
    public Type getType() {
        return type;
    }

    @Override
    public TypeMirror typeMirror() {
        if (element().getKind().isField()) {
            return element().asType();
        } else {
            ExecutableElement executableElement = (ExecutableElement) element();
            return executableElement.getReturnType();
        }
    }

    @Override
    public String fieldName() {
        if (element().getKind().isField()) {
            return element().getSimpleName().toString();
        } else if (element().getKind() == ElementKind.METHOD) {
            ExecutableElement methodElement = (ExecutableElement) element();
            String originalName = methodElement.getSimpleName().toString();
            String name = Names.removeMethodPrefixes(originalName);
            if (Names.isAllUpper(name)) {
                name = name.toLowerCase(Locale.ROOT);
            } else {
                name = Names.lowerCaseFirst(name);
            }
            return Names.checkReservedName(name, originalName);
        } else {
            throw new IllegalStateException();
        }
    }

    @Override
    public String getterName() {
        if (element().getKind().isField()) {
            String name = annotationOf(Naming.class).map(Naming::getter).orElse("");
            String prefix = "";
            if (useBeanStyleProperties()) {
                prefix = isBoolean ? "is" : "get";
            }
            return getMethodName(name, prefix);
        } else {
            return element().getSimpleName().toString();
        }
    }

    @Override
    public String setterName() {
        if (element().getKind().isField()) {
            String name = annotationOf(Naming.class).map(Naming::setter).orElse("");
            return getMethodName(name, useBeanStyleProperties() ? "set" : "");
        } else {
            // if an interface try to find a matching setter to implement
            for (ExecutableElement element :
                ElementFilter.methodsIn(entity.element().getEnclosedElements())) {
                List parameters = element.getParameters();
                if (parameters.size() == 1) {
                    String property =
                        Names.removeMethodPrefixes(element.getSimpleName().toString());
                    if (property.toLowerCase(Locale.ROOT).equalsIgnoreCase(name())) {
                        return element.getSimpleName().toString();
                    }
                }
            }
            // otherwise create one
            ExecutableElement executableElement = (ExecutableElement) element();
            String elementName = element().getSimpleName().toString();
            AccessorNamePrefix prefix = AccessorNamePrefix.fromElement(executableElement);
            switch (prefix) {
                case GET:
                    return elementName.replaceFirst("get", "set");
                case IS:
                    return elementName.replaceFirst("is", "set");
                case NONE:
                default:
                    return elementName;
            }
        }
    }

    private void checkForInvalidSetter(ElementValidator validator) {
        if (!element().getKind().isField()) {
            for (ExecutableElement element :
                    ElementFilter.methodsIn(entity.element().getEnclosedElements())) {
                List parameters = element.getParameters();
                if (parameters.size() == 1) {
                    String property = Names.removeMethodPrefixes(element.getSimpleName().toString());
                    if (property.toLowerCase(Locale.ROOT).equalsIgnoreCase(name())) {
                        validator.error("Element \""+ fieldName() + "\" is read-only but has setter (Kotlin: change var to val)");
                    }
                }
            }
        }
    }

    private boolean useBeanStyleProperties() {
        return entity.propertyNameStyle() == PropertyNameStyle.BEAN ||
               entity.propertyNameStyle() == PropertyNameStyle.FLUENT_BEAN;
    }

    @Override
    public String name() {
        if (!Names.isEmpty(name)) {
            return name;
        }
        String elementName = element().getSimpleName().toString();
        // for a method strip any accessor prefix such as get/is
        if (element().getKind() == ElementKind.METHOD) {
            ExecutableElement executableElement = (ExecutableElement) element();
            String originalName = elementName;
            AccessorNamePrefix prefix = AccessorNamePrefix.fromElement(executableElement);
            switch (prefix) {
                case GET:
                    elementName = elementName.replaceFirst("get", "");
                    break;
                case IS:
                    elementName = elementName.replaceFirst("is", "");
                    break;
            }
            elementName = Names.isAllUpper(elementName) ?
                    elementName : Names.lowerCaseFirst(elementName);
            return Names.checkReservedName(elementName, originalName);
        }
        return elementName;
    }

    @Override
    public String collate() {
        return collate;
    }

    @Override
    public Integer columnLength() {
        return length;
    }

    @Override
    public String converterName() {
        return converterType;
    }

    @Override
    public Set indexNames() {
        return indexNames;
    }

    @Override
    public String defaultValue() {
        return defaultValue;
    }

    @Override
    public String definition() {
        return definition;
    }

    @Override
    public boolean isEmbedded() {
        return isEmbedded;
    }

    @Override
    public boolean isForeignKey() {
        return isForeignKey;
    }

    @Override
    public boolean isNullable() {
        return isNullable;
    }

    @Override
    public boolean isGenerated() {
        return isGenerated;
    }

    @Override
    public boolean isIndexed() {
        return isIndexed;
    }

    @Override
    public boolean isIterable() {
        return isIterable;
    }

    @Override
    public boolean isKey() {
        return isKey;
    }

    @Override
    public boolean isLazy() {
        return isLazy;
    }

    @Override
    public boolean isMap() {
        return isMap;
    }

    @Override
    public boolean isOptional() {
        return isOptional;
    }

    @Override
    public boolean isReadOnly() {
        return isReadOnly;
    }

    @Override
    public boolean isTransient() {
        return isTransient;
    }

    @Override
    public boolean isUnique() {
        return isUnique;
    }

    @Override
    public boolean isVersion() {
        return isVersion;
    }

    @Override
    public Cardinality cardinality() {
        return cardinality;
    }

    @Override
    public ReferentialAction deleteAction() {
        return deleteAction;
    }

    @Override
    public ReferentialAction updateAction() {
        return updateAction;
    }

    @Override
    public Set cascadeActions() {
        EnumSet actions = EnumSet.noneOf(CascadeAction.class);
        if (cascadeActions != null) {
            actions.addAll(Arrays.asList(cascadeActions));
        }
        return actions;
    }

    @Override
    public String referencedColumn() {
        return referencedColumn;
    }

    @Override
    public String referencedType() {
        return referencedType;
    }

    @Override
    public String referencedTable() {
        return referencedTable;
    }

    @Override
    public String mappedBy() {
        return mappedBy;
    }

    @Override
    public String optionalClass() {
        return optionalClassName;
    }

    @Override
    public String orderBy() {
        return orderByColumn;
    }

    @Override
    public Order orderByDirection() {
        return orderByDirection;
    }

    @Override
    public Class builderClass() {
        return builderClass;
    }

    @Override
    public Optional associativeEntity() {
        return Optional.ofNullable(associativeDescriptor);
    }

    @Override
    public String toString() {
        return entity.typeName() + "." + name();
    }

    private static class ReflectiveAssociation {

        private final Annotation annotation;

        ReflectiveAssociation(Annotation annotation) {
            this.annotation = annotation;
        }

        String mappedBy() {
            try {
                return (String) annotation.getClass().getMethod("mappedBy").invoke(annotation);
            } catch (Exception e) {
                return null;
            }
        }

        CascadeAction[] cascade() {
            try {
                return (CascadeAction[])
                    annotation.getClass().getMethod("cascade").invoke(annotation);
            } catch (Exception e) {
                try {
                    CascadeType[] cascadeTypes = (CascadeType[])
                        annotation.getClass().getMethod("cascade").invoke(annotation);
                    return mapCascadeActions(cascadeTypes);
                } catch (Exception ee) {
                    return null;
                }
            }
        }

        private static CascadeAction[] mapCascadeActions(CascadeType[] types) {
            EnumSet actions = EnumSet.noneOf(CascadeAction.class);
            for (CascadeType type : types) {
                switch (type) {
                    case ALL:
                        actions.add(CascadeAction.SAVE);
                        actions.add(CascadeAction.DELETE);
                    case PERSIST:
                        actions.add(CascadeAction.SAVE);
                        break;
                    case MERGE:
                        actions.add(CascadeAction.SAVE);
                        break;
                    case REMOVE:
                        actions.add(CascadeAction.DELETE);
                        break;
                    case REFRESH:
                        break;
                }
            }
            return actions.toArray(new CascadeAction[actions.size()]);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy