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

org.eclipse.persistence.internal.jpa.EntityGraphImpl Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1998, 2023 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     09 Jan 2013-2.5 Gordon Yorke
//       - 397772: JPA 2.1 Entity Graph Support
//     08/23/2023: Tomas Kraus
//       - New Jakarta Persistence 3.2 Features
package org.eclipse.persistence.internal.jpa;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import jakarta.persistence.AttributeNode;
import jakarta.persistence.EntityGraph;
import jakarta.persistence.Subgraph;
import jakarta.persistence.metamodel.Attribute;
import jakarta.persistence.metamodel.MapAttribute;
import jakarta.persistence.metamodel.PluralAttribute;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.internal.core.helper.CoreClassConstants;
import org.eclipse.persistence.internal.jpa.metamodel.AttributeImpl;
import org.eclipse.persistence.internal.localization.ExceptionLocalization;
import org.eclipse.persistence.internal.queries.AttributeItem;
import org.eclipse.persistence.internal.queries.MappedKeyMapContainerPolicy;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.queries.AttributeGroup;

/**
 * Concrete JPA EntityGraph class. For this implementation the EntityGraphImpl
 * wraps the EclipseLink AttributeGroup type.
 */
public class EntityGraphImpl extends AttributeNodeImpl implements EntityGraph, Subgraph {

    protected AttributeGroup attributeGroup;

    protected transient boolean isMutable = false;

    protected transient ClassDescriptor descriptor;

    protected Class classType;

    protected Map> attributeNodes;

    protected EntityGraphImpl(AttributeGroup group, ClassDescriptor descriptor) {
        super();
        this.attributeGroup = group;
        this.classType = descriptor.getJavaClass();
        this.isMutable = true;
        this.descriptor = descriptor;
    }

    @SuppressWarnings("unchecked")
    public EntityGraphImpl(AttributeGroup group) {
        super();
        this.attributeGroup = group;
        this.classType = (Class) group.getType();
        if (this.classType == null){
            this.classType = (Class) CoreClassConstants.OBJECT;
        }
    }

    protected EntityGraphImpl(AttributeGroup group, ClassDescriptor descriptor, String attribute) {
        this(group, descriptor);
        this.currentAttribute = attribute;
    }

    @Override
    public String getName() {
        return attributeGroup.getName();
    }

    @Override
    public  AttributeNode addAttributeNode(String attributeName) {
        if (!isMutable) {
            throw new IllegalStateException(ExceptionLocalization.buildMessage("immutable_entitygraph"));
        }
        return addAttributeNodeImpl(attributeName);
    }

    @Override
    public  AttributeNode addAttributeNode(Attribute attribute) {
        if (!isMutable) {
            throw new IllegalStateException(ExceptionLocalization.buildMessage("immutable_entitygraph"));
        }
        return addAttributeNodeImpl(attribute.getName());
    }

    @Override
    public void addAttributeNodes(String... attributeNames) {
        if (!isMutable) {
            throw new IllegalStateException(ExceptionLocalization.buildMessage("immutable_entitygraph"));
        }
        for (String attrName : attributeNames) {
            addAttributeNodeImpl(attrName);
        }
    }

    @Override
    @SafeVarargs
    public final void addAttributeNodes(Attribute... attributes) {
        if (!isMutable) {
            throw new IllegalStateException(ExceptionLocalization.buildMessage("immutable_entitygraph"));
        }
        for (Attribute attribute : attributes) {
            addAttributeNodeImpl(attribute.getName());
        }
    }

    @Override
    public boolean hasAttributeNode(String attributeName) {
        return attributeNodes.containsKey(attributeName);
    }

    @Override
    public boolean hasAttributeNode(Attribute attribute) {
        return hasAttributeNode(attribute.getName());
    }

    @Override
    @SuppressWarnings("unchecked")
    public  AttributeNode getAttributeNode(String attributeName) {
        return (AttributeNode) attributeNodes.get(attributeName);
    }

    @Override
    @SuppressWarnings("unchecked")
    public  AttributeNode getAttributeNode(Attribute attribute) {
        return (AttributeNode) attributeNodes.get(attribute.getName());
    }

    @Override
    public void removeAttributeNode(String attributeName) {
        removeAttributeNodeImpl(attributeName);
    }

    @Override
    public void removeAttributeNode(Attribute attribute) {
        removeAttributeNodeImpl(attribute.getName());
    }

    @Override
    public void removeAttributeNodes(Attribute.PersistentAttributeType nodeTypes) {
        for (DatabaseMapping mapping : descriptor.getMappings()) {
            if (AttributeImpl.getPersistentAttributeType(mapping) == nodeTypes) {
                removeAttributeNodeImpl(mapping.getAttributeName());
            }
        }
    }

    // Add an attribute node of given name to the entity graph.
    private  AttributeNode addAttributeNodeImpl(String attributeName) {
        if (!isMutable) {
            throw new IllegalStateException(ExceptionLocalization.buildMessage("immutable_entitygraph"));
        }
        if (descriptor.getMappingForAttributeName(attributeName) == null) {
            throw new IllegalArgumentException(
                    ExceptionLocalization.buildMessage(
                            "metamodel_managed_type_attribute_not_present",
                            new Object[] {attributeName, this.getClassType()}));
        }
        AttributeNodeImpl attributeNode = new AttributeNodeImpl<>(attributeName);
        // Order is important here, must add attribute node to node list before adding to group, or it will appear in node list twice.
        addAttributeNodeImpl(attributeNode);
        attributeGroup.addAttribute(attributeName, (AttributeGroup) null);
        return attributeNode;
    }

    protected void addAttributeNodeImpl(AttributeNodeImpl attributeNode) {
        if (attributeNodes == null) {
            buildAttributeNodes();
        }
        attributeNodes.put(attributeNode.getAttributeName(), attributeNode);
    }

    // Remove an attribute node of given name from the entity graph.
    private void removeAttributeNodeImpl(String attributeName) {
        attributeGroup.removeAttribute(attributeName);
        attributeNodes.remove(attributeName);
    }

    @Override
    public  Subgraph addSubgraph(Attribute attribute) {
        Class type = attribute.getJavaType();
        if (attribute.isCollection()) {
            type = ((PluralAttribute) attribute).getBindableJavaType();
        }
        return addSubgraph(attribute.getName(), type);
    }

    @Override
    public  Subgraph addTreatedSubgraph(Class type) {
        if (!this.isMutable) {
            throw new IllegalStateException(ExceptionLocalization.buildMessage("immutable_entitygraph"));
        }
        ClassDescriptor targetDesc = this.descriptor;
        if (targetDesc.hasInheritance()) {
            targetDesc = targetDesc.getInheritancePolicy().getDescriptor(type);
            if (targetDesc == null) {
                throw new IllegalArgumentException(ExceptionLocalization.buildMessage("type_unkown_for_this_entity", new Object[] { type.getName(), this.descriptor.getJavaClassName() }));
            }
        }
        AttributeGroup subGroup = new AttributeGroup(this.attributeGroup.getName(), type, true);
        this.attributeGroup.getSubClassGroups().put(type, subGroup);
        subGroup.setAllSubclasses(this.attributeGroup.getSubClassGroups());
        this.attributeGroup.insertSubClass(subGroup);
        return new EntityGraphImpl<>(subGroup, targetDesc);
    }

    @Override
    @SuppressWarnings({"removal", "rawtypes", "unchecked"})
    public  Subgraph addSubclassSubgraph(Class type) {
        return addTreatedSubgraph((Class)type);
    }

    @Override
    public  Subgraph addTreatedSubgraph(Attribute attribute, Class type) {
        return addSubgraph(attribute.getName(), type);
    }

    @Override
    @SuppressWarnings("removal")
    public  Subgraph addSubgraph(Attribute attribute, Class type) {
        return addSubgraph(attribute.getName(), type);
    }

    @Override
    public  Subgraph addSubgraph(String attributeName) {
        return this.addSubgraph(attributeName, null);
    }

    @Override
    public  Subgraph addSubgraph(String attributeName, Class type) {
        if (!this.isMutable) {
            throw new IllegalStateException(ExceptionLocalization.buildMessage("immutable_entitygraph"));
        }
        AttributeNodeImpl node = null;
        if (this.attributeNodes  != null){
            node = this.attributeNodes.get(attributeName);
        }
        if (node == null){
            node = new AttributeNodeImpl(attributeName);
            addAttributeNodeImpl(node);
        }
        AttributeGroup localGroup = null;
        DatabaseMapping mapping = descriptor.getMappingForAttributeName(attributeName);
        if (mapping == null) {
            throw new IllegalArgumentException(ExceptionLocalization.buildMessage("metamodel_managed_type_attribute_not_present", new Object[] { attributeName, this.descriptor.getJavaClassName() }));
        }

        localGroup = new AttributeGroup(attributeName, type, true);

        ClassDescriptor targetDesc = mapping.getReferenceDescriptor();
        if (type != null && targetDesc.hasInheritance()) {
            targetDesc = targetDesc.getInheritancePolicy().getDescriptor(type);
            if (targetDesc == null) {
                throw new IllegalArgumentException(ExceptionLocalization.buildMessage("type_unkown_for_this_attribute", new Object[] { type.getName(), attributeName }));
            }
        }
        EntityGraphImpl entityGraph = new EntityGraphImpl<>(localGroup, targetDesc, attributeName);
        node.addSubgraph(entityGraph);
        //order is important here, must add entity graph to node list before adding to group or it will appear in node list twice.
        this.attributeGroup.addAttribute(attributeName, localGroup);
        return entityGraph;
    }

    @Override
    public  Subgraph addElementSubgraph(String attributeName) {
        return addKeySubgraph(attributeName, null);
    }

    @Override
    public  Subgraph addElementSubgraph(String attributeName, Class type) {
        return addKeySubgraph(attributeName, type);
    }

    @Override
    public  Subgraph addElementSubgraph(PluralAttribute attribute) {
        return addKeySubgraph(attribute.getName(), attribute.getBindableJavaType());
    }

    @Override
    public  Subgraph addTreatedElementSubgraph(PluralAttribute attribute, Class type) {
        return addKeySubgraph(attribute.getName(), type);
    }

    @Override
    public  Subgraph addMapKeySubgraph(MapAttribute attribute) {
        return addKeySubgraph(attribute.getName(), attribute.getKeyJavaType());
    }

    @Override
    public  Subgraph addTreatedMapKeySubgraph(MapAttribute attribute, Class type) {
        return addKeySubgraph(attribute.getName(), type);
    }

    @Override
    @SuppressWarnings({"removal", "unchecked"})
    public  Subgraph addKeySubgraph(Attribute attribute) {
        if (!this.isMutable) {
            throw new IllegalStateException(ExceptionLocalization.buildMessage("immutable_entitygraph"));
        }
        Class type = attribute.getJavaType();
        if (attribute.isCollection()) {
            type = ((PluralAttribute) attribute).getBindableJavaType();
        }
        return addKeySubgraph(attribute.getName(), type);
    }

    @Override
    @SuppressWarnings("removal")
    public  Subgraph addKeySubgraph(Attribute attribute, Class type) {
        return addKeySubgraph(attribute.getName(), type);
    }

    @Override
    public  Subgraph addKeySubgraph(String attributeName) {
        return this.addKeySubgraph(attributeName, null);
    }

    @Override
    public  Subgraph addKeySubgraph(String attributeName, Class type) {
        if (!this.isMutable) {
            throw new IllegalStateException(ExceptionLocalization.buildMessage("immutable_entitygraph"));
        }
        AttributeNodeImpl node = null;
        if (this.attributeNodes  != null){
            node = this.attributeNodes.get(attributeName);
        }
        if (node == null){
            node = new AttributeNodeImpl<>(attributeName);
            addAttributeNodeImpl(node);
        }
        AttributeGroup localGroup = null;
        DatabaseMapping mapping = descriptor.getMappingForAttributeName(attributeName);
        if (mapping == null) {
            throw new IllegalArgumentException(ExceptionLocalization.buildMessage("metamodel_managed_type_attribute_not_present", new Object[] { this.descriptor.getJavaClassName(), attributeName }));
        }
        if (!mapping.getContainerPolicy().isMappedKeyMapPolicy() && !((MappedKeyMapContainerPolicy) mapping.getContainerPolicy()).isMapKeyAttribute()) {
            throw new IllegalArgumentException(ExceptionLocalization.buildMessage("attribute_is_not_map_with_managed_key", new Object[] { attributeName, descriptor.getJavaClassName() }));
        }

        localGroup = new AttributeGroup(attributeName, type, true);

        ClassDescriptor targetDesc = mapping.getContainerPolicy().getDescriptorForMapKey();
        if (type != null && targetDesc.hasInheritance()) {
            targetDesc = targetDesc.getInheritancePolicy().getDescriptor(type);
            if (targetDesc == null) {
                throw new IllegalArgumentException(ExceptionLocalization.buildMessage("type_unkown_for_this_attribute", new Object[] { type.getName(), attributeName }));
            }
        }
        EntityGraphImpl entityGraph = new EntityGraphImpl<>(localGroup, targetDesc, attributeName);
        node.addKeySubgraph(entityGraph);
        //order is important here, must add entity graph to node list before adding to group or it will appear in node list twice.
        this.attributeGroup.addAttributeKey(attributeName, localGroup);
        return entityGraph;
    }

    @Override
    public List> getAttributeNodes() {
        if (this.attributeNodes == null) {
            buildAttributeNodes();
        }
        return new ArrayList<>(this.attributeNodes.values());
    }

    @Override
    public Class getClassType() {
        return this.classType;
    }

    /**
     * @return the attributeGroup
     */
    public AttributeGroup getAttributeGroup() {
        return attributeGroup;
    }

    @Override
    public String getAttributeName() {
        return currentAttribute;
    }

    protected void buildAttributeNodes() {
        //this instance was built from a pre-existing attribute group so we need to rebuild
        //and entity graph
        this.attributeNodes = new HashMap<>();
        for (AttributeItem item : this.attributeGroup.getItems().values()) {
            AttributeNodeImpl node = new AttributeNodeImpl<>(item.getAttributeName());
            ClassDescriptor localDescriptor = null;
            if (this.descriptor != null) {
                localDescriptor = this.descriptor.getMappingForAttributeName(item.getAttributeName()).getReferenceDescriptor();
            }
            if (item.getGroups() != null && ! item.getGroups().isEmpty()) {
                for (AttributeGroup subGroup : item.getGroups().values()) {
                    Class type = subGroup.getType();
                    if (type == null) {
                        type = CoreClassConstants.OBJECT;
                    }
                    if (localDescriptor != null) {
                        if (!type.equals(CoreClassConstants.OBJECT) && localDescriptor.hasInheritance()) {
                            localDescriptor = localDescriptor.getInheritancePolicy().getDescriptor(type);
                        }
                        node.addSubgraph(new EntityGraphImpl<>(subGroup, localDescriptor));
                    } else {
                        node.addSubgraph(new EntityGraphImpl<>(subGroup));
                    }

                }
            }
            if (item.getKeyGroups() != null && ! item.getKeyGroups().isEmpty()) {
                for (AttributeGroup subGroup : item.getKeyGroups().values()) {
                    Class type = subGroup.getType();
                    if (type == null) {
                        type = CoreClassConstants.OBJECT;
                    }
                    if (localDescriptor != null) {
                        if (!type.equals(CoreClassConstants.OBJECT) && localDescriptor.hasInheritance()) {
                            localDescriptor = localDescriptor.getInheritancePolicy().getDescriptor(type);
                        }
                        node.addKeySubgraph(new EntityGraphImpl<>(subGroup, localDescriptor));
                    } else {
                        node.addKeySubgraph(new EntityGraphImpl<>(subGroup));
                    }
                }
            }
            this.attributeNodes.put(item.getAttributeName(), node);
        }

    }

}