0001: /*
0002: * $RCSfile: ConfigContainer.java,v $
0003: *
0004: * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * Redistribution and use in source and binary forms, with or without
0007: * modification, are permitted provided that the following conditions
0008: * are met:
0009: *
0010: * - Redistribution of source code must retain the above copyright
0011: * notice, this list of conditions and the following disclaimer.
0012: *
0013: * - Redistribution in binary form must reproduce the above copyright
0014: * notice, this list of conditions and the following disclaimer in
0015: * the documentation and/or other materials provided with the
0016: * distribution.
0017: *
0018: * Neither the name of Sun Microsystems, Inc. or the names of
0019: * contributors may be used to endorse or promote products derived
0020: * from this software without specific prior written permission.
0021: *
0022: * This software is provided "AS IS," without a warranty of any
0023: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
0024: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
0025: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
0026: * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
0027: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
0028: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
0029: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
0030: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
0031: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
0032: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
0033: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
0034: * POSSIBILITY OF SUCH DAMAGES.
0035: *
0036: * You acknowledge that this software is not designed, licensed or
0037: * intended for use in the design, construction, operation or
0038: * maintenance of any nuclear facility.
0039: *
0040: * $Revision: 1.6 $
0041: * $Date: 2007/02/09 17:20:43 $
0042: * $State: Exp $
0043: */
0044:
0045: package com.sun.j3d.utils.universe;
0046:
0047: import java.io.*;
0048: import java.util.*;
0049: import java.net.URL;
0050: import java.net.MalformedURLException;
0051: import javax.media.j3d.*;
0052: import com.sun.j3d.utils.behaviors.vp.ViewPlatformBehavior;
0053:
0054: /**
0055: * Loads a Java 3D configuration file and creates a container of named objects
0056: * that will effect the viewing configuration specified in the file. These
0057: * can include Viewers, ViewingPlatforms, ViewPlatformBehaviors, InputDevices,
0058: * Sensors, and other objects.<p>
0059: *
0060: * Clients can construct the view side of a scene graph by retrieving these
0061: * objects using the accessor methods provided by this class. This could
0062: * involve as little as just attaching ViewingPlatforms to a Locale, depending
0063: * upon how completely the viewing configuration is specified in the file.
0064: * The ConfiguredUniverse class is an example of a ConfigContainer client and
0065: * how it can be used.<p>
0066: *
0067: * ConfigContainer can be useful for clients other than ConfiguredUniverse.
0068: * InputDevice and ViewPlatformBehavior configuration is fully supported, so a
0069: * given Java 3D installation can provide configuration files to an
0070: * application that will allow it to fully utilize whatever site-specific
0071: * devices and behaviors are available. The configuration mechanism can be
0072: * extended for any target object through the use of the
0073: * <code>NewObject</code> and <code>ObjectProperty</code> configuration
0074: * commands.
0075: *
0076: * @see ConfiguredUniverse
0077: * @see <a href="doc-files/config-syntax.html">
0078: * The Java 3D Configuration File</a>
0079: * @see <a href="doc-files/config-examples.html">
0080: * Example Configuration Files</a>
0081: *
0082: * @since Java 3D 1.3.1
0083: */
0084: public class ConfigContainer {
0085: //
0086: // The configuration object database is implemented with a HashMap which
0087: // maps their class names to ArrayList objects which contain the actual
0088: // instances. The latter are used since the instances of a given class
0089: // must be evaluated in the order in which they were created.
0090: // LinkedHashMap is available in JDK 1.4 but currently this code must run
0091: // under JDK 1.3.1 as well.
0092: //
0093: private Map baseNameMap = new HashMap();
0094:
0095: // Map containing named canvases for each view.
0096: private Map viewCanvasMap = new HashMap();
0097:
0098: // Read-only Maps for the public interface to the configuration database.
0099: private ReadOnlyMap bodyMap = null;
0100: private ReadOnlyMap environmentMap = null;
0101: private ReadOnlyMap viewerMap = null;
0102: private ReadOnlyMap deviceMap = null;
0103: private ReadOnlyMap sensorMap = null;
0104: private ReadOnlyMap behaviorMap = null;
0105: private ReadOnlyMap platformMap = null;
0106: private ReadOnlyMap genericObjectMap = null;
0107:
0108: // Read-only Sets for the public interface to the configuration database.
0109: private ReadOnlySet bodies = null;
0110: private ReadOnlySet environments = null;
0111: private ReadOnlySet viewers = null;
0112: private ReadOnlySet devices = null;
0113: private ReadOnlySet sensors = null;
0114: private ReadOnlySet behaviors = null;
0115: private ReadOnlySet platforms = null;
0116: private ReadOnlySet genericObjects = null;
0117:
0118: // The number of TransformGroups to include in ViewingPlatforms.
0119: private int transformCount = 1;
0120:
0121: // The visibility status of Viewer AWT components.
0122: private boolean setVisible = false;
0123:
0124: private ClassLoader classLoader = ClassLoader
0125: .getSystemClassLoader();
0126:
0127: /**
0128: * The name of the file this ConfigContainer is currently loading.
0129: */
0130: String currentFileName = null;
0131:
0132: /**
0133: * Creates a new ConfigContainer and loads the configuration file at the
0134: * specified URL. All ViewingPlatform instances are created with a single
0135: * TransformGroup and all Viewer components are initially invisible.
0136: *
0137: * @param userConfig URL of the configuration file to load
0138: */
0139: public ConfigContainer(URL userConfig) {
0140: this (userConfig, false, 1, true);
0141: }
0142:
0143: /**
0144: * Creates a new ConfigContainer and loads the configuration file at the
0145: * specified URL. All ViewingPlatform instances are created with a single
0146: * TransformGroup and all Viewer components are initially invisible.
0147: *
0148: * @param userConfig URL of the configuration file to load
0149: * @param classLoader the class loader to use to load classes specified
0150: * in the config file.
0151: */
0152: public ConfigContainer(URL userConfig, ClassLoader classLoader) {
0153: this (userConfig, false, 1, true, classLoader);
0154: }
0155:
0156: /**
0157: * Creates a new ConfigContainer and loads the configuration file at the
0158: * specified URL. Any ViewingPlatform instantiated by the configuration
0159: * file will be created with the specified number of transforms. Viewer
0160: * components may be set initially visible or invisible with the
0161: * <code>setVisible</code> flag.
0162: *
0163: * @param userConfig URL of the configuration file to load
0164: * @param setVisible if true, <code>setVisible(true)</code> is called on
0165: * all Viewers
0166: * @param transformCount number of transforms to be included in any
0167: * ViewingPlatform created; must be greater than 0
0168: */
0169: public ConfigContainer(URL userConfig, boolean setVisible,
0170: int transformCount) {
0171:
0172: this (userConfig, setVisible, transformCount, true);
0173: }
0174:
0175: /**
0176: * Creates a new ConfigContainer and loads the configuration file at the
0177: * specified URL. Any ViewingPlatform instantiated by the configuration
0178: * file will be created with the specified number of transforms. Viewer
0179: * components may be set initially visible or invisible with the
0180: * <code>setVisible</code> flag.
0181: *
0182: * @param userConfig URL of the configuration file to load
0183: * @param setVisible if true, <code>setVisible(true)</code> is called on
0184: * all Viewers
0185: * @param transformCount number of transforms to be included in any
0186: * ViewingPlatform created; must be greater than 0
0187: * @param classLoader the class loader to use to load classes specified
0188: * in the config file.
0189: */
0190: public ConfigContainer(URL userConfig, boolean setVisible,
0191: int transformCount, ClassLoader classLoader) {
0192:
0193: this (userConfig, setVisible, transformCount, true, classLoader);
0194: }
0195:
0196: /**
0197: * Package-scoped constructor for ConfigContainer. This provides an
0198: * additional flag, <code>attachBehaviors</code>, which indicates whether
0199: * or not ViewPlatformBehaviors should be attached to the ViewingPlatforms
0200: * specified for them.<p>
0201: *
0202: * Normally the flag should be true. However, when instantiated by
0203: * ConfiguredUniverse, this flag is set false so that ConfiguredUniverse
0204: * can set a reference to itself in the ViewingPlatform before attaching
0205: * the behavior. This provides backwards compatibility to behaviors that
0206: * access the ConfiguredUniverse instance from a call to
0207: * <code>setViewingPlatform</code> in order to look up the actual Sensor,
0208: * Viewer, Behavior, etc., instances associated with the names provided
0209: * them from the configuration file.<p>
0210: *
0211: * The preferred methods to retrieve instances of specific objects defined
0212: * in the configuration file are to either 1) get the ConfiguredUniverse
0213: * instance when the behavior's <code>initialize</code> method is called,
0214: * or to 2) define properties that accept object instances directly, and
0215: * then use the newer Device, Sensor, ViewPlatform, etc., built-in
0216: * commands in the configuration file. These built-ins will return an
0217: * object instance from a name.
0218: *
0219: * @param userConfig URL of the configuration file to load
0220: * @param setVisible if true, <code>setVisible(true)</code> is called on
0221: * all Viewers
0222: * @param transformCount number of transforms to be included in any
0223: * ViewingPlatform created; must be greater than 0
0224: * @param attachBehaviors if true, attach ViewPlatformBehaviors to the
0225: * appropriate ViewingPlatforms
0226: */
0227: ConfigContainer(URL userConfig, boolean setVisible,
0228: int transformCount, boolean attachBehaviors) {
0229:
0230: if (transformCount < 1)
0231: throw new IllegalArgumentException(
0232: "transformCount must be greater than 0");
0233:
0234: loadConfig(userConfig);
0235: processConfig(setVisible, transformCount, attachBehaviors);
0236: }
0237:
0238: /**
0239: * Package scoped constructor that adds the ability to set the ClassLoader
0240: * which will be used to load any app specific classes specified in the
0241: * configuration file. By default SystemClassLoader is used.
0242: */
0243: ConfigContainer(URL userConfig, boolean setVisible,
0244: int transformCount, boolean attachBehaviors,
0245: ClassLoader classLoader) {
0246: this (userConfig, setVisible, transformCount, attachBehaviors);
0247: this .classLoader = classLoader;
0248: }
0249:
0250: /**
0251: * Open, parse, and load the contents of a configuration file.
0252: *
0253: * @param userConfig location of the configuration file
0254: */
0255: private void loadConfig(URL userConfig) {
0256: InputStream inputStream = null;
0257: StreamTokenizer streamTokenizer = null;
0258: String lastFileName = currentFileName;
0259:
0260: currentFileName = userConfig.toString();
0261: try {
0262: inputStream = userConfig.openStream();
0263: Reader r = new BufferedReader(new InputStreamReader(
0264: inputStream));
0265: streamTokenizer = new StreamTokenizer(r);
0266: } catch (IOException e) {
0267: throw new IllegalArgumentException(e + "\nUnable to open "
0268: + currentFileName);
0269: }
0270:
0271: //
0272: // Set up syntax tables for the tokenizer.
0273: //
0274: // It would be nice to allow '/' as a word constituent for URL strings
0275: // and Unix paths, but then the scanner won't ignore "//" and "/* */"
0276: // comment style syntax. Treating '/' as an ordinary character will
0277: // allow comments to work, but then '/' becomes a single token which
0278: // has to be concatenated with subsequent tokens to reconstruct the
0279: // original word string.
0280: //
0281: // It is cleaner to just require quoting for forward slashes. '/'
0282: // should still be treated as an ordinary character however, so that a
0283: // non-quoted URL string or Unix path will be treated as a syntax
0284: // error instead of a comment.
0285: //
0286: streamTokenizer.ordinaryChar('/');
0287: streamTokenizer.wordChars('_', '_');
0288: streamTokenizer.wordChars('$', '$'); // for ${...} Java property
0289: streamTokenizer.wordChars('{', '}'); // substitution in word tokens
0290: streamTokenizer.slashSlashComments(true);
0291: streamTokenizer.slashStarComments(true);
0292:
0293: // Create an s-expression parser to use for all top-level (0) commands.
0294: ConfigSexpression sexp = new ConfigSexpression();
0295:
0296: // Loop through all top-level commands. Boolean.FALSE is returned
0297: // after the last one is evaluated.
0298: while (sexp.parseAndEval(this , streamTokenizer, 0) != Boolean.FALSE)
0299: ;
0300:
0301: // Close the input stream.
0302: try {
0303: inputStream.close();
0304: } catch (IOException e) {
0305: throw new IllegalArgumentException(e + "\nUnable to close "
0306: + currentFileName);
0307: }
0308:
0309: // Restore current file name.
0310: currentFileName = lastFileName;
0311: }
0312:
0313: /**
0314: * This method gets called from the s-expression parser to process a
0315: * configuration command.
0316: *
0317: * @param elements tokenized list of sexp elements
0318: * @param lineNumber command line number
0319: */
0320: void evaluateCommand(ArrayList elements, int lineNumber) {
0321: ConfigObject co;
0322: ConfigCommand cmd;
0323:
0324: // Create a command object.
0325: cmd = new ConfigCommand(elements, currentFileName, lineNumber);
0326:
0327: // Process the command according to its type.
0328: switch (cmd.type) {
0329: case ConfigCommand.CREATE:
0330: co = createConfigObject(cmd);
0331: addConfigObject(co);
0332: break;
0333: case ConfigCommand.ALIAS:
0334: co = createConfigAlias(cmd);
0335: addConfigObject(co);
0336: break;
0337: case ConfigCommand.PROPERTY:
0338: co = findConfigObject(cmd.baseName, cmd.instanceName);
0339: co.setProperty(cmd);
0340: break;
0341: case ConfigCommand.INCLUDE:
0342: if (!(cmd.argv[1] instanceof String)) {
0343: throw new IllegalArgumentException(
0344: "Include file must be a URL string");
0345: }
0346: URL url = null;
0347: String urlString = (String) cmd.argv[1];
0348: try {
0349: url = new URL(urlString);
0350: } catch (MalformedURLException e) {
0351: throw new IllegalArgumentException(e.toString());
0352: }
0353: loadConfig(url);
0354: break;
0355: case ConfigCommand.IGNORE:
0356: break;
0357: default:
0358: throw new IllegalArgumentException("Unknown command \""
0359: + cmd.commandName + "\"");
0360: }
0361: }
0362:
0363: /**
0364: * Instantiates and initializes an object that extends the ConfigObject
0365: * base class. The class name of the object is derived from the
0366: * command, which is of the following form:<p>
0367: *
0368: * (New{baseName} {instanceName} ... [Alias {aliasName}])<p>
0369: *
0370: * The first two command elements and the optional trailing Alias syntax
0371: * are processed here, at which point the subclass implementation of
0372: * initialize() is called. Subclasses must override initialize() if they
0373: * need to process more than what is processed by default here.
0374: *
0375: * @param cmd configuration command that creates a new ConfigObject
0376: */
0377: private ConfigObject createConfigObject(ConfigCommand cmd) {
0378: Class objectClass = null;
0379: ConfigObject configObject = null;
0380:
0381: // Instantatiate the ConfigObject if possible. This is not the target
0382: // object, but an object that will gather configuration properties,
0383: // instantiate the target object, and then apply the configuration
0384: // properties to it.
0385: try {
0386: objectClass = Class
0387: .forName("com.sun.j3d.utils.universe.Config"
0388: + cmd.baseName);
0389: } catch (ClassNotFoundException e) {
0390: throw new IllegalArgumentException("\"" + cmd.baseName
0391: + "\""
0392: + " is not a configurable object; ignoring command");
0393: }
0394: try {
0395: configObject = (ConfigObject) (objectClass.newInstance());
0396: } catch (IllegalAccessException e) {
0397: System.out.println(e);
0398: throw new IllegalArgumentException("Ignoring command");
0399: } catch (InstantiationException e) {
0400: System.out.println(e);
0401: throw new IllegalArgumentException("Ignoring command");
0402: }
0403:
0404: // Process an Alias keyword if present. This option is available for
0405: // all New commands so it is processed here. The Alias keyword must
0406: // be the penultimate command element, followed by a String.
0407: for (int i = 2; i < cmd.argc; i++) {
0408: if (cmd.argv[i] instanceof String
0409: && ((String) cmd.argv[i]).equals("Alias")) {
0410: if (i == (cmd.argc - 2)
0411: && cmd.argv[i + 1] instanceof String) {
0412: addConfigObject(new ConfigAlias(cmd.baseName,
0413: (String) cmd.argv[i + 1], configObject));
0414: cmd.argc -= 2;
0415: } else {
0416: throw new IllegalArgumentException(
0417: "The alias name must be a string and "
0418: + "must be the last command argument");
0419: }
0420: }
0421: }
0422:
0423: // Initialize common fields.
0424: configObject.baseName = cmd.baseName;
0425: configObject.instanceName = cmd.instanceName;
0426: configObject.creatingCommand = cmd;
0427: configObject.configContainer = this ;
0428:
0429: // Initialize specific fields and return the ConfigObject.
0430: configObject.setClassLoader(classLoader);
0431: configObject.initialize(cmd);
0432: return configObject;
0433: }
0434:
0435: /**
0436: * Instantiate and initialize a ConfigObject base class containing alias
0437: * information. The command is of the form:<p>
0438: *
0439: * ({baseName}Alias {aliasName} {originalName})
0440: *
0441: * @param cmd configuration command that creates a new alias
0442: * @return the new ConfigObject with alias information
0443: */
0444: private ConfigObject createConfigAlias(ConfigCommand cmd) {
0445: ConfigObject original;
0446:
0447: if (cmd.argc != 3 || !(cmd.argv[2] instanceof String))
0448: throw new IllegalArgumentException("Command \""
0449: + cmd.commandName
0450: + "\" requires an instance name as second argument");
0451:
0452: original = findConfigObject(cmd.baseName, (String) cmd.argv[2]);
0453: return new ConfigAlias(cmd.baseName, cmd.instanceName, original);
0454: }
0455:
0456: /**
0457: * A class that does nothing but reference another ConfigObject. Once
0458: * created, the alias name can be used in all commands that would accept
0459: * the original name. A lookup of the alias name will always return the
0460: * original instance.
0461: */
0462: private static class ConfigAlias extends ConfigObject {
0463: ConfigAlias(String baseName, String instanceName,
0464: ConfigObject targ) {
0465: this .baseName = baseName;
0466: this .instanceName = instanceName;
0467: this .isAlias = true;
0468: this .original = targ;
0469: targ.aliases.add(instanceName);
0470: }
0471: }
0472:
0473: /**
0474: * Adds the specified ConfigObject instance into this container using the
0475: * given ConfigCommand's base name and instance name.
0476: *
0477: * @param object the ConfigObject instance to add into the database
0478: */
0479: private void addConfigObject(ConfigObject object) {
0480: ArrayList instances;
0481:
0482: instances = (ArrayList) baseNameMap.get(object.baseName);
0483: if (instances == null) {
0484: instances = new ArrayList();
0485: baseNameMap.put(object.baseName, instances);
0486: }
0487:
0488: // Disallow duplicate instance names.
0489: for (int i = 0; i < instances.size(); i++) {
0490: ConfigObject co = (ConfigObject) instances.get(i);
0491: if (co.instanceName.equals(object.instanceName)) {
0492: // Don't confuse anybody using Window.
0493: String base = object.baseName;
0494: if (base.equals("Screen"))
0495: base = "Screen or Window";
0496: throw new IllegalArgumentException("Duplicate " + base
0497: + " instance name \"" + object.instanceName
0498: + "\" ignored");
0499: }
0500: }
0501:
0502: instances.add(object);
0503: }
0504:
0505: /**
0506: * Finds a config object matching the given base name and the instance
0507: * name. If an alias is found, then its original is returned. If the
0508: * object is not found, an IllegalArgumentException is thrown.<p>
0509: *
0510: * @param basename base name of the config object
0511: * @param instanceName name associated with this config object instance
0512: * @return the found ConfigObject
0513: */
0514: ConfigObject findConfigObject(String baseName, String instanceName) {
0515: ArrayList instances;
0516: ConfigObject configObject;
0517:
0518: instances = (ArrayList) baseNameMap.get(baseName);
0519: if (instances != null) {
0520: for (int i = 0; i < instances.size(); i++) {
0521: configObject = (ConfigObject) instances.get(i);
0522:
0523: if (configObject.instanceName.equals(instanceName)) {
0524: if (configObject.isAlias)
0525: return configObject.original;
0526: else
0527: return configObject;
0528: }
0529: }
0530: }
0531:
0532: // Throw an error, but don't confuse anybody using Window.
0533: if (baseName.equals("Screen"))
0534: baseName = "Screen or Window";
0535: throw new IllegalArgumentException(baseName + " \""
0536: + instanceName + "\" not found");
0537: }
0538:
0539: /**
0540: * Find instances of config objects with the given base name.
0541: * This is the same as <code>findConfigObjects(baseName, true)</code>.
0542: * Aliases are filtered out so that all returned instances are unique.
0543: *
0544: * @param baseName base name of desired config object class
0545: * @return ArrayList of config object instances of the desired base
0546: * class, or null if instances of the base class don't exist
0547: */
0548: Collection findConfigObjects(String baseName) {
0549: return findConfigObjects(baseName, true);
0550: }
0551:
0552: /**
0553: * Find instances of config objects with the given base name.
0554: *
0555: * @param baseName base name of desired config object class
0556: * @param filterAlias if true, aliases are filtered out so that all
0557: * returned instances are unique
0558: * @return ArrayList of config object instances of the desired base
0559: * class, or null if instances of the base class don't exist
0560: */
0561: Collection findConfigObjects(String baseName, boolean filterAlias) {
0562: ArrayList instances;
0563:
0564: instances = (ArrayList) baseNameMap.get(baseName);
0565: if (instances == null || instances.size() == 0) {
0566: return null; // This is not an error.
0567: }
0568:
0569: if (filterAlias) {
0570: ArrayList output = new ArrayList();
0571: for (int i = 0; i < instances.size(); i++) {
0572: ConfigObject configObject = (ConfigObject) instances
0573: .get(i);
0574:
0575: if (!configObject.isAlias) {
0576: output.add(configObject);
0577: }
0578: }
0579: return output;
0580: } else {
0581: return instances;
0582: }
0583: }
0584:
0585: /**
0586: * Returns the ConfigObject associated with the name in the given
0587: * ConfigCommand. This is used for evaluating retained built-in commands
0588: * after the config file has already been parsed. The parser won't catch
0589: * any of the exceptions generated by this method, so the error messages
0590: * are wrapped accordingly.
0591: *
0592: * @param basename base name of the config object
0593: * @param cmd command containing the name in argv[1]
0594: * @return the found ConfigObject
0595: */
0596: private ConfigObject findConfigObject(String baseName,
0597: ConfigCommand cmd) {
0598: if (cmd.argc != 2 || !(cmd.argv[1] instanceof String))
0599: throw new IllegalArgumentException(ConfigObject
0600: .errorMessage(cmd,
0601: "Parameter must be a single string"));
0602: try {
0603: return findConfigObject(baseName, (String) cmd.argv[1]);
0604: } catch (IllegalArgumentException e) {
0605: throw new IllegalArgumentException(ConfigObject
0606: .errorMessage(cmd, e.getMessage()));
0607: }
0608: }
0609:
0610: /**
0611: * This method gets called from a ConfigObject to evaluate a retained
0612: * built-in command nested within a property command. These are commands
0613: * that can't be evaluated until the entire config file is parsed.
0614: *
0615: * @param cmd the built-in command
0616: * @return object representing result of evaluation
0617: */
0618: Object evaluateBuiltIn(ConfigCommand cmd) {
0619: int argc = cmd.argc;
0620: Object[] argv = cmd.argv;
0621:
0622: if (cmd.commandName.equals("ConfigContainer")) {
0623: // return a reference to this ConfigContainer
0624: return this ;
0625: } else if (cmd.commandName.equals("Canvas3D")) {
0626: // Look for canvases in the screen database.
0627: return ((ConfigScreen) findConfigObject("Screen", cmd)).j3dCanvas;
0628: } else if (baseNameMap.get(cmd.commandName) != null) {
0629: // Handle commands of the form ({objectType} name) that return the
0630: // object associated with the name.
0631: return findConfigObject(cmd.commandName, cmd).targetObject;
0632: } else {
0633: // So far no other retained built-in commands.
0634: throw new IllegalArgumentException(ConfigObject
0635: .errorMessage(cmd, "Unknown built-in command \""
0636: + cmd.commandName + "\""));
0637: }
0638: }
0639:
0640: /**
0641: * Process the configuration after parsing the configuration file.
0642: * Note: the processing order of the various config objects is
0643: * significant.
0644: *
0645: * @param setVisible true if Viewer components should be visible
0646: * @param transformCount number of TransformGroups with which
0647: * ViewingPlatforms should be created
0648: * @param attachBehaviors true if behaviors should be attached to
0649: * ViewingPlatforms
0650: */
0651: private void processConfig(boolean setVisible, int transformCount,
0652: boolean attachBehaviors) {
0653:
0654: Collection c, s, pe, vp;
0655: this .setVisible = setVisible;
0656: this .transformCount = transformCount;
0657:
0658: c = findConfigObjects("PhysicalBody");
0659: if (c != null) {
0660: processPhysicalBodies(c);
0661: }
0662:
0663: pe = findConfigObjects("PhysicalEnvironment");
0664: if (pe != null) {
0665: processPhysicalEnvironments(pe);
0666: }
0667:
0668: c = findConfigObjects("View");
0669: if (c != null) {
0670: processViews(c, setVisible);
0671: }
0672:
0673: c = findConfigObjects("Device");
0674: s = findConfigObjects("Sensor");
0675: if (c != null) {
0676: processDevices(c, s, pe);
0677: }
0678:
0679: vp = findConfigObjects("ViewPlatform");
0680: if (vp != null) {
0681: processViewPlatforms(vp, transformCount);
0682: }
0683:
0684: c = findConfigObjects("ViewPlatformBehavior");
0685: if (c != null) {
0686: processViewPlatformBehaviors(c, vp, attachBehaviors);
0687: }
0688:
0689: c = findConfigObjects("Object");
0690: if (c != null) {
0691: processGenericObjects(c);
0692: }
0693: }
0694:
0695: // Process config physical environments into Java 3D physical
0696: // environments.
0697: private void processPhysicalEnvironments(Collection c) {
0698: Iterator i = c.iterator();
0699: while (i.hasNext()) {
0700: ConfigPhysicalEnvironment e = (ConfigPhysicalEnvironment) i
0701: .next();
0702: e.targetObject = e.createJ3dPhysicalEnvironment();
0703: }
0704: }
0705:
0706: // Process config physical bodys into Java 3D physical bodies.
0707: private void processPhysicalBodies(Collection c) {
0708: Iterator i = c.iterator();
0709: while (i.hasNext()) {
0710: ConfigPhysicalBody b = (ConfigPhysicalBody) i.next();
0711: b.targetObject = b.createJ3dPhysicalBody();
0712: }
0713: }
0714:
0715: // Process config views into Java 3D Views and then create Viewer objects
0716: // for them. This should only be called after all physical bodies and
0717: // physical environments have been processed.
0718: private void processViews(Collection c, boolean setVisible) {
0719: Iterator i = c.iterator();
0720: while (i.hasNext()) {
0721: ConfigView v = (ConfigView) i.next();
0722: v.targetObject = v.createViewer(setVisible);
0723: }
0724: }
0725:
0726: // Process config devices into Java 3D input devices. This should be done
0727: // only after all views have been processed, as some InputDevice
0728: // implementations require the AWT components associated with a view.
0729: private void processDevices(Collection c, Collection s, Collection p) {
0730: ConfigDevice cd = null;
0731: Iterator i = c.iterator();
0732: while (i.hasNext()) {
0733: cd = (ConfigDevice) i.next();
0734: cd.targetObject = cd.createInputDevice();
0735: }
0736:
0737: // Process device properties only after all InputDevices have been
0738: // instantiated. Some InputDevice properties require references
0739: // to other InputDevice implementations.
0740: i = c.iterator();
0741: while (i.hasNext())
0742: ((ConfigDevice) i.next()).processProperties();
0743:
0744: // Initialize the devices only after all have been instantiated, as
0745: // some InputDevices implementations are slaved to the first one
0746: // created and will not initialize otherwise (e.g. LogitechTracker).
0747: i = c.iterator();
0748: while (i.hasNext()) {
0749: cd = (ConfigDevice) i.next();
0750: if (!cd.j3dInputDevice.initialize())
0751: throw new RuntimeException(cd.errorMessage(
0752: cd.creatingCommand,
0753: "could not initialize device \""
0754: + cd.instanceName + "\""));
0755: }
0756:
0757: // An InputDevice implementation will have created all its Sensors by
0758: // the time initialize() returns. Retrieve and configure them here.
0759: if (s != null) {
0760: i = s.iterator();
0761: while (i.hasNext()) {
0762: ConfigSensor cs = (ConfigSensor) i.next();
0763: cs.configureSensor();
0764: cs.targetObject = cs.j3dSensor;
0765: }
0766: }
0767:
0768: // Iterate through the PhysicalEnvironments and process the devices.
0769: if (p != null) {
0770: i = p.iterator();
0771: while (i.hasNext())
0772: ((ConfigPhysicalEnvironment) i.next()).processDevices();
0773: }
0774: }
0775:
0776: // Process config view platforms into Java 3D viewing platforms.
0777: private void processViewPlatforms(Collection c, int numTransforms) {
0778: Iterator i = c.iterator();
0779: while (i.hasNext()) {
0780: ConfigViewPlatform cvp = (ConfigViewPlatform) i.next();
0781: cvp.targetObject = cvp.createViewingPlatform(numTransforms);
0782: }
0783: }
0784:
0785: // Process the configured view platform behaviors.
0786: private void processViewPlatformBehaviors(Collection behaviors,
0787: Collection viewPlatforms, boolean attach) {
0788: Iterator i = behaviors.iterator();
0789: while (i.hasNext()) {
0790: ConfigViewPlatformBehavior b = (ConfigViewPlatformBehavior) i
0791: .next();
0792: b.targetObject = b.createViewPlatformBehavior();
0793: }
0794:
0795: // Process properties only after all behaviors are instantiated.
0796: i = behaviors.iterator();
0797: while (i.hasNext())
0798: ((ConfigViewPlatformBehavior) i.next()).processProperties();
0799:
0800: // Attach behaviors to platforms after properties processed.
0801: if (attach && viewPlatforms != null) {
0802: i = viewPlatforms.iterator();
0803: while (i.hasNext())
0804: ((ConfigViewPlatform) i.next()).processBehavior();
0805: }
0806: }
0807:
0808: // Process generic objects.
0809: private void processGenericObjects(Collection objects) {
0810: Iterator i = objects.iterator();
0811: while (i.hasNext()) {
0812: ConfigObject o = (ConfigObject) i.next();
0813: o.targetObject = o.createTargetObject();
0814: }
0815:
0816: // Process properties only after all target objects are instantiated.
0817: i = objects.iterator();
0818: while (i.hasNext())
0819: ((ConfigObject) i.next()).processProperties();
0820: }
0821:
0822: // Returns a read-only Set containing all unique Java 3D objects of the
0823: // specified base class.
0824: private ReadOnlySet createSet(String baseName) {
0825: Collection c = findConfigObjects(baseName, true);
0826: if (c == null || c.size() == 0)
0827: return null;
0828:
0829: Iterator i = c.iterator();
0830: ArrayList l = new ArrayList();
0831: while (i.hasNext())
0832: l.add(((ConfigObject) i.next()).targetObject);
0833:
0834: return new ReadOnlySet(l);
0835: }
0836:
0837: // Returns a read-only Map that maps all names in the specified base
0838: // class, including aliases, to their corresponding Java 3D objects.
0839: private ReadOnlyMap createMap(String baseName) {
0840: Collection c = findConfigObjects(baseName, false);
0841: if (c == null || c.size() == 0)
0842: return null;
0843:
0844: Iterator i = c.iterator();
0845: HashMap m = new HashMap();
0846: while (i.hasNext()) {
0847: ConfigObject co = (ConfigObject) i.next();
0848: if (co.isAlias)
0849: m.put(co.instanceName, co.original.targetObject);
0850: else
0851: m.put(co.instanceName, co.targetObject);
0852: }
0853:
0854: return new ReadOnlyMap(m);
0855: }
0856:
0857: /**
0858: * Returns a read-only Set of all configured PhysicalBody instances in the
0859: * order they were defined in the configuration file.
0860: *
0861: * PhysicalBody instances are created with the following command:<p>
0862: * <blockquote>
0863: * (NewPhysicalBody <i><instance name></i>
0864: * [Alias <i><alias name></i>])
0865: * </blockquote>
0866: *
0867: * The PhysicalBody is configured through the following command:<p>
0868: * <blockquote>
0869: * (PhysicalBodyProperty <i><instance name>
0870: * <property name> <property value></i>)
0871: * </blockquote>
0872: *
0873: * @return read-only Set of all unique instances, or null
0874: */
0875: public Set getPhysicalBodies() {
0876: if (bodies != null)
0877: return bodies;
0878: bodies = createSet("PhysicalBody");
0879: return bodies;
0880: }
0881:
0882: /**
0883: * Returns a read-only Map that maps PhysicalBody names to instances.
0884: * Names may be aliases and if so will map to the original instances.
0885: *
0886: * @return read-only Map from names to PhysicalBody instances, or null if
0887: * no instances
0888: */
0889: public Map getNamedPhysicalBodies() {
0890: if (bodyMap != null)
0891: return bodyMap;
0892: bodyMap = createMap("PhysicalBody");
0893: return bodyMap;
0894: }
0895:
0896: /**
0897: * Returns a read-only Set of all configured PhysicalEnvironment instances
0898: * in the order they were defined in the configuration file.<p>
0899: *
0900: * PhysicalEnvironment instances are created with the following command:<p>
0901: * <blockquote>
0902: * (NewPhysicalEnvironment <i><instance name></i>
0903: * [Alias <i><alias name></i>])
0904: * </blockquote>
0905: *
0906: * The PhysicalEnvironment is configured through the following command:<p>
0907: * <blockquote>
0908: * (PhysicalEnvironmentProperty <i><instance name>
0909: * <property name> <property value></i>)
0910: * </blockquote>
0911: *
0912: * @return read-only Set of all unique instances, or null
0913: */
0914: public Set getPhysicalEnvironments() {
0915: if (environments != null)
0916: return environments;
0917: environments = createSet("PhysicalEnvironment");
0918: return environments;
0919: }
0920:
0921: /**
0922: * Returns a read-only Map that maps PhysicalEnvironment names to
0923: * instances. Names may be aliases and if so will map to the original
0924: * instances.
0925: *
0926: * @return read-only Map from names to PhysicalEnvironment instances, or
0927: * null if no instances
0928: */
0929: public Map getNamedPhysicalEnvironments() {
0930: if (environmentMap != null)
0931: return environmentMap;
0932: environmentMap = createMap("PhysicalEnvironment");
0933: return environmentMap;
0934: }
0935:
0936: /**
0937: * Returns a read-only Set of all configured Viewer instances in the order
0938: * they were defined in the configuration file. The Viewers will have
0939: * incorporated any PhysicalEnvironment and PhysicalBody objects specfied
0940: * for them in the configuration file, and will be attached to any
0941: * ViewingPlatforms specified for them.<p>
0942: *
0943: * Viewer instances are created with the following command:<p>
0944: * <blockquote>
0945: * (NewView <i><instance name></i> [Alias <i><alias name></i>])
0946: * </blockquote>
0947: *
0948: * The Viewer is configured through the following command:<p>
0949: * <blockquote>
0950: * (ViewProperty <i><instance name>
0951: * <property name> <property value></i>)
0952: * </blockquote>
0953: *
0954: * @return read-only Set of all unique instances, or null
0955: */
0956: public Set getViewers() {
0957: if (viewers != null)
0958: return viewers;
0959: viewers = createSet("View");
0960: return viewers;
0961: }
0962:
0963: /**
0964: * Returns a read-only Map that maps Viewer names to instances.
0965: * Names may be aliases and if so will map to the original instances.
0966: * The Viewers will have incorporated any PhysicalEnvironment and
0967: * PhysicalBody objects specfied for them in the configuration file, and
0968: * will be attached to any ViewingPlatforms specified for them.<p>
0969: *
0970: * @return read-only Map from names to Viewer instances, or
0971: * null if no instances
0972: */
0973: public Map getNamedViewers() {
0974: if (viewerMap != null)
0975: return viewerMap;
0976: viewerMap = createMap("View");
0977: return viewerMap;
0978: }
0979:
0980: /**
0981: * Returns a read-only Set of all configured InputDevice instances in the
0982: * order they were defined in the configuration file. All InputDevice
0983: * instances in the set are initialized and registered with any
0984: * PhysicalEnvironments that reference them.<p>
0985: *
0986: * InputDevice instances are created with the following command:<p>
0987: * <blockquote>
0988: * (NewDevice <i><instanceName> <className></i>
0989: * [Alias <i><alias name></i>])
0990: * </blockquote>
0991: *
0992: * <i>className</i> must be the fully-qualified name of a class that
0993: * implements the InputDevice interface. The implementation
0994: * must provide a parameterless constructor.<p>
0995: *
0996: * The InputDevice is configured through the DeviceProperty command:<p>
0997: * <blockquote>
0998: * (DeviceProperty <i><instanceName> <propertyName>
0999: * <arg0> ... <argn></i>)
1000: * </blockquote>
1001: *
1002: * <i>propertyName</i> must be the name of a input device method that
1003: * takes an array of Objects as its only parameter; the array is populated
1004: * with the values of <i>arg0</i> through <i>argn</i> when the method is
1005: * invoked to set the property. These additional requirements for
1006: * configurable input devices can usually be fulfilled by extending or
1007: * wrapping available InputDevice implementations.
1008: *
1009: * @return read-only Set of all unique instances, or null
1010: */
1011: public Set getInputDevices() {
1012: if (devices != null)
1013: return devices;
1014: devices = createSet("Device");
1015: return devices;
1016: }
1017:
1018: /**
1019: * Returns a read-only Map that maps InputDevice names to instances.
1020: * Names may be aliases and if so will map to the original instances. All
1021: * InputDevice instances in the map are initialized and registered with
1022: * any PhysicalEnvironments that reference them.
1023: *
1024: * @return read-only Map from names to InputDevice instances, or
1025: * null if no instances
1026: * @see #getInputDevices
1027: */
1028: public Map getNamedInputDevices() {
1029: if (deviceMap != null)
1030: return deviceMap;
1031: deviceMap = createMap("Device");
1032: return deviceMap;
1033: }
1034:
1035: /**
1036: * Returns a read-only Set of all configured Sensor instances in the order
1037: * they were defined in the configuration file. The associated
1038: * InputDevices are all initialized and registered with any
1039: * PhysicalEnvironments that reference them.<p>
1040: *
1041: * Sensor instances are named with the following command:<p>
1042: * <blockquote>
1043: * (NewSensor <i><instance name> <device name>
1044: * <sensor index></i> [Alias <i><alias name></i>])
1045: * </blockquote>
1046: *
1047: * <i>device name</i> is the instance name of a previously defined
1048: * InputDevice, and <i>sensor index</i> is the index of the Sensor to be
1049: * bound to <i>instance name</i>. The InputDevice implementation is
1050: * responsible for creating its own Sensor objects, so this command does
1051: * not create any new instances.<p>
1052: *
1053: * The Sensor is configured through the SensorProperty command:<p>
1054: * <blockquote>
1055: * (SensorProperty <i><instance name> <property name>
1056: * <property value></i>)
1057: * </blockquote>
1058: *
1059: * With the sole exception of the Sensor assigned to the head tracker,
1060: * none of the Sensors defined in the configuration file are placed into
1061: * the Sensor array maintained by a PhysicalEnvironment.
1062: *
1063: * @return read-only Set of all unique instances, or null
1064: */
1065: public Set getSensors() {
1066: if (sensors != null)
1067: return sensors;
1068: sensors = createSet("Sensor");
1069: return sensors;
1070: }
1071:
1072: /**
1073: * Returns a read-only Map that maps Sensor names to instances. Names may
1074: * be aliases and if so will map to the original instances. The
1075: * associated InputDevices are all initialized and registered with any
1076: * PhysicalEnvironments that reference them.<p>
1077: *
1078: * With the sole exception of the Sensor assigned to the head tracker,
1079: * none of the Sensors defined in the configuration file are placed into
1080: * the Sensor array maintained by a PhysicalEnvironment.
1081: *
1082: * @return read-only Map from names to Sensor instances, or
1083: * null if no instances
1084: */
1085: public Map getNamedSensors() {
1086: if (sensorMap != null)
1087: return sensorMap;
1088: sensorMap = createMap("Sensor");
1089: return sensorMap;
1090: }
1091:
1092: /**
1093: * Returns a read-only Set of all configured ViewingPlatform instances in
1094: * the order they were defined in the configuration file. The
1095: * ConfigContainer class itself does not attach the ViewingPlatform
1096: * instances to any scengraph components or universe Locales; they are not
1097: * "live" until made so by a separate client such as ConfiguredUniverse.
1098: *
1099: * ViewingPlatform instances are created with the following command:<p>
1100: * <blockquote>
1101: * (NewViewPlatform <i><instance name></i>
1102: * [Alias <i><alias name></i>])
1103: * </blockquote>
1104: *
1105: * The ViewingPlatform is configured through the following command:<p>
1106: * <blockquote>
1107: * (ViewPlatformProperty <i><instance name> <property name>
1108: * <property value></i>)
1109: * </blockquote>
1110: *
1111: * @return read-only Set of all unique instances, or null
1112: */
1113: public Set getViewingPlatforms() {
1114: if (platforms != null)
1115: return platforms;
1116: platforms = createSet("ViewPlatform");
1117: return platforms;
1118: }
1119:
1120: /**
1121: * Returns a read-only Map that maps ViewingPlatform names to instances.
1122: * Names may be aliases and if so will map to the original instances. The
1123: * ConfigContainer class itself does not attach the ViewingPlatform
1124: * instances to any scengraph components or universe Locales; they are not
1125: * "live" until made so by a separate client such as ConfiguredUniverse.
1126: *
1127: * @return read-only Map from names to ViewingPlatform instances, or
1128: * null if no instances
1129: */
1130: public Map getNamedViewingPlatforms() {
1131: if (platformMap != null)
1132: return platformMap;
1133: platformMap = createMap("ViewPlatform");
1134: return platformMap;
1135: }
1136:
1137: /**
1138: * Returns a read-only Set of all configured ViewPlatformBehavior
1139: * instances in the order they were defined in the configuration file.<p>
1140: *
1141: * The behaviors are attached to any ViewingPlatforms that specified them;
1142: * that is, the <code>setViewPlatformBehavior</code> and
1143: * <code>setViewingPlatform</code> methods of ViewingPlatform and
1144: * ViewPlatformBehavior have been called if appropriate. However, a
1145: * behavior's <code>initialize</code> method is not called until the
1146: * ViewingPlatform to which it is attached is made live.<p>
1147: *
1148: * ViewPlatformBehavior instances are created by the following command:<p>
1149: * <blockquote>
1150: * (NewViewPlatformBehavior <i><instanceName> <className></i>)
1151: * </blockquote>
1152: *
1153: * <i>className</i> must be the fully qualified name of a concrete class
1154: * that extends the abstract ViewPlatformBehavior class. The
1155: * implementation must provide a parameterless constructor.<p>
1156: *
1157: * The behavior is configured using ViewPlatformBehaviorProperty:<p>
1158: * <blockquote>
1159: * (ViewPlatformBehaviorProperty <i><instanceName>
1160: * <propertyName> <arg0> ... <argn></i>)
1161: * </blockquote>
1162: *
1163: * ViewPlatformBehavior subclasses inherit a number of pre-defined
1164: * properties that can be directly specified with the <i>propertyName</i>
1165: * string; see the configuration file documentation for details.<p>
1166: *
1167: * Concrete ViewPlatformBehavior instances can also define their own
1168: * unique properties. In those cases, <i>propertyName</i> must be the
1169: * name of a behavior method that takes an array of Objects as its only
1170: * parameter; the array is populated with the values of <i>arg0</i>
1171: * through <i>argn</i> when the method is invoked to set the property.
1172: * These additional requirements for configurable behaviors can usually be
1173: * fulfilled by extending or wrapping available ViewPlatformBehavior
1174: * subclasses.
1175: *
1176: * @return read-only Set of all unique instances, or null
1177: */
1178: public Set getViewPlatformBehaviors() {
1179: if (behaviors != null)
1180: return behaviors;
1181: behaviors = createSet("ViewPlatformBehavior");
1182: return behaviors;
1183: }
1184:
1185: /**
1186: * Returns a read-only Map that maps ViewPlatformBehavior names to
1187: * instances. Names may be aliases and if so will map to the original
1188: * instances.<p>
1189: *
1190: * The behaviors are attached to any ViewingPlatforms that specified them;
1191: * that is, the <code>setViewPlatformBehavior</code> and
1192: * <code>setViewingPlatform</code> methods of ViewingPlatform and
1193: * ViewPlatformBehavior have been called if appropriate. However, a
1194: * behavior's <code>initialize</code> method is not called until the
1195: * ViewingPlatform to which it is attached is made live.<p>
1196: *
1197: * @return read-only Map from names to ViewPlatformBehavior instances, or
1198: * null if no instances
1199: * @see #getViewPlatformBehaviors
1200: */
1201: public Map getNamedViewPlatformBehaviors() {
1202: if (behaviorMap != null)
1203: return behaviorMap;
1204: behaviorMap = createMap("ViewPlatformBehavior");
1205: return behaviorMap;
1206: }
1207:
1208: /**
1209: * Returns a read-only Map containing the named Canvas3D instances used by
1210: * the specified Viewer. Names may be aliases and if so will map to the
1211: * original instances. The set of unique Canvas3D instances used by a
1212: * Viewer may be obtained by calling the Viewer's accessor methods
1213: * directly.<p>
1214: *
1215: * A named Canvas3D is created and added to a Viewer whenever any of the
1216: * following configuration commands are used:<p>
1217: * <blockquote>
1218: * (ViewProperty <i><view></i> Screen <i><screenName></i>)<br>
1219: * (ViewProperty <i><view></i> Window <i><windowName></i>)
1220: * </blockquote>
1221: *
1222: * <i>view</i> is the name of a Viewer created with the NewView command.
1223: * The <i>screenName</i> and <i>windowName</i> parameters of the above
1224: * commands are the keys to use when looking up the associated Canvas3D
1225: * instances in the Map returned by this method. <b>Note:</b> the
1226: * NewScreen and NewWindow commands do <i>not</i> create Canvas3D
1227: * instances themselves; they are created only by the above configuration
1228: * commands.
1229: *
1230: * @param viewName the name of the Viewer
1231: * @return read-only Map containing the Viewer's named Canvas3D instances
1232: */
1233: public Map getNamedCanvases(String viewName) {
1234: Map m = (Map) viewCanvasMap.get(viewName);
1235: if (m != null)
1236: return m;
1237:
1238: m = new HashMap();
1239: ConfigView cv = (ConfigView) findConfigObject("View", viewName);
1240: Iterator i = cv.screens.iterator();
1241: while (i.hasNext()) {
1242: ConfigScreen cs = (ConfigScreen) i.next();
1243: m.put(cs.instanceName, cs.j3dCanvas);
1244:
1245: // The aliases list contains all alias strings for the canvas.
1246: Iterator j = cs.aliases.iterator();
1247: while (j.hasNext())
1248: m.put(j.next(), cs.j3dCanvas);
1249: }
1250: m = new ReadOnlyMap(m);
1251: viewCanvasMap.put(viewName, m);
1252: return m;
1253: }
1254:
1255: /**
1256: * Returns a read-only Set of all generic configuration object
1257: * instances in the order they were defined in the configuration file.<p>
1258: *
1259: * Generic object instances are created with the following command:<p>
1260: * <blockquote>
1261: * (NewObject <i><instanceName> <className></i>)
1262: * </blockquote>
1263: *
1264: * <i>className</i> must be the fully-qualified name of a class that
1265: * provides a parameterless constructor.<p>
1266: *
1267: * The object is configured through the ObjectProperty command:<p>
1268: * <blockquote>
1269: * (ObjectProperty <i><instanceName> <propertyName>
1270: * <arg0> ... <argn></i>)
1271: * </blockquote>
1272: *
1273: * <i>propertyName</i> must be the name of a method provided by object
1274: * <i>instanceName</i>. It must take an array of Objects as its only
1275: * parameter; the array is populated with the values of <i>arg0</i>
1276: * through <i>argn</i> when the method is invoked to set the property.
1277: * These additional requirements for configurable objects can usually be
1278: * fulfilled by extending or wrapping available object classes.
1279: *
1280: * @return read-only Set of all unique instances, or null
1281: */
1282: public Set getGenericObjects() {
1283: if (genericObjects != null)
1284: return genericObjects;
1285: genericObjects = createSet("Object");
1286: return genericObjects;
1287: }
1288:
1289: /**
1290: * Returns a read-only Map that maps generic object names to
1291: * instances. Names may be aliases and if so will map to the original
1292: * instances.
1293: *
1294: * @return read-only Map from names to generic object instances, or
1295: * null if no instances
1296: * @see #getGenericObjects
1297: */
1298: public Map getNamedGenericObjects() {
1299: if (genericObjectMap != null)
1300: return genericObjectMap;
1301: genericObjectMap = createMap("Object");
1302: return genericObjectMap;
1303: }
1304:
1305: /**
1306: * Returns the number of TransformGroups with which ViewingPlatforms
1307: * should be created. This is useful for clients that wish to provide a
1308: * default ViewingPlatform if the configuration file doesn't specify one.
1309: *
1310: * @return the number of TransformGroups
1311: */
1312: public int getViewPlatformTransformCount() {
1313: return transformCount;
1314: }
1315:
1316: /**
1317: * Returns whether Viewers should be created with their AWT components
1318: * initially visible or invisible. This is useful for clients that wish
1319: * to provide a default Viewer if the configuration file doesn't specify
1320: * one.
1321: *
1322: * @return true if Viewer components should be initially visible; false
1323: * otherwise
1324: */
1325: public boolean getViewerVisibility() {
1326: return setVisible;
1327: }
1328:
1329: /**
1330: * Release memory references used by this ConfigContainer. All Sets and
1331: * Maps obtained from this ConfigContainer are cleared.
1332: */
1333: public void clear() {
1334: // Clear baseNameList.
1335: Iterator i = baseNameMap.values().iterator();
1336: while (i.hasNext())
1337: ((Collection) i.next()).clear();
1338: baseNameMap.clear();
1339:
1340: // Clear viewCanvasMap.
1341: i = viewCanvasMap.values().iterator();
1342: while (i.hasNext())
1343: ((ReadOnlyMap) i.next()).map.clear();
1344: viewCanvasMap.clear();
1345:
1346: // Release reference to file name.
1347: currentFileName = null;
1348:
1349: // Clear and release sets.
1350: if (bodies != null) {
1351: bodies.collection.clear();
1352: bodies = null;
1353: }
1354: if (environments != null) {
1355: environments.collection.clear();
1356: environments = null;
1357: }
1358: if (devices != null) {
1359: devices.collection.clear();
1360: devices = null;
1361: }
1362: if (sensors != null) {
1363: sensors.collection.clear();
1364: sensors = null;
1365: }
1366: if (behaviors != null) {
1367: behaviors.collection.clear();
1368: behaviors = null;
1369: }
1370: if (platforms != null) {
1371: platforms.collection.clear();
1372: platforms = null;
1373: }
1374: if (viewers != null) {
1375: viewers.collection.clear();
1376: viewers = null;
1377: }
1378: if (genericObjects != null) {
1379: genericObjects.collection.clear();
1380: genericObjects = null;
1381: }
1382:
1383: // Clear and release maps.
1384: if (bodyMap != null) {
1385: bodyMap.map.clear();
1386: bodyMap = null;
1387: }
1388: if (environmentMap != null) {
1389: environmentMap.map.clear();
1390: environmentMap = null;
1391: }
1392: if (deviceMap != null) {
1393: deviceMap.map.clear();
1394: deviceMap = null;
1395: }
1396: if (sensorMap != null) {
1397: sensorMap.map.clear();
1398: sensorMap = null;
1399: }
1400: if (behaviorMap != null) {
1401: behaviorMap.map.clear();
1402: behaviorMap = null;
1403: }
1404: if (platformMap != null) {
1405: platformMap.map.clear();
1406: platformMap = null;
1407: }
1408: if (viewerMap != null) {
1409: viewerMap.map.clear();
1410: viewerMap = null;
1411: }
1412: if (genericObjectMap != null) {
1413: genericObjectMap.map.clear();
1414: genericObjectMap = null;
1415: }
1416:
1417: }
1418:
1419: /**
1420: * Returns the config file URL based on system properties. The current
1421: * implementation of this method parses the j3d.configURL property as a
1422: * URL string. For example, the following command line would specify that
1423: * the config file is taken from the file "j3dconfig" in the current
1424: * directory:
1425: * <ul>
1426: * <code>java -Dj3d.configURL=file:j3dconfig ...</code>
1427: * </ul>
1428: *
1429: * @return the URL of the config file; null is returned if no valid
1430: * URL is defined by the system properties
1431: */
1432: public static URL getConfigURL() {
1433: return getConfigURL(null);
1434: }
1435:
1436: /**
1437: * Returns the config file URL based on system properties. The current
1438: * implementation of this method parses the j3d.configURL property as a
1439: * URL string. For example, the following command line would specify that
1440: * the config file is taken from the file "j3dconfig" in the current
1441: * directory:
1442: * <ul>
1443: * <code>java -Dj3d.configURL=file:j3dconfig ...</code>
1444: * </ul>
1445: *
1446: * @param defaultURLString the default string used to construct
1447: * the URL if the appropriate system properties are not defined
1448: * @return the URL of the config file; null is returned if no
1449: * valid URL is defined either by the system properties or the
1450: * default URL string
1451: */
1452: public static URL getConfigURL(String defaultURLString) {
1453: URL url = null;
1454: String urlString = null;
1455: final String defProp = defaultURLString;
1456:
1457: urlString = (String) java.security.AccessController
1458: .doPrivileged(new java.security.PrivilegedAction() {
1459: public Object run() {
1460: return System.getProperty("j3d.configURL",
1461: defProp);
1462: }
1463: });
1464:
1465: if (urlString == null) {
1466: return null;
1467: }
1468: try {
1469: url = new URL(urlString);
1470: } catch (MalformedURLException e) {
1471: System.out.println(e);
1472: return null;
1473: }
1474: return url;
1475: }
1476:
1477: // A general purpose read-only Map backed by a HashMap.
1478: private static class ReadOnlyMap extends AbstractMap {
1479: HashMap map;
1480: private Set entrySet = null;
1481:
1482: ReadOnlyMap(Map map) {
1483: this .map = new HashMap(map);
1484: }
1485:
1486: // overridden for efficiency
1487: public Object get(Object key) {
1488: return map.get(key);
1489: }
1490:
1491: // overridden for efficiency
1492: public boolean containsKey(Object key) {
1493: return map.containsKey(key);
1494: }
1495:
1496: // overridden for efficiency
1497: public boolean containsValue(Object value) {
1498: return map.containsValue(value);
1499: }
1500:
1501: public Set entrySet() {
1502: if (entrySet == null)
1503: entrySet = new ReadOnlySet(map.entrySet());
1504:
1505: return entrySet;
1506: }
1507: }
1508:
1509: // A general purpose read-only Set backed by a Collection containing
1510: // unique objects.
1511: private static class ReadOnlySet extends AbstractSet {
1512: Collection collection = null;
1513:
1514: ReadOnlySet(Collection c) {
1515: this .collection = c;
1516: }
1517:
1518: public int size() {
1519: return collection.size();
1520: }
1521:
1522: public Iterator iterator() {
1523: return new ReadOnlyIterator(collection.iterator());
1524: }
1525: }
1526:
1527: // A general purpose read-only Iterator backed by another Iterator.
1528: private static class ReadOnlyIterator implements Iterator {
1529: private Iterator i;
1530:
1531: ReadOnlyIterator(Iterator i) {
1532: this .i = i;
1533: }
1534:
1535: public boolean hasNext() {
1536: return i.hasNext();
1537: }
1538:
1539: public Object next() {
1540: return i.next();
1541: }
1542:
1543: public void remove() {
1544: throw new UnsupportedOperationException();
1545: }
1546: }
1547: }
|