blob: 4b9dd0a2f7debd1c1740b4f997e2d6beb8f50850 [file] [log] [blame]
/*
* 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;
}
}