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 java.util.Iterator;
014:
015: import org.mmbase.bridge.*;
016: import org.mmbase.storage.search.*;
017: import org.mmbase.util.logging.*;
018:
019: /**
020: *
021: * This version of {@link TreeList} is automaticly growing with the same 'branch' every time when that is possible. For that
022: * it needs a kind of template query for every branch, which is defined by the constructor.
023: *
024: *
025: * @author Michiel Meeuwissen
026: * @version $Id: GrowingTreeList.java,v 1.19 2007/02/10 17:44:03 nklasens Exp $
027: * @since MMBase-1.7
028: */
029:
030: public class GrowingTreeList extends TreeList {
031: private static final Logger log = Logging
032: .getLoggerInstance(GrowingTreeList.class);
033: protected Constraint cleanConstraint;
034: protected NodeQuery pathElementTemplate;
035: protected Constraint cleanLeafConstraint = null;
036: protected NodeQuery leafElementTemplate = null;
037: protected int maxNumberOfSteps;
038:
039: /**
040: * @param q The 'base' query defining the minimal depth of the tree elements. The trunk of the tree.
041: * @param maxDepth You must supply a maximal depth of the nodes, because MMBase is basicly a network rather then a tree, so
042: * tree representations could be infinitely deep.
043: * @param nodeManager Destination Nodemanager in the tree
044: * @param role Role of the relations in the tree
045: * @param searchDir Direction of the relations in the tree
046: * @since MMBase-1.7.1
047: */
048: public GrowingTreeList(NodeQuery q, int maxDepth,
049: NodeManager nodeManager, String role, String searchDir) {
050: super (q);
051: if (log.isDebugEnabled()) {
052: log.debug("Making growering tree-list with " + q.toSql());
053: }
054: if (nodeManager == null)
055: nodeManager = cloud.getNodeManager("object");
056: pathElementTemplate = cloud.createNodeQuery();
057: //shiftElementTemplate = cloud.createNodeQuery();
058: Step step = pathElementTemplate.addStep(cloud
059: .getNodeManager("object"));
060: pathElementTemplate.setAlias(step, "object0");
061: pathElementTemplate.setNodeStep(pathElementTemplate
062: .addRelationStep(nodeManager, role, searchDir)
063: .getNext());
064:
065: setMaxDepth(maxDepth);
066: }
067:
068: /**
069: * This may be used in combination with
070: * <code>Queries.addPath(tree.getTemplate(), (String) path.getValue(this), (String) searchDirs.getValue(this));</code>
071: * So you add a template constisting of a bunch of elements.
072: */
073: public GrowingTreeList(NodeQuery q, int maxDepth) {
074: super (q);
075: pathElementTemplate = cloud.createNodeQuery();
076: Step step = pathElementTemplate.addStep(cloud
077: .getNodeManager("object"));
078: pathElementTemplate.setAlias(step, "object0");
079:
080: setMaxDepth(maxDepth);
081: }
082:
083: public GrowingTreeList(TreeList tl, int maxDepth) {
084: super (tl);
085: pathElementTemplate = cloud.createNodeQuery();
086: Step step = pathElementTemplate.addStep(cloud
087: .getNodeManager("object"));
088: pathElementTemplate.setAlias(step, "object0");
089:
090: setMaxDepth(maxDepth);
091: }
092:
093: /**
094: * As long as the tree is not 'started' yet, max depth can still be changed.
095: * @param maxDepth max number of Steps
096: * @since MMBase-1.7.1
097: */
098:
099: public void setMaxDepth(int maxDepth) {
100: maxNumberOfSteps = 2 * maxDepth - 1; // dont consider relation steps.
101:
102: if (maxNumberOfSteps < numberOfSteps) {
103: throw new IllegalArgumentException(
104: "Query is already deeper than maxdepth");
105: }
106: }
107:
108: /**
109: * Returns the Query which is used as a template for the leaves to 'grow' the query. You can change it, add
110: * sort-orders and add constraints before the tree is 'started'. All but the first step of this
111: * query are added. This query itself is never executed, though marked used, to avoid changes on
112: * it after the list has started.
113: *
114: * @return Query which is used as a template
115: * @since MMBase-1.7.1
116: */
117:
118: public NodeQuery getTemplate() {
119: return pathElementTemplate;
120: }
121:
122: /**
123: * The leave template is the 'last' template. This is the same as getTemplate, only, the
124: * constraints set on this, are only used if the template is used 'on the end'.
125: *
126: * It boils down to the fact that constraints set on the query don't change the tree itself, but
127: * only constraint the 'leaves', so it makes for a kind of tree-search.
128: * @since MMBase-1.8
129: */
130: public NodeQuery getLeafTemplate() {
131: if (leafElementTemplate == null) {
132: leafElementTemplate = (NodeQuery) pathElementTemplate
133: .clone();
134: }
135: return leafElementTemplate;
136: }
137:
138: @Override
139: public int size() {
140: while (!foundEnd) {
141: addPathElement();
142: }
143: return super .size();
144: }
145:
146: @Override
147: protected NodeList getList(int queryNumber) {
148: while (queryNumber >= branches.size() && (!foundEnd)) {
149: addPathElement();
150: }
151: return super .getList(queryNumber);
152: }
153:
154: @Override
155: protected NodeList getLeafList(int queryNumber) {
156: while (queryNumber >= branches.size() && (!foundEnd)) {
157: addPathElement();
158: }
159: return super .getLeafList(queryNumber);
160: }
161:
162: /**
163: * Grows the branches of the tree, with the leave.
164: *
165: */
166: protected void addPathElement() {
167: if (numberOfSteps + 2 > maxNumberOfSteps) {
168: foundEnd = true;
169: } else {
170: if (!pathElementTemplate.isUsed()) {
171: pathElementTemplate.markUsed();
172: cleanConstraint = pathElementTemplate
173: .getCleanConstraint();
174: }
175: if (leafElementTemplate != null
176: && !leafElementTemplate.isUsed()) {
177: leafElementTemplate.markUsed();
178: cleanLeafConstraint = leafElementTemplate
179: .getCleanConstraint();
180: }
181:
182: Iterator<Step> steps = pathElementTemplate.getSteps()
183: .iterator();
184: ;
185: steps.next(); // ignore first step
186: if (!steps.hasNext()) {
187: foundEnd = true;
188: return;
189: }
190: while (steps.hasNext()) {
191: RelationStep relationStepTemplate = (RelationStep) steps
192: .next();
193: Step stepTemplate = steps.next();
194: String role;
195: { // it's a pity but role cannot be requested directly from RelationStep
196: // some hackery
197: Integer reldef = relationStepTemplate.getRole();
198: if (reldef == null) {
199: role = null;
200: } else {
201: role = cloud.getNode(reldef.intValue())
202: .getStringValue("sname");
203: }
204: }
205:
206: RelationStep newStep = grow(
207: cloud.getNodeManager(stepTemplate
208: .getTableName()),
209: role,
210: RelationStep.DIRECTIONALITY_DESCRIPTIONS[relationStepTemplate
211: .getDirectionality()]);
212: if (newStep == null) {
213: foundEnd = true;
214: break;
215: }
216: // Step doesn't have a .getQuery() method, so we'll have to fall back to this:
217: Branch branch = branches.get(branches.size() - 1);
218: Query newQuery = branch.getQuery();
219:
220: // add sortorder to the query
221: Step nextStep = newStep.getNext();
222:
223: if (cleanConstraint != null) {
224: Constraint newStepConstraint = Queries
225: .copyConstraint(cleanConstraint,
226: stepTemplate, newQuery, nextStep);
227: Constraint newRelationStepConstraint = Queries
228: .copyConstraint(cleanConstraint,
229: relationStepTemplate, newQuery,
230: newStep);
231: Queries.addConstraint(newQuery, newStepConstraint);
232: Queries.addConstraint(newQuery,
233: newRelationStepConstraint);
234: }
235:
236: Queries.copySortOrders(pathElementTemplate
237: .getSortOrders(), stepTemplate, newQuery,
238: nextStep);
239: Queries.copySortOrders(pathElementTemplate
240: .getSortOrders(), relationStepTemplate,
241: newQuery, newStep);
242:
243: if (cleanLeafConstraint != null) {
244: Constraint newLeafStepConstraint = Queries
245: .copyConstraint(cleanLeafConstraint,
246: stepTemplate, newQuery, nextStep);
247: Constraint newLeafRelationStepConstraint = Queries
248: .copyConstraint(cleanLeafConstraint,
249: relationStepTemplate, newQuery,
250: newStep);
251: if (newLeafStepConstraint != null
252: && newLeafRelationStepConstraint != null) {
253: CompositeConstraint comp = newQuery
254: .createConstraint(
255: newLeafStepConstraint,
256: CompositeConstraint.LOGICAL_AND,
257: newLeafRelationStepConstraint);
258: setLeafConstraint(comp);
259: } else if (newLeafStepConstraint != null) {
260: setLeafConstraint(newLeafStepConstraint);
261: } else if (newLeafRelationStepConstraint != null) {
262: setLeafConstraint(newLeafRelationStepConstraint);
263: } else {
264: // both null, ignore
265: }
266: }
267:
268: if (numberOfSteps + 2 > maxNumberOfSteps) {
269: foundEnd = true;
270: break;
271: }
272: }
273: }
274: }
275:
276: public static void main(String[] args) {
277: Cloud cloud = ContextProvider.getDefaultCloudContext()
278: .getCloud("mmbase");
279: //NodeQuery q = getQuery(args);
280: NodeQuery q = Queries.createNodeQuery(cloud.getNode(args[0]));
281:
282: NodeManager object = cloud.getNodeManager("segments");
283:
284: GrowingTreeList tree = new GrowingTreeList(q, 40, object,
285: "index", "destination");
286:
287: String text = "Exodus 20, vers 7";
288: NodeQuery temp = tree.getTemplate();
289: Queries.addSortOrders(temp, "index.pos", "up");
290: NodeQuery template = tree.getLeafTemplate();
291: Constraint cons1 = Queries.createConstraint(template, "title",
292: FieldCompareConstraint.LIKE, "%" + text + "%");
293: Constraint cons2 = Queries.createConstraint(template, "body",
294: FieldCompareConstraint.LIKE, "%" + text + "%");
295: Constraint compConstraint = template.createConstraint(cons1,
296: CompositeConstraint.LOGICAL_OR, cons2);
297: template.setConstraint(compConstraint);
298:
299: //System.out.println("size " + tree.size());
300: System.out.println("template " + tree.getTemplate());
301: System.out.println("leaf template " + tree.getLeafTemplate());
302:
303: int k = 0;
304: TreeIterator i = tree.treeIterator();
305: while (i.hasNext()) {
306: Node n = i.nextNode();
307: k++;
308: System.out.println(k + " " + i.currentDepth() + " "
309: + n.toString());
310: }
311: }
312: }
|