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

io.requery.processor.EntityGraph 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 javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Holds entity types and navigates relationships between entity objects that will be generated.
 *
 * @author Nikhil Purushe
 */
class EntityGraph {

    private final Map entities;
    private final Map embeddedTypes;
    private final Types types;

    EntityGraph(Types types, Map embeddedTypes) {
        this.types = types;
        this.embeddedTypes = embeddedTypes;
        this.entities = new HashMap<>();
    }

    /**
     * @return the collection of {@link EntityDescriptor} descriptors in this graph.
     */
    Collection entities() {
        return Collections.unmodifiableCollection(entities.values());
    }

    /**
     * Add an {@link EntityDescriptor} to this graph.
     *
     * @param entity to add
     */
    public void add(EntityDescriptor entity) {
        entities.putIfAbsent(entity.element(), entity);
    }

    Optional embeddedDescriptorOf(AttributeDescriptor attribute) {
        TypeMirror mirror = attribute.typeMirror();
        Element element = types.asElement(mirror);
        if (element instanceof TypeElement) {
            return Optional.ofNullable(embeddedTypes.get(element));
        }
        return Optional.empty();
    }

    private Optional entityByName(QualifiedName name) {
        boolean ignorePackage = Names.isEmpty(name.packageName());
        for (EntityDescriptor entity : entities.values()) {
            QualifiedName entityName = entity.typeName();
            if ((ignorePackage && entityName.className().equals(name.className())) ||
                entityName.equals(name) || match(entity, name.className())) {
                return Optional.of(entity);
            }
        }
        return Optional.empty();
    }

    /**
     * Given a association attribute in an Entity find the Entity the attribute is referencing.
     *
     * @param attribute association attribute
     * @return Optional Entity type being referenced.
     */
    Optional referencingEntity(AttributeDescriptor attribute) {
        if (!Names.isEmpty(attribute.referencedTable())) {
            // match by table name
            return entities.values().stream()
                .filter(entity -> entity.tableName().equalsIgnoreCase(attribute.referencedTable()))
                .findFirst();

        } else if (!Names.isEmpty(attribute.referencedType())) {
            // match by type name
            Optional primitiveType = Stream.of(TypeKind.values())
                .filter(TypeKind::isPrimitive)
                .filter(kind -> kind.toString().toLowerCase().equals(attribute.referencedType()))
                .findFirst();
            if (!primitiveType.isPresent()) {
                QualifiedName referencedType = new QualifiedName(attribute.referencedType());
                return entityByName(referencedType);
            } // else attribute is basic foreign key and not referring to an entity
        } else {
            TypeMirror referencedType = attribute.typeMirror();
            if (attribute.isIterable()) {
                referencedType = collectionElementType(referencedType);
            }
            TypeElement referencedElement = (TypeElement) types.asElement(referencedType);
            if (referencedElement != null) {
                String referencedName = referencedElement.getSimpleName().toString();
                return entities.values().stream()
                        .filter(entity -> match(entity, referencedName))
                        .findFirst();
            }
        }
        return Optional.empty();
    }

    private static boolean match(EntityDescriptor entity, String referenceName) {
        return (entity.typeName().className().equals(referenceName) ||
            entity.element().getSimpleName().contentEquals(referenceName));
    }

    /**
     * Given an attribute in a given type finds the corresponding attribute that it is referencing
     * in that referencing type.
     *
     * @param attribute  attribute
     * @param referenced type being referenced
     * @return optional element it references
     */
    Optional referencingAttribute(AttributeDescriptor attribute,
                                                                 EntityDescriptor referenced) {
        String referencedColumn = attribute.referencedColumn();
        if (Names.isEmpty(referencedColumn)) {
            // using the id
            List keys = referenced.attributes().stream()
                .filter(AttributeDescriptor::isKey).collect(Collectors.toList());

            if (keys.size() == 1) {
                return Optional.of(keys.get(0));
            } else {
                return keys.stream()
                    .filter(other -> other.typeMirror().equals(attribute.typeMirror()))
                    .findFirst();
            }
        } else {
            return referenced.attributes().stream()
                .filter(other -> other.name().equals(referencedColumn))
                .findFirst();
        }
    }

    /**
     * Given an association in an entity find the attributes it maps onto in the referenced entity.
     *
     * @param entity     entity owning the attribute
     * @param attribute  association attribute
     * @param referenced type being referenced
     * @return set of mapped attributes
     */
    Set mappedAttributes(EntityDescriptor entity,
                                              AttributeDescriptor attribute,
                                              EntityDescriptor referenced) {
        String mappedBy = attribute.mappedBy();
        if (Names.isEmpty(mappedBy)) {
            return referenced.attributes().stream()
                .filter(other -> other.cardinality() != null)
                .filter(other -> referencingEntity(other).isPresent())
                .filter(other -> referencingEntity(other)
                        .orElseThrow(IllegalStateException::new) == entity)
                .collect(Collectors.toSet());
        } else {
            return referenced.attributes().stream()
                .filter(other -> other.name().equals(mappedBy))
                .collect(Collectors.toSet());
        }
    }

    private static TypeMirror collectionElementType(TypeMirror typeMirror) {
        List arguments = Mirrors.listGenericTypeArguments(typeMirror);
        return arguments.isEmpty() ? typeMirror : arguments.get(0);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy