/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.xml.impl;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.eclipse.xsd.XSDElementDeclaration;
import org.geotools.util.Converters;
import org.geotools.xml.Binding;
import org.geotools.xml.ComplexBinding;
import org.geotools.xml.SimpleBinding;
import org.opengis.feature.ComplexAttribute;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.Text;
public class ElementEncodeExecutor implements BindingWalker.Visitor {
/** the object being encoded **/
Object object;
/** the element being encoded **/
XSDElementDeclaration element;
/** the encoded value **/
Element encoding;
/** the document / factory **/
Document document;
/** logger */
Logger logger;
public ElementEncodeExecutor(Object object, XSDElementDeclaration element, Document document,
Logger logger) {
this.object = object;
this.element = element;
this.document = document;
this.logger = logger;
// if ( element.getTargetNamespace() != null ) {
encoding = document.createElementNS(element.getTargetNamespace(), element.getName());
// }
// else {
// encoding = document.createElementNS(
// element.getSchema().getTargetNamespace(), element.getName()
// );
// }
}
public Element getEncodedElement() {
return encoding;
}
public void visit(Binding binding) {
if( object == null ) {
throw new RuntimeException( "Unable to encode " + element.getName() + ", value is null.");
}
//ensure that the type of the object being encoded matches the type
// of the binding
if (binding.getType() == null) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Binding: " + binding.getTarget() + " does not declare a target type");
}
return;
}
/*
* Notes on the nasty ComplexAttribute instanceof check, based on discussion in GEOT-2474.
*
* ElementEncodeExecutor does too much: it calls the encode method of a binding, but also
* provides automatic conversion services for simple features. This causes encoding of any
* complexType with simpleContent of xs:string to fail if the input data is a GeoAPI object.
* ElementEncodeExecutor was written to support encoding of, for example, CodeType from a
* String. However, CodeType is most fully represented by a ComplexAttribute (it can have a
* codeSpace attribute). The instanceof check retains the old behaviour so
* ElementEncodeExecutor can still automagically convert simple features into XML. The
* instanceof is necessary because ElementEncodeExecutor will otherwise use
* Converters.convert to turn any ComplexAttribute bound to a String into a String. Because
* every Java object has a toString method, this will always "succeed", and garbage will be
* encoded. This breaks XML well-formedness, not to mention being generally useless.
*/
if (!(object instanceof ComplexAttribute)
&& !binding.getType().isAssignableFrom(object.getClass())) {
//try to convert
Object converted = Converters.convert(object, binding.getType());
if (converted != null) {
object = converted;
} else {
if (logger.isLoggable(Level.FINE)) {
// do not log the object, may be a multi-megabyte feature collection
// that can trigger an OOM toStringing itself
logger.fine("[ " + object.getClass() + " ] is not of type " + binding.getType());
}
return;
}
}
if (binding instanceof ComplexBinding) {
ComplexBinding complex = (ComplexBinding) binding;
try {
Element element = complex.encode(object, document, encoding);
if (element != null) {
encoding = element;
}
} catch (Throwable t) {
String msg = "Encode failed for " + element.getName() + ". Cause: "
+ t.getLocalizedMessage();
throw new RuntimeException(msg, t);
}
} else {
SimpleBinding simple = (SimpleBinding) binding;
//figure out if the node has any text
Text text = null;
for (int i = 0; i < encoding.getChildNodes().getLength(); i++) {
Node node = (Node) encoding.getChildNodes().item(i);
if (node instanceof Text) {
text = (Text) node;
break;
}
}
try {
String value = simple.encode(object, (text != null) ? text.getData() : null);
if (value != null) {
//set the text of the node
if (text == null) {
text = document.createTextNode(value);
encoding.appendChild(text);
} else {
text.setData(value);
}
}
} catch (Throwable t) {
String msg = "Encode failed for " + element.getName() + ". Cause: "
+ t.getLocalizedMessage();
throw new RuntimeException(msg, t);
}
}
}
}