001: /*
002: * Copyright (c) 2001 Silvere Martin-Michiellot All Rights Reserved.
003: *
004: * Silvere Martin-Michiellot grants you ("Licensee") a non-exclusive,
005: * royalty free, license to use, modify and redistribute this
006: * software in source and binary code form,
007: * provided that i) this copyright notice and license appear on all copies of
008: * the software; and ii) Licensee does not utilize the software in a manner
009: * which is disparaging to Silvere Martin-Michiellot.
010: *
011: * This software is provided "AS IS," without a warranty of any kind. ALL
012: * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
013: * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
014: * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. Silvere Martin-Michiellot
015: * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES
016: * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
017: * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
018: * Silvere Martin-Michiellot OR ITS LICENSORS BE LIABLE
019: * FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
020: * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
021: * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
022: * OR INABILITY TO USE SOFTWARE, EVEN IF Silvere Martin-Michiellot HAS BEEN
023: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
024: *
025: * This software is not designed or intended for use in on-line control of
026: * aircraft, air traffic, aircraft navigation or aircraft communications; or in
027: * the design, construction, operation or maintenance of any nuclear
028: * facility. Licensee represents and warrants that it will not use or
029: * redistribute the Software for such purposes.
030: *
031: * @Author: Silvere Martin-Michiellot
032: *
033: */
034:
035: package com.db.server;
036:
037: import java.io.*;
038: import java.security.PublicKey;
039: import java.util.*;
040: import javax.media.j3d.*;
041: import javax.vecmath.*;
042: import javax.jdo.*;
043:
044: /**
045: */
046:
047: public abstract class BackgroundWorld extends VirtualElement implements
048: Serializable {
049:
050: //Lords and self are always allowed full rights
051:
052: public final static int NO_CAPABILITY = 0;
053:
054: public final static int SALABLE = 1;
055: public final static int RESHAPEABLE = 2;
056: public final static int SOUNDALTERABLE = 4;
057: public final static int VISIBLE = 8;
058: public final static int SONORE = 16;
059: public final static int DUPLICABLE = 32;
060: //kill should be handled by a GuideWorld with specific rigths
061:
062: private static int LAST_CAPABILITY = (BackgroundWorld.DUPLICABLE * 2) - 1;
063:
064: public static int DEFAULT_DESIGNER_CAPABILITIES = BackgroundWorld.LAST_CAPABILITY;
065:
066: public final static int HIDDEN = 0;
067: public final static int READ = 1;
068: public final static int READ_WRITE = 2;
069:
070: /**
071: * collorable
072: * resizable with no shape modification
073: * reshapeable
074: * collidable
075: * visible
076: * sonore
077: * salable
078: */
079:
080: private Bounds bounds;
081: private Background backgroundDescription;
082: private BackgroundSound soundDescription;
083: private Object status;
084:
085: //user should always be the ruler (this is a wise choice)
086: //this is NOT checked
087:
088: public BackgroundWorld(UniverseServer universeServer,
089: Avatar userOwner, Bounds bounds) {
090:
091: super (universeServer, userOwner);
092: //bounds should be mutually exclusive with the Bounds from every other Background
093: if (checkBounds(bounds)) {
094: this .bounds = bounds;
095: } else {
096: throw new IllegalArgumentException(
097: "A backgroundWorld must have a valid universeServer, a valid owner and valid bounds.");
098: }
099:
100: }
101:
102: //every "get" or "set" call assumes valid user's rights (depending on who calls)
103: //otherwise the server returns a request refused message
104:
105: public final Bounds getBounds() {
106:
107: return this .bounds;
108:
109: }
110:
111: public final Background getBackgroundDescription() {
112:
113: return this .backgroundDescription;
114:
115: }
116:
117: public final BackgroundSound getSoundDescription() {
118:
119: return this .soundDescription;
120:
121: }
122:
123: public final Object getStatus() {
124:
125: return this .status;
126:
127: }
128:
129: public final void setPrice(float price) {
130:
131: if (this .checkWritable(BackgroundWorld.SALABLE)) {
132:
133: if (price >= 0) {
134: this .price = price;
135: this .setDirty();
136: this .doPriceChanged();
137: }
138:
139: }
140:
141: }
142:
143: //any valid string will fit
144: public final void setCurrency(String currency) {
145:
146: if (this .checkWritable(BackgroundWorld.SALABLE)) {
147:
148: if ((currency != null) && currency.length() > 0) {
149: this .currency = currency;
150: this .setDirty();
151: this .doCurrencyChanged();
152: }
153:
154: }
155:
156: }
157:
158: //it is possible but meaningless to set bounds outside the scope of the WorldSpot
159: public final void setBounds(Bounds bounds) {
160:
161: //bounds should be mutually exclusive with the Bounds from every other Background
162: if (checkBounds(bounds)) {
163: this .bounds = this .filterLandBounds(bounds);
164: this .setDirty();
165: this .doBoundsChanged();
166: }
167:
168: }
169:
170: //returns true if bounds don't intersect
171: private final boolean checkBounds(Bounds bounds) {
172:
173: Transaction transaction;
174: Extent extent;
175: Query query;
176: Collection collection;
177: Bounds[] boundsArray;
178: Iterator iterator;
179:
180: if ((bounds != null) && (!bounds.isEmpty())) {
181:
182: //bounds should be mutually exclusive with the Bounds from every other Background
183:
184: try {
185: transaction = this .getPersistenceManager()
186: .currentTransaction();
187: transaction.begin();
188:
189: extent = this .getPersistenceManager().getExtent(
190: BackgroundWorld.class, true);
191: query = this .getPersistenceManager().newQuery(
192: BackgroundWorld.class, extent);
193: collection = (Collection) query.execute();
194:
195: transaction.commit();
196:
197: boundsArray = new Bounds[collection.size() - 1];
198: collection.remove(this );
199: iterator = collection.iterator();
200: int i = 0;
201: while (iterator.hasNext()) {
202: boundsArray[i] = ((BackgroundWorld) iterator.next())
203: .getBounds();
204: i++;
205: }
206:
207: return !(bounds.intersect(boundsArray));
208:
209: } catch (Exception exception) {
210:
211: return false;
212:
213: }
214:
215: } else {
216: return false;
217: }
218:
219: }
220:
221: private final Bounds filterLandBounds(Bounds bounds) {
222:
223: Transaction transaction;
224: Extent extent;
225: Query query;
226: String filter;
227: Collection collection;
228: Iterator iterator;
229: HashSet resultLands;
230: Bounds resultBounds;
231: Land land;
232: int relation;
233:
234: try {
235: transaction = this .getPersistenceManager()
236: .currentTransaction();
237: transaction.begin();
238:
239: extent = this .getPersistenceManager().getExtent(Land.class,
240: true);
241: filter = new String(
242: "SELECT Land FROM Land l WHERE l.getBounds().intersect(bounds())");
243: query = this .getPersistenceManager().newQuery(Land.class,
244: extent, filter);
245: collection = (Collection) query.execute();
246:
247: transaction.commit();
248:
249: iterator = collection.iterator();
250: resultBounds = new BoundingPolytope();
251: while (iterator.hasNext()) {
252: land = (Land) iterator.next();
253: relation = Avatar.getFeudalRelation(
254: this .getUserOwner(), land.getUserOwner());
255: if ((relation == Avatar.LORD)
256: || (relation == Avatar.SELF)) {
257: resultBounds.combine(land.getBounds());
258: } else {
259: if (land.getOthersAccessRights(relation) != VirtualElement.HIDDEN) {
260: resultBounds.combine(land.getBounds());
261: }
262: }
263: }
264:
265: return resultBounds;
266:
267: } catch (Exception exception) {
268:
269: return null;
270:
271: }
272:
273: }
274:
275: //bounds should be restricted to fit the the capabilites of the lands it is on
276: //bounds may therefore be restricted to "null"
277: //center of bounds will be transfered to owner home position if current bounds correspond to an inaccessible land
278: protected final void refreshBounds() {
279:
280: Iterator iterator;
281: HashSet resultLands;
282: Bounds restrictedBounds;
283: Land land;
284: int relation;
285:
286: iterator = this .getCurrentLands().iterator();
287: restrictedBounds = new BoundingPolytope();
288: while (iterator.hasNext()) {
289: land = (Land) iterator.next();
290: relation = Avatar.getFeudalRelation(this .getUserOwner(),
291: land.getUserOwner());
292: if ((relation == Avatar.LORD) || (relation == Avatar.SELF)) {
293: restrictedBounds.combine(land.getBounds());
294: } else {
295: if (land.getOthersAccessRights(relation) != VirtualElement.HIDDEN) {
296: restrictedBounds.combine(land.getBounds());
297: }
298: }
299: }
300:
301: if (!restrictedBounds.isEmpty()) {
302: this .bounds = restrictedBounds;
303: this .setDirty();
304: this .doBoundsChanged();
305: } else {
306: this .bounds = null;
307: this .bounds
308: .transform(this .getUserOwner().getHomePosition());
309: this .setDirty();
310: this .doBoundsChanged();
311: }
312:
313: }
314:
315: //put out of inventory, unglue, unaccessory
316: protected final void refresh() {
317:
318: this .refreshBounds();
319:
320: }
321:
322: public final void setBackgroundDescription(Background background) {
323:
324: if (this .checkWritable(BackgroundWorld.RESHAPEABLE)) {
325:
326: this .backgroundDescription = background;
327: this .setDirty();
328: this .doBackgroundDescriptionChanged();
329: }
330:
331: }
332:
333: public final void setBackgroundSoundDescription(
334: BackgroundSound backgroundSound) {
335:
336: if (this .checkWritable(BackgroundWorld.SOUNDALTERABLE)) {
337:
338: this .soundDescription = backgroundSound;
339: this .setDirty();
340: this .doBackgroundSoundDescriptionChanged();
341:
342: }
343:
344: }
345:
346: //next calls to make an object disappear when inventorized and reappear
347: private Background invisibleGeometry;
348: private BackgroundSound inaudibleSound;
349:
350: public void setInvisible() {
351:
352: if (this .checkWritable(BackgroundWorld.VISIBLE)) {
353:
354: if (this .isVisible()) {
355: this .invisibleGeometry = this .backgroundDescription;
356: this .backgroundDescription = null;
357: }
358:
359: }
360:
361: }
362:
363: protected boolean isVisible() {
364:
365: return (this .invisibleGeometry == null);
366:
367: }
368:
369: public void setVisible() {
370:
371: if (this .checkWritable(BackgroundWorld.VISIBLE)) {
372:
373: if (this .backgroundDescription == null) {
374: this .backgroundDescription = invisibleGeometry;
375: this .invisibleGeometry = null;
376: }
377:
378: }
379:
380: }
381:
382: public void setUnSonore() {
383:
384: if (this .checkWritable(BackgroundWorld.SONORE)) {
385:
386: if (this .isSonore()) {
387: this .inaudibleSound = this .soundDescription;
388: this .soundDescription = null;
389: }
390:
391: }
392:
393: }
394:
395: protected boolean isSonore() {
396:
397: return (this .inaudibleSound == null);
398:
399: }
400:
401: public void setSonore() {
402:
403: if (this .checkWritable(BackgroundWorld.SONORE)) {
404:
405: if (this .inaudibleSound == null) {
406: this .soundDescription = inaudibleSound;
407: this .inaudibleSound = null;
408: }
409:
410: }
411:
412: }
413:
414: //does not fire a status change
415: public final void resetStatus() {
416:
417: this .status = null;
418: this .setDirty();
419:
420: }
421:
422: public final void setStatus(Object status) {
423:
424: //every access to setStatus should be send automatically to system
425: this .status = status;
426: this .setDirty();
427: this .doStatusChanged();
428:
429: }
430:
431: public final Bounds getGeometryBounds() {
432:
433: return this .backgroundDescription.getBounds();
434:
435: }
436:
437: //please note we don't return this.getBackgroundDescription().getApplicationBounds();
438: //but this.getBounds();
439: //system gets this value to build out the Background to the client and internally set the Application Bounds of the Background
440: public final Bounds getApplicationBounds() {
441:
442: return this .getBounds();
443:
444: }
445:
446: public final Bounds getSoundBounds() {
447:
448: return this .soundDescription.getSchedulingBounds();
449:
450: }
451:
452: public final BoundingLeaf getSoundBoundingLeaf() {
453:
454: return this .soundDescription.getSchedulingBoundingLeaf();
455:
456: }
457:
458: //returns the lands this object is currently on if available
459: protected final Collection getCurrentLands() {
460:
461: Transaction transaction;
462: Extent extent;
463: Query query;
464: String filter;
465: Collection collection;
466:
467: try {
468: transaction = this .getPersistenceManager()
469: .currentTransaction();
470: transaction.begin();
471:
472: extent = this .getPersistenceManager().getExtent(Land.class,
473: true);
474: filter = new String(
475: "SELECT Land FROM Land l WHERE l.getBounds().intersect(this.getBounds())");
476: query = this .getPersistenceManager().newQuery(Land.class,
477: extent, filter);
478: collection = (Collection) query.execute();
479:
480: transaction.commit();
481:
482: return collection;
483:
484: } catch (Exception exception) {
485:
486: return null;
487:
488: }
489:
490: }
491:
492: //the land against which security access are performed
493: //bounds may be on multiple lands at a time but are assumed to be "located around their center" and have the properties of this land
494: //not filtered
495: protected final Land getMajorLand() {
496:
497: Transaction transaction;
498: Extent extent;
499: Query query;
500: String filter;
501: Collection collection;
502: BoundingSphere boundingSphere;
503:
504: boundingSphere = new BoundingSphere(this .getBounds());
505:
506: try {
507: transaction = this .getPersistenceManager()
508: .currentTransaction();
509: transaction.begin();
510:
511: extent = this .getPersistenceManager().getExtent(Land.class,
512: true);
513: filter = new String(
514: "SELECT Land FROM Land l WHERE l.getBounds().intersect(boundingSphere)");
515: query = this .getPersistenceManager().newQuery(Land.class,
516: extent, filter);
517: collection = (Collection) query.execute();
518:
519: transaction.commit();
520:
521: //should check the biggest intersection is returned
522: //XXXXXX
523: return (Land) collection.iterator().next();
524:
525: } catch (Exception exception) {
526:
527: return null;
528:
529: }
530:
531: }
532:
533: public final HashSet getCurrentLands(Avatar caller) {
534:
535: Transaction transaction;
536: Extent extent;
537: Query query;
538: String filter;
539: Collection collection;
540:
541: if (caller != null) {
542: try {
543: transaction = this .getPersistenceManager()
544: .currentTransaction();
545: transaction.begin();
546:
547: extent = this .getPersistenceManager().getExtent(
548: Land.class, true);
549: filter = new String(
550: "SELECT Land FROM Land l WHERE l.getBounds().intersect(this.getBounds())");
551: query = this .getPersistenceManager().newQuery(
552: Land.class, extent, filter);
553: collection = (Collection) query.execute();
554:
555: transaction.commit();
556:
557: return this .filterCollection(caller, collection);
558:
559: } catch (Exception exception) {
560:
561: return null;
562:
563: }
564:
565: } else {
566: return null;
567: }
568:
569: }
570:
571: public final void duplicate() {
572:
573: BackgroundWorld backgroundWorld;
574: Transform3D otherTransform;
575: BoundingPolytope newBounds;
576: Transaction transaction;
577: Point3d center;
578:
579: if (this .checkWritable(BackgroundWorld.DUPLICABLE)) {
580:
581: try {
582:
583: //backgroundWorld = new BackgroundWorld(this.getPersistenceManager(), this.getUserOwner());
584: backgroundWorld = (BackgroundWorld) this .clone();
585:
586: //backgroundWorld.setBackgroundDescription(this.getBackgroundDescription());
587: //backgroundWorld.setSoundDescription(this.getSoundDescription());
588: newBounds = new BoundingPolytope(this .getBounds());
589: otherTransform = new Transform3D();
590: center = new Point3d();
591: new BoundingSphere(this .getBounds()).getCenter(center);
592: otherTransform.set(new Vector3d(center));
593: otherTransform.add(new Transform3D(new Matrix3d(),
594: new Vector3d(1, 0, 1), 1));
595: newBounds.transform(otherTransform);
596: backgroundWorld.setBounds(newBounds);
597: //backgroundWorld.setStatus(this.getStatus());
598: //backgroundWorld.setInformation(this.getInformation());
599: //backgroundWorld.setPrice(this.getPrice());
600: //backgroundWorld.setCurrency(this.getCurrency());
601: //backgroundWorld.setOthersAccessRights(this.getOthersAccessRights());
602:
603: try {
604: transaction = this .getPersistenceManager()
605: .currentTransaction();
606: transaction.begin();
607:
608: this .getPersistenceManager().makePersistent(
609: backgroundWorld);
610:
611: transaction.commit();
612: } catch (Exception exception) {
613: }
614:
615: this .doDuplicate();
616:
617: } catch (CloneNotSupportedException cloneNotSupportedException) {
618: }
619:
620: }
621:
622: }
623:
624: private final static HashSet filterCollection(Avatar caller,
625: Collection collection) {
626:
627: Iterator iterator;
628: Iterator iterator2;
629: Collection passCollection;
630: HashSet resultCollection;
631: int relation;
632: VirtualElement virtualElement;
633: boolean found;
634: Pass pass;
635: Avatar avatar;
636:
637: resultCollection = new HashSet();
638:
639: if (caller != null) {
640:
641: //objects with a pass may bypass access
642: passCollection = caller.getPass();
643:
644: iterator = collection.iterator();
645:
646: while (iterator.hasNext()) {
647:
648: virtualElement = (VirtualElement) iterator.next();
649: relation = Avatar.getFeudalRelation(caller,
650: virtualElement.getUserOwner());
651:
652: if ((relation == Avatar.LORD)
653: || (relation == Avatar.SELF)) {
654: resultCollection.add(virtualElement);
655: } else {
656: if (virtualElement.getOthersAccessRights(relation) != VirtualElement.HIDDEN) {
657: resultCollection.add(virtualElement);
658: } else {
659: iterator2 = passCollection.iterator();
660: found = false;
661: while (iterator2.hasNext() && (!found)) {
662: pass = (Pass) iterator2.next();
663: found = (pass.getObject() == virtualElement);
664: }
665: if (found) {
666: resultCollection.add(virtualElement);
667: }
668: }
669: }
670:
671: }
672:
673: }
674:
675: return resultCollection;
676:
677: }
678:
679: //uses caller
680: protected final boolean checkWritable(int capabilities) {
681:
682: int relation;
683: Land land;
684:
685: relation = Avatar.getFeudalRelation(this .getUserOwner(), this
686: .getCaller());
687:
688: if ((relation == Avatar.LORD) || (relation == Avatar.SELF)) {
689: return true;
690: } else {
691: if (this .getOthersAccessRight(relation,
692: VirtualElement.READ_WRITE)) {
693: //if object is owned by the land owner
694: land = this .getMajorLand();
695: if (land.getUserOwner() == this .getUserOwner()) {
696: return land.getOwnersObjectsDefaultRights(relation,
697: capabilities);
698: } else {
699: return true;
700: }
701: } else {
702: return false;
703: }
704: }
705:
706: }
707:
708: //time sliced actions
709:
710: //modifies the Geometry including colors or size
711: public void doBackgroundDescriptionChanged() {
712:
713: }
714:
715: public void doBackgroundSoundDescriptionChanged() {
716:
717: }
718:
719: //when there is a change in the bounds
720: public void doBoundsChanged() {
721:
722: }
723:
724: //by default reacts to status change of this object
725: //which does nothing but indirectly sends a notification to the system
726: //designers can if they wish trigger some actions (doResize, doColor...)
727: public void doStatusChanged() {
728:
729: }
730:
731: public void doDuplicate() {
732: //resulting array should contain original ObjectWorld at position 0 and the copy at position 1
733: //Objects should both be in the database as any other Object and differ only by their position
734:
735: }
736:
737: public void doVisible() {
738: //when the object becomes visible for a user
739:
740: //the designer has to put something here if he wants a visible object
741: //Designers should use the modifier "final" to ensure there is no one counterfeiting their identity
742: //if this is the desired behavior
743:
744: }
745:
746: public void doInvisible() {
747: //when the object becomes invisible for a user
748:
749: }
750:
751: public void doSonore() {
752: //when the object becomes audible for a user
753:
754: //the designer has to put something here if he wants an audible object
755: //Designers should use the modifier "final" to ensure there is no one counterfeiting their identity
756: //if this is the desired behavior
757:
758: }
759:
760: public void doUnSonore() {
761: //when the object becomes inaudible for a user
762:
763: }
764:
765: }
|