}
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 = rootElementType;
if (typeDefintion == null) {
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();
//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, entry.element);
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 the value is null, can we skip it? Or do we have to go out
// with an non empty element with xs:nil?
if (obj == null) {
if (particle.getMinOccurs() == 0) {
// just skip it
continue;
} else if(!child.isNillable()){
// log an error and skip the element, but we're encoding
// something invalid
logger.fine("Property " + ns + ":" + local
+ " not found but minoccurs > 0 ");
//skip this regardless