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.visualweb.outline;
043:
044: import com.sun.rave.designtime.Constants;
045: import com.sun.rave.designtime.DesignBean;
046: import com.sun.rave.designtime.DesignContext;
047: import com.sun.rave.designtime.DesignEvent;
048: import com.sun.rave.designtime.DesignProject;
049: import com.sun.rave.designtime.DesignProperty;
050: import com.sun.rave.designtime.Position;
051: import com.sun.rave.designtime.event.DesignContextListener;
052: import com.sun.rave.designtime.event.DesignProjectListener;
053: import com.sun.rave.designtime.faces.FacesDesignProject; //import com.sun.rave.designtime.event.DesignProjectListener;
054: import java.awt.BorderLayout;
055: import java.awt.EventQueue;
056: import java.awt.Rectangle;
057: import java.awt.event.ActionEvent;
058: import java.awt.event.KeyEvent;
059: import java.beans.PropertyChangeListener;
060: import java.beans.PropertyVetoException;
061: import java.lang.ref.WeakReference;
062: import java.util.ArrayList;
063: import java.util.Arrays;
064: import java.util.Collection;
065: import java.util.Collections;
066: import java.util.Comparator;
067: import java.util.HashSet;
068: import java.util.List;
069: import java.util.Set;
070: import java.util.logging.Level;
071: import java.util.logging.Logger;
072: import javax.swing.AbstractAction;
073: import javax.swing.ActionMap;
074: import javax.swing.InputMap;
075: import javax.swing.JComponent;
076: import javax.swing.JPanel;
077: import javax.swing.KeyStroke;
078: import javax.swing.text.DefaultEditorKit;
079: import javax.swing.tree.DefaultTreeModel;
080: import javax.swing.tree.TreeModel;
081: import javax.swing.tree.TreeNode;
082: import javax.swing.tree.TreePath;
083: import org.openide.ErrorManager;
084: import org.openide.explorer.ExplorerManager;
085: import org.openide.explorer.ExplorerUtils;
086: import org.openide.explorer.view.BeanTreeView;
087: import org.openide.explorer.view.Visualizer;
088: import org.openide.nodes.AbstractNode;
089: import org.openide.nodes.Children;
090: import org.openide.nodes.Node;
091: import org.openide.nodes.NodeOp;
092: import org.openide.util.HelpCtx;
093: import org.openide.util.Lookup;
094: import org.openide.util.WeakListeners;
095:
096: /**
097: * Component showing the outline of the specified <code>DesignBean</code>
098: * It works the way, it looks for the <code>DesignBean</code> instance
099: * in the provided <code>Lookup</code>, and then it finds its root bean
100: * and corresponding <code>DesignContext</code>, which is used to retrieve
101: * the relevant beans and their <code>Node</code> represenations.
102: *
103: * @author Peter Zavadsky
104: */
105: class OutlinePanel extends JPanel implements ExplorerManager.Provider,
106: Lookup.Provider, HelpCtx.Provider {
107:
108: /** Debugging flag. */
109: private static final boolean DEBUG = ErrorManager.getDefault()
110: .getInstance(OutlinePanel.class.getName()).isLoggable(
111: ErrorManager.INFORMATIONAL);
112:
113: private static WeakReference<OutlinePanel> instanceWRef = new WeakReference<OutlinePanel>(
114: null);
115:
116: private final ExplorerManager manager = new ExplorerManager();
117: private final Lookup lookup;
118:
119: private final OutlineTreeView treeView = new OutlineTreeView(this );
120:
121: private final PropertyChangeListener outlineManagerListener = new OutlineManagerListener(
122: this );
123:
124: // private DesignProjectListener designProjectListener;
125:
126: private final OutlineRootChildren outlineRootChildren = new OutlineRootChildren(
127: this );
128:
129: /** Creates a new instance of OutlinePanel */
130: private OutlinePanel() {
131: // same as before...
132: ActionMap map = getActionMap();
133: map.put(DefaultEditorKit.copyAction, ExplorerUtils
134: .actionCopy(manager));
135: map.put(DefaultEditorKit.cutAction, ExplorerUtils
136: .actionCut(manager));
137: map.put(DefaultEditorKit.pasteAction, ExplorerUtils
138: .actionPaste(manager));
139: map.put("delete", ExplorerUtils.actionDelete(manager, true)); // or false // NOI18N
140:
141: // Do not use the InputMap for Cut, Copy, Paste and Delete. Instead let the key bindings
142: // for global Cut, Copy, Paste and Delete.
143: // ...but add e.g.:
144: //InputMap keys = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
145: //keys.put(KeyStroke.getKeyStroke("control C"), DefaultEditorKit.copyAction); // NOI18N
146: //keys.put(KeyStroke.getKeyStroke("control X"), DefaultEditorKit.cutAction); // NOI18N
147: //keys.put(KeyStroke.getKeyStroke("control V"), DefaultEditorKit.pasteAction); // NOI18N
148: //keys.put(KeyStroke.getKeyStroke("DELETE"), "delete"); // NOI18N
149:
150: // >>>Debugging helper
151: InputMap keys = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
152: keys.put(KeyStroke.getKeyStroke(KeyEvent.VK_F2,
153: KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK),
154: "rave-outline-dump-nodes"); // NOI18N
155: map.put("rave-outline-dump-nodes", // NOI18N
156: new AbstractAction() {
157: public void actionPerformed(ActionEvent evt) {
158: dumpActivatedNodes();
159: }
160: });
161: // <<<Debugging helper
162:
163: // ...and initialization of lookup variable
164: lookup = ExplorerUtils.createLookup(manager, map);
165:
166: treeView.setRootVisible(false);
167:
168: setLayout(new BorderLayout());
169: add(treeView, BorderLayout.CENTER);
170:
171: manager.addPropertyChangeListener(outlineManagerListener);
172:
173: // manager.setRootContext(new OutlineRootNode());
174: Node rootNode = new AbstractNode(outlineRootChildren);
175: rootNode.setName("Hidden Outline Root"); // NOI18N
176: manager.setRootContext(rootNode);
177: }
178:
179: public static OutlinePanel getDefault() {
180: synchronized (OutlinePanel.class) {
181: OutlinePanel outlinePanel = instanceWRef.get();
182: if (outlinePanel == null) {
183: outlinePanel = new OutlinePanel();
184: instanceWRef = new WeakReference<OutlinePanel>(
185: outlinePanel);
186: }
187: return outlinePanel;
188: }
189: }
190:
191: // XXX #6486267 Delegating to the treeView.
192: public void requestFocus() {
193: treeView.requestFocus();
194: }
195:
196: // XXX #6486267 Delegating to the treeView.
197: public boolean requestFocusInWindow() {
198: return treeView.requestFocusInWindow();
199: }
200:
201: // ...method as before and getLookup
202: public ExplorerManager getExplorerManager() {
203: return manager;
204: }
205:
206: public Lookup getLookup() {
207: return lookup;
208: }
209:
210: // ...methods as before, but replace componentActivated and
211: // componentDeactivated with e.g.:
212: public void addNotify() {
213: super .addNotify();
214: ExplorerUtils.activateActions(manager, true);
215: }
216:
217: public void removeNotify() {
218: ExplorerUtils.activateActions(manager, false);
219: super .removeNotify();
220: }
221:
222: public void setActiveBeans(DesignBean[] designBeans) {
223: if (DEBUG) {
224: debugLog("designBeans=" + Arrays.asList(designBeans)); // NOI18N
225: }
226:
227: if (designBeans.length == 0) {
228: // ((OutlineRootNode)manager.getRootContext()).setDesignBean(null);
229: outlineRootChildren.setDesignBean(null);
230: // updateDesignProjectListening(null);
231:
232: selectNodesForDesignBeans(new DesignBean[0]);
233: } else {
234: // XXX First only?
235: // ((OutlineRootNode)manager.getRootContext()).setDesignBean(designBeans[0]);
236: outlineRootChildren.setDesignBean(designBeans[0]);
237:
238: selectNodesForDesignBeans(designBeans);
239: // updateDesignProjectListening(rootBean.getDesignContext().getProject());
240: }
241: }
242:
243: private void selectNodesForDesignBeans(
244: final DesignBean[] designBeans) {
245: if (isShowing()) {
246: doSelectNodesForDesignBeans(designBeans);
247: } else {
248: // XXX Neccessary in order to give a chance the component to open.
249: EventQueue.invokeLater(new Runnable() {
250: public void run() {
251: // XXX The nodes are not inited when the component is not on the screen
252: // strange impl (that was supposed to be a model).
253: // if (OutlinePanel.this.isShowing()) {
254: doSelectNodesForDesignBeans(designBeans);
255: // }
256: }
257: });
258: }
259: }
260:
261: private void doSelectNodesForDesignBeans(DesignBean[] designBeans) {
262: Node[] nodes = getNodesForBeans(designBeans);
263: initializeParentNodes(nodes);
264:
265: if (!areNodesUnderRoot(nodes, manager.getRootContext())) {
266: return;
267: }
268:
269: expandNodes(nodes);
270: scrollToNodes(nodes);
271:
272: // XXX Don't listen on these programatic changes.
273: manager.removePropertyChangeListener(outlineManagerListener);
274: try {
275: manager.setSelectedNodes(nodes);
276: } catch (PropertyVetoException pve) {
277: ErrorManager.getDefault().notify(
278: ErrorManager.INFORMATIONAL, pve);
279: } finally {
280: manager.addPropertyChangeListener(outlineManagerListener);
281: }
282: }
283:
284: private static Node[] getNodesForBeans(DesignBean[] designBeans) {
285: List<Node> nodes = new ArrayList<Node>();
286: for (DesignBean designBean : designBeans) {
287: Node node = OutlineUtilities
288: .getNodeForDesignBean(designBean);
289: if (node != null) {
290: nodes.add(node);
291: }
292: }
293:
294: return nodes.toArray(new Node[nodes.size()]);
295: }
296:
297: private void expandNodes(Node[] nodes) {
298: treeView.expandNodes(nodes);
299: }
300:
301: private void scrollToNodes(Node[] nodes) {
302: treeView.scrollToNodes(nodes);
303: }
304:
305: /** See the {@link #initializeParentNodes(Node) method. */
306: private static void initializeParentNodes(Node[] nodes) {
307: for (int i = 0; i < nodes.length; i++) {
308: initializeParentNodes(nodes[i]);
309: }
310: }
311:
312: /** XXX Assures the parents of this node are initialized.
313: * The node structure parent vs. children might not be initialized.
314: * @see org.openide.nodes.Children#getNodes(boolean) */
315: private static void initializeParentNodes(Node node) {
316: while (true) {
317: DesignBean designBean = (DesignBean) node.getLookup()
318: .lookup(DesignBean.class);
319: if (designBean == null) {
320: return;
321: }
322:
323: DesignBean parentBean = designBean.getBeanParent();
324: if (parentBean == null) {
325: return;
326: }
327:
328: node = OutlineUtilities.getNodeForDesignBean(parentBean);
329: // XXX This inits the children.
330: node.getChildren().getNodes(true);
331: }
332: }
333:
334: private static boolean areNodesUnderRoot(Node[] nodes, Node root) {
335: if (root == null || nodes == null || nodes.length == 0) {
336: return false;
337: }
338:
339: for (int i = 0; i < nodes.length; i++) {
340: Node node = nodes[i];
341: if (root != NodeOp.findRoot(node)) {
342: // XXX This happens in the sanity, couldn't reproduce. Therefore under DEBUG.
343: // FIXME It looks like there is another not created for the page1 comp. Needs to be checked.
344: if (DEBUG) {
345: ErrorManager.getDefault().notify(
346: ErrorManager.INFORMATIONAL,
347: new IllegalStateException("Node="
348: + node
349: + " is not under root="
350: + root // NOI18N
351: + "\n\n"
352: + dumpNodeWithChildren(root, 0) // NOI18N
353: + "\n\n"
354: + dumpNodeWithChildren(NodeOp
355: .findRoot(node), 0))); // NOI18N
356: }
357: return false;
358: }
359: }
360:
361: return true;
362: }
363:
364: private void dumpActivatedNodes() {
365: log("Activated nodes:"); // NOI18N
366: Node[] nodes = getExplorerManager().getSelectedNodes();
367: if (nodes == null) {
368: return;
369: }
370: for (Node node : nodes) {
371: log(dumpNodeWithChildren(node, 0));
372: }
373: }
374:
375: private static String dumpNodeWithChildren(Node node, int tabs) {
376: StringBuilder sb = new StringBuilder();
377: sb.append("\n" + getTabs(tabs) + node); // NOI18N
378: Node[] children = node.getChildren().getNodes(true);
379: for (int i = 0; i < children.length; i++) {
380: sb.append(dumpNodeWithChildren(children[i], tabs + 1));
381: }
382: return sb.toString();
383: }
384:
385: private static String getTabs(int tabs) {
386: StringBuilder sb = new StringBuilder();
387: for (int i = 0; i < tabs; i++) {
388: sb.append('\t');
389: }
390:
391: return sb.toString();
392: }
393:
394: /** Logs debug message. Use only after checking <code>DEBUG</code> flag. */
395: private static void debugLog(String message) {
396: ErrorManager.getDefault().getInstance(
397: OutlinePanel.class.getName()).log(message);
398: }
399:
400: boolean isSelectionValid() {
401: return areNodesUnderRoot(manager.getSelectedNodes(), manager
402: .getRootContext());
403: }
404:
405: // private void updateDesignProjectListening(DesignProject designProject) {
406: // if (designProject == null) {
407: // designProjectListener = null;
408: // return;
409: // }
410: //
411: // designProjectListener = new OutlineDesignProjectListener(this);
412: // designProject.addDesignProjectListener(
413: // (DesignProjectListener)WeakListeners.create(DesignProjectListener.class, designProjectListener, designProject));
414: // }
415:
416: public HelpCtx getHelpCtx() {
417: return ExplorerUtils
418: .getHelpCtx(
419: getExplorerManager().getSelectedNodes(),
420: new HelpCtx(
421: "projrave_ui_elements_navigator_win_about_navigator_win")); // NOI18N
422: }
423:
424: private static class OutlineTreeView extends BeanTreeView {
425:
426: private final OutlinePanel outlinePanel;
427:
428: public OutlineTreeView(OutlinePanel outlinePanel) {
429: this .outlinePanel = outlinePanel;
430: }
431:
432: @Override
433: public String toString() {
434: ExplorerManager manager = outlinePanel.manager;
435: if (manager == null) {
436: return super .toString();
437: }
438: Node[] nodes = manager.getSelectedNodes();
439: return super .toString() + ", manager=" + manager
440: + ", selectedNodes="
441: + (nodes == null ? null : Arrays.asList(nodes));
442: }
443:
444: public void expandNodes(Node[] nodes) {
445: TreePath[] treePaths = getTreePathsForNodes(nodes);
446: for (int i = 0; i < treePaths.length; i++) {
447: // XXX Silly Swing impl, if the last element is leaf,
448: // the path is not expanded!
449: TreePath expandPath = getTreePathToExpand(treePaths[i]);
450: if (expandPath != null) {
451: tree.expandPath(expandPath);
452: }
453: }
454: }
455:
456: public void scrollToNodes(Node[] nodes) {
457: TreePath[] treePaths = getTreePathsForNodes(nodes);
458: Rectangle rect = new Rectangle();
459:
460: if (areTreePathsVisible(treePaths)) {
461: return;
462: }
463:
464: for (int i = 0; i < treePaths.length; i++) {
465: Rectangle bounds = tree.getPathBounds(treePaths[i]);
466: if (bounds == null) {
467: ErrorManager.getDefault().notify(
468: ErrorManager.INFORMATIONAL,
469: new NullPointerException(
470: "No bounds for treePath="
471: + treePaths[i])); // NOI18N
472: } else {
473: rect.add(bounds);
474: }
475: }
476:
477: if (!rect.isEmpty()) {
478: tree.scrollRectToVisible(rect);
479: }
480: }
481:
482: private boolean areTreePathsVisible(TreePath[] treePaths) {
483: for (int i = 0; i < treePaths.length; i++) {
484: if (!tree.isVisible(treePaths[i])) {
485: return false;
486: }
487: }
488: return true;
489: }
490:
491: private static TreePath getTreePathToExpand(TreePath original) {
492: Object[] elements = original.getPath();
493: if (elements.length < 2) {
494: return null;
495: } else {
496: List<Object> newElements = new ArrayList<Object>(Arrays
497: .asList(elements));
498: newElements.remove(elements.length - 1);
499: return new TreePath(newElements.toArray());
500: }
501: }
502:
503: private TreePath[] getTreePathsForNodes(Node[] nodes) {
504: List<TreePath> treePaths = new ArrayList<TreePath>();
505: for (Node node : nodes) {
506: TreePath tp = getTreePathForNode(node);
507: if (tp == null) {
508: continue;
509: }
510: treePaths.add(tp);
511: }
512: return treePaths.toArray(new TreePath[treePaths.size()]);
513: }
514:
515: private TreePath getTreePathForNode(Node node) {
516: TreeNode tn = Visualizer.findVisualizer(node);
517: if (tn == null) {
518: ErrorManager.getDefault().notify(
519: ErrorManager.INFORMATIONAL,
520: new NullPointerException(
521: "TreeNode not found for node=" + node)); // NOI18N
522: return null;
523: }
524:
525: TreeModel model = tree.getModel();
526: if (!(model instanceof DefaultTreeModel)) {
527: ErrorManager.getDefault().notify(
528: ErrorManager.INFORMATIONAL,
529: new NullPointerException(
530: "DefaultTreeModel not found, model="
531: + model)); // NOI18N
532: return null;
533: }
534:
535: return new TreePath(((DefaultTreeModel) model)
536: .getPathToRoot(tn));
537: }
538: } // End of OutlineTreeView.
539:
540: // private static class OutlineDesignProjectListener implements DesignProjectListener {
541: // private final OutlinePanel outlinePanel;
542: //
543: // public OutlineDesignProjectListener(OutlinePanel outlinePanel) {
544: // this.outlinePanel = outlinePanel;
545: // }
546: //
547: // public void contextOpened(DesignContext designContext) {
548: // }
549: //
550: // public void contextClosed(DesignContext designContext) {
551: // DesignBean designBean = ((OutlineRootNode)outlinePanel.getExplorerManager().getRootContext()).getDesignBean();
552: //
553: // if (designBean == null) {
554: // return;
555: // }
556: //
557: // if (designBean.getDesignContext() == designContext) {
558: // // Our DesignContext was closed, clean the outline panel.
559: // outlinePanel.setActiveBeans(new DesignBean[0]);
560: // }
561: // }
562: // } // End of OutlineDesignProjectListener.
563:
564: private static class OutlineRootChildren extends
565: Children.Keys<DesignBean> {
566:
567: private final DesignProjectListener designProjectListener = new OutlineDesignProjectListener(
568: this );
569:
570: private DesignContextListener designContextListener;
571:
572: private final OutlinePanel outlinePanel;
573:
574: /** Current root (Page) design bean. */
575: private DesignBean rootDesignBean;
576:
577: public OutlineRootChildren(OutlinePanel outlinePanel) {
578: this .outlinePanel = outlinePanel;
579: }
580:
581: // protected void addNotify() {
582: // super.addNotify();
583: // setKeys(Collections.EMPTY_SET);
584: // }
585: //
586: // protected void removeNotify() {
587: // setKeys(Collections.EMPTY_SET);
588: // super.removeNotify();
589: // }
590:
591: protected Node[] createNodes(DesignBean key) {
592: if (DEBUG) {
593: debugLog("creatingNode for key=" + key); // NOI18N
594: }
595: return key == null ? new Node[0]
596: : new Node[] { OutlineUtilities
597: .getNodeForDesignBean(key) };
598: }
599:
600: private void setDesignBean(DesignBean designBean) {
601: setRootDesignBean(designBean == null ? null : designBean
602: .getDesignContext().getRootContainer());
603: }
604:
605: private void setRootDesignBean(DesignBean rootDesignBean) {
606: if (this .rootDesignBean == rootDesignBean) {
607: return;
608: }
609:
610: removeDesignProjectListening(this .rootDesignBean);
611:
612: this .rootDesignBean = rootDesignBean;
613: updateKeys();
614:
615: addDesignProjectListening(this .rootDesignBean);
616: }
617:
618: private void updateKeys() {
619: Collection<DesignBean> keys = getAllBeansCollection(rootDesignBean);
620: setKeys(keys);
621:
622: // #6463783.
623: updateDesignContextListening(keys);
624:
625: // // XXX Bean nodes may be changed! After refresh,
626: // // the original nodes are destroyed.
627: // for (Iterator it = keys.iterator(); it.hasNext(); ) {
628: // refreshKey(it.next());
629: // }
630: }
631:
632: private void addDesignProjectListening(DesignBean designBean) {
633: if (designBean == null) {
634: return;
635: }
636:
637: DesignContext designContext = designBean.getDesignContext();
638: if (designContext == null) {
639: // XXX Invalid already
640: return;
641: }
642: DesignProject designProject = designContext.getProject();
643: if (designProject == null) {
644: // XXX Invalid already.
645: return;
646: }
647: designProject
648: .addDesignProjectListener(designProjectListener);
649: }
650:
651: private void removeDesignProjectListening(DesignBean designBean) {
652: if (designBean == null) {
653: return;
654: }
655:
656: DesignContext designContext = designBean.getDesignContext();
657: if (designContext == null) {
658: // XXX Invalid already
659: return;
660: }
661: DesignProject designProject = designContext.getProject();
662: if (designProject == null) {
663: // XXX Invalid already.
664: return;
665: }
666: designProject
667: .removeDesignProjectListener(designProjectListener);
668: }
669:
670: private void updateDesignContextListening(
671: Collection<DesignBean> designBeans) {
672: designContextListener = null;
673:
674: Set<DesignContext> designContexts = new HashSet<DesignContext>();
675: for (DesignBean designBean : designBeans) {
676: designContexts.add(designBean.getDesignContext());
677: }
678:
679: if (!designContexts.isEmpty()) {
680: designContextListener = new OutlineDesignContextListener(
681: outlinePanel);
682: }
683: for (DesignContext designContext : designContexts) {
684: if (rootDesignBean != null
685: && designContext == rootDesignBean
686: .getDesignContext()) {
687: // XXX Skipping the context representing the Page.
688: continue;
689: }
690: designContext
691: .addDesignContextListener((DesignContextListener) WeakListeners
692: .create(DesignContextListener.class,
693: designContextListener,
694: designContext));
695: }
696: }
697:
698: // For performance improvement. No need to get all the contexts in the project
699: private static DesignContext[] getDesignContexts(
700: DesignContext context) {
701: DesignProject designProject = context.getProject();
702: DesignContext[] contexts;
703: if (designProject instanceof FacesDesignProject) {
704: contexts = ((FacesDesignProject) designProject)
705: .findDesignContexts(new String[] { "request",
706: "session", "application" });
707: } else {
708: contexts = new DesignContext[0];
709: }
710: DesignContext[] designContexts = new DesignContext[contexts.length + 1];
711: designContexts[0] = context;
712: System.arraycopy(contexts, 0, designContexts, 1,
713: contexts.length);
714: return designContexts;
715: }
716:
717: private static Collection<DesignBean> getAllBeansCollection(
718: DesignBean designBean) {
719: if (designBean == null) {
720: return Collections.emptyList();
721: }
722:
723: List<DesignBean> beans = new ArrayList<DesignBean>();
724: beans.add(designBean);
725:
726: // TODO Also provide children for Application, Session, Request beans.
727: DesignProject project = designBean.getDesignContext()
728: .getProject();
729:
730: if (project == null) {
731: return Collections.emptyList();
732: }
733:
734: //DesignContext[] contexts = project.getDesignContexts();
735: // XXX This is supposed to get the design context except the page bean ones, better name needed?.
736: DesignContext[] contexts;
737: DesignContext designContext = designBean.getDesignContext();
738: if (designContext == null) {
739: ErrorManager.getDefault().notify(
740: ErrorManager.INFORMATIONAL,
741: new IllegalStateException(
742: "Design bean returns null design context, designBean="
743: + designBean)); // NOI18N
744: contexts = new DesignContext[0];
745: } else {
746: contexts = getDesignContexts(designContext);
747: }
748:
749: List<DesignContext> auxiliaryContexts = new ArrayList<DesignContext>();
750: // for (DesignContext context : contexts) {
751: // Object scope = context.getContextData(Constants.ContextData.SCOPE);
752: // // XXX Missing designtime API, do not use just strings!
753: // if (/*"request".equals(scope) ||*/ "application".equals(scope) // NOI18N
754: // || "session".equals(scope) || "none".equals(scope)) { // NOI18N
755: // auxiliaryContexts.add(context);
756: // } else if ("request".equals(scope)) { // NOI18N
757: // if (!InsyncAccessor.isPageRootContainerDesignBean(context.getRootContainer())) {
758: // auxiliaryContexts.add(context);
759: // }
760: // }
761: // }
762: auxiliaryContexts.addAll(Arrays.asList(contexts));
763:
764: Collections.sort(auxiliaryContexts,
765: new AuxiliaryContextComparator());
766:
767: for (DesignContext context : auxiliaryContexts) {
768: DesignBean rootBean = context.getRootContainer();
769: // XXX There cannot be the same keys twice.
770: if (beans.contains(rootBean)) {
771: continue;
772: }
773: beans.add(rootBean);
774: }
775:
776: if (DEBUG) {
777: debugLog("beans=" + beans);
778: }
779:
780: return new ArrayList<DesignBean>(beans);
781: }
782:
783: private static class OutlineDesignProjectListener implements
784: DesignProjectListener {
785: private final OutlineRootChildren outlineRootChildren;
786:
787: public OutlineDesignProjectListener(
788: OutlineRootChildren outlineRootChildren) {
789: this .outlineRootChildren = outlineRootChildren;
790: }
791:
792: public void contextOpened(DesignContext designContext) {
793: updateChildren();
794: }
795:
796: public void contextClosed(DesignContext designContext) {
797: updateChildren();
798: }
799:
800: private void updateChildren() {
801: EventQueue.invokeLater(new Runnable() {
802: public void run() {
803: outlineRootChildren.updateKeys();
804: }
805: });
806: }
807: } // End of OutlineDesignProjectListener.
808:
809: private static class OutlineDesignContextListener implements
810: DesignContextListener {
811: private final OutlinePanel outlinePanel;
812:
813: public OutlineDesignContextListener(
814: OutlinePanel outlinePanel) {
815: this .outlinePanel = outlinePanel;
816: }
817:
818: public void contextActivated(DesignContext designContext) {
819: }
820:
821: public void contextDeactivated(DesignContext designContext) {
822: }
823:
824: public void contextChanged(DesignContext designContext) {
825: }
826:
827: public void beanCreated(DesignBean designBean) {
828: expandNodeForDesignBean(designBean);
829: }
830:
831: public void beanDeleted(DesignBean designBean) {
832: }
833:
834: public void beanMoved(DesignBean designBean,
835: DesignBean designBean0, Position position) {
836: }
837:
838: public void beanContextActivated(DesignBean designBean) {
839: }
840:
841: public void beanContextDeactivated(DesignBean designBean) {
842: }
843:
844: public void instanceNameChanged(DesignBean designBean,
845: String string) {
846: }
847:
848: public void beanChanged(DesignBean designBean) {
849: }
850:
851: public void propertyChanged(DesignProperty designProperty,
852: Object object) {
853: }
854:
855: public void eventChanged(DesignEvent designEvent) {
856: }
857:
858: private void expandNodeForDesignBean(
859: final DesignBean designBean) {
860: EventQueue.invokeLater(new Runnable() {
861: public void run() {
862: Node[] nodes = getNodesForBeans(new DesignBean[] { designBean });
863: initializeParentNodes(nodes);
864: if (areNodesUnderRoot(nodes, outlinePanel
865: .getExplorerManager().getRootContext())) {
866: outlinePanel.expandNodes(nodes);
867: }
868: }
869: });
870: }
871: } // End of OutlineDesignContextListener.
872:
873: /** Compares the auxiliary <code>DesignContext</code>s to order according
874: * request, session, application and none scope. */
875: private static class AuxiliaryContextComparator implements
876: Comparator<DesignContext> {
877:
878: public int compare(DesignContext dc1, DesignContext dc2) {
879: int scopeWeight1 = getScopeWeight(dc1);
880: int scopeWeight2 = getScopeWeight(dc2);
881:
882: return scopeWeight2 - scopeWeight1;
883: }
884:
885: private static int getScopeWeight(
886: DesignContext designContext) {
887: Object scope = designContext
888: .getContextData(Constants.ContextData.SCOPE);
889:
890: // XXX Missing designtime API, there shouldn't be used strings!
891: if ("request".equals(scope)) { // NOI18N
892: return 4;
893: } else if ("session".equals(scope)) { // NOI18N
894: return 3;
895: } else if ("application".equals(scope)) { // NOI18N
896: return 2;
897: } else if ("none".equals(scope)) { // NOI18N
898: return 1;
899: } else {
900: ErrorManager.getDefault().notify(
901: ErrorManager.INFORMATIONAL,
902: new IllegalStateException(
903: "The design context doesn't provide valid scope, designContext="
904: + designContext
905: + ", scope=" + scope)); // NOI18N
906: return 0;
907: }
908:
909: }
910: } // End of AuxiliaryContextComparator.
911:
912: } // End of OutlineRootChildren.
913:
914: private static void log(String message) {
915: Logger logger = getLogger();
916: logger.log(Level.INFO, message);
917: }
918:
919: private static Logger getLogger() {
920: return Logger.getLogger(OutlinePanel.class.getName());
921: }
922: }
|