001: /*
002: * The contents of this file are subject to the terms of the Common Development
003: * and Distribution License (the License). You may not use this file except in
004: * compliance with the License.
005: *
006: * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
007: * or http://www.netbeans.org/cddl.txt.
008: *
009: * When distributing Covered Code, include this CDDL Header Notice in each file
010: * and include the License file at http://www.netbeans.org/cddl.txt.
011: * If applicable, add the following below the CDDL Header, with the fields
012: * enclosed by brackets [] replaced by your own identifying information:
013: * "Portions Copyrighted [year] [name of copyright owner]"
014: *
015: * The Original Software is NetBeans. The Initial Developer of the Original
016: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
017: * Microsystems, Inc. All Rights Reserved.
018: */
019:
020: package org.netbeans.modules.xslt.mapper.view;
021:
022: import java.util.Collection;
023: import java.util.Iterator;
024: import java.util.LinkedList;
025: import java.util.List;
026: import java.util.ListIterator;
027: import javax.swing.JTree;
028: import org.netbeans.modules.soa.mapper.basicmapper.util.MapperUtilities;
029: import org.netbeans.modules.soa.mapper.common.basicmapper.IBasicMapperEvent;
030: import org.netbeans.modules.xml.axi.AXIComponent;
031: import org.netbeans.modules.xml.xpath.XPathPredicateExpression;
032: import org.netbeans.modules.xslt.mapper.model.FindUsedPredicateNodesVisitor;
033: import org.netbeans.modules.xslt.mapper.model.PredicatedAxiComponent;
034: import org.netbeans.modules.xslt.mapper.model.SourceTreeModel;
035: import org.netbeans.modules.xslt.mapper.model.nodes.Node;
036: import org.netbeans.modules.xslt.mapper.model.nodes.NodeFactory;
037: import org.netbeans.modules.xslt.mapper.model.nodes.TreeNode;
038: import org.netbeans.modules.xslt.mapper.model.targettree.PredicatedSchemaNode;
039: import org.netbeans.modules.xslt.mapper.model.targettree.SchemaNode;
040:
041: /**
042: * The class collects all predicate expressions which are in the XSLT model.
043: * Each predicate is declared in the XPath location step.
044: * The location step is bound to the specific schema element or attribute.
045: * The predicate manager keeps the location of predicate in term of
046: * schema elements' path.
047: *
048: * The set of predicates is populated from XSLT model. The manager can
049: * be populated entirely or can track small model's changes only.
050: * It produces notification messages about adding, editing or deleting of
051: * the predicates set.
052: *
053: * The main intention of the predicate manager is to provide showing of
054: * predicates in the mapper source tree.
055: *
056: * @author nk160297
057: */
058: public class PredicateManager {
059:
060: private XsltMapper myMapper;
061:
062: // The cache of predicates.
063: private LinkedList<CachedPredicate> myPredicates;
064:
065: public PredicateManager(XsltMapper mapper) {
066: myMapper = mapper;
067: myPredicates = new LinkedList<CachedPredicate>();
068: }
069:
070: /**
071: * Looks for the predicated tree node similar to the source node
072: * with the specified predicates.
073: */
074: public TreeNode getPredicatedNode(TreeNode baseNode,
075: XPathPredicateExpression[] predArr) {
076: assert baseNode instanceof SchemaNode;
077: //
078: AXIComponent type = baseNode.getType();
079: //
080: // Look for the corresponding predicate
081: CachedPredicate soughtPredicate = null;
082: for (CachedPredicate cp : myPredicates) {
083: if (cp.hasSameParams(type, predArr)
084: && cp.hasSameLocation(baseNode)) {
085: soughtPredicate = cp;
086: break;
087: }
088: }
089: //
090: if (soughtPredicate == null) {
091: return null;
092: }
093: //
094: // Look for a sibling predicated node with the corresponding predicate
095: PredicatedAxiComponent soughtComp = soughtPredicate
096: .getPComponent();
097: TreeNode parentNode = baseNode.getParent();
098: if (parentNode == null) {
099: // The parent node always has to be specified!
100: return null;
101: }
102: for (TreeNode siblingNode : parentNode.getChildren()) {
103: if (siblingNode instanceof PredicatedSchemaNode) {
104: PredicatedAxiComponent comp = ((PredicatedSchemaNode) siblingNode)
105: .getPredicatedAxiComp();
106: if (comp.equals(soughtComp)) {
107: return siblingNode;
108: }
109: }
110: }
111: //
112: return null;
113: }
114:
115: /**
116: * Looks for the predicated tree nodes similar to the source node.
117: */
118: public Collection<PredicatedSchemaNode> getPredicatedNodes(
119: TreeNode baseNode) {
120: assert baseNode instanceof SchemaNode;
121: //
122: TreeNode parentNode = baseNode.getParent();
123: LinkedList result = new LinkedList<PredicatedSchemaNode>();
124: if (parentNode != null) {
125: for (TreeNode siblingNode : parentNode.getChildren()) {
126: if (siblingNode instanceof PredicatedSchemaNode) {
127: result.add((PredicatedSchemaNode) siblingNode);
128: }
129: }
130: }
131: return result;
132: }
133:
134: /**
135: * Construct the new predicated tree nodes similar to the base node.
136: */
137: public Collection<PredicatedSchemaNode> createPredicatedNodes(
138: TreeNode baseNode) {
139: assert baseNode instanceof SchemaNode;
140: //
141: AXIComponent type = baseNode.getType();
142: //
143: // Look for the corresponding predicates
144: LinkedList<CachedPredicate> suitablePredicates = new LinkedList<CachedPredicate>();
145: for (CachedPredicate cp : myPredicates) {
146: if (cp.getType().equals(type)
147: && cp.hasSameLocation(baseNode)) {
148: suitablePredicates.add(cp);
149: }
150: }
151: //
152: // Construct new nodes
153: LinkedList<PredicatedSchemaNode> result = new LinkedList<PredicatedSchemaNode>();
154: for (CachedPredicate cp : suitablePredicates) {
155: Node newNode = NodeFactory.createNode(cp.getPComponent(),
156: myMapper);
157: assert newNode instanceof PredicatedSchemaNode;
158: result.add((PredicatedSchemaNode) newNode);
159: }
160: //
161: return result;
162: }
163:
164: /**
165: * Creates a new predicate and cache it.
166: * Then creates a new predicated node and return it.
167: * This method is intended to be called, when a user creates a new predicate manually.
168: */
169: public PredicatedSchemaNode createPredicatedNode(TreeNode baseNode,
170: XPathPredicateExpression[] predArr) {
171: //
172: assert baseNode instanceof SchemaNode;
173: AXIComponent type = baseNode.getType();
174: //
175: PredicatedAxiComponent newPAxiComp = new PredicatedAxiComponent(
176: type, predArr);
177: //
178: CachedPredicate newPredicate = new CachedPredicate(newPAxiComp,
179: baseNode);
180: newPredicate.setPersistent(true);
181: if (!addPredicate(newPredicate)) {
182: return null;
183: }
184: //
185: TreeNode parentNode = baseNode.getParent();
186: Node newNode = NodeFactory.createNode(newPAxiComp, myMapper);
187: assert newNode instanceof PredicatedSchemaNode;
188: ((PredicatedSchemaNode) newNode).setParent(parentNode);
189: //
190: JTree sourceTree = myMapper.getMapperViewManager()
191: .getSourceView().getTree();
192: assert sourceTree != null;
193: SourceTreeModel sourceTreeModel = (SourceTreeModel) sourceTree
194: .getModel();
195: parentNode.reload();
196: sourceTreeModel.fireTreeChanged(TreeNode
197: .getTreePath(parentNode));
198: //
199: return (PredicatedSchemaNode) newNode;
200: }
201:
202: /**
203: * This method is intended to be called after a new predicate is found
204: * in the XSLT during a parsing.
205: */
206: public void addPredicate(List objLocationPath) {
207: CachedPredicate newPredicate = new CachedPredicate(
208: objLocationPath);
209: newPredicate.setPersistent(false);
210: if (!addPredicate(newPredicate)) {
211: return;
212: }
213: //
214: TreeNode parentNode = lookForParentTreeNode(newPredicate);
215: parentNode.reload();
216: //
217: // Notify the tree
218: JTree sourceTree = myMapper.getMapperViewManager()
219: .getSourceView().getTree();
220: assert sourceTree != null;
221: SourceTreeModel sourceTreeModel = (SourceTreeModel) sourceTree
222: .getModel();
223: sourceTreeModel.fireTreeChanged(TreeNode
224: .getTreePath(parentNode));
225: }
226:
227: private boolean addPredicate(CachedPredicate newPredicate) {
228: //
229: // Check is there the same predicate already in the cache
230: for (CachedPredicate predicate : myPredicates) {
231: if (predicate.equals(newPredicate)) {
232: // Dublicate. Nothing to do
233: return false;
234: }
235: }
236: //
237: myPredicates.add(newPredicate);
238: return true;
239: }
240:
241: /**
242: * This method is called when the predicate is modified manually.
243: */
244: public boolean modifyPredicate(PredicatedSchemaNode node,
245: XPathPredicateExpression[] predArr) {
246: PredicatedAxiComponent pac = node.getPredicatedAxiComp();
247: //
248: for (CachedPredicate cp : myPredicates) {
249: if (cp.hasSameParams(pac) && cp.hasSameLocation(node)) {
250: String oldPredicatesText = cp.getPComponent()
251: .getPredicatesText();
252: //
253: cp.getPComponent().setPredicates(predArr);
254: cp.setPersistent(true); // because it was modified manually
255: String newPredicatesText = cp.getPComponent()
256: .getPredicatesText();
257: //
258: TreeNode parentNode = node.getParent();
259: parentNode.reload();
260: //
261: // Notify the tree
262: JTree sourceTree = myMapper.getMapperViewManager()
263: .getSourceView().getTree();
264: assert sourceTree != null;
265: SourceTreeModel sourceTreeModel = (SourceTreeModel) sourceTree
266: .getModel();
267: sourceTreeModel.fireTreeChanged(TreeNode
268: .getTreePath(parentNode));
269: //
270: // Post notification to update the XSLT sources
271: myMapper
272: .getMapperViewManager()
273: .postMapperEvent(
274: MapperUtilities
275: .getMapperEvent(
276: this ,
277: node.getMapperNode(),
278: IBasicMapperEvent.REQ_UPDATE_NODE,
279: "Predicate is modeified."
280: + " Location: "
281: + cp
282: .locationToString()
283: + " Old: "
284: + oldPredicatesText
285: + " New: "
286: + newPredicatesText));
287:
288: return true;
289: }
290: }
291: //
292: return false;
293: }
294:
295: public boolean deletePredicate(PredicatedSchemaNode node) {
296: PredicatedAxiComponent pac = node.getPredicatedAxiComp();
297: //
298: for (CachedPredicate cp : myPredicates) {
299: if (cp.hasSameParams(pac) && cp.hasSameLocation(node)) {
300: myPredicates.remove(cp);
301: //
302: TreeNode parentNode = node.getParent();
303: parentNode.reload();
304: //
305: // Notify the tree
306: JTree sourceTree = myMapper.getMapperViewManager()
307: .getSourceView().getTree();
308: assert sourceTree != null;
309: SourceTreeModel sourceTreeModel = (SourceTreeModel) sourceTree
310: .getModel();
311: sourceTreeModel.fireTreeChanged(TreeNode
312: .getTreePath(parentNode));
313: return true;
314: }
315: }
316: //
317: return false;
318: }
319:
320: public void clearTemporaryPredicates() {
321: TreeNode root = (TreeNode) myMapper.getMapperViewManager()
322: .getDestView().getTree().getModel().getRoot();
323: if (root == null) {
324: // impossible to understand if the predicated node is used or not.
325: return;
326: }
327: FindUsedPredicateNodesVisitor vis = new FindUsedPredicateNodesVisitor();
328: root.accept(vis);
329:
330: ListIterator<CachedPredicate> predItr = myPredicates
331: .listIterator();
332:
333: while (predItr.hasNext()) {
334: CachedPredicate predicate = predItr.next();
335: PredicatedSchemaNode node = predicate.findNode(myMapper);
336: if (node == null) {
337: // the corresponding node isn't found so the cached predicate
338: // can be removed because its location path is corrupted.
339: predItr.remove();
340: } else {
341: if (predicate.isPersistent()) {
342: // the predicate is persistent
343: continue;
344: }
345: if (vis.getResultList().contains(node)) {
346: // the predicate is used
347: continue;
348: }
349: //
350: predItr.remove();
351: //
352: TreeNode parentNode = node.getParent();
353: parentNode.reload();
354: //
355: // Notify the tree
356: JTree sourceTree = myMapper.getMapperViewManager()
357: .getSourceView().getTree();
358: assert sourceTree != null;
359: SourceTreeModel sourceTreeModel = (SourceTreeModel) sourceTree
360: .getModel();
361: sourceTreeModel.fireTreeChanged(TreeNode
362: .getTreePath(parentNode));
363: }
364: }
365: }
366:
367: //-----------------------------------------------------------
368:
369: public static String toString(
370: XPathPredicateExpression[] predicatesArr) {
371: if (predicatesArr != null && predicatesArr.length != 0) {
372: StringBuilder sb = new StringBuilder();
373: for (XPathPredicateExpression predicate : predicatesArr) {
374: sb.append("[").append(predicate.getExpressionString())
375: .append("]");
376: }
377: return sb.toString();
378: } else {
379: return "";
380: }
381: }
382:
383: /**
384: * Looks for a node in the source tree by a location path of the predicate.
385: * The
386: */
387: private TreeNode lookForParentTreeNode(CachedPredicate predicate) {
388: JTree sourceTree = myMapper.getMapperViewManager()
389: .getSourceView().getTree();
390: SourceTreeModel model = (SourceTreeModel) sourceTree.getModel();
391: //
392: LinkedList locationPath = predicate.getLocationPath();
393: assert !locationPath.isEmpty();
394: //
395: // It is assumed that the root node is the first path element.
396: TreeNode rootNode = (TreeNode) model.getRoot();
397: ListIterator itr = locationPath.listIterator();
398: Object dataObj = itr.next();
399: if (!isCompatible(rootNode, dataObj)) {
400: return null;
401: }
402: //
403: TreeNode parentNode = rootNode;
404: PredicatedAxiComponent lastItem = predicate.getPComponent();
405: while (itr.hasNext() && parentNode != null) {
406: dataObj = itr.next();
407: //
408: if (dataObj == lastItem) {
409: break;
410: }
411: //
412: TreeNode soughtNode = null;
413: for (TreeNode childNode : parentNode.getChildren()) {
414: if (isCompatible(childNode, dataObj)) {
415: soughtNode = childNode;
416: break;
417: }
418: }
419: //
420: if (soughtNode == null) {
421: return null;
422: }
423: //
424: parentNode = soughtNode;
425: }
426: //
427: return parentNode;
428: }
429:
430: /**
431: * Returns true only if the tree node has compatible data object in
432: * comparison with the specified one.
433: */
434: private boolean isCompatible(TreeNode treeNode, Object dataObject) {
435: if (dataObject instanceof PredicatedAxiComponent) {
436: if (treeNode instanceof PredicatedSchemaNode
437: && ((PredicatedSchemaNode) treeNode)
438: .getPredicatedAxiComp().equals(dataObject)) {
439: return true;
440: }
441: } else if (dataObject instanceof AXIComponent) {
442: if (treeNode instanceof SchemaNode
443: && treeNode.getType().equals(dataObject)) {
444: return true;
445: }
446: } else {
447: assert false : "Incompatible data object"; // NOI18N
448: }
449: return false;
450: }
451:
452: //-----------------------------------------------------------
453:
454: public static class CachedPredicate {
455: // It's implied that the list contains either AxiComponents or PredicatedAxiComponent
456: // The last element of the list is always PredicatedAxiComponent
457: private LinkedList myLocationPath;
458:
459: // Persistense means that the instance should not be automatically
460: // deleted from the cache if it is not used.
461: // The predicates which are not persistent will be removed from
462: // the cache automatically.
463: private boolean isPersistent;
464:
465: public static LinkedList buildLocationPathList(
466: PredicatedAxiComponent predicateComp, TreeNode treeNode) {
467: LinkedList locationPath = new LinkedList();
468: //
469: // Add the predicateComp first. Finally it should be at the tail of list.
470: locationPath.add(predicateComp);
471: //
472: TreeNode parentNode = treeNode.getParent();
473: while (parentNode != null) {
474: Object dataObject = parentNode.getDataObject();
475: assert dataObject != null;
476: if (!((dataObject instanceof AXIComponent) || (dataObject instanceof PredicatedAxiComponent))) {
477: // The root node of the tree can be not of a schema type.
478: break;
479: }
480: //
481: // Add to beginning to provide natural location path order.
482: locationPath.addFirst(dataObject);
483: //
484: parentNode = parentNode.getParent();
485: }
486: //
487: //
488: return locationPath;
489: }
490:
491: public CachedPredicate(PredicatedAxiComponent predicateComp,
492: TreeNode baseNode) {
493: this (buildLocationPathList(predicateComp, baseNode));
494: }
495:
496: public CachedPredicate(List locationPath) {
497: if (locationPath instanceof LinkedList) {
498: myLocationPath = (LinkedList) locationPath;
499: } else {
500: myLocationPath = new LinkedList(locationPath);
501: }
502: }
503:
504: public boolean isPersistent() {
505: return isPersistent;
506: }
507:
508: public void setPersistent(boolean newValue) {
509: isPersistent = newValue;
510: }
511:
512: public AXIComponent getType() {
513: return getPComponent().getType();
514: }
515:
516: public PredicatedAxiComponent getPComponent() {
517: return (PredicatedAxiComponent) myLocationPath.getLast();
518: }
519:
520: /**
521: * Returns the list of data objects of TreeNode, which form the tree path location.
522: * Data objects can be either of AxiComponents or PredicatedAxiComponent type.
523: * The PredicatedAxiComponent corresponding to the predicate itself is included to the list.
524: * So the list can't be empty.
525: */
526: public LinkedList getLocationPath() {
527: return myLocationPath;
528: }
529:
530: /**
531: * Check if the cached predicate has the same AXIOM component type
532: * and the same predicates.
533: */
534: public boolean hasSameParams(AXIComponent compType,
535: XPathPredicateExpression[] predArr) {
536: PredicatedAxiComponent pComp = getPComponent();
537: return pComp.getType().equals(compType)
538: && pComp.hasSamePredicates(predArr);
539: }
540:
541: public boolean hasSameParams(PredicatedAxiComponent predAxiComp) {
542: PredicatedAxiComponent pComp = getPComponent();
543: return pComp.equals(predAxiComp);
544: }
545:
546: /**
547: * Check if the specified tree node has the same location path
548: * as the predicate.
549: */
550: public boolean hasSameLocation(TreeNode treeNode) {
551: TreeNode parentNode = treeNode.getParent();
552: //
553: // Set initial position of the iterator to the last but one.
554: ListIterator itr = myLocationPath
555: .listIterator(myLocationPath.size() - 1);
556: while (itr.hasPrevious()) {
557: //
558: if (parentNode == null) {
559: break;
560: }
561: //
562: Object step = itr.previous();
563: Object dataObject = parentNode.getDataObject();
564: if (!dataObject.equals(step)) {
565: // Inconsistent step type or data object class
566: return false;
567: }
568: //
569: // Everything Ok. Go to next step
570: parentNode = parentNode.getParent();
571: }
572: //
573: return true;
574: }
575:
576: public String toString() {
577: return locationToString();
578: }
579:
580: private String locationToString() {
581: StringBuilder sb = new StringBuilder();
582: boolean isFirst = true;
583: for (Object stepObj : myLocationPath) {
584: if (isFirst) {
585: isFirst = false;
586: } else {
587: sb.append("/");
588: }
589: //
590: sb.append(stepObj.toString());
591: }
592: return sb.toString();
593: }
594:
595: public boolean equals(Object obj2) {
596: if (!(obj2 instanceof CachedPredicate)) {
597: return false;
598: }
599: //
600: CachedPredicate pred2 = (CachedPredicate) obj2;
601: LinkedList path2 = pred2.getLocationPath();
602: if (path2.size() != myLocationPath.size()) {
603: return false;
604: }
605: //
606: ListIterator itr = myLocationPath
607: .listIterator(myLocationPath.size());
608: ListIterator itr2 = path2.listIterator(path2.size());
609: //
610: while (itr.hasPrevious()) {
611: Object dataObj = itr.previous();
612: Object dataObj2 = itr2.previous();
613: if (!(dataObj.equals(dataObj2))) {
614: return false;
615: }
616: }
617: //
618: return true;
619: }
620:
621: public PredicatedSchemaNode findNode(XsltMapper myMapper) {
622: JTree sourceTree = myMapper.getMapperViewManager()
623: .getSourceView().getTree();
624: assert sourceTree != null;
625: //
626: TreeNode root = (TreeNode) sourceTree.getModel().getRoot();
627: if (root == null) {
628: return null;
629: }
630: //
631: TreeNode parentNode = null;
632: Iterator pathItr = myLocationPath.iterator();
633: if (pathItr.hasNext()) {
634: Object rootPathObject = pathItr.next();
635: if (root.getDataObject() == rootPathObject) {
636: parentNode = root;
637: }
638: }
639: //
640: if (parentNode == null) {
641: return null;
642: }
643: //
644: TreeNode soughtChildNode = null;
645: while (pathItr.hasNext()) {
646: Object nextPathObject = pathItr.next();
647: //
648: // Look for the child node
649: soughtChildNode = null;
650: for (TreeNode childNode : parentNode.getChildren()) {
651: if (childNode.getDataObject() == nextPathObject) {
652: soughtChildNode = childNode;
653: break;
654: }
655: }
656: //
657: // Check if the sought child node is found
658: if (soughtChildNode == null) {
659: return null;
660: } else {
661: parentNode = soughtChildNode;
662: }
663: }
664: //
665: assert soughtChildNode instanceof PredicatedSchemaNode;
666: return (PredicatedSchemaNode) soughtChildNode;
667: }
668:
669: }
670:
671: }
|