You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2013/06/22 21:36:54 UTC
svn commit: r1495773 [1/3] - in
/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src:
main/java/org/apache/cayenne/ main/java/org/apache/cayenne/access/
main/java/org/apache/cayenne/access/jdbc/
main/java/org/apache/cayenne/access/trans/ main/j...
Author: aadamchik
Date: Sat Jun 22 19:36:53 2013
New Revision: 1495773
URL: http://svn.apache.org/r1495773
Log:
CAY-1789 Lock-free EntityResolver
cleanup
Modified:
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/BaseContext.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/Cayenne.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/CayenneContext.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataContext.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/ProcedureAction.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/ProcedureTranslator.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/EntityResolver.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/EntityResult.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/ObjRelationship.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/SQLResult.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorFactory.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/ClientServerChannelTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextPrefetchTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DeleteObjectTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DeleteRulesTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/FlattenedArcKeyTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/QuotedIdentifiersTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/jdbc/BatchActionLockingTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/jdbc/BatchActionTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/jdbc/SoftDeleteBatchQueryBuilderTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/trans/DeleteBatchQueryBuilderTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/trans/InsertBatchQueryBuilderTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/trans/UpdateBatchQueryBuilderTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/ashwood/AshwoodEntitySorterTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/dba/JdbcPkGeneratorTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/exp/ValueInjectorTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/exp/parser/ExpressionEvaluateInMemoryTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/map/ClientEntityResolverTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/map/EntityInheritanceTreeTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/map/EntityResolverGenericStackTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/map/EntityResolverInheritanceTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/map/EntityResolverTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/map/ObjEntityTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/map/ObjRelationshipTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/query/SelectQueryPrefetchRouterActionQualifiedEntityTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/query/SelectQueryPrefetchRouterActionTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/query/SelectQueryTest.java
Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/BaseContext.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/BaseContext.java?rev=1495773&r1=1495772&r2=1495773&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/BaseContext.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/BaseContext.java Sat Jun 22 19:36:53 2013
@@ -573,7 +573,7 @@ public abstract class BaseContext implem
ObjEntity entity;
try {
- entity = getEntityResolver().lookupObjEntity(object.getClass());
+ entity = getEntityResolver().getObjEntity(object.getClass());
} catch (CayenneRuntimeException ex) {
// ObjEntity cannot be fetched, ignored
entity = null;
Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/Cayenne.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/Cayenne.java?rev=1495773&r1=1495772&r2=1495773&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/Cayenne.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/Cayenne.java Sat Jun 22 19:36:53 2013
@@ -399,7 +399,7 @@ public class Cayenne {
Class<T> dataObjectClass,
Map<String, ?> pk) {
- ObjEntity entity = context.getEntityResolver().lookupObjEntity(dataObjectClass);
+ ObjEntity entity = context.getEntityResolver().getObjEntity(dataObjectClass);
if (entity == null) {
throw new CayenneRuntimeException("Non-existent ObjEntity for class: "
+ dataObjectClass);
@@ -539,7 +539,7 @@ public class Cayenne {
throw new IllegalArgumentException("Null DataObject class.");
}
- ObjEntity entity = context.getEntityResolver().lookupObjEntity(dataObjectClass);
+ ObjEntity entity = context.getEntityResolver().getObjEntity(dataObjectClass);
if (entity == null) {
throw new CayenneRuntimeException("Unmapped DataObject Class: "
+ dataObjectClass.getName());
Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/CayenneContext.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/CayenneContext.java?rev=1495773&r1=1495772&r2=1495773&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/CayenneContext.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/CayenneContext.java Sat Jun 22 19:36:53 2013
@@ -244,7 +244,7 @@ public class CayenneContext extends Base
throw new NullPointerException("Persistent class can't be null.");
}
- ObjEntity entity = getEntityResolver().lookupObjEntity(persistentClass);
+ ObjEntity entity = getEntityResolver().getObjEntity(persistentClass);
if (entity == null) {
throw new CayenneRuntimeException("No entity mapped for class: " + persistentClass);
}
@@ -265,7 +265,7 @@ public class CayenneContext extends Base
throw new NullPointerException("An attempt to register null object.");
}
- ObjEntity entity = getEntityResolver().lookupObjEntity(object.getClass());
+ ObjEntity entity = getEntityResolver().getObjEntity(object.getClass());
ClassDescriptor descriptor = getEntityResolver().getClassDescriptor(entity.getName());
registerNewObject((Persistent) object, entity.getName(), descriptor);
}
@@ -274,7 +274,6 @@ public class CayenneContext extends Base
* Runs a query, returning result as list.
*/
@Override
- @SuppressWarnings("unchecked")
public List performQuery(Query query) {
List result = onQuery(this, query).firstList();
return result != null ? result : new ArrayList<Object>(1);
Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataContext.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataContext.java?rev=1495773&r1=1495772&r2=1495773&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataContext.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataContext.java Sat Jun 22 19:36:53 2013
@@ -415,7 +415,7 @@ public class DataContext extends BaseCon
* @since 3.1
*/
public <T extends Persistent> T objectFromDataRow(Class<T> objectClass, DataRow dataRow) {
- ObjEntity entity = this.getEntityResolver().lookupObjEntity(objectClass);
+ ObjEntity entity = this.getEntityResolver().getObjEntity(objectClass);
if (entity == null) {
throw new CayenneRuntimeException("Unmapped Java class: " + objectClass);
@@ -451,7 +451,7 @@ public class DataContext extends BaseCon
throw new NullPointerException("Null 'persistentClass'");
}
- ObjEntity entity = getEntityResolver().lookupObjEntity(persistentClass);
+ ObjEntity entity = getEntityResolver().getObjEntity(persistentClass);
if (entity == null) {
throw new IllegalArgumentException("Class is not mapped with Cayenne: " + persistentClass.getName());
}
Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/ProcedureAction.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/ProcedureAction.java?rev=1495773&r1=1495772&r2=1495773&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/ProcedureAction.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/ProcedureAction.java Sat Jun 22 19:36:53 2013
@@ -189,7 +189,7 @@ public class ProcedureAction extends Bas
* Returns stored procedure for an internal query.
*/
protected Procedure getProcedure() {
- return getEntityResolver().lookupProcedure(query);
+ return query.getMetaData(getEntityResolver()).getProcedure();
}
/**
Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/ProcedureTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/ProcedureTranslator.java?rev=1495773&r1=1495772&r2=1495773&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/ProcedureTranslator.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/ProcedureTranslator.java Sat Jun 22 19:36:53 2013
@@ -172,7 +172,7 @@ public class ProcedureTranslator {
}
public Procedure getProcedure() {
- return entityResolver.lookupProcedure(query);
+ return query.getMetaData(entityResolver).getProcedure();
}
public ProcedureQuery getProcedureQuery() {
Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/EntityResolver.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/EntityResolver.java?rev=1495773&r1=1495772&r2=1495773&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/EntityResolver.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/EntityResolver.java Sat Jun 22 19:36:53 2013
@@ -337,9 +337,9 @@ public class EntityResolver implements M
*/
@Deprecated
public Collection<SQLResult> getResultSets() {
- return getResults();
+ return getResults();
}
-
+
/**
* @since 3.2
*/
@@ -379,12 +379,31 @@ public class EntityResolver implements M
return result;
}
- public Procedure getProcedure(String name) {
- return lookupProcedure(name);
+ public Procedure getProcedure(String procedureName) {
+ Procedure result = mappingCache.getProcedure(procedureName);
+ if (result == null) {
+ // reconstruct cache just in case some of the datamaps
+ // have changed and now contain the required information
+ refreshMappingCache();
+ result = mappingCache.getProcedure(procedureName);
+ }
+
+ return result;
}
+ /**
+ * Returns a named query or null if no query exists for a given name.
+ */
public Query getQuery(String name) {
- return lookupQuery(name);
+ Query result = mappingCache.getQuery(name);
+
+ if (result == null) {
+ // reconstruct cache just in case some of the datamaps
+ // have changed and now contain the required information
+ refreshMappingCache();
+ result = mappingCache.getQuery(name);
+ }
+ return result;
}
/**
@@ -573,42 +592,34 @@ public class EntityResolver implements M
return getObjEntity(id.getEntityName());
}
} else if (object instanceof Class) {
- return lookupObjEntity((Class<?>) object);
+ return getObjEntity((Class<?>) object);
}
- return lookupObjEntity(object.getClass());
+ return getObjEntity(object.getClass());
}
+ /**
+ * @deprecated since 3.2. Use q.getMetaData(resolver).getProcedure()
+ */
+ @Deprecated
public Procedure lookupProcedure(Query q) {
return q.getMetaData(this).getProcedure();
}
+ /**
+ * @deprecated since 3.2 use {@link #getProcedure(String)}.
+ */
+ @Deprecated
public Procedure lookupProcedure(String procedureName) {
-
- Procedure result = mappingCache.getProcedure(procedureName);
- if (result == null) {
- // reconstruct cache just in case some of the datamaps
- // have changed and now contain the required information
- refreshMappingCache();
- result = mappingCache.getProcedure(procedureName);
- }
-
- return result;
+ return getProcedure(procedureName);
}
/**
- * Returns a named query or null if no query exists for a given name.
+ * @deprecated since 3.2 use {@link #getQuery(String)}.
*/
+ @Deprecated
public Query lookupQuery(String name) {
- Query result = mappingCache.getQuery(name);
-
- if (result == null) {
- // reconstruct cache just in case some of the datamaps
- // have changed and now contain the required information
- refreshMappingCache();
- result = mappingCache.getQuery(name);
- }
- return result;
+ return getQuery(name);
}
public synchronized void removeDataMap(DataMap map) {
Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/EntityResult.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/EntityResult.java?rev=1495773&r1=1495772&r2=1495773&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/EntityResult.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/EntityResult.java Sat Jun 22 19:36:53 2013
@@ -81,7 +81,7 @@ public class EntityResult {
return resolver.getObjEntity(entityName);
}
else if (entityClass != null) {
- return resolver.lookupObjEntity(entityClass);
+ return resolver.getObjEntity(entityClass);
}
else {
throw new IllegalStateException("Both entity name and class are null");
Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/ObjRelationship.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/ObjRelationship.java?rev=1495773&r1=1495772&r2=1495773&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/ObjRelationship.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/ObjRelationship.java Sat Jun 22 19:36:53 2013
@@ -41,781 +41,754 @@ import org.apache.cayenne.util.XMLEncode
*/
public class ObjRelationship extends Relationship implements ConfigurationNode {
- /**
- * Denotes a default type of to-many relationship collection which is a Java
- * List.
- *
- * @since 3.0
- */
- public static final String DEFAULT_COLLECTION_TYPE = "java.util.List";
-
- boolean readOnly;
-
- protected int deleteRule = DeleteRule.NO_ACTION;
- protected boolean usedForLocking;
-
- protected List<DbRelationship> dbRelationships = new ArrayList<DbRelationship>(
- 2);
-
- /**
- * Db-relationships path that is set but not yet parsed (turned into
- * List<DbRelationship>) Used during map loading
- */
- String deferredPath;
-
- /**
- * Stores the type of collection mapped by a to-many relationship. Null for
- * to-one relationships.
- *
- * @since 3.0
- */
- protected String collectionType;
-
- /**
- * Stores a property name of a target entity used to create a relationship
- * map. Only has effect if collectionType property is set to
- * "java.util.Map".
- *
- * @since 3.0
- */
- protected String mapKey;
-
- public ObjRelationship() {
- this(null);
- }
-
- public ObjRelationship(String name) {
- super(name);
- }
-
- /**
- * @since 3.1
- */
- public <T> T acceptVisitor(ConfigurationNodeVisitor<T> visitor) {
- return visitor.visitObjRelationship(this);
- }
-
- /**
- * Prints itself as XML to the provided XMLEncoder.
- *
- * @since 1.1
- */
- public void encodeAsXML(XMLEncoder encoder) {
- ObjEntity source = (ObjEntity) getSourceEntity();
- if (source == null) {
- return;
- }
-
- encoder.print("<obj-relationship name=\"" + getName());
- encoder.print("\" source=\"" + source.getName());
-
- // looking up a target entity ensures that bogus names are not saved...
- // whether
- // this is good or bad is debatable, as users may want to point to
- // non-existent
- // entities on purpose.
- ObjEntity target = (ObjEntity) getTargetEntity();
- if (target != null) {
- encoder.print("\" target=\"" + target.getName());
- }
-
- if (getCollectionType() != null
- && !DEFAULT_COLLECTION_TYPE.equals(getCollectionType())) {
- encoder.print("\" collection-type=\"" + getCollectionType());
- }
-
- if (getMapKey() != null) {
- encoder.print("\" map-key=\"" + getMapKey());
- }
-
- if (isUsedForLocking()) {
- encoder.print("\" lock=\"true");
- }
-
- String deleteRule = DeleteRule.deleteRuleName(getDeleteRule());
- if (getDeleteRule() != DeleteRule.NO_ACTION && deleteRule != null) {
- encoder.print("\" deleteRule=\"" + deleteRule);
- }
-
- // quietly get rid of invalid path... this is not the best way of doing
- // things,
- // but it is consistent across map package
- String path = getValidRelationshipPath();
- if (path != null) {
- encoder.print("\" db-relationship-path=\"" + path);
- }
-
- encoder.println("\"/>");
- }
-
- /**
- * Returns a target ObjEntity of this relationship. Entity is looked up in
- * the parent DataMap using "targetEntityName".
- */
- @Override
- public Entity getTargetEntity() {
- String targetName = getTargetEntityName();
- if (targetName == null) {
- return null;
- }
-
- return getNonNullNamespace().getObjEntity(targetName);
- }
-
- /**
- * Returns the name of a complimentary relationship going in the opposite
- * direction or null if it doesn't exist.
- *
- * @since 1.2
- */
- public String getReverseRelationshipName() {
- ObjRelationship reverse = getReverseRelationship();
- return (reverse != null) ? reverse.getName() : null;
- }
-
- /**
- * Returns a "complimentary" ObjRelationship going in the opposite
- * direction. Returns null if no such relationship is found.
- */
- @Override
- public ObjRelationship getReverseRelationship() {
-
- // reverse the list
- List<DbRelationship> relationships = getDbRelationships();
- List<DbRelationship> reversed = new ArrayList<DbRelationship>(
- relationships.size());
-
- for (DbRelationship relationship : relationships) {
- DbRelationship reverse = relationship.getReverseRelationship();
- if (reverse == null) {
- return null;
- }
-
- reversed.add(0, reverse);
- }
-
- ObjEntity target = (ObjEntity) this.getTargetEntity();
- if (target == null) {
- return null;
- }
-
- Entity source = getSourceEntity();
-
- for (ObjRelationship relationship : target.getRelationships()) {
-
- if (relationship.getTargetEntity() != source) {
- continue;
- }
-
- List<?> otherRels = relationship.getDbRelationships();
- if (reversed.size() != otherRels.size()) {
- continue;
- }
-
- int len = reversed.size();
- boolean relsMatch = true;
- for (int i = 0; i < len; i++) {
- if (otherRels.get(i) != reversed.get(i)) {
- relsMatch = false;
- break;
- }
- }
-
- if (relsMatch) {
- return relationship;
- }
- }
-
- return null;
- }
-
- /**
- * Creates a complimentary reverse relationship from target entity to the
- * source entity. A new relationship is created regardless of whether one
- * already exists. Returned relationship is not attached to the source
- * entity and has no name. Throws a {@link CayenneRuntimeException} if
- * reverse DbRelationship is not mapped.
- *
- * @since 3.0
- */
- public ObjRelationship createReverseRelationship() {
- ObjRelationship reverse = new ObjRelationship();
- reverse.setSourceEntity(getTargetEntity());
- reverse.setTargetEntityName(getSourceEntity().getName());
- reverse.setDbRelationshipPath(getReverseDbRelationshipPath());
- return reverse;
- }
-
- /**
- * Returns an immutable list of underlying DbRelationships.
- */
- public List<DbRelationship> getDbRelationships() {
- refreshFromDeferredPath();
- return Collections.unmodifiableList(dbRelationships);
- }
-
- /**
- * Appends a DbRelationship to the existing list of DbRelationships.
- */
- public void addDbRelationship(DbRelationship dbRel) {
- refreshFromDeferredPath();
- if (dbRel.getName() == null) {
- throw new IllegalArgumentException("DbRelationship has no name");
- }
-
- // Adding a second is creating a flattened relationship.
- // Ensure that the new relationship properly continues
- // on the flattened path
- int numDbRelationships = dbRelationships.size();
- if (numDbRelationships > 0) {
- DbRelationship lastRel = dbRelationships
- .get(numDbRelationships - 1);
- if (!lastRel.getTargetEntityName().equals(
- dbRel.getSourceEntity().getName())) {
- throw new CayenneRuntimeException(
- "Error adding db relationship "
- + dbRel
- + " to ObjRelationship "
- + this
- + " because the source of the newly added relationship "
- + "is not the target of the previous relationship "
- + "in the chain");
- }
- }
-
- dbRelationships.add(dbRel);
-
- this.recalculateReadOnlyValue();
- this.recalculateToManyValue();
- }
-
- /**
- * Removes the relationship <code>dbRel</code> from the list of
- * relationships.
- */
- public void removeDbRelationship(DbRelationship dbRel) {
- refreshFromDeferredPath();
- if (dbRelationships.remove(dbRel)) {
- this.recalculateReadOnlyValue();
- this.recalculateToManyValue();
- }
- }
-
- public void clearDbRelationships() {
- deferredPath = null;
- this.dbRelationships.clear();
- this.readOnly = false;
- this.toMany = false;
- }
-
- /**
- * Returns a boolean indicating whether the presence of a non-null source
- * key(s) will not guarantee a presence of a target record. PK..FK
- * relationships are all optional, but there are other more subtle cases,
- * such as PK..PK, etc.
- *
- * @since 3.0
- */
- public boolean isOptional() {
- if (isToMany() || isFlattened()) {
- return true;
- }
-
- // entities with qualifiers may result in filtering even existing target
- // rows, so
- // such relationships are optional
- if (isQualifiedEntity((ObjEntity) getTargetEntity())) {
- return true;
- }
-
- DbRelationship dbRelationship = getDbRelationships().get(0);
-
- // to-one mandatory relationships are either from non-PK or to master pk
- if (dbRelationship.isToPK()) {
- if (!dbRelationship.isFromPK()) {
- return false;
- }
-
- DbRelationship reverseRelationship = dbRelationship
- .getReverseRelationship();
- if (reverseRelationship.isToDependentPK()) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * Returns true if the relationship is non-optional and target has no
- * subclasses.
- *
- * @since 3.0
- */
- public boolean isSourceDefiningTargetPrecenseAndType(
- EntityResolver entityResolver) {
-
- if (isOptional()) {
- return false;
- }
-
- EntityInheritanceTree inheritanceTree = entityResolver
- .lookupInheritanceTree(getTargetEntityName());
-
- return inheritanceTree == null
- || inheritanceTree.getChildren().isEmpty();
- }
-
- /**
- * Returns true if the entity or its super entities have a limiting
- * qualifier.
- */
- private boolean isQualifiedEntity(ObjEntity entity) {
- if (entity.getDeclaredQualifier() != null) {
- return true;
- }
-
- entity = entity.getSuperEntity();
-
- if (entity == null) {
- return false;
- }
-
- return isQualifiedEntity(entity);
- }
-
- /**
- * Returns a boolean indicating whether modifying a target of such
- * relationship in any way will not change the underlying table row of the
- * source.
- *
- * @since 1.1
- */
- public boolean isSourceIndependentFromTargetChange() {
- // note - call "isToPK" at the end of the chain, since
- // if it is to a dependent PK, we still should return true...
- return isToMany() || isFlattened() || isToDependentEntity()
- || !isToPK();
- }
-
- /**
- * Returns true if underlying DbRelationships point to dependent entity.
- */
- public boolean isToDependentEntity() {
- return (getDbRelationships().get(0)).isToDependentPK();
- }
-
- /**
- * Returns true if the underlying DbRelationships point to a at least one of
- * the columns of the target entity.
- *
- * @since 1.1
- */
- public boolean isToPK() {
- return (getDbRelationships().get(0)).isToPK();
- }
-
- /**
- * Returns true if the relationship is a "flattened" relationship. A
- * relationship is considered "flattened" if it maps to more than one
- * DbRelationship. Such chain of DbRelationships is also called
- * "relationship path". All flattened relationships are at least readable,
- * but only those formed across a many-many join table (with no custom
- * attributes other than foreign keys) can be automatically written.
- *
- * @see #isReadOnly
- * @return flag indicating if the relationship is flattened or not.
- */
- public boolean isFlattened() {
- return getDbRelationships().size() > 1;
- }
-
- /**
- * Returns true if the relationship is flattened, but is not of the single
- * case that can have automatic write support. Otherwise, it returns false.
- *
- * @return flag indicating if the relationship is read only or not
- */
- public boolean isReadOnly() {
- refreshFromDeferredPath();
- recalculateReadOnlyValue();
- return readOnly;
- }
-
- @Override
- public boolean isToMany() {
- refreshFromDeferredPath();
- recalculateToManyValue();
- return super.isToMany();
- }
-
- /**
- * Returns the deleteRule. The delete rule is a constant from the DeleteRule
- * class, and specifies what should happen to the destination object when
- * the source object is deleted.
- *
- * @return int a constant from DeleteRule
- * @see #setDeleteRule
- */
- public int getDeleteRule() {
- return deleteRule;
- }
-
- /**
- * Sets the delete rule of the relationship.
- *
- * @param value
- * New delete rule. Must be one of the constants defined in
- * DeleteRule class.
- * @see DeleteRule
- * @throws IllegalArgumentException
- * if the value is not a valid delete rule.
- */
- public void setDeleteRule(int value) {
- if ((value != DeleteRule.CASCADE) && (value != DeleteRule.DENY)
- && (value != DeleteRule.NULLIFY)
- && (value != DeleteRule.NO_ACTION)) {
-
- throw new IllegalArgumentException("Delete rule value " + value
- + " is not a constant from the DeleteRule class");
- }
-
- this.deleteRule = value;
- }
-
- /**
- * Returns whether this attribute should be used for locking.
- *
- * @since 1.1
- */
- public boolean isUsedForLocking() {
- return usedForLocking;
- }
-
- /**
- * Sets whether this attribute should be used for locking.
- *
- * @since 1.1
- */
- public void setUsedForLocking(boolean usedForLocking) {
- this.usedForLocking = usedForLocking;
- }
-
- /**
- * Returns a dot-separated path over mapped DbRelationships.
- *
- * @since 1.1
- */
- public String getDbRelationshipPath() {
- refreshFromDeferredPath();
-
- // build path on the fly
- if (getDbRelationships().isEmpty()) {
- return null;
- }
-
- StringBuilder path = new StringBuilder();
- Iterator<DbRelationship> it = getDbRelationships().iterator();
- while (it.hasNext()) {
- DbRelationship next = it.next();
- path.append(next.getName());
- if (it.hasNext()) {
- path.append(Entity.PATH_SEPARATOR);
- }
- }
-
- return path.toString();
- }
-
- /**
- * Returns a reversed dbRelationship path.
- *
- * @since 1.2
- */
- public String getReverseDbRelationshipPath() throws ExpressionException {
-
- List<DbRelationship> relationships = getDbRelationships();
- if (relationships == null || relationships.isEmpty()) {
- return null;
- }
-
- StringBuilder buffer = new StringBuilder();
-
- // iterate in reverse order
- ListIterator<DbRelationship> it = relationships
- .listIterator(relationships.size());
- while (it.hasPrevious()) {
-
- DbRelationship relationship = it.previous();
- DbRelationship reverse = relationship.getReverseRelationship();
-
- // another sanity check
- if (reverse == null) {
- throw new CayenneRuntimeException(
- "No reverse relationship exist for " + relationship);
- }
-
- if (buffer.length() > 0) {
- buffer.append(Entity.PATH_SEPARATOR);
- }
-
- buffer.append(reverse.getName());
- }
-
- return buffer.toString();
- }
-
- /**
- * Sets mapped DbRelationships as a dot-separated path.
- */
- public void setDbRelationshipPath(String relationshipPath) {
- if (!Util.nullSafeEquals(getDbRelationshipPath(), relationshipPath)) {
- refreshFromPath(relationshipPath, false);
- }
- }
-
- /**
- * Sets relationship path, but does not trigger its conversion to
- * List<DbRelationship> For internal purposes, primarily datamap loading
- */
- void setDeferredDbRelationshipPath(String relationshipPath) {
- if (!Util.nullSafeEquals(getDbRelationshipPath(), relationshipPath)) {
- deferredPath = relationshipPath;
- }
- }
-
- /**
- * Loads path from "deferredPath" variable (if specified)
- */
- synchronized void refreshFromDeferredPath() {
- if (deferredPath != null) {
- refreshFromPath(deferredPath, true);
- deferredPath = null;
- }
- }
-
- /**
- * Returns dot-separated path over DbRelationships, only including
- * components that have valid DbRelationships.
- */
- String getValidRelationshipPath() {
- String path = getDbRelationshipPath();
- if (path == null) {
- return null;
- }
-
- ObjEntity entity = (ObjEntity) getSourceEntity();
- if (entity == null) {
- throw new CayenneRuntimeException(
- "Can't resolve DbRelationships, null source ObjEntity");
- }
-
- DbEntity dbEntity = entity.getDbEntity();
- if (dbEntity == null) {
- return null;
- }
-
- StringBuilder validPath = new StringBuilder();
-
- try {
- for (PathComponent<DbAttribute, DbRelationship> pathComponent : dbEntity
- .resolvePath(new ASTDbPath(path), Collections.emptyMap())) {
-
- if (validPath.length() > 0) {
- validPath.append(Entity.PATH_SEPARATOR);
- }
- validPath.append(pathComponent.getName());
- }
- } catch (ExpressionException ex) {
-
- }
-
- return validPath.toString();
- }
-
- /**
- * Rebuild a list of relationships if String relationshipPath has changed.
- */
- final void refreshFromPath(String dbRelationshipPath, boolean stripInvalid) {
- synchronized (this) {
-
- // remove existing relationships
- dbRelationships.clear();
-
- if (dbRelationshipPath != null) {
-
- ObjEntity entity = (ObjEntity) getSourceEntity();
- if (entity == null) {
- throw new CayenneRuntimeException(
- "Can't resolve DbRelationships, null source ObjEntity");
- }
-
- try {
- // add new relationships from path
- Iterator<CayenneMapEntry> it = entity
- .resolvePathComponents(new ASTDbPath(
- dbRelationshipPath));
-
- while (it.hasNext()) {
- DbRelationship relationship = (DbRelationship) it
- .next();
-
- dbRelationships.add(relationship);
- }
- } catch (ExpressionException ex) {
- if (!stripInvalid) {
- throw ex;
- }
- }
- }
-
- recalculateToManyValue();
- recalculateReadOnlyValue();
- }
- }
-
- /**
- * Recalculates whether a relationship is toMany or toOne, based on the
- * underlying db relationships.
- */
- public void recalculateToManyValue() {
- // If there is a single toMany along the path, then the flattend
- // rel is toMany. If all are toOne, then the rel is toOne.
- // Simple (non-flattened) relationships form the degenerate case
- // taking the value of the single underlying dbrel.
- for (DbRelationship thisRel : this.dbRelationships) {
- if (thisRel.isToMany()) {
- this.toMany = true;
- return;
- }
- }
-
- this.toMany = false;
- }
-
- /**
- * Recalculates a new readonly value based on the underlying
- * DbRelationships.
- */
- public void recalculateReadOnlyValue() {
- // not flattened, always read/write
- if (dbRelationships.size() < 2) {
- this.readOnly = false;
- return;
- }
-
- // too long, can't handle this yet
- if (dbRelationships.size() > 2) {
- this.readOnly = true;
- return;
- }
-
- DbRelationship firstRel = dbRelationships.get(0);
- DbRelationship secondRel = dbRelationships.get(1);
-
- // many-to-many with single-step join
- // also 1..1..1 (CAY-1744) .. TODO all this should go away eventually
- // per CAY-1743
- if (!secondRel.isToMany()) {
-
- // allow modifications if the joins are from FKs
- if (!secondRel.isToPK()) {
- this.readOnly = true;
- return;
- }
-
- DbRelationship firstReverseRel = firstRel.getReverseRelationship();
- if (firstReverseRel == null || !firstReverseRel.isToPK()) {
- this.readOnly = true;
- return;
- }
-
- this.readOnly = false;
- }
- else {
- readOnly = true;
- }
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this).append("name", getName())
- .append("dbRelationshipPath", getDbRelationshipPath())
- .toString();
- }
-
- /**
- * Returns an ObjAttribute stripped of any server-side information, such as
- * DbAttribute mapping.
- *
- * @since 1.2
- */
- public ObjRelationship getClientRelationship() {
- ObjRelationship reverse = getReverseRelationship();
- String reverseName = reverse != null ? reverse.getName() : null;
-
- ObjRelationship relationship = new ClientObjRelationship(getName(),
- reverseName, isToMany(), isReadOnly());
-
- relationship.setTargetEntityName(getTargetEntityName());
- relationship.setDeleteRule(getDeleteRule());
- relationship.setCollectionType(getCollectionType());
-
- // TODO: copy locking flag...
-
- return relationship;
- }
-
- /**
- * Returns the interface of collection mapped by a to-many relationship.
- * Returns null for to-one relationships. Default for to-many is
- * "java.util.List". Other possible values are "java.util.Set",
- * "java.util.Collection", "java.util.Map".
- *
- * @since 3.0
- */
- public String getCollectionType() {
- if (collectionType != null) {
- return collectionType;
- }
-
- return isToMany() ? DEFAULT_COLLECTION_TYPE : null;
- }
-
- /**
- * @since 3.0
- */
- public void setCollectionType(String collectionType) {
- this.collectionType = collectionType;
- }
-
- /**
- * Returns a property name of a target entity used to create a relationship
- * map. Only has effect if collectionType property is set to
- * "java.util.Map".
- *
- * @return The attribute name used for the map key or <code>null</code> if
- * the default (PK) is used as the map key.
- * @since 3.0
- */
- public String getMapKey() {
- return mapKey;
- }
-
- /**
- * @since 3.0
- */
- public void setMapKey(String mapKey) {
- this.mapKey = mapKey;
- }
-
- @Override
- public boolean isMandatory() {
- refreshFromDeferredPath();
- if (dbRelationships.size() == 0) {
- return false;
- }
+ /**
+ * Denotes a default type of to-many relationship collection which is a Java
+ * List.
+ *
+ * @since 3.0
+ */
+ public static final String DEFAULT_COLLECTION_TYPE = "java.util.List";
+
+ boolean readOnly;
+
+ protected int deleteRule = DeleteRule.NO_ACTION;
+ protected boolean usedForLocking;
+
+ protected List<DbRelationship> dbRelationships = new ArrayList<DbRelationship>(2);
+
+ /**
+ * Db-relationships path that is set but not yet parsed (turned into
+ * List<DbRelationship>) Used during map loading
+ */
+ String deferredPath;
+
+ /**
+ * Stores the type of collection mapped by a to-many relationship. Null for
+ * to-one relationships.
+ *
+ * @since 3.0
+ */
+ protected String collectionType;
+
+ /**
+ * Stores a property name of a target entity used to create a relationship
+ * map. Only has effect if collectionType property is set to
+ * "java.util.Map".
+ *
+ * @since 3.0
+ */
+ protected String mapKey;
+
+ public ObjRelationship() {
+ this(null);
+ }
+
+ public ObjRelationship(String name) {
+ super(name);
+ }
+
+ /**
+ * @since 3.1
+ */
+ public <T> T acceptVisitor(ConfigurationNodeVisitor<T> visitor) {
+ return visitor.visitObjRelationship(this);
+ }
+
+ /**
+ * Prints itself as XML to the provided XMLEncoder.
+ *
+ * @since 1.1
+ */
+ public void encodeAsXML(XMLEncoder encoder) {
+ ObjEntity source = (ObjEntity) getSourceEntity();
+ if (source == null) {
+ return;
+ }
+
+ encoder.print("<obj-relationship name=\"" + getName());
+ encoder.print("\" source=\"" + source.getName());
+
+ // looking up a target entity ensures that bogus names are not saved...
+ // whether
+ // this is good or bad is debatable, as users may want to point to
+ // non-existent
+ // entities on purpose.
+ ObjEntity target = (ObjEntity) getTargetEntity();
+ if (target != null) {
+ encoder.print("\" target=\"" + target.getName());
+ }
+
+ if (getCollectionType() != null && !DEFAULT_COLLECTION_TYPE.equals(getCollectionType())) {
+ encoder.print("\" collection-type=\"" + getCollectionType());
+ }
+
+ if (getMapKey() != null) {
+ encoder.print("\" map-key=\"" + getMapKey());
+ }
+
+ if (isUsedForLocking()) {
+ encoder.print("\" lock=\"true");
+ }
+
+ String deleteRule = DeleteRule.deleteRuleName(getDeleteRule());
+ if (getDeleteRule() != DeleteRule.NO_ACTION && deleteRule != null) {
+ encoder.print("\" deleteRule=\"" + deleteRule);
+ }
+
+ // quietly get rid of invalid path... this is not the best way of doing
+ // things,
+ // but it is consistent across map package
+ String path = getValidRelationshipPath();
+ if (path != null) {
+ encoder.print("\" db-relationship-path=\"" + path);
+ }
+
+ encoder.println("\"/>");
+ }
+
+ /**
+ * Returns a target ObjEntity of this relationship. Entity is looked up in
+ * the parent DataMap using "targetEntityName".
+ */
+ @Override
+ public Entity getTargetEntity() {
+ String targetName = getTargetEntityName();
+ if (targetName == null) {
+ return null;
+ }
+
+ return getNonNullNamespace().getObjEntity(targetName);
+ }
+
+ /**
+ * Returns the name of a complimentary relationship going in the opposite
+ * direction or null if it doesn't exist.
+ *
+ * @since 1.2
+ */
+ public String getReverseRelationshipName() {
+ ObjRelationship reverse = getReverseRelationship();
+ return (reverse != null) ? reverse.getName() : null;
+ }
+
+ /**
+ * Returns a "complimentary" ObjRelationship going in the opposite
+ * direction. Returns null if no such relationship is found.
+ */
+ @Override
+ public ObjRelationship getReverseRelationship() {
+
+ // reverse the list
+ List<DbRelationship> relationships = getDbRelationships();
+ List<DbRelationship> reversed = new ArrayList<DbRelationship>(relationships.size());
+
+ for (DbRelationship relationship : relationships) {
+ DbRelationship reverse = relationship.getReverseRelationship();
+ if (reverse == null) {
+ return null;
+ }
+
+ reversed.add(0, reverse);
+ }
+
+ ObjEntity target = (ObjEntity) this.getTargetEntity();
+ if (target == null) {
+ return null;
+ }
+
+ Entity source = getSourceEntity();
+
+ for (ObjRelationship relationship : target.getRelationships()) {
+
+ if (relationship.getTargetEntity() != source) {
+ continue;
+ }
+
+ List<?> otherRels = relationship.getDbRelationships();
+ if (reversed.size() != otherRels.size()) {
+ continue;
+ }
+
+ int len = reversed.size();
+ boolean relsMatch = true;
+ for (int i = 0; i < len; i++) {
+ if (otherRels.get(i) != reversed.get(i)) {
+ relsMatch = false;
+ break;
+ }
+ }
+
+ if (relsMatch) {
+ return relationship;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates a complimentary reverse relationship from target entity to the
+ * source entity. A new relationship is created regardless of whether one
+ * already exists. Returned relationship is not attached to the source
+ * entity and has no name. Throws a {@link CayenneRuntimeException} if
+ * reverse DbRelationship is not mapped.
+ *
+ * @since 3.0
+ */
+ public ObjRelationship createReverseRelationship() {
+ ObjRelationship reverse = new ObjRelationship();
+ reverse.setSourceEntity(getTargetEntity());
+ reverse.setTargetEntityName(getSourceEntity().getName());
+ reverse.setDbRelationshipPath(getReverseDbRelationshipPath());
+ return reverse;
+ }
+
+ /**
+ * Returns an immutable list of underlying DbRelationships.
+ */
+ public List<DbRelationship> getDbRelationships() {
+ refreshFromDeferredPath();
+ return Collections.unmodifiableList(dbRelationships);
+ }
+
+ /**
+ * Appends a DbRelationship to the existing list of DbRelationships.
+ */
+ public void addDbRelationship(DbRelationship dbRel) {
+ refreshFromDeferredPath();
+ if (dbRel.getName() == null) {
+ throw new IllegalArgumentException("DbRelationship has no name");
+ }
+
+ // Adding a second is creating a flattened relationship.
+ // Ensure that the new relationship properly continues
+ // on the flattened path
+ int numDbRelationships = dbRelationships.size();
+ if (numDbRelationships > 0) {
+ DbRelationship lastRel = dbRelationships.get(numDbRelationships - 1);
+ if (!lastRel.getTargetEntityName().equals(dbRel.getSourceEntity().getName())) {
+ throw new CayenneRuntimeException("Error adding db relationship " + dbRel + " to ObjRelationship "
+ + this + " because the source of the newly added relationship "
+ + "is not the target of the previous relationship " + "in the chain");
+ }
+ }
+
+ dbRelationships.add(dbRel);
+
+ this.recalculateReadOnlyValue();
+ this.recalculateToManyValue();
+ }
+
+ /**
+ * Removes the relationship <code>dbRel</code> from the list of
+ * relationships.
+ */
+ public void removeDbRelationship(DbRelationship dbRel) {
+ refreshFromDeferredPath();
+ if (dbRelationships.remove(dbRel)) {
+ this.recalculateReadOnlyValue();
+ this.recalculateToManyValue();
+ }
+ }
+
+ public void clearDbRelationships() {
+ deferredPath = null;
+ this.dbRelationships.clear();
+ this.readOnly = false;
+ this.toMany = false;
+ }
+
+ /**
+ * Returns a boolean indicating whether the presence of a non-null source
+ * key(s) will not guarantee a presence of a target record. PK..FK
+ * relationships are all optional, but there are other more subtle cases,
+ * such as PK..PK, etc.
+ *
+ * @since 3.0
+ */
+ public boolean isOptional() {
+ if (isToMany() || isFlattened()) {
+ return true;
+ }
+
+ // entities with qualifiers may result in filtering even existing target
+ // rows, so
+ // such relationships are optional
+ if (isQualifiedEntity((ObjEntity) getTargetEntity())) {
+ return true;
+ }
+
+ DbRelationship dbRelationship = getDbRelationships().get(0);
+
+ // to-one mandatory relationships are either from non-PK or to master pk
+ if (dbRelationship.isToPK()) {
+ if (!dbRelationship.isFromPK()) {
+ return false;
+ }
+
+ DbRelationship reverseRelationship = dbRelationship.getReverseRelationship();
+ if (reverseRelationship.isToDependentPK()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns true if the relationship is non-optional and target has no
+ * subclasses.
+ *
+ * @since 3.0
+ */
+ public boolean isSourceDefiningTargetPrecenseAndType(EntityResolver entityResolver) {
+
+ if (isOptional()) {
+ return false;
+ }
+
+ EntityInheritanceTree inheritanceTree = entityResolver.getInheritanceTree(getTargetEntityName());
+ return inheritanceTree == null || inheritanceTree.getChildren().isEmpty();
+ }
+
+ /**
+ * Returns true if the entity or its super entities have a limiting
+ * qualifier.
+ */
+ private boolean isQualifiedEntity(ObjEntity entity) {
+ if (entity.getDeclaredQualifier() != null) {
+ return true;
+ }
+
+ entity = entity.getSuperEntity();
+
+ if (entity == null) {
+ return false;
+ }
+
+ return isQualifiedEntity(entity);
+ }
+
+ /**
+ * Returns a boolean indicating whether modifying a target of such
+ * relationship in any way will not change the underlying table row of the
+ * source.
+ *
+ * @since 1.1
+ */
+ public boolean isSourceIndependentFromTargetChange() {
+ // note - call "isToPK" at the end of the chain, since
+ // if it is to a dependent PK, we still should return true...
+ return isToMany() || isFlattened() || isToDependentEntity() || !isToPK();
+ }
+
+ /**
+ * Returns true if underlying DbRelationships point to dependent entity.
+ */
+ public boolean isToDependentEntity() {
+ return (getDbRelationships().get(0)).isToDependentPK();
+ }
+
+ /**
+ * Returns true if the underlying DbRelationships point to a at least one of
+ * the columns of the target entity.
+ *
+ * @since 1.1
+ */
+ public boolean isToPK() {
+ return (getDbRelationships().get(0)).isToPK();
+ }
+
+ /**
+ * Returns true if the relationship is a "flattened" relationship. A
+ * relationship is considered "flattened" if it maps to more than one
+ * DbRelationship. Such chain of DbRelationships is also called
+ * "relationship path". All flattened relationships are at least readable,
+ * but only those formed across a many-many join table (with no custom
+ * attributes other than foreign keys) can be automatically written.
+ *
+ * @see #isReadOnly
+ * @return flag indicating if the relationship is flattened or not.
+ */
+ public boolean isFlattened() {
+ return getDbRelationships().size() > 1;
+ }
+
+ /**
+ * Returns true if the relationship is flattened, but is not of the single
+ * case that can have automatic write support. Otherwise, it returns false.
+ *
+ * @return flag indicating if the relationship is read only or not
+ */
+ public boolean isReadOnly() {
+ refreshFromDeferredPath();
+ recalculateReadOnlyValue();
+ return readOnly;
+ }
+
+ @Override
+ public boolean isToMany() {
+ refreshFromDeferredPath();
+ recalculateToManyValue();
+ return super.isToMany();
+ }
+
+ /**
+ * Returns the deleteRule. The delete rule is a constant from the DeleteRule
+ * class, and specifies what should happen to the destination object when
+ * the source object is deleted.
+ *
+ * @return int a constant from DeleteRule
+ * @see #setDeleteRule
+ */
+ public int getDeleteRule() {
+ return deleteRule;
+ }
+
+ /**
+ * Sets the delete rule of the relationship.
+ *
+ * @param value
+ * New delete rule. Must be one of the constants defined in
+ * DeleteRule class.
+ * @see DeleteRule
+ * @throws IllegalArgumentException
+ * if the value is not a valid delete rule.
+ */
+ public void setDeleteRule(int value) {
+ if ((value != DeleteRule.CASCADE) && (value != DeleteRule.DENY) && (value != DeleteRule.NULLIFY)
+ && (value != DeleteRule.NO_ACTION)) {
+
+ throw new IllegalArgumentException("Delete rule value " + value
+ + " is not a constant from the DeleteRule class");
+ }
+
+ this.deleteRule = value;
+ }
+
+ /**
+ * Returns whether this attribute should be used for locking.
+ *
+ * @since 1.1
+ */
+ public boolean isUsedForLocking() {
+ return usedForLocking;
+ }
+
+ /**
+ * Sets whether this attribute should be used for locking.
+ *
+ * @since 1.1
+ */
+ public void setUsedForLocking(boolean usedForLocking) {
+ this.usedForLocking = usedForLocking;
+ }
+
+ /**
+ * Returns a dot-separated path over mapped DbRelationships.
+ *
+ * @since 1.1
+ */
+ public String getDbRelationshipPath() {
+ refreshFromDeferredPath();
+
+ // build path on the fly
+ if (getDbRelationships().isEmpty()) {
+ return null;
+ }
+
+ StringBuilder path = new StringBuilder();
+ Iterator<DbRelationship> it = getDbRelationships().iterator();
+ while (it.hasNext()) {
+ DbRelationship next = it.next();
+ path.append(next.getName());
+ if (it.hasNext()) {
+ path.append(Entity.PATH_SEPARATOR);
+ }
+ }
+
+ return path.toString();
+ }
+
+ /**
+ * Returns a reversed dbRelationship path.
+ *
+ * @since 1.2
+ */
+ public String getReverseDbRelationshipPath() throws ExpressionException {
+
+ List<DbRelationship> relationships = getDbRelationships();
+ if (relationships == null || relationships.isEmpty()) {
+ return null;
+ }
+
+ StringBuilder buffer = new StringBuilder();
+
+ // iterate in reverse order
+ ListIterator<DbRelationship> it = relationships.listIterator(relationships.size());
+ while (it.hasPrevious()) {
+
+ DbRelationship relationship = it.previous();
+ DbRelationship reverse = relationship.getReverseRelationship();
+
+ // another sanity check
+ if (reverse == null) {
+ throw new CayenneRuntimeException("No reverse relationship exist for " + relationship);
+ }
+
+ if (buffer.length() > 0) {
+ buffer.append(Entity.PATH_SEPARATOR);
+ }
+
+ buffer.append(reverse.getName());
+ }
+
+ return buffer.toString();
+ }
+
+ /**
+ * Sets mapped DbRelationships as a dot-separated path.
+ */
+ public void setDbRelationshipPath(String relationshipPath) {
+ if (!Util.nullSafeEquals(getDbRelationshipPath(), relationshipPath)) {
+ refreshFromPath(relationshipPath, false);
+ }
+ }
+
+ /**
+ * Sets relationship path, but does not trigger its conversion to
+ * List<DbRelationship> For internal purposes, primarily datamap loading
+ */
+ void setDeferredDbRelationshipPath(String relationshipPath) {
+ if (!Util.nullSafeEquals(getDbRelationshipPath(), relationshipPath)) {
+ deferredPath = relationshipPath;
+ }
+ }
+
+ /**
+ * Loads path from "deferredPath" variable (if specified)
+ */
+ synchronized void refreshFromDeferredPath() {
+ if (deferredPath != null) {
+ refreshFromPath(deferredPath, true);
+ deferredPath = null;
+ }
+ }
+
+ /**
+ * Returns dot-separated path over DbRelationships, only including
+ * components that have valid DbRelationships.
+ */
+ String getValidRelationshipPath() {
+ String path = getDbRelationshipPath();
+ if (path == null) {
+ return null;
+ }
+
+ ObjEntity entity = (ObjEntity) getSourceEntity();
+ if (entity == null) {
+ throw new CayenneRuntimeException("Can't resolve DbRelationships, null source ObjEntity");
+ }
+
+ DbEntity dbEntity = entity.getDbEntity();
+ if (dbEntity == null) {
+ return null;
+ }
+
+ StringBuilder validPath = new StringBuilder();
+
+ try {
+ for (PathComponent<DbAttribute, DbRelationship> pathComponent : dbEntity.resolvePath(new ASTDbPath(path),
+ Collections.emptyMap())) {
+
+ if (validPath.length() > 0) {
+ validPath.append(Entity.PATH_SEPARATOR);
+ }
+ validPath.append(pathComponent.getName());
+ }
+ } catch (ExpressionException ex) {
+
+ }
+
+ return validPath.toString();
+ }
+
+ /**
+ * Rebuild a list of relationships if String relationshipPath has changed.
+ */
+ final void refreshFromPath(String dbRelationshipPath, boolean stripInvalid) {
+ synchronized (this) {
+
+ // remove existing relationships
+ dbRelationships.clear();
+
+ if (dbRelationshipPath != null) {
+
+ ObjEntity entity = (ObjEntity) getSourceEntity();
+ if (entity == null) {
+ throw new CayenneRuntimeException("Can't resolve DbRelationships, null source ObjEntity");
+ }
+
+ try {
+ // add new relationships from path
+ Iterator<CayenneMapEntry> it = entity.resolvePathComponents(new ASTDbPath(dbRelationshipPath));
+
+ while (it.hasNext()) {
+ DbRelationship relationship = (DbRelationship) it.next();
+
+ dbRelationships.add(relationship);
+ }
+ } catch (ExpressionException ex) {
+ if (!stripInvalid) {
+ throw ex;
+ }
+ }
+ }
+
+ recalculateToManyValue();
+ recalculateReadOnlyValue();
+ }
+ }
+
+ /**
+ * Recalculates whether a relationship is toMany or toOne, based on the
+ * underlying db relationships.
+ */
+ public void recalculateToManyValue() {
+ // If there is a single toMany along the path, then the flattend
+ // rel is toMany. If all are toOne, then the rel is toOne.
+ // Simple (non-flattened) relationships form the degenerate case
+ // taking the value of the single underlying dbrel.
+ for (DbRelationship thisRel : this.dbRelationships) {
+ if (thisRel.isToMany()) {
+ this.toMany = true;
+ return;
+ }
+ }
+
+ this.toMany = false;
+ }
+
+ /**
+ * Recalculates a new readonly value based on the underlying
+ * DbRelationships.
+ */
+ public void recalculateReadOnlyValue() {
+ // not flattened, always read/write
+ if (dbRelationships.size() < 2) {
+ this.readOnly = false;
+ return;
+ }
+
+ // too long, can't handle this yet
+ if (dbRelationships.size() > 2) {
+ this.readOnly = true;
+ return;
+ }
+
+ DbRelationship firstRel = dbRelationships.get(0);
+ DbRelationship secondRel = dbRelationships.get(1);
+
+ // many-to-many with single-step join
+ // also 1..1..1 (CAY-1744) .. TODO all this should go away eventually
+ // per CAY-1743
+ if (!secondRel.isToMany()) {
+
+ // allow modifications if the joins are from FKs
+ if (!secondRel.isToPK()) {
+ this.readOnly = true;
+ return;
+ }
+
+ DbRelationship firstReverseRel = firstRel.getReverseRelationship();
+ if (firstReverseRel == null || !firstReverseRel.isToPK()) {
+ this.readOnly = true;
+ return;
+ }
+
+ this.readOnly = false;
+ } else {
+ readOnly = true;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).append("name", getName())
+ .append("dbRelationshipPath", getDbRelationshipPath()).toString();
+ }
+
+ /**
+ * Returns an ObjAttribute stripped of any server-side information, such as
+ * DbAttribute mapping.
+ *
+ * @since 1.2
+ */
+ public ObjRelationship getClientRelationship() {
+ ObjRelationship reverse = getReverseRelationship();
+ String reverseName = reverse != null ? reverse.getName() : null;
+
+ ObjRelationship relationship = new ClientObjRelationship(getName(), reverseName, isToMany(), isReadOnly());
+
+ relationship.setTargetEntityName(getTargetEntityName());
+ relationship.setDeleteRule(getDeleteRule());
+ relationship.setCollectionType(getCollectionType());
+
+ // TODO: copy locking flag...
+
+ return relationship;
+ }
+
+ /**
+ * Returns the interface of collection mapped by a to-many relationship.
+ * Returns null for to-one relationships. Default for to-many is
+ * "java.util.List". Other possible values are "java.util.Set",
+ * "java.util.Collection", "java.util.Map".
+ *
+ * @since 3.0
+ */
+ public String getCollectionType() {
+ if (collectionType != null) {
+ return collectionType;
+ }
+
+ return isToMany() ? DEFAULT_COLLECTION_TYPE : null;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public void setCollectionType(String collectionType) {
+ this.collectionType = collectionType;
+ }
+
+ /**
+ * Returns a property name of a target entity used to create a relationship
+ * map. Only has effect if collectionType property is set to
+ * "java.util.Map".
+ *
+ * @return The attribute name used for the map key or <code>null</code> if
+ * the default (PK) is used as the map key.
+ * @since 3.0
+ */
+ public String getMapKey() {
+ return mapKey;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public void setMapKey(String mapKey) {
+ this.mapKey = mapKey;
+ }
+
+ @Override
+ public boolean isMandatory() {
+ refreshFromDeferredPath();
+ if (dbRelationships.size() == 0) {
+ return false;
+ }
- return dbRelationships.get(0).isMandatory();
- }
+ return dbRelationships.get(0).isMandatory();
+ }
}
Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/SQLResult.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/SQLResult.java?rev=1495773&r1=1495772&r2=1495773&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/SQLResult.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/SQLResult.java Sat Jun 22 19:36:53 2013
@@ -26,9 +26,9 @@ import java.util.Map;
import org.apache.cayenne.reflect.ClassDescriptor;
/**
- * A metadata object that defines how a row in a result set can be converted to result
- * objects. SQLResult can be mapped to a single scalar, a single entity or a mix of
- * scalars and entities that is represented as an Object[].
+ * A metadata object that defines how a row in a result set can be converted to
+ * result objects. SQLResult can be mapped to a single scalar, a single entity
+ * or a mix of scalars and entities that is represented as an Object[].
*
* @since 3.0
*/
@@ -55,32 +55,22 @@ public class SQLResult {
int offset = 0;
for (Object component : getComponents()) {
if (component instanceof String) {
- resolvedComponents.add(new DefaultScalarResultSegment(
- (String) component,
- offset));
+ resolvedComponents.add(new DefaultScalarResultSegment((String) component, offset));
offset = offset + 1;
- }
- else if (component instanceof EntityResult) {
+ } else if (component instanceof EntityResult) {
EntityResult entityResult = (EntityResult) component;
Map<String, String> fields = entityResult.getDbFields(resolver);
String entityName = entityResult.getEntityName();
if (entityName == null) {
- entityName = resolver
- .lookupObjEntity(entityResult.getEntityClass())
- .getName();
+ entityName = resolver.getObjEntity(entityResult.getEntityClass()).getName();
}
ClassDescriptor classDescriptor = resolver.getClassDescriptor(entityName);
- resolvedComponents.add(new DefaultEntityResultSegment(
- classDescriptor,
- fields,
- offset));
+ resolvedComponents.add(new DefaultEntityResultSegment(classDescriptor, fields, offset));
offset = offset + fields.size();
- }
- else {
- throw new IllegalArgumentException(
- "Unsupported result descriptor component: " + component);
+ } else {
+ throw new IllegalArgumentException("Unsupported result descriptor component: " + component);
}
}
@@ -103,12 +93,13 @@ public class SQLResult {
}
/**
- * Returns a list of "uncompiled" result descriptors. Column descriptors are returned
- * as Strings, entity descriptors - as {@link EntityResult}. To get fully resolved
- * descriptors, use {@link #getResolvedComponents(EntityResolver)}.
+ * Returns a list of "uncompiled" result descriptors. Column descriptors are
+ * returned as Strings, entity descriptors - as {@link EntityResult}. To get
+ * fully resolved descriptors, use
+ * {@link #getResolvedComponents(EntityResolver)}.
*/
public List<Object> getComponents() {
- return resultDescriptors != null ? resultDescriptors : Collections.EMPTY_LIST;
+ return resultDescriptors != null ? resultDescriptors : Collections.emptyList();
}
public void addEntityResult(EntityResult entityResult) {
Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java?rev=1495773&r1=1495772&r2=1495773&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java Sat Jun 22 19:36:53 2013
@@ -98,14 +98,13 @@ class BaseQueryMetadata implements Query
if (root != null) {
if (root instanceof Class<?>) {
- entity = resolver.lookupObjEntity((Class<?>) root);
- if (entity == null) { // entity not found, try to resolve it with
+ entity = resolver.getObjEntity((Class<?>) root);
+ if (entity == null) { // entity not found, try to resolve it
+ // with
// client resolver
- EntityResolver clientResolver = resolver
- .getClientEntityResolver();
+ EntityResolver clientResolver = resolver.getClientEntityResolver();
if (clientResolver != resolver) {
- ObjEntity clientEntity = clientResolver
- .lookupObjEntity((Class<?>) root);
+ ObjEntity clientEntity = clientResolver.getObjEntity((Class<?>) root);
if (clientEntity != null) {
entity = resolver.getObjEntity(clientEntity.getName());
@@ -117,27 +116,22 @@ class BaseQueryMetadata implements Query
this.dbEntity = entity.getDbEntity();
this.dataMap = entity.getDataMap();
}
- }
- else if (root instanceof ObjEntity) {
+ } else if (root instanceof ObjEntity) {
entity = (ObjEntity) root;
this.dbEntity = entity.getDbEntity();
this.dataMap = entity.getDataMap();
- }
- else if (root instanceof String) {
+ } else if (root instanceof String) {
entity = resolver.getObjEntity((String) root);
if (entity != null) {
this.dbEntity = entity.getDbEntity();
this.dataMap = entity.getDataMap();
}
- }
- else if (root instanceof DbEntity) {
+ } else if (root instanceof DbEntity) {
this.dbEntity = (DbEntity) root;
this.dataMap = dbEntity.getDataMap();
- }
- else if (root instanceof DataMap) {
+ } else if (root instanceof DataMap) {
this.dataMap = (DataMap) root;
- }
- else if (root instanceof Persistent) {
+ } else if (root instanceof Persistent) {
entity = resolver.lookupObjEntity(root);
if (entity != null) {
this.dbEntity = entity.getDbEntity();
@@ -168,44 +162,35 @@ class BaseQueryMetadata implements Query
Object fetchOffset = properties.get(QueryMetadata.FETCH_OFFSET_PROPERTY);
Object fetchLimit = properties.get(QueryMetadata.FETCH_LIMIT_PROPERTY);
Object pageSize = properties.get(QueryMetadata.PAGE_SIZE_PROPERTY);
- Object statementFetchSize = properties
- .get(QueryMetadata.STATEMENT_FETCH_SIZE_PROPERTY);
- Object fetchingDataRows = properties
- .get(QueryMetadata.FETCHING_DATA_ROWS_PROPERTY);
+ Object statementFetchSize = properties.get(QueryMetadata.STATEMENT_FETCH_SIZE_PROPERTY);
+ Object fetchingDataRows = properties.get(QueryMetadata.FETCHING_DATA_ROWS_PROPERTY);
Object cacheStrategy = properties.get(QueryMetadata.CACHE_STRATEGY_PROPERTY);
Object cacheGroups = properties.get(QueryMetadata.CACHE_GROUPS_PROPERTY);
// init ivars from properties
- this.fetchOffset = (fetchOffset != null) ? Integer.parseInt(fetchOffset
- .toString()) : QueryMetadata.FETCH_OFFSET_DEFAULT;
+ this.fetchOffset = (fetchOffset != null) ? Integer.parseInt(fetchOffset.toString())
+ : QueryMetadata.FETCH_OFFSET_DEFAULT;
- this.fetchLimit = (fetchLimit != null)
- ? Integer.parseInt(fetchLimit.toString())
+ this.fetchLimit = (fetchLimit != null) ? Integer.parseInt(fetchLimit.toString())
: QueryMetadata.FETCH_LIMIT_DEFAULT;
- this.pageSize = (pageSize != null)
- ? Integer.parseInt(pageSize.toString())
- : QueryMetadata.PAGE_SIZE_DEFAULT;
+ this.pageSize = (pageSize != null) ? Integer.parseInt(pageSize.toString()) : QueryMetadata.PAGE_SIZE_DEFAULT;
- this.statementFetchSize = (statementFetchSize != null)
- ? Integer.parseInt(statementFetchSize.toString())
+ this.statementFetchSize = (statementFetchSize != null) ? Integer.parseInt(statementFetchSize.toString())
: QueryMetadata.STATEMENT_FETCH_SIZE_DEFAULT;
- this.fetchingDataRows = (fetchingDataRows != null)
- ? "true".equalsIgnoreCase(fetchingDataRows.toString())
+ this.fetchingDataRows = (fetchingDataRows != null) ? "true".equalsIgnoreCase(fetchingDataRows.toString())
: QueryMetadata.FETCHING_DATA_ROWS_DEFAULT;
- this.cacheStrategy = (cacheStrategy != null) ? QueryCacheStrategy
- .safeValueOf(cacheStrategy.toString()) : QueryCacheStrategy
- .getDefaultStrategy();
+ this.cacheStrategy = (cacheStrategy != null) ? QueryCacheStrategy.safeValueOf(cacheStrategy.toString())
+ : QueryCacheStrategy.getDefaultStrategy();
this.cacheGroups = null;
if (cacheGroups instanceof String[]) {
this.cacheGroups = (String[]) cacheGroups;
- }
- else if (cacheGroups instanceof String) {
+ } else if (cacheGroups instanceof String) {
StringTokenizer toks = new StringTokenizer(cacheGroups.toString(), ",");
this.cacheGroups = new String[toks.countTokens()];
for (int i = 0; i < this.cacheGroups.length; i++) {
@@ -217,9 +202,7 @@ class BaseQueryMetadata implements Query
public void encodeAsXML(XMLEncoder encoder) {
if (fetchingDataRows != QueryMetadata.FETCHING_DATA_ROWS_DEFAULT) {
- encoder.printProperty(
- QueryMetadata.FETCHING_DATA_ROWS_PROPERTY,
- fetchingDataRows);
+ encoder.printProperty(QueryMetadata.FETCHING_DATA_ROWS_PROPERTY, fetchingDataRows);
}
if (fetchOffset != QueryMetadata.FETCH_OFFSET_DEFAULT) {
@@ -234,16 +217,12 @@ class BaseQueryMetadata implements Query
encoder.printProperty(QueryMetadata.PAGE_SIZE_PROPERTY, pageSize);
}
- if (cacheStrategy != null
- && QueryCacheStrategy.getDefaultStrategy() != cacheStrategy) {
- encoder.printProperty(QueryMetadata.CACHE_STRATEGY_PROPERTY, cacheStrategy
- .name());
+ if (cacheStrategy != null && QueryCacheStrategy.getDefaultStrategy() != cacheStrategy) {
+ encoder.printProperty(QueryMetadata.CACHE_STRATEGY_PROPERTY, cacheStrategy.name());
}
if (statementFetchSize != QueryMetadata.STATEMENT_FETCH_SIZE_DEFAULT) {
- encoder.printProperty(
- QueryMetadata.STATEMENT_FETCH_SIZE_PROPERTY,
- statementFetchSize);
+ encoder.printProperty(QueryMetadata.STATEMENT_FETCH_SIZE_PROPERTY, statementFetchSize);
}
if (prefetchTree != null) {