/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.xmlcode;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import javax.xml.parsers.ParserConfigurationException;
import org.jdom2.Attribute;
import org.jdom2.Content;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.Text;
import org.jdom2.filter.ElementFilter;
import org.jdom2.input.SAXBuilder;
import org.xml.sax.SAXException;
/**
* <p>
* Utility class providing XML decoding facilities
* </p>
* This class allow you to decode object to an XML string or streams according to a mapping you define externaly (see {@link XMLMapping}).
* Objects are automatically instancied from XML strings or streams.<br>
* Supposing you have your data in an object myData (a string or a stream object), decoding process is as simpler as this:
*
* <pre>
* XMLMapping myMapping = new XMLMapping(modelFile);
*
* MyClass myObject = (MyClass) XMLDecoder.decodeObjectWithMapping(myData, myMapping);
* </pre>
*
* or directly by specifying model file:
*
* <pre>
* MyClass myObject = (MyClass) XMLDecoder.decodeObjectWithMappingFile(myData, modelFile);
* </pre>
*
* where <code>myData</code> is either a <code>String</code> or an <code>mInputStream</code> and <br>
* In this example, a new instance of <code>MyClass</code> class is instancied and automatically sets with values and newly created
* instances of others sub-classes.
*
* @author <a href="mailto:Sylvain.Guerin@enst-bretagne.fr">Sylvain Guerin</a>
* @see XMLCoder
* @see XMLMapping
*/
public class XMLDecoder {
/** Stores mapping that will be used for decoding */
protected XMLMapping xmlMapping;
/** Stores builder object that will be used for decoding */
protected Object builder;
/**
* Internaly used to get a unique identifier
*/
private int nextReference;
/**
* Stores already serialized objects where value is the serialized object and key is a
*
* <pre>
* Integer
* </pre>
*
* instance coding the unique identifier of the object
*/
private Map<Object, Object> alreadyDeserialized;
/**
* This the variable used to encode objects as strings. If it is not set, the default instance will be used.
*/
private StringEncoder stringEncoder;
/**
* Creates a new <code>XMLDecoder</code> instance given an XMLMapping object
*
* @param anXmlMapping
* an <code>XMLMapping</code> value
*/
public XMLDecoder(XMLMapping anXmlMapping) {
this(anXmlMapping, null, StringEncoder.getDefaultInstance());
}
/**
* Creates a new <code>XMLDecoder</code> instance given an XMLMapping object
*
* @param anXmlMapping
* an <code>XMLMapping</code> value
*/
public XMLDecoder(XMLMapping anXmlMapping, StringEncoder stringEncoder) {
this(anXmlMapping, null, stringEncoder);
}
/**
* Creates a new <code>XMLDecoder</code> instance given an XMLMapping object and a builder object
*
* @param anXmlMapping
* an <code>XMLMapping</code> value
*/
public XMLDecoder(XMLMapping anXmlMapping, Object aBuilder) {
this(anXmlMapping, aBuilder, StringEncoder.getDefaultInstance());
}
/**
* Creates a new <code>XMLDecoder</code> instance, given a mapping file
*
* @param modelFile
* a <code>File</code> value
* @exception IOException
* if an IOException error occurs (eg. file not found)
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception InvalidModelException
* if an error occurs
*/
public XMLDecoder(File modelFile) throws IOException, SAXException, ParserConfigurationException, InvalidModelException {
this(new XMLMapping(modelFile), StringEncoder.getDefaultInstance());
}
/**
* Creates a new <code>XMLDecoder</code> instance, given a mapping stream
*
* @param modelInputStream
* a <code>InputStream</code> value
* @exception IOException
* if an IOException error occurs (eg. file not found)
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception InvalidModelException
* if an error occurs
*/
public XMLDecoder(InputStream modelInputStream) throws IOException, SAXException, ParserConfigurationException, InvalidModelException {
this(new XMLMapping(modelInputStream), StringEncoder.getDefaultInstance());
}
/**
* Creates a new <code>XMLDecoder</code> instance, given a mapping file
*
* @param modelFile
* a <code>File</code> value
* @exception IOException
* if an IOException error occurs (eg. file not found)
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception InvalidModelException
* if an error occurs
*/
public XMLDecoder(File modelFile, StringEncoder stringEncoder) throws IOException, SAXException, ParserConfigurationException,
InvalidModelException {
this(new XMLMapping(modelFile), null, stringEncoder);
}
/**
* Creates a new <code>XMLDecoder</code> instance, given a mapping file and a builder object
*
* @param modelFile
* a <code>File</code> value
* @exception IOException
* if an IOException error occurs (eg. file not found)
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception InvalidModelException
* if an error occurs
*/
public XMLDecoder(File modelFile, Object aBuilder) throws IOException, SAXException, ParserConfigurationException,
InvalidModelException {
this(new XMLMapping(modelFile), aBuilder, StringEncoder.getDefaultInstance());
}
/**
* Creates a new <code>XMLDecoder</code> instance, given a mapping file and a builder object
*
* @param modelFile
* a <code>File</code> value
* @exception IOException
* if an IOException error occurs (eg. file not found)
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception InvalidModelException
* if an error occurs
*/
public XMLDecoder(File modelFile, Object aBuilder, StringEncoder stringEncoder) throws IOException, SAXException,
ParserConfigurationException, InvalidModelException {
this(new XMLMapping(modelFile), aBuilder, stringEncoder);
}
/**
* Creates a new <code>XMLDecoder</code> instance given an XMLMapping object and a builder object
*
* @param anXmlMapping
* an <code>XMLMapping</code> value
* @param stringEncoder
* - the string encoder to use with this decoder
*/
public XMLDecoder(XMLMapping anXmlMapping, Object aBuilder, StringEncoder encoder) {
super();
xmlMapping = anXmlMapping;
alreadyDeserialized = new Hashtable<Object, Object>();
nextReference = 0;
builder = aBuilder;
stringEncoder = encoder;
}
private void delete() {
alreadyDeserialized.clear();
alreadyDeserialized = null;
builder = null;
xmlMapping = null;
stringEncoder = null;
}
/**
* Decode and returns a newly created object from string <code>xmlString</code> according to mapping <code>xmlMapping</code>.
*
* @param xmlString
* a <code>String</code> value
* @param xmlMapping
* a <code>XMLMapping</code> value
* @return an <code>XMLSerializable</code> value
* @exception InvalidXMLDataException
* if an error occurs
* @exception InvalidObjectSpecificationException
* if an error occurs
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception IOException
* if an error occurs
* @exception AccessorInvocationException
* if an error occurs during accessor invocation
* @throws JDOMException
* @throws InvalidModelException
*/
public static XMLSerializable decodeObjectWithMapping(String xmlString, XMLMapping xmlMapping) throws InvalidXMLDataException,
InvalidObjectSpecificationException, IOException, AccessorInvocationException, InvalidModelException, JDOMException {
XMLDecoder decoder = new XMLDecoder(xmlMapping);
return decoder.decodeObject(xmlString);
}
/**
* Decode and returns a newly created object from string <code>xmlString</code> according to mapping defined in model file
* <code>modelFile</code>.
*
* @param xmlString
* a <code>String</code> value
* @param modelFile
* a <code>File</code> value
* @return an <code>XMLSerializable</code> value
* @exception InvalidXMLDataException
* if an error occurs
* @exception InvalidObjectSpecificationException
* if an error occurs
* @exception IOException
* if an error occurs
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception InvalidModelException
* if an error occurs
* @exception AccessorInvocationException
* if an error occurs during accessor invocation
* @throws JDOMException
*/
public static XMLSerializable decodeObjectWithMapping(String xmlString, File modelFile) throws InvalidXMLDataException,
InvalidObjectSpecificationException, IOException, SAXException, ParserConfigurationException, InvalidModelException,
AccessorInvocationException, JDOMException {
XMLDecoder decoder = new XMLDecoder(modelFile);
return decoder.decodeObject(xmlString);
}
/**
* Decode and returns a newly created object from string <code>xmlString</code> according to mapping defined in model stream
* <code>modelInputStream</code>.
*
* @param xmlString
* a <code>String</code> value
* @param modelInputStream
* a <code>InputStream</code> value
* @return an <code>XMLSerializable</code> value
* @exception InvalidXMLDataException
* if an error occurs
* @exception InvalidObjectSpecificationException
* if an error occurs
* @exception IOException
* if an error occurs
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception InvalidModelException
* if an error occurs
* @exception AccessorInvocationException
* if an error occurs during accessor invocation
* @throws JDOMException
*/
public static XMLSerializable decodeObjectWithMapping(String xmlString, InputStream modelInputStream) throws InvalidXMLDataException,
InvalidObjectSpecificationException, IOException, SAXException, ParserConfigurationException, InvalidModelException,
AccessorInvocationException, JDOMException {
XMLDecoder decoder = new XMLDecoder(modelInputStream);
return decoder.decodeObject(xmlString);
}
/**
* Decode and returns a newly created object from input stream <code>xmlStream</code> according to mapping <code>xmlMapping</code>.
*
* @param xmlStream
* a <code>InputStream</code> value
* @param xmlMapping
* a <code>XMLMapping</code> value
* @return an <code>XMLSerializable</code> value
* @exception InvalidXMLDataException
* if an error occurs
* @exception InvalidObjectSpecificationException
* if an error occurs
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception IOException
* if an error occurs
* @exception AccessorInvocationException
* if an error occurs during accessor invocation
* @throws JDOMException
* @throws InvalidModelException
*/
public static XMLSerializable decodeObjectWithMapping(InputStream xmlStream, XMLMapping xmlMapping) throws InvalidXMLDataException,
InvalidObjectSpecificationException, IOException, AccessorInvocationException, InvalidModelException, JDOMException {
XMLDecoder decoder = new XMLDecoder(xmlMapping);
return decoder.decodeObject(xmlStream);
}
/**
* Decode and returns a newly created object from input stream <code>xmlStream</code> according to mapping <code>xmlMapping</code>.
*
* @param xmlStream
* a <code>InputStream</code> value
* @param xmlMapping
* a <code>XMLMapping</code> value
* @return an <code>XMLSerializable</code> value
* @exception InvalidXMLDataException
* if an error occurs
* @exception InvalidObjectSpecificationException
* if an error occurs
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception IOException
* if an error occurs
* @exception AccessorInvocationException
* if an error occurs during accessor invocation
* @throws JDOMException
* @throws InvalidModelException
*/
public static XMLSerializable decodeObjectWithMappingAndStringEncoder(InputStream xmlStream, XMLMapping xmlMapping,
StringEncoder stringEncoder) throws InvalidXMLDataException, InvalidObjectSpecificationException, IOException,
AccessorInvocationException, InvalidModelException, JDOMException {
XMLDecoder decoder = new XMLDecoder(xmlMapping, stringEncoder);
return decoder.decodeObject(xmlStream);
}
/**
* Decode and returns a newly created object from input stream <code>xmlStream</code> according to mapping defined in model file
* <code>modelFile</code>.
*
* @param modelFile
* a <code>File</code> value
* @param xmlStream
* a <code>InputStream</code> value
* @return an <code>XMLSerializable</code> value
* @exception InvalidXMLDataException
* if an error occurs
* @exception InvalidObjectSpecificationException
* if an error occurs
* @exception IOException
* if an error occurs
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception InvalidModelException
* if an error occurs
* @exception AccessorInvocationException
* if an error occurs during accessor invocation
* @throws JDOMException
*/
public static XMLSerializable decodeObjectWithMappingFile(InputStream xmlStream, File modelFile) throws InvalidXMLDataException,
InvalidObjectSpecificationException, IOException, SAXException, ParserConfigurationException, InvalidModelException,
AccessorInvocationException, JDOMException {
XMLDecoder decoder = new XMLDecoder(modelFile);
return decoder.decodeObject(xmlStream);
}
/**
* Decode and returns a newly created object from string with a builder <code>xmlString</code> according to mapping
* <code>xmlMapping</code>.
*
* @param xmlString
* a <code>String</code> value
* @param xmlMapping
* a <code>XMLMapping</code> value
* @return an <code>XMLSerializable</code> value
* @exception InvalidXMLDataException
* if an error occurs
* @exception InvalidObjectSpecificationException
* if an error occurs
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception IOException
* if an error occurs
* @exception AccessorInvocationException
* if an error occurs during accessor invocation
* @throws JDOMException
* @throws InvalidModelException
*/
public static XMLSerializable decodeObjectWithMapping(String xmlString, XMLMapping xmlMapping, Object builder)
throws InvalidXMLDataException, InvalidObjectSpecificationException, IOException, AccessorInvocationException,
InvalidModelException, JDOMException {
XMLDecoder decoder = new XMLDecoder(xmlMapping, builder, StringEncoder.getDefaultInstance());
return decoder.decodeObject(xmlString);
}
/**
* Decode and returns a newly created object from string with a builder <code>xmlString</code> according to mapping
* <code>xmlMapping</code>.
*
* @param xmlString
* a <code>String</code> value
* @param xmlMapping
* a <code>XMLMapping</code> value
* @return an <code>XMLSerializable</code> value
* @exception InvalidXMLDataException
* if an error occurs
* @exception InvalidObjectSpecificationException
* if an error occurs
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception IOException
* if an error occurs
* @exception AccessorInvocationException
* if an error occurs during accessor invocation
* @throws JDOMException
* @throws InvalidModelException
*/
public static XMLSerializable decodeObjectWithMapping(String xmlString, XMLMapping xmlMapping, Object builder,
StringEncoder stringEncoder) throws InvalidXMLDataException, InvalidObjectSpecificationException, IOException,
AccessorInvocationException, InvalidModelException, JDOMException {
XMLDecoder decoder = new XMLDecoder(xmlMapping, builder, stringEncoder);
return decoder.decodeObject(xmlString);
}
/**
* Decode and returns a newly created object from string with a builder <code>xmlString</code> according to mapping defined in model
* file <code>modelFile</code>.
*
* @param xmlString
* a <code>String</code> value
* @param modelFile
* a <code>File</code> value
* @return an <code>XMLSerializable</code> value
* @exception InvalidXMLDataException
* if an error occurs
* @exception InvalidObjectSpecificationException
* if an error occurs
* @exception IOException
* if an error occurs
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception InvalidModelException
* if an error occurs
* @exception AccessorInvocationException
* if an error occurs during accessor invocation
* @throws JDOMException
*/
public static XMLSerializable decodeObjectWithMapping(String xmlString, File modelFile, Object builder) throws InvalidXMLDataException,
InvalidObjectSpecificationException, IOException, SAXException, ParserConfigurationException, InvalidModelException,
AccessorInvocationException, JDOMException {
XMLDecoder decoder = new XMLDecoder(modelFile, builder);
return decoder.decodeObject(xmlString);
}
/**
* Decode and returns a newly created object from input stream with a builder <code>xmlStream</code> according to mapping
* <code>xmlMapping</code>.
*
* @param xmlStream
* a <code>InputStream</code> value
* @param xmlMapping
* a <code>XMLMapping</code> value
* @return an <code>XMLSerializable</code> value
* @exception InvalidXMLDataException
* if an error occurs
* @exception InvalidObjectSpecificationException
* if an error occurs
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception IOException
* if an error occurs
* @exception AccessorInvocationException
* if an error occurs during accessor invocation
* @throws JDOMException
* @throws InvalidModelException
*/
public static XMLSerializable decodeObjectWithMapping(InputStream xmlStream, XMLMapping xmlMapping, Object builder)
throws InvalidXMLDataException, InvalidObjectSpecificationException, IOException, AccessorInvocationException,
InvalidModelException, JDOMException {
XMLDecoder decoder = new XMLDecoder(xmlMapping, builder, StringEncoder.getDefaultInstance());
return decoder.decodeObject(xmlStream);
}
/**
* Decode and returns a newly created object from input stream with a builder <code>xmlStream</code> according to mapping
* <code>xmlMapping</code>.
*
* @param xmlStream
* a <code>InputStream</code> value
* @param xmlMapping
* a <code>XMLMapping</code> value
* @return an <code>XMLSerializable</code> value
* @exception InvalidXMLDataException
* if an error occurs
* @exception InvalidObjectSpecificationException
* if an error occurs
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception IOException
* if an error occurs
* @exception AccessorInvocationException
* if an error occurs during accessor invocation
* @throws JDOMException
* @throws InvalidModelException
*/
public static XMLSerializable decodeObjectWithMapping(InputStream xmlStream, XMLMapping xmlMapping, Object builder,
StringEncoder stringEncoder) throws InvalidXMLDataException, InvalidObjectSpecificationException, IOException,
AccessorInvocationException, InvalidModelException, JDOMException {
XMLDecoder decoder = new XMLDecoder(xmlMapping, builder, stringEncoder);
return decoder.decodeObject(xmlStream);
}
/**
* Decode and returns a newly created object from input stream with a builder <code>xmlStream</code> according to mapping defined in
* model file <code>modelFile</code>.
*
* @param modelFile
* a <code>File</code> value
* @param xmlStream
* a <code>InputStream</code> value
* @return an <code>XMLSerializable</code> value
* @exception InvalidXMLDataException
* if an error occurs
* @exception InvalidObjectSpecificationException
* if an error occurs
* @exception IOException
* if an error occurs
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception InvalidModelException
* if an error occurs
* @exception AccessorInvocationException
* if an error occurs during accessor invocation
* @throws JDOMException
*/
public static XMLSerializable decodeObjectWithMappingFile(InputStream xmlStream, File modelFile, Object builder)
throws InvalidXMLDataException, InvalidObjectSpecificationException, IOException, SAXException, ParserConfigurationException,
InvalidModelException, AccessorInvocationException, JDOMException {
XMLDecoder decoder = new XMLDecoder(modelFile, builder);
return decoder.decodeObject(xmlStream);
}
/**
* Decode and returns a newly created object from input stream with a builder <code>xmlStream</code> according to mapping defined in
* model file <code>modelFile</code>.
*
* @param modelFile
* a <code>File</code> value
* @param xmlStream
* a <code>InputStream</code> value
* @param stringEncoder
* a <code>StringEncoder</code> value
* @return an <code>XMLSerializable</code> value
* @exception InvalidXMLDataException
* if an error occurs
* @exception InvalidObjectSpecificationException
* if an error occurs
* @exception IOException
* if an error occurs
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception InvalidModelException
* if an error occurs
* @exception AccessorInvocationException
* if an error occurs during accessor invocation
* @throws JDOMException
*/
public static XMLSerializable decodeObjectWithMappingFile(InputStream xmlStream, File modelFile, Object builder, StringEncoder encoder)
throws InvalidXMLDataException, InvalidObjectSpecificationException, IOException, SAXException, ParserConfigurationException,
InvalidModelException, AccessorInvocationException, JDOMException {
XMLDecoder decoder = new XMLDecoder(modelFile, builder, encoder);
return decoder.decodeObject(xmlStream);
}
/**
* Decode and returns a newly created object from string <code>xmlString</code> according to mapping defined in this
* <code>XMLDecoder</code> object.
*
* @param xmlString
* a <code>String</code> value
* @return an <code>XMLSerializable</code> value
* @exception InvalidXMLDataException
* if an error occurs
* @exception InvalidObjectSpecificationException
* if an error occurs
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception InvalidModelException
* if no valid mapping nor mapping file were specified
* @exception IOException
* if an error occurs
* @exception AccessorInvocationException
* if an error occurs during accessor invocation
* @throws JDOMException
*/
public XMLSerializable decodeObject(String xmlString) throws InvalidXMLDataException, InvalidObjectSpecificationException,
InvalidModelException, IOException, AccessorInvocationException, JDOMException {
if (xmlMapping == null) {
throw new InvalidModelException("No mapping specified.");
}
XMLSerializable returned = buildObjectFromDocument(parseXMLData(xmlString));
delete();
return returned;
}
/**
* Decode and returns a newly created object from input stream <code>xmlStream</code> according to mapping defined in this
* <code>XMLDecoder</code> object.
*
* @param xmlStream
* a <code>String</code> value
* @return an <code>XMLSerializable</code> value
* @exception InvalidXMLDataException
* if an error occurs
* @exception InvalidObjectSpecificationException
* if an error occurs
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception InvalidModelException
* if no valid mapping nor mapping file were specified
* @exception IOException
* if an error occurs
* @exception AccessorInvocationException
* if an error occurs during accessor invocation
* @throws JDOMException
*/
public XMLSerializable decodeObject(InputStream xmlStream) throws InvalidXMLDataException, InvalidObjectSpecificationException,
InvalidModelException, IOException, AccessorInvocationException, JDOMException {
if (xmlMapping == null) {
throw new InvalidModelException("No mapping specified.");
}
XMLSerializable returned = buildObjectFromDocument(parseXMLData(xmlStream));
delete();
return returned;
}
/**
* Returns flag indicating if specified <code>xmlString</code> is well formed.
*
* @param xmlString
* a <code>String</code> value
* @return a <code>boolean</code> value
* @exception SAXException
* if an error occurs (independantly of well-forming)
* @exception ParserConfigurationException
* if an error occurs (independantly of well-forming)
* @exception IOException
* if an error occurs (independantly of well-forming)
*/
public static boolean isWellFormed(String xmlString) throws IOException {
try {
XMLDecoder decoder = new XMLDecoder((XMLMapping) null);
decoder.parseXMLData(xmlString);
return true;
} catch (JDOMException e) {
return false;
}
}
/**
* Internally used durinf XML decoding process.<br>
* Parse xml data from a string <code>xmlString</code> and returns a newly created XML document.
*
* @param xmlString
* a <code>String</code> value
* @return a <code>Document</code> value
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception IOException
* if an error occurs
* @throws JDOMException
*/
protected Document parseXMLData(String xmlString) throws IOException, JDOMException {
ByteArrayInputStream xmlStream;
byte[] bytes = xmlString.getBytes("UTF-8");// Maybe it is a too big assumption that UTF-8 is the default encoding for XML, but it
// is definitely better to retrieve the bytes with a charset than without.
xmlStream = new ByteArrayInputStream(bytes, 0, bytes.length);// xmlString.length() is not correct unless the charsets codes every
// character in a single byte.
return parseXMLData(xmlStream);
}
/**
* Internally used durinf XML decoding process.<br>
* Parse xml data from an input stream <code>xmlStream</code> and returns a newly created XML document.
*
* @param xmlStream
* an <code>InputStream</code> value
* @return a <code>Document</code> value
* @exception SAXException
* if an error occurs
* @exception ParserConfigurationException
* if an error occurs
* @exception IOException
* if an error occurs
* @throws JDOMException
*/
protected Document parseXMLData(InputStream xmlStream) throws IOException, JDOMException {
SAXBuilder parser = new SAXBuilder();
Document reply = parser.build(xmlStream);
makeIndex(reply);
return reply;
}
static private class ElementWithIDFilter extends ElementFilter {
public ElementWithIDFilter() {
super();
}
@Override
public Element filter(Object arg0) {
Element element = super.filter(arg0);
if (element != null && element.getAttributeValue("id") != null) {
return element;
}
return null;
}
}
private Hashtable<String, Element> _index;
public Document makeIndex(Document doc) {
_index = new Hashtable<String, Element>();
Iterator<Element> it = doc.getDescendants(new ElementWithIDFilter());
Element e = null;
while (it.hasNext()) {
e = it.next();
_index.put(e.getAttributeValue("id"), e);
}
return doc;
}
private Element findElementWithId(String id) {
return _index.get(id);
}
/**
* Internally used during XML decoding process.<br>
* Build and returns newly created object from an XML document <code>dataDocument</code>.
*
* @param dataDocument
* a <code>Document</code> value
* @return an <code>XMLSerializable</code> value
* @exception InvalidXMLDataException
* if an error occurs
* @exception InvalidObjectSpecificationException
* if an error occurs
* @exception AccessorInvocationException
* if an error occurs during accessor invocation
*/
protected XMLSerializable buildObjectFromDocument(Document dataDocument) throws InvalidXMLDataException,
InvalidObjectSpecificationException, AccessorInvocationException {
Element rootElement = dataDocument.getRootElement();
XMLSerializable returnedObject = (XMLSerializable) buildObjectFromNode(rootElement);
runDecodingFinalization(returnedObject);
return returnedObject;
}
private void runDecodingFinalization(Object rootObject) throws AccessorInvocationException {
// Debugging.debug("Run decoding finalization");
Object[] params = { builder };
for (Object next : alreadyDeserialized.values()) {
if (next != rootObject) {
// Gros truc degeulasse a modifier plus tard,
// quand on aura repondu a la question:
// pourquoi le root object est-il parfois dans les
// alreadyDeserialized, parfois non
runDecodingFinalizationForObject(next, params);
}
}
runDecodingFinalizationForObject(rootObject, params);
alreadyDeserialized.clear();
}
private void runDecodingFinalizationForObject(Object next, Object[] params) throws AccessorInvocationException {
ModelEntity entity = xmlMapping.entityForClass(next.getClass());
if (entity != null) {
try {
boolean finalizationHasBeenPerformed = false;
if (xmlMapping.hasBuilderClass()) {
if (entity.hasFinalizerWithParameter()) {
entity.getFinalizerWithParameter().invoke(next, params);
finalizationHasBeenPerformed = true;
}
}
if (entity.hasFinalizerWithoutParameter() && !finalizationHasBeenPerformed) {
entity.getFinalizerWithoutParameter().invoke(next, null);
finalizationHasBeenPerformed = true;
}
} catch (IllegalAccessException e1) {
e1.printStackTrace();
} catch (InvocationTargetException e2) {
e2.getTargetException().printStackTrace();
throw new AccessorInvocationException("Exception " + e2.getClass().getName() + " caught during finalization.", e2);
}
}
}
/**
* Internally used during XML decoding process.<br>
* Build and returns newly created object from an XML element <code>node</code>.
*
* @param node
* an <code>Element</code> value
* @return an <code>XMLSerializable</code> value
* @exception InvalidXMLDataException
* if an error occurs
* @exception InvalidObjectSpecificationException
* if an error occurs
*/
protected Object buildObjectFromNode(Element node) throws InvalidXMLDataException, InvalidObjectSpecificationException {
ModelEntity modelEntity;
String elementName;
// Debugging.debug ("Building object from node:"+node.toString());
elementName = node.getName();
modelEntity = xmlMapping.entityWithXMLTag(elementName);
if (modelEntity != null) {
return buildObjectFromNodeAndModelEntity(node, modelEntity);
} // end of if ()
else {
ModelProperty modelProperty = xmlMapping.propertyWithXMLTag(elementName);
if (modelProperty == null) {
throw new InvalidXMLDataException("Tag '" + elementName + "' not found in model");
} else {
/*NodeList childNodesList = node.getChildNodes();
String textValue = "";
for (int i = 0; i < childNodesList.getLength(); i++) {
Node tempNode = childNodesList.item(i);
if (tempNode.getNodeType() == Node.TEXT_NODE) {
textValue += tempNode.getNodeValue();
}
}*/
String textValue = node.getText();
if (modelProperty.isArray()) {
ArrayKeyValueProperty keyValueProperty = (ArrayKeyValueProperty) modelProperty.getKeyValueProperty();
return stringEncoder._decodeObject(textValue, keyValueProperty.getComponentType());
}
if (modelProperty.isVector()) {
VectorKeyValueProperty keyValueProperty = (VectorKeyValueProperty) modelProperty.getKeyValueProperty();
return stringEncoder._decodeObject(textValue, keyValueProperty.getContentType());
}
return textValue;
}
}
}
/**
* Internally used during XML decoding process.<br>
* Build and returns newly created object from an XML element <code>node</code> and model entity <code>modelEntity</code>.
*
* @param node
* an <code>Element</code> value
* @param modelEntity
* a <code>ModelEntity</code> value
* @return an <code>XMLSerializable</code> value
* @exception InvalidXMLDataException
* if an error occurs
* @exception InvalidObjectSpecificationException
* if an error occurs
*/
protected Object buildObjectFromNodeAndModelEntity(Element node, ModelEntity modelEntity) throws InvalidXMLDataException,
InvalidObjectSpecificationException {
Class returnedObjectClass;
XMLSerializable returnedObject;
// Integer currentDeserializedReference = null;
Object currentDeserializedReference = null;
// This is the class name given by the model entity
returnedObjectClass = modelEntity.getRelatedClass();
if (xmlMapping.handlesReferences()) {
Attribute idAttribute = node.getAttribute(XMLMapping.idLabel);
Attribute idrefAttribute = node.getAttribute(XMLMapping.idrefLabel);
// System.out.println ("buildObjectFromNodeAndModelEntity node
// "+node.getNodeName()+" /
// "+modelEntity.getName()+"/"+modelEntity.getXmlTags());
if (idrefAttribute != null) {
// This seems to be an already deserialized object
Object reference;
if (implementsCustomIdMappingScheme()) {
reference = idrefAttribute.getValue();
} else {
reference = new Integer(StringEncoder.decodeAsInteger(idrefAttribute.getValue()));
}
// System.out.println ("Object with idref = "+reference);
Object referenceObject = alreadyDeserialized.get(reference);
if (referenceObject == null) {
// Try to find this object elsewhere in the document
// NOTE: This should never occur, except if the file was
// manually edited, or
// if the file was generated BEFORE development of ordered
// properties feature
// TODO: Throw here an error in future release but for backward compatibility we leave it for now.
Element idRefElement = findElementWithId(node.getDocument(), idrefAttribute.getValue());
if (idRefElement != null) {
if (xmlMapping.entityWithXMLTag(idRefElement.getName()) != modelEntity) {
System.err.println("SEVERE: Found a referencing object with a non-corresponding entity tag '"
+ idRefElement.getName() + "' than the referred object" + node.getName());
}
return buildObjectFromNodeAndModelEntity(idRefElement, modelEntity);
}
throw new InvalidXMLDataException("No reference to object with identifier " + reference);
} else {
// No need to go further: i've got my object
// Debugging.debug ("Stopping decoding: object found as a
// reference "+reference+" "+referenceObject);
return referenceObject;
}
}
if (idAttribute != null) {
if (implementsCustomIdMappingScheme()) {
currentDeserializedReference = idAttribute.getValue();
} else {
currentDeserializedReference = new Integer(StringEncoder.decodeAsInteger(idAttribute.getValue()));
}
// System.out.println ("Object with id =
// "+currentDeserializedReference);
Object referenceObject = alreadyDeserialized.get(currentDeserializedReference);
if (referenceObject != null) {
// No need to go further: i've got my object
// Debugging.debug ("Stopping decoding: object found as a
// reference "+reference+" "+referenceObject);
return referenceObject;
}
}
} else {
currentDeserializedReference = getNextReference();
}
if (modelEntity.implementsGenericTypingKVProperty() && node.getAttribute(XMLMapping.genericTypingClassName) != null) {
// Generic typing implementation
Attribute classNameAttribute = node.getAttribute(XMLMapping.genericTypingClassName);
try {
returnedObjectClass = Class.forName(classNameAttribute.getValue());
returnedObject = instanciateMoreSpecializedObject(modelEntity, returnedObjectClass);
if (!modelEntity.getRelatedClass().isAssignableFrom(returnedObjectClass)) {
throw new InvalidXMLDataException("Class" + classNameAttribute.getValue() + " was found, but doesn't inherit from "
+ modelEntity.getRelatedClass().getName());
}
} catch (ClassNotFoundException e) {
// System.out.println("modelEntity="+modelEntity);
// System.out.println("modelEntity.getParentEntity()="+modelEntity.getParentEntity());
returnedObject = instanciateNewObject(modelEntity);
KeyValueCoder.setValueForKey(returnedObject, classNameAttribute.getValue(), modelEntity.getGenericTypingKVProperty(),
stringEncoder);
}
}
else {
// Or, may be the class name to instanciate is more specialized
Attribute classNameAttribute = node.getAttribute(XMLMapping.classNameLabel);
// System.out.println("classNameAttribute="+classNameAttribute);
if (classNameAttribute != null) {
try {
returnedObjectClass = Class.forName(classNameAttribute.getValue());
if (returnedObjectClass.isAnonymousClass()) {
// Anomynous classes will be ignored
returnedObject = instanciateNewObject(modelEntity);
} else {
returnedObject = instanciateMoreSpecializedObject(modelEntity, returnedObjectClass);
}
} catch (ClassNotFoundException e) {
// System.out.println("node qui foire="+node);
// throw new InvalidXMLDataException("Cannot find " + classNameAttribute.getValue() + " class.");
System.err.println("Cannot find class " + classNameAttribute.getValue());
returnedObject = instanciateNewObject(modelEntity);
}
if (!modelEntity.getRelatedClass().isAssignableFrom(returnedObjectClass)) {
throw new InvalidXMLDataException("Class" + classNameAttribute.getValue() + " was found, but doesn't inherit from "
+ modelEntity.getRelatedClass().getName());
}
} else {
returnedObject = instanciateNewObject(modelEntity);
}
}
if (currentDeserializedReference != null) {
// Debugging.debug ("Registering object reference
// "+currentDeserializedReference);
// System.out.println ("Registering ref:
// "+currentDeserializedReference+" object
// "+returnedObject.getClass().getName());
alreadyDeserialized.put(currentDeserializedReference, returnedObject);
}
if (modelEntity != null) {
try {
Object[] params = { builder };
boolean initializationHasBeenPerformed = false;
if (xmlMapping.hasBuilderClass()) {
if (modelEntity.hasInitializerWithParameter()) {
modelEntity.getInitializerWithParameter().invoke(returnedObject, params);
initializationHasBeenPerformed = true;
}
}
if (modelEntity.hasInitializerWithoutParameter() && !initializationHasBeenPerformed) {
modelEntity.getInitializerWithoutParameter().invoke(returnedObject, null);
initializationHasBeenPerformed = true;
}
} catch (IllegalAccessException e1) {
e1.printStackTrace();
} catch (InvocationTargetException e2) {
e2.getTargetException().printStackTrace();
throw new AccessorInvocationException("Exception " + e2.getClass().getName() + " caught during finalization.", e2);
}
}
for (Enumeration<ModelProperty> e = modelEntity.getModelProperties(); e.hasMoreElements();) {
ModelProperty modelProperty = e.nextElement();
setPropertyForObject(node, returnedObject, returnedObjectClass, modelProperty, modelEntity);
}
return returnedObject;
}
/**
* Internally used to instanciate new object from known constructors given the declared builder, if necessary
*
* @param modelEntity
* @return
*/
protected XMLSerializable instanciateNewObject(ModelEntity modelEntity) {
try {
if (xmlMapping.hasBuilderClass() && builder != null) {
Object[] params = { builder };
if (modelEntity.hasConstructorWithParameter()) {
return (XMLSerializable) modelEntity.getConstructorWithParameter().newInstance(params);
} else if (modelEntity.hasConstructorWithoutParameter()) {
return (XMLSerializable) modelEntity.getConstructorWithoutParameter().newInstance(null);
} else {
throw new InvalidObjectSpecificationException("Class " + modelEntity.getName()
+ " is not instanciable because no constructor with builder is declared.");
}
} else {
if (modelEntity.hasConstructorWithoutParameter()) {
return (XMLSerializable) modelEntity.getConstructorWithoutParameter().newInstance(null);
} else {
throw new InvalidObjectSpecificationException("Class " + modelEntity.getName()
+ " is not instanciable because no constructor without parameter is declared.");
}
}
} catch (InvocationTargetException e) {
e.getTargetException().printStackTrace();
throw new AccessorInvocationException("Class " + modelEntity.getName()
+ " is not instanciable because an exception raised in object constructor.", e);
} catch (InstantiationException e) {
throw new InvalidObjectSpecificationException("Class " + modelEntity.getName() + " is not instanciable.");
} catch (IllegalAccessException e) {
throw new InvalidObjectSpecificationException("Class " + modelEntity.getName() + " is not accessible.");
} catch (ExceptionInInitializerError e) {
throw new InvalidObjectSpecificationException("Class " + modelEntity.getName() + ": initialization failed");
} catch (SecurityException e) {
throw new InvalidObjectSpecificationException("Class " + modelEntity.getName() + ": security exception.");
}
}
/**
* Internally used to instanciate new object which is more specialized than the one defined in the model. In this case, known
* constructors are not usable !
*
* @param modelEntity
* @return
*/
private XMLSerializable instanciateMoreSpecializedObject(ModelEntity modelEntity, Class returnedObjectClass) {
Constructor constructorWithoutParameter = null;
Constructor constructorWithParameter = null;
try {
constructorWithoutParameter = returnedObjectClass.getConstructor(null);
} catch (NoSuchMethodException e) {
// Ignore for now
}
if (modelEntity.getModel().hasBuilderClass()) {
try {
Class[] builderParams = { modelEntity.getModel().builderClass() };
constructorWithParameter = returnedObjectClass.getConstructor(builderParams);
} catch (NoSuchMethodException e) {
// Ignore for now
}
}
if (constructorWithoutParameter == null && constructorWithParameter == null) {
throw new InvalidObjectSpecificationException("Class " + modelEntity.getName()
+ " is not instanciable because no constructor with or without builder is declared returnedObjectClass="
+ returnedObjectClass);
}
Object returned = null;
try {
if (modelEntity.getModel().hasBuilderClass() && constructorWithParameter != null) {
Object[] params = { builder };
returned = constructorWithParameter.newInstance(params);
return (XMLSerializable) returned;
} else {
returned = constructorWithoutParameter.newInstance(null);
return (XMLSerializable) returned;
}
} catch (InvocationTargetException e) {
throw new AccessorInvocationException("Class " + modelEntity.getName()
+ " is not instanciable because an exception raised in object constructor.", e);
} catch (InstantiationException e) {
throw new InvalidObjectSpecificationException("Class " + modelEntity.getName() + " is not instanciable.");
} catch (ClassCastException e) {
e.printStackTrace();
throw new InvalidObjectSpecificationException("Class " + modelEntity.getName() + " is not XMLSerializable.");
} catch (IllegalAccessException e) {
throw new InvalidObjectSpecificationException("Class " + modelEntity.getName() + " is not accessible.");
} catch (ExceptionInInitializerError e) {
throw new InvalidObjectSpecificationException("Class " + modelEntity.getName() + ": initialization failed");
} catch (SecurityException e) {
throw new InvalidObjectSpecificationException("Class " + modelEntity.getName() + ": security exception.");
}
}
/**
* Internally used during XML decoding process.<br>
* Build and returns newly created object matching the key of current stored object (required for hashtable-like data structures). The
* key is extracted from the first 'key' element found.
*
* @param node
* an <code>Element</code> value
* @param modelEntity
* a <code>ModelEntity</code> value
* @return an <code>Object</code> value
* @exception InvalidXMLDataException
* if an error occurs
* @exception InvalidObjectSpecificationException
* if an error occurs
*/
protected Object buildKeyFromNode(Element node) throws InvalidXMLDataException, InvalidObjectSpecificationException {
List matchingElements;
// Trying to find an attribute (it may be a string object)
if (node.getAttribute(XMLMapping.keyLabel) != null) {
return node.getAttributeValue(XMLMapping.keyLabel);
}
// I don't think so
matchingElements = node.getChildren(XMLMapping.keyLabel);
if (matchingElements.size() >= 1) {
return buildKeyFromKeyNode((Element) matchingElements.get(0));
}
// I've found nothing
throw new InvalidXMLDataException("No key found for storing object in a hastable-like data structure: looking for key in " + node);
}
/**
* Internally used during XML decoding process.<br>
* Build and returns newly created object matching the specified node, asserting <code>node</code> is the key node.<br>
* Note that if some attributes are here defined, the first one will be interpreted as a String and returned as the key (eventual other
* child nodes will be ignored) else the first child node will be interpreted as the key.
*
* @param node
* an <code>Element</code> value
* @param modelEntity
* a <code>ModelEntity</code> value
* @return an <code>Object</code> value
* @exception InvalidXMLDataException
* if an error occurs
* @exception InvalidObjectSpecificationException
* if an error occurs
*/
protected Object buildKeyFromKeyNode(Element node) throws InvalidXMLDataException, InvalidObjectSpecificationException {
List childNodesList = node.getContent();
List attributes = node.getAttributes();
if (attributes.size() > 0) {
// the first one will be interpreted as a String and returned as
// the key (eventual other child nodes will be ignored)
return ((Attribute) attributes.get(0)).getValue();
}
else if (childNodesList.size() > 0) {
int i = 0;
while (i < childNodesList.size()) {
Content tempNode = (Content) childNodesList.get(i);
if (tempNode instanceof Text) {
String potentialValue = ((Text) tempNode).getTextTrim();
if (potentialValue.length() > 0) {
return potentialValue;
}
} else if (tempNode instanceof Element) {
Element element = (Element) tempNode;
ModelEntity relatedModelEntity = xmlMapping.entityWithXMLTag(element.getName());
if (relatedModelEntity == null) {
throw new InvalidXMLDataException("Tag '" + element.getName() + "' not found in model");
} else {
return buildObjectFromNodeAndModelEntity(element, relatedModelEntity);
}
}
i++;
}
}
throw new InvalidXMLDataException("No key found for storing object in a hastable-like data structure");
}
/**
* Internally used during XML decoding process.<br>
* Sets property <code>modelProperty</code> for object <code>object</code> according to node <code>node</code> and class
* <code>objectClass</code>.
*
* @param node
* an <code>Element</code> value
* @param object
* an <code>Object</code> value
* @param objectClass
* a <code>Class</code> value
* @param modelProperty
* a <code>ModelProperty</code> value
* @exception InvalidXMLDataException
* if an error occurs
* @exception InvalidObjectSpecificationException
* if an error occurs
*/
protected void setPropertyForObject(Element node, Object object, Class objectClass, ModelProperty modelProperty, ModelEntity modelEntity)
throws InvalidXMLDataException, InvalidObjectSpecificationException {
KeyValueProperty keyValueProperty;
// First, get the key-value property
keyValueProperty = modelProperty.getKeyValueProperty();
if (keyValueProperty instanceof SingleKeyValueProperty) {
List childNodesList = node.getContent();
Content tempNode;
SingleKeyValueProperty singleKeyValueProperty = (SingleKeyValueProperty) keyValueProperty;
boolean primitiveProperty = singleKeyValueProperty.classIsPrimitive(stringEncoder);
// Then, check if property is a text property attribute
if (modelProperty.getIsText()) {
StringBuffer textValue = new StringBuffer();
for (int i = 0; i < childNodesList.size(); i++) {
tempNode = (Content) childNodesList.get(i);
if (tempNode instanceof Text) {
textValue.append(((Text) tempNode).getTextTrim());
}
}
KeyValueCoder.setValueForKey(object, textValue.toString(), singleKeyValueProperty, stringEncoder);
}
// Check if property is an attribute
else if (modelProperty.getIsAttribute()) {
Attribute attribute = node.getAttribute(modelProperty.getDefaultXmlTag());
if (attribute != null) {
// System.out.println ("Property "+modelProperty.getName()+"
// is not null and match attribute
// "+modelProperty.getDefaultXmlTag()+"
// value="+attribute.getNodeValue());
KeyValueCoder.setValueForKey(object, attribute.getValue(), singleKeyValueProperty, stringEncoder);
}
}
else { // Property seem to match an element
Element element;
Iterator matchingElements = elementsMatchingHandledXMLTags(node, modelProperty);
if (matchingElements.hasNext()) {
element = (Element) matchingElements.next();
if (primitiveProperty) {
List childNodes = element.getContent();
if (childNodes.size() == 0) {
// Nothing to do
} else if (childNodes.size() >= 1) {
if (childNodes.get(0) instanceof Text) {
KeyValueCoder.setValueForKey(object, ((Text) childNodes.get(0)).getTextTrim(), singleKeyValueProperty,
stringEncoder);
} else {
throw new InvalidXMLDataException("Tag '" + modelProperty.getName()
+ "' is not well-formed according to model file");
}
} else {
throw new InvalidXMLDataException("Tag '" + modelProperty.getName()
+ "' is not well-formed according to model file (more than one child node)");
}
} else {
KeyValueCoder.setObjectForKey(object, buildObjectFromNode(element), keyValueProperty);
} // end of else
}
else {
// No XML tag matching this property, ignoring
// Nothing to do
}
}
}
else if (keyValueProperty instanceof ArrayKeyValueProperty) {
KeyValueCoder.setArrayForKey(object, vectorOfObjectsMatchingHandledXMLTags(node, modelProperty),
(ArrayKeyValueProperty) keyValueProperty);
}
else if (keyValueProperty instanceof VectorKeyValueProperty) {
Vector myVector = vectorOfObjectsMatchingHandledXMLTags(node, modelProperty);
// System.out.println ("modelProperty="+modelProperty);
// System.out.println ("node="+node);
// System.out.println ("vector="+myVector);
KeyValueCoder.setVectorForKey(object, myVector, (VectorKeyValueProperty) keyValueProperty);
}
else if (keyValueProperty instanceof PropertiesKeyValueProperty) {
if (modelProperty.isProperties()) {
KeyValueCoder.setHashtableForKey(object, propertiesHashtableOfObjectsMatchingHandledXMLTags(node, modelProperty),
(HashtableKeyValueProperty) keyValueProperty);
} else if (modelProperty.isSafeProperties()) {
KeyValueCoder.setHashtableForKey(object, safePropertiesHashtableOfObjectsMatchingHandledXMLTags(node, modelProperty),
(HashtableKeyValueProperty) keyValueProperty);
} else if (modelProperty.isUnmappedAttributes()) {
Vector unmappedAttributes = unmappedAttributes(node, modelProperty, modelEntity);
if (unmappedAttributes.size() > 0) {
// System.out.println("Found unmapped attribute(s) : "+unmappedAttributes);
HashtableKeyValueProperty kvProperty = (HashtableKeyValueProperty) keyValueProperty;
for (Enumeration en = unmappedAttributes.elements(); en.hasMoreElements();) {
Attribute att = (Attribute) en.nextElement();
kvProperty.setObjectValueForKey(att.getValue(), att.getName(), object);
}
}
}
}
else if (keyValueProperty instanceof HashtableKeyValueProperty) {
KeyValueCoder.setHashtableForKey(object, hashtableOfObjectsMatchingHandledXMLTags(node, modelProperty),
(HashtableKeyValueProperty) keyValueProperty);
}
else {
throw new InvalidXMLDataException("Unexpected KeyValueProperty. Please send a bug report.");
}
}
protected Vector unmappedAttributes(Element node, ModelProperty modelProperty, ModelEntity modelEntity) {
Vector returned = new Vector();
Iterator it = node.getAttributes().iterator();
while (it.hasNext()) {
Attribute att = (Attribute) it.next();
if (attributeNameMatchesWildcard(att.getName(), modelProperty.getDefaultXmlTag())) {
boolean isMapped = false;
ModelEntity currentEntity = modelEntity;
while (!isMapped && currentEntity != null) {
for (Enumeration e = currentEntity.getModelProperties(); e.hasMoreElements();) {
ModelProperty temp = (ModelProperty) e.nextElement();
if (temp != modelProperty) {
// System.out.println("Comparing "+temp.getDefaultXmlTag()+" with "+att.getName());
if (temp.hasXmlTag() && temp.getDefaultXmlTag().equals(att.getName())) {
isMapped = true;
break;
}
}
}
if (!isMapped) {
currentEntity = currentEntity.getParentEntity();
}
}
if (!isMapped) {
returned.add(att);
}
}
}
return returned;
}
private boolean attributeNameMatchesWildcard(String attributeName, String wildcard) {
// TODO: We might implement this later interpreting wildcard
return !attributeName.equals("id");
}
/*
protected Vector elementsMatchingHandledXMLTags(Element node, ModelProperty modelProperty)
{
String[] tagNames = modelProperty.getXmlTags();
Vector matchingElements = new Vector();
for (int i = 0; i < tagNames.length; i++) {
List childElements = node.getChildren();
for (int j = 0; j < childElements.size(); j++) {
Element element = (Element) childElements.get(j);
for (int k = 0; k < tagNames.length; k++) {
if (element.getName().equals(tagNames[k])) {
if (!matchingElements.contains(element)) {
matchingElements.add(element);
}
}
}
}
}
return matchingElements;
}
*/
private Iterator elementsMatchingHandledXMLTags(Element node, ModelProperty modelProperty) {
return node.getContent(new MultipleNameFilter(modelProperty.getXmlTags(), node)).iterator();
}
private class MultipleNameFilter extends ElementFilter {
private String[] _tags;
private Element _parent;
MultipleNameFilter(String[] tags, Element node) {
super();
_tags = tags;
_parent = node;
}
@Override
public Element filter(Object content) {
Element element = super.filter(content);
if (element != null) {
if (_parent.equals(element.getParentElement())) {
for (int i = 0; i < _tags.length; i++) {
if (element.getName().equals(_tags[i])) {
return element;
}
}
}
}
return null;
}
}
/**
* Return a vector of {@link XMLSerializable} objects, decoded from elements matching declared handled XML tag of specified
* <code>modelProperty</code>
*/
protected Vector vectorOfObjectsMatchingHandledXMLTags(Element node, ModelProperty modelProperty) {
Vector returnedVector = new Vector();
// Vector matchingElements = elementsMatchingHandledXMLTags(node, modelProperty);
Iterator it = elementsMatchingHandledXMLTags(node, modelProperty);
while (it.hasNext()) {
returnedVector.addElement(buildObjectFromNode((Element) it.next()));
}
return returnedVector;
}
/**
* Return a hashtable of {@link XMLSerializable} objects, decoded from elements matching declared handled XML tag of specified
* <code>modelProperty</code>
*/
protected Map hashtableOfObjectsMatchingHandledXMLTags(Element node, ModelProperty modelProperty) {
Map returnedHashtable = new Hashtable();
Iterator matchingElements = elementsMatchingHandledXMLTags(node, modelProperty);
while (matchingElements.hasNext()) {
Element element = (Element) matchingElements.next();
Object key;
Object object = buildObjectFromNode(element);
if (modelProperty.getKeyToUse() == null) {
key = buildKeyFromNode(element);
} else {
key = KeyValueDecoder.objectForKey(object, modelProperty.getKeyToUse());
}
returnedHashtable.put(key, object);
}
return returnedHashtable;
}
/**
* Return a properties hashtable decoded from elements matching declared handled XML tag of specified <code>modelProperty</code>
*/
protected Hashtable propertiesHashtableOfObjectsMatchingHandledXMLTags(Element node, ModelProperty modelProperty) {
Hashtable returnedHashtable = new Hashtable();
List propertiesNodeList = node.getChildren();
Element propertiesNode;
if (propertiesNodeList.size() == 0) {
// throw new InvalidXMLDataException ("Properties tag
// '"+modelProperty.getName()+"' not found");
return new Hashtable(); // Returns an empty hashtable
} else {
int i = 0;
do {
propertiesNode = (Element) propertiesNodeList.get(i);
i++;
} while (!propertiesNode.getName().equals(modelProperty.getDefaultXmlTag()) && i < propertiesNodeList.size());
}
if (!propertiesNode.getName().equals(modelProperty.getDefaultXmlTag())) {
// throw new InvalidXMLDataException ("Properties tag
// '"+modelProperty.getDefaultXmlTag()+"' not found");
return new Hashtable(); // Returns an empty hashtable
}
List childElements = propertiesNode.getChildren();
for (int j = 0; j < childElements.size(); j++) {
Element element = (Element) childElements.get(j);
Attribute classNameAttr = element.getAttribute(XMLMapping.classNameLabel);
Class objectType = null;
if (classNameAttr != null) {
try {
objectType = Class.forName(classNameAttr.getValue());
} catch (ClassNotFoundException e) {
System.err.println("Class not found " + classNameAttr.getValue() + " for " + element.getName());
// throw new InvalidXMLDataException("Class named '" + classNameAttr.getValue() + "' not found");
}
String value = element.getTextTrim();
if (objectType == null || stringEncoder._converterForClass(objectType) == null) {
// No converter, just keep reference of that object for persistance
// (without instanciating it)
// In this case, class matching property is not loaded, and thus
// Object is not instanciated. But, we must keep serialized version
returnedHashtable.put(element.getName(), new PropertiesKeyValueProperty.UndecodableProperty(classNameAttr.getValue(),
value));
} else {
Object decodedValue = stringEncoder._decodeObject(value, objectType);
if (decodedValue == null) {
System.err.println("Value for " + value + " is null !");
} else {
returnedHashtable.put(element.getName(), decodedValue);
}
}
}
}
return returnedHashtable;
}
/**
* Return a properties hashtable decoded from elements matching declared handled XML tag of specified <code>modelProperty</code>
*/
protected Hashtable safePropertiesHashtableOfObjectsMatchingHandledXMLTags(Element node, ModelProperty modelProperty) {
Hashtable returnedHashtable = new Hashtable();
List propertiesNodeList = node.getChildren();
Element propertiesNode;
if (propertiesNodeList.size() == 0) {
// throw new InvalidXMLDataException ("Properties tag
// '"+modelProperty.getName()+"' not found");
return new Hashtable(); // Returns an empty hashtable
} else {
int i = 0;
do {
propertiesNode = (Element) propertiesNodeList.get(i);
i++;
} while (!propertiesNode.getName().equals(modelProperty.getDefaultXmlTag()) && i < propertiesNodeList.size());
}
if (!propertiesNode.getName().equals(modelProperty.getDefaultXmlTag())) {
// throw new InvalidXMLDataException ("Properties tag
// '"+modelProperty.getDefaultXmlTag()+"' not found");
return new Hashtable(); // Returns an empty hashtable
}
List childElements = propertiesNode.getChildren();
for (int j = 0; j < childElements.size(); j++) {
Element element = (Element) childElements.get(j);
Attribute keyAttr = element.getAttribute(XMLMapping.keyLabel);
Attribute classNameAttr = element.getAttribute(XMLMapping.classNameLabel);
Class objectType = null;
if (classNameAttr != null) {
try {
objectType = Class.forName(classNameAttr.getValue());
} catch (ClassNotFoundException e) {
System.err.println("Class not found " + classNameAttr.getValue() + " for " + keyAttr.getValue());
// throw new InvalidXMLDataException("Class named '" + classNameAttr.getValue() + "' not found");
}
String value = element.getAttributeValue(XMLMapping.valueLabel);
if (objectType == null || stringEncoder._converterForClass(objectType) == null) {
// No converter, just keep reference of that object for persistance
// (without instanciating it)
// In this case, class matching property is not loaded, and thus
// Object is not instanciated. But, we must keep serialized version
returnedHashtable.put(keyAttr.getValue(), new PropertiesKeyValueProperty.UndecodableProperty(classNameAttr.getValue(),
value));
} else {
Object decodedValue = stringEncoder._decodeObject(value, objectType);
if (decodedValue == null) {
System.err.println("Value for " + value + " is null !");
} else {
returnedHashtable.put(keyAttr.getValue(), decodedValue);
}
}
}
}
return returnedHashtable;
}
private Integer getNextReference() {
nextReference++;
return Integer.valueOf(nextReference);
}
private Element findElementWithId(Document document, String idRef) {
if (idRef == null) {
return null;
}
return findElementWithId(idRef);
/*Iterator it = document.getDescendants(new IDFilter(idRef));
try{
return (Element)it.next();
}catch(NoSuchElementException e){
return null;
}*/
// return (Element)document.getDescendants(new IDFilter(idRef)).next();
// Element returned = findElementWithId(document.getRootElement(), idRef);
// return returned;
}
private Element findElementWithId(Element element, String idRef) {
Element returnedElement = null;
String id = element.getAttributeValue(XMLMapping.idLabel);
if (id != null) {
if (id.equals(idRef)) {
return element;
}
}
List childs = element.getChildren();
for (int i = 0; i < childs.size(); i++) {
Element next = (Element) childs.get(i);
returnedElement = findElementWithId(next, idRef);
if (returnedElement != null) {
return returnedElement;
}
}
return returnedElement;
}
public boolean implementsCustomIdMappingScheme() {
return xmlMapping.implementsCustomIdMappingScheme();
}
}