001/**
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved
005 *
006 * The contents of this file are subject to the terms
007 * of the Common Development and Distribution License
008 * (the License). You may not use this file except in
009 * compliance with the License.
010 *
011 * You can obtain a copy of the License at
012 * https://opensso.dev.java.net/public/CDDLv1.0.html or
013 * opensso/legal/CDDLv1.0.txt
014 * See the License for the specific language governing
015 * permission and limitations under the License.
016 *
017 * When distributing Covered Code, include this CDDL
018 * Header Notice in each file and include the License file
019 * at opensso/legal/CDDLv1.0.txt.
020 * If applicable, add the following below the CDDL Header,
021 * with the fields enclosed by brackets [] replaced by
022 * your own identifying information:
023 * "Portions Copyrighted [year] [name of copyright owner]"
024 *
025 * $Id: ResourceImpl.java,v 1.3 2008/06/25 05:48:13 qcheng Exp $
026 *
027 */
028
029package com.sun.identity.xacml.context.impl;
030
031import com.sun.identity.xacml.common.XACMLSDKUtils;
032import com.sun.identity.shared.xml.XMLUtils;
033import com.sun.identity.xacml.common.XACMLSDKUtils;
034import com.sun.identity.xacml.common.XACMLConstants;
035import com.sun.identity.xacml.common.XACMLException;
036import com.sun.identity.xacml.context.Attribute;
037import com.sun.identity.xacml.context.ContextFactory;
038import com.sun.identity.xacml.context.Resource;
039import com.sun.identity.xacml.context.ResourceContent;
040import java.util.ArrayList;
041
042import java.util.List;
043import org.w3c.dom.Document;
044import org.w3c.dom.Element;
045import org.w3c.dom.Node;
046import org.w3c.dom.NodeList;
047
048/**
049 * The <code>Resource</code> element specifies information about the
050 * resource to which access is requested by listing a 
051 * sequence of <code>Attribute</code> elements associated with the
052 * resource. it may include <code>ResourceContent</code>
053 * <p>
054 * <pre>
055 * &lt;xs:element name="Resource" type="xacml-context:ResourceType"/>
056 *   &lt;xs:complexType name="ResourceType">
057 *     &lt;xs:sequence>
058 *       &lt;xs:element ref="xacml-context:ResourceContent" minOccurs="0"/>
059 *       &lt;xs:element ref="xacml-context:Attribute" minOccurs="0" 
060 *          maxOccurs="unbounded"/>
061 *    &lt;xs:sequence>
062 *  &lt;xs:complexType>
063 * </pre>
064 *@supported.all.api
065 */
066public class ResourceImpl implements Resource {
067    private List  attributes;
068    private Element resourceContent;
069    private boolean isMutable = true;
070
071   /** 
072    * Default constructor
073    */
074    public ResourceImpl() {
075    }
076
077    /**
078     * This constructor is used to build <code>Resource</code> object from a
079     * XML string.
080     *
081     * @param xml A <code>java.lang.String</code> representing
082     *        a <code>Resource</code> object
083     * @exception XACMLException if it could not process the XML string
084     */
085    public ResourceImpl(String xml) throws XACMLException {
086        Document document = XMLUtils.toDOMDocument(xml, XACMLSDKUtils.debug);
087        if (document != null) {
088            Element rootElement = document.getDocumentElement();
089            processElement(rootElement);
090            makeImmutable();
091        } else {
092            XACMLSDKUtils.debug.error(
093                "SubjectImpl.processElement(): invalid XML input");
094            throw new XACMLException(
095                XACMLSDKUtils.xacmlResourceBundle.getString(
096                "errorObtainingElement"));
097        }
098    }
099
100    /**
101     * This constructor is used to build <code>resource</code> object from a
102     * block of existing XML that has already been built into a DOM.
103     *
104     * @param element A <code>org.w3c.dom.Element</code> representing
105     *        DOM tree for <code>Resource</code> object
106     * @exception XACML2Exception if it could not process the Element
107     */
108    public ResourceImpl(Element element) throws XACMLException {
109        processElement(element);
110        makeImmutable();
111    }
112
113    private void processElement(Element element) throws XACMLException {
114        if (element == null) {
115            XACMLSDKUtils.debug.error(
116                "ResourceImpl.processElement(): invalid root element");
117            throw new XACMLException( 
118                XACMLSDKUtils.xacmlResourceBundle.getString(
119                "invalid_element"));
120        }
121        String elemName = element.getLocalName(); 
122        if (elemName == null) {
123             XACMLSDKUtils.debug.error(
124                "ResourceImpl.processElement(): local name missing");
125            throw new XACMLException( 
126                XACMLSDKUtils.xacmlResourceBundle.getString(
127                "missing_local_name"));
128        }
129
130        if (!elemName.equals(XACMLConstants.RESOURCE)) {
131            XACMLSDKUtils.debug.error(
132                "ResourceImpl.processElement(): invalid local name " +
133                 elemName);
134            throw new XACMLException(
135                XACMLSDKUtils.xacmlResourceBundle.getString(
136                "invalid_local_name"));
137        }
138
139        // starts processing subelements
140        NodeList nodes = element.getChildNodes();
141        int numOfNodes = nodes.getLength();
142        if (numOfNodes > 0) {
143            ContextFactory factory = ContextFactory.getInstance();
144            for (int i=0; i< numOfNodes; i++) {
145                Node child = (Node)nodes.item(i);
146                if (child.getNodeType() == Node.ELEMENT_NODE) {
147                    String childName = child.getLocalName();
148                    // The child nodes should be <Attribute> or 
149                    // <ResourceContent>
150                    if (childName.equals(XACMLConstants.ATTRIBUTE)) {
151                        if (attributes == null) {
152                            attributes = new ArrayList();
153                        }
154                        Attribute attribute = factory.getInstance().
155                            createAttribute((Element)child);
156                        attributes.add(attribute);
157                    } else if (childName.equals(
158                            XACMLConstants.RESOURCE_CONTENT)) {
159                        resourceContent = (Element)child;
160                    }
161                }
162            }
163         } else {
164             /* not a schema violation
165             XACMLSDKUtils.debug.error(
166                "ResourceImpl.processElement(): no attributes or resource "
167                +"content");
168            throw new XACMLException( 
169                XACMLSDKUtils.xacmlResourceBundle.getString(
170                "missing_subelements"));
171            */
172         }
173    }
174
175    /**
176     * Returns the ResourceConent
177     *
178     * @return the ResourceContent of the Resource
179     */
180    public Element getResourceContent() {
181        return resourceContent;
182    }
183
184    /**
185     * Sets the ResourceContent of this Resource
186     *
187     * @param resourceContent  ResourceContent of this Resource. 
188     * ResourceContent  is optional, so could be null.
189     *
190     * @exception XACMLException if the object is immutable
191     * An object is considered <code>immutable</code> if <code>
192     * makeImmutable()</code> has been invoked on it. It can
193     * be determined by calling <code>isMutable</code> on the object.
194     */
195    public void setResourceContent(Element resourceContent) 
196        throws XACMLException {
197          if (!isMutable) {
198            throw new XACMLException(
199                XACMLSDKUtils.xacmlResourceBundle.getString(
200                "objectImmutable"));
201        }
202        String elemName = resourceContent.getLocalName();
203        if (elemName == null 
204                || !elemName.equals(XACMLConstants.RESOURCE_CONTENT)) {
205            XACMLSDKUtils.debug.error(
206                "StatusMessageImpl.processElement():"
207                + "local name missing or incorrect");
208            throw new XACMLException(
209                XACMLSDKUtils.xacmlResourceBundle.getString(
210                    "missing_local_name"));
211        }
212        this.resourceContent = resourceContent;
213    }
214
215    /**
216     * Returns zero to many <code>Attribute</code> elements of this object
217     * If no attributes and present, empty <code>List</code> will be returned.
218     * Typically a <code>Resource</code> element will contain an <code>
219     * Attribute</code> with an <code>AttributeId</code> of
220     * "urn:oasis:names:tc:xacml:1.0:resource:resource-id". Each such
221     * <code>Attribute</code> SHALL be an absolute abd fully resolved 
222     * representation of the identity of the single resource to which
223     * access is requested.
224     *
225     * @return <code>List</code> containing the <code>Attribute</code> 
226     * elements of this object
227     */
228    public List getAttributes() {
229        return attributes;
230    }
231
232    /**
233     * Sets the <code>Attribute</code> elements of this object
234     *
235     * @param attributes <code>Attribute</code> elements of this object
236     * attributes could be an empty <code>List</code>, if no attributes
237     * are present.
238     *
239     * @exception XACMLException if the object is immutable
240     * An object is considered <code>immutable</code> if <code>
241     * makeImmutable()</code> has been invoked on it. It can
242     * be determined by calling <code>isMutable</code> on the object.
243     */
244    public void setAttributes(List attributes) throws XACMLException {
245         if (!isMutable) {
246            throw new XACMLException(
247                XACMLSDKUtils.xacmlResourceBundle.getString(
248                "objectImmutable"));
249        }
250        if (attributes != null &&  !attributes.isEmpty()) {
251             if (this.attributes == null) {
252                 this.attributes = new ArrayList();
253            }
254            this.attributes.addAll(attributes);
255        }
256    }
257
258   /**
259    * Returns a <code>String</code> representation of this object
260    * @param includeNSPrefix Determines whether or not the namespace qualifier
261    *        is prepended to the Element when converted
262    * @param declareNS Determines whether or not the namespace is declared
263    *        within the Element.
264    * @return a string representation of this object
265    * @exception XACMLException if conversion fails for any reason
266     */
267    public String toXMLString(boolean includeNSPrefix, boolean declareNS)
268            throws XACMLException
269    {
270        StringBuffer sb = new StringBuffer(2000);
271        StringBuffer NS = new StringBuffer(100);
272        String appendNS = "";
273        if (declareNS) {
274            NS.append(XACMLConstants.CONTEXT_NS_DECLARATION)
275                    .append(XACMLConstants.SPACE);
276            NS.append(XACMLConstants.XSI_NS_URI)
277                    .append(XACMLConstants.SPACE)
278                    .append(XACMLConstants.CONTEXT_SCHEMA_LOCATION);
279        }
280        if (includeNSPrefix) {
281            appendNS = XACMLConstants.CONTEXT_NS_PREFIX + ":";
282        }
283        sb.append("<").append(appendNS).append(XACMLConstants.RESOURCE)
284                .append(NS);
285        sb.append(">");
286        int length = 0;
287        if (attributes != null) {
288            sb.append("\n");
289            length = attributes.size();
290            for (int i = 0; i < length; i++) {
291                Attribute attr = (Attribute)attributes.get(i);
292                sb.append(attr.toXMLString(includeNSPrefix, false));
293            }
294        }
295        if (resourceContent != null) {
296            sb.append("\n");
297            // ignore trailing ":"
298            if (includeNSPrefix && (resourceContent.getPrefix() == null)) {
299                resourceContent.setPrefix(appendNS.substring(0, appendNS.length()-1));
300            }
301            if(declareNS) {
302                int index = NS.indexOf("=");
303                String namespaceName = NS.substring(0, index);
304                String namespaceURI = NS.substring(index+1);
305                if (resourceContent.getNamespaceURI() == null) {
306                    resourceContent.setAttribute(namespaceName, namespaceURI);
307                    // does not seem to work to append namespace TODO
308                }
309            }
310            sb.append(XMLUtils.print(resourceContent));
311        }
312        sb.append("</").append(appendNS).append(XACMLConstants.RESOURCE);
313        sb.append(">\n");
314        return sb.toString();
315    }
316
317   /**
318    * Returns a string representation of this object
319    *
320    * @return a string representation of this object
321    * @exception XACMLException if conversion fails for any reason
322    */
323    public String toXMLString() throws XACMLException {
324        return  toXMLString(true, false);
325    }
326
327   /*
328    * Makes the object immutable
329    */
330    public void makeImmutable() {// TODO 
331    }
332
333   /**
334    * Checks if the object is mutable
335    *
336    * @return <code>true</code> if the object is mutable,
337    *         <code>false</code> otherwise
338    */
339    public boolean isMutable() {
340        return isMutable;
341    }
342}