/* * @(#)BaseAttributeFactory.java * * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistribution of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed or intended for use in * the design, construction, operation or maintenance of any nuclear facility. */ package com.sun.xacml.attr; import com.sun.xacml.ParsingException; import com.sun.xacml.UnknownIdentifierException; import com.sun.xacml.debug.RuntimeInfo; import com.sun.xacml.debug.RuntimeInfo.ELEMENT_TYPE; import java.net.URI; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.w3c.dom.Node; /** * This is a basic implementation of <code>AttributeFactory</code>. It * implements the insertion and retrieval methods, but doesn't actually * setup the factory with any datatypes. * <p> * Note that while this class is thread-safe on all creation methods, it * is not safe to add support for a new datatype while creating an instance * of a value. This follows from the assumption that most people will * initialize these factories up-front, and then start processing without * ever modifying the factories. If you need these mutual operations to * be thread-safe, then you should write a wrapper class that implements * the right synchronization. * * @since 1.2 * @author Seth Proctor */ public class BaseAttributeFactory extends AttributeFactory { // the map of proxies private HashMap<String, AttributeProxy> attributeMap; /** * Default constructor. */ public BaseAttributeFactory() { this.attributeMap = new HashMap<String, AttributeProxy>(); } /** * Constructor that configures this factory with an initial set of * supported datatypes. * * @param attributes a <code>Map</code> of <code>String</code>s to * </code>AttributeProxy</code>s * * @throws IllegalArgumentException if any elements of the Map are not * </code>AttributeProxy</code>s */ public BaseAttributeFactory(Map<String, AttributeProxy> attributes) { this.attributeMap = new HashMap<String, AttributeProxy>(); Iterator<String> it = attributes.keySet().iterator(); while (it.hasNext()) { try { String id = it.next(); AttributeProxy proxy = attributes.get(id); this.attributeMap.put(id, proxy); } catch (ClassCastException cce) { throw new IllegalArgumentException("an element of the map " + "was not an instance of " + "AttributeProxy"); } } } /** * Adds a proxy to the factory, which in turn will allow new attribute * types to be created using the factory. Typically the proxy is * provided as an anonymous class that simply calls the getInstance * methods (or something similar) of some <code>AttributeValue</code> * class. * * @param id the name of the attribute type * @param proxy the proxy used to create new attributes of the given type */ public void addDatatype(String id, AttributeProxy proxy) { // make sure this doesn't already exist if (this.attributeMap.containsKey(id)) { throw new IllegalArgumentException("datatype already exists"); } this.attributeMap.put(id, proxy); } /** * Returns the datatype identifiers supported by this factory. * * @return a <code>Set</code> of <code>String</code>s */ public Set<String> getSupportedDatatypes() { return Collections.unmodifiableSet(this.attributeMap.keySet()); } /** * Creates a value based on the given DOM root node. The type of the * attribute is assumed to be present in the node as an XACML attribute * named <code>DataType</code>, as is the case with the * AttributeValueType in the policy schema. The value is assumed to be * the first child of this node. * * @param root the DOM root of an attribute value * * @return a new <code>AttributeValue</code> * * @throws UnknownIdentifierException if the type in the node isn't * known to the factory * @throws ParsingException if the node is invalid or can't be parsed * by the appropriate proxy */ public AttributeValue createValue(Node root) throws UnknownIdentifierException, ParsingException { if (root.getNodeType() != Node.ELEMENT_NODE || root.getAttributes().getNamedItem("DataType") == null) { RuntimeInfo src = RuntimeInfo.getRuntimeInfo(root, ELEMENT_TYPE.ATTRIBUTE_VALUE); String message = ""; if ( root.getNodeType() != Node.ELEMENT_NODE ) { message = ": received element is not a node (" + root.toString() + ")"; } else if ( root.getAttributes().getNamedItem("DataType") == null) { message = ": missing attribute \"DataType\""; } throw new ParsingException("Error while parsing AttributeValue" + message + ( src == null ? "" : src.getLocationMsgForError())); } Node node = root.getAttributes().getNamedItem("DataType"); return createValue(root, node.getNodeValue()); } /** * Creates a value based on the given DOM root node and data type. * * @param root the DOM root of an attribute value * @param dataType the type of the attribute * * @return a new <code>AttributeValue</code> * * @throws UnknownIdentifierException if the data type isn't known to * the factory * @throws ParsingException if the node is invalid or can't be parsed * by the appropriate proxy */ public AttributeValue createValue(Node root, URI dataType) throws UnknownIdentifierException, ParsingException { return createValue(root, dataType.toString()); } /** * Creates a value based on the given DOM root node and data type. * * @param root the DOM root of an attribute value * @param type the type of the attribute * * @return a new <code>AttributeValue</code> * * @throws UnknownIdentifierException if the type isn't known to * the factory * @throws ParsingException if the node is invalid or can't be parsed * by the appropriate proxy */ public AttributeValue createValue(Node root, String type) throws UnknownIdentifierException, ParsingException { AttributeProxy proxy = (AttributeProxy)(this.attributeMap.get(type)); RuntimeInfo src = RuntimeInfo.getRuntimeInfo(root, ELEMENT_TYPE.ATTRIBUTE_VALUE); if (proxy != null) { try { AttributeValue val = proxy.getInstance(root); if ( src != null ) { val.setSrcLocFromFactory(src); src.setXACMLObject(val); } return val; } catch (Exception e) { throw new ParsingException("couldn't create " + type + " attribute based on DOM node" + (src != null ? src.getLocationMsgForError() : ""), e); } } throw new UnknownIdentifierException("Attributes of type " + type + " aren't supported." + (src != null ? src.getLocationMsgForError() : "")); } /** * Creates a value based on the given data type and text-encoded value. * Used primarily by code that does an XPath query to get an * attribute value, and then needs to turn the resulting value into * an Attribute class. * * @param dataType the type of the attribute * @param value the text-encoded representation of an attribute's value * * @return a new <code>AttributeValue</code> * * @throws UnknownIdentifierException if the data type isn't known to * the factory * @throws ParsingException if the text is invalid or can't be parsed * by the appropriate proxy */ public AttributeValue createValue(URI dataType, String value) throws UnknownIdentifierException, ParsingException { String type = dataType.toString(); AttributeProxy proxy = (AttributeProxy)(this.attributeMap.get(type)); if (proxy != null) { try { return proxy.getInstance(value); } catch (Exception e) { throw new ParsingException("couldn't create " + type + " attribute from input: " + value, e); } } throw new UnknownIdentifierException("Attributes of type " + type + " aren't supported."); } }