1 /*
2  * $RCSfile: LinkRetained.java,v $
3  *
4  * Copyright 1997-2008 Sun Microsystems, Inc.  All Rights Reserved.
5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6  *
7  * This code is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License version 2 only, as
9  * published by the Free Software Foundation.  Sun designates this
10  * particular file as subject to the "Classpath" exception as provided
11  * by Sun in the LICENSE file that accompanied this code.
12  *
13  * This code is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16  * version 2 for more details (a copy is included in the LICENSE file that
17  * accompanied this code).
18  *
19  * You should have received a copy of the GNU General Public License version
20  * 2 along with this work; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22  *
23  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
24  * CA 95054 USA or visit www.sun.com if you need additional information or
25  * have any questions.
26  *
27  * $Revision: 1.6 $
28  * $Date: 2008/02/28 20:17:26 $
29  * $State: Exp $
30  */
31 
32 package javax.media.j3d;
33 import java.util.*;
34 
35 /**
36  * A Link leaf node consisting of a reference to a SharedGroup node.
37  */
38 
39 class LinkRetained extends LeafRetained {
40     /**
41      * The SharedGroup component of the link node.
42      */
43     SharedGroupRetained sharedGroup;
44 
45     static String plus = "+";
46 
47     // This is used when setLive to check for cycle scene graph
48     boolean visited = false;
49 
LinkRetained()50     LinkRetained() {
51 	this.nodeType = NodeRetained.LINK;
52 	localBounds = new BoundingBox();
53 	((BoundingBox)localBounds).setLower( 1.0, 1.0, 1.0);
54 	((BoundingBox)localBounds).setUpper(-1.0,-1.0,-1.0);
55     }
56 
57     /**
58      * Sets the SharedGroup reference.
59      * @param sharedGroup the SharedGroup node
60      */
setSharedGroup(SharedGroup sharedGroup)61     void setSharedGroup(SharedGroup sharedGroup) {
62 	// Note that it is possible that the sharedGroup pass
63 	// in already link to another link and live.
64 	HashKey newKeys[] = null;
65 	boolean abort = false;
66 
67 	if (source.isLive()) {
68 	    // bug 4370407: if sharedGroup is a parent, then don't do anything
69 	    if (sharedGroup != null) {
70 	        synchronized(universe.sceneGraphLock) {
71 	            NodeRetained pa;
72 	            for (pa = parent; pa != null; pa = pa.parent) {
73 	                if (pa == (NodeRetained)sharedGroup.retained) {
74 			    abort = true;
75                             throw new SceneGraphCycleException(J3dI18N.getString("LinkRetained1"));
76 	                }
77 	            }
78 	        }
79 	        if (abort)
80 		    return;
81 	    }
82 
83 	    newKeys = getNewKeys(locale.nodeId, localToVworldKeys);
84 
85 	    if (this.sharedGroup != null) {
86 		((GroupRetained) parent).checkClearLive(this.sharedGroup,
87 							newKeys, true, null,
88 							0, 0, this);
89 		this.sharedGroup.parents.removeElement(this);
90 	    }
91 	}
92 
93 	if (sharedGroup != null) {
94 	    this.sharedGroup =
95 		(SharedGroupRetained)sharedGroup.retained;
96 	}  else {
97 	    this.sharedGroup = null;
98 	}
99 
100       if (source.isLive() && (sharedGroup != null)) {
101 
102 	  this.sharedGroup.parents.addElement(this);
103 	  visited = true;
104 	  try {
105 	      int ci = ((GroupRetained) parent).indexOfChild((Node)this.sharedGroup.source);
106 	      ((GroupRetained) parent).checkSetLive(this.sharedGroup,  ci,
107 						    newKeys, true, null,
108 						    0, this);
109 	  } catch (SceneGraphCycleException e) {
110 	      throw e;
111           } finally {
112 	      visited = false;
113 	  }
114       }
115 
116     }
117 
118   /**
119    * Retrieves the SharedGroup reference.
120    * @return the SharedGroup node
121    */
getSharedGroup()122     SharedGroup getSharedGroup() {
123 	return (sharedGroup != null ?
124 		(SharedGroup)this.sharedGroup.source : null);
125     }
126 
computeCombineBounds(Bounds bounds)127     void computeCombineBounds(Bounds bounds) {
128 
129 	if (boundsAutoCompute) {
130 	    sharedGroup.computeCombineBounds(bounds);
131 	} else {
132 	    // Should this be lock too ? ( MT safe  ? )
133 	    synchronized(localBounds) {
134 		bounds.combine(localBounds);
135 	    }
136 	}
137     }
138 
139 
140     /**
141      * Gets the bounding object of a node.
142      * @return the node's bounding object
143      */
getBounds()144     Bounds getBounds() {
145 	return (boundsAutoCompute ?
146 	        (Bounds)sharedGroup.getBounds().clone() :
147 	        super.getBounds());
148     }
149 
150 
151     /**
152      * assign a name to this node when it is made live.
153      */
setLive(SetLiveState s)154     void setLive(SetLiveState s) {
155 
156         super.doSetLive(s);
157 
158         if (inBackgroundGroup) {
159             throw new
160                IllegalSceneGraphException(J3dI18N.getString("LinkRetained0"));
161         }
162 
163 	if (nodeId == null) {
164             nodeId = universe.getNodeId();
165 	}
166 
167 	if (sharedGroup != null) {
168 	    this.sharedGroup.parents.addElement(this);
169 	    HashKey newKeys[] = getNewKeys(s.locale.nodeId, s.keys);
170 	    HashKey oldKeys[] = s.keys;
171 	    s.keys = newKeys;
172 	    s.inSharedGroup = true;
173 	    if (visited) {
174 		throw new SceneGraphCycleException(J3dI18N.getString("LinkRetained1"));
175 	    }
176 	    visited = true;
177 	    try {
178 		this.sharedGroup.setLive(s);
179 	    } catch (SceneGraphCycleException e) {
180 		throw e;
181 	    } finally {
182 		visited = false;
183 	    }
184 
185 	    s.inSharedGroup = inSharedGroup;
186 	    s.keys = oldKeys;
187 
188 	    localBounds.setWithLock(this.sharedGroup.localBounds);
189 	}
190 
191 	super.markAsLive();
192     }
193 
setNodeData(SetLiveState s)194     void setNodeData(SetLiveState s) {
195 
196         super.setNodeData(s);
197 
198         // add this node to parentTransformLink's childTransformLink
199         if (s.childTransformLinks != null) {
200             // do not duplicate shared nodes
201             synchronized(s.childTransformLinks) {
202                 if(!inSharedGroup || !s.childTransformLinks.contains(this)) {
203                     s.childTransformLinks.add(this);
204                 }
205             }
206         }
207 
208         // add this node to parentSwitchLink's childSwitchLink
209         if (s.childSwitchLinks != null) {
210             if(!inSharedGroup ||
211                         // only add if not already added in sharedGroup
212                         !s.childSwitchLinks.contains(this)) {
213                 s.childSwitchLinks.add(this);
214             }
215         }
216     }
217 
218 
recombineAbove()219     void recombineAbove() {
220         localBounds.setWithLock(sharedGroup.localBounds);
221 	parent.recombineAbove();
222     }
223 
224     /**
225      * assign a name to this node when it is made live.
226      */
clearLive(SetLiveState s)227     void clearLive(SetLiveState s) {
228 
229 	if (sharedGroup != null) {
230 	    HashKey newKeys[] = getNewKeys(s.locale.nodeId, s.keys);
231             super.clearLive(s);
232 	    HashKey oldKeys[] = s.keys;
233 	    s.keys = newKeys;
234 	    s.inSharedGroup = true;
235 	    this.sharedGroup.parents.removeElement(this);
236 	    this.sharedGroup.clearLive(s);
237 	    s.inSharedGroup = inSharedGroup;
238 	    s.keys = oldKeys;
239 	} else {
240             super.clearLive(s);
241 	}
242     }
243 
removeNodeData(SetLiveState s)244     void removeNodeData(SetLiveState s) {
245         if(refCount <= 0) {
246 	    // either not in sharedGroup or last instance in sharedGroup
247             // remove this node from parentTransformLink's childTransformLink
248             if (parentTransformLink != null) {
249                 ArrayList obj;
250                 if (parentTransformLink instanceof TransformGroupRetained) {
251                     obj = ((TransformGroupRetained)
252                         parentTransformLink).childTransformLinks;
253                 } else {
254                     obj = ((SharedGroupRetained)
255                         parentTransformLink).childTransformLinks;
256                 }
257                 synchronized(obj) {
258                     obj.remove(this);
259                 }
260             }
261 
262             // remove this node from parentSwitchLink's childSwitchLink
263             if (parentSwitchLink != null) {
264                 ArrayList switchLinks;
265                 for(int i=0; i<parentSwitchLink.childrenSwitchLinks.size();i++){
266                     switchLinks = (ArrayList)
267                                 parentSwitchLink.childrenSwitchLinks.get(i);
268                     if (switchLinks.contains(this)) {
269                         switchLinks.remove(this);
270                         break;
271                     }
272                 }
273             }
274         }
275 	super.removeNodeData(s);
276     }
277 
278 
updatePickable(HashKey keys[], boolean pick[])279     void updatePickable(HashKey keys[], boolean pick[]) {
280 	super.updatePickable(keys, pick);
281 
282 	if (sharedGroup != null) {
283 	    HashKey newKeys[] = getNewKeys(locale.nodeId, keys);
284 	    sharedGroup.updatePickable(newKeys, pick);
285 	}
286     }
287 
updateCollidable(HashKey keys[], boolean collide[])288     void updateCollidable(HashKey keys[], boolean collide[]) {
289 	 super.updateCollidable(keys, collide);
290 
291 	 if (sharedGroup != null) {
292 	     HashKey newKeys[] = getNewKeys(locale.nodeId, keys);
293 	     sharedGroup.updateCollidable(newKeys, collide);
294 	 }
295     }
296 
setBoundsAutoCompute(boolean autoCompute)297     void setBoundsAutoCompute(boolean autoCompute) {
298         super.setBoundsAutoCompute(autoCompute);
299         if (!autoCompute) {
300             localBounds = getBounds();
301         }
302     }
303 
setCompiled()304     void setCompiled() {
305 	super.setCompiled();
306 	if (sharedGroup != null) {
307 	    sharedGroup.setCompiled();
308 	}
309     }
310 
compile(CompileState compState)311     void compile(CompileState compState) {
312 
313 	super.compile(compState);
314 
315         // XXXX: for now keep the static transform in the parent tg
316         compState.keepTG = true;
317 
318         // don't remove this group node
319         mergeFlag = SceneGraphObjectRetained.DONT_MERGE;
320 
321         if (J3dDebug.devPhase && J3dDebug.debug) {
322             compState.numLinks++;
323         }
324     }
325 
getNewKeys(String localeNodeId, HashKey oldKeys[])326     HashKey[] getNewKeys(String localeNodeId,  HashKey oldKeys[]) {
327 	HashKey newKeys[];
328 
329 	if (!inSharedGroup) {
330 	    newKeys = new HashKey[1];
331 	    newKeys[0] = new HashKey(localeNodeId);
332 	    newKeys[0].append(plus + nodeId);
333 	} else {
334 	    // Need to append this link node id to all keys passed in.
335 	    newKeys = new HashKey[oldKeys.length];
336 	    for (int i=oldKeys.length-1; i>=0; i--) {
337 		newKeys[i] = new HashKey(oldKeys[i].toString()
338 					 + plus + nodeId);
339 	    }
340 	}
341 	return newKeys;
342     }
343 
searchGeometryAtoms(UnorderList list)344     void searchGeometryAtoms(UnorderList list) {
345 	if (sharedGroup != null) {
346 	    sharedGroup.searchGeometryAtoms(list);
347 	}
348     }
349 }
350