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