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: package org.netbeans.modules.bpel.mapper.model;
020:
021: import java.awt.datatransfer.DataFlavor;
022: import java.awt.datatransfer.Transferable;
023: import java.awt.datatransfer.UnsupportedFlavorException;
024: import java.io.IOException;
025: import java.util.ArrayList;
026: import java.util.Collection;
027: import java.util.HashMap;
028: import java.util.List;
029: import java.util.ListIterator;
030: import java.util.Map;
031: import java.util.Set;
032: import javax.swing.event.TreeModelListener;
033: import javax.swing.tree.TreePath;
034: import org.netbeans.modules.bpel.mapper.tree.MapperSwingTreeModel;
035: import org.netbeans.modules.bpel.mapper.tree.spi.MapperTreeModel;
036: import org.netbeans.modules.soa.mappercore.model.GraphSubset;
037: import org.netbeans.modules.soa.mappercore.model.MapperModel;
038: import org.netbeans.modules.soa.mappercore.model.SourcePin;
039: import org.netbeans.modules.soa.mappercore.model.TargetPin;
040: import org.netbeans.modules.soa.mappercore.model.TreeSourcePin;
041: import org.netbeans.modules.soa.mappercore.model.Vertex;
042: import org.netbeans.modules.soa.mappercore.model.Link;
043: import org.netbeans.modules.soa.mappercore.model.VertexItem;
044: import org.netbeans.modules.xml.xpath.ext.metadata.ArgumentDescriptor;
045: import org.netbeans.modules.xml.xpath.ext.metadata.ArgumentGroup;
046: import org.netbeans.modules.xml.xpath.ext.metadata.XPathType;
047: import org.netbeans.modules.bpel.mapper.palette.Palette;
048: import org.netbeans.modules.bpel.mapper.tree.spi.MapperTcContext;
049: import org.netbeans.modules.soa.mappercore.model.Graph;
050: import static org.netbeans.modules.soa.ui.util.UI.*;
051:
052: /**
053: * The default implementation of the MapperModel interface for the BPEL Mapper.
054: *
055: * @author nk160297
056: */
057: public class BpelMapperModel implements MapperModel,
058: MapperTcContext.Provider {
059:
060: public final Graph STUB_GRAPH;
061:
062: private MapperTcContext mMapperTcContext;
063: private GraphChangeProcessor mChangeProcessor;
064: private MapperSwingTreeModel mLeftTreeModel;
065: private MapperSwingTreeModel mRightTreeModel;
066:
067: // Maps a TreePath to a Graph
068: private Map<TreePath, Graph> mPathGraphMap = new HashMap<TreePath, Graph>();
069:
070: public BpelMapperModel(MapperTcContext mapperTcContext,
071: GraphChangeProcessor changeProcessor,
072: MapperTreeModel leftModel, MapperTreeModel rightModel) {
073: //
074: mMapperTcContext = mapperTcContext;
075: mChangeProcessor = changeProcessor;
076: //
077: mLeftTreeModel = new MapperSwingTreeModel(mMapperTcContext,
078: leftModel);
079: //
080: mRightTreeModel = new MapperSwingTreeModel(mMapperTcContext,
081: rightModel);
082: //
083: STUB_GRAPH = new Graph(this );
084: }
085:
086: public MapperTcContext getMapperTcContext() {
087: return mMapperTcContext;
088: }
089:
090: public MapperSwingTreeModel getRightTreeModel() {
091: return mRightTreeModel;
092: }
093:
094: public MapperSwingTreeModel getLeftTreeModel() {
095: return mLeftTreeModel;
096: }
097:
098: //==========================================================================
099: // Gentral graph methods
100: //==========================================================================
101:
102: public TreeSourcePin getTreeSourcePin(TreePath treePath) {
103: return new TreeSourcePin(treePath);
104: }
105:
106: public Graph getGraph(TreePath treePath) {
107: Graph result = mPathGraphMap.get(treePath);
108: return (result == null) ? STUB_GRAPH : result;
109: }
110:
111: public boolean searchGraphsInside(TreePath path) {
112: Object parent = path.getLastPathComponent();
113:
114: for (TreePath treePath : mPathGraphMap.keySet()) {
115: while (true) {
116: // The last path object is skipped here
117: treePath = treePath.getParentPath();
118: if (treePath == null) {
119: break;
120: }
121: Object pathItem = treePath.getLastPathComponent();
122: if (pathItem == parent) {
123: return true;
124: }
125: }
126: }
127: //
128: return false;
129: }
130:
131: public void addGraph(Graph newGraph, TreePath treePath) {
132: mPathGraphMap.put(treePath, newGraph);
133: // fireGraphChanged(treePath);
134: mRightTreeModel.fireTreeChanged(this , treePath);
135: }
136:
137: /**
138: * Delete graph from mapper model.
139: * The method doesn't do any checks and doesn't send any notifications.
140: * It simply delete the graph from the cache.
141: *
142: * This method is intended to be called as part of update processing
143: * is initiated by a change in mapper.
144: * If you need to remove a graph, you better use emptyGraph method
145: * and it will be deleted automatically.
146: *
147: * @param treePath
148: */
149: public void deleteGraph(TreePath treePath) {
150: Graph graph = mPathGraphMap.get(treePath);
151: if (graph != null) {
152: mPathGraphMap.remove(treePath);
153: }
154: }
155:
156: /**
157: * Remove all graphs are located in a tree branch.
158: * @param rootTreePath the root of the tree branch.
159: */
160: public void removeNestedGraphs(TreePath rootTreePath) {
161: Map<TreePath, Graph> nestedGraphs = getGraphsInside(rootTreePath);
162: Set<TreePath> graphPathesToDelete = nestedGraphs.keySet();
163: //
164: boolean modified = false;
165: for (TreePath graphTPath : graphPathesToDelete) {
166: Graph graph = mPathGraphMap.get(graphTPath);
167: if (graph != null) {
168: //
169: modified = emptyGraph(graph);
170: //
171: // It's not necessary to remove the graph itself.
172: // It has to be removed by the BPEL updater a bit later.
173: // mPathGraphMap.remove(graphTPath);
174: }
175: }
176: //
177: if (modified) {
178: // Modify BPEL model for all changed graphs in one transaction.
179: fireGraphsChanged(graphPathesToDelete);
180: }
181: }
182:
183: private boolean emptyGraph(Graph graph) {
184: boolean modified = false;
185: List<Vertex> vertexList = graph.getVerteces();
186: for (Vertex vertex : vertexList) {
187: graph.removeVertex(vertex);
188: modified = true;
189: }
190: //
191: List<Link> linksList = graph.getLinks();
192: for (Link link : linksList) {
193: graph.removeLink(link);
194: modified = true;
195: }
196: //
197: return modified;
198: }
199:
200: /**
201: * Takes an existing graph or creates a new one and registers it in this mapper.
202: * @param treePath - the graph's location.
203: * @return required graph.
204: */
205: public Graph graphRequired(TreePath treePath) {
206: Graph graph = getGraph(treePath);
207: if (graph == null || graph == STUB_GRAPH) {
208: graph = new Graph(this );
209: mPathGraphMap.put(treePath, graph);
210: }
211: return graph;
212: }
213:
214: public Map<TreePath, Graph> getGraphsInside(TreePath root) {
215: if (root == null
216: || root.getLastPathComponent() == getRightTreeModel()
217: .getRoot()) {
218: return mPathGraphMap;
219: }
220: //
221: HashMap<TreePath, Graph> result = new HashMap<TreePath, Graph>();
222: for (TreePath tPath : mPathGraphMap.keySet()) {
223: if (root.isDescendant(tPath)) {
224: Graph graph = mPathGraphMap.get(tPath);
225: assert graph != null;
226: result.put(tPath, graph);
227: }
228: }
229: //
230: Graph rootGraph = mPathGraphMap.get(root);
231: if (rootGraph != null) {
232: result.put(root, rootGraph);
233: }
234: //
235: return result;
236: }
237:
238: //==========================================================================
239: // Modification methods
240: //==========================================================================
241:
242: public boolean canConnect(TreePath treePath, SourcePin source,
243: TargetPin target, TreePath oldTreePath, Link oldLink) {
244: if (oldTreePath != null && !oldTreePath.equals(treePath)) {
245: // Reconnect
246: // link to another graph is not allowed for a while
247: return false;
248: }
249: Boolean result = true;
250: SourcePin oldSource = null;
251: TargetPin oldTarget = null;
252: if (oldLink != null) {
253: oldSource = oldLink.getSource();
254: oldTarget = oldLink.getTarget();
255: oldLink.setSource(null);
256: oldLink.setTarget(null);
257: }
258: if (target instanceof Graph) {
259: if (!mRightTreeModel.isConnectable(treePath)) {
260: result = false;
261: }
262: //
263: if (((Graph) target).hasOutgoingLinks()) {
264: // The target tree node already has a connected link
265: result = false;
266: }
267: }
268: //
269: if (source instanceof TreeSourcePin) {
270: TreePath sourceTreePath = ((TreeSourcePin) source)
271: .getTreePath();
272: if (!mLeftTreeModel.isConnectable(sourceTreePath)) {
273: result = false;
274: }
275: }
276: //
277: // Check there is only one outgoing link
278: if (source instanceof Vertex) {
279: Link outgoingLink = ((Vertex) source).getOutgoingLink();
280: if (outgoingLink != null) {
281: result = false;
282: }
283: }
284: //
285: if (target instanceof VertexItem) {
286: //
287: // Check if the target vertex item has a value
288: // Object value = ((VertexItem) target).getValue();
289: // if (value != null) {
290: // return false;
291: // }
292: //
293: // Check the item doesn't have incoming link yet
294: Link ingoingLink = ((VertexItem) target).getIngoingLink();
295: if (ingoingLink != null) {
296: result = false;
297: }
298: //
299: // Check connection 2 vertexes
300: if (source instanceof Vertex) {
301: //
302: // Trying connect the vertex to itself isn't allowed
303: Vertex targetVertex = ((VertexItem) target).getVertex();
304: if (targetVertex == source) {
305: result = false;
306: }
307: // Check cyclic dependences
308: if (BpelMapperUtils.areVertexDependent((Vertex) source,
309: targetVertex)) {
310: result = false;
311: }
312: }
313: }
314: //
315: if (oldLink != null) {
316: oldLink.setSource(oldSource);
317: oldLink.setTarget(oldTarget);
318: }
319: return result;
320: }
321:
322: public boolean canCopy(TreePath treePath, GraphSubset graphSubset) {
323: return true;
324: }
325:
326: public boolean canMove(TreePath treePath, GraphSubset graphSubset) {
327: return true;
328: }
329:
330: public void connect(TreePath treePath, SourcePin source,
331: TargetPin target, TreePath oldTreePath, Link oldLink) {
332: // if (oldLink != null) return;
333:
334: Graph graph = getGraph(treePath);
335: //
336: Graph resultGraph;
337: if (graph == STUB_GRAPH) {
338: //
339: // Add the new Graph
340: resultGraph = new Graph(this );
341: mPathGraphMap.put(treePath, resultGraph);
342: target = resultGraph;
343: } else {
344: resultGraph = graph;
345: }
346: //
347: // Process the case when link is drawn to a hairline vertex item
348: if (target instanceof VertexItem) {
349: VertexItem vItem = (VertexItem) target;
350: if (vItem.isHairline()) {
351: Vertex vertex = vItem.getVertex();
352: Object dataObject = vItem.getDataObject();
353: int index = vertex.getItemIndex(vItem);
354: if (dataObject instanceof ArgumentDescriptor) {
355: //
356: // A new real vertex item has to be inserted after the hairline item
357: VertexItem newRealVItem = VertexFactory
358: .constructVItem(vertex,
359: (ArgumentDescriptor) dataObject);
360: vertex.addItem(newRealVItem, index + 1);
361: //
362: // A new hairline item has to be inserted after the real vertex item
363: VertexItem newHirelineVItem = VertexFactory
364: .constructHairline(vertex, dataObject);
365: vertex.addItem(newHirelineVItem, index + 2);
366: //
367: // Eventually 2 new vertex item is added: real and additional hairline
368: target = newRealVItem;
369: } else if (dataObject instanceof ArgumentGroup) {
370: List<VertexItem> itemsList = VertexFactory
371: .getInstance().createGroupItems(vertex,
372: (ArgumentGroup) dataObject);
373: //
374: // Insert new vertex items in the back direction
375: //
376: // A new hairline item will appear just after the group's items.
377: VertexItem newHirelineVItem = VertexFactory
378: .constructHairline(vertex, dataObject);
379: vertex.addItem(newHirelineVItem, index + 1);
380: //
381: // Insert a sequence of vertex items to the position next to
382: // the initial hairline. The items are inserted in the
383: // back direction but in the same place, so previous item move
384: // down when the next is inserted.
385: ListIterator<VertexItem> backItr = itemsList
386: .listIterator(itemsList.size());
387: while (backItr.hasPrevious()) {
388: VertexItem vertItem = backItr.previous();
389: vertex.addItem(vertItem, index + 1);
390: }
391: //
392: // Looking for the item to which the link has to be connected
393: VertexItem newTargetVItem = null;
394: XPathType sourceType = BpelMapperUtils
395: .calculateXPathSourcePinType(source);
396: if (sourceType != null) {
397: newTargetVItem = BpelMapperUtils
398: .findBestFittedItem(itemsList,
399: sourceType);
400: }
401: //
402: if (newTargetVItem != null) {
403: target = newTargetVItem;
404: }
405: }
406: }
407: vItem.setValue(null);
408: }
409: //
410: if (oldLink == null) {
411: Link newLink = new Link(source, target);
412: resultGraph.addLink(newLink);
413: } else {
414: oldLink.setSource(source);
415: oldLink.setTarget(target);
416: }
417: //
418: fireGraphChanged(treePath);
419: mRightTreeModel.fireTreeChanged(this , treePath);
420: }
421:
422: // vlv
423: public GraphSubset getGraphSubset(Transferable transferable) {
424: //out();
425: for (DataFlavor flavor : transferable.getTransferDataFlavors()) {
426: try {
427: //out("see: " + transferable.getTransferData(flavor));
428: Object[] objects = (Object[]) transferable
429: .getTransferData(flavor);
430: myHandler = (ItemHandler) objects[0];
431: GraphSubset graph = myHandler.createGraphSubset();
432: Palette palette = (Palette) objects[1];
433: //out("graph: " + graph);
434:
435: if (graph != null) {
436: palette.hideMenu();
437: return graph;
438: }
439: } catch (IOException e) {
440: continue;
441: } catch (UnsupportedFlavorException e) {
442: continue;
443: }
444: }
445: return null;
446: }
447:
448: private ItemHandler myHandler;
449:
450: private boolean isConnectable(TreePath treePath) {
451: if (treePath == null) {
452: return false;
453: }
454: return mRightTreeModel.isConnectable(treePath);
455: }
456:
457: public GraphSubset add(TreePath treePath, ItemHandler handler,
458: int x, int y) {
459: myHandler = handler;
460: return doCopy(treePath, null, x, y);
461: }
462:
463: public GraphSubset copy(TreePath treePath, GraphSubset graphSubset,
464: int x, int y) {
465: return doCopy(treePath, graphSubset, x, y);
466: }
467:
468: private GraphSubset doCopy(TreePath treePath,
469: GraphSubset graphSubset, int x, int y) {
470: if (!isConnectable(treePath)) {
471: return null;
472: }
473: if (myHandler != null) {
474: if (myHandler.canAddGraphSubset()) {
475: graphSubset = myHandler.createGraphSubset();
476: myHandler = null;
477: } else {
478: graphSubset = null;
479: }
480: } else {
481: graphSubset = new GraphSubset(graphSubset);
482: }
483:
484: if (graphSubset == null) {
485: return null;
486: }
487:
488: Graph graph = graphRequired(treePath);
489: // int nextX = x;
490: int x0 = 0;
491: int y0 = 0;
492: if (graphSubset.getVertexCount() > 0) {
493: x0 = graphSubset.getVertex(0).getX();
494: y0 = graphSubset.getVertex(0).getY();
495: }
496: for (int i = 0; i < graphSubset.getVertexCount(); i++) {
497: Vertex vertex = graphSubset.getVertex(i);
498:
499: int xi = graphSubset.getVertex(i).getX();
500: int yi = graphSubset.getVertex(i).getY();
501: vertex.setLocation(xi - x0 + x, yi - y0 + y);
502: // vertex.setLocation(nextX, y);
503: // nextX = vertex.getX() + vertex.getWidth() + 3;
504: }
505: for (int i = 0; i < graphSubset.getVertexCount(); i++) {
506: Vertex vertex = graphSubset.getVertex(i);
507: graph.addVertex(vertex);
508:
509: }
510:
511: for (int i = 0; i < graphSubset.getLinkCount(); i++) {
512: Link link = graphSubset.getLink(i);
513: graph.addLink(link);
514: }
515: fireGraphChanged(treePath);
516: mRightTreeModel.fireTreeChanged(this , treePath);
517: //
518: return graphSubset;
519: }
520:
521: public void move(TreePath treePath, GraphSubset graphSubset,
522: int newX, int newY) {
523: if (!isConnectable(treePath)) {
524: return;
525: }
526:
527: if (graphSubset == null) {
528: return;
529: }
530: Graph graph = graphRequired(treePath);
531: Graph oldGraph = null;
532: List<Link> links = new ArrayList<Link>();
533: int x0 = 0;
534: int y0 = 0;
535: if (graphSubset.getVertexCount() > 0) {
536: x0 = graphSubset.getVertex(0).getX();
537: y0 = graphSubset.getVertex(0).getY();
538: }
539: for (int i = graphSubset.getVertexCount() - 1; i >= 0; i--) {
540: Vertex vertex = graphSubset.getVertex(i);
541:
542: if (vertex.getGraph() != null) {
543: oldGraph = vertex.getGraph();
544: if (oldGraph != graph) {
545: // oldGraph.removeVertex(vertex);
546: graph.addVertex(vertex);
547: Link link = vertex.getOutgoingLink();
548: if (link != null && oldGraph == link.getGraph()) {
549: oldGraph.removeLink(link);
550: links.add(link);
551: }
552: for (int j = vertex.getItemCount() - 1; j >= 0; j--) {
553: link = vertex.getItem(j).getIngoingLink();
554: if (link != null && oldGraph == link.getGraph()) {
555: oldGraph.removeLink(link);
556: links.add(link);
557: }
558: }
559: }
560:
561: int xi = graphSubset.getVertex(i).getX();
562: int yi = graphSubset.getVertex(i).getY();
563: vertex.setLocation(xi - x0 + newX, yi - y0 + newY);
564: }
565: }
566:
567: if (oldGraph != graph) {
568: for (int i = graphSubset.getLinkCount() - 1; i >= 0; i--) {
569: Link link = graphSubset.getLink(i);
570: TargetPin target = link.getTarget();
571: SourcePin source = link.getSource();
572: if (!(target instanceof Graph)
573: && graphSubset
574: .containVertex(((VertexItem) target)
575: .getVertex())
576: && (source instanceof TreeSourcePin || graphSubset
577: .containVertex((Vertex) source))) {
578: graph.addLink(link);
579: } else {
580: link.setSource(null);
581: link.setTarget(null);
582:
583: }
584: links.remove(link);
585: }
586: for (Link l : links) {
587: l.setSource(null);
588: l.setTarget(null);
589: }
590: }
591: //
592: fireGraphChanged(treePath);
593: mRightTreeModel.fireTreeChanged(this , treePath);
594: }
595:
596: public void fireGraphChanged(TreePath treePath) {
597: if (mChangeProcessor != null) {
598: mChangeProcessor.processChanges(treePath);
599: }
600: }
601:
602: public void fireGraphsChanged(Collection<TreePath> treePathList) {
603: if (mChangeProcessor != null) {
604: mChangeProcessor.processChanges(treePathList);
605: }
606: }
607:
608: //==========================================================================
609: // Right tree methods
610: //==========================================================================
611:
612: public Object getRoot() {
613: return mRightTreeModel.getRoot();
614: }
615:
616: public Object getChild(Object parent, int index) {
617: return mRightTreeModel.getChild(parent, index);
618: }
619:
620: public int getChildCount(Object parent) {
621: return mRightTreeModel.getChildCount(parent);
622: }
623:
624: public boolean isLeaf(Object node) {
625: return mRightTreeModel.isLeaf(node);
626: }
627:
628: public void valueForPathChanged(TreePath path, Object newValue) {
629: mRightTreeModel.valueForPathChanged(path, newValue);
630: }
631:
632: public int getIndexOfChild(Object parent, Object child) {
633: return mRightTreeModel.getIndexOfChild(parent, child);
634: }
635:
636: public void addTreeModelListener(TreeModelListener l) {
637: mRightTreeModel.addTreeModelListener(l);
638: }
639:
640: public void removeTreeModelListener(TreeModelListener l) {
641: mRightTreeModel.removeTreeModelListener(l);
642: }
643:
644: public void valueChanged(TreePath treePath, VertexItem vertexItem,
645: Object newValue) {
646: if (newValue != null) {
647: vertexItem.setValue(newValue);
648: Link link = vertexItem.getIngoingLink();
649: if (link != null) {
650: link.disconnect();
651: }
652: fireGraphChanged(treePath);
653: mRightTreeModel.fireTreeChanged(this , treePath);
654: }
655: }
656:
657: //===================================================================
658: // Predicates support methods
659: //===================================================================
660:
661: /**
662: * Looks for a list of graphs which depends on the specified data object.
663: * The data object relates to the left tree item.
664: * @param leftTreeItemDO
665: * @return
666: */
667: public List<TreePath> getDependentGraphs(Object leftTreeItemDO) {
668: ArrayList<TreePath> result = new ArrayList<TreePath>();
669: //
670: Map<TreePath, Graph> graphs = getGraphsInside(null);
671: for (TreePath path : graphs.keySet()) {
672: Graph graph = graphs.get(path);
673: List<Link> connectedLinksList = graph
674: .getConnectedIngoingLinks(new ArrayList<Link>());
675: for (Link link : connectedLinksList) {
676: SourcePin sourcePin = link.getSource();
677: assert sourcePin instanceof TreeSourcePin;
678: TreePath leftTreePath = ((TreeSourcePin) sourcePin)
679: .getTreePath();
680: if (MapperSwingTreeModel.containsDataObject(
681: leftTreePath, leftTreeItemDO)) {
682: result.add(path);
683: break;
684: }
685: }
686: }
687: //
688: return result;
689: }
690:
691: /**
692: * Remove links which go from the leftNodePath (left tree), which is
693: * going to be deleted. It is necessary because the links has to be
694: * started somewhere. If a link is connected to a node in the left tree,
695: * then it has to be deleted if the node is deleted.
696: * @param graphPath - specified the tree path (right tree) of the graph,
697: * from which the link is going to be deleted.
698: * @param leftNodePath - specified the tree path (left tree) of the node,
699: * which is going to be deleted.
700: */
701: public void removeIngoingLinks(TreePath graphPath,
702: TreePath leftNodePath) {
703: //
704: Graph graph = getGraph(graphPath);
705: List<Link> ingoingLinks = graph.getIngoingLinks();
706: for (Link link : ingoingLinks) {
707: SourcePin sourcePin = link.getSource();
708: assert sourcePin instanceof TreeSourcePin;
709: TreePath tPath = ((TreeSourcePin) sourcePin).getTreePath();
710: //
711: // The link can be connected not directly to the node, which
712: // is going to be deleted, but to its descendant node.
713: // If a node is deleted, then all branch of its children
714: // is also deleted.
715: if (tPath != null && leftNodePath.isDescendant(tPath)) {
716: link.disconnect();
717: }
718: }
719: }
720:
721: public boolean canEditInplace(VertexItem vItem) {
722: //return vItem.getIngoingLink() == null;
723: return true;
724: }
725: }
|