KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > core > windows > model > SplitSubModel


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.core.windows.model;
21
22
23
24 import java.util.*;
25 import java.util.logging.Level JavaDoc;
26 import java.util.logging.Logger JavaDoc;
27 import org.netbeans.core.windows.*;
28
29
30 /**
31  * Sub-model of split (n-branch) tree, which represents structure
32  * of split components. It's used in ModesModel as representation
33  * of split modes, and also as a representation of editor area,
34  * which is in fact the same, just it is inside the enclosed splits.
35  *
36  * @author Peter Zavadsky
37  */

38 class SplitSubModel {
39
40     /** Parent model instance. */
41     protected final Model parentModel;
42     
43     /** Maps modes to nodes of this n-branch tree model. */
44     private final Map<ModeImpl, ModeNode> modes2nodes =
45             new WeakHashMap<ModeImpl, ModeNode>(20);
46     
47     /** Root <code>Node</code> which represents the split panes structure
48      * with modes as leaves. */

49     protected Node root;
50     
51     /** Debugging flag. */
52     private static final boolean DEBUG = Debug.isLoggable(SplitSubModel.class);
53
54     
55     /** Creates a new instance of SplitModel */
56     public SplitSubModel(Model parentModel) {
57         this.parentModel = parentModel;
58     }
59
60     
61     private ModeNode getModeNode(ModeImpl mode) {
62         synchronized(modes2nodes) {
63             ModeNode node = modes2nodes.get(mode);
64             if(node == null) {
65                 node = new ModeNode(mode);
66                 modes2nodes.put(mode, node);
67             }
68         
69             return node;
70         }
71     }
72     
73     public SplitConstraint[] getModelElementConstraints(ModelElement element) {
74         if(element instanceof Node) {
75             Node node = (Node)element;
76             if(!isInTree(node)) {
77                 return null;
78             }
79             return node.getNodeConstraints();
80         }
81         
82         return null;
83     }
84     
85     public SplitConstraint[] getModeConstraints(ModeImpl mode) {
86         ModeNode modeNode = getModeNode(mode);
87         return modeNode.getNodeConstraints();
88     }
89     
90     /**
91      * Find the side (LEFT/RIGHT/BOTTOM) where the TopComponent from the given
92      * mode should slide to. The position is deducted from mode's position relative
93      * to the editor mode in the split hierarchy.
94      *
95      * @param mode Mode
96      * @return The slide side for TopComponents from the given mode.
97      */

98     public String JavaDoc getSlideSideForMode( ModeImpl mode ) {
99         ModeNode modeNode = getModeNode( mode );
100         return getPositionRelativeToEditor( modeNode, modeNode.getParent() );
101     }
102     
103     /**
104      * Find recursively the position of the given Node relative to editor node.
105      *
106      * @param node The Node who's position is being compared with editor node's position.
107      * @param parent Node's split parent.
108      * @return LEFT/RIGHT/BOTTOM/TOP according to Node's position in the hierarchy.
109      */

110     private String JavaDoc getPositionRelativeToEditor( Node node, SplitNode parent ) {
111         if( null == parent )
112             return Constants.LEFT; //fallback - we're at the top level of the hierarchy
113

114         Node editorNode = getEditorChildNode( parent );
115         if( null != editorNode ) {
116             //the split parent contains a node or sub-tree with editor node
117
//so let's compare their positions
118
int orientation = parent.getOrientation();
119             int nodeIndex = parent.getChildIndex( node );
120             int editorIndex = parent.getChildIndex( editorNode );
121             if( orientation == Constants.VERTICAL ) {
122                 if( nodeIndex > editorIndex )
123                     return Constants.BOTTOM;
124                 return Constants.TOP; //TODO: is this really OK?
125
} else {
126                 if( nodeIndex < editorIndex )
127                     return Constants.LEFT;
128                 return Constants.RIGHT;
129             }
130         }
131         //try one level up in the tree hierarchy
132
return getPositionRelativeToEditor( parent, parent.getParent() );
133     }
134     
135     /**
136      * Find the child node that contains the editor node or the child that has the editor
137      * node in its sub-tree.
138      *
139      * @param split SplitNode searched for editor node.
140      * @return Node which is the editor node or which has the editor node in its sub-tree.
141      */

142     private Node getEditorChildNode( SplitNode split ) {
143         List<Node> children = split.getChildren();
144         for( Iterator<Node> i=children.iterator(); i.hasNext(); ) {
145             Node node = i.next();
146             if( node instanceof EditorSplitSubModel.EditorNode
147                 ||
148                 node instanceof SplitNode && null != getEditorChildNode( (SplitNode)node ) ) {
149                 return node;
150             }
151         }
152         return null;
153     }
154     
155     
156     /** Adds mode which is <code>Node</code>
157      * with specified constraints designating the path in model.
158      * <em>Note: It is important to know that adding of mode can affect the structure
159      * the way, it can change constraints of already added modes (they could
160      * be moved in that tree)</em> */

161     public boolean addMode(ModeImpl mode, SplitConstraint[] constraints) {
162         // PENDING do we support empty constraints?
163
if(mode == null || constraints == null) {
164             Logger.getLogger(SplitSubModel.class.getName()).log(Level.WARNING, null,
165                               new java.lang.IllegalArgumentException JavaDoc("Mode=" +
166                                                                      mode +
167                                                                      " constraints=" +
168                                                                      Arrays.toString(constraints)));
169             return false;
170         }
171
172         
173         Node modeNode = getModeNode(mode);
174         
175         if(DEBUG) {
176             debugLog(""); // NOI18N
177
debugLog(""); // NOI18N
178
debugLog("=========================================="); // NOI18N
179
debugLog("Adding mode to tree=" + mode); // NOI18N
180
debugLog("constraints=" + Arrays.asList(constraints)); // NOI18N
181
debugLog("modeNode=" + modeNode); // NOI18N
182
}
183
184         return addNodeToTree(modeNode, constraints);
185     }
186     
187     // XXX
188
public boolean addModeToSide(ModeImpl mode, ModeImpl attachMode, String JavaDoc side) {
189         if(mode == null || mode.getState() == Constants.MODE_STATE_SEPARATED) {
190             Logger.getLogger(SplitSubModel.class.getName()).log(Level.WARNING, null,
191                               new java.lang.IllegalArgumentException JavaDoc("Mode=" +
192                                                                      mode));
193             return false;
194         }
195
196         Node modeNode = getModeNode(mode);
197         Node attachModeNode = getModeNode(attachMode);
198         
199         if(DEBUG) {
200             debugLog(""); // NOI18N
201
debugLog(""); // NOI18N
202
debugLog("=========================================="); // NOI18N
203
debugLog("Adding mode to between=" + mode); // NOI18N
204
debugLog("attachMode=" + attachMode); // NOI18N
205
debugLog("side=" + side); // NOI18N
206
}
207
208         return addNodeToTreeToSide(modeNode, attachModeNode, side);
209     }
210
211     public boolean addModeToSideRoot(ModeImpl mode, String JavaDoc side) {
212         if(mode == null || mode.getState() == Constants.MODE_STATE_SEPARATED) {
213             Logger.getLogger(SplitSubModel.class.getName()).log(Level.WARNING, null,
214                               new java.lang.IllegalArgumentException JavaDoc("Mode=" +
215                                                                      mode));
216             return false;
217         }
218
219         Node modeNode = getModeNode(mode);
220         
221         if(DEBUG) {
222             debugLog(""); // NOI18N
223
debugLog(""); // NOI18N
224
debugLog("=========================================="); // NOI18N
225
debugLog("Adding mode to root's side=" + mode); // NOI18N
226
debugLog("side=" + side); // NOI18N
227
}
228
229         return addNodeToTreeToSide(modeNode, root, side);
230     }
231
232     // XXX
233
public boolean addModeAround(ModeImpl mode, String JavaDoc side) {
234         if(mode == null || mode.getState() == Constants.MODE_STATE_SEPARATED) {
235             Logger.getLogger(SplitSubModel.class.getName()).log(Level.WARNING, null,
236                               new java.lang.IllegalArgumentException JavaDoc("Mode=" +
237                                                                      mode));
238             return false;
239         }
240
241         Node modeNode = getModeNode(mode);
242         
243         if(DEBUG) {
244             debugLog(""); // NOI18N
245
debugLog(""); // NOI18N
246
debugLog("=========================================="); // NOI18N
247
debugLog("Adding mode to around=" + mode); // NOI18N
248
debugLog("side=" + side); // NOI18N
249
}
250
251         return addNodeToTreeAround(modeNode, side);
252     }
253     
254     // XXX
255
public boolean addModeAroundEditor(ModeImpl mode, String JavaDoc side) {
256         if(mode == null || mode.getState() == Constants.MODE_STATE_SEPARATED) {
257             Logger.getLogger(SplitSubModel.class.getName()).log(Level.WARNING, null,
258                               new java.lang.IllegalArgumentException JavaDoc("Mode=" +
259                                                                      mode));
260             return false;
261         }
262
263         Node modeNode = getModeNode(mode);
264         
265         if(DEBUG) {
266             debugLog(""); // NOI18N
267
debugLog(""); // NOI18N
268
debugLog("=========================================="); // NOI18N
269
debugLog("Adding mode to around=" + mode); // NOI18N
270
debugLog("side=" + side); // NOI18N
271
}
272
273         return addNodeToTreeAroundEditor(modeNode, side);
274     }
275
276     private boolean isInTree(Node descendant) {
277         if(root == null) {
278             return false;
279         }
280         
281         if(descendant == root) {
282             return true;
283         }
284         
285         Node parent = descendant.getParent();
286         while(parent != null) {
287             if(parent == root) {
288                 return true;
289             }
290             parent = parent.getParent();
291         }
292         
293         return false;
294     }
295
296     /** Adds node into the tree structure if there isn't yet. */
297     protected boolean addNodeToTree(Node addingNode, SplitConstraint[] constraints) {
298         if(isInTree(addingNode)) {
299             return false;
300         }
301         
302         // Find starting split.
303
SplitNode splitNode;
304         // First solve root.
305
if(root == null) {
306             if(constraints.length == 0) {
307                 root = addingNode;
308                 return true;
309             }
310             
311             // There is nothing, create split.
312
splitNode = new SplitNode(constraints[0].orientation);
313             root = splitNode;
314         } else if(root instanceof SplitNode) {
315             splitNode = (SplitNode)root;
316         } else {
317             // All other nodes (ModeNode, and EditorNode in subclass).
318
splitNode = new SplitNode(0); // Default orientation when splitting root?
319
splitNode.setChildAt(-1, 0.5D, root);
320             root = splitNode;
321         }
322
323         // Traverse the structure.
324
for(int level = 0; level < constraints.length; level++) {
325             int orientation = constraints[level].orientation;
326
327             // First solve orientation
328
if(orientation != splitNode.getOrientation()) {
329                 // Orientation doesn't fit, create new split.
330
SplitNode newSplit = new SplitNode(orientation);
331                 if(splitNode == root) {
332                     // Creating new branch.
333
newSplit.setChildAt(-1, 0.5D, splitNode);
334                     root = newSplit;
335                 } else {
336                     SplitNode parent = splitNode.getParent();
337                     int oldIndex = parent.getChildIndex(splitNode);
338                     double oldSplitWeight = parent.getChildSplitWeight(splitNode);
339                     // move the original split as child of new one and newSplit put under parent.
340
parent.removeChild(splitNode);
341
342                     // Creating new branch.
343
newSplit.setChildAt(-1, 0.5D, splitNode);
344                     parent.setChildAt(oldIndex, oldSplitWeight, newSplit);
345                 }
346                 
347                 splitNode = newSplit;
348             }
349             
350             // Then solve next position (together with splitWeight).
351
// But if this is the last iteration, don't do anything the adding will be done after loop.
352
if(level < constraints.length - 1) {
353                 int index = constraints[level].index;
354                 double splitWeight = constraints[level].splitWeight;
355
356                 Node child = splitNode.getChildAt(index);
357                 if(child instanceof SplitNode) {
358                     // Traverse to split.
359
// Possible wrong orientation solves next iteration (see above).
360
splitNode = (SplitNode)child;
361                 } else {
362                     // There is some leaf node or null, just create new split that way.
363
SplitNode newSplit = new SplitNode(constraints[level + 1].orientation);
364                     splitNode.setChildAt(index, splitWeight, newSplit);
365                     splitNode = newSplit;
366                 }
367             }
368         }
369         
370         // Finally add the node into tree.
371
if(constraints.length == 0) {
372             splitNode.setChildAt(-1, 0.5D, addingNode);
373         } else {
374             splitNode.setChildAt(
375                 constraints[constraints.length - 1].index,
376                 constraints[constraints.length - 1].splitWeight,
377                 addingNode
378             );
379         }
380         
381         verifyNode(root);
382         
383         return true;
384     }
385
386     // XXX
387
private boolean addNodeToTreeToSide(Node addingNode, Node attachNode, String JavaDoc side) {
388         if(isInTree(addingNode)) {
389             return false;
390         }
391
392         if(!isInTree(attachNode)) {
393             return false;
394         }
395         
396         if(DEBUG) {
397             debugLog(""); // NOI18N
398
debugLog("Inserting to side="+side); // NOI18N
399
}
400
401         // Update
402
if(attachNode == root) {
403             int addingIndex = (side == Constants.TOP || side == Constants.LEFT) ? 0 : -1;
404             int oldIndex = addingIndex == 0 ? -1 : 0;
405             // Create new branch.
406
int orientation = (side == Constants.TOP || side == Constants.BOTTOM) ? Constants.VERTICAL : Constants.HORIZONTAL;
407             SplitNode newSplit = new SplitNode(orientation);
408             newSplit.setChildAt(addingIndex, Constants.DROP_TO_SIDE_RATIO, addingNode);
409             newSplit.setChildAt(oldIndex, 1D - Constants.DROP_TO_SIDE_RATIO, attachNode);
410             root = newSplit;
411         } else {
412             SplitNode parent = attachNode.getParent();
413             if(parent == null) {
414                 return false;
415             }
416
417             int attachIndex = parent.getChildIndex(attachNode);
418             double attachWeight = parent.getChildSplitWeight(attachNode);
419             // Create new branch.
420
int orientation = (side == Constants.TOP || side == Constants.BOTTOM) ? Constants.VERTICAL : Constants.HORIZONTAL;
421             if( orientation == parent.getOrientation() ) {
422                 //reuse existing split
423
if( side == Constants.BOTTOM || side == Constants.RIGHT )
424                     attachIndex++;
425                 parent.setChildAt( attachIndex, Constants.DROP_TO_SIDE_RATIO, addingNode );
426             } else {
427                 //split orientation does not match, create a new sub-split
428
SplitNode newSplit = new SplitNode(orientation);
429                 parent.removeChild(attachNode);
430                 int addingIndex = (side == Constants.TOP || side == Constants.LEFT) ? 0 : -1;
431                 int oldIndex = addingIndex == 0 ? -1 : 0;
432                 newSplit.setChildAt(addingIndex, Constants.DROP_TO_SIDE_RATIO, addingNode);
433                 newSplit.setChildAt(oldIndex, 1D - Constants.DROP_TO_SIDE_RATIO, attachNode);
434                 parent.setChildAt(attachIndex, attachWeight, newSplit);
435             }
436         }
437         
438         return true;
439     }
440     
441     // XXX
442
private boolean addNodeToTreeAround(Node addingNode, String JavaDoc side) {
443         Node top = root;
444         
445         if(top instanceof SplitNode) {
446             SplitNode parent = (SplitNode)top;
447             
448             if((parent.getOrientation() == Constants.VERTICAL
449                 && (side == Constants.TOP || side == Constants.BOTTOM))
450             || (parent.getOrientation() == Constants.HORIZONTAL
451                 && (side == Constants.LEFT || side == Constants.RIGHT))) {
452                     // Has the needed orientation (no new branch).
453
double splitWeights = 0D;
454                 for(Iterator it = parent.getChildren().iterator(); it.hasNext(); ) {
455                     Node next = (Node)it.next();
456                     splitWeights += parent.getChildSplitWeight(next);
457                 }
458
459                 double addingSplitWeight = splitWeights * Constants.DROP_AROUND_RATIO;
460                 int index = (side == Constants.TOP || side == Constants.LEFT) ? 0 : -1;
461                 
462                 parent.setChildAt(index, addingSplitWeight, addingNode);
463                 if(addingSplitWeight > 1D) {
464                     double ratio = 1D/addingSplitWeight;
465                     parent.normalizeWeights(ratio);
466                 }
467                 return true;
468             } else {
469                 // Create new branch.
470
int orientation = (side == Constants.TOP || side == Constants.BOTTOM) ? Constants.VERTICAL : Constants.HORIZONTAL;
471                 SplitNode newSplit = new SplitNode(orientation);
472                 int addingIndex = (side == Constants.TOP || side == Constants.LEFT) ? 0 : -1;
473                 int oldIndex = addingIndex == 0 ? -1 : 0;
474                 newSplit.setChildAt(addingIndex, Constants.DROP_AROUND_RATIO, addingNode);
475                 newSplit.setChildAt(oldIndex, 1D - Constants.DROP_AROUND_RATIO, parent);
476                 root = newSplit;
477                 return true;
478             }
479         }
480         
481         SplitConstraint[] newConstraints; // Adding constraint to new mode.
482
if(side == Constants.TOP) {
483             newConstraints = new SplitConstraint[] {new SplitConstraint(Constants.VERTICAL, 0, Constants.DROP_AROUND_RATIO)};
484         } else if(side == Constants.BOTTOM) {
485             newConstraints = new SplitConstraint[] {new SplitConstraint(Constants.VERTICAL, -1, Constants.DROP_AROUND_RATIO)};
486         } else if(side == Constants.LEFT) {
487             newConstraints = new SplitConstraint[] {new SplitConstraint(Constants.HORIZONTAL, 0, Constants.DROP_AROUND_RATIO)};
488         } else if(side == Constants.RIGHT) {
489             newConstraints = new SplitConstraint[] {new SplitConstraint(Constants.HORIZONTAL, -1, Constants.DROP_AROUND_RATIO)};
490         } else {
491             // XXX wrong side
492
return false;
493         }
494
495         return addNodeToTree(addingNode, newConstraints);
496     }
497     
498     // XXX
499
protected boolean addNodeToTreeAroundEditor(Node addingNode, String JavaDoc side) {
500         // XXX No op here, it's impelmented in editor split subclass.
501
return false;
502     }
503
504     
505     /** Removes specified mode as <code>Node</code> from this model. */
506     public boolean removeMode(ModeImpl mode) {
507         if(mode == null) {
508             throw new NullPointerException JavaDoc("Cannot remove null mode!");
509         }
510
511         return removeNodeFromTree(getModeNode(mode));
512     }
513
514     /** Removes node from this tree. */
515     protected boolean removeNodeFromTree(Node node) {
516         if(!isInTree(node)) {
517             return false;
518         }
519         
520         SplitNode parent = node.getParent();
521         if(parent == null && node != root) {
522             // PENDING incorrect state?
523
return false;
524         }
525
526         if(node == root) {
527             root = null;
528         } else {
529             parent.removeChild(node);
530             
531             List children = parent.getChildren();
532
533             if(children.isEmpty()) {
534                 // Parent split is empty, remove it too.
535
if(parent == root) {
536                     root = null;
537                 } else {
538                     SplitNode grandParent = parent.getParent();
539                     grandParent.removeChild(parent);
540                 }
541             } else if( children.size() == 1 ) {
542                 //the parent has only one child left - move the orphan to its grand-parent
543
Node orphan = (Node)children.get( 0 );
544                 if( parent == root ) {
545                     orphan.setParent( null );
546                     root = orphan;
547                 } else {
548                     SplitNode grandParent = parent.getParent();
549                     int index = grandParent.getChildIndex( parent );
550                     double weight = grandParent.getChildSplitWeight( parent );
551                     grandParent.removeChild( parent );
552                     grandParent.setChildAt( index, weight, orphan );
553                 }
554             }
555         }
556
557         verifyNode(root);
558         
559         return true;
560     }
561
562     // PENDING Currently verifies parent-child links only.
563
/** Verifies the tree structure. */
564     private /*static*/ void verifyNode(Node node) {
565         if(node instanceof SplitNode) {
566             SplitNode splitNode = (SplitNode)node;
567             for(Iterator it = splitNode.getChildren().iterator(); it.hasNext(); ) {
568                 Node child = (Node)it.next();
569
570                 if(child.getParent() != splitNode) {
571                     Logger.getLogger(SplitSubModel.class.getName()).log(Level.WARNING, null,
572                                       new java.lang.IllegalStateException JavaDoc("Node->" +
573                                                                           child +
574                                                                           " has wrong parent->" +
575                                                                           child.getParent() +
576                                                                           " is has to be->" +
577                                                                           splitNode +
578                                                                           " \nModel: " +
579                                                                           toString()));
580                     // Repair model.
581
child.setParent(splitNode);
582                 }
583
584                 verifyNode(child);
585             }
586         }
587     }
588     
589     /** Resets model. Removes all nodes. */
590     public void reset() {
591         detachNodes(root);
592         root = null;
593     }
594
595     /** Detaches nodes tree from itself. */
596     private static void detachNodes(Node node) {
597         if(node instanceof SplitNode) {
598             SplitNode splitNode = (SplitNode)node;
599             
600             for(Iterator it = splitNode.getChildren().iterator(); it.hasNext(); ) {
601                 Node child = (Node)it.next();
602                 splitNode.removeChild(child);
603                 detachNodes(child);
604             }
605         }
606     }
607
608     public boolean setSplitWeights( ModelElement[] snapshots, double[] splitWeights ) {
609         if( 0 == snapshots.length )
610             return false;
611         for( int i=0; i<snapshots.length; i++ ) {
612             Node node = (Node)snapshots[i];
613             if( null == node || null == node.getParent() )
614                 return false;
615         }
616         Node firstNode = (Node)snapshots[0];
617         SplitNode parent = firstNode.getParent();
618         
619         if(parent == null || !isInTree(parent)) {
620             return false;
621         }
622        
623         boolean res = true;
624         for( int i=0; i<snapshots.length; i++ ) {
625             Node node = (Node)snapshots[ i ];
626             double weight = splitWeights[ i ];
627             SplitNode parentNode = node.getParent();
628             if( null == parentNode || !isInTree( parentNode ) ) {
629                 res = false;
630             } else {
631                 parentNode.setChildSplitWeight( node, weight );
632             }
633         }
634         
635         return res;
636     }
637     
638     /** */
639     public ModeStructureSnapshot.ElementSnapshot createSplitSnapshot() {
640         return root == null ? null : root.createSnapshot();
641     }
642     
643     public Set<ModeStructureSnapshot.ModeSnapshot> createSeparateSnapshots() {
644         return findSeparateModeSnapshots(root);
645     }
646     
647     private Set<ModeStructureSnapshot.ModeSnapshot> findSeparateModeSnapshots(Node node) {
648         Set<ModeStructureSnapshot.ModeSnapshot> s =
649                 new HashSet<ModeStructureSnapshot.ModeSnapshot>();
650         if(node instanceof ModeNode) {
651             ModeNode modeNode = (ModeNode)node;
652             if(modeNode.isVisibleSeparate()) {
653                 s.add((ModeStructureSnapshot.ModeSnapshot)modeNode.createSnapshot());
654             }
655         } else if(node instanceof SplitNode) {
656             SplitNode splitNode = (SplitNode)node;
657             for(Iterator it = splitNode.getChildren().iterator(); it.hasNext(); ) {
658                 Node child = (Node)it.next();
659                 s.addAll(findSeparateModeSnapshots(child));
660             }
661         }
662         
663         return s;
664     }
665     
666     /** Overrides superclass method, adds dump of this model tree. */
667     public String JavaDoc toString() {
668         // PENDING Better method name, some refinements possible of the dump.
669
return dumpNode(root, 0, null);
670     }
671     
672     /** Recursively dump tree content */
673     private static String JavaDoc dumpNode(Node node, int ind, String JavaDoc state) {
674         ind++;
675         if (node == null) {
676             return "NULL NODE\n";
677         }
678         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
679         if(state == null) {
680             buffer.append("\n");
681         }
682         StringBuffer JavaDoc sb = getOffset(ind);
683         if(node instanceof ModeNode) {
684             buffer.append(sb);
685             buffer.append("<mode-node"); // NOI18N
686
buffer.append(" [" + Integer.toHexString(System.identityHashCode(node)) + "]"); // NOI18N
687
buffer.append(" index=\""); // NOI18N
688
if (node.getParent() != null) {
689                 buffer.append(node.getParent().getChildIndex(node));
690             }
691             buffer.append(" splitWeight="); // NOI18N
692
if (node.getParent() != null) {
693                 buffer.append(node.getParent().getChildSplitWeight(node));
694             }
695             buffer.append("\""); // NOI18N
696
buffer.append(" state=\""); // NOI18N
697
buffer.append(state);
698             buffer.append("\""); // NOI18N
699
buffer.append(" name=\"" + ((ModeNode)node).getMode().getName() + "\""); // NOI18N
700
buffer.append(" parent="); // NOI18N
701
buffer.append(node.getParent() == null ? null : "["+Integer.toHexString(node.getParent().hashCode())+"]");
702             buffer.append(" constraints=\'" + java.util.Arrays.asList(node.getNodeConstraints()) + "\"");
703             buffer.append("</mode-node>\n"); // NOI18N
704
} else if(node instanceof SplitNode) {
705             buffer.append(sb);
706             buffer.append("<split-node"); // NOI18N
707
buffer.append(" [" + Integer.toHexString(System.identityHashCode(node)) + "]"); // NOI18N
708
buffer.append(" index=\""); // NOI18N
709
if (node.getParent() != null) {
710                 buffer.append(node.getParent().getChildIndex(node));
711             }
712             buffer.append(" splitWeight="); // NOI18N
713
if (node.getParent() != null) {
714                 buffer.append(node.getParent().getChildSplitWeight(node));
715             }
716             buffer.append("\""); // NOI18N
717
SplitNode split = (SplitNode) node;
718             buffer.append(" state=\""); // NOI18N
719
buffer.append(state);
720             buffer.append("\" orientation=\""); // NOI18N
721
buffer.append(split.getOrientation());
722             buffer.append("\">\n");
723             int j = 0;
724             for(Iterator it = split.getChildren().iterator(); it.hasNext(); j++ ) {
725                 Node child = (Node)it.next();
726                 buffer.append(dumpNode(child, ind, "child["+j+"]"));
727             }
728             buffer.append(sb);
729             buffer.append("</split-node>\n"); // NOI18N
730
} else {
731             // supposing it's editor mode.
732
buffer.append(sb);
733             buffer.append("<editor-node"); // NOI18N
734
buffer.append(" [" + Integer.toHexString(System.identityHashCode(node)) + "]"); // NOI18N
735
buffer.append(" index=\""); // NOI18N
736
if (node.getParent() != null) {
737                 buffer.append(node.getParent().getChildIndex(node));
738             }
739             buffer.append("\""); // NOI18N
740
buffer.append(" splitWeight="); // NOI18N
741
if (node.getParent() != null) {
742                 buffer.append(node.getParent().getChildSplitWeight(node));
743             }
744             buffer.append(" parent="); // NOI18N
745
buffer.append(node.getParent() == null ? null : "["+Integer.toHexString(node.getParent().hashCode())+"]");
746             buffer.append("</editor-node>\n"); // NOI18N
747
}
748         return buffer.toString();
749     }
750     
751     private static StringBuffer JavaDoc getOffset (int ind) {
752         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
753         for (int i = 0; i < ind - 1; i++) {
754             sb.append("\t"); // NOI18N
755
}
756         return sb;
757     }
758
759     ///////////////////////////////
760
// Controller updates >>
761

762     public ModeImpl getModeForOriginator(ModelElement originator) {
763         if(originator instanceof ModeNode) {
764             return ((ModeNode)originator).getMode();
765         }
766         
767         return null;
768     }
769     
770     // Controller updates <<
771
///////////////////////////////
772

773     private static void debugLog(String JavaDoc message) {
774         Debug.log(SplitSubModel.class, message);
775     }
776     
777     ////////////////////////////////////////
778
/// Nodes of this tree model
779
////////////////////////////////////////
780
/** Class representing one node in SplitSubModel. */
781     protected static abstract class Node implements ModelElement {
782         /** Reference to parent node. */
783         private SplitNode parent;
784
785         /** Creates a new instance of TreeNode. */
786         public Node() {
787         }
788
789         /** Overrides superclass method, adds info about parent node. */
790         public String JavaDoc toString() {
791             return super.toString()
792                 + "[parent=" + (parent == null // NOI18N
793
? null
794                     : (parent.getClass() + "@" // NOI18N
795
+ Integer.toHexString(parent.hashCode())))
796                 + "]"; // NOI18N
797
}
798
799         /** Setter of parent property. */
800         public void setParent(SplitNode parent) {
801             if(this.parent == parent) {
802                 return;
803             }
804
805             this.parent = parent;
806         }
807
808         /** Getter of parent property. */
809         public SplitNode getParent() {
810             return parent;
811         }
812
813         public abstract double getResizeWeight();
814         
815         /** Gets constraints of this <code>Node</code>, designating
816          * the path in the model */

817         public SplitConstraint[] getNodeConstraints() {
818             Node node = this;
819             List<SplitConstraint> conList = new ArrayList<SplitConstraint>(5);
820             do {
821                 SplitConstraint item = getConstraintForNode(node);
822                 if(item != null) {
823                     conList.add(item);
824                 }
825                 
826                 node = node.getParent();
827             } while(node != null);
828
829             Collections.reverse(conList);
830             return conList.toArray(new SplitConstraint[0]);
831         }
832
833         /** Gets constraint of this <code>Node</code> from parent. */
834         private static SplitConstraint getConstraintForNode(Node node) {
835             SplitNode parent = node.getParent();
836             if(parent != null) {
837                 return new SplitConstraint(
838                     parent.getOrientation(),
839                     parent.getChildIndex(node),
840                     parent.getChildSplitWeight(node)
841                 );
842             }
843
844             return null;
845         }
846         
847         //////////////////////////////////////////////////////////////////////////////
848
//////////////////////////////////////////////////////////////////////////////
849

850         /** Indicates whether component represented by this node is visible or not. */
851         public boolean isVisibleInSplit() {
852             return false;
853         }
854
855         /** Indicates whether there is at least one visible descendant. */
856         public boolean hasVisibleDescendant() {
857             return isVisibleInSplit();
858         }
859
860         /** Creates snapshot of this node. */
861         public abstract ModeStructureSnapshot.ElementSnapshot createSnapshot();
862         
863     } // End of nested Node class.
864

865     
866     /** Class representing one split in SplitSubModel. The split is n-branched, i.e.
867      * it can have more than two children. */

868     protected static class SplitNode extends Node {
869
870         /** Constraint of first node (VERTICAL or HORIZONTAL). */
871         private final int orientation;
872         
873         // XXX some better structure needed? List is not enough since the indices may
874
// not be continuous (like 0, 1, 2, 3) but even like (0, 3, 8, 9).
875
/** Maps index to child node, while keeps ordering according to keys (indices). */
876         private final TreeMap<Integer JavaDoc, Node> index2child = new TreeMap<Integer JavaDoc, Node>();
877
878         /** Maps child node to its splitWeight. */
879         private final Map<Node, Double JavaDoc> child2splitWeight = new HashMap<Node, Double JavaDoc>();
880
881         /** Creates a new instance of SplitNode */
882         public SplitNode(int orientation) {
883             this.orientation = orientation;
884         }
885
886
887         /** Overrides superclass method. Adds info about dividePos, orientation,
888          * first and second sub-nodes. */

889         public String JavaDoc toString() {
890             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
891             sb.append(super.toString());
892             
893             for(Iterator it = index2child.keySet().iterator(); it.hasNext(); ) {
894                 Integer JavaDoc index = (Integer JavaDoc)it.next();
895                 Node child = (Node)index2child.get(index);
896                 sb.append("child[" + index.intValue() +"]=" + child.getClass()
897                     + "@" + Integer.toHexString(child.hashCode())); // NOI18N
898
}
899             
900             return sb.toString();
901         }
902
903         /** Getter of orientation property. */
904         public int getOrientation() {
905             return orientation;
906         }
907         
908         public void setChildAt(int index, double splitWeight, Node child) {
909             // XXX -1 means, put it at the end.
910
if(index == -1) {
911                 if(index2child.isEmpty()) {
912                     index = 0;
913                 } else {
914                     index = ((Integer JavaDoc)index2child.lastKey()).intValue() + 1;
915                 }
916             }
917             
918             Integer JavaDoc ind = Integer.valueOf(index);
919             
920             Node oldChild = (Node)index2child.get(ind);
921             // There are some other nodes at the index, shift them first.
922
for(int i = ind.intValue() + 1; oldChild != null; i++) {
923                 oldChild = (Node)index2child.put(Integer.valueOf(i), oldChild);
924             }
925
926             // Finally add the new node.
927
index2child.put(ind, child);
928             // Also add it to child2splitWeight map
929
setChildSplitWeightImpl(child, splitWeight);
930             child.setParent(this);
931             
932             verifyChildren();
933         }
934         
935         public Node getChildAt(int index) {
936             return (Node)index2child.get(Integer.valueOf(index));
937         }
938         
939         private void verifyChildren() {
940             for(Iterator it = index2child.values().iterator(); it.hasNext(); ) {
941                 Node child = (Node)it.next();
942                 if(child.getParent() != this) {
943                     Logger.getLogger(SplitSubModel.class.getName()).log(Level.WARNING, null,
944                                       new java.lang.IllegalStateException JavaDoc("Node " +
945                                                                           child +
946                                                                           " is a child in split " +
947                                                                           this +
948                                                                           " but his parent is " +
949                                                                           child.getParent() +
950                                                                           ". Repairing")); // NOI18N
951
// Repair.
952
child.setParent(this);
953                 }
954             }
955         }
956
957         public double getChildSplitWeight(Node child) {
958             Double JavaDoc db = child2splitWeight.get(child);
959             if(db != null) {
960                 return db.doubleValue();
961             }
962             
963             return -1D;
964         }
965         
966         public void setChildSplitWeight(Node child, double weight) {
967             if(child == null || !child2splitWeight.keySet().contains(child)) {
968                 return;
969             }
970             
971             setChildSplitWeightImpl(child, weight);
972         }
973         
974         private void setChildSplitWeightImpl(Node child, double weight) {
975             child2splitWeight.put(child, Double.valueOf(weight));
976         }
977         
978         
979         private void normalizeWeights(double ratio) {
980             for(Map.Entry<Node, Double JavaDoc> entry: child2splitWeight.entrySet()) {
981                 double w = entry.getValue().doubleValue();
982                 w = ratio * w;
983                 entry.setValue(Double.valueOf(w));
984             }
985         }
986         
987         public int getChildIndex(Node child) {
988             for(Iterator it = index2child.keySet().iterator(); it.hasNext(); ) {
989                 Object JavaDoc key = it.next();
990                 if(child == index2child.get(key)) {
991                     return ((Integer JavaDoc)key).intValue();
992                 }
993             }
994             
995             return -1;
996         }
997         
998         public List<Node> getChildren() {
999             return new ArrayList<Node>(index2child.values());
1000        }
1001        
1002        public List<Node> getVisibleChildren() {
1003            List<Node> l = getChildren();
1004            for(Iterator<Node> it = l.iterator(); it.hasNext(); ) {
1005                Node node = it.next();
1006                if(!node.hasVisibleDescendant()) {
1007                    it.remove();
1008                }
1009            }
1010            
1011            return l;
1012        }
1013        
1014        protected boolean removeChild(Node child) {
1015            boolean result = index2child.values().remove(child);
1016            child2splitWeight.remove(child);
1017            child.setParent(null);
1018            
1019            return result;
1020        }
1021
1022        /** Indicates whether component represented by this node is visible or not. */
1023        public boolean isVisibleInSplit() {
1024            int count = 0;
1025            for(Iterator it = index2child.values().iterator(); it.hasNext(); ) {
1026                Node node = (Node)it.next();
1027                if(node.hasVisibleDescendant()) {
1028                    count++;
1029                    // At leas two are needed so the split is showing.
1030
if(count >= 2) {
1031                        return true;
1032                    }
1033                }
1034            }
1035            
1036            return false;
1037        }
1038
1039        /** Indicates whether there is at least one visible descendant. */
1040        public boolean hasVisibleDescendant() {
1041            for(Iterator it = index2child.values().iterator(); it.hasNext(); ) {
1042                Node node = (Node)it.next();
1043                if(node.hasVisibleDescendant()) {
1044                    return true;
1045                }
1046            }
1047            
1048            return false;
1049        }
1050        
1051        public double getResizeWeight() {
1052            List children = getVisibleChildren();
1053            double max = 0D;
1054            for(Iterator it = children.iterator(); it.hasNext(); ) {
1055                double resizeWeight = ((Node)it.next()).getResizeWeight();
1056                max = Math.max(max, resizeWeight);
1057            }
1058            
1059            return max;
1060        }
1061
1062        public ModeStructureSnapshot.ElementSnapshot createSnapshot() {
1063            List<ModeStructureSnapshot.ElementSnapshot> childSnapshots =
1064                    new ArrayList<ModeStructureSnapshot.ElementSnapshot>();
1065            Map<ModeStructureSnapshot.ElementSnapshot,Double JavaDoc> childSnapshot2splitWeight =
1066                    new HashMap<ModeStructureSnapshot.ElementSnapshot, Double JavaDoc>();
1067            for(Node child: getChildren()) {
1068                ModeStructureSnapshot.ElementSnapshot childSnapshot = child.createSnapshot();
1069                childSnapshots.add(childSnapshot);
1070                childSnapshot2splitWeight.put(childSnapshot, child2splitWeight.get(child));
1071            }
1072            
1073            ModeStructureSnapshot.SplitSnapshot splitSnapshot = new ModeStructureSnapshot.SplitSnapshot(this, null,
1074                getOrientation(), childSnapshots, childSnapshot2splitWeight, getResizeWeight());
1075            
1076            // Set parent for children.
1077
for(ModeStructureSnapshot.ElementSnapshot snapshot: childSnapshots) {
1078                snapshot.setParent(splitSnapshot);
1079            }
1080            
1081            return splitSnapshot;
1082        }
1083    } // End of nested SplitNode class.
1084

1085
1086    /** Class representing leaf node in SplitSubModel which corresponds to Mode. */
1087    protected static class ModeNode extends Node {
1088
1089        private final ModeImpl mode;
1090        
1091
1092        /** Creates a new instance of ModeNode */
1093        public ModeNode(ModeImpl mode) {
1094            this.mode = mode;
1095        }
1096
1097        public ModeImpl getMode() {
1098            return mode;
1099        }
1100
1101        public boolean isVisibleInSplit() {
1102            if(mode.getOpenedTopComponents().isEmpty()) {
1103                return false;
1104            }
1105
1106            if(mode.getState() == Constants.MODE_STATE_SEPARATED) {
1107                return false;
1108            }
1109            
1110            if(mode.getKind() == Constants.MODE_KIND_EDITOR ) {
1111                WindowManagerImpl wm = WindowManagerImpl.getInstance();
1112                if( null != wm.getEditorMaximizedMode() && wm.getEditorMaximizedMode() != mode )
1113                    return false;
1114            }
1115
1116            return true;
1117        }
1118        
1119        public boolean isVisibleSeparate() {
1120            if(mode.getOpenedTopComponents().isEmpty()) {
1121                return false;
1122            }
1123            
1124            if(mode.getState() == Constants.MODE_STATE_JOINED) {
1125                return false;
1126            }
1127            
1128            return true;
1129        }
1130        
1131        public double getResizeWeight() {
1132            return 0D;
1133        }
1134        
1135        public ModeStructureSnapshot.ElementSnapshot createSnapshot() {
1136            return new ModeStructureSnapshot.ModeSnapshot(this, null, mode, getResizeWeight());
1137        }
1138    } // End of nested ModeNode class.
1139

1140
1141}
1142
1143
Popular Tags