/*
 * $Header: /home/cvspublic/jakarta-slide/src/share/org/apache/slide/security/NodePermission.java,v 1.19 2004/10/05 10:53:27 ozeigermann Exp $
 * $Revision: 1.19 $
 * $Date: 2004/10/05 10:53:27 $
 *
 * ====================================================================
 *
 * Copyright 1999-2002 The Apache Software Foundation 
 *
 * Licensed 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.slide.security;

import java.io.Serializable;

import org.apache.slide.common.ObjectValidationFailedException;
import org.apache.slide.content.NodeRevisionNumber;
import org.apache.slide.structure.ActionNode;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.structure.SubjectNode;
import org.apache.slide.util.Messages;

/**
 * Permission object.
 *
 * @version $Revision: 1.19 $
 */
public final class NodePermission implements Serializable, Cloneable {
    
    
    // ----------------------------------------------------------- Constructors
    
    
    /**
     * Constructor 1.
     *
     * @param objectUri Object Uri
     * @param subjectUri Subject Uri
     * @param actionUri Action Uri
     */
    public NodePermission(String objectUri, String subjectUri,
                          String actionUri) {
        if (objectUri != null) {
            this.objectUri = objectUri;
        }
        if (subjectUri != null) {
            setSubject(subjectUri);
        }
        if (actionUri != null) {
            setAction(actionUri);
        }
        inheritable = true;
        negative = false;
    }
    
    
    /**
     * Constructor 2.
     *
     * @param objectUri Object Uri
     * @param subjectUri Subject Uri
     * @param actionUri Action Uri
     * @param inheritable Inheritance flag
     */
    public NodePermission(String objectUri, String subjectUri,
                          String actionUri, boolean inheritable) {
        this(objectUri, subjectUri, actionUri);
        this.inheritable = inheritable;
    }
    
    
    /**
     * Constructor 3.
     *
     * @param objectUri Object Uri
     * @param subjectUri Subject Uri
     * @param actionUri Action Uri
     * @param inheritable Inheritance flag
     * @param negative Negative flag
     */
    public NodePermission(String objectUri, String subjectUri,
                          String actionUri, boolean inheritable,
                          boolean negative) {
        this(objectUri, subjectUri, actionUri);
        this.inheritable = inheritable;
        this.negative = negative;
    }
    
    
    /**
     * Constructor 4.
     *
     * @param object Object
     * @param subject Subject
     * @param action Action
     */
    public NodePermission(ObjectNode object, SubjectNode subject,
                          ActionNode action) {
        this(object.getUri(), subject.getUri(), action.getUri());
    }
    
    
    /**
     * Constructor 5.
     *
     * @param object Object
     * @param subject Subject
     * @param action Action
     * @param inheritable Inheritance flag
     */
    public NodePermission(ObjectNode object, SubjectNode subject,
                          ActionNode action, boolean inheritable) {
        this(object, subject, action);
        this.inheritable = inheritable;
    }
    
    
    /**
     * Constructor 6.
     *
     * @param object Object
     * @param subject Subject
     * @param action Action
     * @param inheritable Inheritance flag
     * @param negative Negative flag
     */
    public NodePermission(ObjectNode object, SubjectNode subject,
                          ActionNode action, boolean inheritable,
                          boolean negative) {
        this(object, subject, action);
        this.inheritable = inheritable;
        this.negative = negative;
    }
    
    
    /**
     * Constructor 7.
     *
     * @param object Object
     * @param revisionNumber Revision number
     * @param subject Subject
     * @param action Action
     * @param inheritable Inheritance flag
     * @param negative Negative flag
     */
    public NodePermission(ObjectNode object, NodeRevisionNumber revisionNumber,
                          SubjectNode subject, ActionNode action,
                          boolean inheritable, boolean negative) {
        this(object, subject, action);
        this.inheritable = inheritable;
        if (revisionNumber != null)
            this.inheritable = false;
        this.negative = negative;
        this.revisionNumber = revisionNumber;
    }
    
    
    /**
     * Constructor 8.
     *
     * @param objectUri Object Uri
     * @param subjectUri Subject Uri
     * @param actionUri Action Uri
     * @param inheritable Inheritance flag
     * @param negative Negative flag
     */
    public NodePermission(String objectUri, String revisionNumber,
                          String subjectUri, String actionUri,
                          boolean inheritable, boolean negative) {
        this(objectUri, subjectUri, actionUri);
        this.inheritable = inheritable;
        if (revisionNumber != null) {
            this.revisionNumber = new NodeRevisionNumber(revisionNumber);
            this.inheritable = false;
        }
        this.negative = negative;
    }
    
    
    // ----------------------------------------------------- Instance Variables
    
    
    /**
     * Object Uri.
     */
    protected String objectUri;
    
    
    /**
     * Revision number.
     */
    protected NodeRevisionNumber revisionNumber;
    
    
    /**
     * Subject Uri.
     */
    protected String subjectUri;
    
    
    /**
     * Action Uri.
     */
    protected String actionUri;
    
    
    /**
     * Inheritance flag. True if permission is inheritable.
     */
    protected boolean inheritable;
    
    /**
     * URI of the resource this permission was inherited from
     * NOT TO BE MADE PERSISTENT
     */
    protected transient String inheritedFrom;
    
    
    /**
     * Negative permission flag.
     */
    protected boolean negative;
    
    
    /**
     * Invert permission flag.
     */
    protected boolean invert;
    
    /**
     * Protected flag
     */
    protected boolean protect = false;
    
    
    // ------------------------------------------------------------- Properties
    
    
    /**
     * Inheritance flag mutator.
     *
     * @param inheritable New flag value
     */
    public void setInheritable(boolean inheritable) {
        this.inheritable = inheritable;
    }
    
    
    /**
     * Inheritance flag accessor.
     *
     * @return boolean True if permission is inheritable
     */
    public boolean isInheritable() {
        return inheritable;
    }
    
    /**
     * Method setInheritedFrom
     *
     * @param  uri  the URI this permission will be inherited from
     *
     */
    public void setInheritedFrom(String uri) {
        this.inheritedFrom = uri;
    }
    
    /**
     * Method getInheritedFrom
     *
     * @return   the URI this permission was inherited from
     *
     */
    public String getInheritedFrom() {
        return inheritedFrom;
    }
    
    
    /**
     * Negative flag mutator.
     *
     * @param negative New negative value
     */
    public void setNegative(boolean negative) {
        this.negative = negative;
    }
    
    
    /**
     * Negative flag accessor.
     *
     * @return boolean True if permission is negative
     */
    public boolean isNegative() {
        return negative;
    }
    
    
    /**
     * Method setInvert
     *
     * @param    invert              a  boolean
     */
    public void setInvert(boolean invert) {
        this.invert = invert;
    }
    
    /**
     * Method isInvert
     *
     * @return   a boolean
     */
    public boolean isInvert() {
        return invert;
    }
    
    /**
     * Method setProtected
     *
     * @param    protect             a  boolean
     *
     */
    public void setProtected(boolean protect) {
        this.protect = protect;
    }
    
    /**
     * Method isProtected
     *
     * @return   true, if this permission cannot be removed
     *
     */
    public boolean isProtected() {
        return protect;
    }
    
    
    /**
     * Object Uri accessor.
     *
     * @return String Object Uri
     */
    public String getObjectUri() {
        return objectUri;
    }
    
    
    /**
     * Object Uri mutator.
     *
     * @param object New object
     */
    void setObject(ObjectNode object) {
        if (object != null) {
            this.objectUri = object.getUri();
        }
    }
    
    
    /**
     * Object Uri mutator
     *
     * @param objectUri New object Uri
     */
    public void setObject(String objectUri) {
        this.objectUri = objectUri;
    }
    
    
    /**
     * Subject Uri mutator.
     *
     * @return String Subject Uri
     */
    public String getSubjectUri() {
        return subjectUri;
    }
    
    
    /**
     * Subject Uri mutator.
     *
     * @param subject New subject
     */
    void setSubject(SubjectNode subject) {
        if (subject != null) {
            setSubject(subject.getUri());
        }
    }
    
    
    /**
     * Subject Uri mutator.
     *
     * @param subjectUri New subject Uri
     */
    void setSubject(String subjectUri) {
        this.subjectUri = SubjectNode.getSubjectNode(subjectUri).getUri();
    }
    
    SubjectNode getSubjectNode() {
        return SubjectNode.getSubjectNode(subjectUri);
    }
    
    /**
     * Revision number accessor.
     *
     * @return Revision number to which this permission applies
     */
    public NodeRevisionNumber getRevisionNumber() {
        return revisionNumber;
    }
    
    
    /**
     * Revision number mutator.
     *
     * @param revisionNumber Revision number
     */
    void setRevisionNumber(NodeRevisionNumber revisionNumber) {
        this.revisionNumber = revisionNumber;
    }
    
    
    /**
     * Action Uri accessor.
     *
     * @return String Action Uri
     */
    public String getActionUri() {
        return actionUri;
    }
    
    ActionNode getActionNode() {
        return ActionNode.getActionNode(actionUri);
    }
    
    
    /**
     * Action Uri mutator.
     *
     * @param action New action
     */
    void setAction(ActionNode action) {
        if (action != null) {
            setAction(action.getUri());
        }
    }
    
    
    /**
     * Action Uri mutator.
     *
     * @param actionUri New action Uri
     */
    void setAction(String actionUri) {
        this.actionUri = actionUri;
    }
    
    
    // --------------------------------------------------------- Object Methods
    
    
    /**
     * Equals.
     *
     * @param obj Object to test
     * @return boolean True if the two object are equal :
     * <li>obj is of type SlidePermission and is not null</li>
     * <li>All three Uris are equal</li>
     */
    public boolean equals(Object obj) {
        boolean result = false;
        if ((obj != null) && (obj instanceof NodePermission)) {
            NodePermission permission = (NodePermission) obj;
            result = (this.objectUri.equals(permission.getObjectUri()))
                && (this.subjectUri.equals(permission.getSubjectUri()))
                && (this.actionUri.equals(permission.getActionUri()))
                && (this.isNegative() == permission.isNegative());
        }
        return result;
    }
    
    
    /**
     * Hash Code.
     *
     * @return int Hash code value
     */
    public int hashCode() {
        return toString().hashCode();
    }
    
    
    /**
     * String representation of the permission.
     * <p/>
     * Format : ObjectUri-SubjectUri-ActionUri-InheritanceFlag
     *
     * @return String String representation
     */
    public String toString() {
        //        return objectUri + "-" + subjectUri + "-" + actionUri + "-"
        //            + inheritable;
        return "[object=" + objectUri + ", subject=" + subjectUri + ", action=" + actionUri + ", ->"
                + (negative ? "DENY" : "GRANT") + (inheritable ? ", INHERITABLE" : ", LOCAL") + "]";
    }
    
    
    /**
     * Clone.
     *
     * @return Object clone
     */
    public NodePermission cloneObject() {
        NodePermission result = null;
        try {
            result = (NodePermission) super.clone();
        } catch(CloneNotSupportedException e) {
        }
        return result;
    }
    
    
    /**
     * Validate.
     *
     * @param expectedUri Uri
     */
    public void validate(String expectedUri) {
        
        if (objectUri == null)
            throw new ObjectValidationFailedException
                (expectedUri, Messages.message
                     (NodePermission.class.getName() + ".nullObjectUri"));
        
        if (!objectUri.equals(expectedUri))  {
            // extra test to make "/files/x" == "/files/x/"
            String tmpObjectUri=objectUri;
            if (tmpObjectUri.endsWith("/"))
                tmpObjectUri=tmpObjectUri.substring(0,tmpObjectUri.length()-1);
            String tmpExpectedUri=expectedUri;
            if (tmpExpectedUri.endsWith("/"))
                tmpExpectedUri=tmpExpectedUri.substring(0,tmpExpectedUri.length()-1);
            
            if (!tmpObjectUri.equals(tmpExpectedUri))
                throw new ObjectValidationFailedException
                    (expectedUri, Messages.message
                         (NodePermission.class.getName() + ".incorrectObjectUri"));
        }
        
        if (subjectUri == null)
            throw new ObjectValidationFailedException
                (expectedUri, Messages.message
                     (NodePermission.class.getName() + ".nullSubjectUri"));
        
        if (actionUri == null)
            throw new ObjectValidationFailedException
                (expectedUri, Messages.message
                     (NodePermission.class.getName() + ".nullActionUri"));
        
    }
    
    
}
