0001: /*
0002: * $RCSfile: Loader.java,v $
0003: *
0004: * @(#)Loader.java 1.46 99/03/24 15:35:16
0005: *
0006: * Copyright (c) 1996-1998 Sun Microsystems, Inc. All Rights Reserved.
0007: *
0008: * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
0009: * modify and redistribute this software in source and binary code form,
0010: * provided that i) this copyright notice and license appear on all copies of
0011: * the software; and ii) Licensee does not utilize the software in a manner
0012: * which is disparaging to Sun.
0013: *
0014: * This software is provided "AS IS," without a warranty of any kind. ALL
0015: * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
0016: * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
0017: * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
0018: * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
0019: * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
0020: * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
0021: * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
0022: * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
0023: * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
0024: * POSSIBILITY OF SUCH DAMAGES.
0025: *
0026: * This software is not designed or intended for use in on-line control of
0027: * aircraft, air traffic, aircraft navigation or aircraft communications; or in
0028: * the design, construction, operation or maintenance of any nuclear
0029: * facility. Licensee represents and warrants that it will not use or
0030: * redistribute the Software for such purposes.
0031: * $Revision: 1.4 $
0032: * $Date: 2006/04/08 14:45:24 $
0033: * $State: Exp $
0034: */
0035: /*
0036: * @Author: Rick Goldberg
0037: * @Author: Doug Gehringer
0038: * @author Scott Hong
0039: * @author Nikolai V. Chr.
0040: *
0041: */
0042: package org.jdesktop.j3d.loaders.vrml97.impl;
0043:
0044: import java.awt.AWTEvent;
0045: import java.io.BufferedInputStream;
0046: import java.io.ByteArrayInputStream;
0047: import java.io.DataInputStream;
0048: import java.io.IOException;
0049: import java.io.InputStream;
0050: import java.io.InputStreamReader;
0051: import java.io.Reader;
0052: import java.lang.System;
0053: import java.net.MalformedURLException;
0054: import java.net.URL;
0055: import java.net.URLClassLoader;
0056: import java.net.URLConnection;
0057: import java.util.Enumeration;
0058: import java.util.HashSet;
0059: import java.util.Hashtable;
0060: import java.util.Stack;
0061: import java.util.Vector;
0062: import java.util.zip.GZIPInputStream;
0063:
0064: import javax.media.j3d.*;
0065: import javax.vecmath.*;
0066:
0067: /** Description of the Class */
0068: public class Loader {
0069:
0070: GZIPInputStream gis;
0071:
0072: // the browser which owns the loader (null if using J3D Loader interface)
0073: Browser browser = null;
0074:
0075: // The top level URL and it's base, which is used for relative URLs
0076: URL worldURL;
0077: URL worldURLBase;
0078: String worldURLBaseName;
0079:
0080: int loaderMode;
0081:
0082: // State which depends on the current file (pushed/popped in load() for
0083: // inlines, extern protos, etc)
0084: Parser parser = null;
0085: Scene scene = null;
0086: // DEF/USE table stack, current namespace is on top, loader is always there
0087: Stack defTableStack = new Stack();
0088:
0089: // temp state for defining or instancing a proto
0090: Proto curProto = null;
0091: ProtoInstance protoInstance = null;
0092:
0093: // temp areas for parser
0094: IntBuf intBuf = new IntBuf();
0095: FloatBuf floatBuf = new FloatBuf();
0096: DoubleBuf doubleBuf = new DoubleBuf();
0097:
0098: // utility constants
0099: Bounds infiniteBounds;
0100: BoundingLeaf infiniteBoundingLeaf;
0101: Bounds zeroBounds;
0102: boolean autoSmooth = false;
0103:
0104: // Java bytecode loader
0105: URLClassLoader scl;
0106:
0107: // debug stuff
0108: boolean debug = false;
0109: boolean traceLex = false;
0110: boolean nowarn = false;
0111: boolean warnall = false;
0112: boolean parserMarks = false;
0113: boolean printRoutes = false;
0114: boolean timing = false;
0115:
0116: TreePrinter treePrinter = new TreePrinter();
0117:
0118: // timing stuff
0119: double nodeInitImpl;
0120: double ifsParse;
0121: double ifsInit;
0122: double MFInt32Parse, MFInt32Copy;
0123: double MFVec3fParse, MFVec3fCopy;
0124: int numGroups;
0125: int numIfs;
0126: NumFormat numFormat = new NumFormat();
0127:
0128: int warnLevel = WARN_ONCE;
0129: HashSet warnSet = new HashSet();
0130:
0131: /** Description of the Field */
0132: public final static int LOAD_STATIC = 0;
0133: /** Description of the Field */
0134: public final static int LOAD_OPTIMIZED = 1;
0135: /** Description of the Field */
0136: public final static int LOAD_CONFORMANT = 2;
0137:
0138: final static int WARN_NEVER = 0;
0139: final static int WARN_ONCE = 1;
0140: final static int WARN_ALL = 2;
0141:
0142: /**
0143: *Constructor for the Loader object
0144: *
0145: * @param initBrowser Description of the Parameter
0146: * @param loadMode Description of the Parameter
0147: */
0148: public Loader(Browser initBrowser, int loadMode) {
0149: browser = initBrowser;
0150: loaderMode = loadMode;
0151: try {
0152: debug = ((Boolean) java.security.AccessController
0153: .doPrivileged(new java.security.PrivilegedAction() {
0154: public Object run() {
0155: Boolean b = new Boolean(
0156: Boolean
0157: .getBoolean("org.jdesktop.j3d.loaders.vrml97.debug"));
0158: return b;
0159: }
0160: })).booleanValue();
0161: timing = ((Boolean) java.security.AccessController
0162: .doPrivileged(new java.security.PrivilegedAction() {
0163: public Object run() {
0164: Boolean b = new Boolean(
0165: Boolean
0166: .getBoolean("org.jdesktop.j3d.loaders.vrml97.timing"));
0167: return b;
0168: }
0169: })).booleanValue();
0170: printRoutes = ((Boolean) java.security.AccessController
0171: .doPrivileged(new java.security.PrivilegedAction() {
0172: public Object run() {
0173: Boolean b = new Boolean(
0174: Boolean
0175: .getBoolean("org.jdesktop.j3d.loaders.vrml97.printRoutes"));
0176: return b;
0177: }
0178: })).booleanValue();
0179: parserMarks = ((Boolean) java.security.AccessController
0180: .doPrivileged(new java.security.PrivilegedAction() {
0181: public Object run() {
0182: Boolean b = new Boolean(
0183: Boolean
0184: .getBoolean("org.jdesktop.j3d.loaders.vrml97.parserMarks"));
0185: return b;
0186: }
0187: })).booleanValue();
0188: warnall = ((Boolean) java.security.AccessController
0189: .doPrivileged(new java.security.PrivilegedAction() {
0190: public Object run() {
0191: Boolean b = new Boolean(
0192: Boolean
0193: .getBoolean("org.jdesktop.j3d.loaders.vrml97.warnall"));
0194: return b;
0195: }
0196: })).booleanValue();
0197: nowarn = ((Boolean) java.security.AccessController
0198: .doPrivileged(new java.security.PrivilegedAction() {
0199: public Object run() {
0200: Boolean b = new Boolean(
0201: Boolean
0202: .getBoolean("org.jdesktop.j3d.loaders.vrml97.nowarn"));
0203: return b;
0204: }
0205: })).booleanValue();
0206:
0207: } catch (java.security.AccessControlException ace) {
0208: ;
0209: }// silently, Applet
0210:
0211: if (nowarn) {
0212: warnLevel = WARN_NEVER;
0213: }
0214: if (warnall) {
0215: warnLevel = WARN_ALL;
0216: }
0217: defTableStack = new Stack();
0218: infiniteBounds = new BoundingSphere(new Point3d(),
0219: Double.MAX_VALUE);
0220: infiniteBoundingLeaf = new BoundingLeaf(infiniteBounds);
0221: infiniteBoundingLeaf
0222: .setCapability(BoundingLeaf.ALLOW_REGION_READ);
0223: zeroBounds = new BoundingSphere(new Point3d(), -1.0);
0224: }
0225:
0226: /**
0227: *Constructor for the Loader object
0228: *
0229: * @param browser Description of the Parameter
0230: */
0231: public Loader(Browser browser) {
0232: this (browser, LOAD_OPTIMIZED);
0233: }
0234:
0235: /**
0236: * Sets the worldURL attribute of the Loader object
0237: *
0238: * @param baseURL The new worldURL value
0239: * @param worldURL The new worldURL value
0240: * @exception MalformedURLException Either no legal protocol could be found in one of the urls or one could not be parsed.
0241: */
0242: public void setWorldURL(URL baseURL, URL worldURL)
0243: throws MalformedURLException {
0244: this .worldURL = worldURL;
0245: if (baseURL == null) {
0246: if (worldURL == null) {// input is from an input stream
0247: worldURLBaseName = new String("." + "/");
0248: } else {
0249: // figure out baseURL from the worldURL
0250: java.util.StringTokenizer stok = new java.util.StringTokenizer(
0251: worldURL.toString(), "/");
0252: int tocount = stok.countTokens() - 1;
0253: StringBuffer sb = new StringBuffer(80);
0254: for (int ji = 0; ji < tocount; ji++) {
0255: String a = stok.nextToken();
0256: //Issue 4 : https://j3d-vrml97.dev.java.net/issues/show_bug.cgi?id=4
0257: if ((ji == 0) && (!a.equals("file:"))
0258: && (!a.equals("jar:file:"))) {
0259: sb.append(a);
0260: sb.append("/");
0261: sb.append("/");
0262: } else {
0263: sb.append(a);
0264: sb.append("/");
0265: }
0266: }
0267: worldURLBaseName = sb.toString();
0268: }
0269: } else {
0270: worldURLBaseName = baseURL.toString();
0271: }
0272: if (debug) {
0273: System.out.println("World URL base is \""
0274: + worldURLBaseName + "\"");
0275: }
0276: try {
0277: worldURLBase = new URL(worldURLBaseName);
0278: // Give Applet style security for where classes
0279: // can come from. A Script must originate relative
0280: // to the parent URL.
0281: scl = (java.net.URLClassLoader) java.security.AccessController
0282: .doPrivileged(new java.security.PrivilegedAction() {
0283: public Object run() {
0284: return new URLClassLoader(
0285: new URL[] { worldURLBase });
0286: }
0287: });
0288:
0289: } catch (java.net.MalformedURLException murle) {
0290: worldURLBase = (URL) null;
0291: throw murle;
0292: }
0293: }
0294:
0295: /**
0296: * Description of the Method
0297: *
0298: * @param url Description of the Parameter
0299: * @return Description of the Return Value
0300: * @exception IOException If the file could not be opened.
0301: */
0302: InputStream openURL(URL url) throws IOException {
0303: InputStream is = null;
0304: if (url.getProtocol().equals("file")) {
0305: if (debug) {
0306: System.out
0307: .println("Using direct input stream on file url");
0308: }
0309: URLConnection urlc = url.openConnection();
0310: urlc.connect();
0311: is = new DataInputStream(urlc.getInputStream());
0312: } else {
0313: double start = 0;
0314: if (timing) {
0315: start = Time.getNow();
0316: }
0317: ContentNegotiator cn = new ContentNegotiator(url);
0318: byte[] buf = (byte[]) cn.getContent();
0319: is = new ByteArrayInputStream(buf);
0320: if (timing) {
0321: double elapsed = Time.getNow() - start;
0322: System.out.println("Loader: open and buffer URL in: "
0323: + numFormat.format(elapsed, 2) + " seconds");
0324: }
0325: }
0326: return is;
0327: }
0328:
0329: /**
0330: * Description of the Method
0331: *
0332: * @param worldURL Description of the Parameter
0333: * @return Description of the Return Value
0334: * @exception IOException If the file could not be opened.
0335: * @exception vrml.InvalidVRMLSyntaxException If there was a problem unzipping or parsing.
0336: */
0337: public Scene load(URL worldURL) throws IOException,
0338: vrml.InvalidVRMLSyntaxException {
0339: return load(openURL(worldURL));
0340: }
0341:
0342: /**
0343: * Description of the Method
0344: *
0345: * @param is Description of the Parameter
0346: * @return Description of the Return Value
0347: * @exception vrml.InvalidVRMLSyntaxException If there was a problem unzipping.
0348: */
0349: Reader checkInput(InputStream is)
0350: throws vrml.InvalidVRMLSyntaxException {
0351: if (is.markSupported() == false) {
0352: is = new BufferedInputStream(is);
0353: }
0354:
0355: // check for a gzipped input stream
0356: byte[] buf = new byte[20];
0357: try {
0358: is.mark(buf.length);
0359: is.read(buf, 0, buf.length);
0360: is.reset();
0361: //if (buf[0]==0x1f && buf[1]==0x8b && buf[2]==0x08 && buf[3]==0x08)
0362: // don't ask why the header changes.
0363: if ((buf[0] == 37 && buf[1] == 213 && buf[2] == 8 && buf[3] == 8)
0364: || (buf[0] == 31 && buf[1] == -117 && buf[2] == 8 && buf[3] == 8)) {
0365: double start = 0.0;
0366: if (timing) {
0367: start = Time.getNow();
0368: }
0369: gis = new GZIPInputStream(is);
0370: ByteBuf bb = new ByteBuf();
0371: int r;
0372: while ((r = gis.read()) != -1) {
0373: bb.add((byte) r);
0374: }
0375: bb.trim();
0376: is = new ByteArrayInputStream(bb.array);
0377: if (debug) {
0378: System.out.println(new String(bb.array));
0379: }
0380: if (timing) {
0381: double elapsed = Time.getNow() - start;
0382: System.out
0383: .println("Loader: upzip URL in: "
0384: + numFormat.format(elapsed, 2)
0385: + " seconds");
0386: }
0387: is.mark(buf.length);
0388: is.read(buf, 0, buf.length);
0389: is.reset();
0390: }
0391: } catch (IOException e) {
0392: vrml.InvalidVRMLSyntaxException i = new vrml.InvalidVRMLSyntaxException(
0393: "Problem unzipping");
0394: i.initCause(e);
0395: throw i;
0396: }
0397: return new InputStreamReader(is);
0398: }
0399:
0400: /**
0401: * Description of the Method
0402: *
0403: * @param reader Description of the Parameter
0404: * @return Description of the Return Value
0405: */
0406: Parser newParser(Reader reader) {
0407: // only one of these blocks of code should be in use:
0408:
0409: // For using generated Token manager:
0410: return new Parser(this , reader);
0411: // for custom token manager (USER_TOKEN_MANAGER=true in Parser.jj):
0412: // VrmlCharStream charStream = new VrmlCharStream(reader, 0, 0);
0413: // VrmlTokenManager tokenManager =
0414: // new VrmlTokenManager(charStream, this);
0415: // return new Parser(this, tokenManager);
0416: }
0417:
0418: /**
0419: * Description of the Method
0420: *
0421: * @param is Description of the Parameter
0422: * @return Description of the Return Value
0423: * @exception vrml.InvalidVRMLSyntaxException If there was a problem unzipping or parsing.
0424: */
0425: public Scene load(InputStream is)
0426: throws vrml.InvalidVRMLSyntaxException {
0427: Reader reader = checkInput(is);
0428: return load(reader);
0429: }
0430:
0431: /**
0432: * Description of the Method
0433: *
0434: * @param reader Description of the Parameter
0435: * @return Description of the Return Value
0436: * @exception vrml.InvalidVRMLSyntaxException If there was a problem parsing.
0437: */
0438: public Scene load(Reader reader)
0439: throws vrml.InvalidVRMLSyntaxException {
0440: double start = 0.0;
0441:
0442: // Check for VRML header
0443: // if (reader.markSupported() == false) {
0444: // reader = new BufferedReader(reader);
0445: //}
0446: //char[] buf = new char[20];
0447: //reader.mark(buf.length);
0448: //reader.read(buf, 0, buf.length);
0449: //reader.reset();
0450: //String header = new String(buf);
0451: //bug: doesn't always recognize as correct header even if it is
0452: //if (!header.startsWith("#VRML V2.0 utf8",header.indexOf('#'))) {
0453: // throw new vrml.InvalidVRMLSyntaxException();
0454: //}
0455:
0456: if (timing) {
0457: start = Time.getNow();
0458: nodeInitImpl = 0.0;
0459: ifsParse = 0.0;
0460: ifsInit = 0.0;
0461: MFInt32Parse = 0.0;
0462: MFInt32Copy = 0.0;
0463: MFVec3fParse = 0.0;
0464: MFVec3fCopy = 0.0;
0465: numGroups = 0;
0466: numIfs = 0;
0467: }
0468:
0469: // save the old values (if any) to restore after the load
0470: Parser oldParser = parser;
0471: Scene oldScene = scene;
0472:
0473: parser = newParser(reader);
0474: scene = new Scene();
0475: // general Scene bug:
0476: // A WorldInfo node can only be instanced by someone who
0477: // has a Loader. The Scene object has no knowledge of
0478: // its Loader owner, but has to handle the WorldInfo,
0479: // and in the case one is not provided in the file,
0480: // can't create the default WorldInfo.
0481: // this is also a general impl.Node bug, that you need
0482: // the Loader to instance one is too restrictive.
0483: scene.setWorldInfo(new WorldInfo(this ));
0484:
0485: defTableStack.push(scene);
0486:
0487: try {
0488: parser.Scene();
0489: } catch (ParseException e) {
0490: vrml.InvalidVRMLSyntaxException i = new vrml.InvalidVRMLSyntaxException(
0491: "Last token was \"" + parser.token.image
0492: + "\" at line " + parser.token.beginLine);
0493: i.initCause(e);
0494: throw i;
0495: }
0496: defTableStack.pop();
0497:
0498: if (timing) {
0499: double elapsed = Time.getNow() - start;
0500: System.out.println("Parse in "
0501: + numFormat.format(elapsed, 2) + " seconds");
0502: System.out.println("Node init impl: "
0503: + numFormat.format(nodeInitImpl, 2)
0504: + " IFS parse: " + numFormat.format(ifsParse, 2)
0505: + " IFS init: " + numFormat.format(ifsInit, 2));
0506: System.out.println("MFInt32 parse: "
0507: + numFormat.format(MFInt32Parse, 2) + " copy: "
0508: + numFormat.format(MFInt32Copy, 2)
0509: + " MFVec3f parse: "
0510: + numFormat.format(MFVec3fParse, 2) + " copy: "
0511: + numFormat.format(MFVec3fCopy, 2));
0512: System.out.println("Scene contains " + numGroups
0513: + " groups " + numIfs + " IFSs");
0514: }
0515:
0516: Scene retval = scene;
0517:
0518: // restore the prevous load's values
0519: parser = oldParser;
0520: scene = oldScene;
0521:
0522: return retval;
0523: }
0524:
0525: /**
0526: * Description of the Method
0527: *
0528: * @param urlString Description of the Parameter
0529: * @return Description of the Return Value
0530: * @exception java.net.MalformedURLException Either no legal protocol could be found in a specification string or the string could not be parsed.
0531: */
0532: public URL stringToURL(String urlString)
0533: throws java.net.MalformedURLException {
0534: URL loadUrl;
0535: try {
0536: loadUrl = new URL(urlString);
0537: } catch (java.net.MalformedURLException e) {
0538: // might be a relative URL
0539: loadUrl = new URL(worldURLBaseName + urlString);
0540: // will throw MalformedURLException if still bad
0541: }
0542: return loadUrl;
0543: }
0544:
0545: // load the proto specified by the urlString, name it protoName, ensure
0546: // that it has fields that match protoFields
0547: // TODO: cache old loads to avoid reloading multiple EXTPROTOS with
0548: // the same URL but different "#name" specifications
0549: /**
0550: * Description of the Method
0551: *
0552: * @param urlString Description of the Parameter
0553: * @param protoName Description of the Parameter
0554: * @param protoFields Description of the Parameter
0555: * @exception IOException If the proto could not be opened.
0556: * @exception MalformedURLException Either no legal protocol could be found in urlString or it could not be parsed.
0557: * @exception ParseException If there was a problem parsing.
0558: */
0559: public void loadProto(String urlString, String protoName,
0560: Vector protoFields) throws IOException,
0561: MalformedURLException, ParseException {
0562: // check for a external name
0563: String extName = null;
0564: int hashIndex = urlString.lastIndexOf("#");
0565: if (hashIndex != -1) {
0566: extName = urlString.substring(hashIndex + 1);
0567: urlString = urlString.substring(0, hashIndex);
0568: }
0569: // TODO: cut short the parse once we find the right proto
0570: URL url = stringToURL(urlString);
0571: Scene protoScene;
0572: try {
0573: protoScene = load(url);
0574: } catch (Exception e) {
0575: if (e instanceof java.io.IOException) {
0576: throw (IOException) e;
0577: } else if (e instanceof java.net.MalformedURLException) {
0578: throw (MalformedURLException) e;
0579: } else if (e instanceof ParseException) {
0580: throw (ParseException) e;
0581: } else {
0582: System.out
0583: .println("Internal error parsing EXTERNPROTO:");
0584: e.printStackTrace(System.err);
0585: return;
0586: }
0587: }
0588: Proto proto;
0589: if (extName != null) {
0590: if ((proto = (Proto) protoScene.protos.get(extName)) == null) {
0591: throw new ParseException(
0592: "Parsing EXTERNPROTO, no PROTO " + " named \""
0593: + extName + "\" found in URL \""
0594: + urlString + "\"");
0595: }
0596: } else {
0597: if ((proto = protoScene.firstProto) == null) {
0598: throw new ParseException(
0599: "Parsing EXTERNPROTO, no PROTO "
0600: + "found in URL \"" + urlString + "\"");
0601: }
0602: }
0603:
0604: proto.name = protoName;
0605: // TODO: check if protoFields match fields in proto
0606: scene.protos.put(protoName, proto);
0607: }
0608:
0609: // TODO: these should get passed to the application so they can be
0610: // displayed in a GUI
0611: /**
0612: * Description of the Method
0613: *
0614: * @param id Description of the Parameter
0615: * @param warning Description of the Parameter
0616: */
0617: void warning(String id, String warning) {
0618: if (warnLevel == WARN_NEVER) {
0619: return;
0620: }
0621: if (warnLevel == WARN_ONCE) {
0622: if (warnSet.contains(id)) {
0623: return;
0624: } else {
0625: warnSet.add(id);
0626: }
0627: }
0628: // this should be passed to a listener
0629: System.err.println("VRML Loader warning: " + id
0630: + " reports: \n\"" + warning + "\" at or before line "
0631: + parser.currentLine());
0632: return;
0633: }
0634:
0635: /** Description of the Method */
0636: void clear() {
0637: curProto = null;
0638: }
0639:
0640: /**
0641: * Description of the Method
0642: *
0643: * @return Description of the Return Value
0644: */
0645: Proto curProto() {
0646: return curProto;
0647: }
0648:
0649: // these are the "switching" versions which use the current namespace
0650: /**
0651: * Description of the Method
0652: *
0653: * @param defName Description of the Parameter
0654: * @param node Description of the Parameter
0655: */
0656: void namespaceDefine(String defName, BaseNode node) {
0657: if (node == null) {
0658: System.err.println("Define with name " + defName
0659: + " is null");
0660: } else {
0661: node.define(defName);
0662: }
0663: Namespace namespace = (Namespace) defTableStack.peek();
0664: namespace.define(defName, node);
0665: }
0666:
0667: /**
0668: * Description of the Method
0669: *
0670: * @param defName Description of the Parameter
0671: * @return Description of the Return Value
0672: */
0673: BaseNode namespaceUse(String defName) {
0674: Namespace namespace = (Namespace) defTableStack.peek();
0675: BaseNode retval = namespace.use(defName);
0676: return retval;
0677: }
0678:
0679: /**
0680: * Description of the Method
0681: *
0682: * @param proto Description of the Parameter
0683: */
0684: synchronized void beginProtoDefine(Proto proto) {
0685: curProto = proto;
0686: if (debug) {
0687: System.out.println("Starting to define proto: "
0688: + proto.getName());
0689: }
0690: if (scene.firstProto == null) {
0691: if (debug) {
0692: System.out.println("Proto is first in scene");
0693: }
0694: scene.firstProto = proto;
0695: }
0696: scene.protos.put(proto.getName(), proto);
0697: defTableStack.push(proto);
0698: }
0699:
0700: /** Description of the Method */
0701: synchronized void endProtoDefine() {
0702: if (debug) {
0703: System.out.println("Proto " + curProto.getName()
0704: + " is stored as\n" + curProto);
0705: }
0706: curProto = null;
0707: defTableStack.pop();
0708: }
0709:
0710: /**
0711: * Description of the Method
0712: *
0713: * @param name Description of the Parameter
0714: * @return Description of the Return Value
0715: */
0716: synchronized Proto lookupProto(String name) {
0717: Proto proto = (Proto) scene.protos.get(name);
0718: if (debug) {
0719: System.out.println("Proto " + proto.getName()
0720: + " is retrived as:\n" + proto);
0721: }
0722: return proto;
0723: }
0724:
0725: /**
0726: * Description of the Method
0727: *
0728: * @param newInstance Description of the Parameter
0729: */
0730: synchronized void beginProtoInstance(ProtoInstance newInstance) {
0731: protoInstance = newInstance;
0732: defTableStack.push(newInstance);
0733: }
0734:
0735: /**
0736: * Description of the Method
0737: *
0738: * @param org Description of the Parameter
0739: * @param clone Description of the Parameter
0740: */
0741: synchronized void registerClone(BaseNode org, BaseNode clone) {
0742:
0743: if (debug) {
0744: System.out.println("Registering clone "
0745: + clone.toStringId() + " = " + clone);
0746: }
0747: // make sure the clone is defined in the new namespace if needed
0748: if (org.defName != null) {
0749: if (debug) {
0750: System.out.println("Register clone DEF " + org.defName);
0751: }
0752: namespaceDefine(org.defName, clone);
0753: }
0754:
0755: // Make sure the clone has been initialized (done after the name
0756: // is registered so that anonymous nodes can be optimized
0757: clone.initImpl();
0758:
0759: // tell the protoInstance about the new clone
0760: if (protoInstance != null) {
0761: protoInstance.proto.registerClone(org, clone);
0762: }
0763: }
0764:
0765: /** Description of the Method */
0766: synchronized void endProtoInstance() {
0767: protoInstance = null;
0768: defTableStack.pop();
0769: }
0770:
0771: // internal version, only test types if flag is set. Protos make
0772: // illegal EventIn->EventIn and EventOut->EventOut routes for IS maps
0773: /**
0774: * Description of the Method
0775: *
0776: * @param fromNode Description of the Parameter
0777: * @param fromEventOut Description of the Parameter
0778: * @param toNode Description of the Parameter
0779: * @param toEventIn Description of the Parameter
0780: * @param testTypes Description of the Parameter
0781: */
0782: synchronized void connect(BaseNode fromNode, String fromEventOut,
0783: BaseNode toNode, String toEventIn, boolean testTypes) {
0784: Field fromField;
0785: Field toField;
0786:
0787: if (curProto != null) {
0788: curProto
0789: .addRoute(fromNode, fromEventOut, toNode, toEventIn);
0790: return;
0791: }
0792:
0793: if (fromNode instanceof Script) {
0794: fromField = ((Script) fromNode).getField(fromEventOut);
0795: } else {
0796: fromField = ((Node) fromNode).getField(fromEventOut);
0797: }
0798: if (fromField instanceof ConstField) {
0799: fromField = ((ConstField) fromField).ownerField;
0800: }
0801: if (testTypes && !fromField.isEventOut()) {
0802: throw new vrml.InvalidEventOutException();
0803: }
0804:
0805: if (toNode instanceof Script) {
0806: toField = ((Script) toNode).getField(toEventIn);
0807: } else {
0808: toField = ((Node) toNode).getField(toEventIn);
0809: }
0810: if (fromField instanceof ConstField) {
0811: throw new vrml.InvalidEventOutException();
0812: }
0813: if (testTypes && !toField.isEventIn()) {
0814: throw new vrml.InvalidEventInException();
0815: }
0816:
0817: fromField.connectToField(toField);
0818:
0819: if (debug) {
0820: System.out.println("Added ROUTE ");
0821: }
0822: if (debug) {
0823: System.out.println(" " + fromNode.toStringId() + "."
0824: + fromField.toStringId());
0825: }
0826: if (debug) {
0827: System.out.println("TO " + toNode.toStringId() + "."
0828: + toField.toStringId());
0829: }
0830:
0831: // tell the toNode to make the capability writeable
0832: // Note: will only work if Node has called eventify--
0833: // should be done in the Field constructors...
0834: if (!(toNode instanceof SceneTransform)) {
0835: toField.markWriteable();
0836: }
0837:
0838: }
0839:
0840: /**
0841: * Description of the Method
0842: *
0843: * @param scriptClassName Description of the Parameter
0844: * @return Description of the Return Value
0845: * @exception IllegalAccessException No access to the definition of the specified class.
0846: * @exception InstantiationException There was a problem making an instance of the class.
0847: * @exception ClassNotFoundException The class was not found.
0848: */
0849: public vrml.node.Script newScriptInstanceFromClassName(
0850: String scriptClassName) throws IllegalAccessException,
0851: InstantiationException, ClassNotFoundException {
0852: return (vrml.node.Script) scl.loadClass(scriptClassName)
0853: .newInstance();
0854: }
0855:
0856: /**
0857: * Gets the bytes attribute of the Loader object
0858: *
0859: * @param URLstring Description of the Parameter
0860: * @return The bytes value
0861: * @exception IOException If the URLstring could not be opened.
0862: * @exception MalformedURLException Either no legal protocol could be found in URLstring or it could not be parsed.
0863: */
0864: byte[] getBytes(String URLstring) throws MalformedURLException,
0865: IOException {
0866: URL fu;
0867: byte[] buf = null;
0868: if (debug) {
0869: System.out.println("loader.getBytes(" + URLstring
0870: + ") called");
0871: }
0872: // try {
0873: fu = new URL(URLstring);
0874: if (fu.getProtocol().equals("file")) {
0875: if (debug) {
0876: System.out
0877: .println("Using direct input stream on file url");
0878: }
0879: URLConnection urlc = fu.openConnection();
0880: urlc.connect();
0881: urlc.getContentType();
0882: DataInputStream is = new DataInputStream(urlc
0883: .getInputStream());
0884: int length = urlc.getContentLength();
0885: buf = new byte[length];
0886: is.readFully(buf, 0, length);
0887: } else {
0888: ContentNegotiator cn = new ContentNegotiator(fu);
0889: buf = (byte[]) cn.getContent();
0890: }
0891: return buf;
0892: }
0893:
0894: /**
0895: * Gets the relBytes attribute of the Loader object
0896: *
0897: * @param relURL Description of the Parameter
0898: * @return The relBytes value
0899: * @exception IOException If the relURL could not be opened.
0900: * @exception MalformedURLException Either no legal protocol could be found in relURL or it could not be parsed.
0901: */
0902: byte[] getRelBytes(String relURL) throws MalformedURLException,
0903: IOException {
0904: String fullURL = worldURLBaseName + relURL;
0905: byte[] buf = getBytes(fullURL);
0906: return buf;
0907: }
0908:
0909: /**
0910: * Gets the viewpointStack attribute of the Loader object
0911: *
0912: * @return The viewpointStack value
0913: */
0914: Stack getViewpointStack() {
0915: if (browser != null) {
0916: return browser.viewpointStack;
0917: } else {
0918: return null;
0919: }
0920: }
0921:
0922: /**
0923: * Gets the navigationInfoStack attribute of the Loader object
0924: *
0925: * @return The navigationInfoStack value
0926: */
0927: Stack getNavigationInfoStack() {
0928: if (browser != null) {
0929: return browser.navigationInfoStack;
0930: } else {
0931: return null;
0932: }
0933: }
0934:
0935: /**
0936: * Gets the fogStack attribute of the Loader object
0937: *
0938: * @return The fogStack value
0939: */
0940: Stack getFogStack() {
0941: if (browser != null) {
0942: return browser.fogStack;
0943: } else {
0944: return null;
0945: }
0946: }
0947:
0948: /**
0949: * Gets the backgroundStack attribute of the Loader object
0950: *
0951: * @return The backgroundStack value
0952: */
0953: Stack getBackgroundStack() {
0954: if (browser != null) {
0955: return browser.backgroundStack;
0956: } else {
0957: return null;
0958: }
0959: }
0960:
0961: /** Description of the Method */
0962: void cleanUp() {
0963: if (browser != null) {
0964: browser.cleanUp();
0965: }
0966: }
0967:
0968: // Parser callbacks:
0969: /**
0970: * Sets the description attribute of the Loader object
0971: *
0972: * @param description The new description value
0973: */
0974: void setDescription(String description) {
0975: scene.setDescription(description);
0976: }
0977:
0978: /**
0979: * Adds a feature to the Route attribute of the Loader object
0980: *
0981: * @param fromNode The feature to be added to the Route attribute
0982: * @param fromEventOut The feature to be added to the Route attribute
0983: * @param toNode The feature to be added to the Route attribute
0984: * @param toEventIn The feature to be added to the Route attribute
0985: */
0986: void addRoute(BaseNode fromNode, String fromEventOut,
0987: BaseNode toNode, String toEventIn) {
0988: if (loaderMode > LOAD_STATIC) {
0989: fromEventOut = Field.baseName(fromEventOut);
0990: toEventIn = Field.baseName(toEventIn);
0991: connect(fromNode, fromEventOut, toNode, toEventIn, true);
0992: }
0993: }
0994:
0995: /**
0996: * Adds a feature to the Object attribute of the Loader object
0997: *
0998: * @param node The feature to be added to the Object attribute
0999: */
1000: void addObject(BaseNode node) {
1001: if (curProto != null) {
1002: if (debug) {
1003: System.out.println("Loader.addObject(): adding node "
1004: + node.toStringId()
1005: + " to curProto instead of scene");
1006: }
1007: curProto.addObject(node);
1008: } else {
1009: scene.addObject(node);
1010: }
1011: }
1012:
1013: // The following add the node to the scene, unless we are defining a
1014: // proto. For protos, the definition does not get added to the scene.
1015: // When the proto is instanced, the instance does get added to the scene.
1016: // Note the scene interface is shared with other file loaders, otherwise
1017: // this would be more direct.
1018: /**
1019: * Adds a feature to the Viewpoint attribute of the Loader object
1020: *
1021: * @param viewpoint The feature to be added to the Viewpoint attribute
1022: */
1023: void addViewpoint(Viewpoint viewpoint) {
1024: if ((curProto == null) && (scene != null)) {
1025: scene.addViewpoint(viewpoint);
1026: }
1027: }
1028:
1029: /**
1030: * Adds a feature to the NavigationInfo attribute of the Loader object
1031: *
1032: * @param navInfo The feature to be added to the NavigationInfo attribute
1033: */
1034: void addNavigationInfo(NavigationInfo navInfo) {
1035: if ((curProto == null) && (scene != null)) {
1036: scene.addNavigationInfo(navInfo);
1037: }
1038: }
1039:
1040: /**
1041: * Adds a feature to the Background attribute of the Loader object
1042: *
1043: * @param background The feature to be added to the Background attribute
1044: */
1045: void addBackground(Background background) {
1046: if ((curProto == null) && (scene != null)) {
1047: scene.addBackground(background);
1048: }
1049: }
1050:
1051: /**
1052: * Adds a feature to the Fog attribute of the Loader object
1053: *
1054: * @param fog The feature to be added to the Fog attribute
1055: */
1056: void addFog(Fog fog) {
1057: if ((curProto == null) && (scene != null)) {
1058: scene.addFog(fog);
1059: }
1060: }
1061:
1062: /**
1063: * Adds a feature to the Light attribute of the Loader object
1064: *
1065: * @param light The feature to be added to the Light attribute
1066: */
1067: void addLight(Light light) {
1068: if ((curProto == null) && (scene != null)) {
1069: scene.addLight(light);
1070: }
1071: }
1072:
1073: /**
1074: * Adds a feature to the SharedGroup attribute of the Loader object
1075: *
1076: * @param newGroup The feature to be added to the SharedGroup attribute
1077: */
1078: void addSharedGroup(SharedGroup newGroup) {
1079: if ((curProto == null) && (scene != null)) {
1080: scene.addSharedGroup(newGroup);
1081: }
1082: }
1083:
1084: /**
1085: * Adds a feature to the TimeSensor attribute of the Loader object
1086: *
1087: * @param ts The feature to be added to the TimeSensor attribute
1088: */
1089: void addTimeSensor(TimeSensor ts) {
1090: if ((curProto == null) && (scene != null)) {
1091: scene.addTimeSensor(ts);
1092: }
1093: }
1094:
1095: /**
1096: * Adds a feature to the VisibilitySensor attribute of the Loader object
1097: *
1098: * @param vs The feature to be added to the VisibilitySensor attribute
1099: */
1100: void addVisibilitySensor(VisibilitySensor vs) {
1101: if ((curProto == null) && (scene != null)) {
1102: scene.addVisibilitySensor(vs);
1103: }
1104: }
1105:
1106: /**
1107: * Adds a feature to the TouchSensor attribute of the Loader object
1108: *
1109: * @param ts The feature to be added to the TouchSensor attribute
1110: */
1111: void addTouchSensor(TouchSensor ts) {
1112: if ((curProto == null) && (scene != null)) {
1113: scene.addTouchSensor(ts);
1114: }
1115: }
1116:
1117: /**
1118: * Adds a feature to the AudioClip attribute of the Loader object
1119: *
1120: * @param clip The feature to be added to the AudioClip attribute
1121: */
1122: void addAudioClip(AudioClip clip) {
1123: if ((curProto == null) && (scene != null)) {
1124: scene.addAudioClip(clip);
1125: }
1126: }
1127:
1128: /**
1129: * Sets the worldInfo attribute of the Loader object
1130: *
1131: * @param worldInfo The new worldInfo value
1132: */
1133: void setWorldInfo(WorldInfo worldInfo) {
1134: if ((curProto == null) && (scene != null)) {
1135: scene.setWorldInfo(worldInfo);
1136: }
1137: }
1138: }
|