001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.compapp.casaeditor.design;
043:
044: import java.awt.Point;
045: import java.awt.Rectangle;
046: import java.util.ArrayList;
047: import java.util.HashMap;
048: import java.util.List;
049: import java.util.Map;
050: import org.netbeans.api.visual.action.WidgetAction;
051: import org.netbeans.api.visual.action.WidgetAction.Chain;
052: import org.netbeans.api.visual.action.WidgetAction.WidgetDropTargetDragEvent;
053: import org.netbeans.api.visual.action.WidgetAction.WidgetDropTargetDropEvent;
054: import org.netbeans.api.visual.action.WidgetAction.WidgetDropTargetEvent;
055: import org.netbeans.api.visual.action.WidgetAction.WidgetKeyEvent;
056: import org.netbeans.api.visual.action.WidgetAction.WidgetMouseEvent;
057: import org.netbeans.api.visual.router.RouterFactory;
058: import org.netbeans.api.visual.widget.ConnectionWidget;
059: import org.netbeans.api.visual.widget.Widget;
060: import org.netbeans.modules.compapp.casaeditor.Constants;
061: import org.netbeans.modules.compapp.casaeditor.graph.WaitMessageHandler;
062: import org.netbeans.modules.compapp.casaeditor.graph.CasaNodeWidget;
063: import org.netbeans.modules.compapp.casaeditor.graph.CasaNodeWidgetEngine;
064: import org.netbeans.modules.compapp.casaeditor.graph.CasaPinWidget;
065: import org.netbeans.modules.compapp.casaeditor.graph.CasaRegionWidget;
066: import org.netbeans.modules.compapp.casaeditor.graph.CasaProcessTitleWidget;
067: import org.netbeans.modules.compapp.casaeditor.graph.RegionUtilities;
068: import org.netbeans.modules.compapp.casaeditor.graph.layout.CasaCollisionCollector;
069: import org.netbeans.modules.compapp.casaeditor.model.casa.CasaWrapperModel;
070: import org.netbeans.modules.compapp.casaeditor.model.casa.CasaComponent;
071: import org.netbeans.modules.compapp.casaeditor.model.casa.CasaConnection;
072: import org.netbeans.modules.compapp.casaeditor.model.casa.CasaConsumes;
073: import org.netbeans.modules.compapp.casaeditor.model.casa.CasaEndpoint;
074: import org.netbeans.modules.compapp.casaeditor.model.casa.CasaEndpointRef;
075: import org.netbeans.modules.compapp.casaeditor.model.casa.CasaServiceEngineServiceUnit;
076: import org.netbeans.modules.compapp.casaeditor.model.casa.CasaPort;
077: import org.netbeans.modules.compapp.casaeditor.model.casa.CasaProvides;
078: import org.netbeans.modules.compapp.casaeditor.model.casa.CasaRegion;
079: import org.netbeans.modules.compapp.projects.jbi.api.JbiBuildTask;
080: import org.netbeans.modules.compapp.projects.jbi.api.JbiDefaultComponentInfo;
081: import org.openide.ErrorManager;
082: import org.openide.util.NbBundle;
083:
084: /**
085: *
086: * @author Josh Sandusky
087: */
088: public class CasaModelGraphUtilities {
089:
090: private static final DisablingAction DISABLER = new DisablingAction();
091:
092: private static final String NULL_PROCESS_NAME = "<NULL_PROCESS_NAME>";
093:
094: public static void renderModel(final CasaWrapperModel model,
095: final CasaModelGraphScene scene) {
096: setSceneEnabled(scene, false);
097: scene.setIsAdjusting(true);
098: try {
099: safeRenderModel(model, scene);
100: } catch (final Throwable t) {
101: scene.autoLayout(false);
102: ErrorManager.getDefault().notify(t);
103: } finally {
104: scene.setIsAdjusting(false);
105: scene.finalizeModelPositions();
106: setSceneEnabled(scene, scene.canEdit());
107: }
108: }
109:
110: private static void safeRenderModel(CasaWrapperModel model,
111: CasaModelGraphScene scene) {
112: if (model == null || scene == null) {
113: return;
114: }
115:
116: // clean up any pre-existing model widgets
117:
118: for (CasaComponent ob : new ArrayList<CasaComponent>(scene
119: .getEdges())) {
120: scene.removeEdge(ob);
121: }
122: for (CasaComponent ob : new ArrayList<CasaComponent>(scene
123: .getNodes())) {
124: scene.removeNode(ob);
125: }
126: for (CasaComponent ob : new ArrayList<CasaComponent>(scene
127: .getRegions())) {
128: scene.removeRegion(ob);
129: }
130:
131: // Initially the empty graph should be finalized, we later set this to
132: // false if any node widget we add has an invalid model location.
133: scene.setModelPositionsFinalized(true);
134:
135: // add JBIComponent objects to scene
136:
137: // add regions
138: createRegions(model, scene);
139:
140: // add nodes and ports
141: for (CasaPort casaPort : model.getCasaPorts()) {
142: createNode(casaPort, model, scene, casaPort.getX(),
143: casaPort.getY());
144: }
145:
146: for (CasaServiceEngineServiceUnit su : model
147: .getServiceEngineServiceUnits()) {
148: createNode(su, model, scene, su.getX(), su.getY());
149: }
150:
151: // validate, and layout if required
152: scene.validate();
153: renderLayout(scene);
154:
155: // validateModel(model, scene);
156: // add connections last
157: for (CasaConnection connection : model
158: .getCasaConnectionList(false)) {
159: CasaConsumes consumes = (CasaConsumes) model
160: .getCasaEndpointRef(connection, true);
161: CasaProvides provides = (CasaProvides) model
162: .getCasaEndpointRef(connection, false);
163: if (consumes != null && provides != null) {
164: createEdge(connection, consumes, provides, scene, false);
165: }
166: }
167: scene
168: .setOrthogonalRouter(RouterFactory
169: .createOrthogonalSearchRouter(new CasaCollisionCollector(
170: scene.getBindingRegion(), scene
171: .getEngineRegion(), scene
172: .getExternalRegion(), scene
173: .getConnectionLayer())));
174: scene.updateEdgeRouting(null);
175: scene.validate();
176: }
177:
178: private static void renderLayout(final CasaModelGraphScene scene) {
179: int nodeCount = 0;
180: int badCount = 0;
181:
182: // determine what kind of layout is required, if any
183: for (CasaComponent node : scene.getNodes()) {
184: Widget widget = scene.findWidget(node);
185: if (!(widget instanceof CasaNodeWidget)) {
186: continue;
187: }
188: nodeCount++;
189: Point point = widget.getPreferredLocation();
190: boolean isBadPoint = point == null || point.x < 0
191: || point.y < 0;
192: if (isBadPoint) {
193: badCount++;
194: }
195: }
196:
197: if (badCount > 0) {
198: if (badCount == nodeCount) {
199: scene.autoLayout(false);
200: } else {
201: scene.progressiveLayout(false);
202: RegionUtilities.stretchScene(scene);
203: }
204: }
205: }
206:
207: /**
208: * Callers to this method must remember to call scene.validate().
209: */
210: public static void createRegions(CasaWrapperModel model,
211: CasaModelGraphScene scene) {
212:
213: CasaRegion bindingRegion = model
214: .getCasaRegion(CasaRegion.Name.WSDL_ENDPOINTS);
215: CasaRegion engineRegion = model
216: .getCasaRegion(CasaRegion.Name.JBI_MODULES);
217: CasaRegion externalRegion = model
218: .getCasaRegion(CasaRegion.Name.EXTERNAL_MODULES);
219:
220: CasaRegionWidget bindingRegionWidget = (CasaRegionWidget) scene
221: .addRegion(bindingRegion);
222: CasaRegionWidget engineRegionWidget = (CasaRegionWidget) scene
223: .addRegion(engineRegion);
224: CasaRegionWidget externalRegionWidget = (CasaRegionWidget) scene
225: .addRegion(externalRegion);
226:
227: // Ensure the engine region is on top so that its banner, if displayed,
228: // won't get hidden by the other regions.
229: engineRegionWidget.bringToFront();
230:
231: bindingRegionWidget.setPreferredLocation(new Point(0, 0));
232: int bindingWidth = bindingRegion.getWidth();
233: if (bindingWidth <= 0) {
234: bindingWidth = 200;
235: }
236: bindingRegionWidget.setPreferredBounds(new Rectangle(
237: bindingWidth, RegionUtilities.DEFAULT_HEIGHT));
238:
239: engineRegionWidget.setPreferredLocation(new Point(bindingWidth,
240: 0));
241: int engineWidth = engineRegion.getWidth();
242: if (engineWidth <= 0) {
243: engineWidth = 500;
244: }
245: engineRegionWidget.setPreferredBounds(new Rectangle(
246: engineWidth, RegionUtilities.DEFAULT_HEIGHT));
247:
248: externalRegionWidget.setPreferredLocation(new Point(
249: bindingWidth + engineWidth, 0));
250: int externalWidth = externalRegion.getWidth();
251: if (externalWidth <= 0) {
252: externalWidth = 200;
253: }
254: externalRegionWidget.setPreferredBounds(new Rectangle(
255: externalWidth, RegionUtilities.DEFAULT_HEIGHT));
256:
257: // Resizers
258: scene.getLeftResizer().setPreferredLocation(
259: new Point(engineRegionWidget.getPreferredLocation().x
260: - RegionUtilities.RESIZER_HALF_WIDTH, 0));
261: scene.getMiddleResizer().setPreferredLocation(
262: new Point(externalRegionWidget.getPreferredLocation().x
263: - RegionUtilities.RESIZER_HALF_WIDTH, 0));
264:
265: RegionUtilities.stretchScene(scene);
266: }
267:
268: /**
269: * Callers to this method must remember to call scene.validate().
270: */
271: public static Widget createNode(CasaPort casaPort,
272: CasaWrapperModel model, CasaModelGraphScene scene, int x,
273: int y) {
274: CasaNodeWidget widget = (CasaNodeWidget) scene
275: .addNode(casaPort);
276:
277: CasaConsumes consumes = casaPort.getConsumes();
278: if (consumes != null) {
279: createPin(casaPort, consumes, null, scene, false);
280: }
281: CasaProvides provides = casaPort.getProvides();
282: if (provides != null) {
283: createPin(casaPort, provides, null, scene, false);
284: }
285: scene.validate();
286:
287: // set the location
288: if (x > 0 && y > 0) {
289: widget.setPreferredLocation(new Point(x, y));
290: } else {
291: scene.setModelPositionsFinalized(false);
292: }
293:
294: return widget;
295: }
296:
297: public static Widget createNode(CasaServiceEngineServiceUnit su,
298: CasaWrapperModel model, CasaModelGraphScene scene, int x,
299: int y) {
300: CasaNodeWidgetEngine sesuWidget = (CasaNodeWidgetEngine) scene
301: .addNode(su);
302:
303: // mapping process names to endpoint lists
304: Map<String, List<CasaEndpointRef>> process2EndpointRefMap = new HashMap<String, List<CasaEndpointRef>>();
305:
306: buildProcess2EndpointMap(process2EndpointRefMap, su
307: .getProvides());
308: buildProcess2EndpointMap(process2EndpointRefMap, su
309: .getConsumes());
310:
311: for (String processName : process2EndpointRefMap.keySet()) {
312: if (!processName.equals(NULL_PROCESS_NAME)) {
313: List<CasaEndpointRef> endpointRefs = process2EndpointRefMap
314: .get(processName);
315: CasaEndpoint endpoint = endpointRefs.get(0)
316: .getEndpoint().get();
317: createProcess(su, endpoint, scene, false);
318: }
319:
320: List<CasaEndpointRef> endpointRefs = process2EndpointRefMap
321: .get(processName);
322: for (CasaEndpointRef endpointRef : endpointRefs) {
323: createPin(su, endpointRef,
324: endpointRef.getDisplayName(), scene, false);
325: }
326: }
327: sesuWidget.doneAddingWidget();
328:
329: scene.validate();
330:
331: // set the location
332: if (x > 0 && y > 0) {
333: sesuWidget.setPreferredLocation(new Point(x, y));
334: } else {
335: scene.setModelPositionsFinalized(false);
336: }
337:
338: return sesuWidget;
339: }
340:
341: private static void buildProcess2EndpointMap(
342: Map<String, List<CasaEndpointRef>> process2EndpointMap,
343: List<? extends CasaEndpointRef> endpoints) {
344:
345: for (CasaEndpointRef endpoint : endpoints) {
346:
347: String processName = endpoint.getProcessName();
348: if (processName == null) {
349: processName = NULL_PROCESS_NAME;
350: }
351:
352: List<CasaEndpointRef> endpointList = process2EndpointMap
353: .get(processName);
354: if (endpointList == null) {
355: endpointList = new ArrayList<CasaEndpointRef>();
356: process2EndpointMap.put(processName, endpointList);
357: }
358:
359: endpointList.add(endpoint);
360: }
361: }
362:
363: public static boolean updateNodeProperties(CasaWrapperModel model,
364: CasaPort casaPort, CasaNodeWidget widget) {
365: String name = getShortNameInUpperCase(casaPort
366: .getEndpointName());
367:
368: String bcCompName = model.getBindingComponentName(casaPort);
369: if (bcCompName == null || bcCompName.length() == 0) {
370: ErrorManager
371: .getDefault()
372: .notify(
373: new UnsupportedOperationException(
374: NbBundle
375: .getMessage(
376: CasaModelGraphUtilities.class,
377: "Error_No_Binding_Component_name_for_endpoint")
378: + name)); // NOI18N
379: return false;
380: }
381: String bindingType = model.getBindingType(casaPort);
382: if (bindingType == null) {
383: ErrorManager.getDefault().notify(
384: new UnsupportedOperationException(NbBundle
385: .getMessage(CasaModelGraphUtilities.class,
386: "Error_Invalid_Binding_Component")
387: + bcCompName));
388: return false;
389: }
390: bindingType = bindingType.toUpperCase();
391:
392: widget.setNodeProperties(name, bindingType);
393:
394: return true;
395: }
396:
397: public static void updateNodeProperties(CasaWrapperModel model,
398: CasaServiceEngineServiceUnit su, CasaNodeWidget widget) {
399: String name = su.getUnitName();
400: String type = su.getComponentName();
401: type = JbiDefaultComponentInfo.getDisplayName(type)
402: .toUpperCase();
403: if (type.endsWith("SERVICEENGINE")) { // NOI18N
404: type = type.substring(0, type.length() - 13);
405: }
406: widget.setNodeProperties(name, type);
407: }
408:
409: private static String getShortNameInUpperCase(String str) {
410: int shortNameIndex = str.lastIndexOf('.') + 1;
411: if (shortNameIndex > 0 && shortNameIndex < str.length()) {
412: return str.substring(shortNameIndex).toUpperCase();
413: }
414: return str;
415: }
416:
417: public static CasaPinWidget createPin(CasaComponent node,
418: CasaComponent pin, String name, CasaModelGraphScene scene,
419: boolean doUpdate) {
420: CasaPinWidget pinWidget = (CasaPinWidget) scene.addPin(node,
421: pin);
422: pinWidget.setProperties(name);
423: pinWidget.setToolTipText(getToolTipName(node, pin, scene
424: .getModel()));
425:
426: if (doUpdate) {
427: scene.validate();
428: }
429: return pinWidget;
430: }
431:
432: public static CasaProcessTitleWidget createProcess(
433: CasaServiceEngineServiceUnit sesu, CasaEndpoint endpoint,
434: CasaModelGraphScene scene, boolean doUpdate) {
435: CasaProcessTitleWidget processWidget = (CasaProcessTitleWidget) scene
436: .addProcess(sesu, endpoint);
437:
438: if (doUpdate) {
439: scene.validate();
440: }
441: return processWidget;
442: }
443:
444: /**
445: * Callers to this method must remember to call scene.validate().
446: */
447: public static ConnectionWidget createEdge(
448: CasaConnection connection, CasaConsumes source,
449: CasaProvides target, CasaModelGraphScene scene,
450: boolean doUpdate) {
451: ConnectionWidget widget = (ConnectionWidget) scene
452: .addEdge(connection);
453: scene.setEdgeSource(connection, source);
454: scene.setEdgeTarget(connection, target);
455: if (doUpdate) {
456: scene.updateEdgeRouting(widget);
457: scene.validate();
458: }
459: return widget;
460: }
461:
462: public static CasaNodeWidget findNodeWidget(CasaPinWidget pinWidget) {
463: Widget parentWidget = pinWidget.getParentWidget();
464: CasaNodeWidget nodeWidget = null;
465: while (parentWidget != null) {
466: if (parentWidget instanceof CasaNodeWidget) {
467: nodeWidget = (CasaNodeWidget) parentWidget;
468: break;
469: }
470: parentWidget = parentWidget.getParentWidget();
471: }
472: return nodeWidget;
473: }
474:
475: public static String getToolTipName(CasaComponent node,
476: CasaComponent pin, CasaWrapperModel model) {
477: String toolTip = "";
478: if (pin instanceof CasaEndpointRef) {
479: CasaEndpointRef endPointRef = (CasaEndpointRef) pin;
480: toolTip = endPointRef.getServiceQName().toString();
481:
482: if (toolTip != null && toolTip.trim().length() > 0) {
483: toolTip += Constants.PERIOD;
484: }
485: toolTip += endPointRef.getEndpointName();
486: }
487: return toolTip;
488: }
489:
490: /*
491: * Either a region or any widget could be made visible.
492: */
493: public static void ensureVisibity(Widget w) {
494: w.getScene().getView().scrollRectToVisible(
495: w.convertLocalToScene(w.getBounds()));
496: }
497:
498: public static void updateModelPosition(CasaModelGraphScene scene,
499: CasaComponent component, Point location) {
500: if (component instanceof CasaServiceEngineServiceUnit) {
501: CasaServiceEngineServiceUnit su = (CasaServiceEngineServiceUnit) component;
502: if (su.getX() != location.x || su.getY() != location.y) {
503: scene.getModel()
504: .setLocation(su, location.x, location.y);
505: }
506: } else if (component instanceof CasaPort) {
507: CasaPort port = (CasaPort) component;
508: if (port.getX() != location.x || port.getY() != location.y) {
509: scene.getModel().setLocation(port, location.x,
510: location.y);
511: }
512: }
513: }
514:
515: public static void updateWidth(CasaModelGraphScene scene,
516: CasaRegionWidget widget) {
517: CasaRegion region = (CasaRegion) scene.findObject(widget);
518: Rectangle bounds = widget.getPreferredBounds();
519: if (bounds == null) {
520: bounds = widget.getBounds();
521: }
522: if (region.getWidth() != bounds.width) {
523: scene.getModel().setCasaRegionWidth(region, bounds.width);
524: }
525: }
526:
527: public static void setSceneEnabled(CasaModelGraphScene scene,
528: JbiBuildTask task) {
529: Chain priorActions = scene.getPriorActions();
530: WaitMessageHandler.addToScene(scene, task);
531: if (!priorActions.getActions().contains(DISABLER)) {
532: priorActions.addAction(0, DISABLER);
533: }
534: }
535:
536: public static void setSceneEnabled(CasaModelGraphScene scene,
537: boolean isEnabled) {
538: Chain priorActions = scene.getPriorActions();
539: if (isEnabled) {
540: priorActions.removeAction(DISABLER);
541: WaitMessageHandler.removeFromScene(scene);
542: } else {
543: WaitMessageHandler.addToScene(scene, null);
544: if (!priorActions.getActions().contains(DISABLER)) {
545: priorActions.addAction(0, DISABLER);
546: }
547: }
548: }
549:
550: private static class DisablingAction extends
551: WidgetAction.LockedAdapter {
552:
553: protected boolean isLocked() {
554: return true;
555: }
556:
557: @Override
558: public State mouseClicked(Widget arg0, WidgetMouseEvent arg1) {
559: return State.createLocked(arg0, DisablingAction.this );
560: }
561:
562: @Override
563: public State mousePressed(Widget arg0, WidgetMouseEvent arg1) {
564: return State.createLocked(arg0, DisablingAction.this );
565: }
566:
567: @Override
568: public State mouseReleased(Widget arg0, WidgetMouseEvent arg1) {
569: return State.createLocked(arg0, DisablingAction.this );
570: }
571:
572: @Override
573: public State mouseEntered(Widget arg0, WidgetMouseEvent arg1) {
574: return State.createLocked(arg0, DisablingAction.this );
575: }
576:
577: @Override
578: public State mouseExited(Widget arg0, WidgetMouseEvent arg1) {
579: return State.createLocked(arg0, DisablingAction.this );
580: }
581:
582: @Override
583: public State mouseDragged(Widget arg0, WidgetMouseEvent arg1) {
584: return State.createLocked(arg0, DisablingAction.this );
585: }
586:
587: @Override
588: public State mouseMoved(Widget arg0, WidgetMouseEvent arg1) {
589: return State.createLocked(arg0, DisablingAction.this );
590: }
591:
592: @Override
593: public State keyTyped(Widget arg0, WidgetKeyEvent arg1) {
594: return State.createLocked(arg0, DisablingAction.this );
595: }
596:
597: @Override
598: public State keyPressed(Widget arg0, WidgetKeyEvent arg1) {
599: return State.createLocked(arg0, DisablingAction.this );
600: }
601:
602: @Override
603: public State keyReleased(Widget arg0, WidgetKeyEvent arg1) {
604: return State.createLocked(arg0, DisablingAction.this );
605: }
606:
607: @Override
608: public State dragEnter(Widget arg0,
609: WidgetDropTargetDragEvent arg1) {
610: return State.createLocked(arg0, DisablingAction.this );
611: }
612:
613: @Override
614: public State dragOver(Widget arg0,
615: WidgetDropTargetDragEvent arg1) {
616: return State.createLocked(arg0, DisablingAction.this );
617: }
618:
619: @Override
620: public State dropActionChanged(Widget arg0,
621: WidgetDropTargetDragEvent arg1) {
622: return State.createLocked(arg0, DisablingAction.this );
623: }
624:
625: @Override
626: public State dragExit(Widget arg0, WidgetDropTargetEvent arg1) {
627: return State.createLocked(arg0, DisablingAction.this );
628: }
629:
630: @Override
631: public State drop(Widget arg0, WidgetDropTargetDropEvent arg1) {
632: return State.createLocked(arg0, DisablingAction.this);
633: }
634: }
635: }
|