package org.nexml.model.impl; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.nexml.model.NexmlWritable; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; /** * Base DOM implementation of {@code NexmlWritable}. */ abstract class NexmlWritableImpl implements NexmlWritable { private Document mDocument = null; private Element mElement; private static long objectCounter = 0; private static Map<String,Boolean> seenIdStrings = new HashMap<String,Boolean>(); protected static String XSI_URI = javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI; protected static String XMLNS_URI = javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI; protected static String XS_URI = javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; protected static String RDF_URI = "http://www.w3.org/1999/02/22-rdf-syntax-ns"; protected static String XMLNS_PRE = javax.xml.XMLConstants.XMLNS_ATTRIBUTE; protected static String XSI_PRE = "xsi"; protected static String XSD_PRE = "xsd"; protected static String NEX_PRE = "nex"; protected static String RDF_PRE = "rdf"; protected static String XSI_TYPE = XSI_PRE + ":type"; private Map<String,String> prefixForNs = new HashMap<String,String>(); private Map<String, String> nsForPrefix = new HashMap<String,String>(); private URI mBaseURI; /** Default constructor. */ protected NexmlWritableImpl() { } /** * This constructor is intended for situations where we create a document * from scratch: the DocumentFactory will pass in a DOM Document object, and * subsequently created objects will all carry around a reference to it so * that they can instantiate Element objects from the right Document object * and insert them in the right location in the element tree. @author rvosa * * @param document */ protected NexmlWritableImpl(Document document) { mDocument = document; mElement = document.createElementNS(DEFAULT_NAMESPACE, getTagName()); identify(mElement); } /** * Generates and attaches an id attribute to the provided element. * The id is intended to be unique within document scope. * * @param element */ protected String identify(Element element,boolean setIdAttribute) { String prefix = element.getTagName(); String id = prefix + objectCounter; while ( seenIdStrings.containsKey(id) ) { objectCounter++; id = prefix + objectCounter; } if ( setIdAttribute ) { element.setAttribute("id",id); } seenIdStrings.put(id,new Boolean(true)); objectCounter++; return id; } protected String identify(Element element) { return identify(element,true); } /** * Protected constructors are intended for recursive parsing, i.e. * starting from the root element (which maps onto DocumentImpl) we * traverse the element tree such that for every child element that maps * onto an Impl class the containing class calls that child's protected * constructor, passes in the element of the child. From there the * child takes over, populates itself and calls the protected * constructors of its children. These should probably be protected * because there is all sorts of opportunity for outsiders to call * these in the wrong context, passing in the wrong elements etc. * @param document the containing DOM document object. Every Impl * class needs a reference to this so that it can create DOM element * objects * @param element the equivalent NeXML element (e.g. for OTUsImpl, it's * the <otus/> element) * @author rvosa */ protected NexmlWritableImpl(Document document,Element element) { mDocument = document; mElement = element; seenIdStrings.put(getId(),new Boolean(true)); } /** * Get the root DOM document of this {@code NexmlWritable}. * * @return the root DOM document of this {@code NexmlWritable}. */ /** * The DOM document object here is typically used for creating * new Element objects. * @return A DOM document object */ protected final Document getDocument() { return mDocument; } /** * Get all child elements of {@code element} named {@tagName}. * * @param element see description. * @param tagName see description. * @return all child elements of {@code element} named {@tagName}. */ protected List<Element> getChildrenByTagName(Element element, String tagName) { List<Element> result = new ArrayList<Element>(); NodeList children = element.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { String localName = children.item(i).getNodeName(); if (null != localName && localName.equals(tagName)) { result.add((Element) children.item(i)); } } return result; } /** * Get the wrapped DOM element of this {@code NexmlWritable}. * * @return the wrapped DOM element of this {@code NexmlWritable}. */ /** * This returns the equivalent NeXML element for the * invocant object. * @return a DOM Element object */ protected final Element getElement() { return mElement; } /** * Attaches an attribute node to the current element, * such that name="value" * @param name * @param value */ protected void setAttribute(String name,String value) { getElement().setAttribute(name, value); } /** * Getter. * * @return the label. */ /* * (non-Javadoc) * @see org.nexml.model.NexmlWritable#getLabel() */ public String getLabel() { return getElement().getAttribute("label"); } /* * (non-Javadoc) * @see org.nexml.model.NexmlWritable#setLabel(java.lang.String) */ public void setLabel(String label) { getElement().setAttribute("label", label); } /* * (non-Javadoc) * @see org.nexml.model.NexmlWritable#getId() */ public String getId() { return getElement().getAttribute("id"); } /* * (non-Javadoc) * @see org.nexml.model.NexmlWritable#setId(java.lang.String) */ public void setId(String id) { getElement().setAttribute("id", id); } /** * This method returns the NeXML element name that the * {@code NexmlWritable} object is equivalent to. * @return the (XML) tag name of this {@code NexmlWritable}. */ abstract String getTagName(); protected void setNameSpace(Element element,String prefix, String nameSpaceURI) { setNameSpace(element, prefix, nameSpaceURI, true); } protected void setNameSpace(Element element,String prefix, String nameSpaceURI, boolean optimize) { if ( null == prefix || null == nameSpaceURI ) { return; } if ( optimize ) { if ( nsForPrefix.containsKey(prefix) && prefixForNs.containsKey(nameSpaceURI) ) { if ( prefixForNs.get(nameSpaceURI).equals(prefix) && nsForPrefix.get(prefix).equals(nameSpaceURI) ) { // here the method call simply re-affirms an existing namespace/prefix mapping return; // do nothing } else { // either the namespace of the prefix are bound to something else // create a new NS statement on the argument node element.setAttributeNS( XMLNS_URI, XMLNS_PRE + ":" + prefix, nameSpaceURI ); } } else if ( ! nsForPrefix.containsKey(prefix) && ! prefixForNs.containsKey(nameSpaceURI) ) { // neither have been seen before, create de novo on the root element nsForPrefix.put(prefix, nameSpaceURI); prefixForNs.put(nameSpaceURI, prefix); while ( element.getParentNode() != null ) { element = (Element) element.getParentNode(); } element.setAttributeNS( XMLNS_URI, XMLNS_PRE + ":" + prefix, nameSpaceURI ); } else { element.setAttributeNS( XMLNS_URI, XMLNS_PRE + ":" + prefix, nameSpaceURI ); } } else { element.setAttributeNS( XMLNS_URI, XMLNS_PRE + ":" + prefix, nameSpaceURI ); } } public URI getBaseURI() { if ( null == mBaseURI ) { Element element = getElement(); while ( element != null ) { String baseURIString = element.getAttribute("xml:base"); if ( baseURIString != null && baseURIString.length() > 0 ) { try { mBaseURI = new URI(baseURIString); } catch (URISyntaxException e) { // TODO Auto-generated catch block e.printStackTrace(); } break; } element = (Element) element.getParentNode(); } } return mBaseURI; } public void setBaseURI(URI baseURI) { mBaseURI = baseURI; getElement().setAttribute("xml:base",baseURI.toString()); } }