KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > buchuki > ensmer > Area


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

16 package com.buchuki.ensmer;
17
18 import java.util.*;
19 import javax.media.j3d.*;
20 import javax.vecmath.Matrix4f;
21 import com.buchuki.ensmer.input.InputManager;
22 import com.buchuki.ensmer.input.ObjectInputManager;
23 import com.buchuki.ensmer.input.event.semantic.ObjectEvent;
24 import com.buchuki.ensmer.object.*;
25 import com.buchuki.ensmer.prevayler.queries.*;
26 import org.prevayler.*;
27
28 /**
29  * Class to manage a collection of visible objects. The objects themseleves
30  * are managed by the BackHoe. Only the frontends have much impact on the
31  * Area. Each Area has a distinct user position as well.
32  *
33  * @author Dusty Phillips [dusty@buchuki.com]
34  */

35 public class Area {
36
37
38     /**
39      * Constructor for the Area object
40      *
41      * @param identifier Description of the Parameter
42      */

43     @SuppressWarnings JavaDoc({"unchecked"}) Area(Long JavaDoc identifier) {
44         prevayler = EnsmerManager.instance().getPrevayler();
45         areaID = identifier;
46         branchGraph.setCapability(Group.ALLOW_CHILDREN_READ);
47         branchGraph.setCapability(Group.ALLOW_CHILDREN_WRITE);
48         branchGraph.setCapability(Group.ALLOW_CHILDREN_EXTEND);
49         branchGraph.setCapability(BranchGroup.ALLOW_DETACH);
50         loading = true;
51     }
52
53     /**
54      * Create a new object in this Area. Load a frontend for the given backend
55      * and display it.
56      *
57      * @param backend A backend object that has not yet been
58      * added to the prevayler and has no frontend associated with it.
59      * @param location the location to display the object at
60      * @return the identifier of the new object, or null
61      * if the object could not be created.
62      * @throws IllegalStateException if the area is read only
63      */

64     public Long JavaDoc newObject(Backend backend, Matrix4f location) {
65         if (isReadOnly()) {
66             throw new IllegalStateException JavaDoc("Area is read only");
67         }
68         try {
69             //Serialize the new object
70
Long JavaDoc newObjectId = (Long JavaDoc) prevayler.execute(
71                 new GetNextObjectIDQuery());
72             prevayler.execute(new AddObjectTransaction(
73                 newObjectId, backend.getClass()));
74             prevayler.execute(new SetObjectLocationTransaction(
75                 newObjectId, location));
76             prevayler.execute(new SetObjectDataTransaction(
77                 newObjectId, backend.getSerializable()));
78             backend.setId(newObjectId);
79             setupNewObject(backend);
80             return newObjectId;
81         } catch (Exception JavaDoc e) {
82             e.printStackTrace();
83             return null;
84         }
85     }
86
87     /**
88      * Add an object to the Area based on its identifier. The object is assumed
89      * to exist in the prevayler with the location properly set, but the data
90      * may be null. The prevayler is updated to reflect the new Area Identifier.
91      * A new backend and frontend are created to represent the object in this
92      * area.
93      *
94      * @param id the identifier in the prevayler of the object to be added
95      */

96     public void addObject(Long JavaDoc id) {
97         try {
98             Backend backend = EnsmerManager.instance().getBackhoe().getBackend(id);
99             addObject(backend);
100         } catch (Exception JavaDoc e) {
101             e.printStackTrace();
102         }
103     }
104
105     /**
106      * Add an object to the Area based on an existing backend. The object is
107      * assumed to exist in the prevayler with the location properly set, but the
108      * data may be null. This behaves much like the addObject method with a Long
109      * parameter except that the backend has been constructed elsewhere.
110      *
111      * @param backend The feature to be added to the Object attribute
112      */

113     public void addObject(Backend backend) {
114         try {
115             setupNewObject(backend);
116         } catch (Exception JavaDoc e) {
117             e.printStackTrace();
118         }
119     }
120
121     /**
122      * Remove the selected object from this area, but do not destroy its
123      * prevayled instance. Usually it would be added to another area directly.
124      *
125      * @param id Description of the Parameter
126      */

127     public void removeObject(Long JavaDoc id) {
128         if (!objectMap.containsKey(id)) {
129             return;
130         }
131         SelectionManager sel = EnsmerManager.instance().getSelectionManager();
132         if (sel.isSelected(id)) {
133             sel.toggleSelection(id);
134         }
135         InputManager inMan = EnsmerManager.instance().getUserManager().getInputManager();
136         if (id.equals(inMan.getObjectManager().getFocusedObject())) {
137             inMan.systemMode();
138         }
139
140         prevayler.execute(new SetObjectAreaTransaction(id, null));
141         Frontend front = objectMap.remove(id);
142         BranchGroup group = transformMap.remove(id);
143         branchGraph.removeChild(group);
144         front.destroy();
145         EnsmerManager.instance().getAreaManager().fireObjectEvent(
146             new ObjectEvent(this, id, ObjectEvent.EventType.OBJECT_REMOVED));
147     }
148
149     /**
150      * Remove an object completely. This removes the object from the area and
151      * from the Prevayler, as well as cleaning up any command mappings made on
152      * it. There will be no more access to it after removal is complete. An
153      * object is only removed if it exists in this area
154      *
155      * @param id the identifier of the object to remove
156      */

157     public void destroyObject(Long JavaDoc id) {
158         if (!objectMap.containsKey(id)) {
159             return;
160         }
161         Frontend frontend = objectMap.get(id);
162         Backend backend = frontend.getBackend();
163         removeObject(id);
164         InputManager.getCommandMap().removeAllCommands(backend);
165         prevayler.execute(new RemoveObjectTransaction(id));
166         EnsmerManager.instance().getAreaManager().fireObjectEvent(
167             new ObjectEvent(this, id, ObjectEvent.EventType.OBJECT_DESTROYED));
168         EnsmerManager.instance().getConfigManager().objectDestroyed(id);
169     }
170
171     /**
172      * Destroy all objects in this Area. This method can be used to clear the
173      * area, but is usually used to clean up the area's objects before removal.
174      */

175     public void destroyAll() {
176         Set<Long JavaDoc> set = new HashSet<Long JavaDoc>(objectMap.keySet());
177         for (Long JavaDoc id : set) {
178             destroyObject(id);
179         }
180     }
181
182     /**
183      * Retrieve the frontend for an object in this area based on its ID. The
184      * backend can be referenced from within the frontend
185      *
186      * @param id the identifier of an object to get a frontend for.
187      * @return the frontend for the object with that id, or null if that
188      * object is not in this area.
189      */

190     public Frontend getFrontend(Long JavaDoc id) {
191         ensureLoaded(id);
192         return objectMap.get(id);
193     }
194
195     /**
196      * Retrieve a copy of the branchgroup for the frontend with the specified
197      * id. This copy can then be used in the frontends of other objects that
198      * reference that frontend for whatever reason.
199      *
200      * @param id the identifier of the object to retrieve a frontend copy of
201      * @return a branchgroup containing a copy of the frontend branchgroup
202      * ready to be added to another subgraph.
203      */

204     public BranchGroup getFrontendCopy(Long JavaDoc id) {
205         Frontend frontend = getFrontend(id);
206         if (frontend == null) {
207             return null;
208         }
209         BranchGroup retVal = frontend.getBranchGroup();
210         BranchGroup newGroup = frontend.getBranchGroup();
211         SceneGraphUtils.enableEnsmerCapabilities(newGroup);
212         newGroup.setCapability(BranchGroup.ALLOW_DETACH);
213         TransformGroup transGroup = (TransformGroup) transformMap.get(id).getChild(0);
214         transGroup.removeAllChildren();
215         transGroup.addChild(newGroup);
216         return retVal;
217     }
218
219     /**
220      * Get a list of all Backend objects currently loaded for this area.
221      *
222      * @return loaded backends for this area
223      */

224     public List<Backend> getBackends() {
225         List<Backend> list = new ArrayList<Backend>();
226         for (Frontend front : objectMap.values()) {
227             list.add(front.getBackend());
228         }
229         return list;
230     }
231
232     /**
233      * Retrieve the identifier for this area.
234      *
235      * @return The areaID value
236      */

237     public Long JavaDoc getAreaID() {
238         return areaID;
239     }
240
241     /**
242      * Get whether or not this particular area can have objects added to it.
243      *
244      * @return true if objects cannot be added, false if they can
245      */

246     public boolean isReadOnly() {
247         try {
248             return (Boolean JavaDoc) prevayler.execute(new GetAreaReadOnlyQuery(areaID));
249         } catch (Exception JavaDoc e) {
250             e.printStackTrace();
251             return true;
252         }
253     }
254
255     /**
256      * Get the user's position in this area. The position is retrieved from the
257      * prevayler.
258      *
259      * @return the user's position in this area or an identity matrix if no
260      * user has been set yet
261      */

262     public Matrix4f getUserPosition() {
263         try {
264             return (Matrix4f) prevayler.execute(new
265                 GetAreaUserPositionQuery(areaID));
266         } catch (Exception JavaDoc e) {
267             e.printStackTrace();
268             return new Matrix4f();
269         }
270     }
271
272     /**
273      * Get the given object's location.
274      *
275      * @param id the identifier for the object to retrieve a location for
276      * @return return the object's location in this area or null if the
277      * object does not exist in this area
278      */

279     public Matrix4f getObjectPosition(Long JavaDoc id) {
280         ensureLoaded(id);
281         if (!objectMap.containsKey(id)) {
282             return null;
283         }
284         try {
285             return (Matrix4f) prevayler.execute(new GetObjectLocationQuery(id));
286         } catch (Exception JavaDoc e) {
287             e.printStackTrace();
288             return null;
289         }
290     }
291
292     /**
293      * Sets the readOnly attribute of the Area object
294      *
295      * @param readOnly The new readOnly value
296      */

297     public void setReadOnly(boolean readOnly) {
298         try {
299             prevayler.execute(new SetAreaReadOnlyTransaction(areaID, readOnly));
300         } catch (Exception JavaDoc e) {
301             e.printStackTrace();
302         }
303     }
304
305     /**
306      * Set the user's position in this area; specifically, serialize this
307      * position to be loaded in the future.
308      *
309      * @param newPosition a Matrix4f defining the new position of the user
310      */

311     public void setUserPosition(Matrix4f newPosition) {
312         try {
313             prevayler.execute(
314                 new SetAreaUserPositionTransaction(areaID, newPosition));
315         } catch (Exception JavaDoc e) {
316             e.printStackTrace();
317         }
318     }
319
320     /**
321      * Set the position of the given object, if it exists in this area
322      *
323      * @param id the id of the object to set the position for
324      * @param newPosition a Matrix4f defining the new position of the object
325      */

326     public void setObjectPosition(Long JavaDoc id, Matrix4f newPosition) {
327         if (!objectMap.containsKey(id)) {
328             return;
329         }
330         try {
331             prevayler.execute(new SetObjectLocationTransaction(id, newPosition));
332             TransformGroup transform = (TransformGroup) transformMap.get(id).getChild(0);
333             transform.setTransform(new Transform3D(newPosition));
334         } catch (Exception JavaDoc e) {
335             e.printStackTrace();
336         }
337     }
338
339     /**
340      * Retrieve the BranchGraph for addition to the scene. This method should
341      * ensure that the BranchGraph is entirely up to date with the data and
342      * objects that the area contains.
343      *
344      * @return the object's BranchGraph
345      */

346     protected BranchGroup getBranchGraph() {
347         return branchGraph;
348     }
349
350     /**
351      * Load the objects into the area. This method is a sort of hack around the
352      * problem that objects cannot be loaded in the constructor, as serialized
353      * objects tend to need to have access to the AreaManager.
354      *
355      * @throws Exception if the objects cannot be loaded
356      */

357     void loadObjects() throws Exception JavaDoc {
358         //This allows the method to be called multiple times; it only loads
359
//the objects if they have not been finished loading
360
if (!loading) {
361             return;
362         }
363         //unchecked cast warning seems unavoidable
364
List<Long JavaDoc> prevayledObjects = (List<Long JavaDoc>) prevayler.execute(
365             new GetAreaObjectsQuery(areaID));
366         for (Long JavaDoc id : prevayledObjects) {
367             //only load it if it wasn't loaded previously 'by necessity'
368
if (!objectMap.containsKey(id)) {
369                 addObject(id);
370             }
371         }
372         loading = false;
373     }
374
375     /**
376      * Ensure that an object is fully loaded before it is referenced. This
377      * method is called each time an object is referenced, but is only relevant
378      * if the objects are in the process of being loaded.
379      *
380      * @param id the identifier for the object to ensure it is loaded
381      */

382     private void ensureLoaded(Long JavaDoc id) {
383         if (loading) {
384             if (objectMap.containsKey(id)) {
385                 return; //the object has been loaded
386
}
387             try {
388                 Long JavaDoc objArea = (Long JavaDoc) prevayler.execute(new GetAreaForObjectQuery(id));
389
390                 if (objArea != null && objArea.equals(areaID)) {
391                     addObject(id);
392                 }
393             } catch (Exception JavaDoc e) {
394                 e.printStackTrace();
395             }
396         }
397     }
398
399     /**
400      * Utility method used by both AddObject and NewObject to try to refactor
401      * out some of the duplicate code. Used to add an object to the scene when
402      * the Backend has been created and has an identifier in the prevayler.
403      * Further, the location in the prevayler is assumed to be up to date. This
404      * method instantiates a frontend, adds it to the map of frontend/long
405      * identifiers, adds it to the scene (at the correct location), and sets the
406      * area identifier in the prevayler.
407      *
408      * @param backend an instantiated backend
409      * @exception Exception Description of the Exception
410      */

411     private void setupNewObject(Backend backend) throws Exception JavaDoc {
412         Long JavaDoc id = backend.getId();
413         Frontend frontend = EnsmerManager.instance().getBackhoe().loadFrontend(backend);
414
415         Matrix4f location = (Matrix4f) prevayler.execute(new
416             GetObjectLocationQuery(id));
417
418         BranchGroup topGroup = new BranchGroup();
419         TransformGroup transGroup = new TransformGroup(new Transform3D(location));
420         BranchGroup objGroup = frontend.getBranchGroup();
421         SceneGraphUtils.enableEnsmerCapabilities(objGroup);
422         objGroup.setCapability(BranchGroup.ALLOW_DETACH);
423         transGroup.addChild(objGroup);
424         transGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
425         transGroup.setCapability(TransformGroup.ALLOW_CHILDREN_READ);
426         transGroup.setCapability(TransformGroup.ALLOW_CHILDREN_WRITE);
427         transGroup.setCapability(TransformGroup.ALLOW_CHILDREN_EXTEND);
428         topGroup.addChild(transGroup);
429         topGroup.setUserData(id);
430         topGroup.setBoundsAutoCompute(true);
431         topGroup.setCapability(BranchGroup.ENABLE_PICK_REPORTING);
432         topGroup.setCapability(Node.ALLOW_BOUNDS_READ);
433         topGroup.setCapability(Group.ALLOW_CHILDREN_READ);
434         topGroup.setCapability(BranchGroup.ALLOW_DETACH);
435         topGroup.compile();
436         branchGraph.addChild(topGroup);
437         prevayler.execute(new SetObjectAreaTransaction(id, areaID));
438         objectMap.put(id, frontend);
439         transformMap.put(id, topGroup);
440         EnsmerManager.instance().getAreaManager().fireObjectEvent(
441             new ObjectEvent(this, id, ObjectEvent.EventType.OBJECT_ADDED));
442     }
443
444     /**
445      * The BranchGraph for the Area. When the area is displayed, this
446      * BranchGraph should be the only one in the InterfaceManager's locale. The
447      * BranchGraph is created by calling methods in the <code>Backhoe</code>.
448      */

449     private BranchGroup branchGraph = new BranchGroup();
450
451     /**
452      * Map of long identifiers to Frontend objects
453      */

454     private Map<Long JavaDoc, Frontend> objectMap = new HashMap<Long JavaDoc, Frontend>();
455
456     /**
457      * Map of Long identifiers to the TransformGroup above the Frontend's group.
458      * This is used for simplifying computation (at the expense of space); it
459      * could be retrieved by iterating through the branchGraph's children and
460      * testing each UserData (userData is associated with ID)
461      */

462     private Map<Long JavaDoc, BranchGroup> transformMap = new HashMap<Long JavaDoc, BranchGroup>();
463
464     /**
465      * A reference to the Area's identifier. This identifier should not be
466      * required for access outside of the Area and the AreaManager; it is
467      * specifically used only for accessing data from the Prevayler.
468      */

469     private Long JavaDoc areaID;
470
471     /**
472      * The EnsmerManager's Prevayler. This variable is mostly just used for
473      * convenience, as it is called in most methods
474      */

475     private Prevayler prevayler;
476
477     /**
478      * boolean value to indicate if the objects are currently loading. Set to
479      * true on construction and false when the objects are loaded. It is needed
480      * so that attempts to retrieve references to an object succeed even if the
481      * object has not been loaded in order yet.
482      */

483     private boolean loading;
484 }
485
486
Popular Tags