KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > gvt > CompositeGraphicsNode


1 /*
2
3    Copyright 2000-2003 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17  */

18 package org.apache.batik.gvt;
19
20 import java.awt.Graphics2D JavaDoc;
21 import java.awt.Rectangle JavaDoc;
22 import java.awt.Shape JavaDoc;
23 import java.awt.geom.AffineTransform JavaDoc;
24 import java.awt.geom.GeneralPath JavaDoc;
25 import java.awt.geom.Point2D JavaDoc;
26 import java.awt.geom.Rectangle2D JavaDoc;
27 import java.util.Collection JavaDoc;
28 import java.util.ConcurrentModificationException JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.List JavaDoc;
31 import java.util.ListIterator JavaDoc;
32 import java.util.NoSuchElementException JavaDoc;
33
34 import org.apache.batik.util.HaltingThread;
35
36 /**
37  * A CompositeGraphicsNode is a graphics node that can contain graphics nodes.
38  *
39  * @author <a HREF="mailto:Thierry.Kormann@sophia.inria.fr">Thierry Kormann</a>
40  * @version $Id: CompositeGraphicsNode.java,v 1.44 2005/03/27 08:58:34 cam Exp $
41  */

42 public class CompositeGraphicsNode extends AbstractGraphicsNode
43     implements List JavaDoc {
44
45     public static final Rectangle2D JavaDoc VIEWPORT = new Rectangle JavaDoc();
46     public static final Rectangle2D JavaDoc NULL_RECT = new Rectangle JavaDoc();
47
48     /**
49      * The children of this composite graphics node.
50      */

51     protected GraphicsNode [] children;
52
53     /**
54      * The number of children of this composite graphics node.
55      */

56     protected int count;
57
58     /**
59      * The number of times the children list has been structurally modified.
60      */

61     protected int modCount;
62
63     /**
64      * This flag indicates if this node has BackgroundEnable = 'new'.
65      * If so traversal of the gvt tree can halt here.
66      */

67     protected Rectangle2D JavaDoc backgroundEnableRgn = null;
68
69     /**
70      * Internal Cache: Geometry bounds for this node, not taking into
71      * account any of its children rendering attributes into account
72      */

73     private Rectangle2D JavaDoc geometryBounds;
74
75     /**
76      * Internal Cache: Primitive bounds.
77      */

78     private Rectangle2D JavaDoc primitiveBounds;
79
80     /**
81      * Internal Cache: Sensitive bounds.
82      */

83     private Rectangle2D JavaDoc sensitiveBounds;
84
85     /**
86      * Internal Cache: the outline.
87      */

88     private Shape JavaDoc outline;
89
90     /**
91      * Constructs a new empty <tt>CompositeGraphicsNode</tt>.
92      */

93     public CompositeGraphicsNode() {}
94
95     //
96
// Structural methods
97
//
98

99     /**
100      * Returns the list of children.
101      */

102     public List JavaDoc getChildren() {
103         return this;
104     }
105
106     /**
107      * Sets the enable background property to the specified rectangle.
108      *
109      * @param bgRgn the region that defines the background enable property
110      */

111     public void setBackgroundEnable(Rectangle2D JavaDoc bgRgn) {
112         backgroundEnableRgn = bgRgn;
113     }
114
115     /**
116      * Returns the region defining the background enable property.
117      */

118     public Rectangle2D JavaDoc getBackgroundEnable() {
119         return backgroundEnableRgn;
120     }
121
122     /**
123      * Sets if this node is visible or not depending on the specified value.
124      * Don't fire a graphicsNodeChange event because this doesn't really
125      * effect us (it effects our children through CSS inheritence).
126      *
127      * @param isVisible If true this node is visible
128      */

129     public void setVisible(boolean isVisible) {
130         // fireGraphicsNodeChangeStarted();
131
this.isVisible = isVisible;
132         // fireGraphicsNodeChangeCompleted();
133
}
134
135
136     //
137
// Drawing methods
138
//
139

140     /**
141      * Paints this node without applying Filter, Mask, Composite, and clip.
142      *
143      * @param g2d the Graphics2D to use
144      */

145     public void primitivePaint(Graphics2D JavaDoc g2d) {
146         if (count == 0) {
147             return;
148         }
149
150         // Paint children
151
for (int i=0; i < count; ++i) {
152             if (HaltingThread.hasBeenHalted())
153                 return;
154
155             GraphicsNode node = children[i];
156             if (node == null) {
157                 continue;
158             }
159             node.paint(g2d);
160
161         }
162     }
163
164     //
165
// Event support methods
166
//
167

168
169     //
170
// Geometric methods
171
//
172

173     /**
174      * Invalidates the cached geometric bounds. This method is called
175      * each time an attribute that affects the bounds of this node
176      * changed.
177      */

178     protected void invalidateGeometryCache() {
179         super.invalidateGeometryCache();
180         geometryBounds = null;
181         primitiveBounds = null;
182         sensitiveBounds = null;
183         outline = null;
184     }
185
186     /**
187      * Returns the bounds of the area covered by this node's primitive paint.
188      */

189     public Rectangle2D JavaDoc getPrimitiveBounds() {
190         if (primitiveBounds != null) {
191             if (primitiveBounds == NULL_RECT) return null;
192             return primitiveBounds;
193         }
194
195         int i=0;
196         Rectangle2D JavaDoc bounds = null;
197         while ((bounds == null) && i < count) {
198             bounds = children[i++].getTransformedBounds(IDENTITY);
199             if (((i & 0x0F) == 0) && HaltingThread.hasBeenHalted())
200                 break; // check every 16 children if we have been interrupted.
201
}
202         if (HaltingThread.hasBeenHalted()) {
203             invalidateGeometryCache();
204             return null;
205         }
206
207         if (bounds == null) {
208             primitiveBounds = NULL_RECT;
209             return null;
210         }
211
212         primitiveBounds = bounds;
213         Rectangle2D JavaDoc ctb = null;
214         while (i < count) {
215             ctb = children[i++].getTransformedBounds(IDENTITY);
216             if (ctb != null) {
217                 if (primitiveBounds == null) {
218                     // another thread has set the primitive bounds to null,
219
// need to recall this function
220
return null;
221                 } else {
222                     primitiveBounds.add(ctb);
223                 }
224             }
225
226             if (((i & 0x0F) == 0) && HaltingThread.hasBeenHalted())
227                 break; // check every 16 children if we have been interrupted.
228
}
229         
230         // Check If we should halt early.
231
if (HaltingThread.hasBeenHalted()) {
232             // The Thread has been halted.
233
// Invalidate any cached values and proceed.
234
invalidateGeometryCache();
235         }
236         return primitiveBounds;
237     }
238
239     /**
240      * Transforms a Rectangle 2D by an affine transform. It assumes the transform
241      * is only scale/translate so there is no loss of precision over transforming
242      * the source geometry.
243      */

244     public static Rectangle2D JavaDoc getTransformedBBox(Rectangle2D JavaDoc r2d, AffineTransform JavaDoc t) {
245         if ((t == null) || (r2d == null)) return r2d;
246
247         double x = r2d.getX();
248         double w = r2d.getWidth();
249         double y = r2d.getY();
250         double h = r2d.getHeight();
251
252         double sx = t.getScaleX();
253         double sy = t.getScaleY();
254         if (sx < 0) {
255             x = -(x + w);
256             sx = -sx;
257         }
258         if (sy < 0) {
259             y = -(y + h);
260             sy = -sy;
261         }
262
263         return new Rectangle2D.Float JavaDoc
264             ((float)(x*sx+t.getTranslateX()),
265              (float)(y*sy+t.getTranslateY()),
266              (float)(w*sx), (float)(h*sy));
267     }
268
269     /**
270      * Returns the bounds of this node's primitivePaint after applying
271      * the input transform (if any), concatenated with this node's
272      * transform (if any).
273      *
274      * @param txf the affine transform with which this node's transform should
275      * be concatenated. Should not be null. */

276     public Rectangle2D JavaDoc getTransformedPrimitiveBounds(AffineTransform JavaDoc txf) {
277         AffineTransform JavaDoc t = txf;
278         if (transform != null) {
279             t = new AffineTransform JavaDoc(txf);
280             t.concatenate(transform);
281         }
282
283         if ((t == null) || ((t.getShearX() == 0) && (t.getShearY() == 0))) {
284             // No rotation it's safe to simply transform our bounding box.
285
return getTransformedBBox(getPrimitiveBounds(), t);
286         }
287     
288         int i = 0;
289         Rectangle2D JavaDoc tpb = null;
290         while (tpb == null && i < count) {
291             tpb = children[i++].getTransformedBounds(t);
292         }
293
294         Rectangle2D JavaDoc ctb = null;
295         while (i < count) {
296             ctb = children[i++].getTransformedBounds(t);
297             if(ctb != null){
298                 tpb.add(ctb);
299             }
300         }
301
302         return tpb;
303     }
304
305     /**
306      * Returns the bounds of the area covered by this node, without
307      * taking any of its rendering attribute into account. That is,
308      * exclusive of any clipping, masking, filtering or stroking, for
309      * example.
310      */

311     public Rectangle2D JavaDoc getGeometryBounds() {
312         if (geometryBounds == null) {
313             // System.err.println("geometryBounds are null");
314
int i=0;
315             while(geometryBounds == null && i < count){
316                 geometryBounds =
317             children[i++].getTransformedGeometryBounds (IDENTITY);
318             }
319
320             Rectangle2D JavaDoc cgb = null;
321             while (i<count) {
322                 cgb = children[i++].getTransformedGeometryBounds(IDENTITY);
323                 if (cgb != null) {
324                     if (geometryBounds == null) {
325                         // another thread has set the geometry bounds to null,
326
// need to recall this function
327
return getGeometryBounds();
328                     } else {
329                         geometryBounds.add(cgb);
330                     }
331                 }
332             }
333         }
334
335         return geometryBounds;
336     }
337
338     /**
339      * Returns the bounds of the area covered by this node, without taking any
340      * of its rendering attribute into accoun. That is, exclusive of any clipping,
341      * masking, filtering or stroking, for example. The returned value is
342      * transformed by the concatenation of the input transform and this node's
343      * transform.
344      *
345      * @param txf the affine transform with which this node's transform should
346      * be concatenated. Should not be null.
347      */

348     public Rectangle2D JavaDoc getTransformedGeometryBounds(AffineTransform JavaDoc txf) {
349         AffineTransform JavaDoc t = txf;
350         if (transform != null) {
351             t = new AffineTransform JavaDoc(txf);
352             t.concatenate(transform);
353         }
354
355         if ((t == null) || ((t.getShearX() == 0) && (t.getShearY() == 0))) {
356             // No rotation it's safe to simply transform our bounding box.
357
return getTransformedBBox(getGeometryBounds(), t);
358         }
359     
360         Rectangle2D JavaDoc gb = null;
361         int i=0;
362         while (gb == null && i < count) {
363             gb = children[i++].getTransformedGeometryBounds(t);
364         }
365     
366         Rectangle2D JavaDoc cgb = null;
367         while (i < count) {
368             cgb = children[i++].getTransformedGeometryBounds(t);
369             if (cgb != null) {
370                 gb.add(cgb);
371             }
372         }
373
374         return gb;
375     }
376
377     /**
378      * Returns the bounds of the sensitive area covered by this node,
379      * This includes the stroked area but does not include the effects
380      * of clipping, masking or filtering.
381      */

382     public Rectangle2D JavaDoc getSensitiveBounds() {
383         if (sensitiveBounds != null)
384             return sensitiveBounds;
385
386         // System.out.println("sensitiveBoundsBounds are null");
387
int i=0;
388         while(sensitiveBounds == null && i < count){
389             sensitiveBounds =
390                 children[i++].getTransformedSensitiveBounds(IDENTITY);
391         }
392
393         Rectangle2D JavaDoc cgb = null;
394         while (i<count) {
395             cgb = children[i++].getTransformedSensitiveBounds(IDENTITY);
396             if (cgb != null) {
397                 if (sensitiveBounds == null)
398                     // another thread has set the geometry bounds to null,
399
// need to recall this function
400
return getSensitiveBounds();
401                 
402                 sensitiveBounds.add(cgb);
403             }
404         }
405
406         return sensitiveBounds;
407     }
408
409     /**
410      * Returns the bounds of the sensitive area covered by this node,
411      * This includes the stroked area but does not include the effects
412      * of clipping, masking or filtering. The returned value is
413      * transformed by the concatenation of the input transform and
414      * this node's transform.
415      *
416      * @param txf the affine transform with which this node's
417      * transform should be concatenated. Should not be null.
418      */

419     public Rectangle2D JavaDoc getTransformedSensitiveBounds(AffineTransform JavaDoc txf) {
420         AffineTransform JavaDoc t = txf;
421         if (transform != null) {
422             t = new AffineTransform JavaDoc(txf);
423             t.concatenate(transform);
424         }
425     
426         if ((t == null) || ((t.getShearX() == 0) && (t.getShearY() == 0))) {
427             // No rotation it's safe to simply transform our bounding box.
428
return getTransformedBBox(getSensitiveBounds(), t);
429         }
430     
431         Rectangle2D JavaDoc sb = null;
432         int i=0;
433         while (sb == null && i < count) {
434             sb = children[i++].getTransformedSensitiveBounds(t);
435         }
436     
437         Rectangle2D JavaDoc csb = null;
438         while (i < count) {
439             csb = children[i++].getTransformedSensitiveBounds(t);
440             if (csb != null) {
441                 sb.add(csb);
442             }
443         }
444
445         return sb;
446     }
447
448     /**
449      * Returns true if the specified Point2D is inside the boundary of this
450      * node, false otherwise.
451      *
452      * @param p the specified Point2D in the user space
453      */

454     public boolean contains(Point2D JavaDoc p) {
455         Rectangle2D JavaDoc bounds = getSensitiveBounds();
456         if (count > 0 && bounds != null && bounds.contains(p)) {
457             Point2D JavaDoc pt = null;
458             Point2D JavaDoc cp = null; // Propagated to children
459
for (int i=0; i < count; ++i) {
460                 AffineTransform JavaDoc t = children[i].getInverseTransform();
461                 if(t != null){
462                     pt = t.transform(p, pt);
463                     cp = pt;
464                 } else {
465                     cp = p;
466                 }
467                 if (children[i].contains(cp)) {
468                     return true;
469                 }
470             }
471         }
472
473         return false;
474     }
475
476
477     /**
478      * Returns the GraphicsNode containing point p if this node or one of its
479      * children is sensitive to mouse events at p.
480      *
481      * @param p the specified Point2D in the user space
482      */

483     public GraphicsNode nodeHitAt(Point2D JavaDoc p) {
484         Rectangle2D JavaDoc bounds = getSensitiveBounds();
485         if (count > 0 && bounds != null && bounds.contains(p)) {
486             // Go backward because the children are in rendering order
487
Point2D JavaDoc pt = null;
488             Point2D JavaDoc cp = null; // Propagated to children
489
for (int i=count-1; i >= 0; --i) {
490                 AffineTransform JavaDoc t = children[i].getInverseTransform();
491                 if(t != null){
492                     pt = t.transform(p, pt);
493                     cp = pt;
494                 } else {
495                     cp = p;
496                 }
497                 GraphicsNode node = children[i].nodeHitAt(cp);
498                 if (node != null) {
499                     return node;
500                 }
501             }
502         }
503
504         return null;
505     }
506
507     /**
508      * Returns the outline of this node.
509      */

510     public Shape JavaDoc getOutline() {
511         if (outline != null)
512             return outline;
513
514         outline = new GeneralPath JavaDoc();
515         for (int i = 0; i < count; i++) {
516             Shape JavaDoc childOutline = children[i].getOutline();
517             if (childOutline != null) {
518                 AffineTransform JavaDoc tr = children[i].getTransform();
519                 if (tr != null) {
520                     ((GeneralPath JavaDoc)outline).append(tr.createTransformedShape(childOutline), false);
521                 } else {
522                     ((GeneralPath JavaDoc)outline).append(childOutline, false);
523                 }
524             }
525         }
526
527         return outline;
528     }
529
530     //
531
// Structural info
532
//
533

534     /**
535      * Sets the root node of this grahics node and modify all its children.
536      */

537     protected void setRoot(RootGraphicsNode newRoot) {
538         super.setRoot(newRoot);
539         for (int i=0; i < count; ++i) {
540             GraphicsNode node = children[i];
541             ((AbstractGraphicsNode)node).setRoot(newRoot);
542         }
543     }
544
545     //
546
// List implementation
547
//
548

549     /**
550      * Returns the number of children of this composite graphics node.
551      */

552     public int size() {
553         return count;
554     }
555
556     /**
557      * Returns true if this composite graphics node does not contain
558      * graphics node, false otherwise.
559      */

560     public boolean isEmpty() {
561         return (count == 0);
562     }
563
564     /**
565      * Returns true if this composite graphics node contains the
566      * specified graphics node, false otherwise.
567      * @param node the node to check
568      */

569     public boolean contains(Object JavaDoc node) {
570         return (indexOf(node) >= 0);
571     }
572
573     /**
574      * Returns an iterator over the children of this graphics node.
575      */

576     public Iterator JavaDoc iterator() {
577         return new Itr();
578     }
579
580     /**
581      * Returns an array containing all of the graphics node in the
582      * children list of this composite graphics node in the correct
583      * order. If the children list fits in the specified array, it is
584      * returned therein. Otherwise, a new array is allocated.
585      */

586     public Object JavaDoc [] toArray() {
587         GraphicsNode [] result = new GraphicsNode[count];
588         for (int i=0; i < count; ++i) {
589             result[i] = children[i];
590         }
591         return result;
592     }
593
594     /**
595      * Returns an array containing all of the graphics node in the children list
596      * of this composite graphics node in the correct order.
597      *
598      * @param a the array to fit if possible
599      */

600     public Object JavaDoc[] toArray(Object JavaDoc [] a) {
601         if (a.length < count) {
602             a = new GraphicsNode[count];
603         }
604         System.arraycopy(children, 0, a, 0, count);
605         if (a.length > count) {
606             a[count] = null;
607         }
608         return a;
609     }
610
611     /**
612      * Returns the graphics node at the specified position in the children list.
613      *
614      * @param index the index of the graphics node to return
615      * @exception IndexOutOfBoundsException if the index is out of range
616      */

617     public Object JavaDoc get(int index) {
618         checkRange(index);
619         return children[index];
620     }
621
622     // Modification Operations
623

624     /**
625      * Replaces the graphics node at the specified position in the children list
626      * with the specified graphics node.
627      *
628      * @param index the index of the graphics node to replace
629      * @param o the graphics node to be stored at the specified position
630      * @return the graphics node previously at the specified position
631      * @exception IndexOutOfBoundsException if the index is out of range
632      * @exception IllegalArgumentException if the node is not an
633      * instance of GraphicsNode
634      */

635     public Object JavaDoc set(int index, Object JavaDoc o) {
636         // Check for correct arguments
637
if (!(o instanceof GraphicsNode)) {
638             throw new IllegalArgumentException JavaDoc(o+" is not a GraphicsNode");
639         }
640         checkRange(index);
641         GraphicsNode node = (GraphicsNode) o;
642         {
643             fireGraphicsNodeChangeStarted(node);
644         }
645         // Reparent the graphics node and tidy up the tree's state
646
if (node.getParent() != null) {
647             node.getParent().getChildren().remove(node);
648         }
649         // Replace the node to the children list
650
GraphicsNode oldNode = children[index];
651         children[index] = node;
652         // Set the parents of the graphics nodes
653
((AbstractGraphicsNode) node).setParent(this);
654         ((AbstractGraphicsNode) oldNode).setParent(null);
655         // Set the root of the graphics node
656
((AbstractGraphicsNode) node).setRoot(this.getRoot());
657         ((AbstractGraphicsNode) oldNode).setRoot(null);
658         // Invalidates cached values
659
invalidateGeometryCache();
660         // Create and dispatch events
661
// int id = CompositeGraphicsNodeEvent.GRAPHICS_NODE_REMOVED;
662
// dispatchEvent(new CompositeGraphicsNodeEvent(this, id, oldNode));
663
// id = CompositeGraphicsNodeEvent.GRAPHICS_NODE_ADDED;
664
// dispatchEvent(new CompositeGraphicsNodeEvent(this, id, node));
665
fireGraphicsNodeChangeCompleted();
666         return oldNode;
667      }
668
669     /**
670      * Adds the specified graphics node to this composite graphics node.
671      *
672      * @param o the graphics node to add
673      * @return true (as per the general contract of Collection.add)
674      * @exception IllegalArgumentException if the node is not an
675      * instance of GraphicsNode
676      */

677     public boolean add(Object JavaDoc o) {
678         // Check for correct argument
679
if (!(o instanceof GraphicsNode)) {
680             throw new IllegalArgumentException JavaDoc(o+" is not a GraphicsNode");
681         }
682         GraphicsNode node = (GraphicsNode) o;
683         {
684             fireGraphicsNodeChangeStarted(node);
685         }
686         // Reparent the graphics node and tidy up the tree's state
687
if (node.getParent() != null) {
688             node.getParent().getChildren().remove(node);
689         }
690         // Add the graphics node to the children list
691
ensureCapacity(count + 1); // Increments modCount!!
692
children[count++] = node;
693         // Set the parent of the graphics node
694
((AbstractGraphicsNode) node).setParent(this);
695         // Set the root of the graphics node
696
((AbstractGraphicsNode) node).setRoot(this.getRoot());
697         // Invalidates cached values
698
invalidateGeometryCache();
699         // Create and dispatch event
700
// int id = CompositeGraphicsNodeEvent.GRAPHICS_NODE_ADDED;
701
// dispatchEvent(new CompositeGraphicsNodeEvent(this, id, node));
702
fireGraphicsNodeChangeCompleted();
703         return true;
704     }
705
706     /**
707      * Inserts the specified graphics node at the specified position in this
708      * children list. Shifts the graphics node currently at that position (if
709      * any) and any subsequent graphics nodes to the right (adds one to their
710      * indices).
711      *
712      * @param index the position at which the specified graphics node is to
713      * be inserted.
714      * @param o the graphics node to be inserted.
715      * @exception IndexOutOfBoundsException if the index is out of range
716      * @exception IllegalArgumentException if the node is not an
717      * instance of GraphicsNode
718      */

719     public void add(int index, Object JavaDoc o) {
720         // Check for correct arguments
721
if (!(o instanceof GraphicsNode)) {
722             throw new IllegalArgumentException JavaDoc(o+" is not a GraphicsNode");
723         }
724         if (index > count || index < 0) {
725             throw new IndexOutOfBoundsException JavaDoc(
726                 "Index: "+index+", Size: "+count);
727         }
728         GraphicsNode node = (GraphicsNode) o;
729         {
730             fireGraphicsNodeChangeStarted(node);
731         }
732         // Reparent the graphics node and tidy up the tree's state
733
if (node.getParent() != null) {
734             node.getParent().getChildren().remove(node);
735         }
736         // Insert the node to the children list
737
ensureCapacity(count+1); // Increments modCount!!
738
System.arraycopy(children, index, children, index+1, count-index);
739         children[index] = node;
740         count++;
741         // Set parent of the graphics node
742
((AbstractGraphicsNode) node).setParent(this);
743         // Set root of the graphics node
744
((AbstractGraphicsNode) node).setRoot(this.getRoot());
745         // Invalidates cached values
746
invalidateGeometryCache();
747         // Create and dispatch event
748
// int id = CompositeGraphicsNodeEvent.GRAPHICS_NODE_ADDED;
749
// dispatchEvent(new CompositeGraphicsNodeEvent(this, id, node));
750
fireGraphicsNodeChangeCompleted();
751     }
752
753     /**
754      * <b>Not supported</b> -
755      * Throws <tt>UnsupportedOperationException</tt> exception.
756      */

757     public boolean addAll(Collection JavaDoc c) {
758         throw new UnsupportedOperationException JavaDoc();
759     }
760
761     /**
762      * <b>Not supported</b> -
763      * Throws <tt>UnsupportedOperationException</tt> exception.
764      */

765     public boolean addAll(int index, Collection JavaDoc c) {
766         throw new UnsupportedOperationException JavaDoc();
767     }
768
769     /**
770      * Removes the specified graphics node from the children list.
771      *
772      * @param o the node the remove
773      * @return true if the children list contains the specified graphics node
774      * @exception IllegalArgumentException if the node is not an
775      * instance of GraphicsNode
776      */

777     public boolean remove(Object JavaDoc o) {
778         // Check for correct argument
779
if (!(o instanceof GraphicsNode)) {
780             throw new IllegalArgumentException JavaDoc(o+" is not a GraphicsNode");
781         }
782         GraphicsNode node = (GraphicsNode) o;
783         if (node.getParent() != this) {
784             return false;
785         }
786         // Remove the node
787
int index = 0;
788         for (; node != children[index]; index++);
789         remove(index);
790         return true;
791     }
792
793     /**
794      * Removes the graphics node at the specified position in the children list.
795      * Shifts any subsequent graphics nodes to the left (subtracts one from
796      * their indices).
797      *
798      * @param index the position of the graphics node to remove
799      * @return the graphics node that was removed
800      * @exception IndexOutOfBoundsException if index out of range <tt>
801      */

802     public Object JavaDoc remove(int index) {
803         // Check for correct argument
804
checkRange(index);
805         GraphicsNode oldNode = children[index];
806         {
807             fireGraphicsNodeChangeStarted(oldNode);
808         }
809         // Remove the node at the specified index
810
modCount++;
811         int numMoved = count - index - 1;
812         if (numMoved > 0) {
813             System.arraycopy(children, index+1, children, index, numMoved);
814         }
815         children[--count] = null; // Let gc do its work
816
if (count == 0) {
817             children = null;
818         }
819         // Set parent of the node
820
((AbstractGraphicsNode) oldNode).setParent(null);
821         // Set root of the node
822
((AbstractGraphicsNode) oldNode).setRoot(null);
823         // Invalidates cached values
824
invalidateGeometryCache();
825         // Create and dispatch event
826
// int id = CompositeGraphicsNodeEvent.GRAPHICS_NODE_REMOVED;
827
// dispatchEvent(new CompositeGraphicsNodeEvent(this, id, oldNode));
828
fireGraphicsNodeChangeCompleted();
829         return oldNode;
830     }
831
832     /**
833      * <b>Not supported</b> -
834      * Throws <tt>UnsupportedOperationException</tt> exception.
835      */

836     public boolean removeAll(Collection JavaDoc c) {
837         throw new UnsupportedOperationException JavaDoc();
838     }
839
840     /**
841      * <b>Not supported</b> -
842      * Throws <tt>UnsupportedOperationException</tt> exception.
843      */

844     public boolean retainAll(Collection JavaDoc c) {
845         throw new UnsupportedOperationException JavaDoc();
846     }
847
848     /**
849      * <b>Not supported</b> -
850      * Throws <tt>UnsupportedOperationException</tt> exception.
851      */

852     public void clear() {
853         throw new UnsupportedOperationException JavaDoc();
854     }
855
856     /**
857      * Returns true if this composite graphics node contains all the graphics
858      * node in the specified collection, false otherwise.
859      *
860      * @param c the collection to be checked for containment
861      */

862     public boolean containsAll(Collection JavaDoc c) {
863         Iterator JavaDoc i = c.iterator();
864         while (i.hasNext()) {
865             if (!contains(i.next())) {
866                     return false;
867             }
868         }
869         return true;
870     }
871
872     // Search Operations
873

874     /**
875      * Returns the index in the children list of the specified graphics node or
876      * -1 if the children list does not contain this graphics node.
877      *
878      * @param node the graphics node to search for
879      */

880     public int indexOf(Object JavaDoc node) {
881         if (node == null || !(node instanceof GraphicsNode)) {
882             return -1;
883         }
884         if (((GraphicsNode) node).getParent() == this) {
885             for (int i = 0; i < count; i++) {
886                 if (node == children[i]) {
887                     return i;
888                 }
889             }
890         }
891         return -1;
892     }
893
894     /**
895      * Returns the index in this children list of the last occurence of the
896      * specified graphics node, or -1 if the list does not contain this graphics
897      * node.
898      *
899      * @param node the graphics node to search for
900      */

901     public int lastIndexOf(Object JavaDoc node) {
902         if (node == null || !(node instanceof GraphicsNode)) {
903             return -1;
904         }
905         if (((GraphicsNode) node).getParent() == this) {
906             for (int i = count-1; i >= 0; i--) {
907                 if (node == children[i]) {
908                     return i;
909                 }
910             }
911         }
912         return -1;
913     }
914
915     // List Iterators
916

917     /**
918      * Returns an iterator over the children of this graphics node.
919      */

920     public ListIterator JavaDoc listIterator() {
921         return listIterator(0);
922     }
923
924     /**
925      * Returns an iterator over the children of this graphics node, starting at
926      * the specified position in the children list.
927      *
928      * @param index the index of the first graphics node to return
929      * from the children list
930      */

931     public ListIterator JavaDoc listIterator(int index) {
932         if (index < 0 || index > count) {
933             throw new IndexOutOfBoundsException JavaDoc("Index: "+index);
934         }
935         return new ListItr(index);
936     }
937
938     // View
939

940     /**
941      * <b>Not supported</b> -
942      * Throws <tt>UnsupportedOperationException</tt> exception.
943      */

944     public List JavaDoc subList(int fromIndex, int toIndex) {
945         throw new UnsupportedOperationException JavaDoc();
946     }
947
948     /**
949      * Checks if the given index is in range. If not, throws an appropriate
950      * runtime exception.
951      *
952      * @param index the index to check
953      */

954     private void checkRange(int index) {
955         if (index >= count || index < 0) {
956             throw new IndexOutOfBoundsException JavaDoc(
957                 "Index: "+index+", Size: "+count);
958         }
959     }
960
961     /**
962      * Increases the capacity of the children list, if necessary, to ensure that
963      * it can hold at least the number of graphics nodes specified by the
964      * minimum capacity argument.
965      *
966      * @param minCapacity the desired minimum capacity.
967      */

968     public void ensureCapacity(int minCapacity) {
969         if (children == null) {
970             children = new GraphicsNode[4];
971         }
972         modCount++;
973         int oldCapacity = children.length;
974         if (minCapacity > oldCapacity) {
975             GraphicsNode [] oldData = children;
976             int newCapacity = (oldCapacity * 3)/2 + 1;
977             if (newCapacity < minCapacity) {
978                 newCapacity = minCapacity;
979             }
980             children = new GraphicsNode[newCapacity];
981             System.arraycopy(oldData, 0, children, 0, count);
982         }
983     }
984
985     /**
986      * An implementation of the java.util.Iterator interface.
987      */

988     private class Itr implements Iterator JavaDoc {
989
990         /**
991          * Index of graphics node to be returned by subsequent call to next.
992          */

993         int cursor = 0;
994
995         /**
996          * Index of graphics node returned by most recent call to next or
997          * previous. Reset to -1 if this graphics node is deleted by a call
998          * to remove.
999          */

1000        int lastRet = -1;
1001
1002        /**
1003         * The modCount value that the iterator believes that the backing
1004         * List should have. If this expectation is violated, the iterator
1005         * has detected concurrent modification.
1006         */

1007        int expectedModCount = modCount;
1008
1009        public boolean hasNext() {
1010            return cursor != count;
1011        }
1012
1013        public Object JavaDoc next() {
1014            try {
1015                Object JavaDoc next = get(cursor);
1016                checkForComodification();
1017                lastRet = cursor++;
1018                return next;
1019            } catch(IndexOutOfBoundsException JavaDoc e) {
1020                checkForComodification();
1021                throw new NoSuchElementException JavaDoc();
1022            }
1023        }
1024
1025        public void remove() {
1026            if (lastRet == -1) {
1027                throw new IllegalStateException JavaDoc();
1028            }
1029            checkForComodification();
1030
1031            try {
1032                CompositeGraphicsNode.this.remove(lastRet);
1033                if (lastRet < cursor) {
1034                    cursor--;
1035                }
1036                lastRet = -1;
1037                expectedModCount = modCount;
1038            } catch(IndexOutOfBoundsException JavaDoc e) {
1039                throw new ConcurrentModificationException JavaDoc();
1040            }
1041        }
1042
1043        final void checkForComodification() {
1044            if (modCount != expectedModCount) {
1045                throw new ConcurrentModificationException JavaDoc();
1046            }
1047        }
1048    }
1049
1050
1051    /**
1052     * An implementation of the java.util.ListIterator interface.
1053     */

1054    private class ListItr extends Itr implements ListIterator JavaDoc {
1055
1056        ListItr(int index) {
1057            cursor = index;
1058        }
1059
1060        public boolean hasPrevious() {
1061            return cursor != 0;
1062        }
1063
1064        public Object JavaDoc previous() {
1065            try {
1066                Object JavaDoc previous = get(--cursor);
1067                checkForComodification();
1068                lastRet = cursor;
1069                return previous;
1070            } catch(IndexOutOfBoundsException JavaDoc e) {
1071                checkForComodification();
1072                throw new NoSuchElementException JavaDoc();
1073            }
1074        }
1075
1076        public int nextIndex() {
1077            return cursor;
1078        }
1079
1080        public int previousIndex() {
1081            return cursor-1;
1082        }
1083
1084        public void set(Object JavaDoc o) {
1085            if (lastRet == -1) {
1086                throw new IllegalStateException JavaDoc();
1087            }
1088            checkForComodification();
1089            try {
1090                CompositeGraphicsNode.this.set(lastRet, o);
1091                expectedModCount = modCount;
1092            } catch(IndexOutOfBoundsException JavaDoc e) {
1093                throw new ConcurrentModificationException JavaDoc();
1094            }
1095        }
1096
1097        public void add(Object JavaDoc o) {
1098            checkForComodification();
1099            try {
1100                CompositeGraphicsNode.this.add(cursor++, o);
1101                lastRet = -1;
1102                expectedModCount = modCount;
1103            } catch(IndexOutOfBoundsException JavaDoc e) {
1104                throw new ConcurrentModificationException JavaDoc();
1105            }
1106        }
1107    }
1108}
1109
Popular Tags