001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010:
011: package org.mmbase.bridge.util;
012:
013: import org.mmbase.bridge.*;
014: import org.mmbase.storage.search.*;
015: import org.mmbase.util.logging.*;
016:
017: import java.util.*;
018:
019: /**
020: * Queries a Tree from MMBase. A Tree is presented as a List of MultiLevel results (ClusterNodes),
021: * combined with a smart iterator which iterates through the elements of these lists as if it was one
022: * list ordered as a Tree.
023: *
024: *
025: * @author Michiel Meeuwissen
026: * @version $Id: TreeList.java,v 1.30 2008/02/28 12:23:51 michiel Exp $
027: * @since MMBase-1.7
028: */
029:
030: public class TreeList extends AbstractSequentialBridgeList<Node>
031: implements NodeList {
032: private static final Logger log = Logging
033: .getLoggerInstance(TreeList.class);
034:
035: public static final String REAL_NODES = "realnodes";
036:
037: protected Cloud cloud;
038: protected final List<Branch> branches = new ArrayList<Branch>();
039:
040: protected int topQuery = 0;
041: protected int numberOfSteps;
042: private int size;
043: private boolean needsSizeCheck = true;
044:
045: protected boolean foundEnd = false;
046: protected int leafConstraintOffset = Integer.MAX_VALUE;
047:
048: /**
049: * @since MMBase-1.8.1
050: */
051: protected int max = SearchQuery.DEFAULT_MAX_NUMBER;
052:
053: /**
054: * @param q The 'base' query defining the minimal depth of the tree elements. The trunk of the tree.
055: */
056:
057: public TreeList(NodeQuery q) {
058:
059: if (q.getOffset() > 0) {
060: throw new UnsupportedOperationException(
061: "Don't know how to implement that");
062: }
063: cloud = q.getCloud();
064: branches.add(new Branch(q));
065: numberOfSteps = q.getSteps().size();
066:
067: }
068:
069: /**
070: * Copy-constructor
071: * @since MMBase-1.8
072: */
073: public TreeList(TreeList tl) {
074: cloud = tl.cloud;
075: Iterator<Branch> i = tl.branches.iterator();
076: while (i.hasNext()) {
077: Branch b = i.next();
078: branches.add(new Branch(b));
079: }
080: topQuery = tl.topQuery;
081: numberOfSteps = tl.numberOfSteps;
082: size = tl.size;
083: needsSizeCheck = tl.needsSizeCheck;
084: foundEnd = tl.foundEnd;
085: leafConstraintOffset = tl.leafConstraintOffset;
086: }
087:
088: /**
089: * @since MMBase-1.8.1
090: */
091: public void setMax(int m) {
092: max = m;
093: }
094:
095: /**
096: * @since MMBase-1.8.1
097: */
098: public int getMax() {
099: return max;
100: }
101:
102: /**
103: * @since MMBase-1.8
104: */
105: public Cloud getCloud() {
106: return cloud;
107: }
108:
109: // javadoc inherited
110: @Override
111: public int size() {
112: sizeCheck();
113: return max != SearchQuery.DEFAULT_MAX_NUMBER ? (max < size ? max
114: : size)
115: : size;
116: }
117:
118: /**
119: * Checks if the size of the List needs to be (re)determined, and if not, does so. After growing
120: * a List the size needs recalculation.
121: * @since MMBase-1.7.1
122: */
123: protected void sizeCheck() {
124: if (needsSizeCheck) {
125: int count;
126: Branch branch = branches.get(topQuery);
127: if (branch.leafResult != null) { // not quite sure that this can hapen
128: count = branch.leafResult.size();
129: } else {
130: NodeQuery newQuery = branch.getLeafQuery();
131: count = Queries.count(newQuery);
132: }
133:
134: if (count == 0) {
135: foundEnd = branch.leafConstraint == null
136: || Queries.count(branch.getQuery()) == 0;
137: }
138: size += count;
139: needsSizeCheck = false;
140: }
141: }
142:
143: /**
144: * Grows branches of the Tree, which means that one new query will be created which is one
145: * relationStep longer than the longest one until now.
146: * This new relationStep is returned, which can be used to create new constraints.
147: *
148: * @return <code>null</code> if no relationstep is added because that would not increase the number of results.
149: */
150:
151: public RelationStep grow(NodeManager nodeManager, String role,
152: String searchDir) {
153: sizeCheck();
154: if (foundEnd) {
155: return null;
156: }
157: needsSizeCheck = true;
158:
159: NodeQuery lastQuery = branches.get(topQuery).getQuery();
160: NodeQuery newQuery = (NodeQuery) lastQuery.cloneWithoutFields();
161:
162: // add relations step
163: RelationStep step = newQuery.addRelationStep(nodeManager, role,
164: searchDir);
165: Step nextStep = step.getNext();
166:
167: // make sure every step has a unique alias
168: newQuery.setAlias(step, step.getTableName()
169: + (numberOfSteps - 1));
170: newQuery.setAlias(nextStep, nodeManager.getName()
171: + numberOfSteps);
172:
173: // new number of steps
174: numberOfSteps = newQuery.getSteps().size();
175:
176: // the new step must be the 'node' step
177: newQuery.setNodeStep(nextStep);
178:
179: branches.add(new Branch(newQuery));
180: topQuery++;
181:
182: return step;
183: }
184:
185: /**
186: * Returns the top most query, associated with the last call to {@link #grow}.
187: * @since MMBase-1.8
188: */
189: public NodeQuery getLeafQuery() {
190: return branches.get(topQuery).getQuery();
191: }
192:
193: /**
194: * Sets a 'leaf constraint' on the last 'growed' step. A leaf constraint is a constraint which is only
195: * used on leafs, so if the tree is grown further, the leaf constraint will not be passed to the branches.
196: * @since MMBase-1.8
197: */
198: public void setLeafConstraint(Constraint constraint) {
199:
200: Branch branch = branches.get(topQuery);
201: if (branch.result != null) {
202: throw new IllegalStateException("The query for branch "
203: + topQuery + " was already executed");
204: }
205: if (topQuery < leafConstraintOffset) {
206: leafConstraintOffset = topQuery;
207: }
208: leafConstraintOffset = 0;
209: branch.leafConstraint = constraint;
210:
211: }
212:
213: /**
214: * Executes one query if that did not happen yet, and stores the result in the 'results' List
215: * @return NodeList or <code>null</code> if queryNumber too big
216: * @throws IndexOutOfBoundsException if queryNumber < 0
217: */
218: protected NodeList getList(int queryNumber) {
219: if (queryNumber < 0) {
220: throw new IndexOutOfBoundsException("No query for '"
221: + queryNumber + "'");
222: }
223:
224: if (queryNumber >= branches.size()) {
225: return null;
226: }
227:
228: Branch branch = branches.get(queryNumber);
229: if (branch.result == null) {
230: NodeQuery query = branch.getQuery();
231: branch.result = cloud.getList(query);
232: if (branch.leafConstraint == null) {
233: branch.leafResult = branch.result;
234: }
235: }
236: return branch.result;
237: }
238:
239: /**
240: * Executes one query as a 'leaf' query.
241: * @since MMBase-1.8
242: */
243: protected NodeList getLeafList(int queryNumber) {
244: if (queryNumber < 0) {
245: throw new IndexOutOfBoundsException("No query for '"
246: + queryNumber + "'");
247: }
248:
249: if (queryNumber >= branches.size()) {
250: return null;
251: }
252:
253: Branch branch = branches.get(queryNumber);
254: if (branch.leafResult == null) {
255: NodeQuery query = branch.getLeafQuery();
256: branch.leafResult = cloud.getList(query);
257: branch.leafResult.setProperty(REAL_NODES, null);
258: if (branch.leafConstraint == null) {
259: branch.result = branch.leafResult;
260: }
261: }
262: return branch.leafResult;
263: }
264:
265: // javadoc inherited
266: @Override
267: public ListIterator<Node> listIterator(int ind) {
268: return treeIterator(ind);
269: }
270:
271: public NodeIterator nodeIterator() {
272: return treeIterator(0);
273: }
274:
275: public TreeIterator treeIterator() {
276: return treeIterator(0);
277: }
278:
279: protected TreeIterator treeIterator(int ind) {
280: return new TreeItr(ind);
281: }
282:
283: // javadoc inherited
284: public Node getNode(int i) {
285: return get(i);
286: }
287:
288: /**
289: * Returns node 'index' of query result 'queryIndex' as a 'real' node (so not a cluster node)
290: */
291: protected Node getRealNode(int queryIndex, int index) {
292: NodeList nodeList = getLeafList(queryIndex);
293: NodeList realNodes = (NodeList) nodeList
294: .getProperty(REAL_NODES);
295: if (realNodes == null || realNodes.size() != nodeList.size()) {
296: Branch branch = branches.get(queryIndex);
297: NodeQuery nq = branch.getLeafQuery();
298: realNodes = nq.getNodeManager().getList(nq); // We trust the query cache! (the query is performed already, but on Cloud)
299: nodeList.setProperty(REAL_NODES, realNodes);
300: }
301: assert realNodes.size() == nodeList.size() : "The size of nodeList "
302: + nodeList.size()
303: + " does not match realNodes "
304: + realNodes.size()
305: + " at queryIndex; "
306: + queryIndex
307: + " query "
308: + branches.get(queryIndex).getLeafQuery().toSql();
309: assert realNodes.size() >= index : "The size of realNodes ("
310: + realNodes.size() + ") is too small (index = " + index
311: + ")";
312: return realNodes.getNode(index);
313: }
314:
315: public NodeList subNodeList(int start, int end) {
316: throw new UnsupportedOperationException(
317: "SubNodeLists not implemented for TreeList");
318: }
319:
320: public NodeList subList(int start, int end) {
321: throw new UnsupportedOperationException(
322: "SubNodeLists not implemented for TreeList");
323: }
324:
325: @Override
326: public String toString() {
327: int size = size();
328: return "size: " + size + " " + branches.toString();
329: }
330:
331: /**
332: * Structure to hold the information for every branch-depth.
333: * @since MMBase-1.8
334: */
335: protected class Branch {
336: final private NodeQuery query;
337: private NodeQuery leafQuery = null;
338: NodeList result = null;
339: NodeList leafResult = null;
340: Constraint leafConstraint = null;
341:
342: Branch(NodeQuery q) {
343: query = q;
344: }
345:
346: Branch(Branch b) {
347: query = (NodeQuery) b.query.clone();
348: result = b.result;
349: leafQuery = b.leafQuery == null ? null
350: : (NodeQuery) b.leafQuery.clone();
351: leafResult = null;
352: leafConstraint = b.leafConstraint;
353: }
354:
355: NodeQuery getQuery() {
356: return query;
357: }
358:
359: NodeQuery getLeafQuery() {
360: if (leafQuery != null)
361: return leafQuery;
362: Queries.sortUniquely(query);
363: Queries.addSortedFields(query);
364: int m = TreeList.this .getMax();
365: if (m != SearchQuery.DEFAULT_MAX_NUMBER) {
366: int cm = query.getMaxNumber();
367: if (cm == -1 || m < cm) {
368: query.setMaxNumber(m);
369: }
370: }
371: query.markUsed();
372: if (leafConstraint != null) {
373: leafQuery = (NodeQuery) query.clone();
374: Queries.addConstraint(leafQuery, leafConstraint);
375: leafQuery.markUsed();
376: } else {
377: leafQuery = query;
378: }
379: return leafQuery;
380: }
381:
382: @Override
383: public String toString() {
384: return query.toString()
385: + (leafConstraint != null ? "[" + leafConstraint
386: + "]" : "");
387: }
388:
389: }
390:
391: /**
392: * The TreeIterator contains the core-functionality of TreeList.
393: */
394: protected class TreeItr implements TreeIterator {
395:
396: private List<NodeIterator> nodeIterators = new ArrayList<NodeIterator>(); // an iterator for each query result
397: private NodeList nextNodes = TreeList.this .cloud
398: .createNodeList();
399: // contains 'next' nodes for each query result (needed for 'next()')
400:
401: private NodeList previousNodes = TreeList.this .cloud
402: .createNodeList();
403: // contains 'previous' nodes for each query result (needed for 'previous()')
404:
405: private int currentIterator; // number of current iterator which is iterated
406: private int nextIndex; // the next index number, so this is 0 on the beginning, and <size> just before the last next()
407:
408: private boolean encounteredLeafConstraint = false;
409: private Node current;
410:
411: TreeItr(int i) {
412: if (i < 0 || (i > 0 && i > TreeList.this .size())) {
413: throw new IndexOutOfBoundsException("Index: " + i
414: + ", Size: " + TreeList.this .size());
415: }
416: currentIterator = 0;
417: nextIndex = 0;
418: while (nextIndex < i) {
419: next(); // fast forward to requested start index.
420: }
421:
422: }
423:
424: public boolean hasNext() {
425: if (TreeList.this .max != SearchQuery.DEFAULT_MAX_NUMBER
426: && nextIndex > TreeList.this .max)
427: return false;
428: if (TreeList.this .foundEnd) { // why bother
429: return nextIndex < TreeList.this .size();
430: } else {
431: int i = 0;
432: while (prepare(i)) {
433: NodeIterator iterator = nodeIterators.get(i);
434: Node nextNode = nextNodes.get(i);
435: if (nextNode != null) {
436: return true;
437: } else {
438: i++;
439: }
440: }
441: return false;
442: }
443: }
444:
445: /**
446: * Makes sure that query with given index has an iterator, a 'next' node and a 'previous' node.
447: * @return true it such query existed, false, otherwise
448: */
449: protected final boolean prepare(int index) {
450: for (int i = nodeIterators.size(); i <= index; i++) {
451: NodeList nl = TreeList.this .getLeafList(i);
452: if (TreeList.this .leafConstraintOffset <= i) {
453: encounteredLeafConstraint = true;
454: }
455: NodeIterator iterator = null;
456: if (nl != null) {
457: iterator = nl.nodeIterator();
458: }
459: nodeIterators.add(iterator);
460: previousNodes.add(null); // just prepared iterator never has a previous node already
461: if (iterator == null) {
462: nextNodes.add(null);
463: return false;
464: } else {
465: if (iterator.hasNext()) {
466: nextNodes.add(iterator.nextNode());
467: } else {
468: nextNodes.add(null);
469: return true;
470: }
471: }
472: }
473: return true;
474: }
475:
476: /**
477: * Uses the new 'next' node of the iterator with the given index.
478: * This means that it becomes the previous node and that a new 'next' node will be determined
479: */
480: protected final void useNext(int index) {
481: Node node = nextNodes.getNode(index);
482: if (node == null)
483: throw new NoSuchElementException("No such element "
484: + index + " in " + nextNodes);
485: previousNodes.set(index, node);
486: NodeIterator iterator = nodeIterators.get(index);
487: if (iterator.hasNext()) {
488: Node nextNode = iterator.nextNode();
489: nextNodes.set(index, nextNode);
490: } else {
491: nextNodes.set(index, null);
492: }
493: }
494:
495: /**
496: * Returns the 'real' node, thus the just used 'next' node of index.
497: */
498: protected final Node getRealNode(int index) {
499: ListIterator<Node> iterator = nodeIterators.get(index);
500: return TreeList.this .getRealNode(index, iterator
501: .previousIndex());
502: }
503:
504: public Node nextNode() {
505: nextIndex++;
506: current = getNextNode();
507: return current;
508: }
509:
510: public Node getParent() {
511: NodeList nl = TreeList.this .getLeafList(currentDepth() - 1);
512: Query q = TreeList.this .branches.get(currentDepth() - 1)
513: .getQuery();
514: List<Step> steps = q.getSteps();
515: if (steps.size() >= 3) {
516: Step this Step = steps.get(steps.size() - 1);
517: Step parentStep = steps.get(steps.size() - 3);
518: for (Node sibling : nl) {
519: if (current.getNumber() == sibling
520: .getIntValue(this Step.getAlias()
521: + ".number")) {
522: return getCloud().getNode(
523: sibling.getIntValue(parentStep
524: .getAlias()
525: + ".number"));
526: }
527: }
528: }
529: return null;
530: }
531:
532: public NodeList getSiblings() {
533: NodeList nl = TreeList.this .getLeafList(currentDepth() - 1);
534: Query q = TreeList.this .branches.get(currentDepth() - 1)
535: .getQuery();
536: List<Step> steps = q.getSteps();
537: NodeList l = getCloud().createNodeList();
538: if (steps.size() >= 3) {
539: int start = 0;
540: int end = 0;
541: int parent = getParent().getNumber();
542: Step this Step = steps.get(steps.size() - 1);
543: Step parentStep = steps.get(steps.size() - 3);
544: for (Node sibling : nl) {
545: if (sibling.getIntValue(parentStep.getAlias()
546: + ".number") == parent) {
547: l.add(getCloud().getNode(
548: sibling.getIntValue(this Step.getAlias()
549: + ".number")));
550: }
551: }
552: return l;
553: } else {
554: l.add(current);
555: return l;
556: }
557: }
558:
559: /**
560: * Depth of the last node fetched with next() or nextNode()
561: */
562: public int currentDepth() {
563: Branch branch = TreeList.this .branches.get(currentIterator);
564: int depth = (branch.query.getSteps().size() + 1) / 2;
565: if (nextIndex == 0) {
566: return depth - 1;
567: } else {
568: return depth;
569: }
570: }
571:
572: public Node next() {
573: return nextNode();
574: }
575:
576: /**
577: *
578: * Implementation idea graphicly.
579: <pre>
580: iterators
581:
582:
583: current-2 current-1 current current+1 [///]: used node
584: [///] [///] [///] [///] [|||]: last used node (lastNode)
585: [ ]: unused node
586: ... [///] [///] [|||] _ [///] previousNodes [ * ]: considered next node (nextListNextNode)
587: \
588: [ ] [ ] [ ] `---> [ * ] nextNodes
589:
590: if (! [|||] contained by [ * ]) current--
591: </pre>
592:
593: Every time next is called, the last used node is compared with the next node of the
594: next iterator (the arrow in the above scheme). If the last used node is 'contained' by
595: this next node, then this next node of the next iterator will be 'next()' otherwise current
596: is decreased by one and next is called recursively. This means that the next node is always
597: one longer than the current one, equally long, or shorter.
598:
599: If 'leaf constraints' are in use, then the implementation jumps to getNextLeafNode, which simply returns the 'smallest node' of all iterators.
600: */
601: protected final Node getNextNode() {
602: prepare(currentIterator);
603: if (encounteredLeafConstraint) {
604: return getNextLeafNode();
605: }
606:
607: final Branch currentBranch = TreeList.this .branches
608: .get(currentIterator);
609:
610: Node previousNode = previousNodes.getNode(currentIterator);
611: if (previousNode == null) { // first of iterator
612: Node node = getRealNode(currentIterator);
613: useNext(currentIterator);
614: return node;
615: }
616:
617: Node nextListNextNode = prepare(currentIterator + 1) ? nextNodes
618: .getNode(currentIterator + 1)
619: : null;
620:
621: if (nextListNextNode == null) {
622: if (currentIterator > 0) {
623: currentIterator--;
624: return getNextNode();
625: } else {
626: Node node = getRealNode(0);
627: useNext(0);
628: return node;
629: }
630: }
631:
632: List<SortOrder> sortOrders = currentBranch.getQuery()
633: .getSortOrders();
634: final boolean contains = Queries.compare(previousNode,
635: nextListNextNode, sortOrders) >= 0;
636:
637: if (log.isDebugEnabled()) {
638: log.debug("comparing " + previousNode + " with "
639: + nextListNextNode);
640: }
641:
642: if (contains) {
643: currentIterator++;
644: Node node = getRealNode(currentIterator);
645: useNext(currentIterator);
646: return node;
647: } else {
648: if (currentIterator > 0) {
649: currentIterator--;
650: return getNextNode();
651: } else {
652: Node node = getRealNode(0);
653: useNext(0);
654: return node;
655: }
656: }
657: }
658:
659: /**
660: * Simply returns the 'smallest' of all available nodes (compared to the 'previous node')
661: * This is actually an alternavite implementation for getNextNode, but it also works when
662: * 'leaf' constraints are applied.
663: * @since MMBase-1.8
664: */
665: protected final Node getNextLeafNode() {
666: Node smallestAvailableNode = null;
667: List<SortOrder> smallestSortOrders = null; // Sort-Orders list of smallest availabe node.
668: int i = -1;
669:
670: while (prepare(++i)) {
671: Node candidate = i < nextNodes.size() ? nextNodes
672: .getNode(i) : null;
673: if (candidate == null) {
674: continue;
675: }
676: Branch branch = TreeList.this .branches.get(i);
677: List<SortOrder> sortOrders = branch.getLeafQuery()
678: .getSortOrders();
679: if (smallestAvailableNode == null) {
680: smallestAvailableNode = candidate;
681: smallestSortOrders = sortOrders;
682: currentIterator = i;
683: } else {
684: List<SortOrder> compareSortOrders = sortOrders
685: .size() < smallestSortOrders.size() ? sortOrders
686: : smallestSortOrders;
687: int compare = Queries.compare(candidate,
688: smallestAvailableNode, compareSortOrders);
689: if (compare < 0) {
690: smallestAvailableNode = candidate;
691: smallestSortOrders = sortOrders;
692: currentIterator = i;
693: }
694: }
695: }
696: if (smallestAvailableNode == null) {
697: throw new NoSuchElementException();
698: }
699: Node node = getRealNode(currentIterator);
700: useNext(currentIterator);
701: return node;
702: }
703:
704: public boolean hasPrevious() {
705: return nextIndex > 0;
706: }
707:
708: public Node previousNode() {
709: nextIndex--;
710: throw new UnsupportedOperationException("unfinished");
711: }
712:
713: public Node previous() {
714: return previousNode();
715: }
716:
717: public int nextIndex() {
718: return nextIndex;
719: }
720:
721: public int previousIndex() {
722: return nextIndex - 1;
723: }
724:
725: public void remove() {
726: throw new UnsupportedOperationException(
727: "TreeList is not modifiable");
728: }
729:
730: public void set(Node o) {
731: throw new UnsupportedOperationException(
732: "TreeList is not modifiable");
733: }
734:
735: public void add(Node o) {
736: throw new UnsupportedOperationException(
737: "TreeList is not modifiable");
738: }
739:
740: }
741:
742: /**
743: * For testing only. Based on RMMCI,
744: * please use the System property to specify de cloud context
745: * -Dmmbase.defaultcloudcontext=rmi://localhost:1111/remotecontext
746: * @param args the start node (in one argument)
747: */
748:
749: protected static NodeQuery getQuery(String[] args) {
750: if (args.length == 0) {
751: System.err
752: .println("Usage: java -Dmmbase.defaultcloudcontext=rmi://localhost:1111/remotecontext "
753: + TreeList.class.getName() + " <startnode>");
754: System.exit(1);
755: }
756:
757: String startNodes = args[0];
758: Cloud cloud = ContextProvider.getDefaultCloudContext()
759: .getCloud("mmbase");
760:
761: String type = args.length > 1 ? args[1] : "segments";
762: String role = args.length > 2 ? args[2] : "index";
763: NodeManager object = cloud.getNodeManager(type);
764:
765: NodeQuery q = cloud.createNodeQuery();
766: Step step = q.addStep(object);
767: q.setNodeStep(step);
768: RelationStep relationStep = q.addRelationStep(object, role,
769: "destination");
770: q.setNodeStep(relationStep.getNext());
771: StepField pos = q.createStepField(relationStep, "pos");
772: q.addSortOrder(pos, SortOrder.ORDER_ASCENDING);
773:
774: object.getList(q);
775:
776: Queries.addStartNodes(q, startNodes);
777: return q;
778: }
779:
780: public static void doTest(java.io.Writer writer, NodeQuery q) {
781: Cloud cloud = q.getCloud();
782:
783: NodeManager object = q.getNodeManager();
784: try {
785: //String text = "%potjandosie%";
786: String text = "%%";
787:
788: long startTime = System.currentTimeMillis();
789:
790: TreeList tree = new TreeList(q);
791: if (object.hasField("body")) {
792: Constraint con2 = Queries.createConstraint(tree
793: .getLeafQuery(), "body", Queries
794: .getOperator("LIKE"), text);
795: //tree.setLeafConstraint(con2);
796: }
797:
798: writer.write("grow1:\n");
799: writer.flush();
800: RelationStep step = tree.grow(object, "posrel",
801: "destination");
802: NodeQuery top = tree.getLeafQuery();
803: if (object.hasField("body")) {
804: Constraint con1 = Queries.createConstraint(top, "body",
805: Queries.getOperator("LIKE"), text);
806: //tree.setLeafConstraint(con1);
807: }
808: assert step != null;
809: StepField pos = top.createStepField(step, "pos");
810: top.addSortOrder(pos, SortOrder.ORDER_ASCENDING);
811:
812: writer.write("top " + top.toSql() + " grow2:\n");
813: writer.flush();
814: tree.grow(object, "posrel", "destination");
815: NodeQuery leaf = tree.getLeafQuery();
816: Constraint con = Queries.createConstraint(leaf, "body",
817: Queries.getOperator("LIKE"), text);
818: //tree.setLeafConstraint(con);
819:
820: writer
821: .write("GROWN, now using ================================================================================");
822: writer.flush();
823: TreeIterator i = tree.treeIterator();
824: writer.write("initial depth " + i.currentDepth() + "\n");
825: writer.flush();
826: writer.write("size: " + tree.size() + "\n");
827: writer.flush();
828: while (i.hasNext()) {
829: Node n = i.next();
830: try {
831: writer.write(n.getFunctionValue("index", null)
832: .toString()
833: + "\t");
834: } catch (Exception e) {
835: }
836: writer.write(i.currentDepth() + " " + n.getNumber()
837: + " " + n.getFunctionValue("gui", null) + "\n");
838: writer.flush();
839: }
840: writer.write("size: " + tree.size() + "\n");
841: writer.write("duration: "
842: + (System.currentTimeMillis() - startTime)
843: + " ms\n");
844: writer.write("finish depth: " + i.currentDepth());
845: writer.flush();
846: } catch (Exception e) {
847: System.err.println(e.getClass().getName() + e.getMessage()
848: + Logging.stackTrace(e));
849: }
850:
851: }
852:
853: public static void main(String[] args) {
854: NodeQuery q = getQuery(args);
855: doTest(new java.io.OutputStreamWriter(System.out), q);
856:
857: }
858:
859: }
|