/* * Javolution - Java(TM) Solution for Real-Time and Embedded Systems * Copyright (C) 2012 - Javolution (http://javolution.org/) * All rights reserved. * * Permission to use, copy, modify, and distribute this software is * freely granted, provided that this notice is preserved. */ package javolution.xml; import javolution.text.CharArray; import javolution.text.TextBuilder; import javolution.util.FastMap; import javolution.util.FastTable; import javolution.util.Index; import javolution.util.function.Equalities; import javolution.xml.stream.XMLStreamException; /** * <p> This class represents a resolver for XML cross references during * the marshalling/unmarshalling process.</p> * * <p> Instances of this class may only be shared by {@link XMLObjectReader}/ * {@link XMLObjectWriter} running sequentially (for cross references * spawning multiple documents).</p> * * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> * @version 4.0, September 4, 2006 */ public class XMLReferenceResolver { /** * Holds object to identifier (FastTable.Index) mapping. */ private FastMap<Object, Index> _objectToId = new FastMap<Object, Index>( Equalities.IDENTITY); /** * Holds the objects (index to object mapping). */ private FastTable<Object> _idToObject = new FastTable<Object>(); /** * Holds the id counter. */ private int _counter; /** * Holds the identifier attribute name. */ private String _idName = "id"; /** * Holds the identifier attribute URI if any. */ private String _idURI = null; /** * Holds the reference attribute name. */ private String _refName = "ref"; /** * Holds the reference attribute URI if any. */ private String _refURI = null; /** * Default constructor. */ public XMLReferenceResolver() {} /** * Sets the name of the identifier attribute (by default<code>"id"</code>). * If the name is <code>null</code> then the identifier attribute * is never read/written (which may prevent unmarshalling). * * @param name the name of the attribute or <code>null</code>. */ public void setIdentifierAttribute(String name) { setIdentifierAttribute(name, null); } /** * Sets the local name and namespace URI of the identifier attribute. * * @param localName the local name of the attribute or <code>null</code>. * @param uri the URI of the attribute or <code>null</code> if the attribute * has no namespace URI. */ public void setIdentifierAttribute(String localName, String uri) { _idName = localName; _idURI = uri; } /** * Sets the name of the reference attribute (by default<code>"ref"</code>). * If the name is <code>null</code> then the reference attribute * is never read/written (which may prevent unmarshalling). * * @param name the name of the attribute or <code>null</code>. */ public void setReferenceAttribute(String name) { setReferenceAttribute(name, null); } /** * Sets the local name and namespace URI of the identifier attribute. * * @param localName the local name of the attribute or <code>null</code>. * @param uri the URI of the attribute or <code>null</code> if the attribute * has no namespace URI. */ public void setReferenceAttribute(String localName, String uri) { _refName = localName; _refURI = uri; } /** * Writes a reference to the specified object into the specified XML * element. The default implementation writes the reference into the * reference attribute and for the first occurences an identifier * (counter starting at 1) is written into the identifier attribute. * * @param obj the object for which the reference is written. * @param xml the output XML element. * @return <code>true</code> if a reference is written; * <code>false</code> if a new identifier is written. */ public boolean writeReference(Object obj, XMLFormat.OutputElement xml) throws XMLStreamException { Index id = (Index) _objectToId.get(obj); if (id == null) { // New identifier. id = Index.of(_counter++); _objectToId.put(obj, id); _tmp.clear().append(id.intValue()); if (_idURI == null) { xml.getStreamWriter().writeAttribute(_idName, _tmp); } else { xml.getStreamWriter().writeAttribute(_idURI, _idName, _tmp); } return false; } _tmp.clear().append(id.intValue()); if (_refURI == null) { xml._writer.writeAttribute(_refName, _tmp); } else { xml._writer.writeAttribute(_refURI, _refName, _tmp); } return true; } private TextBuilder _tmp = new TextBuilder(); /** * Reads the object referenced by the specified xml input element if any. * The default implementation reads the reference attribute to retrieve * the object. * * @param xml the input XML element. * @return the referenced object or <code>null</code> if the specified * XML input does not have a reference attribute. */ public Object readReference(XMLFormat.InputElement xml) throws XMLStreamException { CharArray value = xml._reader.getAttributeValue(_refURI, _refName); if (value == null) return null; int ref = value.toInt(); if (ref >= _idToObject.size()) throw new XMLStreamException("Reference: " + value + " not found"); return _idToObject.get(ref); } /** * Creates a reference for the specified object (the identifier * being specified by the input XML element). * The default implementation reads the identifier attribute (if any) * and associates it to the specified object. * * @param obj the object being referenced. * @param xml the input XML element holding the reference identifier. */ public void createReference(Object obj, XMLFormat.InputElement xml) throws XMLStreamException { CharArray value = xml._reader.getAttributeValue(_idURI, _idName); if (value == null) return; int i = value.toInt(); if (_idToObject.size() != i) throw new XMLStreamException("Identifier discontinuity detected " + "(expected " + _idToObject.size() + " found " + i + ")"); _idToObject.add(obj); } public void reset() { _idName = "id"; _idURI = null; _refName = "ref"; _refURI = null; _idToObject.clear(); _objectToId.clear(); _counter = 0; } }