org.apache.cayenne.access.PrefetchProcessorNode Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cayenne-client-nodeps
Show all versions of cayenne-client-nodeps
Cayenne Object Persistence Framework
/*****************************************************************
* 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.access;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.DataObject;
import org.apache.cayenne.Fault;
import org.apache.cayenne.ValueHolder;
import org.apache.cayenne.map.ObjRelationship;
import org.apache.cayenne.query.PrefetchTreeNode;
/**
* A specialized PrefetchTreeNode used for disjoint prefetch resolving.
*
* @since 1.2
* @author Andrus Adamchik
*/
// TODO: Andrus 2/9/2006 optional to-one relationships (Painting -> Artist) are not
// connected by this algorithm. They are being intercepted later when a corresponding
// fault is being resolved, but this seems like a wasteful approach. Test case that
// succeeds, but goes through this wasteful route is
// DataContextPrefetchTst.testPrefetchingToOneNull().
class PrefetchProcessorNode extends PrefetchTreeNode {
List dataRows;
List objects;
ObjRelationship incoming;
ObjectResolver resolver;
Map partitionByParent;
boolean jointChildren;
boolean partitionedByParent;
DataObject lastResolved;
PrefetchProcessorNode(PrefetchTreeNode parent, String segmentPath) {
super(parent, segmentPath);
}
/**
* Sets up derived flags and values for faster lookup during traversal. Called after
* all properties are initialized.
*/
void afterInit() {
partitionedByParent = !phantom
&& incoming != null
&& incoming.isSourceIndependentFromTargetChange();
if (partitionedByParent) {
partitionByParent = new HashMap();
}
}
/**
* Creates a temporary association between child and parent objects. Permanent
* relationship is set using the information created here, by calling
* 'connectToParents'.
*/
void linkToParent(DataObject object, DataObject parent) {
if (parent != null) {
// if a relationship is to-one (i.e. flattened to-one), can connect right
// away....
if (!incoming.isToMany()) {
parent.writeProperty(getName(), object);
}
else {
List peers = (List) partitionByParent.get(parent);
// wrap in a list even if relationship is to-one... will unwrap at the end
// of the processing cycle.
if (peers == null) {
peers = new ArrayList();
partitionByParent.put(parent, peers);
}
// checking for duplicates is needed in case of nested joint prefetches
// when there is more than one row with the same combination of adjacent
// parent and child...
else if (peers.contains(object)) {
return;
}
peers.add(object);
}
}
}
void connectToParents() {
// to-one's were connected earlier...
if (isPartitionedByParent()) {
// depending on whether parent is a "phantom" node,
// use different strategy
PrefetchProcessorNode parent = (PrefetchProcessorNode) getParent();
boolean parentObjectsExist = parent.getObjects() != null
&& parent.getObjects().size() > 0;
if (incoming.isToMany()) {
if (parentObjectsExist) {
connectToNodeParents(parent.getObjects());
}
else {
connectToFaultedParents();
}
}
else {
// optional to-one ... need to fill in unresolved relationships with
// null...
if (parentObjectsExist) {
clearNullRelationships(parent.getObjects());
}
}
}
}
private final void clearNullRelationships(List parentObjects) {
Iterator it = parentObjects.iterator();
while (it.hasNext()) {
DataObject object = (DataObject) it.next();
if (object.readPropertyDirectly(name) instanceof Fault) {
object.writeProperty(name, null);
}
}
}
private final void connectToNodeParents(List parentObjects) {
Iterator it = parentObjects.iterator();
while (it.hasNext()) {
DataObject object = (DataObject) it.next();
List related = (List) partitionByParent.get(object);
connect(object, related);
}
}
private final void connectToFaultedParents() {
Iterator it = partitionByParent.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
DataObject object = (DataObject) entry.getKey();
List related = (List) entry.getValue();
connect(object, related);
}
}
private final void connect(DataObject object, List related) {
if (incoming.isToMany()) {
ValueHolder toManyList = (ValueHolder) object.readProperty(getName());
// TODO, Andrus 11/15/2005 - if list is modified, shouldn't we attempt to
// merge the changes instead of overwriting?
toManyList.setValueDirectly(related != null ? related : new ArrayList(1));
}
else {
// this should've been handled elsewhere
throw new CayenneRuntimeException(
"To-one relationship wasn't handled properly: " + incoming.getName());
}
}
List getDataRows() {
return dataRows;
}
List getObjects() {
return objects;
}
void setResolver(ObjectResolver resolver) {
this.resolver = resolver;
}
ObjectResolver getResolver() {
return resolver;
}
ObjRelationship getIncoming() {
return incoming;
}
void setIncoming(ObjRelationship incoming) {
this.incoming = incoming;
}
void setDataRows(List dataRows) {
this.dataRows = dataRows;
}
void setObjects(List objects) {
this.objects = objects;
}
boolean isJointChildren() {
return jointChildren;
}
void setJointChildren(boolean jointChildren) {
this.jointChildren = jointChildren;
}
boolean isPartitionedByParent() {
return partitionedByParent;
}
DataObject getLastResolved() {
return lastResolved;
}
void setLastResolved(DataObject lastResolved) {
this.lastResolved = lastResolved;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy