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 2014/11/09 19:26:02 UTC
[1/2] cayenne git commit: CAY-1959 Chainable API for SelectQuery
Repository: cayenne
Updated Branches:
refs/heads/master fb8660e76 -> 4d8b2e1a6
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4d8b2e1a/cayenne-server/src/main/java/org/apache/cayenne/query/PrefetchTreeNode.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/PrefetchTreeNode.java b/cayenne-server/src/main/java/org/apache/cayenne/query/PrefetchTreeNode.java
index 70b3a9f..11711fc 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/PrefetchTreeNode.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/PrefetchTreeNode.java
@@ -38,535 +38,579 @@ import org.apache.cayenne.util.XMLSerializable;
*/
public class PrefetchTreeNode implements Serializable, XMLSerializable {
- public static final int UNDEFINED_SEMANTICS = 0;
- public static final int JOINT_PREFETCH_SEMANTICS = 1;
- public static final int DISJOINT_PREFETCH_SEMANTICS = 2;
- public static final int DISJOINT_BY_ID_PREFETCH_SEMANTICS = 3;
-
- protected String name;
- protected boolean phantom;
- protected int semantics;
- protected String ejbqlPathEntityId;
- protected String entityName;
-
- // transient parent allows cloning parts of the tree via serialization
- protected transient PrefetchTreeNode parent;
-
- // Using Collection instead of Map for children storage (even though there cases of
- // lookup by segment) is a reasonable tradeoff considering that
- // each node has no more than a few children and lookup by name doesn't happen on
- // traversal, only during creation.
- protected Collection<PrefetchTreeNode> children;
-
- /**
- * Creates a root node of the prefetch tree. Children can be added to the parent by
- * calling "addPath".
- */
- public PrefetchTreeNode() {
- this(null, null);
- }
-
- /**
- * Creates a phantom PrefetchTreeNode, initializing it with parent node and a name of
- * a relationship segment connecting this node with the parent.
- */
- protected PrefetchTreeNode(PrefetchTreeNode parent, String name) {
- this.parent = parent;
- this.name = name;
- this.phantom = true;
- this.semantics = UNDEFINED_SEMANTICS;
- }
-
- public void encodeAsXML(XMLEncoder encoder) {
- traverse(new XMLEncoderOperation(encoder));
- }
-
- /**
- * Returns the root of the node tree. Root is the topmost parent node that itself has
- * no parent set.
- */
- public PrefetchTreeNode getRoot() {
- return (parent != null) ? parent.getRoot() : this;
- }
-
- /**
- * Returns full prefetch path, that is a dot separated String of node names starting
- * from root and up to and including this node. Note that root "name" is considered to
- * be an empty string.
- */
- public String getPath() {
- return getPath(null);
- }
-
- public String getPath(PrefetchTreeNode upTillParent) {
- if (parent == null || upTillParent == this) {
- return "";
- }
-
- StringBuilder path = new StringBuilder(getName());
- PrefetchTreeNode node = this.getParent();
-
- // root node has no path
- while (node.getParent() != null && node != upTillParent) {
- path.insert(0, node.getName() + ".");
- node = node.getParent();
- }
-
- return path.toString();
- }
-
- /**
- * Returns a subset of nodes with "joint" semantics that are to be prefetched in the
- * same query as the current node. Result excludes this node, regardless of its
- * semantics.
- */
- public Collection<PrefetchTreeNode> adjacentJointNodes() {
- Collection<PrefetchTreeNode> c = new ArrayList<PrefetchTreeNode>();
- traverse(new AdjacentJoinsOperation(c));
- return c;
- }
-
- /**
- * Returns a collection of PrefetchTreeNodes in this tree with joint semantics.
- */
- public Collection<PrefetchTreeNode> jointNodes() {
- Collection<PrefetchTreeNode> c = new ArrayList<PrefetchTreeNode>();
- traverse(new CollectionBuilderOperation(c, false, false, true, false, false));
- return c;
- }
-
- /**
- * Returns a collection of PrefetchTreeNodes with disjoint semantics.
- */
- public Collection<PrefetchTreeNode> disjointNodes() {
- Collection<PrefetchTreeNode> c = new ArrayList<PrefetchTreeNode>();
- traverse(new CollectionBuilderOperation(c, true, false, false, false, false));
- return c;
- }
-
- /**
- * Returns a collection of PrefetchTreeNodes with disjoint semantics
- * @since 3.1
- */
- public Collection<PrefetchTreeNode> disjointByIdNodes() {
- Collection<PrefetchTreeNode> c = new ArrayList<PrefetchTreeNode>();
- traverse(new CollectionBuilderOperation(c, false, true, false, false, false));
- return c;
- }
-
- /**
- * Returns a collection of PrefetchTreeNodes that are not phantoms.
- */
- public Collection<PrefetchTreeNode> nonPhantomNodes() {
- Collection<PrefetchTreeNode> c = new ArrayList<PrefetchTreeNode>();
- traverse(new CollectionBuilderOperation(c, true, true, true, true, false));
- return c;
- }
-
- /**
- * Returns a clone of subtree that includes all joint children
- * starting from this node itself and till the first occurrence of non-joint node
- *
- * @since 3.1
- */
- public PrefetchTreeNode cloneJointSubtree() {
- return cloneJointSubtree(null);
- }
-
- private PrefetchTreeNode cloneJointSubtree(PrefetchTreeNode parent) {
- PrefetchTreeNode cloned = new PrefetchTreeNode(parent, getName());
- if (parent != null) {
- cloned.setSemantics(getSemantics());
- cloned.setPhantom(isPhantom());
- }
-
- if (children != null) {
- for (PrefetchTreeNode child : children) {
- if (child.isJointPrefetch()) {
- cloned.addChild(child.cloneJointSubtree(cloned));
- }
- }
- }
-
- return cloned;
- }
-
- /**
- * Traverses the tree depth-first, invoking callback methods of the processor when
- * passing through the nodes.
- */
- public void traverse(PrefetchProcessor processor) {
-
- boolean result = false;
-
- if (isPhantom()) {
- result = processor.startPhantomPrefetch(this);
- }
- else if (isDisjointPrefetch()) {
- result = processor.startDisjointPrefetch(this);
- }
- else if (isDisjointByIdPrefetch()) {
- result = processor.startDisjointByIdPrefetch(this);
- }
- else if (isJointPrefetch()) {
- result = processor.startJointPrefetch(this);
- }
- else {
- result = processor.startUnknownPrefetch(this);
- }
-
- // process children unless processing is blocked...
- if (result && children != null) {
- for (PrefetchTreeNode child : children) {
- child.traverse(processor);
- }
- }
-
- // call finish regardless of whether children were processed
- processor.finishPrefetch(this);
- }
-
- /**
- * Looks up an existing node in the tree desribed by the dot-separated path. Will
- * return null if no matching child exists.
- */
- public PrefetchTreeNode getNode(String path) {
- if (Util.isEmptyString(path)) {
- throw new IllegalArgumentException("Empty path: " + path);
- }
-
- PrefetchTreeNode node = this;
- StringTokenizer toks = new StringTokenizer(path, Entity.PATH_SEPARATOR);
- while (toks.hasMoreTokens() && node != null) {
- String segment = toks.nextToken();
- node = node.getChild(segment);
- }
-
- return node;
- }
-
- /**
- * Adds a "path" with specified semantics to this prefetch node. All yet non-existent
- * nodes in the created path will be marked as phantom.
- *
- * @return the last segment in the created path.
- */
- public PrefetchTreeNode addPath(String path) {
- if (Util.isEmptyString(path)) {
- throw new IllegalArgumentException("Empty path: " + path);
- }
-
- PrefetchTreeNode node = this;
- StringTokenizer toks = new StringTokenizer(path, Entity.PATH_SEPARATOR);
- while (toks.hasMoreTokens()) {
- String segment = toks.nextToken();
-
- PrefetchTreeNode child = node.getChild(segment);
- if (child == null) {
- child = new PrefetchTreeNode(node, segment);
- node.addChild(child);
- }
-
- node = child;
- }
-
- return node;
- }
-
- /**
- * Removes or makes phantom a node defined by this path. If the node for this path
- * doesn't have any children, it is removed, otherwise it is made phantom.
- */
- public void removePath(String path) {
-
- PrefetchTreeNode node = getNode(path);
- while (node != null) {
-
- if (node.children != null) {
- node.setPhantom(true);
- break;
- }
-
- String segment = node.getName();
-
- node = node.getParent();
-
- if (node != null) {
- node.removeChild(segment);
- }
- }
- }
-
- public void addChild(PrefetchTreeNode child) {
-
- if (Util.isEmptyString(child.getName())) {
- throw new IllegalArgumentException("Child has no segmentPath: " + child);
- }
-
- if (child.getParent() != this) {
- child.getParent().removeChild(child.getName());
- child.parent = this;
- }
-
- if (children == null) {
- children = new ArrayList<PrefetchTreeNode>(4);
- }
-
- children.add(child);
- }
-
- public void removeChild(PrefetchTreeNode child) {
- if (children != null && child != null) {
- children.remove(child);
- child.parent = null;
- }
- }
-
- protected void removeChild(String segment) {
- if (children != null) {
- PrefetchTreeNode child = getChild(segment);
- if (child != null) {
- children.remove(child);
- child.parent = null;
- }
- }
- }
-
- protected PrefetchTreeNode getChild(String segment) {
- if (children != null) {
- for (PrefetchTreeNode child : children) {
- if (segment.equals(child.getName())) {
- return child;
- }
- }
- }
-
- return null;
- }
-
- public PrefetchTreeNode getParent() {
- return parent;
- }
-
- /**
- * Returns an unmodifiable collection of children.
- */
- public Collection<PrefetchTreeNode> getChildren() {
- return children == null ? Collections.EMPTY_SET : Collections
- .unmodifiableCollection(children);
- }
-
- public boolean hasChildren() {
- return children != null && !children.isEmpty();
- }
-
- public String getName() {
- return name;
- }
-
- public boolean isPhantom() {
- return phantom;
- }
-
- public void setPhantom(boolean phantom) {
- this.phantom = phantom;
- }
-
- public int getSemantics() {
- return semantics;
- }
-
- public void setSemantics(int semantics) {
- this.semantics = semantics;
- }
-
- public boolean isJointPrefetch() {
- return semantics == JOINT_PREFETCH_SEMANTICS;
- }
-
- public boolean isDisjointPrefetch() {
- return semantics == DISJOINT_PREFETCH_SEMANTICS;
- }
-
- public boolean isDisjointByIdPrefetch() {
- return semantics == DISJOINT_BY_ID_PREFETCH_SEMANTICS;
- }
-
- public String getEjbqlPathEntityId() {
- return ejbqlPathEntityId;
- }
-
- public void setEjbqlPathEntityId(String ejbqlPathEntityId) {
- this.ejbqlPathEntityId = ejbqlPathEntityId;
- }
-
- public String getEntityName() {
- return entityName;
- }
-
- public void setEntityName(String entityName) {
- this.entityName = entityName;
- }
-
-
- // **** custom serialization that supports serializing subtrees...
-
- // implementing 'readResolve' instead of 'readObject' so that this would work with
- // hessian
- private Object readResolve() throws ObjectStreamException {
-
- if (hasChildren()) {
- for (PrefetchTreeNode child : children) {
- child.parent = this;
- }
- }
-
- return this;
- }
-
- // **** common tree operations
-
- // An operation that encodes prefetch tree as XML.
- class XMLEncoderOperation implements PrefetchProcessor {
-
- XMLEncoder encoder;
-
- XMLEncoderOperation(XMLEncoder encoder) {
- this.encoder = encoder;
- }
-
- public boolean startPhantomPrefetch(PrefetchTreeNode node) {
- // don't encode phantoms
- return true;
- }
-
- public boolean startDisjointPrefetch(PrefetchTreeNode node) {
- encoder.print("<prefetch type=\"disjoint\">");
- encoder.print(node.getPath());
- encoder.println("</prefetch>");
- return true;
- }
-
- public boolean startDisjointByIdPrefetch(PrefetchTreeNode node) {
- encoder.print("<prefetch type=\"disjointById\">");
- encoder.print(node.getPath());
- encoder.println("</prefetch>");
- return true;
- }
-
- public boolean startJointPrefetch(PrefetchTreeNode node) {
- encoder.print("<prefetch type=\"joint\">");
- encoder.print(node.getPath());
- encoder.println("</prefetch>");
- return true;
- }
-
- public boolean startUnknownPrefetch(PrefetchTreeNode node) {
- encoder.print("<prefetch>");
- encoder.print(node.getPath());
- encoder.println("</prefetch>");
-
- return true;
- }
-
- public void finishPrefetch(PrefetchTreeNode node) {
- // noop
- }
- }
-
- // An operation that collects all nodes in a single collection.
- class CollectionBuilderOperation implements PrefetchProcessor {
-
- Collection<PrefetchTreeNode> nodes;
- boolean includePhantom;
- boolean includeDisjoint;
- boolean includeDisjointById;
- boolean includeJoint;
- boolean includeUnknown;
-
- CollectionBuilderOperation(Collection<PrefetchTreeNode> nodes, boolean includeDisjoint,
- boolean includeDisjointById, boolean includeJoint, boolean includeUnknown, boolean includePhantom) {
- this.nodes = nodes;
-
- this.includeDisjoint = includeDisjoint;
- this.includeDisjointById = includeDisjointById;
- this.includeJoint = includeJoint;
- this.includeUnknown = includeUnknown;
- this.includePhantom = includePhantom;
- }
-
- public boolean startPhantomPrefetch(PrefetchTreeNode node) {
- if (includePhantom) {
- nodes.add(node);
- }
-
- return true;
- }
-
- public boolean startDisjointPrefetch(PrefetchTreeNode node) {
- if (includeDisjoint) {
- nodes.add(node);
- }
- return true;
- }
-
- public boolean startDisjointByIdPrefetch(PrefetchTreeNode node) {
- if (includeDisjointById) {
- nodes.add(node);
- }
- return true;
- }
-
- public boolean startJointPrefetch(PrefetchTreeNode node) {
- if (includeJoint) {
- nodes.add(node);
- }
- return true;
- }
-
- public boolean startUnknownPrefetch(PrefetchTreeNode node) {
- if (includeUnknown) {
- nodes.add(node);
- }
- return true;
- }
-
- public void finishPrefetch(PrefetchTreeNode node) {
- }
- }
-
- class AdjacentJoinsOperation implements PrefetchProcessor {
-
- Collection<PrefetchTreeNode> nodes;
-
- AdjacentJoinsOperation(Collection<PrefetchTreeNode> nodes) {
- this.nodes = nodes;
- }
-
- public boolean startPhantomPrefetch(PrefetchTreeNode node) {
- return true;
- }
-
- public boolean startDisjointPrefetch(PrefetchTreeNode node) {
- return node == PrefetchTreeNode.this;
- }
-
- public boolean startDisjointByIdPrefetch(PrefetchTreeNode node) {
- return startDisjointPrefetch(node);
- }
-
- public boolean startJointPrefetch(PrefetchTreeNode node) {
- if (node != PrefetchTreeNode.this) {
- nodes.add(node);
- }
- return true;
- }
-
- public boolean startUnknownPrefetch(PrefetchTreeNode node) {
- return node == PrefetchTreeNode.this;
- }
-
- public void finishPrefetch(PrefetchTreeNode node) {
- }
- }
+ private static final long serialVersionUID = 1112629504025820837L;
+
+ public static final int UNDEFINED_SEMANTICS = 0;
+ public static final int JOINT_PREFETCH_SEMANTICS = 1;
+ public static final int DISJOINT_PREFETCH_SEMANTICS = 2;
+ public static final int DISJOINT_BY_ID_PREFETCH_SEMANTICS = 3;
+
+ protected String name;
+ protected boolean phantom;
+ protected int semantics;
+ protected String ejbqlPathEntityId;
+ protected String entityName;
+
+ // transient parent allows cloning parts of the tree via serialization
+ protected transient PrefetchTreeNode parent;
+
+ // Using Collection instead of Map for children storage (even though there
+ // cases of
+ // lookup by segment) is a reasonable tradeoff considering that
+ // each node has no more than a few children and lookup by name doesn't
+ // happen on
+ // traversal, only during creation.
+ protected Collection<PrefetchTreeNode> children;
+
+ /**
+ * Creates a root node of the prefetch tree. Children can be added to the
+ * parent by calling "addPath".
+ */
+ public PrefetchTreeNode() {
+ this(null, null);
+ }
+
+ /**
+ * Creates a phantom PrefetchTreeNode, initializing it with parent node and
+ * a name of a relationship segment connecting this node with the parent.
+ */
+ protected PrefetchTreeNode(PrefetchTreeNode parent, String name) {
+ this.parent = parent;
+ this.name = name;
+ this.phantom = true;
+ this.semantics = UNDEFINED_SEMANTICS;
+ }
+
+ public void encodeAsXML(XMLEncoder encoder) {
+ traverse(new XMLEncoderOperation(encoder));
+ }
+
+ /**
+ * Returns the root of the node tree. Root is the topmost parent node that
+ * itself has no parent set.
+ */
+ public PrefetchTreeNode getRoot() {
+ return (parent != null) ? parent.getRoot() : this;
+ }
+
+ /**
+ * Returns full prefetch path, that is a dot separated String of node names
+ * starting from root and up to and including this node. Note that root
+ * "name" is considered to be an empty string.
+ */
+ public String getPath() {
+ return getPath(null);
+ }
+
+ public String getPath(PrefetchTreeNode upTillParent) {
+ if (parent == null || upTillParent == this) {
+ return "";
+ }
+
+ StringBuilder path = new StringBuilder(getName());
+ PrefetchTreeNode node = this.getParent();
+
+ // root node has no path
+ while (node.getParent() != null && node != upTillParent) {
+ path.insert(0, node.getName() + ".");
+ node = node.getParent();
+ }
+
+ return path.toString();
+ }
+
+ /**
+ * Returns a subset of nodes with "joint" semantics that are to be
+ * prefetched in the same query as the current node. Result excludes this
+ * node, regardless of its semantics.
+ */
+ public Collection<PrefetchTreeNode> adjacentJointNodes() {
+ Collection<PrefetchTreeNode> c = new ArrayList<PrefetchTreeNode>();
+ traverse(new AdjacentJoinsOperation(c));
+ return c;
+ }
+
+ /**
+ * Returns a collection of PrefetchTreeNodes in this tree with joint
+ * semantics.
+ */
+ public Collection<PrefetchTreeNode> jointNodes() {
+ Collection<PrefetchTreeNode> c = new ArrayList<PrefetchTreeNode>();
+ traverse(new CollectionBuilderOperation(c, false, false, true, false, false));
+ return c;
+ }
+
+ /**
+ * Returns a collection of PrefetchTreeNodes with disjoint semantics.
+ */
+ public Collection<PrefetchTreeNode> disjointNodes() {
+ Collection<PrefetchTreeNode> c = new ArrayList<PrefetchTreeNode>();
+ traverse(new CollectionBuilderOperation(c, true, false, false, false, false));
+ return c;
+ }
+
+ /**
+ * Returns a collection of PrefetchTreeNodes with disjoint semantics
+ *
+ * @since 3.1
+ */
+ public Collection<PrefetchTreeNode> disjointByIdNodes() {
+ Collection<PrefetchTreeNode> c = new ArrayList<PrefetchTreeNode>();
+ traverse(new CollectionBuilderOperation(c, false, true, false, false, false));
+ return c;
+ }
+
+ /**
+ * Returns a collection of PrefetchTreeNodes that are not phantoms.
+ */
+ public Collection<PrefetchTreeNode> nonPhantomNodes() {
+ Collection<PrefetchTreeNode> c = new ArrayList<PrefetchTreeNode>();
+ traverse(new CollectionBuilderOperation(c, true, true, true, true, false));
+ return c;
+ }
+
+ /**
+ * Returns a clone of subtree that includes all joint children starting from
+ * this node itself and till the first occurrence of non-joint node
+ *
+ * @since 3.1
+ */
+ public PrefetchTreeNode cloneJointSubtree() {
+ return cloneJointSubtree(null);
+ }
+
+ private PrefetchTreeNode cloneJointSubtree(PrefetchTreeNode parent) {
+ PrefetchTreeNode cloned = new PrefetchTreeNode(parent, getName());
+ if (parent != null) {
+ cloned.setSemantics(getSemantics());
+ cloned.setPhantom(isPhantom());
+ }
+
+ if (children != null) {
+ for (PrefetchTreeNode child : children) {
+ if (child.isJointPrefetch()) {
+ cloned.addChild(child.cloneJointSubtree(cloned));
+ }
+ }
+ }
+
+ return cloned;
+ }
+
+ /**
+ * Traverses the tree depth-first, invoking callback methods of the
+ * processor when passing through the nodes.
+ */
+ public void traverse(PrefetchProcessor processor) {
+
+ boolean result = false;
+
+ if (isPhantom()) {
+ result = processor.startPhantomPrefetch(this);
+ } else if (isDisjointPrefetch()) {
+ result = processor.startDisjointPrefetch(this);
+ } else if (isDisjointByIdPrefetch()) {
+ result = processor.startDisjointByIdPrefetch(this);
+ } else if (isJointPrefetch()) {
+ result = processor.startJointPrefetch(this);
+ } else {
+ result = processor.startUnknownPrefetch(this);
+ }
+
+ // process children unless processing is blocked...
+ if (result && children != null) {
+ for (PrefetchTreeNode child : children) {
+ child.traverse(processor);
+ }
+ }
+
+ // call finish regardless of whether children were processed
+ processor.finishPrefetch(this);
+ }
+
+ /**
+ * Looks up an existing node in the tree desribed by the dot-separated path.
+ * Will return null if no matching child exists.
+ */
+ public PrefetchTreeNode getNode(String path) {
+ if (Util.isEmptyString(path)) {
+ throw new IllegalArgumentException("Empty path: " + path);
+ }
+
+ PrefetchTreeNode node = this;
+ StringTokenizer toks = new StringTokenizer(path, Entity.PATH_SEPARATOR);
+ while (toks.hasMoreTokens() && node != null) {
+ String segment = toks.nextToken();
+ node = node.getChild(segment);
+ }
+
+ return node;
+ }
+
+ /**
+ * Adds a "path" with specified semantics to this prefetch node. All yet
+ * non-existent nodes in the created path will be marked as phantom.
+ *
+ * @return the last segment in the created path.
+ */
+ public PrefetchTreeNode addPath(String path) {
+ if (Util.isEmptyString(path)) {
+ throw new IllegalArgumentException("Empty path: " + path);
+ }
+
+ PrefetchTreeNode node = this;
+ StringTokenizer toks = new StringTokenizer(path, Entity.PATH_SEPARATOR);
+ while (toks.hasMoreTokens()) {
+ String segment = toks.nextToken();
+
+ PrefetchTreeNode child = node.getChild(segment);
+ if (child == null) {
+ child = new PrefetchTreeNode(node, segment);
+ node.addChild(child);
+ }
+
+ node = child;
+ }
+
+ return node;
+ }
+
+ /**
+ * Merges {@link PrefetchTreeNode} into the current prefetch tree, cloning
+ * the nodes added to this tree. Merged nodes semantics (if defined) and
+ * non-phantom status are applied to the nodes of this tree.
+ *
+ * @param node
+ * a root node of a tree to merge into this tree. The path of the
+ * merged node within the resulting tree is determined from its
+ * name.
+ *
+ * @since 4.0
+ */
+ public void merge(PrefetchTreeNode node) {
+ if (node == null) {
+ throw new NullPointerException("Null node");
+ }
+
+ PrefetchTreeNode start = node.getName() != null ? addPath(node.getName()) : this;
+ merge(start, node);
+ }
+
+ void merge(PrefetchTreeNode original, PrefetchTreeNode toMerge) {
+
+ if (toMerge.getSemantics() != UNDEFINED_SEMANTICS) {
+ original.setSemantics(toMerge.getSemantics());
+ }
+
+ if (!toMerge.isPhantom()) {
+ original.setPhantom(false);
+ }
+
+ for (PrefetchTreeNode childToMerge : toMerge.getChildren()) {
+
+ PrefetchTreeNode childOrigin = original.getChild(childToMerge.getName());
+ if (childOrigin == null) {
+ childOrigin = original.addPath(childToMerge.getName());
+ }
+
+ merge(childOrigin, childToMerge);
+ }
+ }
+
+ /**
+ * Removes or makes phantom a node defined by this path. If the node for
+ * this path doesn't have any children, it is removed, otherwise it is made
+ * phantom.
+ */
+ public void removePath(String path) {
+
+ PrefetchTreeNode node = getNode(path);
+ while (node != null) {
+
+ if (node.children != null) {
+ node.setPhantom(true);
+ break;
+ }
+
+ String segment = node.getName();
+
+ node = node.getParent();
+
+ if (node != null) {
+ node.removeChild(segment);
+ }
+ }
+ }
+
+ public void addChild(PrefetchTreeNode child) {
+
+ if (Util.isEmptyString(child.getName())) {
+ throw new IllegalArgumentException("Child has no segmentPath: " + child);
+ }
+
+ if (child.getParent() != this) {
+ child.getParent().removeChild(child.getName());
+ child.parent = this;
+ }
+
+ if (children == null) {
+ children = new ArrayList<PrefetchTreeNode>(4);
+ }
+
+ children.add(child);
+ }
+
+ public void removeChild(PrefetchTreeNode child) {
+ if (children != null && child != null) {
+ children.remove(child);
+ child.parent = null;
+ }
+ }
+
+ protected void removeChild(String segment) {
+ if (children != null) {
+ PrefetchTreeNode child = getChild(segment);
+ if (child != null) {
+ children.remove(child);
+ child.parent = null;
+ }
+ }
+ }
+
+ protected PrefetchTreeNode getChild(String segment) {
+ if (children != null) {
+ for (PrefetchTreeNode child : children) {
+ if (segment.equals(child.getName())) {
+ return child;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public PrefetchTreeNode getParent() {
+ return parent;
+ }
+
+ /**
+ * Returns an unmodifiable collection of children.
+ */
+ public Collection<PrefetchTreeNode> getChildren() {
+ return children == null ? Collections.<PrefetchTreeNode> emptySet() : children;
+ }
+
+ public boolean hasChildren() {
+ return children != null && !children.isEmpty();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isPhantom() {
+ return phantom;
+ }
+
+ public void setPhantom(boolean phantom) {
+ this.phantom = phantom;
+ }
+
+ public int getSemantics() {
+ return semantics;
+ }
+
+ public void setSemantics(int semantics) {
+ this.semantics = semantics;
+ }
+
+ public boolean isJointPrefetch() {
+ return semantics == JOINT_PREFETCH_SEMANTICS;
+ }
+
+ public boolean isDisjointPrefetch() {
+ return semantics == DISJOINT_PREFETCH_SEMANTICS;
+ }
+
+ public boolean isDisjointByIdPrefetch() {
+ return semantics == DISJOINT_BY_ID_PREFETCH_SEMANTICS;
+ }
+
+ public String getEjbqlPathEntityId() {
+ return ejbqlPathEntityId;
+ }
+
+ public void setEjbqlPathEntityId(String ejbqlPathEntityId) {
+ this.ejbqlPathEntityId = ejbqlPathEntityId;
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public void setEntityName(String entityName) {
+ this.entityName = entityName;
+ }
+
+ // **** custom serialization that supports serializing subtrees...
+
+ // implementing 'readResolve' instead of 'readObject' so that this would
+ // work with
+ // hessian
+ private Object readResolve() throws ObjectStreamException {
+
+ if (hasChildren()) {
+ for (PrefetchTreeNode child : children) {
+ child.parent = this;
+ }
+ }
+
+ return this;
+ }
+
+ // **** common tree operations
+
+ // An operation that encodes prefetch tree as XML.
+ class XMLEncoderOperation implements PrefetchProcessor {
+
+ XMLEncoder encoder;
+
+ XMLEncoderOperation(XMLEncoder encoder) {
+ this.encoder = encoder;
+ }
+
+ public boolean startPhantomPrefetch(PrefetchTreeNode node) {
+ // don't encode phantoms
+ return true;
+ }
+
+ public boolean startDisjointPrefetch(PrefetchTreeNode node) {
+ encoder.print("<prefetch type=\"disjoint\">");
+ encoder.print(node.getPath());
+ encoder.println("</prefetch>");
+ return true;
+ }
+
+ public boolean startDisjointByIdPrefetch(PrefetchTreeNode node) {
+ encoder.print("<prefetch type=\"disjointById\">");
+ encoder.print(node.getPath());
+ encoder.println("</prefetch>");
+ return true;
+ }
+
+ public boolean startJointPrefetch(PrefetchTreeNode node) {
+ encoder.print("<prefetch type=\"joint\">");
+ encoder.print(node.getPath());
+ encoder.println("</prefetch>");
+ return true;
+ }
+
+ public boolean startUnknownPrefetch(PrefetchTreeNode node) {
+ encoder.print("<prefetch>");
+ encoder.print(node.getPath());
+ encoder.println("</prefetch>");
+
+ return true;
+ }
+
+ public void finishPrefetch(PrefetchTreeNode node) {
+ // noop
+ }
+ }
+
+ // An operation that collects all nodes in a single collection.
+ class CollectionBuilderOperation implements PrefetchProcessor {
+
+ Collection<PrefetchTreeNode> nodes;
+ boolean includePhantom;
+ boolean includeDisjoint;
+ boolean includeDisjointById;
+ boolean includeJoint;
+ boolean includeUnknown;
+
+ CollectionBuilderOperation(Collection<PrefetchTreeNode> nodes, boolean includeDisjoint,
+ boolean includeDisjointById, boolean includeJoint, boolean includeUnknown, boolean includePhantom) {
+ this.nodes = nodes;
+
+ this.includeDisjoint = includeDisjoint;
+ this.includeDisjointById = includeDisjointById;
+ this.includeJoint = includeJoint;
+ this.includeUnknown = includeUnknown;
+ this.includePhantom = includePhantom;
+ }
+
+ public boolean startPhantomPrefetch(PrefetchTreeNode node) {
+ if (includePhantom) {
+ nodes.add(node);
+ }
+
+ return true;
+ }
+
+ public boolean startDisjointPrefetch(PrefetchTreeNode node) {
+ if (includeDisjoint) {
+ nodes.add(node);
+ }
+ return true;
+ }
+
+ public boolean startDisjointByIdPrefetch(PrefetchTreeNode node) {
+ if (includeDisjointById) {
+ nodes.add(node);
+ }
+ return true;
+ }
+
+ public boolean startJointPrefetch(PrefetchTreeNode node) {
+ if (includeJoint) {
+ nodes.add(node);
+ }
+ return true;
+ }
+
+ public boolean startUnknownPrefetch(PrefetchTreeNode node) {
+ if (includeUnknown) {
+ nodes.add(node);
+ }
+ return true;
+ }
+
+ public void finishPrefetch(PrefetchTreeNode node) {
+ }
+ }
+
+ class AdjacentJoinsOperation implements PrefetchProcessor {
+
+ Collection<PrefetchTreeNode> nodes;
+
+ AdjacentJoinsOperation(Collection<PrefetchTreeNode> nodes) {
+ this.nodes = nodes;
+ }
+
+ public boolean startPhantomPrefetch(PrefetchTreeNode node) {
+ return true;
+ }
+
+ public boolean startDisjointPrefetch(PrefetchTreeNode node) {
+ return node == PrefetchTreeNode.this;
+ }
+
+ public boolean startDisjointByIdPrefetch(PrefetchTreeNode node) {
+ return startDisjointPrefetch(node);
+ }
+
+ public boolean startJointPrefetch(PrefetchTreeNode node) {
+ if (node != PrefetchTreeNode.this) {
+ nodes.add(node);
+ }
+ return true;
+ }
+
+ public boolean startUnknownPrefetch(PrefetchTreeNode node) {
+ return node == PrefetchTreeNode.this;
+ }
+
+ public void finishPrefetch(PrefetchTreeNode node) {
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4d8b2e1a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java
index 8046a8c..b7a4fdf 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java
@@ -588,11 +588,8 @@ public class SelectQuery<T> extends AbstractQuery implements ParameterizedQuery,
*
* @since 4.0
*/
- public PrefetchTreeNode addPrefetch(PrefetchTreeNode prefetchElement) {
- String path = prefetchElement.getPath();
- int semantics = prefetchElement.getSemantics();
-
- return metaData.addPrefetch(path, semantics);
+ public void addPrefetch(PrefetchTreeNode prefetchElement) {
+ metaData.mergePrefetch(prefetchElement);
}
/**
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4d8b2e1a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextDisjointByIdPrefetch_ExtrasIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextDisjointByIdPrefetch_ExtrasIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextDisjointByIdPrefetch_ExtrasIT.java
index 9b84973..daa9acd 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextDisjointByIdPrefetch_ExtrasIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextDisjointByIdPrefetch_ExtrasIT.java
@@ -347,6 +347,7 @@ public class DataContextDisjointByIdPrefetch_ExtrasIT extends ServerCase {
queryInterceptor.runWithQueriesBlocked(new UnitTestClosure() {
+ @Override
public void execute() {
assertFalse(result.isEmpty());
[2/2] cayenne git commit: CAY-1959 Chainable API for SelectQuery
Posted by aa...@apache.org.
CAY-1959 Chainable API for SelectQuery
* merge method for PrefetchTreeNode for more comprehensive prefetch subtree merging
* using the new merge in SelectQuery as well
UPGRADE NOTES:
The most visible side effect of this change is that Property.xyzPrefetch() method will now return
a root of the prefetch tree that can merged into the query instead of a leaf that can't.
So in an unlikely case someone did MyEntity.MY_PROPERTY.disjoint().setSemantics(JOINT), the last part in this chain
is not longer going to work.
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/4d8b2e1a
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/4d8b2e1a
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/4d8b2e1a
Branch: refs/heads/master
Commit: 4d8b2e1a6a6db08bd68ffab227381eb981111893
Parents: fb8660e
Author: aadamchik <aa...@apache.org>
Authored: Sun Nov 9 21:20:21 2014 +0300
Committer: aadamchik <aa...@apache.org>
Committed: Sun Nov 9 21:24:50 2014 +0300
----------------------------------------------------------------------
.../cayenne/query/PrefetchTreeNodeTest.java | 169 ++-
.../java/org/apache/cayenne/exp/Property.java | 812 ++++++-------
.../apache/cayenne/query/BaseQueryMetadata.java | 839 ++++++-------
.../apache/cayenne/query/PrefetchTreeNode.java | 1106 +++++++++---------
.../org/apache/cayenne/query/SelectQuery.java | 7 +-
...ataContextDisjointByIdPrefetch_ExtrasIT.java | 1 +
6 files changed, 1546 insertions(+), 1388 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4d8b2e1a/cayenne-client/src/test/java/org/apache/cayenne/query/PrefetchTreeNodeTest.java
----------------------------------------------------------------------
diff --git a/cayenne-client/src/test/java/org/apache/cayenne/query/PrefetchTreeNodeTest.java b/cayenne-client/src/test/java/org/apache/cayenne/query/PrefetchTreeNodeTest.java
index 90d4169..1dee8a9 100644
--- a/cayenne-client/src/test/java/org/apache/cayenne/query/PrefetchTreeNodeTest.java
+++ b/cayenne-client/src/test/java/org/apache/cayenne/query/PrefetchTreeNodeTest.java
@@ -18,51 +18,142 @@
****************************************************************/
package org.apache.cayenne.query;
-import org.apache.cayenne.map.EntityResolver;
-import org.apache.cayenne.remote.hessian.service.HessianUtil;
-import org.junit.Test;
-
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.remote.hessian.service.HessianUtil;
+import org.junit.Test;
public class PrefetchTreeNodeTest {
- @Test
- public void testTreeSerializationWithHessian() throws Exception {
- PrefetchTreeNode n1 = new PrefetchTreeNode();
- PrefetchTreeNode n2 = n1.addPath("abc");
-
- PrefetchTreeNode nc1 = (PrefetchTreeNode) HessianUtil.cloneViaClientServerSerialization(n1,
- new EntityResolver());
- assertNotNull(nc1);
-
- PrefetchTreeNode nc2 = nc1.getNode("abc");
- assertNotNull(nc2);
- assertNotSame(nc2, n2);
- assertSame(nc1, nc2.getParent());
- assertEquals("abc", nc2.getName());
- }
-
- @Test
- public void testSubtreeSerializationWithHessian() throws Exception {
- PrefetchTreeNode n1 = new PrefetchTreeNode();
- PrefetchTreeNode n2 = n1.addPath("abc");
- PrefetchTreeNode n3 = n2.addPath("xyz");
-
- // test that substree was serialized as independent tree, instead of
- // sucking
- PrefetchTreeNode nc2 = (PrefetchTreeNode) HessianUtil.cloneViaClientServerSerialization(n2,
- new EntityResolver());
- assertNotNull(nc2);
- assertNull(nc2.getParent());
-
- PrefetchTreeNode nc3 = nc2.getNode("xyz");
- assertNotNull(nc3);
- assertNotSame(nc3, n3);
- assertSame(nc2, nc3.getParent());
- assertEquals("xyz", nc3.getName());
- }
+ @Test
+ public void testTreeSerializationWithHessian() throws Exception {
+ PrefetchTreeNode n1 = new PrefetchTreeNode();
+ PrefetchTreeNode n2 = n1.addPath("abc");
+
+ PrefetchTreeNode nc1 = (PrefetchTreeNode) HessianUtil.cloneViaClientServerSerialization(n1,
+ new EntityResolver());
+ assertNotNull(nc1);
+
+ PrefetchTreeNode nc2 = nc1.getNode("abc");
+ assertNotNull(nc2);
+ assertNotSame(nc2, n2);
+ assertSame(nc1, nc2.getParent());
+ assertEquals("abc", nc2.getName());
+ }
+
+ @Test
+ public void testSubtreeSerializationWithHessian() throws Exception {
+ PrefetchTreeNode n1 = new PrefetchTreeNode();
+ PrefetchTreeNode n2 = n1.addPath("abc");
+ PrefetchTreeNode n3 = n2.addPath("xyz");
+
+ // test that substree was serialized as independent tree, instead of
+ // sucking
+ PrefetchTreeNode nc2 = (PrefetchTreeNode) HessianUtil.cloneViaClientServerSerialization(n2,
+ new EntityResolver());
+ assertNotNull(nc2);
+ assertNull(nc2.getParent());
+
+ PrefetchTreeNode nc3 = nc2.getNode("xyz");
+ assertNotNull(nc3);
+ assertNotSame(nc3, n3);
+ assertSame(nc2, nc3.getParent());
+ assertEquals("xyz", nc3.getName());
+ }
+
+ @Test
+ public void testMerge() {
+ PrefetchTreeNode original = new PrefetchTreeNode();
+ original.addPath("a").setPhantom(true);
+ original.addPath("a.b").setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
+ original.addPath("a.b").setPhantom(false);
+ original.addPath("c").setSemantics(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS);
+ original.addPath("c").setPhantom(false);
+ original.addPath("f").setSemantics(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS);
+ original.addPath("f").setPhantom(false);
+
+ PrefetchTreeNode toMerge = new PrefetchTreeNode();
+ toMerge.addPath("a").setPhantom(false);
+ toMerge.addPath("a.b").setSemantics(PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS);
+ toMerge.addPath("d.e").setSemantics(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS);
+ toMerge.addPath("d.e").setPhantom(false);
+ toMerge.addPath("c").setSemantics(PrefetchTreeNode.UNDEFINED_SEMANTICS);
+
+ original.merge(toMerge);
+
+ assertSame(original, original.getRoot());
+ assertEquals(4, original.getChildren().size());
+
+ PrefetchTreeNode mergedA = original.getChild("a");
+ assertEquals(1, mergedA.getChildren().size());
+ assertFalse("Phantom flag wasn't turned off", mergedA.isPhantom());
+ assertEquals(PrefetchTreeNode.UNDEFINED_SEMANTICS, mergedA.getSemantics());
+
+ PrefetchTreeNode mergedB = mergedA.getChild("b");
+ assertEquals(0, mergedB.getChildren().size());
+ assertFalse(mergedB.isPhantom());
+ assertEquals("Semantics was't merged", PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS,
+ mergedB.getSemantics());
+
+ PrefetchTreeNode mergedC = original.getChild("c");
+ assertEquals(0, mergedC.getChildren().size());
+ assertFalse(mergedC.isPhantom());
+ assertEquals("Semantics was overridden to undefined", PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS,
+ mergedC.getSemantics());
+
+ PrefetchTreeNode mergedD = original.getChild("d");
+ assertEquals(1, mergedD.getChildren().size());
+ assertTrue(mergedD.isPhantom());
+ assertEquals(PrefetchTreeNode.UNDEFINED_SEMANTICS, mergedD.getSemantics());
+ assertNotSame("Merged node wasn't cloned", toMerge.getChild("d"), mergedD);
+
+ PrefetchTreeNode mergedE = mergedD.getChild("e");
+ assertEquals(0, mergedE.getChildren().size());
+ assertFalse(mergedE.isPhantom());
+ assertEquals(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS, mergedE.getSemantics());
+
+ PrefetchTreeNode mergedF = original.getChild("f");
+ assertEquals(0, mergedF.getChildren().size());
+ assertFalse(mergedF.isPhantom());
+ assertEquals(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS, mergedF.getSemantics());
+ }
+
+ @Test
+ public void testMerge_NonRoot() {
+ PrefetchTreeNode original = new PrefetchTreeNode();
+ original.addPath("a").setPhantom(true);
+ original.addPath("a.b").setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
+ original.addPath("a.b").setPhantom(false);
+
+ PrefetchTreeNode toMerge = new PrefetchTreeNode(null, "a.b.c");
+ toMerge.setPhantom(false);
+ toMerge.setSemantics(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS);
+
+ original.merge(toMerge);
+
+ assertSame(original, original.getRoot());
+ assertEquals(1, original.getChildren().size());
+
+ PrefetchTreeNode mergedA = original.getChild("a");
+ assertEquals(1, mergedA.getChildren().size());
+ assertTrue(mergedA.isPhantom());
+ assertEquals(PrefetchTreeNode.UNDEFINED_SEMANTICS, mergedA.getSemantics());
+
+ PrefetchTreeNode mergedB = mergedA.getChild("b");
+ assertEquals(1, mergedB.getChildren().size());
+ assertFalse(mergedB.isPhantom());
+ assertEquals(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS, mergedB.getSemantics());
+
+ PrefetchTreeNode mergedC = mergedB.getChild("c");
+ assertEquals(0, mergedC.getChildren().size());
+ assertFalse(mergedC.isPhantom());
+ assertEquals(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS, mergedC.getSemantics());
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4d8b2e1a/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
index 999b346..48f1549 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
@@ -48,405 +48,417 @@ import org.apache.cayenne.reflect.PropertyUtils;
*/
public class Property<E> {
- /**
- * Name of the property in the object
- */
- private final String name;
-
- /**
- * Constructs a new property with the given name.
- */
- public Property(String name) {
- this.name = name;
- }
-
- /**
- * @return Name of the property in the object.
- */
- public String getName() {
- return name;
- }
-
- @Override
- public int hashCode() {
- return getName().hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- return obj instanceof Property && ((Property<?>) obj).getName().equals(getName());
- }
-
- /**
- * @return Constructs a property path by appending the argument to the
- * existing property separated by a dot
- */
- public Property<Object> dot(String property) {
- return new Property<Object>(getName() + "." + property);
- }
-
- /**
- * @return Constructs a property path by appending the argument to the
- * existing property separated by a dot
- */
- public <T> Property<T> dot(Property<T> property) {
- return new Property<T>(getName() + "." + property.getName());
- }
-
- /**
- * Returns a version of this property that represents an OUTER join. It is
- * up to caller to ensure that the property corresponds to a relationship,
- * as "outer" attributes make no sense.
- */
- public Property<E> outer() {
- return isOuter() ? this: new Property<E>(name + "+");
- }
-
- private boolean isOuter() {
- return name.endsWith("+");
- }
-
- /**
- * @return An expression representing null.
- */
- public Expression isNull() {
- return ExpressionFactory.matchExp(getName(), null);
- }
-
- /**
- * @return An expression representing a non-null value.
- */
- public Expression isNotNull() {
- return ExpressionFactory.matchExp(getName(), null).notExp();
- }
-
- /**
- * @return An expression representing equality to TRUE.
- */
- public Expression isTrue() {
- return ExpressionFactory.matchExp(getName(), Boolean.TRUE);
- }
-
- /**
- * @return An expression representing equality to FALSE.
- */
- public Expression isFalse() {
- return ExpressionFactory.matchExp(getName(), Boolean.FALSE);
- }
-
- /**
- * @return An expression representing equality to a value.
- */
- public Expression eq(E value) {
- return ExpressionFactory.matchExp(getName(), value);
- }
-
- /**
- * @return An expression representing equality between two attributes
- * (columns).
- */
- public Expression eq(Property<?> value) {
- return ExpressionFactory.matchExp(getName(), new ASTObjPath(value.getName()));
- }
-
- /**
- * @return An expression representing inequality to a value.
- */
- public Expression ne(E value) {
- return ExpressionFactory.noMatchExp(getName(), value);
- }
-
- /**
- * @return An expression representing inequality between two attributes
- * (columns).
- */
- public Expression ne(Property<?> value) {
- return ExpressionFactory.noMatchExp(getName(), new ASTObjPath(value.getName()));
- }
-
- /**
- * @return An expression for a Database "Like" query.
- */
- public Expression like(E value) {
- return ExpressionFactory.likeExp(getName(), value);
- }
-
- /**
- * @return An expression for a case insensitive "Like" query.
- */
- public Expression likeInsensitive(E value) {
- return ExpressionFactory.likeIgnoreCaseExp(getName(), value);
- }
-
- /**
- * @return An expression for a Database "NOT LIKE" query.
- */
- public Expression nlike(E value) {
- return ExpressionFactory.notLikeExp(getName(), value);
- }
-
- /**
- * @return An expression for a case insensitive "NOT LIKE" query.
- */
- public Expression nlikeInsensitive(E value) {
- return ExpressionFactory.notLikeIgnoreCaseExp(getName(), value);
- }
-
- /**
- * @return An expression checking for objects between a lower and upper
- * bound inclusive
- *
- * @param lower
- * The lower bound.
- * @param upper
- * The upper bound.
- */
- public Expression between(E lower, E upper) {
- return ExpressionFactory.betweenExp(getName(), lower, upper);
- }
-
- /**
- * @return An expression for finding objects with values in the given set.
- */
- public Expression in(E firstValue, E... moreValues) {
-
- int moreValuesLength = moreValues != null ? moreValues.length : 0;
-
- Object[] values = new Object[moreValuesLength + 1];
- values[0] = firstValue;
-
- if (moreValuesLength > 0) {
- System.arraycopy(moreValues, 0, values, 1, moreValuesLength);
- }
-
- return ExpressionFactory.inExp(getName(), values);
- }
-
- /**
- * @return An expression for finding objects with values not in the given
- * set.
- */
- public Expression nin(E firstValue, E... moreValues) {
-
- int moreValuesLength = moreValues != null ? moreValues.length : 0;
-
- Object[] values = new Object[moreValuesLength + 1];
- values[0] = firstValue;
-
- if (moreValuesLength > 0) {
- System.arraycopy(moreValues, 0, values, 1, moreValuesLength);
- }
-
- return ExpressionFactory.notInExp(getName(), values);
- }
-
- /**
- * @return An expression for finding objects with values in the given set.
- */
- public Expression in(Collection<E> values) {
- return ExpressionFactory.inExp(getName(), values);
- }
-
- /**
- * @return An expression for finding objects with values not in the given
- * set.
- */
- public Expression nin(Collection<E> values) {
- return ExpressionFactory.notInExp(getName(), values);
- }
-
- /**
- * @return A greater than Expression.
- */
- public Expression gt(E value) {
- return ExpressionFactory.greaterExp(getName(), value);
- }
-
- /**
- * @return Represents a greater than relationship between two attributes
- * (columns).
- */
- public Expression gt(Property<?> value) {
- return ExpressionFactory.greaterExp(getName(), new ASTObjPath(value.getName()));
- }
-
- /**
- * @return A greater than or equal to Expression.
- */
- public Expression gte(E value) {
- return ExpressionFactory.greaterOrEqualExp(getName(), value);
- }
-
- /**
- * @return Represents a greater than or equal relationship between two
- * attributes (columns).
- */
- public Expression gte(Property<?> value) {
- return ExpressionFactory.greaterOrEqualExp(getName(), new ASTObjPath(value.getName()));
- }
-
- /**
- * @return A less than Expression.
- */
- public Expression lt(E value) {
- return ExpressionFactory.lessExp(getName(), value);
- }
-
- /**
- * @return Represents a less than relationship between two attributes
- * (columns).
- */
- public Expression lt(Property<?> value) {
- return ExpressionFactory.lessExp(getName(), new ASTObjPath(value.getName()));
- }
-
- /**
- * @return A less than or equal to Expression.
- */
- public Expression lte(E value) {
- return ExpressionFactory.lessOrEqualExp(getName(), value);
- }
-
- /**
- * @return Represents a less than or equal relationship between two
- * attributes (columns).
- */
- public Expression lte(Property<?> value) {
- return ExpressionFactory.lessOrEqualExp(getName(), new ASTObjPath(value.getName()));
- }
-
- /**
- * @return Ascending sort orderings on this property.
- */
- public Ordering asc() {
- return new Ordering(getName(), SortOrder.ASCENDING);
- }
-
- /**
- * @return Ascending sort orderings on this property.
- */
- public List<Ordering> ascs() {
- List<Ordering> result = new ArrayList<Ordering>(1);
- result.add(asc());
- return result;
- }
-
- /**
- * @return Ascending case insensitive sort orderings on this property.
- */
- public Ordering ascInsensitive() {
- return new Ordering(getName(), SortOrder.ASCENDING_INSENSITIVE);
- }
-
- /**
- * @return Ascending case insensitive sort orderings on this property.
- */
- public List<Ordering> ascInsensitives() {
- List<Ordering> result = new ArrayList<Ordering>(1);
- result.add(ascInsensitive());
- return result;
- }
-
- /**
- * @return Descending sort orderings on this property.
- */
- public Ordering desc() {
- return new Ordering(getName(), SortOrder.DESCENDING);
- }
-
- /**
- * @return Descending sort orderings on this property.
- */
- public List<Ordering> descs() {
- List<Ordering> result = new ArrayList<Ordering>(1);
- result.add(desc());
- return result;
- }
-
- /**
- * @return Descending case insensitive sort orderings on this property.
- */
- public Ordering descInsensitive() {
- return new Ordering(getName(), SortOrder.DESCENDING_INSENSITIVE);
- }
-
- /**
- * @return Descending case insensitive sort orderings on this property.
- */
- public List<Ordering> descInsensitives() {
- List<Ordering> result = new ArrayList<Ordering>(1);
- result.add(descInsensitive());
- return result;
- }
-
- public PrefetchTreeNode joint() {
- PrefetchTreeNode node = prefetch();
- node.setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
- return node;
- }
-
- public PrefetchTreeNode disjoint() {
- PrefetchTreeNode node = prefetch();
- node.setSemantics(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS);
- return node;
- }
-
- public PrefetchTreeNode disjointById() {
- PrefetchTreeNode node = prefetch();
- node.setSemantics(PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS);
- return node;
- }
-
- PrefetchTreeNode prefetch() {
-
- // TODO: not very efficient - we are creating a prefetch that
- // SelectQuery would throw away and recreate...
- PrefetchTreeNode root = new PrefetchTreeNode();
- PrefetchTreeNode node = root.addPath(name);
- node.setPhantom(false);
- return node;
- }
-
- /**
- * Extracts property value from an object using JavaBean-compatible
- * introspection with one addition - a property can be a dot-separated
- * property name path.
- */
- @SuppressWarnings("unchecked")
- public E getFrom(Object bean) {
- return (E) PropertyUtils.getProperty(bean, getName());
- }
-
- /**
- * Extracts property value from a collection of objects using
- * JavaBean-compatible introspection with one addition - a property can be a
- * dot-separated property name path.
- */
- public List<E> getFromAll(Collection<?> beans) {
- List<E> result = new ArrayList<E>(beans.size());
- for (Object bean : beans) {
- result.add(getFrom(bean));
- }
- return result;
- }
-
- /**
- * Sets a property value in 'obj' using JavaBean-compatible introspection
- * with one addition - a property can be a dot-separated property name path.
- */
- public void setIn(Object bean, E value) {
- PropertyUtils.setProperty(bean, getName(), value);
- }
-
- /**
- * Sets a property value in a collection of objects using
- * JavaBean-compatible introspection with one addition - a property can be a
- * dot-separated property name path.
- */
- public void setInAll(Collection<?> beans, E value) {
- for (Object bean : beans) {
- setIn(bean, value);
- }
- }
+ /**
+ * Name of the property in the object
+ */
+ private final String name;
+
+ /**
+ * Constructs a new property with the given name.
+ */
+ public Property(String name) {
+ this.name = name;
+ }
+
+ /**
+ * @return Name of the property in the object.
+ */
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public int hashCode() {
+ return getName().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof Property && ((Property<?>) obj).getName().equals(getName());
+ }
+
+ /**
+ * @return Constructs a property path by appending the argument to the
+ * existing property separated by a dot
+ */
+ public Property<Object> dot(String property) {
+ return new Property<Object>(getName() + "." + property);
+ }
+
+ /**
+ * @return Constructs a property path by appending the argument to the
+ * existing property separated by a dot
+ */
+ public <T> Property<T> dot(Property<T> property) {
+ return new Property<T>(getName() + "." + property.getName());
+ }
+
+ /**
+ * Returns a version of this property that represents an OUTER join. It is
+ * up to caller to ensure that the property corresponds to a relationship,
+ * as "outer" attributes make no sense.
+ */
+ public Property<E> outer() {
+ return isOuter() ? this : new Property<E>(name + "+");
+ }
+
+ private boolean isOuter() {
+ return name.endsWith("+");
+ }
+
+ /**
+ * @return An expression representing null.
+ */
+ public Expression isNull() {
+ return ExpressionFactory.matchExp(getName(), null);
+ }
+
+ /**
+ * @return An expression representing a non-null value.
+ */
+ public Expression isNotNull() {
+ return ExpressionFactory.matchExp(getName(), null).notExp();
+ }
+
+ /**
+ * @return An expression representing equality to TRUE.
+ */
+ public Expression isTrue() {
+ return ExpressionFactory.matchExp(getName(), Boolean.TRUE);
+ }
+
+ /**
+ * @return An expression representing equality to FALSE.
+ */
+ public Expression isFalse() {
+ return ExpressionFactory.matchExp(getName(), Boolean.FALSE);
+ }
+
+ /**
+ * @return An expression representing equality to a value.
+ */
+ public Expression eq(E value) {
+ return ExpressionFactory.matchExp(getName(), value);
+ }
+
+ /**
+ * @return An expression representing equality between two attributes
+ * (columns).
+ */
+ public Expression eq(Property<?> value) {
+ return ExpressionFactory.matchExp(getName(), new ASTObjPath(value.getName()));
+ }
+
+ /**
+ * @return An expression representing inequality to a value.
+ */
+ public Expression ne(E value) {
+ return ExpressionFactory.noMatchExp(getName(), value);
+ }
+
+ /**
+ * @return An expression representing inequality between two attributes
+ * (columns).
+ */
+ public Expression ne(Property<?> value) {
+ return ExpressionFactory.noMatchExp(getName(), new ASTObjPath(value.getName()));
+ }
+
+ /**
+ * @return An expression for a Database "Like" query.
+ */
+ public Expression like(E value) {
+ return ExpressionFactory.likeExp(getName(), value);
+ }
+
+ /**
+ * @return An expression for a case insensitive "Like" query.
+ */
+ public Expression likeInsensitive(E value) {
+ return ExpressionFactory.likeIgnoreCaseExp(getName(), value);
+ }
+
+ /**
+ * @return An expression for a Database "NOT LIKE" query.
+ */
+ public Expression nlike(E value) {
+ return ExpressionFactory.notLikeExp(getName(), value);
+ }
+
+ /**
+ * @return An expression for a case insensitive "NOT LIKE" query.
+ */
+ public Expression nlikeInsensitive(E value) {
+ return ExpressionFactory.notLikeIgnoreCaseExp(getName(), value);
+ }
+
+ /**
+ * @return An expression checking for objects between a lower and upper
+ * bound inclusive
+ *
+ * @param lower
+ * The lower bound.
+ * @param upper
+ * The upper bound.
+ */
+ public Expression between(E lower, E upper) {
+ return ExpressionFactory.betweenExp(getName(), lower, upper);
+ }
+
+ /**
+ * @return An expression for finding objects with values in the given set.
+ */
+ public Expression in(E firstValue, E... moreValues) {
+
+ int moreValuesLength = moreValues != null ? moreValues.length : 0;
+
+ Object[] values = new Object[moreValuesLength + 1];
+ values[0] = firstValue;
+
+ if (moreValuesLength > 0) {
+ System.arraycopy(moreValues, 0, values, 1, moreValuesLength);
+ }
+
+ return ExpressionFactory.inExp(getName(), values);
+ }
+
+ /**
+ * @return An expression for finding objects with values not in the given
+ * set.
+ */
+ public Expression nin(E firstValue, E... moreValues) {
+
+ int moreValuesLength = moreValues != null ? moreValues.length : 0;
+
+ Object[] values = new Object[moreValuesLength + 1];
+ values[0] = firstValue;
+
+ if (moreValuesLength > 0) {
+ System.arraycopy(moreValues, 0, values, 1, moreValuesLength);
+ }
+
+ return ExpressionFactory.notInExp(getName(), values);
+ }
+
+ /**
+ * @return An expression for finding objects with values in the given set.
+ */
+ public Expression in(Collection<E> values) {
+ return ExpressionFactory.inExp(getName(), values);
+ }
+
+ /**
+ * @return An expression for finding objects with values not in the given
+ * set.
+ */
+ public Expression nin(Collection<E> values) {
+ return ExpressionFactory.notInExp(getName(), values);
+ }
+
+ /**
+ * @return A greater than Expression.
+ */
+ public Expression gt(E value) {
+ return ExpressionFactory.greaterExp(getName(), value);
+ }
+
+ /**
+ * @return Represents a greater than relationship between two attributes
+ * (columns).
+ */
+ public Expression gt(Property<?> value) {
+ return ExpressionFactory.greaterExp(getName(), new ASTObjPath(value.getName()));
+ }
+
+ /**
+ * @return A greater than or equal to Expression.
+ */
+ public Expression gte(E value) {
+ return ExpressionFactory.greaterOrEqualExp(getName(), value);
+ }
+
+ /**
+ * @return Represents a greater than or equal relationship between two
+ * attributes (columns).
+ */
+ public Expression gte(Property<?> value) {
+ return ExpressionFactory.greaterOrEqualExp(getName(), new ASTObjPath(value.getName()));
+ }
+
+ /**
+ * @return A less than Expression.
+ */
+ public Expression lt(E value) {
+ return ExpressionFactory.lessExp(getName(), value);
+ }
+
+ /**
+ * @return Represents a less than relationship between two attributes
+ * (columns).
+ */
+ public Expression lt(Property<?> value) {
+ return ExpressionFactory.lessExp(getName(), new ASTObjPath(value.getName()));
+ }
+
+ /**
+ * @return A less than or equal to Expression.
+ */
+ public Expression lte(E value) {
+ return ExpressionFactory.lessOrEqualExp(getName(), value);
+ }
+
+ /**
+ * @return Represents a less than or equal relationship between two
+ * attributes (columns).
+ */
+ public Expression lte(Property<?> value) {
+ return ExpressionFactory.lessOrEqualExp(getName(), new ASTObjPath(value.getName()));
+ }
+
+ /**
+ * @return Ascending sort orderings on this property.
+ */
+ public Ordering asc() {
+ return new Ordering(getName(), SortOrder.ASCENDING);
+ }
+
+ /**
+ * @return Ascending sort orderings on this property.
+ */
+ public List<Ordering> ascs() {
+ List<Ordering> result = new ArrayList<Ordering>(1);
+ result.add(asc());
+ return result;
+ }
+
+ /**
+ * @return Ascending case insensitive sort orderings on this property.
+ */
+ public Ordering ascInsensitive() {
+ return new Ordering(getName(), SortOrder.ASCENDING_INSENSITIVE);
+ }
+
+ /**
+ * @return Ascending case insensitive sort orderings on this property.
+ */
+ public List<Ordering> ascInsensitives() {
+ List<Ordering> result = new ArrayList<Ordering>(1);
+ result.add(ascInsensitive());
+ return result;
+ }
+
+ /**
+ * @return Descending sort orderings on this property.
+ */
+ public Ordering desc() {
+ return new Ordering(getName(), SortOrder.DESCENDING);
+ }
+
+ /**
+ * @return Descending sort orderings on this property.
+ */
+ public List<Ordering> descs() {
+ List<Ordering> result = new ArrayList<Ordering>(1);
+ result.add(desc());
+ return result;
+ }
+
+ /**
+ * @return Descending case insensitive sort orderings on this property.
+ */
+ public Ordering descInsensitive() {
+ return new Ordering(getName(), SortOrder.DESCENDING_INSENSITIVE);
+ }
+
+ /**
+ * @return Descending case insensitive sort orderings on this property.
+ */
+ public List<Ordering> descInsensitives() {
+ List<Ordering> result = new ArrayList<Ordering>(1);
+ result.add(descInsensitive());
+ return result;
+ }
+
+ /**
+ * Returns a prefetch tree that follows this property path, potentially
+ * spanning a number of phantom nodes, and having a single leaf with "joint"
+ * prefetch semantics.
+ */
+ public PrefetchTreeNode joint() {
+ PrefetchTreeNode node = prefetch();
+ node.setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
+ return node.getRoot();
+ }
+
+ /**
+ * Returns a prefetch tree that follows this property path, potentially
+ * spanning a number of phantom nodes, and having a single leaf with
+ * "disjoint" prefetch semantics.
+ */
+ public PrefetchTreeNode disjoint() {
+ PrefetchTreeNode node = prefetch();
+ node.setSemantics(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS);
+ return node.getRoot();
+ }
+
+ /**
+ * Returns a prefetch tree that follows this property path, potentially
+ * spanning a number of phantom nodes, and having a single leaf with
+ * "disjoint by id" prefetch semantics.
+ */
+ public PrefetchTreeNode disjointById() {
+ PrefetchTreeNode node = prefetch();
+ node.setSemantics(PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS);
+ return node.getRoot();
+ }
+
+ PrefetchTreeNode prefetch() {
+ PrefetchTreeNode root = new PrefetchTreeNode();
+ PrefetchTreeNode node = root.addPath(name);
+ node.setPhantom(false);
+ return node;
+ }
+
+ /**
+ * Extracts property value from an object using JavaBean-compatible
+ * introspection with one addition - a property can be a dot-separated
+ * property name path.
+ */
+ @SuppressWarnings("unchecked")
+ public E getFrom(Object bean) {
+ return (E) PropertyUtils.getProperty(bean, getName());
+ }
+
+ /**
+ * Extracts property value from a collection of objects using
+ * JavaBean-compatible introspection with one addition - a property can be a
+ * dot-separated property name path.
+ */
+ public List<E> getFromAll(Collection<?> beans) {
+ List<E> result = new ArrayList<E>(beans.size());
+ for (Object bean : beans) {
+ result.add(getFrom(bean));
+ }
+ return result;
+ }
+
+ /**
+ * Sets a property value in 'obj' using JavaBean-compatible introspection
+ * with one addition - a property can be a dot-separated property name path.
+ */
+ public void setIn(Object bean, E value) {
+ PropertyUtils.setProperty(bean, getName(), value);
+ }
+
+ /**
+ * Sets a property value in a collection of objects using
+ * JavaBean-compatible introspection with one addition - a property can be a
+ * dot-separated property name path.
+ */
+ public void setInAll(Collection<?> beans, E value) {
+ for (Object bean : beans) {
+ setIn(bean, value);
+ }
+ }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4d8b2e1a/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java b/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java
index d97e811..7f7c6dd 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java
@@ -43,417 +43,430 @@ import org.apache.cayenne.util.XMLSerializable;
*/
class BaseQueryMetadata implements QueryMetadata, XMLSerializable, Serializable {
- int fetchLimit = QueryMetadata.FETCH_LIMIT_DEFAULT;
- int fetchOffset = QueryMetadata.FETCH_OFFSET_DEFAULT;
-
- int statementFetchSize = QueryMetadata.FETCH_OFFSET_DEFAULT;
-
- int pageSize = QueryMetadata.PAGE_SIZE_DEFAULT;
- boolean fetchingDataRows = QueryMetadata.FETCHING_DATA_ROWS_DEFAULT;
- QueryCacheStrategy cacheStrategy = QueryCacheStrategy.getDefaultStrategy();
-
- PrefetchTreeNode prefetchTree;
- String cacheKey;
- String[] cacheGroups;
-
- transient List<Object> resultSetMapping;
- transient DbEntity dbEntity;
- transient DataMap dataMap;
- transient Object lastRoot;
- transient ClassDescriptor classDescriptor;
- transient EntityResolver lastEntityResolver;
-
- /**
- * Copies values of another QueryMetadata object to this object.
- */
- void copyFromInfo(QueryMetadata info) {
- this.lastEntityResolver = null;
- this.lastRoot = null;
- this.classDescriptor = null;
- this.dbEntity = null;
- this.dataMap = null;
-
- this.fetchingDataRows = info.isFetchingDataRows();
- this.fetchLimit = info.getFetchLimit();
- this.pageSize = info.getPageSize();
- this.cacheStrategy = info.getCacheStrategy();
- this.cacheKey = info.getCacheKey();
- this.cacheGroups = info.getCacheGroups();
- this.resultSetMapping = info.getResultSetMapping();
-
- setPrefetchTree(info.getPrefetchTree());
- }
-
- boolean resolve(Object root, EntityResolver resolver, String cacheKey) {
-
- if (lastRoot != root || lastEntityResolver != resolver) {
-
- this.cacheKey = cacheKey;
-
- this.classDescriptor = null;
- this.dbEntity = null;
- this.dataMap = null;
-
- ObjEntity entity = null;
-
- if (root != null) {
- if (root instanceof Class<?>) {
- entity = resolver.getObjEntity((Class<?>) root);
- if (entity == null) { // entity not found, try to resolve it
- // with
- // client resolver
- EntityResolver clientResolver = resolver.getClientEntityResolver();
- if (clientResolver != resolver) {
- ObjEntity clientEntity = clientResolver.getObjEntity((Class<?>) root);
-
- if (clientEntity != null) {
- entity = resolver.getObjEntity(clientEntity.getName());
- }
- }
- }
-
- if (entity != null) {
- this.dbEntity = entity.getDbEntity();
- this.dataMap = entity.getDataMap();
- }
- } else if (root instanceof ObjEntity) {
- entity = (ObjEntity) root;
- this.dbEntity = entity.getDbEntity();
- this.dataMap = entity.getDataMap();
- } 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) {
- this.dbEntity = (DbEntity) root;
- this.dataMap = dbEntity.getDataMap();
- } else if (root instanceof DataMap) {
- this.dataMap = (DataMap) root;
- } else if (root instanceof Persistent) {
- entity = resolver.getObjEntity((Persistent) root);
- if (entity != null) {
- this.dbEntity = entity.getDbEntity();
- this.dataMap = entity.getDataMap();
- }
- }
- }
-
- if (entity != null) {
- this.classDescriptor = resolver.getClassDescriptor(entity.getName());
- }
-
- this.lastRoot = root;
- this.lastEntityResolver = resolver;
-
- return true;
- }
-
- return false;
- }
-
- void initWithProperties(Map<String, ?> properties) {
- // must init defaults even if properties are empty
- if (properties == null) {
- properties = Collections.EMPTY_MAP;
- }
-
- 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 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.fetchLimit = (fetchLimit != null) ? Integer.parseInt(fetchLimit.toString())
- : QueryMetadata.FETCH_LIMIT_DEFAULT;
-
- this.pageSize = (pageSize != null) ? Integer.parseInt(pageSize.toString()) : QueryMetadata.PAGE_SIZE_DEFAULT;
-
- this.statementFetchSize = (statementFetchSize != null) ? Integer.parseInt(statementFetchSize.toString())
- : QueryMetadata.STATEMENT_FETCH_SIZE_DEFAULT;
-
- this.fetchingDataRows = (fetchingDataRows != null) ? "true".equalsIgnoreCase(fetchingDataRows.toString())
- : QueryMetadata.FETCHING_DATA_ROWS_DEFAULT;
-
- 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) {
- StringTokenizer toks = new StringTokenizer(cacheGroups.toString(), ",");
- this.cacheGroups = new String[toks.countTokens()];
- for (int i = 0; i < this.cacheGroups.length; i++) {
- this.cacheGroups[i] = toks.nextToken();
- }
- }
- }
-
- public void encodeAsXML(XMLEncoder encoder) {
-
- if (fetchingDataRows != QueryMetadata.FETCHING_DATA_ROWS_DEFAULT) {
- encoder.printProperty(QueryMetadata.FETCHING_DATA_ROWS_PROPERTY, fetchingDataRows);
- }
-
- if (fetchOffset != QueryMetadata.FETCH_OFFSET_DEFAULT) {
- encoder.printProperty(QueryMetadata.FETCH_OFFSET_PROPERTY, fetchOffset);
- }
-
- if (fetchLimit != QueryMetadata.FETCH_LIMIT_DEFAULT) {
- encoder.printProperty(QueryMetadata.FETCH_LIMIT_PROPERTY, fetchLimit);
- }
-
- if (pageSize != QueryMetadata.PAGE_SIZE_DEFAULT) {
- encoder.printProperty(QueryMetadata.PAGE_SIZE_PROPERTY, pageSize);
- }
-
- 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);
- }
-
- if (prefetchTree != null) {
- prefetchTree.encodeAsXML(encoder);
- }
-
- if (cacheGroups != null && cacheGroups.length > 0) {
- StringBuilder buffer = new StringBuilder(cacheGroups[0]);
- for (int i = 1; i < cacheGroups.length; i++) {
- buffer.append(',').append(cacheGroups[i]);
- }
- encoder.printProperty(QueryMetadata.CACHE_GROUPS_PROPERTY, buffer.toString());
- }
- }
-
- /**
- * @since 1.2
- */
- public String getCacheKey() {
- return cacheKey;
- }
-
- /**
- * @since 1.2
- */
- public DataMap getDataMap() {
- return dataMap;
- }
-
- /**
- * @since 1.2
- */
- public Procedure getProcedure() {
- return null;
- }
-
- /**
- * @since 3.0
- */
- public Map<String, String> getPathSplitAliases() {
- return Collections.emptyMap();
- }
-
- /**
- * @since 1.2
- */
- public DbEntity getDbEntity() {
- return dbEntity;
- }
-
- /**
- * @since 1.2
- */
- public ObjEntity getObjEntity() {
- return classDescriptor != null ? classDescriptor.getEntity() : null;
- }
-
- /**
- * @since 3.0
- */
- public ClassDescriptor getClassDescriptor() {
- return classDescriptor;
- }
-
- /**
- * @since 3.0
- */
- public List<Object> getResultSetMapping() {
- return resultSetMapping;
- }
-
- /**
- * @since 1.2
- */
- public PrefetchTreeNode getPrefetchTree() {
- return prefetchTree;
- }
-
- void setPrefetchTree(PrefetchTreeNode prefetchTree) {
- this.prefetchTree = prefetchTree != null ? deepClone(prefetchTree, null) : null;
- }
-
- private PrefetchTreeNode deepClone(PrefetchTreeNode source, PrefetchTreeNode targetParent) {
-
- PrefetchTreeNode target = new PrefetchTreeNode(targetParent, source.getName());
- target.setEjbqlPathEntityId(source.getEjbqlPathEntityId());
- target.setEntityName(source.getEntityName());
- target.setPhantom(source.isPhantom());
- target.setSemantics(source.getSemantics());
-
- for (PrefetchTreeNode child : source.getChildren()) {
- target.addChild(deepClone(child, target));
- }
-
- return target;
- }
-
- /**
- * @since 3.0
- */
- public QueryCacheStrategy getCacheStrategy() {
- return cacheStrategy;
- }
-
- /**
- * @since 3.0
- */
- void setCacheStrategy(QueryCacheStrategy cacheStrategy) {
- this.cacheStrategy = cacheStrategy;
- }
-
- /**
- * @since 3.0
- */
- public String[] getCacheGroups() {
- return cacheGroups;
- }
-
- /**
- * @since 3.0
- */
- void setCacheGroups(String... groups) {
- this.cacheGroups = groups;
- }
-
- public boolean isFetchingDataRows() {
- return fetchingDataRows;
- }
-
- public int getFetchLimit() {
- return fetchLimit;
- }
-
- public int getPageSize() {
- return pageSize;
- }
-
- public Query getOrginatingQuery() {
- return null;
- }
-
- /**
- * @since 3.0
- */
- public int getFetchOffset() {
- return fetchOffset;
- }
-
- public boolean isRefreshingObjects() {
- return true;
- }
-
- void setFetchingDataRows(boolean b) {
- fetchingDataRows = b;
- }
-
- void setFetchLimit(int i) {
- fetchLimit = i;
- }
-
- void setFetchOffset(int i) {
- fetchOffset = i;
- }
-
- void setPageSize(int i) {
- pageSize = i;
- }
-
- /**
- * Sets statement's fetch size (0 for no default size)
- *
- * @since 3.0
- */
- void setStatementFetchSize(int size) {
- this.statementFetchSize = size;
- }
-
- /**
- * @return statement's fetch size
- * @since 3.0
- */
- public int getStatementFetchSize() {
- return statementFetchSize;
- }
-
- /**
- * Adds a joint prefetch.
- *
- * @since 1.2
- */
- PrefetchTreeNode addPrefetch(String path, int semantics) {
- if (prefetchTree == null) {
- prefetchTree = new PrefetchTreeNode();
- }
-
- PrefetchTreeNode node = prefetchTree.addPath(path);
- node.setSemantics(semantics);
- node.setPhantom(false);
- return node;
- }
-
- /**
- * Adds all prefetches from a provided collection.
- *
- * @since 1.2
- */
- void addPrefetches(Collection<String> prefetches, int semantics) {
- if (prefetches != null) {
- for (String prefetch : prefetches) {
- addPrefetch(prefetch, semantics);
- }
- }
- }
-
- /**
- * Clears all joint prefetches.
- *
- * @since 1.2
- */
- void clearPrefetches() {
- prefetchTree = null;
- }
-
- /**
- * Removes joint prefetch.
- *
- * @since 1.2
- */
- void removePrefetch(String prefetch) {
- if (prefetchTree != null) {
- prefetchTree.removePath(prefetch);
- }
- }
+ int fetchLimit = QueryMetadata.FETCH_LIMIT_DEFAULT;
+ int fetchOffset = QueryMetadata.FETCH_OFFSET_DEFAULT;
+
+ int statementFetchSize = QueryMetadata.FETCH_OFFSET_DEFAULT;
+
+ int pageSize = QueryMetadata.PAGE_SIZE_DEFAULT;
+ boolean fetchingDataRows = QueryMetadata.FETCHING_DATA_ROWS_DEFAULT;
+ QueryCacheStrategy cacheStrategy = QueryCacheStrategy.getDefaultStrategy();
+
+ PrefetchTreeNode prefetchTree;
+ String cacheKey;
+ String[] cacheGroups;
+
+ transient List<Object> resultSetMapping;
+ transient DbEntity dbEntity;
+ transient DataMap dataMap;
+ transient Object lastRoot;
+ transient ClassDescriptor classDescriptor;
+ transient EntityResolver lastEntityResolver;
+
+ /**
+ * Copies values of another QueryMetadata object to this object.
+ */
+ void copyFromInfo(QueryMetadata info) {
+ this.lastEntityResolver = null;
+ this.lastRoot = null;
+ this.classDescriptor = null;
+ this.dbEntity = null;
+ this.dataMap = null;
+
+ this.fetchingDataRows = info.isFetchingDataRows();
+ this.fetchLimit = info.getFetchLimit();
+ this.pageSize = info.getPageSize();
+ this.cacheStrategy = info.getCacheStrategy();
+ this.cacheKey = info.getCacheKey();
+ this.cacheGroups = info.getCacheGroups();
+ this.resultSetMapping = info.getResultSetMapping();
+
+ setPrefetchTree(info.getPrefetchTree());
+ }
+
+ boolean resolve(Object root, EntityResolver resolver, String cacheKey) {
+
+ if (lastRoot != root || lastEntityResolver != resolver) {
+
+ this.cacheKey = cacheKey;
+
+ this.classDescriptor = null;
+ this.dbEntity = null;
+ this.dataMap = null;
+
+ ObjEntity entity = null;
+
+ if (root != null) {
+ if (root instanceof Class<?>) {
+ entity = resolver.getObjEntity((Class<?>) root);
+ if (entity == null) { // entity not found, try to resolve it
+ // with
+ // client resolver
+ EntityResolver clientResolver = resolver.getClientEntityResolver();
+ if (clientResolver != resolver) {
+ ObjEntity clientEntity = clientResolver.getObjEntity((Class<?>) root);
+
+ if (clientEntity != null) {
+ entity = resolver.getObjEntity(clientEntity.getName());
+ }
+ }
+ }
+
+ if (entity != null) {
+ this.dbEntity = entity.getDbEntity();
+ this.dataMap = entity.getDataMap();
+ }
+ } else if (root instanceof ObjEntity) {
+ entity = (ObjEntity) root;
+ this.dbEntity = entity.getDbEntity();
+ this.dataMap = entity.getDataMap();
+ } 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) {
+ this.dbEntity = (DbEntity) root;
+ this.dataMap = dbEntity.getDataMap();
+ } else if (root instanceof DataMap) {
+ this.dataMap = (DataMap) root;
+ } else if (root instanceof Persistent) {
+ entity = resolver.getObjEntity((Persistent) root);
+ if (entity != null) {
+ this.dbEntity = entity.getDbEntity();
+ this.dataMap = entity.getDataMap();
+ }
+ }
+ }
+
+ if (entity != null) {
+ this.classDescriptor = resolver.getClassDescriptor(entity.getName());
+ }
+
+ this.lastRoot = root;
+ this.lastEntityResolver = resolver;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ void initWithProperties(Map<String, ?> properties) {
+ // must init defaults even if properties are empty
+ if (properties == null) {
+ properties = Collections.EMPTY_MAP;
+ }
+
+ 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 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.fetchLimit = (fetchLimit != null) ? Integer.parseInt(fetchLimit.toString())
+ : QueryMetadata.FETCH_LIMIT_DEFAULT;
+
+ this.pageSize = (pageSize != null) ? Integer.parseInt(pageSize.toString()) : QueryMetadata.PAGE_SIZE_DEFAULT;
+
+ this.statementFetchSize = (statementFetchSize != null) ? Integer.parseInt(statementFetchSize.toString())
+ : QueryMetadata.STATEMENT_FETCH_SIZE_DEFAULT;
+
+ this.fetchingDataRows = (fetchingDataRows != null) ? "true".equalsIgnoreCase(fetchingDataRows.toString())
+ : QueryMetadata.FETCHING_DATA_ROWS_DEFAULT;
+
+ 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) {
+ StringTokenizer toks = new StringTokenizer(cacheGroups.toString(), ",");
+ this.cacheGroups = new String[toks.countTokens()];
+ for (int i = 0; i < this.cacheGroups.length; i++) {
+ this.cacheGroups[i] = toks.nextToken();
+ }
+ }
+ }
+
+ public void encodeAsXML(XMLEncoder encoder) {
+
+ if (fetchingDataRows != QueryMetadata.FETCHING_DATA_ROWS_DEFAULT) {
+ encoder.printProperty(QueryMetadata.FETCHING_DATA_ROWS_PROPERTY, fetchingDataRows);
+ }
+
+ if (fetchOffset != QueryMetadata.FETCH_OFFSET_DEFAULT) {
+ encoder.printProperty(QueryMetadata.FETCH_OFFSET_PROPERTY, fetchOffset);
+ }
+
+ if (fetchLimit != QueryMetadata.FETCH_LIMIT_DEFAULT) {
+ encoder.printProperty(QueryMetadata.FETCH_LIMIT_PROPERTY, fetchLimit);
+ }
+
+ if (pageSize != QueryMetadata.PAGE_SIZE_DEFAULT) {
+ encoder.printProperty(QueryMetadata.PAGE_SIZE_PROPERTY, pageSize);
+ }
+
+ 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);
+ }
+
+ if (prefetchTree != null) {
+ prefetchTree.encodeAsXML(encoder);
+ }
+
+ if (cacheGroups != null && cacheGroups.length > 0) {
+ StringBuilder buffer = new StringBuilder(cacheGroups[0]);
+ for (int i = 1; i < cacheGroups.length; i++) {
+ buffer.append(',').append(cacheGroups[i]);
+ }
+ encoder.printProperty(QueryMetadata.CACHE_GROUPS_PROPERTY, buffer.toString());
+ }
+ }
+
+ /**
+ * @since 1.2
+ */
+ public String getCacheKey() {
+ return cacheKey;
+ }
+
+ /**
+ * @since 1.2
+ */
+ public DataMap getDataMap() {
+ return dataMap;
+ }
+
+ /**
+ * @since 1.2
+ */
+ public Procedure getProcedure() {
+ return null;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public Map<String, String> getPathSplitAliases() {
+ return Collections.emptyMap();
+ }
+
+ /**
+ * @since 1.2
+ */
+ public DbEntity getDbEntity() {
+ return dbEntity;
+ }
+
+ /**
+ * @since 1.2
+ */
+ public ObjEntity getObjEntity() {
+ return classDescriptor != null ? classDescriptor.getEntity() : null;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public ClassDescriptor getClassDescriptor() {
+ return classDescriptor;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public List<Object> getResultSetMapping() {
+ return resultSetMapping;
+ }
+
+ /**
+ * @since 1.2
+ */
+ public PrefetchTreeNode getPrefetchTree() {
+ return prefetchTree;
+ }
+
+ void setPrefetchTree(PrefetchTreeNode prefetchTree) {
+ this.prefetchTree = prefetchTree != null ? deepClone(prefetchTree, null) : null;
+ }
+
+ private PrefetchTreeNode deepClone(PrefetchTreeNode source, PrefetchTreeNode targetParent) {
+
+ PrefetchTreeNode target = new PrefetchTreeNode(targetParent, source.getName());
+ target.setEjbqlPathEntityId(source.getEjbqlPathEntityId());
+ target.setEntityName(source.getEntityName());
+ target.setPhantom(source.isPhantom());
+ target.setSemantics(source.getSemantics());
+
+ for (PrefetchTreeNode child : source.getChildren()) {
+ target.addChild(deepClone(child, target));
+ }
+
+ return target;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public QueryCacheStrategy getCacheStrategy() {
+ return cacheStrategy;
+ }
+
+ /**
+ * @since 3.0
+ */
+ void setCacheStrategy(QueryCacheStrategy cacheStrategy) {
+ this.cacheStrategy = cacheStrategy;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public String[] getCacheGroups() {
+ return cacheGroups;
+ }
+
+ /**
+ * @since 3.0
+ */
+ void setCacheGroups(String... groups) {
+ this.cacheGroups = groups;
+ }
+
+ public boolean isFetchingDataRows() {
+ return fetchingDataRows;
+ }
+
+ public int getFetchLimit() {
+ return fetchLimit;
+ }
+
+ public int getPageSize() {
+ return pageSize;
+ }
+
+ public Query getOrginatingQuery() {
+ return null;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public int getFetchOffset() {
+ return fetchOffset;
+ }
+
+ public boolean isRefreshingObjects() {
+ return true;
+ }
+
+ void setFetchingDataRows(boolean b) {
+ fetchingDataRows = b;
+ }
+
+ void setFetchLimit(int i) {
+ fetchLimit = i;
+ }
+
+ void setFetchOffset(int i) {
+ fetchOffset = i;
+ }
+
+ void setPageSize(int i) {
+ pageSize = i;
+ }
+
+ /**
+ * Sets statement's fetch size (0 for no default size)
+ *
+ * @since 3.0
+ */
+ void setStatementFetchSize(int size) {
+ this.statementFetchSize = size;
+ }
+
+ /**
+ * @return statement's fetch size
+ * @since 3.0
+ */
+ public int getStatementFetchSize() {
+ return statementFetchSize;
+ }
+
+ /**
+ * Adds a joint prefetch.
+ *
+ * @since 1.2
+ */
+ PrefetchTreeNode addPrefetch(String path, int semantics) {
+ if (prefetchTree == null) {
+ prefetchTree = new PrefetchTreeNode();
+ }
+
+ PrefetchTreeNode node = prefetchTree.addPath(path);
+ node.setSemantics(semantics);
+ node.setPhantom(false);
+ return node;
+ }
+
+ /**
+ * Adds a joint prefetch.
+ *
+ * @since 4.0
+ */
+ void mergePrefetch(PrefetchTreeNode node) {
+ if (prefetchTree == null) {
+ prefetchTree = new PrefetchTreeNode();
+ }
+
+ prefetchTree.merge(node);
+ }
+
+ /**
+ * Adds all prefetches from a provided collection.
+ *
+ * @since 1.2
+ */
+ void addPrefetches(Collection<String> prefetches, int semantics) {
+ if (prefetches != null) {
+ for (String prefetch : prefetches) {
+ addPrefetch(prefetch, semantics);
+ }
+ }
+ }
+
+ /**
+ * Clears all joint prefetches.
+ *
+ * @since 1.2
+ */
+ void clearPrefetches() {
+ prefetchTree = null;
+ }
+
+ /**
+ * Removes joint prefetch.
+ *
+ * @since 1.2
+ */
+ void removePrefetch(String prefetch) {
+ if (prefetchTree != null) {
+ prefetchTree.removePath(prefetch);
+ }
+ }
}