001: /*
002: * $RCSfile: ConfigCommand.java,v $
003: *
004: * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * - Redistribution of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * - Redistribution in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * Neither the name of Sun Microsystems, Inc. or the names of
019: * contributors may be used to endorse or promote products derived
020: * from this software without specific prior written permission.
021: *
022: * This software is provided "AS IS," without a warranty of any
023: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
024: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
025: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
026: * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
027: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
028: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
029: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
030: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
031: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
032: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
033: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
034: * POSSIBILITY OF SUCH DAMAGES.
035: *
036: * You acknowledge that this software is not designed, licensed or
037: * intended for use in the design, construction, operation or
038: * maintenance of any nuclear facility.
039: *
040: * $Revision: 1.4 $
041: * $Date: 2007/02/09 17:20:43 $
042: * $State: Exp $
043: */
044:
045: package com.sun.j3d.utils.universe;
046:
047: import java.text.DecimalFormat;
048: import java.text.FieldPosition;
049: import java.util.Collection;
050: import javax.vecmath.Matrix3d;
051: import javax.vecmath.Matrix4d;
052:
053: /**
054: * Contains the elements which compose a configuration file command,
055: * including the command name, type, and arguments.
056: */
057: class ConfigCommand {
058: /**
059: * Specifies that this command creates a new ConfigObject.
060: */
061: static final int CREATE = 0;
062:
063: /**
064: * Specifies that this command sets an attribute for a class known
065: * to ConfiguredUniverse. As of Java 3D 1.3.1, these commands are
066: * handled the same as property commands (see PROPERTY below) and
067: * this constant is no longer used.
068: */
069: static final int ATTRIBUTE = 1;
070:
071: /**
072: * Specifies that this command sets a Java system property or a
073: * property for a class unknown to ConfiguredUniverse. Properties
074: * for such a class are set by using the reflection API to invoke a
075: * method whose name is specified in the command's argument list.
076: * Such a method must accept an array of Object as its sole
077: * parameter, where that array contains all the command elements
078: * which appear after the method name.<p>
079: *
080: * As of Java 3D 1.3.1, this is handled the same as an attribute.
081: * The individual setProperty() method implementations of
082: * ConfigObject determine whether the method to set the property can
083: * be invoked directly or through introspection. If through
084: * introspection, then the evaluation of the property must be
085: * delayed until the target object is instantiated.
086: */
087: static final int PROPERTY = 2;
088:
089: /**
090: * Specifies that this command creates an alias for a ConfigObject of the
091: * same base name.
092: */
093: static final int ALIAS = 3;
094:
095: /**
096: * Specifies that this command is a deferred built-in command that can't
097: * be immediately evaluated by the parser. Its evaluation is delayed
098: * until all config objects are instantiated and their properties can be
099: * evaluated.
100: */
101: static final int BUILTIN = 4;
102:
103: /**
104: * Specifies that this command is an include file directive.
105: */
106: static final int INCLUDE = 5;
107:
108: /**
109: * Specifes that this command is entirely processed by the
110: * constructor and should be ignored by subsequent recipients.
111: */
112: static final int IGNORE = 6;
113:
114: /**
115: * The type of this command, either CREATE, PROPERTY, ALIAS,
116: * BUILTIN, INCLUDE, or IGNORE.
117: */
118: int type = -1;
119:
120: /**
121: * The number of arguments in this command, including the command
122: * name.
123: */
124: int argc = 0;
125:
126: /**
127: * An array containing all of this command's arguments, including
128: * the command name.
129: */
130: Object[] argv = null;
131:
132: /**
133: * The name of the command being invoked, which is always the first
134: * argument of the command.
135: */
136: String commandName = null;
137:
138: /**
139: * The base name of this command, from which the name of the ConfigObject
140: * subclass that processes it is derived. This is constructed by
141: * stripping off the leading "New" prefix or the trailing "Attribute",
142: * "Property", or "Alias" suffix of the command name. The name of the
143: * ConfigObject subclass which handles the command is derived by adding
144: * "Config" as a prefix to the base name.
145: */
146: String baseName = null;
147:
148: /**
149: * The instance name of the ConfigObject subclass which processes this
150: * command. Together with the base name this provides the handle by which
151: * a ConfigObject can be referenced by other commands in the configuration
152: * file.
153: */
154: String instanceName = null;
155:
156: /**
157: * The file from which this command was read.
158: */
159: String fileName = null;
160:
161: /**
162: * The line number from which this command was read.
163: */
164: int lineNumber = 0;
165:
166: /**
167: * Constructs a ConfigCommand from configuration file command arguments.
168: *
169: * @param elements arguments to this command, including the command name
170: * @param fileName name of the file from where the command was read
171: * @param lineNumber line number where the command is found in the file
172: */
173: ConfigCommand(Collection elements, String fileName, int lineNumber) {
174: this .fileName = fileName;
175: this .lineNumber = lineNumber;
176:
177: argc = elements.size();
178: argv = elements.toArray(new Object[0]);
179:
180: if (!(argc > 0 && (argv[0] instanceof String)))
181: throw new IllegalArgumentException("malformed command");
182:
183: commandName = (String) argv[0];
184:
185: if (commandName.startsWith("New")) {
186: type = CREATE;
187: baseName = commandName.substring(3);
188: instanceName = checkName(argv[1]);
189: } else if (commandName.endsWith("Property")) {
190: baseName = commandName.substring(0,
191: commandName.length() - 8);
192: if (baseName.equals("Java")) {
193: type = IGNORE;
194: processJavaProperty(argc, argv);
195: } else {
196: type = PROPERTY;
197: instanceName = checkName(argv[1]);
198: }
199: } else if (commandName.endsWith("Attribute")) {
200: // Backward compatibility.
201: type = PROPERTY;
202: baseName = commandName.substring(0,
203: commandName.length() - 9);
204: instanceName = checkName(argv[1]);
205: } else if (commandName.endsWith("Alias")) {
206: type = ALIAS;
207: baseName = commandName.substring(0,
208: commandName.length() - 5);
209: instanceName = checkName(argv[1]);
210: } else if (commandName.equals("Include")) {
211: type = INCLUDE;
212: } else {
213: type = BUILTIN;
214: }
215:
216: // We allow "Window" as an equivalent to "Screen".
217: if (baseName != null && baseName.equals("Window"))
218: baseName = "Screen";
219: }
220:
221: /**
222: * Sets the Java property specified in the command. If the command
223: * has 3 arguments then it's an unconditional assignment. If the
224: * 3rd argument is "Default", then the property is set to the value
225: * of the 4th argument only if the specified property has no
226: * existing value.
227: *
228: * @param argc the number of arguments in the command
229: * @param argv command arguments as an array of Objects; the 1st is
230: * the command name (ignored), the 2nd is the name of the Java
231: * property, the 3rd is the value to be set or the keyword
232: * "Default", and the 4th is thevalue to be set if the Java
233: * property doesn't already exist
234: */
235: private static void processJavaProperty(int argc, Object[] argv) {
236: for (int i = 1; i < argc; i++) {
237: // Check args.
238: if (argv[i] instanceof Boolean) {
239: argv[i] = ((Boolean) argv[i]).toString();
240: } else if (!(argv[i] instanceof String)) {
241: throw new IllegalArgumentException(
242: "JavaProperty arguments must be Strings or Booleans");
243: }
244: }
245: if (argc == 3) {
246: // Unconditional assignment.
247: setJavaProperty((String) argv[1], (String) argv[2]);
248: } else if (argc != 4) {
249: // Conditional assignment must have 4 args.
250: throw new IllegalArgumentException(
251: "JavaProperty must have either 2 or 3 arguments");
252: } else if (!((String) argv[2]).equals("Default")) {
253: // Penultimate arg must be "Default" keyword.
254: throw new IllegalArgumentException(
255: "JavaProperty 2nd argument must be \"Default\"");
256: } else if (evaluateJavaProperty((String) argv[1]) == null) {
257: // Assignment only if no existing value.
258: setJavaProperty((String) argv[1], (String) argv[3]);
259: }
260: }
261:
262: /**
263: * Sets the given Java system property if allowed by the security manager.
264: *
265: * @param key property name
266: * @param value property value
267: * @return previous property value if any
268: */
269: static String setJavaProperty(final String key, final String value) {
270: return (String) java.security.AccessController
271: .doPrivileged(new java.security.PrivilegedAction() {
272: public Object run() {
273: return System.setProperty(key, value);
274: }
275: });
276: }
277:
278: /**
279: * Evaluates the specified Java property string if allowed by the security
280: * manager.
281: *
282: * @param key string containing a Java property name
283: * @return string containing the Java property valaue
284: */
285: static String evaluateJavaProperty(final String key) {
286: return (String) java.security.AccessController
287: .doPrivileged(new java.security.PrivilegedAction() {
288: public Object run() {
289: return System.getProperty(key);
290: }
291: });
292: }
293:
294: /**
295: * Checks if the given object is an instance of String.
296: *
297: * @param o the object to be checked
298: * @return the object cast to a String
299: * @exception IllegalArgumentException if the object is not a String
300: */
301: private final String checkName(Object o) {
302: if (!(o instanceof String))
303: throw new IllegalArgumentException("second argument to \""
304: + commandName + "\" must be a name");
305:
306: return (String) o;
307: }
308:
309: /**
310: * Calls <code>formatMatrixRows(3, 3, m)</code>, where <code>m</code> is a
311: * an array of doubles retrieved from the given Matrix3d.
312: *
313: * @param m3 matrix to be formatted
314: * @return matrix rows formatted into strings
315: */
316: static String[] formatMatrixRows(Matrix3d m3) {
317: double[] m = new double[9];
318: m[0] = m3.m00;
319: m[1] = m3.m01;
320: m[2] = m3.m02;
321: m[3] = m3.m10;
322: m[4] = m3.m11;
323: m[5] = m3.m12;
324: m[6] = m3.m20;
325: m[7] = m3.m21;
326: m[8] = m3.m22;
327:
328: return formatMatrixRows(3, 3, m);
329: }
330:
331: /**
332: * Calls <code>formatMatrixRows(4, 4, m)</code>, where <code>m</code> is a
333: * an array of doubles retrieved from the given Matrix4d.
334: *
335: * @param m4 matrix to be formatted
336: * @return matrix rows formatted into strings
337: */
338: static String[] formatMatrixRows(Matrix4d m4) {
339: double[] m = new double[16];
340: m[0] = m4.m00;
341: m[1] = m4.m01;
342: m[2] = m4.m02;
343: m[3] = m4.m03;
344: m[4] = m4.m10;
345: m[5] = m4.m11;
346: m[6] = m4.m12;
347: m[7] = m4.m13;
348: m[8] = m4.m20;
349: m[9] = m4.m21;
350: m[10] = m4.m22;
351: m[11] = m4.m23;
352: m[12] = m4.m30;
353: m[13] = m4.m31;
354: m[14] = m4.m32;
355: m[15] = m4.m33;
356:
357: return formatMatrixRows(4, 4, m);
358: }
359:
360: /**
361: * Formats a matrix with fixed fractional digits and integer padding to
362: * align the decimal points in columns. Non-negative numbers print up to
363: * 7 integer digits, while negative numbers print up to 6 integer digits
364: * to account for the negative sign. 6 fractional digits are printed.
365: *
366: * @param rowCount number of rows in the matrix
367: * @param colCount number of columns in the matrix
368: * @param m matrix to be formatted
369: * @return matrix rows formatted into strings
370: */
371: static String[] formatMatrixRows(int rowCount, int colCount,
372: double[] m) {
373: DecimalFormat df = new DecimalFormat("0.000000");
374: FieldPosition fp = new FieldPosition(
375: DecimalFormat.INTEGER_FIELD);
376: StringBuffer sb0 = new StringBuffer();
377: StringBuffer sb1 = new StringBuffer();
378: String[] rows = new String[rowCount];
379:
380: for (int i = 0; i < rowCount; i++) {
381: sb0.setLength(0);
382: for (int j = 0; j < colCount; j++) {
383: sb1.setLength(0);
384: df.format(m[i * colCount + j], sb1, fp);
385: int pad = 8 - fp.getEndIndex();
386: for (int k = 0; k < pad; k++) {
387: sb1.insert(0, " ");
388: }
389: sb0.append(sb1);
390: }
391: rows[i] = sb0.toString();
392: }
393: return rows;
394: }
395:
396: /**
397: * Returns the String representation of this command.
398: *
399: * @return string representing this command
400: */
401: public String toString() {
402: String[] lines = null;
403: StringBuffer sb = new StringBuffer("(");
404:
405: for (int i = 0; i < argc; i++) {
406: if (argv[i] instanceof Matrix3d) {
407: lines = formatMatrixRows((Matrix3d) argv[i]);
408: sb.append("\n ((" + lines[0] + ")\n");
409: sb.append(" (" + lines[1] + ")\n");
410: sb.append(" (" + lines[2] + "))");
411: if (i != (argc - 1))
412: sb.append("\n");
413: } else if (argv[i] instanceof Matrix4d) {
414: lines = formatMatrixRows((Matrix4d) argv[i]);
415: sb.append("\n ((" + lines[0] + ")\n");
416: sb.append(" (" + lines[1] + ")\n");
417: sb.append(" (" + lines[2] + ")\n");
418: sb.append(" (" + lines[3] + "))");
419: if (i != (argc - 1))
420: sb.append("\n");
421: } else {
422: if (i > 0)
423: sb.append(" ");
424: sb.append(argv[i].toString());
425: }
426: }
427:
428: sb.append(")");
429: return sb.toString();
430: }
431: }
|