Esempio n. 1
0
  public void encode(Object object, QName name, ContentHandler handler)
      throws IOException, SAXException {

    // maintain a stack of (encoding,element declaration pairs)
    Stack encoded = null;

    try {
      serializer = handler;

      if (!inline) {
        serializer.startDocument();
      }

      if (namespaceAware) {
        // write out all the namespace prefix value mappings
        for (Enumeration e = namespaces.getPrefixes(); e.hasMoreElements(); ) {
          String prefix = (String) e.nextElement();
          String uri = namespaces.getURI(prefix);

          if ("xml".equals(prefix)) {
            continue;
          }
          serializer.startPrefixMapping(prefix, uri);
        }
        for (Iterator itr = schema.getQNamePrefixToNamespaceMap().entrySet().iterator();
            itr.hasNext(); ) {
          Map.Entry entry = (Map.Entry) itr.next();
          String pre = (String) entry.getKey();
          String ns = (String) entry.getValue();

          if (XSDUtil.SCHEMA_FOR_SCHEMA_URI_2001.equals(ns)) {
            continue;
          }

          serializer.startPrefixMapping(pre, ns);
          serializer.endPrefixMapping(pre);

          namespaces.declarePrefix((pre != null) ? pre : "", ns);
        }

        // ensure a default namespace prefix set
        if (namespaces.getURI("") == null) {
          namespaces.declarePrefix("", schema.getTargetNamespace());
        }
      }

      // create the document
      DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();

      try {
        doc = docFactory.newDocumentBuilder().newDocument();
      } catch (ParserConfigurationException e) {
        new IOException().initCause(e);
      }

      encoded = new Stack();

      // add the first entry
      XSDElementDeclaration root = index.getElementDeclaration(name);

      if (root == null) {
        // check for context hint, this is only used when running the encoder
        // in test mode
        QName typeDefintion =
            (QName) context.getComponentInstance("http://geotools.org/typeDefinition");

        if (typeDefintion != null) {
          XSDTypeDefinition type = index.getTypeDefinition(typeDefintion);

          if (type == null) {
            throw new NullPointerException();
          }

          // create a mock element declaration
          root = XSDFactory.eINSTANCE.createXSDElementDeclaration();
          root.setName(name.getLocalPart());
          root.setTargetNamespace(name.getNamespaceURI());
          root.setTypeDefinition(type);
        }
      }

      if (root == null) {
        String msg = "Could not find element declaration for:" + name;
        throw new IllegalArgumentException(msg);
      }

      encoded.add(new EncodingEntry(object, root, null));

      while (!encoded.isEmpty()) {
        EncodingEntry entry = (EncodingEntry) encoded.peek();

        if (entry.encoding != null) {
          // element has been started, get the next child
          if (!entry.children.isEmpty()) {
            Object[] child = (Object[]) entry.children.get(0);
            XSDElementDeclaration element = (XSDElementDeclaration) child[0];
            Iterator itr = (Iterator) child[1];

            if (itr.hasNext()) {
              Object next = itr.next();
              if (next == null) {
                logger.warning("Iterator returned null for " + element.getName());
              }

              // here we check for instanceof EncoderDelegate
              if (next instanceof EncoderDelegate) {
                // do not add entry to the stack, just delegate to encode
                try {
                  ((EncoderDelegate) next).encode(handler);
                } catch (Exception e) {
                  throw new RuntimeException(e);
                }
              } else {
                // add the next object to be encoded to the stack
                encoded.push(new EncodingEntry(next, element, entry));
              }
            } else {
              // iterator done, close it
              Object source = child[2];
              closeIterator(itr, source);

              // this child is done, remove from child list
              entry.children.remove(0);
            }
          } else {
            // no more children, finish the element
            end(entry.encoding);
            encoded.pop();

            // clean up the entry
            entry.object = null;
            entry.element = null;
            entry.encoding = null;
            entry.children = null;
            entry.parent = null;
          }
        } else {
          // start the encoding of the entry

          // first make sure the element is not abstract
          if (entry.element.isAbstract()) {
            // look for a non abstract substitute - substitution groups are subject to
            // changes over time, so we make a copy to avoid being hit with a
            // ConcurrentModificationException
            List sub = safeCopy(entry.element.getSubstitutionGroup());

            if (sub.size() > 0) {
              // match up by type
              List matches = new ArrayList();

              for (Iterator s = sub.iterator(); s.hasNext(); ) {
                XSDElementDeclaration e = (XSDElementDeclaration) s.next();

                if (e == null || e.equals(entry.element)) {
                  continue;
                }

                if (e.getName() == null) {
                  continue;
                }

                // look up hte binding
                Binding binding =
                    bindingLoader.loadBinding(
                        new QName(e.getTargetNamespace(), e.getName()), context);

                if (binding == null) {
                  // try the type
                  XSDTypeDefinition type = e.getType();

                  if (type == null || type.getName() == null) {
                    continue;
                  }

                  binding =
                      bindingLoader.loadBinding(
                          new QName(type.getTargetNamespace(), type.getName()), context);
                }

                if (binding == null) {
                  continue;
                }

                if (binding.getType() == null) {
                  logger.warning("Binding: " + binding.getTarget() + " returns null type.");
                  continue;
                }

                // match up the type
                if (binding.getType().isAssignableFrom(entry.object.getClass())) {
                  // we have a match, store as an (element,binding) tuple
                  matches.add(new Object[] {e, binding});
                }
              }

              // if one, we are gold
              if (matches.size() == 1) {
                entry.element = (XSDElementDeclaration) ((Object[]) matches.get(0))[0];
              }
              // if multiple we have a problem
              else if (matches.size() > 0) {
                if (logger.isLoggable(Level.FINE)) {
                  StringBuffer msg = new StringBuffer("Found multiple non-abstract bindings for ");
                  msg.append(entry.element.getName()).append(": ");

                  for (Iterator m = matches.iterator(); m.hasNext(); ) {
                    msg.append(m.next().getClass().getName());
                    msg.append(", ");
                  }

                  logger.fine(msg.toString());
                }

                // try sorting by the type of the binding
                Collections.sort(
                    matches,
                    new Comparator() {
                      public int compare(Object o1, Object o2) {
                        Object[] match1 = (Object[]) o1;
                        Object[] match2 = (Object[]) o2;

                        Binding b1 = (Binding) match1[1];
                        Binding b2 = (Binding) match2[1];

                        if (b1.getType() != b2.getType()) {
                          if (b2.getType().isAssignableFrom(b1.getType())) {
                            return -1;
                          }

                          if (b1.getType().isAssignableFrom(b2.getType())) {
                            return 1;
                          }
                        }

                        // use binding comparability
                        if (b1 instanceof Comparable) {
                          return ((Comparable) b1).compareTo(b2);
                        }

                        if (b2 instanceof Comparable) {
                          return -1 * ((Comparable) b2).compareTo(b1);
                        }

                        return 0;
                      }
                    });
              }

              if (matches.size() > 0) {
                entry.element = (XSDElementDeclaration) ((Object[]) matches.get(0))[0];
              }

              // if zero, just use the abstract element
            }
          }

          if (entry.element.isAbstract()) {
            logger.fine(entry.element.getName() + " is abstract");
          }

          entry.encoding =
              entry.parent != null
                  ? (Element) encode(entry.object, entry.element, entry.parent.element.getType())
                  : (Element) encode(entry.object, entry.element);

          // add any more attributes
          List attributes = index.getAttributes(entry.element);

          for (Iterator itr = attributes.iterator(); itr.hasNext(); ) {
            XSDAttributeDeclaration attribute = (XSDAttributeDeclaration) itr.next();

            // do not encode the attribute if it has already been
            // encoded by the parent
            String ns = attribute.getTargetNamespace();
            String local = attribute.getName();

            if ((entry.encoding.getAttributeNS(ns, local) != null)
                && !"".equals(entry.encoding.getAttributeNS(ns, local))) {
              continue;
            }

            // get the object(s) for this attribute
            GetPropertyExecutor executor = new GetPropertyExecutor(entry.object, attribute);

            BindingVisitorDispatch.walk(object, bindingWalker, entry.element, executor, context);

            if (executor.getChildObject() != null) {
              // encode the attribute
              Attr attr = (Attr) encode(executor.getChildObject(), attribute);

              if (attr != null) {
                entry.encoding.setAttributeNodeNS(attr);
              }
            }
          }

          // write out the leading edge of the element
          if (schemaLocations != null) {
            // root element, add schema locations if set
            if (!schemaLocations.isEmpty()) {
              // declare the schema instance mapping
              serializer.startPrefixMapping("xsi", XSDUtil.SCHEMA_INSTANCE_URI_2001);
              serializer.endPrefixMapping("xsi");
              namespaces.declarePrefix("xsi", XSDUtil.SCHEMA_INSTANCE_URI_2001);

              StringBuffer schemaLocation = new StringBuffer();

              for (Iterator e = schemaLocations.entrySet().iterator(); e.hasNext(); ) {
                Map.Entry tuple = (Map.Entry) e.next();
                String namespaceURI = (String) tuple.getKey();
                String location = (String) tuple.getValue();

                schemaLocation.append(namespaceURI + " " + location);

                if (e.hasNext()) {
                  schemaLocation.append(" ");
                }
              }

              entry.encoding.setAttributeNS(
                  XSDUtil.SCHEMA_INSTANCE_URI_2001,
                  "xsi:schemaLocation",
                  schemaLocation.toString());
            }

            schemaLocations = null;
          }

          start(entry.encoding, entry.element);

          // TODO: this method of getting at properties wont maintain order very well, need
          // to come up with a better system that is capable of hanlding feature types
          for (Iterator pe = propertyExtractors.iterator(); pe.hasNext(); ) {
            PropertyExtractor propertyExtractor = (PropertyExtractor) pe.next();

            if (propertyExtractor.canHandle(entry.object)) {
              List extracted = propertyExtractor.properties(entry.object, entry.element);
              O:
              for (Iterator e = extracted.iterator(); e.hasNext(); ) {
                Object[] tuple = (Object[]) e.next();
                XSDParticle particle = (XSDParticle) tuple[0];
                XSDElementDeclaration child = (XSDElementDeclaration) particle.getContent();

                // check for a comment
                if ((child != null)
                    && (COMMENT.getNamespaceURI().equals(child.getTargetNamespace()))
                    && COMMENT.getLocalPart().equals(child.getName())) {
                  comment(child.getElement());

                  continue;
                }

                if (child.isElementDeclarationReference()) {
                  child = child.getResolvedElementDeclaration();
                }

                // do not encode the element if the parent has already
                // been encoded by the parent
                String ns = child.getTargetNamespace();
                String local = child.getName();

                for (int i = 0; i < entry.encoding.getChildNodes().getLength(); i++) {
                  Node node = entry.encoding.getChildNodes().item(i);

                  if (node instanceof Element) {
                    if (ns != null) {
                      if (ns.equals(node.getNamespaceURI()) && local.equals(node.getLocalName())) {
                        continue O;
                      }
                    } else {
                      if (local.equals(node.getLocalName())) {
                        continue O;
                      }
                    }
                  }
                }

                Object obj = tuple[1];

                if (obj == null) {
                  if (particle.getMinOccurs() == 0) {
                    // cool
                  } else {
                    // log an error
                    logger.fine("Property " + ns + ":" + local + " not found but minoccurs > 0 ");
                  }

                  // skip this regardless
                  continue;
                }

                // figure out the maximum number of occurences
                int maxOccurs = 1;

                if (particle.isSetMaxOccurs()) {
                  maxOccurs = particle.getMaxOccurs();
                } else {
                  // look the containing group
                  if (particle.eContainer() instanceof XSDModelGroup) {
                    XSDModelGroup group = (XSDModelGroup) particle.eContainer();

                    if (group.eContainer() instanceof XSDParticle) {
                      XSDParticle cParticle = (XSDParticle) group.eContainer();

                      if (cParticle.isSetMaxOccurs()) {
                        maxOccurs = cParticle.getMaxOccurs();
                      }
                    }
                  }
                }

                if ((maxOccurs == -1) || (maxOccurs > 1)) {
                  // may have a collection or array, unwrap it
                  Iterator iterator = null;

                  if (obj instanceof Iterator) {
                    iterator = (Iterator) obj;
                  } else if (obj.getClass().isArray()) {
                    Object[] array = (Object[]) obj;
                    iterator = Arrays.asList(array).iterator();
                  } else if (obj instanceof Collection) {
                    Collection collection = (Collection) obj;
                    iterator = collection.iterator();
                  } else if (obj instanceof FeatureCollection) {
                    FeatureCollection collection = (FeatureCollection) obj;
                    iterator = collection.iterator();
                  } else {
                    iterator = new SingleIterator(obj);
                  }

                  entry.children.add(new Object[] {child, iterator, obj});
                } else {
                  // only one, just add the object
                  entry.children.add(new Object[] {child, new SingleIterator(obj), obj});
                }
              }
            }
          }
        }
      }

      if (!inline) {
        serializer.endDocument();
      }

    } finally {
      // cleanup
      index.destroy();

      // close any iterators still present in the stack, this will only occur in an exception
      // case
      if (encoded != null) {
        while (!encoded.isEmpty()) {
          EncodingEntry entry = (EncodingEntry) encoded.pop();
          if (!entry.children.isEmpty()) {
            Object[] child = (Object[]) entry.children.get(0);
            Iterator itr = (Iterator) child[1];
            try {
              closeIterator(itr, child[2]);
            } catch (Exception e) {
              // ignore, we are already in an error case.
            }
          }
        }
      }
      // TODO: there are probably other refences to elements of XSDScheam objects, we should
      // kill them too
    }
  }