/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2008, Open Source Geospatial Foundation (OSGeo)
*
* This library 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;
* version 2.1 of the License.
*
* This library 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.
*/
package org.geotools.xacml.geoxacml.finder.impl;
import java.net.URI;
import java.util.ArrayList;
import org.apache.xpath.XPathAPI;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.sun.xacml.EvaluationCtx;
import com.sun.xacml.ParsingException;
import com.sun.xacml.PolicyMetaData;
import com.sun.xacml.UnknownIdentifierException;
import com.sun.xacml.attr.AttributeFactory;
import com.sun.xacml.attr.AttributeValue;
import com.sun.xacml.attr.BagAttribute;
import com.sun.xacml.cond.EvaluationResult;
import com.sun.xacml.ctx.Status;
import com.sun.xacml.finder.AttributeFinderModule;
/**
* @author Christian Mueller
*
* Modluel to handle XPATH constructs (XACML AttribueSelector)
*
*/
public class GeoSelectorModule extends AttributeFinderModule {
public boolean isSelectorSupported() {
return true;
}
private EvaluationResult createProcessingError(String msg) {
ArrayList<String> code = new ArrayList<String>();
code.add(Status.STATUS_PROCESSING_ERROR);
return new EvaluationResult(new Status(code, msg));
}
/**
* Tries to find attribute values based on the given selector data. The result, if successful,
* always contains a <code>BagAttribute</code>, even if only one value was found. If no values
* were found, but no other error occurred, an empty bag is returned.
*
* @param path
* the XPath expression to search against
* @param namespaceNode
* the DOM node defining namespace mappings to use, or null if mappings come from the
* context root
* @param type
* the datatype of the attributes to find
* @param context
* the representation of the request data
* @param xpathVersion
* the XPath version to use
*
* @return the result of attribute retrieval, which will be a bag of attributes or an error
*/
public EvaluationResult findAttribute(String path, Node namespaceNode, URI type,
EvaluationCtx context, String xpathVersion) {
if (!xpathVersion.equals(PolicyMetaData.XPATH_1_0_IDENTIFIER))
return new EvaluationResult(BagAttribute.createEmptyBag(type));
// get the DOM root of the request document
Node root = context.getRequestRoot();
// if we were provided with a non-null namespace node, then use it
// to resolve namespaces, otherwise use the context root node
Node nsNode = (namespaceNode != null) ? namespaceNode : root;
// setup the root path (pre-pended to the context path), which...
String rootPath = "";
// ...only has content if the context path is relative
if (path.charAt(0) != '/') {
String rootName = root.getLocalName();
// see if the request root is in a namespace
String namespace = root.getNamespaceURI();
if (namespace == null) {
// no namespacing, so we're done
rootPath = "/" + rootName + "/";
} else {
// namespaces are used, so we need to lookup the correct
// prefix to use in the search string
NamedNodeMap nmap = namespaceNode.getAttributes();
rootPath = null;
for (int i = 0; i < nmap.getLength(); i++) {
Node n = nmap.item(i);
if (n.getNodeValue().equals(namespace)) {
// we found the matching namespace, so get the prefix
// and then break out
String name = n.getNodeName();
int pos = name.indexOf(':');
if (pos == -1) {
// the namespace was the default namespace
rootPath = "/";
} else {
// we found a prefixed namespace
rootPath = "/" + name.substring(pos + 1);
}
// finish off the string
rootPath += ":" + rootName + "/";
break;
}
}
// if the rootPath is still null, then we don't have any
// definitions for the namespace
if (rootPath == null)
return createProcessingError("Failed to map a namespace"
+ " in an XPath expression");
}
}
// now do the query, pre-pending the root path to the context path
NodeList matches = null;
try {
// NOTE: see comments in XALAN docs about why this is slow
matches = XPathAPI.selectNodeList(root, rootPath + path, nsNode);
} catch (Exception e) {
// in the case of any exception, we need to return an error
return createProcessingError("error in XPath: " + e.getMessage());
}
if (matches.getLength() == 0) {
// we didn't find anything, so we return an empty bag
return new EvaluationResult(BagAttribute.createEmptyBag(type));
}
// there was at least one match, so try to generate the values
try {
ArrayList<AttributeValue> list = new ArrayList<AttributeValue>();
AttributeFactory attrFactory = AttributeFactory.getInstance();
for (int i = 0; i < matches.getLength(); i++) {
String text = null;
Node node = matches.item(i);
short nodeType = node.getNodeType();
// see if this is straight text, or a node with data under
// it and then get the values accordingly
AttributeValue attrValue = null;
if ((nodeType == Node.CDATA_SECTION_NODE) || (nodeType == Node.COMMENT_NODE)
|| (nodeType == Node.TEXT_NODE) || (nodeType == Node.ATTRIBUTE_NODE)) {
// there is no child to this node
text = node.getNodeValue();
attrValue = attrFactory.createValue(type, text);
} else if (nodeType == Node.DOCUMENT_NODE || nodeType == Node.ELEMENT_NODE) {
attrValue = attrFactory.createValue(node, type);
} else {
// the data is in a child node
text = node.getFirstChild().getNodeValue();
attrValue = attrFactory.createValue(type, text);
}
list.add(attrValue);
}
return new EvaluationResult(new BagAttribute(type, list));
} catch (ParsingException pe) {
return createProcessingError(pe.getMessage());
} catch (UnknownIdentifierException uie) {
return createProcessingError("unknown attribute type: " + type);
}
}
}