001: package org.geotools.xml;
002:
003: import java.io.IOException;
004: import java.io.OutputStream;
005: import java.util.ArrayList;
006: import java.util.Arrays;
007: import java.util.Collection;
008: import java.util.Collections;
009: import java.util.Comparator;
010: import java.util.HashMap;
011: import java.util.Iterator;
012: import java.util.List;
013: import java.util.Map;
014: import java.util.Stack;
015: import java.util.logging.Logger;
016:
017: import javax.xml.namespace.QName;
018: import javax.xml.parsers.DocumentBuilderFactory;
019: import javax.xml.parsers.ParserConfigurationException;
020:
021: import org.apache.xml.serialize.OutputFormat;
022: import org.apache.xml.serialize.XMLSerializer;
023: import org.eclipse.xsd.XSDAttributeDeclaration;
024: import org.eclipse.xsd.XSDElementDeclaration;
025: import org.eclipse.xsd.XSDFactory;
026: import org.eclipse.xsd.XSDModelGroup;
027: import org.eclipse.xsd.XSDNamedComponent;
028: import org.eclipse.xsd.XSDParticle;
029: import org.eclipse.xsd.XSDSchema;
030: import org.eclipse.xsd.XSDTypeDefinition;
031: import org.eclipse.xsd.util.XSDUtil;
032: import org.geotools.xml.impl.AttributeEncodeExecutor;
033: import org.geotools.xml.impl.BindingFactoryImpl;
034: import org.geotools.xml.impl.BindingLoader;
035: import org.geotools.xml.impl.BindingPropertyExtractor;
036: import org.geotools.xml.impl.BindingWalker;
037: import org.geotools.xml.impl.BindingWalkerFactoryImpl;
038: import org.geotools.xml.impl.ElementEncoder;
039: import org.geotools.xml.impl.GetPropertyExecutor;
040: import org.geotools.xml.impl.NamespaceSupportWrapper;
041: import org.geotools.xml.impl.SchemaIndexImpl;
042: import org.picocontainer.MutablePicoContainer;
043: import org.picocontainer.defaults.DefaultPicoContainer;
044: import org.w3c.dom.Attr;
045: import org.w3c.dom.Document;
046: import org.w3c.dom.Element;
047: import org.w3c.dom.NamedNodeMap;
048: import org.w3c.dom.Node;
049: import org.w3c.dom.Text;
050: import org.xml.sax.Attributes;
051: import org.xml.sax.ContentHandler;
052: import org.xml.sax.SAXException;
053: import org.xml.sax.helpers.NamespaceSupport;
054:
055: /**
056: * Encodes objects as xml based on a schema.
057: * <p>
058: * The function of the encoder is to traverse a tree of objects seializing them
059: * out as xml as it goes. Navigation and serialization of the tree is performed by
060: * instances of {@link org.geotools.xml.Binding} which are bound to types in the
061: * schema.
062: * </p>
063: * <br>
064: * <p>
065: * To execute the encoder, one must have 3 bits of information:
066: * <ol>
067: * <li>The root object in the tree to be encoded
068: * <li>The schema / configuration of the intsance document being encoded.
069: * <li>A name of the element defined in the schema which corresponds to the
070: * root object in the tree.
071: * </ol>
072: * </p>
073: * <br>
074: * <p>
075: * As an exmaple, consider the encoding of a {@link org.opengis.filter.Filter}
076: * instance.
077: * <pre>
078: * <code>
079: * //instantiate hte configuration for the filter schmea
080: * Configuration configuration = new OGCConfiguration();
081: *
082: * //create the encoder
083: * Encoder encoder = new Encoder( configuration );
084: *
085: * //get a filter
086: * Filter filter = ...;
087: *
088: * //get the name of the 'filter' element in the schema
089: * QName name = new QName( "http://www.opengis.net/ogc", "Filter" );
090: *
091: * //encode
092: * encoder.encode( filter, name );
093: * </code>
094: * </pre>
095: * </p>
096: * @author Justin Deoliveira, The Open Planning Project
097: *
098: */
099: public class Encoder {
100:
101: /** the schema + index **/
102: private XSDSchema schema;
103: private SchemaIndex index;
104:
105: /** binding factory + context **/
106: private BindingLoader bindingLoader;
107: private MutablePicoContainer context;
108:
109: /** binding walker */
110: private BindingWalker bindingWalker;
111:
112: /** property extractors */
113: private List propertyExtractors;
114:
115: /** element encoder */
116: private ElementEncoder encoder;
117:
118: /** factory for creating nodes **/
119: private Document doc;
120: /** namespaces */
121: private NamespaceSupport namespaces;
122:
123: /** document serializer **/
124: private ContentHandler serializer;
125:
126: /** schema location */
127: private HashMap schemaLocations;
128:
129: /** output format */
130: private OutputFormat outputFormat;
131:
132: /** true if we are encoding a full document */
133: private boolean encodeFullDocument = true;
134:
135: /** namespace aware */
136: private boolean namespaceAware = true;
137:
138: /**
139: * Logger logger;
140: */
141: private Logger logger;
142:
143: /**
144: * Creates an encoder from a configuration.
145: * <p>
146: * This constructor calls through to {@link #Encoder(Configuration, XSDSchema)}
147: * obtaining the schema instance from {@link Configuration#schema()}.
148: * </p>
149: * @param configuration The encoder configuration.
150: */
151: public Encoder(Configuration configuration) {
152: this (configuration, configuration.schema());
153: }
154:
155: /**
156: * Creates an encoder from a configuration and a specific schema
157: * instance.
158: *
159: * @param configuration The encoder configuration.
160: * @param schema The schema instance.
161: */
162: public Encoder(Configuration configuration, XSDSchema schema) {
163: this .schema = schema;
164:
165: index = new SchemaIndexImpl(new XSDSchema[] { schema });
166:
167: bindingLoader = new BindingLoader();
168: bindingLoader.setContainer(configuration
169: .setupBindings(bindingLoader.getContainer()));
170:
171: bindingWalker = new BindingWalker(bindingLoader);
172:
173: //create the context
174: context = new DefaultPicoContainer();
175:
176: //register hte binding factory in the context
177: BindingFactory bindingFactory = new BindingFactoryImpl(
178: bindingLoader);
179: context.registerComponentInstance(bindingFactory);
180:
181: //register the element encoder in the context
182: encoder = new ElementEncoder(bindingWalker, context);
183: context.registerComponentInstance(encoder);
184:
185: //register the schema index
186: context.registerComponentInstance(index);
187:
188: //bindign walker support
189: context.registerComponentInstance(new BindingWalkerFactoryImpl(
190: bindingLoader, context));
191:
192: //pass the context off to the configuration
193: context = configuration.setupContext(context);
194: encoder.setContext(context);
195:
196: //schema location setup
197: schemaLocations = new HashMap();
198:
199: // get a logger from the context
200: logger = (Logger) context
201: .getComponentInstanceOfType(Logger.class);
202:
203: if (logger == null) {
204: //create a default
205: logger = org.geotools.util.logging.Logging
206: .getLogger("org.geotools.xml");
207: context.registerComponentInstance(logger);
208: }
209:
210: encoder.setLogger(logger);
211:
212: //namespaces
213: namespaces = new NamespaceSupport();
214: context.registerComponentInstance(namespaces);
215: context.registerComponentInstance(new NamespaceSupportWrapper(
216: namespaces));
217:
218: //property extractors
219: propertyExtractors = Schemas.getComponentInstancesOfType(
220: context, PropertyExtractor.class);
221:
222: //add the property extractor for bindings as first
223: propertyExtractors.add(0, new BindingPropertyExtractor(this ,
224: context));
225: }
226:
227: /**
228: * Sets wether the encoder should be namespace aware.
229: * <p>
230: * Warning that setting this to <code>false</code> will result in no
231: * namespace prefixes on encoded elements and attributes, and no schema
232: * declarations on the root element.document;
233: * </p>
234: * @param namespaces
235: */
236: public void setNamespaceAware(boolean namespaceAware) {
237: this .namespaceAware = namespaceAware;
238: }
239:
240: public void setEncodeFullDocument(boolean encodeFullDocument) {
241: this .encodeFullDocument = encodeFullDocument;
242: }
243:
244: /**
245: * Sets the schema location for a particular namespace uri.
246: * <p>
247: * Registering a schema location will include it on the "schemaLocation" attribute of the
248: * root element of the encoding.
249: * </p>
250: * @param namespaceURI A namespace uri.
251: * @param location A schema location.
252: *
253: */
254: public void setSchemaLocation(String namespaceURI, String location) {
255: schemaLocations.put(namespaceURI, location);
256: }
257:
258: /**
259: * Sets hte output format to be used by the encoder.
260: *
261: * @param outputFormat The output format.
262: */
263: public void setOutputFormat(OutputFormat outputFormat) {
264: this .outputFormat = outputFormat;
265: }
266:
267: /**
268: * @return The walker used to traverse bindings, this method is for internal use only.
269: */
270: public BindingWalker getBindingWalker() {
271: return bindingWalker;
272: }
273:
274: /**
275: * @return The index of schema components, this method is for internal use only.
276: */
277: public SchemaIndex getSchemaIndex() {
278: return index;
279: }
280:
281: /**
282: * @return the schema.
283: */
284: public XSDSchema getSchema() {
285: return schema;
286: }
287:
288: /**
289: * @deprecated use {@link #encode(Object, QName, OutputStream)}.
290: *
291: */
292: public void write(Object object, QName name, OutputStream out)
293: throws IOException, SAXException {
294: encode(object, name, out);
295: }
296:
297: /**
298: * Encodes an object.
299: * <p>
300: * An object is encoded as an object, name pair, where the name is the name
301: * of an element declaration in a schema.
302: * </p>
303: * @param object The object being encoded.
304: * @param name The name of the element being encoded in the schema.
305: * @param out The output stream.
306: *
307: * @throws IOException
308: * @throws SAXException
309: */
310: public void encode(Object object, QName name, OutputStream out)
311: throws IOException, SAXException {
312:
313: //create the document seriaizer
314: XMLSerializer xmls = null;
315: if (outputFormat != null) {
316: xmls = new XMLSerializer(out, outputFormat);
317: } else {
318: xmls = new XMLSerializer(out, new OutputFormat());
319: }
320:
321: xmls.setNamespaces(namespaceAware);
322: encode(object, name, xmls);
323: }
324:
325: /**
326: * Encodes an object.
327: * <p>
328: * An object is encoded as an object, name pair, where the name is the name
329: * of an element declaration in a schema.
330: * </p>
331: * @param object The object being encoded.
332: * @param name The name of the element being encoded in the schema.
333: * @param handler
334: * @param true if this encode run is trying to encode a full xml document, false if otherwise
335: * the generated xml is part of a bigger xml file
336: *
337: * @throws IOException
338: * @throws SAXException
339: */
340: public void encode(Object object, QName name, ContentHandler handler)
341: throws IOException, SAXException {
342: serializer = handler;
343: if (encodeFullDocument)
344: serializer.startDocument();
345:
346: if (namespaceAware) {
347: //write out all the namespace prefix value mappings
348: for (Iterator itr = schema.getQNamePrefixToNamespaceMap()
349: .entrySet().iterator(); itr.hasNext();) {
350:
351: Map.Entry entry = (Map.Entry) itr.next();
352: String pre = (String) entry.getKey();
353: String ns = (String) entry.getValue();
354:
355: if (XSDUtil.SCHEMA_FOR_SCHEMA_URI_2001.equals(ns))
356: continue;
357:
358: serializer.startPrefixMapping(pre, ns);
359: serializer.endPrefixMapping(pre);
360:
361: namespaces.declarePrefix(pre != null ? pre : "", ns);
362: }
363: //ensure a default namespace prefix set
364: if (namespaces.getURI("") == null) {
365: namespaces.declarePrefix("", schema
366: .getTargetNamespace());
367: }
368: }
369:
370: //create the document
371: DocumentBuilderFactory docFactory = DocumentBuilderFactory
372: .newInstance();
373: try {
374: doc = docFactory.newDocumentBuilder().newDocument();
375: } catch (ParserConfigurationException e) {
376: new IOException().initCause(e);
377: }
378: //maintain a stack of (encoding,element declaration pairs)
379: Stack encoded = new Stack();
380:
381: //add the first entry
382: XSDElementDeclaration root = index.getElementDeclaration(name);
383: encoded.add(new EncodingEntry(object, root));
384:
385: while (!encoded.isEmpty()) {
386: EncodingEntry entry = (EncodingEntry) encoded.peek();
387:
388: if (entry.encoding != null) {
389:
390: //element has been started, get the next child
391: if (!entry.children.isEmpty()) {
392: Object[] child = (Object[]) entry.children.get(0);
393: XSDElementDeclaration element = (XSDElementDeclaration) child[0];
394: Iterator itr = (Iterator) child[1];
395:
396: if (itr.hasNext()) {
397: //add the next object to be encoded to the stack
398: encoded.push(new EncodingEntry(itr.next(),
399: element));
400: } else {
401: //this child is done, remove from child list
402: entry.children.remove(0);
403: }
404: } else {
405: // no more children, finish the element
406: end(entry.encoding);
407: encoded.pop();
408: }
409:
410: } else {
411: //start the encoding of the entry
412:
413: //first make sure the element is not abstract
414: if (entry.element.isAbstract()) {
415: //look for a non abstract substitute
416: List sub = entry.element.getSubstitutionGroup();
417: if (sub.size() > 0) {
418: //match up by type
419: List matches = new ArrayList();
420: for (Iterator s = sub.iterator(); s.hasNext();) {
421: XSDElementDeclaration e = (XSDElementDeclaration) s
422: .next();
423: if (e.equals(entry.element))
424: continue;
425:
426: if (e.getName() == null)
427: continue;
428:
429: //look up hte binding
430: Binding binding = bindingLoader
431: .loadBinding(new QName(e
432: .getTargetNamespace(), e
433: .getName()), context);
434: if (binding == null) {
435: //try the type
436: XSDTypeDefinition type = e.getType();
437: if (type.getName() == null)
438: continue;
439:
440: binding = bindingLoader.loadBinding(
441: new QName(type
442: .getTargetNamespace(),
443: type.getName()),
444: context);
445: }
446:
447: if (binding == null)
448: continue;
449:
450: if (binding.getType() == null)
451: continue;
452:
453: //match up the type
454: if (binding.getType().isAssignableFrom(
455: entry.object.getClass())) {
456: //we have a match, store as an (element,binding) tuple
457: matches
458: .add(new Object[] { e, binding });
459: }
460: }
461:
462: //if one, we are gold
463: if (matches.size() > 1) {
464: //try sorting by the type of the binding
465: Collections.sort(matches, new Comparator() {
466:
467: public int compare(Object o1, Object o2) {
468: Object[] match1 = (Object[]) o1;
469: Object[] match2 = (Object[]) o2;
470:
471: Binding b1 = (Binding) match1[1];
472: Binding b2 = (Binding) match2[1];
473:
474: if (b2.getType().isAssignableFrom(
475: b1.getType())) {
476: return -1;
477: }
478: if (b1.getType().isAssignableFrom(
479: b2.getType())) {
480: return 1;
481: }
482:
483: // in this case use the bindings comparability
484: // required by the javadocs
485: if (!(b1 instanceof Comparable))
486: throw new RuntimeException(
487: b1.getClass()
488: + " does not implement comparable, "
489: + "but another binding, "
490: + b2.getClass()
491: + " is associated to the same type");
492:
493: if (!(b2 instanceof Comparable))
494: throw new RuntimeException(
495: b2.getClass()
496: + " does not implement comparable, "
497: + "but another binding, "
498: + b1.getClass()
499: + " is associated to the same type");
500:
501: return ((Comparable) b1)
502: .compareTo(b2);
503: }
504:
505: });
506: }
507: if (matches.size() > 0)
508: entry.element = (XSDElementDeclaration) ((Object[]) matches
509: .get(0))[0];
510:
511: //if zero, just use the absttract element
512: }
513: }
514:
515: if (entry.element.isAbstract()) {
516: logger.fine(entry.element.getName()
517: + " is abstract");
518: }
519:
520: entry.encoding = (Element) encode(entry.object,
521: entry.element);
522:
523: //add any more attributes
524: List attributes = index.getAttributes(entry.element);
525: for (Iterator itr = attributes.iterator(); itr
526: .hasNext();) {
527: XSDAttributeDeclaration attribute = (XSDAttributeDeclaration) itr
528: .next();
529:
530: //do not encode the attribute if it has already been
531: // encoded by the parent
532: String ns = attribute.getTargetNamespace();
533: String local = attribute.getName();
534: if (entry.encoding.getAttributeNS(ns, local) != null
535: && !"".equals(entry.encoding
536: .getAttributeNS(ns, local)))
537: continue;
538:
539: //get the object(s) for this attribute
540: GetPropertyExecutor executor = new GetPropertyExecutor(
541: entry.object, attribute);
542:
543: bindingWalker
544: .walk(entry.element, executor, context);
545:
546: if (executor.getChildObject() != null) {
547: //encode the attribute
548: Attr attr = (Attr) encode(executor
549: .getChildObject(), attribute);
550: if (attr != null) {
551: entry.encoding.setAttributeNodeNS(attr);
552: }
553: }
554: }
555:
556: //write out the leading edge of the element
557: if (schemaLocations != null) {
558: //root element, add schema locations if set
559: if (!schemaLocations.isEmpty()) {
560: //declare the schema instance mapping
561: serializer.startPrefixMapping("xsi",
562: XSDUtil.SCHEMA_INSTANCE_URI_2001);
563: serializer.endPrefixMapping("xsi");
564: namespaces.declarePrefix("xsi",
565: XSDUtil.SCHEMA_INSTANCE_URI_2001);
566:
567: StringBuffer schemaLocation = new StringBuffer();
568: for (Iterator e = schemaLocations.entrySet()
569: .iterator(); e.hasNext();) {
570: Map.Entry tuple = (Map.Entry) e.next();
571: String namespaceURI = (String) tuple
572: .getKey();
573: String location = (String) tuple.getValue();
574:
575: schemaLocation.append(namespaceURI + " "
576: + location);
577: if (e.hasNext()) {
578: schemaLocation.append(" ");
579: }
580: }
581:
582: entry.encoding.setAttributeNS(
583: XSDUtil.SCHEMA_INSTANCE_URI_2001,
584: "xsi:schemaLocation", schemaLocation
585: .toString());
586: }
587:
588: schemaLocations = null;
589: }
590:
591: start(entry.encoding);
592:
593: //TODO: this method of getting at properties wont maintain order very well, need
594: // to come up with a better system that is capable of hanlding feature types
595: for (Iterator pe = propertyExtractors.iterator(); pe
596: .hasNext();) {
597: PropertyExtractor propertyExtractor = (PropertyExtractor) pe
598: .next();
599: if (propertyExtractor.canHandle(entry.object)) {
600: List extracted = propertyExtractor.properties(
601: entry.object, entry.element);
602: O: for (Iterator e = extracted.iterator(); e
603: .hasNext();) {
604: Object[] tuple = (Object[]) e.next();
605: XSDParticle particle = (XSDParticle) tuple[0];
606: XSDElementDeclaration child = (XSDElementDeclaration) particle
607: .getContent();
608: if (child.isElementDeclarationReference()) {
609: child = child
610: .getResolvedElementDeclaration();
611: }
612:
613: //do not encode the element if the parent has already
614: // been encoded by the parent
615: String ns = child.getTargetNamespace();
616: String local = child.getName();
617: for (int i = 0; i < entry.encoding
618: .getChildNodes().getLength(); i++) {
619: Node node = entry.encoding
620: .getChildNodes().item(i);
621: if (node instanceof Element) {
622: if (ns != null) {
623: if (ns.equals(node
624: .getNamespaceURI())
625: && local
626: .equals(node
627: .getLocalName())) {
628: continue O;
629: }
630: } else {
631: if (local.equals(node
632: .getLocalName()))
633: continue O;
634: }
635: }
636: }
637:
638: Object obj = tuple[1];
639:
640: if (obj == null) {
641: if (particle.getMinOccurs() == 0) {
642: //cool
643: } else {
644: //log an error
645: logger
646: .fine("Property "
647: + ns
648: + ":"
649: + local
650: + " not found but minoccurs > 0 ");
651: }
652:
653: //skip this regardless
654: continue;
655: }
656:
657: //figure out the maximum number of occurences
658: int maxOccurs = 1;
659: if (particle.isSetMaxOccurs()) {
660: maxOccurs = particle.getMaxOccurs();
661: } else {
662: //look the containing group
663: if (particle.eContainer() instanceof XSDModelGroup) {
664: XSDModelGroup group = (XSDModelGroup) particle
665: .eContainer();
666: if (group.eContainer() instanceof XSDParticle) {
667: XSDParticle cParticle = (XSDParticle) group
668: .eContainer();
669: if (cParticle.isSetMaxOccurs()) {
670: maxOccurs = cParticle
671: .getMaxOccurs();
672: }
673: }
674: }
675: }
676:
677: if (maxOccurs == -1 || maxOccurs > 1) {
678: //may have a collection or array, unwrap it
679: Iterator iterator = null;
680: if (obj instanceof Iterator) {
681: iterator = (Iterator) obj;
682: } else if (obj.getClass().isArray()) {
683: Object[] array = (Object[]) obj;
684: iterator = Arrays.asList(array)
685: .iterator();
686: } else if (obj instanceof Collection) {
687: Collection collection = (Collection) obj;
688: iterator = collection.iterator();
689: } else {
690: iterator = new SingleIterator(obj);
691: }
692:
693: entry.children.add(new Object[] {
694: child, iterator });
695: } else {
696: //only one, just add the object
697: entry.children
698: .add(new Object[] { child,
699: new SingleIterator(obj) });
700: }
701: }
702: }
703: }
704:
705: }
706: }
707:
708: if (encodeFullDocument)
709: serializer.endDocument();
710: }
711:
712: protected Node encode(Object object, XSDNamedComponent component) {
713:
714: if (component instanceof XSDElementDeclaration) {
715: XSDElementDeclaration element = (XSDElementDeclaration) component;
716:
717: return encoder.encode(object, element, doc);
718: } else if (component instanceof XSDAttributeDeclaration) {
719: XSDAttributeDeclaration attribute = (XSDAttributeDeclaration) component;
720:
721: return encoder.encode(object, attribute, doc);
722: }
723:
724: return null;
725: }
726:
727: protected void start(Element element) throws SAXException {
728:
729: String uri = element.getNamespaceURI();
730: String local = element.getLocalName();
731:
732: String qName = element.getLocalName();
733:
734: NamespaceSupport namespaces = this .namespaces;
735:
736: if (namespaceAware) {
737: uri = (uri != null) ? uri : namespaces.getURI("");
738: qName = namespaces.getPrefix(uri) + ":" + qName;
739: } else {
740: uri = "";
741: namespaces = null;
742: }
743:
744: DOMAttributes atts = new DOMAttributes(element.getAttributes(),
745: namespaces);
746: serializer.startElement(uri, local, qName, atts);
747:
748: //write out any text
749: for (int i = 0; i < element.getChildNodes().getLength(); i++) {
750: Node node = (Node) element.getChildNodes().item(i);
751: if (node instanceof Text) {
752: char[] ch = ((Text) node).getData().toCharArray();
753: serializer.characters(ch, 0, ch.length);
754: }
755: }
756:
757: //write out any child elements
758: for (int i = 0; i < element.getChildNodes().getLength(); i++) {
759: Node node = (Node) element.getChildNodes().item(i);
760: if (node instanceof Element) {
761: start((Element) node);
762: end((Element) node);
763: }
764: }
765:
766: if (namespaceAware) {
767: //push a new context for children, declaring the default prefix to be the one of this
768: // element
769: namespaces.pushContext();
770: if (uri != null) {
771: namespaces.declarePrefix("", uri);
772: }
773: }
774: }
775:
776: protected void end(Element element) throws SAXException {
777: //push off last context
778: if (namespaceAware)
779: namespaces.popContext();
780:
781: String uri = element.getNamespaceURI();
782: String local = element.getLocalName();
783:
784: String qName = element.getLocalName();
785: if (element.getPrefix() != null
786: && !"".equals(element.getPrefix()))
787: qName = element.getPrefix() + ":" + qName;
788:
789: serializer.endElement(uri, local, qName);
790: }
791:
792: private static class NullIterator implements Iterator {
793:
794: public void remove() {
795: // do nothing
796: }
797:
798: public boolean hasNext() {
799: return false;
800: }
801:
802: public Object next() {
803: // TODO Auto-generated method stub
804: return null;
805: }
806: }
807:
808: private static class SingleIterator implements Iterator {
809: Object object;
810: boolean more;
811:
812: public SingleIterator(Object object) {
813: this .object = object;
814: more = true;
815: }
816:
817: public void remove() {
818: //unsupported
819: }
820:
821: public boolean hasNext() {
822: return more;
823: }
824:
825: public Object next() {
826: more = false;
827: return object;
828: }
829: }
830:
831: /**
832: * Encoding stack entries.
833: */
834: private static class EncodingEntry {
835:
836: public Object object;
837:
838: public XSDElementDeclaration element;
839: public Element encoding;
840:
841: public List children; //list of (element,iterator) tuples
842:
843: public EncodingEntry(Object object,
844: XSDElementDeclaration element) {
845: this .object = object;
846: this .element = element;
847:
848: children = new ArrayList();
849: }
850:
851: }
852:
853: private static class DOMAttributes implements Attributes {
854:
855: NamedNodeMap atts;
856: NamespaceSupport namespaces;
857:
858: public DOMAttributes(NamedNodeMap atts,
859: NamespaceSupport namespaces) {
860: this .atts = atts;
861: this .namespaces = namespaces;
862: }
863:
864: public int getLength() {
865: return atts.getLength();
866: }
867:
868: public String getLocalName(int index) {
869: return atts.item(index).getLocalName();
870: }
871:
872: public String getQName(int index) {
873: Node n = atts.item(index);
874:
875: if (namespaces != null) {
876: String uri = n.getNamespaceURI();
877: String prefix = uri != null ? namespaces.getPrefix(uri)
878: : null;
879:
880: if (prefix != null) {
881: return prefix + ":" + n.getLocalName();
882: }
883: }
884:
885: return n.getLocalName();
886: }
887:
888: public String getType(int index) {
889: return "CDATA"; //TODO: this properly
890: }
891:
892: public String getURI(int index) {
893: return atts.item(index).getNamespaceURI();
894: }
895:
896: public String getValue(int index) {
897: return atts.item(index).getNodeValue();
898: }
899:
900: public int getIndex(String qName) {
901: String pre = null;
902: String local = null;
903:
904: if (qName.lastIndexOf(':') != -1) {
905: String[] split = qName.split(":");
906: pre = split[0];
907: local = split[1];
908: } else {
909: pre = "";
910: local = qName;
911: }
912:
913: for (int i = 0; i < atts.getLength(); i++) {
914: Node att = (Node) atts.item(i);
915: if (att.getLocalName().equals(local)) {
916: String apre = att.getPrefix();
917: if (apre == null)
918: apre = "";
919:
920: if (pre.equals(apre))
921: return i;
922: }
923: }
924:
925: return -1;
926: }
927:
928: public String getType(String qName) {
929: return getType(getIndex(qName));
930: }
931:
932: public String getValue(String qName) {
933: return getValue(getIndex(qName));
934: }
935:
936: public int getIndex(String uri, String localName) {
937: if (uri == null || uri.equals(""))
938: return getIndex(localName);
939:
940: return getIndex(uri + ":" + localName);
941: }
942:
943: public String getType(String uri, String localName) {
944: return getType(getIndex(uri, localName));
945: }
946:
947: public String getValue(String uri, String localName) {
948: return getValue(getIndex(uri, localName));
949: }
950:
951: }
952: }
|