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

org.apache.cayenne.reflect.PersistentDescriptor Maven / Gradle / Ivy

/*****************************************************************
 *   Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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.apache.cayenne.reflect;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.PersistenceState;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.EntityInheritanceTree;
import org.apache.cayenne.map.ObjAttribute;
import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.map.ObjRelationship;

/**
 * A default ClassDescriptor implementation for persistent objects.
 * 
 * @since 3.0
 */
public class PersistentDescriptor implements ClassDescriptor {

    static final Integer TRANSIENT_STATE = Integer.valueOf(PersistenceState.TRANSIENT);
    static final Integer HOLLOW_STATE = Integer.valueOf(PersistenceState.HOLLOW);
    static final Integer COMMITTED_STATE = Integer.valueOf(PersistenceState.COMMITTED);

    protected ClassDescriptor superclassDescriptor;

    // compiled properties ...
    protected Class objectClass;
    protected Map declaredProperties;
    protected Map properties;
    protected Map subclassDescriptors;
    protected Accessor persistenceStateAccessor;

    protected ObjEntity entity;
    protected Collection rootDbEntities;
    protected EntityInheritanceTree entityInheritanceTree;

    // combines declared and super properties
    protected Collection idProperties;

    // combines declared and super properties
    protected Collection mapArcProperties;

    // inheritance information
    protected Collection allDiscriminatorColumns;
    protected Expression entityQualifier;

    /**
     * Creates a PersistentDescriptor.
     */
    public PersistentDescriptor() {
        this.declaredProperties = new HashMap();
        this.properties = new HashMap();
        this.subclassDescriptors = new HashMap();

        // must be a set as duplicate addition attempts are expected...
        this.rootDbEntities = new HashSet(1);
    }

    public void setDiscriminatorColumns(Collection columns) {
        if (columns == null || columns.isEmpty()) {
            allDiscriminatorColumns = null;
        }
        else {
            allDiscriminatorColumns = new ArrayList(columns);
        }
    }

    /**
     * Registers a superclass property.
     */
    public void addSuperProperty(Property property) {
        properties.put(property.getName(), property);
        indexAddedProperty(property);
    }

    /**
     * Registers a property. This method is useful to customize default ClassDescriptor
     * generated from ObjEntity by adding new properties or overriding the standard ones.
     */
    public void addDeclaredProperty(Property property) {
        declaredProperties.put(property.getName(), property);
        properties.put(property.getName(), property);
        indexAddedProperty(property);
    }

    /**
     * Adds a root DbEntity to the list of roots, filtering duplicates.
     */
    public void addRootDbEntity(DbEntity dbEntity) {
        this.rootDbEntities.add(dbEntity);
    }
    
    void sortProperties() {

       // ensure properties are stored in predictable order per CAY-1729

        // 'properties' is a superset of 'declaredProperties', so let's sort all
        // properties, and populated both ordered collections at once
        if (properties.size() > 1) {

            List> entries = new ArrayList>(
                    properties.entrySet());

            Collections.sort(entries, PropertyComparator.comparator);

            Map orderedProperties = new LinkedHashMap(
                    (int) (entries.size() / 0.75));

            Map orderedDeclared = new LinkedHashMap(
                    (int) (declaredProperties.size() / 0.75));

            for (Entry e : entries) {
                orderedProperties.put(e.getKey(), e.getValue());
                
                if (declaredProperties.containsKey(e.getKey())) {
                    orderedDeclared.put(e.getKey(), e.getValue());
                }
            }

            this.properties = orderedProperties;
            this.declaredProperties = orderedDeclared;
        }
    }
    
    void indexAddedProperty(Property property) {
        if (property instanceof AttributeProperty) {

            AttributeProperty attributeProperty = (AttributeProperty) property;
            ObjAttribute attribute = attributeProperty.getAttribute();
            if (attribute.isPrimaryKey()) {

                if (idProperties == null) {
                    idProperties = new ArrayList(2);
                }

                idProperties.add(attributeProperty);
            }
        }
        else if (property instanceof ArcProperty) {
            ObjRelationship relationship = ((ArcProperty) property).getRelationship();
            ObjRelationship reverseRelationship = relationship.getReverseRelationship();
            if (reverseRelationship != null
                    && "java.util.Map".equals(reverseRelationship.getCollectionType())) {

                if (mapArcProperties == null) {
                    mapArcProperties = new ArrayList(2);
                }

                mapArcProperties.add((ArcProperty) property);
            }
        }
    }

    /**
     * Removes declared property. This method can be used to customize default
     * ClassDescriptor generated from ObjEntity.
     */
    public void removeDeclaredProperty(String propertyName) {
        Object removed = declaredProperties.remove(propertyName);

        if (removed != null) {
            if (idProperties != null) {
                idProperties.remove(removed);
            }

            if (mapArcProperties != null) {
                mapArcProperties.remove(removed);
            }
            
            properties.remove(propertyName);
        }
    }

    /**
     * Adds a subclass descriptor that maps to a given class name.
     */
    public void addSubclassDescriptor(String className, ClassDescriptor subclassDescriptor) {
        // note that 'className' should be used instead of
        // "subclassDescriptor.getEntity().getClassName()", as this method is called in
        // the early phases of descriptor initialization and we do not want to trigger
        // subclassDescriptor resolution just yet to prevent stack overflow.
        subclassDescriptors.put(className, subclassDescriptor);
    }

    public ObjEntity getEntity() {
        return entity;
    }

    public Collection getRootDbEntities() {
        return rootDbEntities;
    }

    public boolean isFault(Object object) {
        if (superclassDescriptor != null) {
            return superclassDescriptor.isFault(object);
        }

        if (object == null) {
            return false;
        }

        return HOLLOW_STATE.equals(persistenceStateAccessor.getValue(object));
    }

    public Class getObjectClass() {
        return objectClass;
    }

    void setObjectClass(Class objectClass) {
        this.objectClass = objectClass;
    }

    public ClassDescriptor getSubclassDescriptor(Class objectClass) {
        if (objectClass == null) {
            throw new IllegalArgumentException("Null objectClass");
        }

        if (subclassDescriptors.isEmpty()) {
            return this;
        }

        ClassDescriptor subclassDescriptor = subclassDescriptors.get(objectClass
                .getName());

        // ascend via the class hierarchy (only doing it if there are multiple choices)
        if (subclassDescriptor == null) {
            Class currentClass = objectClass;
            while (subclassDescriptor == null
                    && (currentClass = currentClass.getSuperclass()) != null) {
                subclassDescriptor = subclassDescriptors.get(currentClass.getName());
            }
        }

        return subclassDescriptor != null ? subclassDescriptor : this;
    }

    public Collection getDiscriminatorColumns() {
        return allDiscriminatorColumns != null ? allDiscriminatorColumns : Collections
                . emptyList();
    }

    public Collection getIdProperties() {

        if (idProperties != null) {
            return idProperties;
        }

        return Collections.emptyList();
    }

    public Collection getMapArcProperties() {

        if (mapArcProperties != null) {
            return mapArcProperties;
        }

        return Collections.EMPTY_LIST;
    }

    /**
     * Recursively looks up property descriptor in this class descriptor and all
     * superclass descriptors.
     */
    public Property getProperty(String propertyName) {
        Property property = getDeclaredProperty(propertyName);

        if (property == null && superclassDescriptor != null) {
            property = superclassDescriptor.getProperty(propertyName);
        }

        return property;
    }

    public Property getDeclaredProperty(String propertyName) {
        return declaredProperties.get(propertyName);
    }

    /**
     * Returns a descriptor of the mapped superclass or null if the descriptor's entity
     * sits at the top of inheritance hierarchy.
     */
    public ClassDescriptor getSuperclassDescriptor() {
        return superclassDescriptor;
    }

    /**
     * Creates a new instance of a class described by this object.
     */
    public Object createObject() {
        if (objectClass == null) {
            throw new NullPointerException(
                    "Null objectClass. Descriptor wasn't initialized properly.");
        }

        try {
            return objectClass.newInstance();
        }
        catch (Throwable e) {
            throw new CayenneRuntimeException("Error creating object of class '"
                    + objectClass.getName()
                    + "'", e);
        }
    }

    /**
     * Invokes 'prepareForAccess' of a super descriptor and then invokes
     * 'prepareForAccess' of each declared property.
     */
    public void injectValueHolders(Object object) throws PropertyException {

        // do super first
        if (getSuperclassDescriptor() != null) {
            getSuperclassDescriptor().injectValueHolders(object);
        }

        for (Property property : declaredProperties.values()) {
            property.injectValueHolder(object);
        }
    }

    /**
     * Copies object properties from one object to another. Invokes 'shallowCopy' of a
     * super descriptor and then invokes 'shallowCopy' of each declared property.
     */
    public void shallowMerge(final Object from, final Object to) throws PropertyException {

        visitProperties(new PropertyVisitor() {

            public boolean visitAttribute(AttributeProperty property) {
                property.writePropertyDirectly(
                        to,
                        property.readPropertyDirectly(to),
                        property.readPropertyDirectly(from));
                return true;
            }

            public boolean visitToOne(ToOneProperty property) {
                property.invalidate(to);
                return true;
            }

            public boolean visitToMany(ToManyProperty property) {
                return true;
            }
        });
    }

    /**
     * @since 3.0
     */
   public boolean visitDeclaredProperties(PropertyVisitor visitor) {

        for (Property next : declaredProperties.values()) {
            if (!next.visit(visitor)) {
                return false;
            }
        }

        return true;
    }

    /**
     * @since 3.0
     */
    public boolean visitAllProperties(PropertyVisitor visitor) {
        if (!visitProperties(visitor)) {
            return false;
        }

        if (!subclassDescriptors.isEmpty()) {
            for (ClassDescriptor next : subclassDescriptors.values()) {
                if (!next.visitDeclaredProperties(visitor)) {
                    return false;
                }
            }
        }

        return true;
    }

    public boolean visitProperties(PropertyVisitor visitor) {
        
        for (Property next : properties.values()) {
            if (!next.visit(visitor)) {
                return false;
            }
        }

        return true;
    }

    public void setPersistenceStateAccessor(Accessor persistenceStateAccessor) {
        this.persistenceStateAccessor = persistenceStateAccessor;
    }

    public void setEntity(ObjEntity entity) {
        this.entity = entity;
    }

    public void setSuperclassDescriptor(ClassDescriptor superclassDescriptor) {
        this.superclassDescriptor = superclassDescriptor;
    }

    public Expression getEntityQualifier() {
        return entityQualifier;
    }

    public void setEntityQualifier(Expression entityQualifier) {
        this.entityQualifier = entityQualifier;
    }

    public EntityInheritanceTree getEntityInheritanceTree() {
        return entityInheritanceTree;
    }

    public void setEntityInheritanceTree(EntityInheritanceTree entityInheritanceTree) {
        this.entityInheritanceTree = entityInheritanceTree;
    }

    public boolean hasSubclasses() {
        return entityInheritanceTree != null
                && !entityInheritanceTree.getChildren().isEmpty();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy