
/*
 * @(#)Obligation.java
 *
 * Copyright 2003-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;

import com.sun.xacml.attr.AttributeFactory;
import com.sun.xacml.attr.AttributeValue;

import com.sun.xacml.ctx.Attribute;
import com.sun.xacml.ctx.Result;

import java.io.OutputStream;
import java.io.PrintStream;

import java.net.URI;
import java.net.URISyntaxException;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


/**
 * Represents the ObligationType XML type in XACML. This also stores all the
 * AttriubteAssignmentType XML types.
 *
 * @since 1.0
 * @author Seth Proctor
 */
public class Obligation
{

    // the obligation id
    private URI id;

    // effect to fulfill on, as defined in Result
    private int fulfillOn;

    // the attribute assignments
    private List assignments;

    /**
     * Constructor that takes all the data associated with an obligation.
     * The attribute assignment list contains <code>Attribute</code> objects,
     * but only the fields used by the AttributeAssignmentType are used.
     *
     * @param id the obligation's id
     * @param fulfillOn the effect denoting when to fulfill this obligation
     * @param assignments a <code>List</code> of <code>Attribute</code>s
     */
    public Obligation(URI id, int fulfillOn, List assignments) {
        this.id = id;
        this.fulfillOn = fulfillOn;
        this.assignments = Collections.
            unmodifiableList(new ArrayList(assignments));
    }

    /**
     * Creates an instance of <code>Obligation</code> based on the DOM root
     * node.
     *
     * @param root the DOM root of the ObligationType XML type
     *
     * @return an instance of an obligation
     *
     * @throws ParsingException if the structure isn't valid
     */
    public static Obligation getInstance(Node root) throws ParsingException {
        URI id;
        int fulfillOn = -1;
        List assignments = new ArrayList();

        AttributeFactory attrFactory = AttributeFactory.getInstance();
        NamedNodeMap attrs = root.getAttributes();

        try {
            id = new URI(attrs.getNamedItem("ObligationId").getNodeValue());
        } catch (Exception e) {
            throw new ParsingException("Error parsing required attriubte " +
                                       "ObligationId", e);
        }

        String effect = null;

        try {
            effect = attrs.getNamedItem("FulfillOn").getNodeValue();
        } catch (Exception e) {
            throw new ParsingException("Error parsing required attriubte " +
                                       "FulfillOn", e);
        }

        if (effect.equals("Permit")) {
            fulfillOn = Result.DECISION_PERMIT;
        } else if (effect.equals("Deny")) {
            fulfillOn = Result.DECISION_DENY;
        } else {
            throw new ParsingException("Invlid Effect type: " + effect);
        }

        NodeList nodes = root.getChildNodes();
        for (int i = 0; i < nodes.getLength(); i++) {
            Node node = nodes.item(i);
            if (node.getNodeName().equals("AttributeAssignment")) {
                try {
                    URI attrId =
                        new URI(node.getAttributes().
                                getNamedItem("AttributeId").getNodeValue());
                    AttributeValue attrValue = attrFactory.createValue(node);
                    assignments.add(new Attribute(attrId, null, null,
                                                  attrValue));
                } catch (URISyntaxException use) {
                    throw new ParsingException("Error parsing URI", use);
                } catch (UnknownIdentifierException uie) {
                    throw new ParsingException("Unknown AttributeId", uie);
                } catch (Exception e) {
                    throw new ParsingException("Error parsing attribute " +
                                               "assignments", e);
                }
            }
        }

        return new Obligation(id, fulfillOn, assignments);
    }
    
    /**
     * Returns the id of this obligation
     *
     * @return the id
     */
    public URI getId() {
        return id;
    }

    /**
     * Returns effect that will cause this obligation to be included in a
     * response
     *
     * @return the fulfillOn effect
     */
    public int getFulfillOn() {
        return fulfillOn;
    }

    /**
     * Returns the attribute assignment data in this obligation. The
     * <code>List</code> contains objects of type <code>Attribute</code>
     * with only the correct attribute fields being used.
     *
     * @return the assignments
     */
    public List getAssignments() {
        return assignments;
    }

    /**
     * Encodes this <code>Obligation</code> into its XML form and writes this
     * out to the provided <code>OutputStream<code> with no indentation.
     *
     * @param output a stream into which the XML-encoded data is written
     */
    public void encode(OutputStream output) {
        encode(output, new Indenter(0));
    }

    /**
     * Encodes this <code>Obligation</code> into its XML form and writes this
     * out to the provided <code>OutputStream<code> with indentation.
     *
     * @param output a stream into which the XML-encoded data is written
     * @param indenter an object that creates indentation strings
     */
    public void encode(OutputStream output, Indenter indenter) {
        PrintStream out = new PrintStream(output);
        String indent = indenter.makeString();
        
        out.println(indent + "<Obligation ObligationId=\"" + id.toString() +
                    "\" FulfillOn=\"" + Result.DECISIONS[fulfillOn] + "\">");

        indenter.in();
        
        Iterator it = assignments.iterator();

        while (it.hasNext()) {
            Attribute attr = (Attribute)(it.next());
            out.println(indenter.makeString() +
                        "<AttributeAssignment AttributeId=\"" +
                        attr.getId().toString() + "\" DataType=\"" +
                        attr.getType().toString() + "\">" +
                        attr.getValue().encode() +
                        "</AttributeAssignment>");
        }

        indenter.out();

        out.println(indent + "</Obligation>");
    }

}
