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.bpel.mapper.tree;
021:
022: import java.util.ArrayList;
023: import java.util.Collection;
024: import java.util.Collections;
025: import java.util.List;
026: import java.util.Stack;
027: import javax.swing.Action;
028: import javax.swing.Icon;
029: import javax.swing.JMenuItem;
030: import javax.swing.JPopupMenu;
031: import javax.swing.event.EventListenerList;
032: import javax.swing.event.TreeModelEvent;
033: import javax.swing.event.TreeModelListener;
034: import javax.swing.tree.DefaultTreeModel;
035: import javax.swing.tree.TreeModel;
036: import javax.swing.tree.TreePath;
037: import org.netbeans.modules.bpel.mapper.multiview.DesignContextController;
038: import org.netbeans.modules.bpel.mapper.predicates.editor.PathConverter;
039: import org.netbeans.modules.bpel.mapper.tree.spi.MapperTcContext;
040: import org.netbeans.modules.bpel.mapper.tree.spi.MapperTreeModel;
041: import org.netbeans.modules.bpel.mapper.tree.spi.RestartableIterator;
042: import org.netbeans.modules.bpel.mapper.tree.spi.TreeItemFinder;
043: import org.netbeans.modules.bpel.mapper.tree.spi.TreeItemFinder.FindResult;
044: import org.netbeans.modules.bpel.mapper.tree.spi.TreeItemInfoProvider;
045:
046: /**
047: * An internal tree model based on the Swing Tree Model.
048: * It performs caching of data is obtained from another tree models,
049: * which are described by the MapperTreeModel and TreeItemInfoProvider
050: * interfaces.
051: *
052: * @author nk160297
053: */
054: public class MapperSwingTreeModel implements TreeModel,
055: MapperTcContext.Provider {
056:
057: protected EventListenerList listenerList = new EventListenerList();
058: private MapperTreeModel mSourceModel;
059: private MapperTcContext mMapperTcContext;
060: private MapperTreeNode mRootNode;
061:
062: public MapperSwingTreeModel(MapperTcContext mapperTcContext,
063: MapperTreeModel sourceModel) {
064: //
065: mMapperTcContext = mapperTcContext;
066: mSourceModel = sourceModel;
067: }
068:
069: public MapperTcContext getMapperTcContext() {
070: return mMapperTcContext;
071: }
072:
073: public Object getRoot() {
074: if (mRootNode == null) {
075: Object dataObject = mSourceModel.getRoot();
076: mRootNode = new MapperTreeNode(null, dataObject);
077: }
078: return mRootNode;
079: }
080:
081: public Object getChild(Object parent, int index) {
082: assert parent instanceof MapperTreeNode;
083: MapperTreeNode mNode = (MapperTreeNode) parent;
084: List<MapperTreeNode> childrenNodes = getChildren(mNode);
085: //
086: return childrenNodes.get(index);
087: }
088:
089: public int getChildCount(Object parent) {
090: assert parent instanceof MapperTreeNode;
091: MapperTreeNode mNode = (MapperTreeNode) parent;
092: List<MapperTreeNode> childrenNodes = getChildren(mNode);
093: //
094: return childrenNodes.size();
095: }
096:
097: public boolean isLeaf(Object node) {
098: assert node instanceof MapperTreeNode;
099: MapperTreeNode mNode = (MapperTreeNode) node;
100: Object dataObject = mNode.getDataObject();
101: //
102: Boolean isLeafObj = mNode.isLeaf();
103: if (isLeafObj == null) {
104: isLeafObj = mSourceModel.isLeaf(dataObject);
105: }
106: //
107: if (isLeafObj == null) {
108: List<MapperTreeNode> childrenNodes = getChildren(mNode);
109: isLeafObj = childrenNodes.isEmpty();
110: }
111: //
112: mNode.setIsLeaf(isLeafObj);
113: return isLeafObj;
114: }
115:
116: public void valueForPathChanged(TreePath path, Object newValue) {
117: throw new UnsupportedOperationException("Not supported yet.");
118: }
119:
120: public int getIndexOfChild(Object parent, Object child) {
121: assert parent instanceof MapperTreeNode;
122: MapperTreeNode mNode = (MapperTreeNode) parent;
123: List<MapperTreeNode> childrenNodes = getChildren(mNode);
124: //
125: return childrenNodes.indexOf(child);
126: }
127:
128: public void addTreeModelListener(TreeModelListener l) {
129: listenerList.add(TreeModelListener.class, l);
130: }
131:
132: public void removeTreeModelListener(TreeModelListener l) {
133: listenerList.remove(TreeModelListener.class, l);
134: }
135:
136: public void insertChild(TreePath parentPath, int index,
137: Object itemDataObject) {
138: Object parentNode = parentPath.getLastPathComponent();
139: assert parentNode instanceof MapperTreeNode;
140: List<MapperTreeNode> childrenList = getChildren((MapperTreeNode) parentNode);
141: MapperTreeNode newChildNode = new MapperTreeNode(
142: (MapperTreeNode) parentNode, itemDataObject);
143: childrenList.add(index, newChildNode);
144: //
145: fireTreeNodesInserted(this , parentPath, index, newChildNode);
146: }
147:
148: public void remove(TreePath treePath) {
149: TreePath parentTreePath = treePath.getParentPath();
150: Object parentComp = parentTreePath.getLastPathComponent();
151: assert parentComp instanceof MapperTreeNode;
152: //
153: Object lastComp = treePath.getLastPathComponent();
154: int childIndex = getIndexOfChild(parentComp, lastComp);
155: //
156: ((MapperTreeNode) parentComp).removeChild(lastComp);
157: //
158: fireTreeNodesRemoved(this , parentTreePath, childIndex, lastComp);
159: }
160:
161: public MapperTreeModel getSourceModel() {
162: return mSourceModel;
163: }
164:
165: public String getDisplayName(Object node) {
166: assert node instanceof MapperTreeNode;
167: MapperTreeNode mNode = (MapperTreeNode) node;
168: String displayName = mNode.getDisplayName();
169: if (displayName == null) {
170: if (mSourceModel != null) {
171: TreeItemInfoProvider infoProvider = mSourceModel
172: .getTreeItemInfoProvider();
173: if (infoProvider != null) {
174: Object dataObject = mNode.getDataObject();
175: displayName = infoProvider
176: .getDisplayName(dataObject);
177: mNode.setDisplayName(displayName);
178: }
179: }
180: }
181: return displayName;
182: }
183:
184: public String getToolTipText(Object node) {
185: assert node instanceof MapperTreeNode;
186: MapperTreeNode mNode = (MapperTreeNode) node;
187: String toolTipText = null;
188: if (mSourceModel != null) {
189: TreeItemInfoProvider infoProvider = mSourceModel
190: .getTreeItemInfoProvider();
191: if (infoProvider != null) {
192: Object dataObject = mNode.getDataObject();
193: toolTipText = infoProvider.getToolTipText(dataObject);
194: }
195: }
196: return toolTipText;
197: }
198:
199: public Icon getIcon(Object node) {
200: assert node instanceof MapperTreeNode;
201: MapperTreeNode mNode = (MapperTreeNode) node;
202: Icon icon = mNode.getIcon();
203: if (icon == null) {
204: if (mSourceModel != null) {
205: TreeItemInfoProvider infoProvider = mSourceModel
206: .getTreeItemInfoProvider();
207: if (infoProvider != null) {
208: Object dataObject = mNode.getDataObject();
209: icon = infoProvider.getIcon(dataObject);
210: mNode.setIcon(icon);
211: }
212: }
213: }
214: return icon;
215: }
216:
217: public boolean isConnectable(TreePath treePath) {
218: Object node = treePath.getLastPathComponent();
219: assert node instanceof MapperTreeNode;
220: MapperTreeNode mNode = (MapperTreeNode) node;
221: Object dataObject = mNode.getDataObject();
222: //
223: return mSourceModel.isConnectable(dataObject) == Boolean.TRUE;
224: }
225:
226: public JPopupMenu getPopupMenu(Object node) {
227: assert node instanceof MapperTreeNode;
228: MapperTreeNode mNode = (MapperTreeNode) node;
229: //
230: TreePath treePath = mNode.getTreePath();
231: RestartableIterator<Object> dataObjectPathItr = getDataObjectsPathIterator(mNode);
232: //
233: TreeItemInfoProvider infoProvider = mSourceModel
234: .getTreeItemInfoProvider();
235: if (infoProvider != null) {
236: //
237: // Determine if the model is the left one
238: TreeModel leftTreeModel = mMapperTcContext.getMapper()
239: .getModel().getLeftTreeModel();
240: boolean isLeft = (leftTreeModel == this );
241: //
242: List<Action> menuActionList = infoProvider.getMenuActions(
243: mMapperTcContext, isLeft, treePath,
244: dataObjectPathItr);
245: if (menuActionList != null && !menuActionList.isEmpty()) {
246: JPopupMenu newMenu = new JPopupMenu();
247: for (Action menuAction : menuActionList) {
248: JMenuItem newItem = new JMenuItem(menuAction);
249: newMenu.add(newItem);
250: }
251: //
252: return newMenu;
253: }
254: }
255: //
256: return null;
257: }
258:
259: private List<MapperTreeNode> getChildren(MapperTreeNode parent) {
260: List<MapperTreeNode> childrenList = parent.getChildren();
261: if (childrenList == null) {
262: //
263: // Construct children nodes here
264: childrenList = new ArrayList<MapperTreeNode>();
265: RestartableIterator<Object> dataObjectPathItr = getDataObjectsPathIterator(parent);
266: List<Object> childrenDataObjectList = mSourceModel
267: .getChildren(dataObjectPathItr);
268:
269: if (childrenDataObjectList != null) {
270: DesignContextController dcc = mMapperTcContext
271: .getDesignContextController();
272:
273: for (Object childDataObject : childrenDataObjectList) {
274: if (dcc != null) {
275: dcc.processDataObject(childDataObject);
276: }
277:
278: MapperTreeNode newNode = new MapperTreeNode(parent,
279: childDataObject);
280: childrenList.add(newNode);
281:
282: }
283: }
284: }
285: //
286: parent.setChildren(childrenList);
287: return childrenList;
288: }
289:
290: public void fireTreeChanged(Object source, TreePath tPath) {
291: // Guaranteed to return a non-null array
292: Object[] listeners = listenerList.getListenerList();
293: TreeModelEvent e = null;
294: // Process the listeners last to first, notifying
295: // those that are interested in this event
296: for (int i = listeners.length - 2; i >= 0; i -= 2) {
297: if (listeners[i] == TreeModelListener.class) {
298: // Lazily create the event:
299: if (e == null) {
300: TreePath parentTreePath = tPath.getParentPath();
301: Object lastComp = tPath.getLastPathComponent();
302: assert lastComp instanceof MapperTreeNode;
303: //
304: // Reload cached data object
305: ((MapperTreeNode) lastComp).discardCachedData();
306: //
307: int childIndex = getIndexOfChild(parentTreePath
308: .getLastPathComponent(), lastComp);
309: //
310: e = new TreeModelEvent(source, parentTreePath,
311: new int[] { childIndex },
312: new Object[] { lastComp });
313: }
314: ((TreeModelListener) listeners[i + 1])
315: .treeNodesChanged(e);
316: }
317: }
318: }
319:
320: protected void fireTreeNodesRemoved(Object source,
321: TreePath parentTreePath, int childIndex, Object removedObj) {
322: // Guaranteed to return a non-null array
323: Object[] listeners = listenerList.getListenerList();
324: TreeModelEvent e = null;
325: // Process the listeners last to first, notifying
326: // those that are interested in this event
327: for (int i = listeners.length - 2; i >= 0; i -= 2) {
328: if (listeners[i] == TreeModelListener.class) {
329: // Lazily create the event:
330: if (e == null) {
331: //
332: e = new TreeModelEvent(source, parentTreePath,
333: new int[] { childIndex },
334: new Object[] { removedObj });
335: }
336: ((TreeModelListener) listeners[i + 1])
337: .treeNodesRemoved(e);
338: }
339: }
340: }
341:
342: protected void fireTreeNodesInserted(Object source,
343: TreePath parentTreePath, int childIndex, Object inserted) {
344: // Guaranteed to return a non-null array
345: Object[] listeners = listenerList.getListenerList();
346: TreeModelEvent e = null;
347: // Process the listeners last to first, notifying
348: // those that are interested in this event
349: for (int i = listeners.length - 2; i >= 0; i -= 2) {
350: if (listeners[i] == TreeModelListener.class) {
351: // Lazily create the event:
352: if (e == null) {
353: e = new TreeModelEvent(source, parentTreePath,
354: new int[] { childIndex }, // put next to the base node
355: new Object[] { inserted });
356: }
357: ((TreeModelListener) listeners[i + 1])
358: .treeNodesInserted(e);
359: }
360: }
361: }
362:
363: /**
364: * Returns an iterator, which provides a tree path from the specifed node to
365: * the tree root. The path consists not from the MapperTreeNode objects, but
366: * from containing data objects. The first element is get from the result
367: * iterator references the data object of the specified node.
368: * @param node
369: * @return
370: */
371: private RestartableIterator<Object> getDataObjectsPathIterator(
372: final MapperTreeNode node) {
373: return new RestartableIterator() {
374:
375: private MapperTreeNode mNextNode = node;
376:
377: public boolean hasNext() {
378: return mNextNode != null;
379: }
380:
381: public Object next() {
382: assert mNextNode != null;
383: Object result = mNextNode.getDataObject();
384: mNextNode = mNextNode.getParent();
385: return result;
386: }
387:
388: public void remove() {
389: throw new UnsupportedOperationException();
390: }
391:
392: public void restart() {
393: mNextNode = node;
394: }
395:
396: @Override
397: public String toString() {
398: return PathConverter.toString(this );
399: }
400: };
401: }
402:
403: public List<TreePath> sortByLocation(Collection<TreePath> unsorted) {
404: //
405: ArrayList<TreePath> sorted = new ArrayList<TreePath>(unsorted
406: .size());
407: sorted.addAll(unsorted);
408:
409: Collections.sort(sorted, new TreePathComparator(this ));
410: //
411: return sorted;
412: }
413:
414: //-------------------------------------------------------------------------
415: // Search Engine functions
416: //-------------------------------------------------------------------------
417:
418: /**
419: * Converts content of the TreePath to the list of data objects.
420: * @param treePath
421: * @return
422: */
423: public static List<Object> convertTreePath(TreePath treePath) {
424: ArrayList<Object> result = new ArrayList<Object>();
425: for (Object item : treePath.getPath()) {
426: if (item instanceof MapperTreeNode) {
427: MapperTreeNode treeNode = (MapperTreeNode) item;
428: Object dataObject = treeNode.getDataObject();
429: result.add(dataObject);
430: }
431: }
432: //
433: return result;
434: }
435:
436: public static Object getDataObject(TreePath treePath) {
437: Object lastComp = treePath.getLastPathComponent();
438: if (lastComp instanceof MapperTreeNode) {
439: Object dataObject = ((MapperTreeNode) lastComp)
440: .getDataObject();
441: return dataObject;
442: }
443: return null;
444: }
445:
446: /**
447: * Returns true if the specified tree path contains a tree item with the
448: * specified data object.
449: * @param treePath
450: * @param dataObj
451: * @return
452: */
453: public static boolean containsDataObject(TreePath treePath,
454: Object dataObj) {
455: while (treePath != null) {
456: Object lastComp = treePath.getLastPathComponent();
457: if (lastComp instanceof MapperTreeNode) {
458: Object pathDataObj = ((MapperTreeNode) lastComp)
459: .getDataObject();
460: if (pathDataObj.equals(dataObj)) {
461: return true;
462: }
463: }
464: treePath = treePath.getParentPath();
465: }
466: //
467: return false;
468: }
469:
470: /**
471: * Looks for the first node, which satisfies the search conditions,
472: * which are specified by the finderList argument.
473: * The finderList contains the list of TreeItemFinder objects which
474: * has to be applied sequentially.
475: *
476: * @param helper
477: * @return TreePath of the found tree item.
478: */
479: public TreePath findFirstNode(List<TreeItemFinder> finderList) {
480: if (finderList == null || finderList.isEmpty()) {
481: return null;
482: }
483: //
484: MapperTreeNode rootNode = (MapperTreeNode) getRoot();
485: Stack<MapperTreeNode> locationStack = new Stack<MapperTreeNode>();
486: locationStack.push(rootNode);
487: //
488: for (TreeItemFinder finder : finderList) {
489: //
490: boolean found = findFirstChild(locationStack, finder, -1);
491: //
492: if (!found) {
493: return null;
494: }
495: }
496: //
497: TreePath result = new TreePath(locationStack.toArray());
498: return result;
499: }
500:
501: /**
502: * An auxiliary method is intended to help seach nodes recursively.
503: * The locationStack parameter specifies a chain of MapperTreeNode
504: * objects, which points to the tree node, from which the searching
505: * has to be started.
506: * <p>
507: * The finder parameter is an object which makes decision.
508: * It has to be implemented externally.
509: * <p>
510: * The maxDepth parameter specifies the maximum depth do
511: * which the recursive algorithm can go.
512: * <p>
513: * If it equals to -1, then infinite depth is emplied.
514: * <p>
515: * if it equals to 0 than it means that it only necessary to check
516: * if the top node in the stack satisfies to the searching conditions.
517: * <p>
518: * if it equals to 1 than it means that searching is requested
519: * only among direct children of the source node.
520: */
521: public boolean checkNode(Stack<MapperTreeNode> locationStack,
522: TreeItemFinder finder, int maxDepth) {
523: //
524: MapperTreeNode parentNode = locationStack.peek();
525: Object dataObject = parentNode.getDataObject();
526: //
527: FindResult fr = finder.process(dataObject, null);
528: //
529: if (fr.isFit()) {
530: return true;
531: }
532: //
533: if (maxDepth == 0) {
534: return false;
535: }
536: //
537: if (fr.drillDeeper()) {
538: return findFirstChild(locationStack, finder, maxDepth);
539: }
540: return false;
541: }
542:
543: public boolean findFirstChild(Stack<MapperTreeNode> locationStack,
544: TreeItemFinder finder, int maxDepth) {
545: //
546: MapperTreeNode parentNode = locationStack.peek();
547: List<MapperTreeNode> children = getChildren(parentNode);
548: if (children != null && children.size() != 0) {
549: maxDepth--;
550: for (MapperTreeNode child : children) {
551: locationStack.push(child);
552: //
553: if (checkNode(locationStack, finder, maxDepth)) {
554: return true;
555: }
556: //
557: locationStack.pop();
558: }
559: }
560: //
561: return false;
562: }
563:
564: /**
565: * An auxiliary method is intended to help seach nodes recursively.
566: * See description of the findFirstNode method.
567: * Unlike the findFirstNode it can find more then one node.
568: */
569: public void fillNodesList(
570: List<List<MapperTreeNode>> foundLocationsList,
571: Stack<MapperTreeNode> locationStack, TreeItemFinder finder,
572: int maxDepth, boolean lookDeeperIfFound) {
573: //
574: MapperTreeNode parentNode = locationStack.peek();
575: Object dataObject = parentNode.getDataObject();
576: //
577: FindResult fr = finder.process(dataObject, null);
578: //
579: if (fr.isFit()) {
580: // Copy location stack content to separate list and save it to result list.
581: ArrayList<MapperTreeNode> foundLocation = new ArrayList<MapperTreeNode>(
582: locationStack);
583: foundLocationsList.add(foundLocation);
584: if (!lookDeeperIfFound) {
585: return;
586: }
587: }
588: //
589: if (maxDepth == 0) {
590: return;
591: }
592: //
593: if (fr.drillDeeper()) {
594: List<MapperTreeNode> children = getChildren(parentNode);
595: maxDepth--;
596: for (MapperTreeNode child : children) {
597: locationStack.push(child);
598: //
599: fillNodesList(foundLocationsList, locationStack,
600: finder, maxDepth, lookDeeperIfFound);
601: //
602: locationStack.pop();
603: }
604: }
605: return;
606: }
607:
608: /**
609: * Looks for the first child of the specified parent according to the
610: * finder.
611: * @param parentPath
612: * @param finder
613: * @return the tree path of the found child or null.
614: */
615: public TreePath findChild(TreePath parentPath, TreeItemFinder finder) {
616: if (finder == null) {
617: return null;
618: }
619: //
620: Object parentObj = parentPath.getLastPathComponent();
621: assert parentObj instanceof MapperTreeNode;
622: //
623: List<MapperTreeNode> children = getChildren((MapperTreeNode) parentObj);
624: for (MapperTreeNode childNode : children) {
625: Object childDo = childNode.getDataObject();
626: assert childDo != null;
627: //
628: FindResult fr = finder.process(childDo, null);
629: //
630: if (fr.isFit()) {
631: return parentPath.pathByAddingChild(childNode);
632: }
633: }
634: //
635: return null;
636: }
637:
638: /**
639: * Looks for the set of children of the specified parent according to the
640: * finder.
641: * @param parentPath
642: * @param finder
643: * @return the tree path of the found child or null.
644: */
645: public List<TreePath> findChildren(TreePath parentPath,
646: TreeItemFinder finder) {
647: if (finder == null) {
648: return null;
649: }
650: //
651: Object parentObj = parentPath.getLastPathComponent();
652: assert parentObj instanceof MapperTreeNode;
653: //
654: ArrayList<TreePath> result = new ArrayList<TreePath>();
655: List<MapperTreeNode> children = getChildren((MapperTreeNode) parentObj);
656: for (MapperTreeNode childNode : children) {
657: Object childDo = childNode.getDataObject();
658: assert childDo != null;
659: //
660: FindResult fr = finder.process(childDo, null);
661: //
662: if (fr.isFit()) {
663: TreePath foundChildPath = parentPath
664: .pathByAddingChild(childNode);
665: result.add(foundChildPath);
666: }
667: }
668: //
669: return result;
670: }
671:
672: /**
673: * Looks for a child node by data object
674: * @param parentPath
675: * @param dataObject
676: * @return
677: */
678: public TreePath findChildByDataObj(TreePath parentPath,
679: Object dataObject) {
680: if (dataObject == null) {
681: return null;
682: }
683: //
684: Object parentObj = parentPath.getLastPathComponent();
685: assert parentObj instanceof MapperTreeNode;
686: //
687: List<MapperTreeNode> children = getChildren((MapperTreeNode) parentObj);
688: for (MapperTreeNode childNode : children) {
689: Object childDo = childNode.getDataObject();
690: assert childDo != null;
691: //
692: if (childDo.equals(dataObject)) {
693: return childNode.getTreePath();
694: }
695: }
696: //
697: return null;
698: }
699:
700: /**
701: * Looks for a child node by index
702: * @param parentPath
703: * @param dataObject
704: * @return
705: */
706: public TreePath findChildByIndex(TreePath parentPath, int index) {
707: if (index < 0) {
708: return null;
709: }
710: //
711: Object parentObj = parentPath.getLastPathComponent();
712: assert parentObj instanceof MapperTreeNode;
713: //
714: List<MapperTreeNode> children = getChildren((MapperTreeNode) parentObj);
715: if (index >= children.size()) {
716: return null;
717: }
718: //
719: MapperTreeNode childNode = children.get(index);
720: return childNode.getTreePath();
721: }
722:
723: }
|