/* * eXist EXPath * Copyright (C) 2011 Adam Retter <adam@existsolutions.com> * www.existsolutions.com * * 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.expath.httpclient.model.exist; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import org.exist.xquery.XPathException; import org.exist.xquery.XQueryContext; import org.exist.xquery.value.NodeValue; import org.exist.xquery.value.ValueSequence; import org.expath.httpclient.HttpClientException; import org.expath.httpclient.HttpConstants; import org.expath.httpclient.model.Attribute; import org.expath.httpclient.model.Element; import org.expath.httpclient.model.Sequence; import org.w3c.dom.Attr; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * @author Adam Retter <adam@existsolutions.com> */ public class EXistElement implements Element { private final NodeValue element; private final XQueryContext context; public EXistElement(NodeValue element, XQueryContext context) { this.element = element; this.context = context; } //@Override public Iterable<Attribute> attributes() throws HttpClientException { return new Iterable<Attribute>() { //@Override public Iterator<Attribute> iterator() { return new Iterator<Attribute>() { private final NamedNodeMap attrs = element.getNode().getAttributes(); private final int length = attrs.getLength(); private int position = 0; //@Override public boolean hasNext() { return(position < length); } //@Override public Attribute next() { if(position >= length){ throw new NoSuchElementException(); } return new EXistAttribute((Attr)attrs.item(position++)); } //@Override public void remove() { throw new UnsupportedOperationException("Not supported yet."); } }; } }; } //@Override public Iterable<Element> children() throws HttpClientException { final Node node = element.getNode(); return new IterableElement(node); } //@Override public String getAttribute(String local_name) throws HttpClientException { String attrValue = null; NamedNodeMap attrs = element.getNode().getAttributes(); Node attr = attrs.getNamedItem(local_name); if(attr != null) { attrValue = ((Attr)attr).getValue(); } return attrValue; } //@Override public Sequence getContent() throws HttpClientException { org.exist.xquery.value.Sequence valueSequence = new ValueSequence(); NodeList children = element.getNode().getChildNodes(); try { for(int i = 0; i < children.getLength(); i++) { Node child = children.item(i); valueSequence.add((NodeValue)child); } return new EXistSequence(valueSequence, context); } catch(XPathException xpe) { throw new HttpClientException(xpe.getMessage(), xpe); } } //@Override public String getDisplayName() throws HttpClientException { return element.getNode().getNodeName(); } //@Override public String getLocalName() throws HttpClientException { return element.getNode().getLocalName(); } //@Override public String getNamespaceUri() { return element.getNode().getNamespaceURI(); } //@Override public boolean hasNoNsChild() throws HttpClientException { NodeList children = element.getNode().getChildNodes(); for(int i = 0; i < children.getLength(); i++) { Node child = children.item(i); if(child.getNamespaceURI() == null && child.getPrefix() == null || child.getNamespaceURI().equalsIgnoreCase("")) { return true; } } return false; } //@Override public Iterable<Element> httpNsChildren() throws HttpClientException { final Node node = element.getNode(); return new IterableElement(node, HttpConstants.HTTP_CLIENT_NS_URI); } /** * Check the element {@code elem} does not have attributes other than {@code names}. * * {@code names} contains non-qualified names, for allowed attributes. The * element can have other attributes in other namespace (not in the HTTP * Client namespace) but no attributes in no namespace. * * @param names The non-qualified names of allowed attributes (cannot be * null, but can be empty.) * * @throws HttpClientException If the element contains an attribute in the * HTTP Client namespace, or in no namespace and the name of which is not * in {@code names}. */ //@Override public void noOtherNCNameAttribute(String[] names) throws HttpClientException { NamedNodeMap attributes = element.getNode().getAttributes(); for(int i = 0; i < attributes.getLength(); i++) { Node attribute = attributes.item(i); if(attribute.getNamespaceURI() != null) { if(attribute.getNamespaceURI().equals(HttpConstants.HTTP_CLIENT_NS_URI)) { throw new HttpClientException("Element contains an attribute '" + attribute.getNodeName() + "' in the HTTP Client namespace"); } } else { boolean matched = false; for(String name: names) { if(attribute.getLocalName().equals(name)) { matched = true; break; } } if(!matched) { throw new HttpClientException("Element contains an attribute '" + attribute.getNodeName() + "' in no namespace but that attribute is not permitted"); } } } } public class IterableElement implements Iterable<Element> { private final Node node; private String inNamespaceURI = null; public IterableElement(Node node) { this.node = node; } public IterableElement(Node node, String inNamespaceURI) { this.node = node; this.inNamespaceURI = inNamespaceURI; } //@Override public Iterator<Element> iterator() { return new ElementIterator(node, inNamespaceURI); } } public class ElementIterator implements Iterator<Element> { private final Node parent; private final String inNamespaceURI; private List<org.w3c.dom.Element> elements = null; private int position = 0; public ElementIterator(Node parent, String inNamespaceURI) { this.parent = parent; this.inNamespaceURI = inNamespaceURI; } //@Override public boolean hasNext() { return(position < getLength()); } //@Override public Element next() { if(position >= getLength()){ throw new NoSuchElementException(); } return new EXistElement((NodeValue)getElements().get(position++), context); } //@Override public void remove() { throw new UnsupportedOperationException("Not supported yet."); } private int getLength() { return getElements().size(); } /** * lazy initialised */ private List<org.w3c.dom.Element> getElements() { if(elements == null) { elements = new ArrayList<org.w3c.dom.Element>(); NodeList children = parent.getChildNodes(); for(int i = 0; i < children.getLength(); i++) { Node child = children.item(i); if(child.getNodeType() == Node.ELEMENT_NODE) { if(inNamespaceURI != null) { if(inNamespaceURI.equals(child.getNamespaceURI())){ elements.add((org.w3c.dom.Element)child); } } else { elements.add((org.w3c.dom.Element)child); } } } } return elements; } } }