package org.geotoolkit.util.dom; import org.apache.sis.util.logging.Logging; import org.geotoolkit.util.DomUtilities; import org.w3c.dom.Attr; import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.TypeInfo; import org.w3c.dom.UserDataHandler; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.nio.file.Path; import java.util.logging.Level; import java.util.logging.Logger; /** * Implementation of {@link org.w3c.dom.Element} to lazy load XML file * on element access. * This may be used to reduce memory footprint of {@link javax.imageio.metadata.IIOMetadata} * when metadata tree is merged with an external metadata xml file. * * @author Quentin Boileau (Geomatys) */ public class LazyLoadElement implements Element { private static final Logger LOGGER = Logging.getLogger("org.geotoolkit.util.dom"); private final Path xmlFile; private volatile Element element; private volatile boolean loaded = false; public LazyLoadElement(Path xmlFile) { this.xmlFile = xmlFile; } /** * Use double check synchronization to ensure Element is loaded once by * the first thread using it. * * @return {@link org.w3c.dom.Element} or null if file can't be read or invalid. */ private Element getElement() { if (!loaded) { synchronized (this) { if (!loaded) { try { Document doc = DomUtilities.read(xmlFile); element = doc.getDocumentElement(); } catch (IOException | ParserConfigurationException | SAXException ex) { LOGGER.log(Level.WARNING, "Unable to load file : "+xmlFile, ex); } loaded = true; } } } return element; } /////////////////////////////////////////// // Element method implementation /////////////////////////////////////////// @Override public String getTagName() { final Element elem = getElement(); return elem != null ? elem.getTagName() : null; } @Override public String getAttribute(String name) { final Element elem = getElement(); return elem != null ? elem.getAttribute(name) : null; } @Override public void setAttribute(String name, String value) throws DOMException { final Element elem = getElement(); if (elem != null) elem.setAttribute(name, value); } @Override public void removeAttribute(String name) throws DOMException { final Element elem = getElement(); if (elem != null) elem.removeAttribute(name); } @Override public Attr getAttributeNode(String name) { final Element elem = getElement(); return elem != null ? elem.getAttributeNode(name) : null; } @Override public Attr setAttributeNode(Attr newAttr) throws DOMException { final Element elem = getElement(); return elem != null ? elem.setAttributeNode(newAttr) : null; } @Override public Attr removeAttributeNode(Attr oldAttr) throws DOMException { final Element elem = getElement(); return elem != null ? elem.removeAttributeNode(oldAttr) : null; } @Override public NodeList getElementsByTagName(String name) { final Element elem = getElement(); return elem != null ? elem.getElementsByTagName(name) : null; } @Override public String getAttributeNS(String namespaceURI, String localName) throws DOMException { final Element elem = getElement(); return elem != null ? elem.getAttributeNS(namespaceURI, localName) : null; } @Override public void setAttributeNS(String namespaceURI, String qualifiedName, String value) throws DOMException { final Element elem = getElement(); if (elem != null) elem.setAttributeNS(namespaceURI, qualifiedName, value); } @Override public void removeAttributeNS(String namespaceURI, String localName) throws DOMException { final Element elem = getElement(); if (elem != null) elem.removeAttributeNS(namespaceURI, localName); } @Override public Attr getAttributeNodeNS(String namespaceURI, String localName) throws DOMException { final Element elem = getElement(); return elem != null ? elem.getAttributeNodeNS(namespaceURI, localName) : null; } @Override public Attr setAttributeNodeNS(Attr newAttr) throws DOMException { final Element elem = getElement(); return elem != null ? elem.setAttributeNodeNS(newAttr) : null; } @Override public NodeList getElementsByTagNameNS(String namespaceURI, String localName) throws DOMException { final Element elem = getElement(); return elem != null ? elem.getElementsByTagNameNS(namespaceURI, localName) : null; } @Override public boolean hasAttribute(String name) { final Element elem = getElement(); return elem != null ? elem.hasAttribute(name) : Boolean.FALSE; } @Override public boolean hasAttributeNS(String namespaceURI, String localName) throws DOMException { final Element elem = getElement(); return elem != null ? elem.hasAttributeNS(namespaceURI, localName) : Boolean.FALSE; } @Override public TypeInfo getSchemaTypeInfo() { final Element elem = getElement(); return elem != null ? elem.getSchemaTypeInfo() : null; } @Override public void setIdAttribute(String name, boolean isId) throws DOMException { final Element elem = getElement(); if (elem != null) elem.setIdAttribute(name, isId); } @Override public void setIdAttributeNS(String namespaceURI, String localName, boolean isId) throws DOMException { final Element elem = getElement(); if (elem != null) elem.setIdAttributeNS(namespaceURI, localName, isId); } @Override public void setIdAttributeNode(Attr idAttr, boolean isId) throws DOMException { final Element elem = getElement(); if (elem != null) elem.setIdAttributeNode(idAttr, isId); } /////////////////////////////////////////// // Node method implementation /////////////////////////////////////////// @Override public String getNodeName() { final Element elem = getElement(); return elem != null ? elem.getNodeName() : null; } @Override public String getNodeValue() throws DOMException { final Element elem = getElement(); return elem != null ? elem.getNodeValue() : null; } @Override public void setNodeValue(String nodeValue) throws DOMException { final Element elem = getElement(); if (elem != null) elem.setNodeValue(nodeValue); } @Override public short getNodeType() { final Element elem = getElement(); return elem != null ? elem.getNodeType() : Node.ELEMENT_NODE; } @Override public Node getParentNode() { final Element elem = getElement(); return elem != null ? elem.getParentNode() : null; } @Override public NodeList getChildNodes() { final Element elem = getElement(); return elem != null ? elem.getChildNodes() : null; } @Override public Node getFirstChild() { final Element elem = getElement(); return elem != null ? elem.getFirstChild() : null; } @Override public Node getLastChild() { final Element elem = getElement(); return elem != null ? elem.getLastChild() : null; } @Override public Node getPreviousSibling() { final Element elem = getElement(); return elem != null ? elem.getPreviousSibling() : null; } @Override public Node getNextSibling() { final Element elem = getElement(); return elem != null ? elem.getNextSibling() : null; } @Override public NamedNodeMap getAttributes() { final Element elem = getElement(); return elem != null ? elem.getAttributes() : null; } @Override public Document getOwnerDocument() { final Element elem = getElement(); return elem != null ? elem.getOwnerDocument() : null; } @Override public Node insertBefore(Node newChild, Node refChild) throws DOMException { final Element elem = getElement(); return elem != null ? elem.insertBefore(newChild, refChild) : null; } @Override public Node replaceChild(Node newChild, Node oldChild) throws DOMException { final Element elem = getElement(); return elem != null ? elem.replaceChild(newChild, oldChild) : null; } @Override public Node removeChild(Node oldChild) throws DOMException { final Element elem = getElement(); return elem != null ? elem.removeChild(oldChild) : null; } @Override public Node appendChild(Node newChild) throws DOMException { final Element elem = getElement(); return elem != null ? elem.appendChild(newChild) : null; } @Override public boolean hasChildNodes() { final Element elem = getElement(); return elem != null ? elem.hasChildNodes() : Boolean.FALSE; } @Override public Node cloneNode(boolean deep) { final Element elem = getElement(); return elem != null ? elem.cloneNode(deep) : null; } @Override public void normalize() { final Element elem = getElement(); if (elem != null) elem.normalize(); } @Override public boolean isSupported(String feature, String version) { final Element elem = getElement(); return elem != null ? elem.isSupported(feature, version) : Boolean.FALSE; } @Override public String getNamespaceURI() { final Element elem = getElement(); return elem != null ? elem.getNamespaceURI() : null; } @Override public String getPrefix() { final Element elem = getElement(); return elem != null ? elem.getPrefix() : null; } @Override public void setPrefix(String prefix) throws DOMException { final Element elem = getElement(); if (elem != null) elem.setPrefix(prefix); } @Override public String getLocalName() { final Element elem = getElement(); return elem != null ? elem.getLocalName() : null; } @Override public boolean hasAttributes() { final Element elem = getElement(); return elem != null ? elem.hasAttributes() : Boolean.FALSE; } @Override public String getBaseURI() { final Element elem = getElement(); return elem != null ? elem.getBaseURI() : null; } @Override public short compareDocumentPosition(Node other) throws DOMException { final Element elem = getElement(); return elem != null ? elem.compareDocumentPosition(other) : 0; } @Override public String getTextContent() throws DOMException { final Element elem = getElement(); return elem != null ? elem.getTextContent() : null; } @Override public void setTextContent(String textContent) throws DOMException { final Element elem = getElement(); if (elem != null) elem.setTextContent(textContent); } @Override public boolean isSameNode(Node other) { final Element elem = getElement(); return elem != null ? elem.isSameNode(other) : Boolean.FALSE; } @Override public String lookupPrefix(String namespaceURI) { final Element elem = getElement(); return elem != null ? elem.lookupPrefix(namespaceURI) : null; } @Override public boolean isDefaultNamespace(String namespaceURI) { final Element elem = getElement(); return elem != null ? elem.isDefaultNamespace(namespaceURI) : Boolean.FALSE; } @Override public String lookupNamespaceURI(String prefix) { final Element elem = getElement(); return elem != null ? elem.lookupNamespaceURI(prefix) : null; } @Override public boolean isEqualNode(Node arg) { final Element elem = getElement(); return elem != null ? elem.isEqualNode(arg) : Boolean.FALSE; } @Override public Object getFeature(String feature, String version) { final Element elem = getElement(); return elem != null ? elem.getFeature(feature, version) : null; } @Override public Object setUserData(String key, Object data, UserDataHandler handler) { final Element elem = getElement(); return elem != null ? elem.setUserData(key, data, handler) : null; } @Override public Object getUserData(String key) { final Element elem = getElement(); return elem != null ? elem.getUserData(key) : null; } }