| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.cocoon.forms.util; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Map; |
| import javax.xml.XMLConstants; |
| |
| import org.apache.avalon.framework.service.ServiceException; |
| import org.apache.avalon.framework.service.ServiceManager; |
| import org.apache.excalibur.xml.sax.SAXParser; |
| import org.apache.excalibur.xml.sax.XMLizable; |
| |
| import org.apache.cocoon.forms.FormsException; |
| import org.apache.cocoon.util.location.Location; |
| import org.apache.cocoon.util.location.LocationAttributes; |
| import org.apache.cocoon.xml.SaxBuffer; |
| import org.apache.cocoon.xml.dom.DOMBuilder; |
| import org.apache.cocoon.xml.dom.DOMStreamer; |
| |
| import org.apache.commons.lang.BooleanUtils; |
| import org.w3c.dom.Attr; |
| import org.w3c.dom.CDATASection; |
| 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.Text; |
| import org.xml.sax.ContentHandler; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.SAXNotSupportedException; |
| |
| /** |
| * Helper class to create and retrieve information from DOM-trees. It provides |
| * some functionality comparable to what's found in Avalon's Configuration |
| * objects. These lasts one could however not be used by Cocoon Forms because they |
| * don't provide an accurate model of an XML file (no mixed content, |
| * no namespaced attributes, no namespace declarations, ...). |
| * |
| * <p>This class depends specifically on the Xerces DOM implementation to be |
| * able to provide information about the location of elements in their source |
| * XML file. See the {@link #getLocation(Element)} method. |
| * |
| * @version $Id$ |
| */ |
| public class DomHelper { |
| |
| public static final String XMLNS_URI = XMLConstants.XMLNS_ATTRIBUTE_NS_URI; |
| |
| public static Location getLocationObject(Element element) { |
| return LocationAttributes.getLocation(element); |
| } |
| |
| /** |
| * Retrieves the location of an element node in the source file from which |
| * the Document was created. This will only work for Document's created |
| * with the method {@link #parse(InputSource, ServiceManager)} of this class. |
| */ |
| public static String getLocation(Element element) { |
| return LocationAttributes.getLocationString(element); |
| } |
| |
| public static String getSystemIdLocation(Element element) { |
| return LocationAttributes.getURI(element); |
| } |
| |
| public static int getLineLocation(Element element) { |
| return LocationAttributes.getLine(element); |
| } |
| |
| public static int getColumnLocation(Element element) { |
| return LocationAttributes.getColumn(element); |
| } |
| |
| /** |
| * Returns all Element children of an Element that belong to the given |
| * namespace. |
| */ |
| public static Element[] getChildElements(Element element, String namespace) { |
| ArrayList elements = new ArrayList(); |
| NodeList nodeList = element.getChildNodes(); |
| for (int i = 0; i < nodeList.getLength(); i++) { |
| Node node = nodeList.item(i); |
| if (node instanceof Element |
| && namespace.equals(node.getNamespaceURI())) |
| elements.add(node); |
| } |
| return (Element[]) elements.toArray(new Element[elements.size()]); |
| } |
| |
| /** |
| * Returns all Element children of an Element that belong to the given |
| * namespace and have the given local name. |
| */ |
| public static Element[] getChildElements(Element element, |
| String namespace, |
| String localName) { |
| ArrayList elements = new ArrayList(); |
| NodeList nodeList = element.getChildNodes(); |
| for (int i = 0; i < nodeList.getLength(); i++) { |
| Node node = nodeList.item(i); |
| if (node instanceof Element |
| && namespace.equals(node.getNamespaceURI()) |
| && localName.equals(node.getLocalName())) { |
| elements.add(node); |
| } |
| } |
| return (Element[]) elements.toArray(new Element[elements.size()]); |
| } |
| |
| /** |
| * Returns the first child element with the given namespace and localName, |
| * or null if there is no such element. |
| */ |
| public static Element getChildElement(Element element, |
| String namespace, |
| String localName) { |
| Element node; |
| try { |
| node = getChildElement(element, namespace, localName, false); |
| } catch (Exception e) { |
| node = null; |
| } |
| return node; |
| } |
| |
| /** |
| * Returns the first child element with the given namespace and localName, |
| * or null if there is no such element and required flag is unset or |
| * throws an Exception if the "required" flag is set. |
| */ |
| public static Element getChildElement(Element element, |
| String namespace, |
| String localName, |
| boolean required) |
| throws FormsException { |
| NodeList nodeList = element.getChildNodes(); |
| for (int i = 0; i < nodeList.getLength(); i++) { |
| Node node = nodeList.item(i); |
| if (node instanceof Element |
| && namespace.equals(node.getNamespaceURI()) |
| && localName.equals(node.getLocalName())) { |
| return (Element) node; |
| } |
| } |
| |
| if (required) { |
| throw new FormsException("Required element '" + localName + "' is missing.", |
| DomHelper.getLocationObject(element)); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns the value of an element's attribute, but throws an exception |
| * if the element has no such attribute. |
| */ |
| public static String getAttribute(Element element, String attributeName) |
| throws FormsException { |
| String attrValue = element.getAttribute(attributeName); |
| if (attrValue.length() == 0) { |
| throw new FormsException("Required attribute '" + attributeName + "' is missing.", |
| DomHelper.getLocationObject(element)); |
| } |
| |
| return attrValue; |
| } |
| |
| /** |
| * Returns the value of an element's attribute, or a default value if the |
| * element has no such attribute. |
| */ |
| public static String getAttribute(Element element, |
| String attributeName, |
| String defaultValue) { |
| String attrValue = element.getAttribute(attributeName); |
| if (attrValue.length() == 0) { |
| return defaultValue; |
| } |
| return attrValue; |
| } |
| |
| public static int getAttributeAsInteger(Element element, |
| String attributeName) |
| throws FormsException { |
| String attrValue = getAttribute(element, attributeName); |
| try { |
| return Integer.parseInt(attrValue); |
| } catch (NumberFormatException e) { |
| throw new FormsException("Cannot parse the value '" + attrValue + "' " + |
| "as an integer in the attribute '" + attributeName + "'," + |
| DomHelper.getLocationObject(element)); |
| } |
| } |
| |
| public static int getAttributeAsInteger(Element element, |
| String attributeName, |
| int defaultValue) |
| throws FormsException { |
| String attrValue = element.getAttribute(attributeName); |
| if (attrValue.length() == 0) { |
| return defaultValue; |
| } |
| |
| try { |
| return Integer.parseInt(attrValue); |
| } catch (NumberFormatException e) { |
| throw new FormsException("Cannot parse the value '" + attrValue + "' " + |
| "as an integer in the attribute '" + attributeName + "'," + |
| DomHelper.getLocationObject(element)); |
| } |
| } |
| |
| public static boolean getAttributeAsBoolean(Element element, |
| String attributeName, |
| boolean defaultValue) { |
| String attrValue = element.getAttribute(attributeName); |
| if (attrValue.length() == 0) { |
| return defaultValue; |
| } |
| |
| Boolean result; |
| try { |
| result = BooleanUtils.toBooleanObject(attrValue, "true", "false", null); |
| } catch (IllegalArgumentException e1) { |
| try { |
| result = BooleanUtils.toBooleanObject(attrValue, "yes", "no", null); |
| } catch (IllegalArgumentException e2) { |
| result = null; |
| } |
| } |
| if (result == null) { |
| return defaultValue; |
| } |
| |
| return result.booleanValue(); |
| } |
| |
| public static String getElementText(Element element) { |
| StringBuffer value = new StringBuffer(); |
| NodeList nodeList = element.getChildNodes(); |
| for (int i = 0; i < nodeList.getLength(); i++) { |
| Node node = nodeList.item(i); |
| if (node instanceof Text || node instanceof CDATASection) { |
| value.append(node.getNodeValue()); |
| } |
| } |
| return value.toString(); |
| } |
| |
| /** |
| * Returns the content of the given Element as an object implementing the |
| * XMLizable interface. Practically speaking, the implementation uses the |
| * {@link SaxBuffer} class. The XMLizable object will be a standalone blurb |
| * of SAX events, not producing start/endDocument calls and containing all |
| * necessary namespace declarations. |
| */ |
| public static XMLizable compileElementContent(Element element) { |
| // Remove location information |
| LocationAttributes.remove(element, true); |
| |
| SaxBuffer saxBuffer = new SaxBuffer(); |
| DOMStreamer domStreamer = new DOMStreamer(); |
| domStreamer.setContentHandler(saxBuffer); |
| |
| NodeList childNodes = element.getChildNodes(); |
| for (int i = 0; i < childNodes.getLength(); i++) { |
| try { |
| domStreamer.stream(childNodes.item(i)); |
| } catch (SAXException e) { |
| // It's unlikely that an exception will occur here, |
| // so use a runtime exception |
| throw new RuntimeException("Error in DomHelper.compileElementContent: " + |
| e.toString()); |
| } |
| } |
| return saxBuffer; |
| } |
| |
| /** |
| * Creates a W3C Document that remembers the location of each element in |
| * the source file. The location of element nodes can then be retrieved |
| * using the {@link #getLocation(Element)} method. |
| * |
| * @param inputSource the inputSource to read the document from |
| * @param manager the service manager where to lookup the entity resolver |
| */ |
| public static Document parse(InputSource inputSource, ServiceManager manager) |
| throws SAXException, SAXNotSupportedException, IOException, ServiceException { |
| |
| SAXParser parser = (SAXParser)manager.lookup(SAXParser.ROLE); |
| DOMBuilder builder = new DOMBuilder(); |
| |
| // Enhance the sax stream with location information |
| ContentHandler locationHandler = new LocationAttributes.Pipe(builder); |
| |
| try { |
| parser.parse(inputSource, locationHandler); |
| } finally { |
| manager.release(parser); |
| } |
| |
| return builder.getDocument(); |
| } |
| |
| public static Map getLocalNSDeclarations(Element elm) { |
| return addLocalNSDeclarations(elm, null); |
| } |
| |
| private static Map addLocalNSDeclarations(Element elm, Map nsDeclarations) { |
| NamedNodeMap atts = elm.getAttributes(); |
| int attsSize = atts.getLength(); |
| |
| for (int i = 0; i < attsSize; i++) { |
| Attr attr = (Attr) atts.item(i); |
| if (XMLNS_URI.equals(attr.getNamespaceURI())) { |
| String nsUri = attr.getValue(); |
| String pfx = attr.getLocalName(); |
| if (nsDeclarations == null) |
| nsDeclarations = new HashMap(); |
| nsDeclarations.put(nsUri, pfx); |
| } |
| } |
| return nsDeclarations; |
| } |
| |
| public static Map getInheritedNSDeclarations(Element elm) { |
| List ancestorsAndSelf = new LinkedList(); |
| Element current = elm; |
| while (current != null) { |
| ancestorsAndSelf.add(current); |
| Node parent = current.getParentNode(); |
| if (parent.getNodeType() == Node.ELEMENT_NODE) |
| current = (Element) parent; |
| else |
| current = null; |
| } |
| |
| Map nsDeclarations = null; |
| ListIterator i = ancestorsAndSelf.listIterator(ancestorsAndSelf.size()); |
| while (i.hasPrevious()) { |
| Element element = (Element) i.previous(); |
| nsDeclarations = addLocalNSDeclarations(element, nsDeclarations); |
| } |
| |
| return nsDeclarations; |
| } |
| } |