/* * eXist Open Source Native XML Database * Copyright (C) 2008-2009 The eXist Project * http://exist-db.org * * This program 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; either version 2 * of the License, or (at your option) any later version. * * This program 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. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id$ */ package org.exist.xslt.compiler; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Map; import org.exist.interpreter.ContextAtExist; import org.exist.numbering.NodeId; import org.exist.xquery.AttributeConstructor; import org.exist.xquery.CDATAConstructor; import org.exist.xquery.CommentConstructor; import org.exist.xquery.ElementConstructor; import org.exist.xquery.Expression; import org.exist.xquery.NodeConstructor; import org.exist.xquery.NodeTest; import org.exist.xquery.PathExpr; import org.exist.xquery.TextConstructor; import org.exist.xquery.XPathException; import org.exist.xquery.XQueryContext; import org.exist.dom.NodeListImpl; import org.exist.dom.QName; import org.exist.dom.DocumentAtExist; import org.exist.dom.ElementAtExist; import org.exist.dom.NodeAtExist; import org.exist.xslt.XSLContext; import org.exist.xslt.XSLStylesheet; import org.exist.xslt.expression.XSLExpression; import org.exist.xslt.expression.XSLPathExpr; import org.w3c.dom.Attr; import org.w3c.dom.DOMException; import org.w3c.dom.Document; 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; /** * @author <a href="mailto:shabanovd@gmail.com">Dmitriy Shabanov</a> * */ public class XSLElement implements ElementAtExist, Names { protected XSLPathExpr expr = null; protected ElementAtExist element; public XSLElement(ElementAtExist element) { this.element = element; } // protected XQueryContext getContext() { // return (XQueryContext) getDocument().getContext(); // } // @SuppressWarnings("unchecked") protected XSLPathExpr getExpressionInstance(ContextAtExist context) throws XPathException { if (expr != null) return expr; if (getQName() == null) //TODO: remove System.out.println(getQName()); Class<XSLPathExpr> clazz = Factory.qns.get(getQName()); try { Constructor<XSLPathExpr> constructor = clazz.getConstructor(XSLContext.class); expr = constructor.newInstance(context); return expr; } catch (SecurityException e) { throw new XPathException("SecurityException",e); } catch (NoSuchMethodException e) { throw new XPathException("NoSuchMethodException",e); } catch (IllegalArgumentException e) { throw new XPathException("IllegalArgumentException",e); } catch (InstantiationException e) { throw new XPathException("InstantiationException",e); } catch (IllegalAccessException e) { throw new XPathException("IllegalAccessException",e); } catch (InvocationTargetException e) { throw new XPathException("InvocationTargetException",e); } } public Expression compile(ContextAtExist context) throws XPathException { XSLPathExpr exec; if ((!isXSLElement(this)) && (isParentNode())) { //UNDERSTAND: put to XSLStylesheet? expr = new XSLStylesheet((XSLContext) context, true); NodeConstructor constructer = getNodeConstructor(context, this, expr); if (constructer != null) { expr.add(constructer); compileNode(context, expr, this); } exec = expr; } else { preprocess(context); exec = getExpressionInstance(context); compileNode(context, (PathExpr) exec, this); } exec.validate();//UNDERSTAND: at compile time??? analyze ??? return exec; } private NodeConstructor getNodeConstructor(ContextAtExist context, NodeAtExist node, PathExpr content) throws XPathException { NodeConstructor constructer = null; if (node.getNodeType() == Node.ELEMENT_NODE) { ElementConstructor elementConstructer = new ElementConstructor((XQueryContext) context, node.getNodeName()); content.add(elementConstructer); PathExpr content_sub = new PathExpr((XQueryContext) context); elementConstructer.setContent(content_sub); NamedNodeMap attrs = node.getAttributes(); for (int index = 0; index < attrs.getLength(); index++) { Node attr = attrs.item(index); if (("xmlns:".equals(attr.getNodeName())) && ("".equals(attr.getLocalName()))) { //getNodeValue continue;//UNDERSTAND: is it required, to have empty namespace } if (isParentNode()) { if ("xmlns:xsl".equals(attr.getNodeName())) { continue; } else if (attr.getNodeName().startsWith("xsl")) { continue; } } AttributeConstructor attributeConstructer = new AttributeConstructor((XQueryContext) context, attr.getNodeName()); //XXX: rethinks String value = attr.getNodeValue(); if (value.contains("{")) { value = value.replace("{", ""); value = value.replace("}", ""); PathExpr expr = new PathExpr((XQueryContext) context); org.exist.xslt.pattern.Pattern.parse(context, value, expr); attributeConstructer.addEnclosedExpr(expr); } else { attributeConstructer.addValue(value); } elementConstructer.addAttribute(attributeConstructer); } compileNode(context, content_sub, node); } else if (node.getNodeType() == Node.COMMENT_NODE) { constructer = new CommentConstructor((XQueryContext) context, node.getNodeName()); } else if (node.getNodeType() == Node.TEXT_NODE) { if (content instanceof XSLPathExpr) { XSLPathExpr xslExpr = (XSLPathExpr) content; xslExpr.addText(node.getNodeValue()); } else constructer = new TextConstructor((XQueryContext) context, node.getNodeValue()); } else if (node.getNodeType() == Node.CDATA_SECTION_NODE) { constructer = new CDATAConstructor((XQueryContext) context, node.getNodeName()); } else { throw new XPathException("not supported node type: "+node.getNodeType()); // ATTRIBUTE_NODE = 2; // CDATA_SECTION_NODE = 4; // ENTITY_REFERENCE_NODE = 5; // ENTITY_NODE = 6; // PROCESSING_INSTRUCTION_NODE = 7; // DOCUMENT_NODE = 9; // DOCUMENT_TYPE_NODE = 10; // DOCUMENT_FRAGMENT_NODE = 11; // NOTATION_NODE = 12; } return constructer; } public void compileNode(ContextAtExist context, PathExpr content, Node node) throws XPathException { //namespaces if (node instanceof ElementAtExist) { ElementAtExist elementAtExist = (ElementAtExist) node; Map<String, String> namespaceMap = elementAtExist.getNamespaceMap(); for (String name : namespaceMap.keySet()) { //getContext().declareInScopeNamespace(name, namespaceMap.get(name)); context.declareNamespace(name, namespaceMap.get(name)); //TODO: rewrite, changes at xquery.parser. it use static } } if (!node.hasChildNodes()) return; NodeConstructor constructer = null; NodeList children = node.getChildNodes(); for (int i=0; i<children.getLength(); i++) { constructer = null; NodeAtExist child = (NodeAtExist)children.item(i); if (isXSLElement(child)) { XSLElement xslElement = (XSLElement) child; content.add(xslElement.compile(context)); } else { constructer = getNodeConstructor(context, child, content); } if (constructer != null) { content.add(constructer); compileNode(context, content, child); } } } protected void prepareAttributes(ContextAtExist context) throws XPathException { XSLExpression exec = getExpressionInstance(context); NamedNodeMap attrs = getAttributes(); for (int i = 0; i < attrs.getLength(); i++) exec.prepareAttribute(null, (Attr)attrs.item(i)); } protected void preprocess(ContextAtExist context) throws XPathException { // XSLPathExpr exec = getExpressionInstance(); prepareAttributes(context); preprocessNode(context, this); } protected void preprocessNode(ContextAtExist context, Node node) throws XPathException { if (!node.hasChildNodes()) return; NodeList children = node.getChildNodes(); for (int i=0; i<children.getLength(); i++) { NodeAtExist child = (NodeAtExist)children.item(i); if (isXSLElement(child)) { XSLElement xslElement = (XSLElement) child; xslElement.preprocess(context); } else { preprocessNode(context, child); } } } private boolean isXSLElement(NodeAtExist child) { return Factory.qns.containsKey(child.getQName()); } /* (non-Javadoc) * @see org.exist.dom.i.NodeAtExist#getDocument() */ public DocumentAtExist getDocument() { return element.getDocument(); } /* (non-Javadoc) * @see org.w3c.dom.Node#appendChild(org.w3c.dom.Node) */ public Node appendChild(Node newChild) throws DOMException { return element.appendChild(newChild); } /* (non-Javadoc) * @see org.w3c.dom.Node#cloneNode(boolean) */ public Node cloneNode(boolean deep) { return element.cloneNode(deep); } /* (non-Javadoc) * @see org.w3c.dom.Node#compareDocumentPosition(org.w3c.dom.Node) */ public short compareDocumentPosition(Node other) throws DOMException { return element.compareDocumentPosition(other); } /* (non-Javadoc) * @see org.w3c.dom.Node#getAttributes() */ public NamedNodeMap getAttributes() { return element.getAttributes(); } /* (non-Javadoc) * @see org.w3c.dom.Node#getBaseURI() */ public String getBaseURI() { return element.getBaseURI(); } NodeListImpl nl; //TODO: handle changes some how /* (non-Javadoc) * @see org.w3c.dom.Node#getChildNodes() */ public NodeList getChildNodes() { if (nl != null) return nl; DocumentAtExist document = getDocument(); nl = new NodeListImpl(); int nextNode = document.getFirstChildFor(getNodeNumber()); while (nextNode > getNodeNumber()) { NodeAtExist n = document.getNode(nextNode); if (n instanceof ElementAtExist) { n = new XSLElement((ElementAtExist) n); } nl.add(n); nextNode = document.getNextNodeNumber(nextNode); } return nl; } /* (non-Javadoc) * @see org.w3c.dom.Node#getFeature(java.lang.String, java.lang.String) */ public Object getFeature(String feature, String version) { return element.getFeature(feature, version); } /* (non-Javadoc) * @see org.w3c.dom.Node#getFirstChild() */ public Node getFirstChild() { return element.getFirstChild(); } /* (non-Javadoc) * @see org.w3c.dom.Node#getLastChild() */ public Node getLastChild() { return element.getLastChild(); } /* (non-Javadoc) * @see org.w3c.dom.Node#getLocalName() */ public String getLocalName() { return element.getLocalName(); } /* (non-Javadoc) * @see org.w3c.dom.Node#getNamespaceURI() */ public String getNamespaceURI() { return element.getNamespaceURI(); } /* (non-Javadoc) * @see org.w3c.dom.Node#getNextSibling() */ public Node getNextSibling() { return element.getNextSibling(); } /* (non-Javadoc) * @see org.w3c.dom.Node#getNodeName() */ public String getNodeName() { return element.getNodeName(); } /* (non-Javadoc) * @see org.w3c.dom.Node#getNodeType() */ public short getNodeType() { return element.getNodeType(); } /* (non-Javadoc) * @see org.w3c.dom.Node#getNodeValue() */ public String getNodeValue() throws DOMException { return element.getNodeValue(); } /* (non-Javadoc) * @see org.w3c.dom.Node#getOwnerDocument() */ public Document getOwnerDocument() { return element.getOwnerDocument(); } /* (non-Javadoc) * @see org.w3c.dom.Node#getParentNode() */ public Node getParentNode() { return element.getParentNode(); } public boolean isParentNode() { return (element.getNodeId().getTreeLevel() == 1); } /* (non-Javadoc) * @see org.w3c.dom.Node#getPrefix() */ public String getPrefix() { return element.getPrefix(); } /* (non-Javadoc) * @see org.w3c.dom.Node#getPreviousSibling() */ public Node getPreviousSibling() { return element.getPreviousSibling(); } /* (non-Javadoc) * @see org.w3c.dom.Node#getTextContent() */ public String getTextContent() throws DOMException { return element.getTextContent(); } /* (non-Javadoc) * @see org.w3c.dom.Node#getUserData(java.lang.String) */ public Object getUserData(String key) { return element.getUserData(key); } /* (non-Javadoc) * @see org.w3c.dom.Node#hasAttributes() */ public boolean hasAttributes() { return element.hasAttributes(); } /* (non-Javadoc) * @see org.w3c.dom.Node#hasChildNodes() */ public boolean hasChildNodes() { return element.hasChildNodes(); } /* (non-Javadoc) * @see org.w3c.dom.Node#insertBefore(org.w3c.dom.Node, org.w3c.dom.Node) */ public Node insertBefore(Node newChild, Node refChild) throws DOMException { return element.insertBefore(newChild, refChild); } /* (non-Javadoc) * @see org.w3c.dom.Node#isDefaultNamespace(java.lang.String) */ public boolean isDefaultNamespace(String namespaceURI) { return element.isDefaultNamespace(namespaceURI); } /* (non-Javadoc) * @see org.w3c.dom.Node#isEqualNode(org.w3c.dom.Node) */ public boolean isEqualNode(Node arg) { return element.isEqualNode(arg); } /* (non-Javadoc) * @see org.w3c.dom.Node#isSameNode(org.w3c.dom.Node) */ public boolean isSameNode(Node other) { return element.isSameNode(other); } /* (non-Javadoc) * @see org.w3c.dom.Node#isSupported(java.lang.String, java.lang.String) */ public boolean isSupported(String feature, String version) { return element.isSupported(feature, version); } /* (non-Javadoc) * @see org.w3c.dom.Node#lookupNamespaceURI(java.lang.String) */ public String lookupNamespaceURI(String prefix) { return element.lookupNamespaceURI(prefix); } /* (non-Javadoc) * @see org.w3c.dom.Node#lookupPrefix(java.lang.String) */ public String lookupPrefix(String namespaceURI) { return element.lookupPrefix(namespaceURI); } /* (non-Javadoc) * @see org.w3c.dom.Node#normalize() */ public void normalize() { element.normalize(); } /* (non-Javadoc) * @see org.w3c.dom.Node#removeChild(org.w3c.dom.Node) */ public Node removeChild(Node oldChild) throws DOMException { return element.removeChild(oldChild); } /* (non-Javadoc) * @see org.w3c.dom.Node#replaceChild(org.w3c.dom.Node, org.w3c.dom.Node) */ public Node replaceChild(Node newChild, Node oldChild) throws DOMException { return element.replaceChild(newChild, oldChild); } /* (non-Javadoc) * @see org.w3c.dom.Node#setNodeValue(java.lang.String) */ public void setNodeValue(String nodeValue) throws DOMException { element.setNodeValue(nodeValue); } /* (non-Javadoc) * @see org.w3c.dom.Node#setPrefix(java.lang.String) */ public void setPrefix(String prefix) throws DOMException { element.setPrefix(prefix); } /* (non-Javadoc) * @see org.w3c.dom.Node#setTextContent(java.lang.String) */ public void setTextContent(String textContent) throws DOMException { element.setTextContent(textContent); } /* (non-Javadoc) * @see org.w3c.dom.Node#setUserData(java.lang.String, java.lang.Object, org.w3c.dom.UserDataHandler) */ public Object setUserData(String key, Object data, UserDataHandler handler) { return element.setUserData(key, data, handler); } /* (non-Javadoc) * @see org.w3c.dom.Element#getAttribute(java.lang.String) */ public String getAttribute(String name) { return element.getAttribute(name); } /* (non-Javadoc) * @see org.w3c.dom.Element#getAttributeNS(java.lang.String, java.lang.String) */ public String getAttributeNS(String namespaceURI, String localName) throws DOMException { return element.getAttributeNS(namespaceURI, localName); } /* (non-Javadoc) * @see org.w3c.dom.Element#getAttributeNode(java.lang.String) */ public Attr getAttributeNode(String name) { return element.getAttributeNode(name); } /* (non-Javadoc) * @see org.w3c.dom.Element#getAttributeNodeNS(java.lang.String, java.lang.String) */ public Attr getAttributeNodeNS(String namespaceURI, String localName) throws DOMException { return element.getAttributeNodeNS(namespaceURI, localName); } /* (non-Javadoc) * @see org.w3c.dom.Element#getElementsByTagName(java.lang.String) */ public NodeList getElementsByTagName(String name) { return element.getElementsByTagName(name); } /* (non-Javadoc) * @see org.w3c.dom.Element#getElementsByTagNameNS(java.lang.String, java.lang.String) */ public NodeList getElementsByTagNameNS(String namespaceURI, String localName) throws DOMException { return element.getElementsByTagNameNS(namespaceURI, localName); } /* (non-Javadoc) * @see org.w3c.dom.Element#getSchemaTypeInfo() */ public TypeInfo getSchemaTypeInfo() { return element.getSchemaTypeInfo(); } /* (non-Javadoc) * @see org.w3c.dom.Element#getTagName() */ public String getTagName() { return element.getTagName(); } /* (non-Javadoc) * @see org.w3c.dom.Element#hasAttribute(java.lang.String) */ public boolean hasAttribute(String name) { return element.hasAttribute(name); } /* (non-Javadoc) * @see org.w3c.dom.Element#hasAttributeNS(java.lang.String, java.lang.String) */ public boolean hasAttributeNS(String namespaceURI, String localName) throws DOMException { return element.hasAttributeNS(namespaceURI, localName); } /* (non-Javadoc) * @see org.w3c.dom.Element#removeAttribute(java.lang.String) */ public void removeAttribute(String name) throws DOMException { element.removeAttribute(name); } /* (non-Javadoc) * @see org.w3c.dom.Element#removeAttributeNS(java.lang.String, java.lang.String) */ public void removeAttributeNS(String namespaceURI, String localName) throws DOMException { element.removeAttributeNS(namespaceURI, localName); } /* (non-Javadoc) * @see org.w3c.dom.Element#removeAttributeNode(org.w3c.dom.Attr) */ public Attr removeAttributeNode(Attr oldAttr) throws DOMException { return element.removeAttributeNode(oldAttr); } /* (non-Javadoc) * @see org.w3c.dom.Element#setAttribute(java.lang.String, java.lang.String) */ public void setAttribute(String name, String value) throws DOMException { element.setAttribute(name, value); } /* (non-Javadoc) * @see org.w3c.dom.Element#setAttributeNS(java.lang.String, java.lang.String, java.lang.String) */ public void setAttributeNS(String namespaceURI, String qualifiedName, String value) throws DOMException { element.setAttributeNS(namespaceURI, qualifiedName, value); } /* (non-Javadoc) * @see org.w3c.dom.Element#setAttributeNode(org.w3c.dom.Attr) */ public Attr setAttributeNode(Attr newAttr) throws DOMException { return element.setAttributeNode(newAttr); } /* (non-Javadoc) * @see org.w3c.dom.Element#setAttributeNodeNS(org.w3c.dom.Attr) */ public Attr setAttributeNodeNS(Attr newAttr) throws DOMException { return element.setAttributeNodeNS(newAttr); } /* (non-Javadoc) * @see org.w3c.dom.Element#setIdAttribute(java.lang.String, boolean) */ public void setIdAttribute(String name, boolean isId) throws DOMException { element.setIdAttribute(name, isId); } /* (non-Javadoc) * @see org.w3c.dom.Element#setIdAttributeNS(java.lang.String, java.lang.String, boolean) */ public void setIdAttributeNS(String namespaceURI, String localName, boolean isId) throws DOMException { element.setIdAttributeNS(namespaceURI, localName, isId); } /* (non-Javadoc) * @see org.w3c.dom.Element#setIdAttributeNode(org.w3c.dom.Attr, boolean) */ public void setIdAttributeNode(Attr idAttr, boolean isId) throws DOMException { element.setIdAttributeNode(idAttr, isId); } /* (non-Javadoc) * @see org.exist.dom.QNameable#getQName() */ public QName getQName() { return element.getQName(); } /* (non-Javadoc) * @see org.exist.dom.i.NodeAtExist#getNodeNumber() */ public int getNodeNumber() { return element.getNodeNumber(); } /* (non-Javadoc) * @see java.lang.Comparable#compareTo(java.lang.Object) */ public int compareTo(Object o) { return element.compareTo(o); } // /* (non-Javadoc) // * @see org.exist.dom.i.NodeAtExist#matchChildren(org.exist.xquery.NodeTest) // */ // public Boolean matchChildren(NodeTest test) throws XPathException { // return element.matchChildren(test); // } public String toString() { return element.toString(); } /* (non-Javadoc) * @see org.exist.dom.i.ElementAtExist#getNamespaceMap() */ public Map<String, String> getNamespaceMap() { return element.getNamespaceMap(); } /* (non-Javadoc) * @see org.exist.dom.i.NodeAtExist#getNodeId() */ public NodeId getNodeId() { return element.getNodeId(); } }