/***************************************************************************** * Web3d Consortium Copyright (c) 2005 * Java Source * * This source is licensed under the GNU LGPL v2.1 * Please read http://www.gnu.org/copyleft/lgpl.html for more information * * This software comes with the standard NO WARRANTY disclaimer for any * purpose. Use it at your own risk. If there's a problem you get to fix it. * *****************************************************************************/ package org.web3d.parser.x3d; // External imports import org.xml.sax.SAXException; import org.xml.sax.Attributes; import com.sun.xml.fastinfoset.sax.AttributesHolder; import org.jvnet.fastinfoset.EncodingAlgorithmIndexes; import org.jvnet.fastinfoset.sax.PrimitiveTypeContentHandler; import org.jvnet.fastinfoset.sax.EncodingAlgorithmContentHandler; // Local imports import org.web3d.util.*; import org.web3d.vrml.sav.*; import org.web3d.vrml.lang.InvalidFieldException; import org.web3d.x3d.jaxp.X3DSAVAdapter; import org.web3d.vrml.export.compressors.NodeCompressor; //import org.web3d.vrml.export.compressors.TestCompressor; /** * Handles the reading of elements from a FastInfoSet stream and converts * it to an X3D world. * * This expects attributes of type TypeAttributes instead of just Attributes. * * @author Alan Hudson * @version $Revision: 1.15 $ */ class FastInfosetElementReader extends X3DSAVAdapter implements PrimitiveTypeContentHandler, EncodingAlgorithmContentHandler { private final BooleanStack inCompressorStack; private final BooleanStack skipEEStack; private NodeCompressor currentCompressor; private String lastNodeName; private boolean skipEndElement; /** After the payload any metadata is ignored */ private boolean pastPayload; /** Switch between methods, should go away */ private final boolean compressedAttWay = false; /** * Initialise a new instance of the reader */ FastInfosetElementReader() { inCompressorStack = new BooleanStack(); inCompressorStack.push(false); skipEEStack = new BooleanStack(); } /** * Start the processing of a new element with the given collection of * attribute information. * * @param namespace The namespace for the element. Null if not used * @param name The local name of the element to create * @param qName The qualified name of the element including prefix * @param attribs The collection of attributes to use * @throws SAXException The element can't be found in the underlying * factory */ @Override public void startElement(String namespace, String localName, String qName, Attributes attribs) throws SAXException { //System.out.println("SE: " + qName); boolean inCompressor = inCompressorStack.peek(); if(useIsCurrent) throw new SAXException(USE_WITH_KIDS_MSG); // Remove X3D: namespace prefix. Need to handle generically if(qName.startsWith("X3D:")) qName = qName.substring(4); Integer el_type = elementMap.get(qName); String value; AttributesHolder atts = (AttributesHolder) attribs; BinaryContentHandler bch = (BinaryContentHandler) contentHandler; // TODO: Would a HashMap be faster here? if (qName.equals("MetadataSet")) { String name = attribs.getValue("name"); if (name.equals(".x3db")) { // Pop Parents inCompressor and replace with true inCompressorStack.pop(); inCompressorStack.push(true); inCompressor = true; inCompressorStack.push(inCompressor); skipEEStack.push(true); pastPayload = false; return; } else if (pastPayload) { pastPayload = false; inCompressorStack.push(inCompressor); skipEEStack.push(true); return; } } if (inCompressor) { String name = attribs.getValue("name"); if (name != null) { switch (name) { case "encoding": // currentCompressor = new TestCompressor(); inCompressorStack.push(inCompressor); skipEEStack.push(true); return; case "payload": int idx = attribs.getIndex("value"); int algo = atts.getAlgorithmIndex(idx); switch (algo) { case EncodingAlgorithmIndexes.INT: int[] ival = (int[]) atts.getAlgorithmData(idx); currentCompressor.decompress(ival); currentCompressor.fillData(lastNodeName, bch); break; case X3DBinaryConstants.DELTA_ZLIB_INT_ARRAY_ALGORITHM_ID: int[] i4val = (int[]) atts.getAlgorithmData(idx); currentCompressor.decompress(i4val); currentCompressor.fillData(lastNodeName, bch); break; default: errorReporter.errorReport("Data in wrong format! " + atts.getAlgorithmIndex(idx), null); break; } pastPayload = true; inCompressorStack.push(inCompressor); skipEEStack.push(true); return; } } } else { lastNodeName = qName; } if(el_type == null) { if(checkForSceneTag) throw new SAXException(NO_SCENE_TAG_MSG); // We're obviously in a new node, so go look for // the container field attribute to do a "startField" // call. However, no point doing anything if we don't have a // content handler to call. Need to weed out the one case were // we are the root node of a proto declaration body. if(fieldDeclDepth != 0) { String field_name = attribs.getValue(CONTAINER_ATTR); // No container field name defined? Look one up. If that // fails, guess and put in "children" since that is the // most commonly used default. if(field_name == null) { field_name = containerFields.getProperty(qName); if(field_name == null) field_name = "children"; } if(contentHandler != null) { try { contentHandler.startField(field_name); } catch(InvalidFieldException ife) { errorReporter.errorReport("No field: " + field_name + " for: " + qName, null); } } } value = attribs.getValue(USE_ATTR); fieldDeclDepth++; if(value != null) { if(contentHandler != null) contentHandler.useDecl(value); useIsCurrent = true; } else { value = attribs.getValue(DEF_ATTR); if(contentHandler != null) contentHandler.startNode(qName, value); } Object type; String att_name; Integer id_int; /* if (inCompressor) { currentCompressor.fillData(qName, bch); } */ // now process all the attributes! int num_attr = attribs.getLength(); for(int i = 0; !useIsCurrent && i < num_attr; i++) { att_name = atts.getLocalName(i); //System.out.println("att_name: " + att_name); switch (att_name) { case "encoder": // TODO: make hashmap if (atts.getValue(i).equals("1")) { inCompressor = true; // currentCompressor = new TestCompressor(); } continue; case "data": type = atts.getAlgorithmData(i); if (atts.getAlgorithmIndex(i) == EncodingAlgorithmIndexes.INT) { // TODO: Assume all INT's are arrays int[] ival = (int[]) atts.getAlgorithmData(i); currentCompressor.decompress(ival); currentCompressor.fillData(qName, bch); } else { errorReporter.errorReport("Data in wrong format!", null); } continue; } // Check to see if the attribute is one of the reserved // set. If so, treat separately from the normal field // processing. id_int = attributeMap.get(att_name); if (id_int != null) { continue; } decodeField(atts,i,bch,att_name); } inCompressorStack.push(inCompressor); skipEEStack.push(false); return; } switch(el_type) { case X3D_TAG: loadContainerProperties(Float.parseFloat(versionString.substring(1))); String version = attribs.getValue("version"); if (version == null) { version = "V3.0"; } else { version = "V" + version; } if(contentHandler != null) { contentHandler.startDocument(null, // TODO: Not sure how to get filename worldURL, XML_ENCODING, "#X3D", version, null); } value = attribs.getValue("profile"); if(value == null) throw new SAXException(NO_PROFILE_MSG); if(contentHandler != null) contentHandler.profileDecl(value); // Setup cData if(overrideLex) characterDataBuffer.append("\""); useIsCurrent = false; checkForSceneTag = true; break; case HEAD_TAG: // do nothing break; case COMPONENT_TAG: value = attribs.getValue(NAME_ATTR) + ':' + attribs.getValue("level"); if(contentHandler != null) contentHandler.componentDecl(value); break; case SCENE_TAG: checkForSceneTag = false; break; case PROTO_DECL_TAG: if(checkForSceneTag) throw new SAXException(NO_SCENE_TAG_MSG); if(protoHandler != null) protoHandler.startProtoDecl(attribs.getValue(NAME_ATTR)); scriptFlagStack.push(false); inScript = false; break; case PROTO_INTERFACE_TAG: if(checkForSceneTag) throw new SAXException(NO_SCENE_TAG_MSG); // Don't do anything from here. We've already started the // proto decl handling in the PROTO_DECL_TAG. break; case PROTO_BODY_TAG: if(checkForSceneTag) throw new SAXException(NO_SCENE_TAG_MSG); if(protoHandler != null) { protoHandler.endProtoDecl(); protoHandler.startProtoBody(); } break; case EXTERNPROTO_DECL_TAG: if(checkForSceneTag) throw new SAXException(NO_SCENE_TAG_MSG); if(protoHandler != null) { value = attribs.getValue(NAME_ATTR); protoHandler.startExternProtoDecl(value); epUrl = attribs.getValue("url"); } scriptFlagStack.push(false); inScript = false; break; case IS_TAG: if(checkForSceneTag) throw new SAXException(NO_SCENE_TAG_MSG); break; case CONNECT_TAG: if(checkForSceneTag) throw new SAXException(NO_SCENE_TAG_MSG); if(contentHandler != null) contentHandler.startField(attribs.getValue("nodeField")); if(protoHandler != null) protoHandler.protoIsDecl(attribs.getValue("protoField")); break; case FIELD_TAG: if(checkForSceneTag) throw new SAXException(NO_SCENE_TAG_MSG); int access_type = processFieldAccess(attribs.getValue("accessType"), attribs.getValue(NAME_ATTR)); // perhaps this should do some prior checking of the // access type to make sure that the user don't do anything // dumb like set a value for eventIn/Out. boolean is_used = false; depthCountStack.push(fieldDeclDepth); fieldDeclDepth = 0; if((value = attribs.getValue("USE")) != null) is_used = true; else value = attribs.getValue(VALUE_ATTR); if(inScript) { if(is_used) { if(scriptHandler != null) { scriptHandler.scriptFieldDecl(access_type, attribs.getValue("type"), attribs.getValue(NAME_ATTR), null); } if(contentHandler != null) contentHandler.useDecl(value); } else { if (value != null && value.length() == 0) { value = null; } if(scriptHandler != null) scriptHandler.scriptFieldDecl(access_type, attribs.getValue("type"), attribs.getValue(NAME_ATTR), value); } } else { if(is_used) { if(protoHandler != null) { protoHandler.protoFieldDecl(access_type, attribs.getValue("type"), attribs.getValue(NAME_ATTR), null); } if(contentHandler != null) contentHandler.useDecl(value); } else { if (value != null && value.length() == 0) { value = null; } if(protoHandler != null) protoHandler.protoFieldDecl(access_type, attribs.getValue("type"), attribs.getValue(NAME_ATTR), value); } } break; case META_TAG: if(contentHandler != null) { contentHandler.metaDecl(attribs.getValue(NAME_ATTR), attribs.getValue("type")); } break; case PROTO_INSTANCE_TAG: if(checkForSceneTag) throw new SAXException(NO_SCENE_TAG_MSG); if(contentHandler != null) { String field_name = attribs.getValue(CONTAINER_ATTR); // No container field name defined? Look one up. If that // fails, guess and put in "children" since that is the // most commonly used default. if(field_name == null) { field_name = containerFields.getProperty(localName); if(field_name == null) field_name = "children"; } if(fieldDeclDepth != 0) { contentHandler.startField(field_name); } fieldDeclDepth++; contentHandler.startNode(attribs.getValue(NAME_ATTR), attribs.getValue(DEF_ATTR)); } /* // All of the above was commented out, restored for now? if (contentHandler != null) contentHandler.startNode(attribs.getValue(NAME_ATTR), attribs.getValue(DEF_ATTR)); */ inScript = false; break; case IMPORT_TAG: if(contentHandler != null) contentHandler.importDecl(attribs.getValue("inlineDEF"), attribs.getValue("exportedDEF"), attribs.getValue(AS_ATTR)); break; case EXPORT_TAG: if(checkForSceneTag) throw new SAXException(NO_SCENE_TAG_MSG); if(contentHandler != null) contentHandler.exportDecl(attribs.getValue("localDEF"), attribs.getValue(AS_ATTR)); break; case ROUTE_TAG: if(checkForSceneTag) throw new SAXException(NO_SCENE_TAG_MSG); if(routeHandler != null) routeHandler.routeDecl(attribs.getValue("fromNode"), attribs.getValue("fromField"), attribs.getValue("toNode"), attribs.getValue("toField")); break; case SCRIPT_TAG: if(checkForSceneTag) throw new SAXException(NO_SCENE_TAG_MSG); // Force an automatic startCDATA in parser doesn't startScript(); scriptFlagStack.push(true); inScript = true; // Clear any CData garbage characterDataBuffer.setLength(0); characterDataBuffer.append('\"'); String field_name = attribs.getValue(CONTAINER_ATTR); // No container field name defined? Look one up. If that // fails, guess and put in "children" since that is the // most commonly used default. if(field_name == null) { field_name = containerFields.getProperty(localName); if(field_name == null) field_name = "children"; } if(fieldDeclDepth != 0) { if(contentHandler != null) contentHandler.startField(field_name); } fieldDeclDepth++; value = attribs.getValue(USE_ATTR); if(value != null) { if(contentHandler != null) contentHandler.useDecl(value); is_used = true; useIsCurrent = true; } else { value = attribs.getValue(DEF_ATTR); is_used = false; if(contentHandler != null) contentHandler.startNode(qName, value); } if(scriptHandler != null && !is_used) scriptHandler.startScriptDecl(); // The definite fields of a script need to be passed through. // Only pass through if you're not in a USE though. if(!is_used && contentHandler != null) { value = attribs.getValue("url"); if(value != null) { scriptUrlStack.push(true); bch.startField("url"); bch.fieldValue(value); } else { scriptUrlStack.push(false); } value = attribs.getValue("mustEvaluate"); if(value != null) { bch.startField("mustEvaluate"); bch.fieldValue(value); } value = attribs.getValue("directOutput"); if(value != null) { bch.startField("directOutput"); bch.fieldValue(value); } } break; case FIELD_VALUE_TAG: if(checkForSceneTag) throw new SAXException(NO_SCENE_TAG_MSG); if(contentHandler != null) { String DEFName = attribs.getValue(USE_ATTR); if (DEFName != null) { contentHandler.startField(attribs.getValue(NAME_ATTR)); contentHandler.useDecl(DEFName); } else { // String att_name; int val_attr = attribs.getIndex(VALUE_ATTR); decodeField(atts,val_attr,bch,attribs.getValue(NAME_ATTR)); } } declDepthStack.push(fieldDeclDepth); fieldDeclDepth = 0; break; default: errorReporter.errorReport("Unknown start element type " + qName, null); } inCompressorStack.push(inCompressor); skipEEStack.push(false); //System.out.println("End SE"); } /** * End the element processing. * * @param namespace The namespace for the element. Null if not used * @param name The local name of the element to create * @param qName The qualified name of the element including prefix * @throws SAXException Not thrown in this implementation */ @Override public void endElement(String namespace, String name, String qName) throws SAXException { boolean inCompressor = inCompressorStack.pop(); skipEndElement = skipEEStack.pop(); if (inCompressor && currentCompressor != null) { BinaryContentHandler bch = (BinaryContentHandler) contentHandler; currentCompressor.fillData(qName, bch); } if (!skipEndElement) super.endElement(namespace, name, qName); } // Methods implementing PrimitiveTypeContentHandler /** * Receive notification of character data as an array of boolean. * * <p>The application must not attempt to read from the array * outside of the specified range.</p> * * <p>Such notifications will occur for a Fast Infoset SAX parser * when processing data encoded using the "boolean" encoding * algorithm, see subclause 10.7<p>. * * @param b the array of boolean * @param start the start position in the array * @param length the number of boolean to read from the array * @throws org.xml.sax.SAXException any SAX exception, possibly * wrapping another exception */ @Override public void booleans(boolean [] b, int start, int length) throws SAXException { } /** * Receive notification of character data as an array of byte. * * <p>The application must not attempt to read from the array * outside of the specified range.</p> * * <p>Such notifications will occur for a Fast Infoset SAX parser * when processing data encoded using the "base64" encoding * algorithm, see subclause 10.3. * * <p>Such a notification may occur for binary data that would * normally require base 64 encoding and reported as character data * using the {@link org.xml.sax.ContentHandler#characters characters} * method <p>. * * @param b the array of byte * @param start the start position in the array * @param length the number of byte to read from the array * @throws org.xml.sax.SAXException any SAX exception, possibly * wrapping another exception */ @Override public void bytes(byte[] b, int start, int length) throws SAXException { } /** * Receive notification of character data as an array of short. * * <p>The application must not attempt to read from the array * outside of the specified range.</p> * * <p>Such notifications will occur for a Fast Infoset SAX parser * when processing data encoded using the "short" encoding * algorithm, see subclause 10.4<p>. * * @param s the array of short * @param start the start position in the array * @param length the number of short to read from the array * @throws org.xml.sax.SAXException any SAX exception, possibly * wrapping another exception */ @Override public void shorts(short[] s, int start, int length) throws SAXException { } /** * Receive notification of character data as an array of int. * * <p>The application must not attempt to read from the array * outside of the specified range.</p> * * <p>Such notifications will occur for a Fast Infoset SAX parser * when processing data encoded using the "int" encoding * algorithm, see subclause 10.5<p>. * * @param i the array of int * @param start the start position in the array * @param length the number of int to read from the array * @throws org.xml.sax.SAXException any SAX exception, possibly * wrapping another exception */ @Override public void ints(int [] i, int start, int length) throws SAXException { } /** * Receive notification of character data as an array of long. * * <p>The application must not attempt to read from the array * outside of the specified range.</p> * * <p>Such notifications will occur for a Fast Infoset SAX parser * when processing data encoded using the "long" encoding * algorithm, see subclause 10.6<p>. * * @param l the array of long * @param start the start position in the array * @param length the number of long to read from the array * @throws org.xml.sax.SAXException any SAX exception, possibly * wrapping another exception */ @Override public void longs(long [] l, int start, int length) throws SAXException { } /** * Receive notification of character data as an array of float. * * <p>The application must not attempt to read from the array * outside of the specified range.</p> * * <p>Such notifications will occur for a Fast Infoset SAX parser * when processing data encoded using the "float" encoding * algorithm, see subclause 10.8<p>. * * @param f the array of float * @param start the start position in the array * @param length the number of float to read from the array * @throws org.xml.sax.SAXException any SAX exception, possibly * wrapping another exception */ @Override public void floats(float [] f, int start, int length) throws SAXException { } /** * Receive notification of character data as an array of double. * * <p>The application must not attempt to read from the array * outside of the specified range.</p> * * <p>Such notifications will occur for a Fast Infoset SAX parser * when processing data encoded using the "double" encoding * algorithm, see subclause 10.9<p>. * * @param d the array of double * @param start the start position in the array * @param length the number of double to read from the array * @throws org.xml.sax.SAXException any SAX exception, possibly * wrapping another exception */ @Override public void doubles(double [] d, int start, int length) throws SAXException { } /** * Receive notification of character data as an two array of UUID. * * <p>The application must not attempt to read from the array * outside of the specified range.</p> * * <p>Such notifications will occur for a Fast Infoset SAX parser * when processing data encoded using the "uuid" encoding * algorithm, see subclause 10.10<p>. * * @param msblsb the array of long containing pairs of most signficant * bits and least significant bits of the UUIDs * @param start the start position in the array * @param length the number of long to read from the array. This will * be twice the number of UUIDs, which are pairs of two long values * @throws org.xml.sax.SAXException any SAX exception, possibly * wrapping another exception */ @Override public void uuids(long[] msblsb, int start, int length) throws SAXException { } /** * Receive notification of character data as an two array of UUID. * * <p>The application must not attempt to read from the array * outside of the specified range.</p> * * <p>Such notifications will occur for a Fast Infoset SAX parser * when processing data encoded using the "uuid" encoding * algorithm, see subclause 10.10<p>. * * @param msb the array of long of the most sigificant bits of * the UUIDs * @param lsb the array of long of the least sigificant bits of * the UUIDs * @param start the start position in the array * @param length the number of UUID to read from the array * @throws org.xml.sax.SAXException any SAX exception, possibly * wrapping another exception */ public void uuids(long[] msb, long[] lsb, int start, int length) throws SAXException { } // Methods from EncodingAlgorithmContentHandler /** * Receive notification of encoding algorithm data as an array * of byte. * * <p>The application must not attempt to read from the array * outside of the specified range.</p> * * <p>Such notifications will occur for a Fast Infoset SAX parser * when processing encoding algorithm data.<p> * * <p>The Parser will call the method of this interface to report each * encoding algorithm data. Parsers MUST return all contiguous * characters in a single chunk</p> * * <p>Parsers may return all contiguous bytes in a single chunk, or * they may split it into several chunks providing that the length of * each chunk is of the required length to successfully apply the * encoding algorithm to the chunk.</p> * * @param URI the URI of the encoding algorithm * @param algorithm the encoding algorithm index * @param b the array of byte * @param start the start position in the array * @param length the number of byte to read from the array * @throws org.xml.sax.SAXException any SAX exception, possibly * wrapping another exception * @see org.jvnet.fastinfoset.EncodingAlgorithmIndexes */ @Override public void octets(String URI, int algorithm, byte[] b, int start, int length) throws SAXException { } /** * Receive notification of encoding algorithm data as an object. * * <p>Such notifications will occur for a Fast Infoset SAX parser * when processing encoding algorithm data that is converted from an * array of byte to an object more suitable for processing.<p> * * @param URI the URI of the encoding algorithm * @param algorithm the encoding algorithm index * @param o the encoding algorithm object * @throws org.xml.sax.SAXException any SAX exception, possibly * wrapping another exception * @see org.jvnet.fastinfoset.EncodingAlgorithmIndexes */ @Override public void object(String URI, int algorithm, Object o) throws SAXException { } /** * Decode a field */ private void decodeField(AttributesHolder atts, int i, BinaryContentHandler bch, String att_name) { if (i < 0) { bch.startField(att_name); return; } Object type = atts.getAlgorithmData(i); if (type == null) { // No typed data, handle as a string bch.startField(att_name); bch.fieldValue(atts.getValue(i)); } else { // TODO: DODGY!!! algo != data type // Handle most correctly now, not sure about byte Object o = atts.getAlgorithmData(i); if (o instanceof float[]) { float[] f2val = (float[]) atts.getAlgorithmData(i); bch.startField(att_name); bch.fieldValue(f2val, f2val.length); return; } else if (o instanceof int[]) { int[] ival = (int[]) atts.getAlgorithmData(i); bch.startField(att_name); bch.fieldValue(ival, ival.length); return; } else if(o instanceof double[]) { double[] dval = (double[]) atts.getAlgorithmData(i); bch.startField(att_name); bch.fieldValue(dval, dval.length); bch.endField(); return; } else if (o instanceof short[]) { short[] sval = (short[]) atts.getAlgorithmData(i); int alen = sval.length; int[] i2val = new int[alen]; for(int j=0; j < alen; j++) { i2val[j] = sval[j]; } bch.startField(att_name); bch.fieldValue(i2val, i2val.length); } switch(atts.getAlgorithmIndex(i)) { /* // Still not sure how to tell single from double case EncodingAlgorithmIndexes.BOOLEAN: boolean[] fval = (boolean[]) atts.getAlgorithmData(i); bch.startField(att_name); bch.fieldValue(fval, fval.length); break; */ case EncodingAlgorithmIndexes.FLOAT: // TODO: Assume all FLOAT's are arrays float[] fval = (float[]) atts.getAlgorithmData(i); bch.startField(att_name); bch.fieldValue(fval, fval.length); break; case EncodingAlgorithmIndexes.DOUBLE: // TODO: Assume all DOUBLES's are arrays double[] dval = (double[]) atts.getAlgorithmData(i); bch.startField(att_name); bch.fieldValue(dval, dval.length); bch.endField(); break; case EncodingAlgorithmIndexes.INT: // TODO: Assume all INT's are arrays int[] ival = (int[]) atts.getAlgorithmData(i); bch.startField(att_name); bch.fieldValue(ival, ival.length); break; case EncodingAlgorithmIndexes.SHORT: // TODO: Assume all INT's are arrays short[] sval = (short[]) atts.getAlgorithmData(i); int alen = sval.length; int[] i2val = new int[alen]; for(int j=0; j < alen; j++) { i2val[j] = sval[j]; } bch.startField(att_name); bch.fieldValue(i2val, i2val.length); break; case X3DBinaryConstants.BYTE_ALGORITHM_ID: // TODO: stop hardcoding number for BYTE, reference from tables byte[] bval = (byte[]) atts.getAlgorithmData(i); int blen = bval.length; int[] i3val = new int[blen]; for(int j=0; j < blen; j++) { i3val[j] = bval[j]; } bch.startField(att_name); bch.fieldValue(i3val, i3val.length); break; case X3DBinaryConstants.DELTA_ZLIB_INT_ARRAY_ALGORITHM_ID: int[] i4val = (int[]) atts.getAlgorithmData(i); //System.out.println("FI Element reader " + att_name + " " + java.util.Arrays.toString(i4val)); bch.startField(att_name); bch.fieldValue(i4val, i4val.length); break; case X3DBinaryConstants.QUANTIZED_ZLIB_FLOAT_ARRAY_ALGORITHM_ID: case X3DBinaryConstants.QUANTIZED_ZLIB_FLOAT_ARRAY_ALGORITHM_ID2: float[] f2val = (float[]) atts.getAlgorithmData(i); bch.startField(att_name); bch.fieldValue(f2val, f2val.length); break; default: errorReporter.warningReport("Unhandled algorithm: " + atts.getAlgorithmIndex(i), null); } } } }