/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/taglib/src/common/org/apache/slide/taglib/bean/NodeBean.java,v 1.3 2004/07/30 06:51:47 ozeigermann Exp $
 * $Revision: 1.3 $
 * $Date: 2004/07/30 06:51:47 $
 *
 * ====================================================================
 *
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999 The Apache Software Foundation.  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. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions 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.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:  
 *       "This product includes software developed by the 
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. the names "The Jakarta Project", "Slide", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written 
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 * [Additional notices, if required by prior licensing conditions]
 *
 */ 

package org.apache.slide.taglib.bean;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import org.apache.slide.common.NamespaceAccessToken;
import org.apache.slide.common.NamespaceConfig;
import org.apache.slide.common.SlideException;
import org.apache.slide.common.SlideToken;
import org.apache.slide.content.Content;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.content.NodeRevisionDescriptors;
import org.apache.slide.content.NodeRevisionNumber;
import org.apache.slide.lock.Lock;
import org.apache.slide.lock.NodeLock;
import org.apache.slide.security.NodePermission;
import org.apache.slide.security.Security;
import org.apache.slide.structure.ActionNode;
import org.apache.slide.structure.GroupNode;
import org.apache.slide.structure.LinkNode;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.structure.Structure;
import org.apache.slide.structure.SubjectNode;

/**
 * Provides a Java Bean style interface wrapped around an ObjectNode and the 
 * corresponding NodeRevisionDescriptors.
 *
 * <p>This class is intended to be used in the context of web presentation 
 * layer technologies such as JSP or Velocity.</p>
 * 
 * @author <a href="mailto:cmlenz@apache.org">Christopher Lenz</a>
 * @version $Revision: 1.3 $
 */
public class NodeBean
    extends AbstractBean {
    
    
    // ----------------------------------------------------- Instance Variables
    
    
    /**
     * The wrapped ObjectNode.
     */
    private ObjectNode node;
    
    
    /**
     * The NodeRevisionDescriptors associated with the ObjectNode.
     */
    private NodeRevisionDescriptors revisionDescriptors;
    
    
    // ----------------------------------------------------------- Construction
    
    
    /**
     * Default constructor.
     */
    public NodeBean() {
        super();
        
    }
    
    
    /**
     * Constructor.
     *
     * @param nat the namespace access token
     * @param st the slide token
     * @param node the node to wrap
     */
    public NodeBean(NamespaceAccessToken nat, SlideToken st, ObjectNode node) {
        super(nat, st);
        
        setObjectNode(node);
    }
    
    
    // ------------------------------------------------------------- Properties
    
    
    /**
     * Returns the names of the branches that exist for the node.
     * 
     * @return a List of Strings, or an empty List if no branches exist for
     *         the node
     */
    public List getBranches() {
        
        List branches = new ArrayList();
        if (revisionDescriptors != null) {
            Enumeration enum = revisionDescriptors.enumerateBranchNames();
            while (enum.hasMoreElements()) {
                branches.add((String)enum.nextElement());
            }
        }
        
        return branches;
    }
    
    
    /**
     * Returns the children of the node.
     *
     * @return a List  of NodeBean objects, or an empty List if the node
     *         doesn't have any children
     * 
     * @see #getHasChildren
     */
    public List getChildren() {
        
        List children = new ArrayList();
        Enumeration enum = node.enumerateChildren();
        while (enum.hasMoreElements()) {
            try {
                Structure structure = nat.getStructureHelper();
                ObjectNode node =
                    structure.retrieve(st, (String)enum.nextElement(), 
                                       false);
                children.add(new NodeBean(nat, st, node));
            } catch (SlideException e) {
                // ignore for now
            }
        }
        
        return children;
    }
    
    
    /**
     * Determines whether the node has children.
     *
     * @return true if the node has children, false if not
     * 
     * @see #getChildren
     */
    public boolean getHasChildren() {
        
        return node.hasChildren();
    }
    
    
    /**
     * Determines whether any kind of lock exists on the node.
     *
     * @return true if the node is locked, false if not
     * 
     * @see #getLocks
     */
    public boolean getIsLocked() {
        
        boolean result = false;
        try {
            Lock lock = nat.getLockHelper();
            result = lock.isLocked(st, node, new SubjectNode("/"),
                                   new ActionNode("/"), false);
        }
        catch (SlideException e) {
            // ignore for now
        }
        
        return result;
    }
    
    
    /**
     * Returns the list of active locks on the node.
     *
     * @return a List of LockBean objects, or an empty List if the node
     *         is not locked
     * 
     * @see #getIsLocked
     */
    public List getLocks() {
        
        List locks = new ArrayList();
        try {
            Lock lock = nat.getLockHelper();
            Enumeration enum = lock.enumerateLocks(st, node.getUri(), false);
            while (enum.hasMoreElements()) {
                NodeLock nl = (NodeLock)enum.nextElement();
                if (nl != null) {
                    locks.add(new LockBean(nat, st, nl));
                }
            }
        } catch (SlideException e) {
            // ignore for now
        }
        
        return locks;
    }
    
    
    /**
     * Returns the leaf name of the node's URI.
     *
     * @return the leaf name of the node (which is the last section of the URI)
     */
    public String getName() {
        
        String name = node.getUri();
        int i = name.lastIndexOf("/");
        if (i >= 0) {
            name = name.substring(i + 1);
        }
        
        return name;
    }
    
    
    /**
     * Returns the parent of the node.
     *
     * @return NodeBean of the node's parent, or null if the node is the root 
     *         of the namespace
     */
    public NodeBean getParent() {
        
        NodeBean parent = null;
        try {
            Structure structure = nat.getStructureHelper();
            ObjectNode on = structure.getParent(st, node);
            if (on != null) {
                parent = new NodeBean(nat, st, on);
            }
        }
        catch (SlideException e) {
            // ignore for now
        }
        
        return parent;
    }
    
    
    /**
     * Returns the permissions on the node.
     *
     * @return a List of PermissionBean objects, or an empty List if 
     *         no permissions exist for the node
     */
    public List getPermissions() {
        
        List perms = new ArrayList();
        try {
            Security security = nat.getSecurityHelper();
            Enumeration enum = security.enumeratePermissions(st, node);
            while (enum.hasMoreElements()) {
                NodePermission np = (NodePermission)enum.nextElement();
                if (np != null) {
                    perms.add(new PermissionBean(nat, st, np));
                }
            }
        } catch (SlideException e) {
            // ignore for now
        }
        
        return perms;
    }
    
    
    /**
     * Determines whether the node has revisions.
     *
     * @return true if the node has revisions, false if not
     * 
     * @see #getRevisions
     * @see #getInitialRevision
     * @see #getLatestRevision
     */
    public boolean getHasRevisions() {
        
        boolean hasRevisions = false;
        if (revisionDescriptors != null) {
            hasRevisions = revisionDescriptors.hasRevisions();
        }
        
        return hasRevisions;
    }
    
    
    /**
     * Determine whether the node is versioned.
     *
     * @return true if the node is versioned, false if not
     */
    public boolean getIsVersioned() {
        
        boolean isVersioned = false;
        if (revisionDescriptors != null) {
            isVersioned = revisionDescriptors.isVersioned();
        }
        
        return isVersioned;
    }
    
    
    /**
     * Returns the initial revision of the node.
     * 
     * @return a RevisionBean representing the initial revision, or null if 
     *         the node has no revisions
     * 
     * @see #getRevisions
     * @see #getHasRevisions
     * @see #getLatestRevision
     */
    public RevisionBean getInitialRevision() {
        
        RevisionBean bean = null;
        try {
            if (revisionDescriptors != null) {
                Content content = nat.getContentHelper();
                NodeRevisionNumber nrn =
                    revisionDescriptors.getInitialRevision();
                NodeRevisionDescriptor nrd =
                    content.retrieve(st, revisionDescriptors, nrn);
                bean = new RevisionBean(nat, st, revisionDescriptors, nrd);
            }
        }
        catch (SlideException e) {
            // ignore for now
        }
        
        return bean;
    }
    
    
    /**
     * Returns the latest revision of the node.
     * 
     * @return a RevisionBean representing the latest revision, or null if the 
     *         node has no revisions
     * 
     * @see #getRevisions
     * @see #getHasRevisions
     * @see #getInitialRevision
     */
    public RevisionBean getLatestRevision() {
        
        RevisionBean revision = null;
        try {
            if (revisionDescriptors != null) {
                Content content = nat.getContentHelper();
                NodeRevisionNumber nrn = 
                    revisionDescriptors.getLatestRevision();
                NodeRevisionDescriptor nrd =
                    content.retrieve(st, revisionDescriptors, nrn);
                revision = new RevisionBean(nat, st, revisionDescriptors, nrd);
            }
        }
        catch (SlideException e) {
            // ignore for now
        }
        
        return revision;
    }
    
    
    /**
     * Returns the revisions of the node.
     *
     * @return a List of RevisionBean objects, or an empty List if the 
     *         node doesn't have revisions
     */
    public List getRevisions() {
        
        List revisions = new ArrayList();
        if (revisionDescriptors != null) {
            Enumeration enum = 
                revisionDescriptors.enumerateRevisionNumbers();
            while (enum.hasMoreElements()) {
                NodeRevisionNumber nrn =
                    (NodeRevisionNumber)enum.nextElement();
                RevisionBean rw = getRevision(nrn);
                if (rw != null) {
                    revisions.add(rw);
                }
            }
        }
        
        return revisions;
    }
    
    
    /**
     * Returns the roles of the node. This excludes the generic 
     * &quot;nobody&quot; role.
     *
     * @return a List of roles as String objects
     */
    public List getRoles() {
        
        NamespaceConfig nc = nat.getNamespaceConfig();
        String nobody = nc.NOBODY;
        
        List roles = new ArrayList();
        Security security = nat.getSecurityHelper();
        Enumeration enum = security.getRoles(node);
        while (enum.hasMoreElements()) {
            String role = (String)enum.nextElement();
            // exclude the nobody role from the list, as we only want "real" 
            // roles to show up in the list
            if (!role.equals(nobody)) {
                roles.add(role);
            }
        }
        
        return roles;
    }
    
    /**
     * Returns the type of node as String, for example &quot;action&quot;,
     * &quot;group&quot; or &quot;subject&quot;
     *
     * @return a String containing the type of node
     */
    public String getType() {
        
        if (node instanceof ActionNode) {
            return "action";
        }
        else if (node instanceof GroupNode) {
            return "group";
        }
        else if (node instanceof LinkNode) {
            return "link";
        }
        else if (node instanceof SubjectNode) {
            return "subject";
        }
        
        return "object";
    }
    
    
    /**
     * Returns the URI of the node.
     * 
     * @return a String containing the node's URI
     */
    public String getUri() {
        
        return node.getUri();
    }
    
    
    /**
     * Returns the node this bean wraps around.
     *
     * @return the wrapped ObjectNode
     */
    public ObjectNode getObjectNode() {
        
        return node;
    }
    
    
    /**
     * Sets the node this bean wraps around.
     *
     * @param node the ObjectNode to wrap
     */
    public void setObjectNode(
        ObjectNode node) {
        
        this.node = node;
        if (nat != null) {
            try {
                Content content = nat.getContentHelper();
                revisionDescriptors = content.retrieve(st, node.getUri());
            } catch (SlideException e) {
                // ignore for now
            }
        }
    }
    
    
    // --------------------------------------------------------- Public Methods
    
    
    /**
     * Returns a specific revision of the node, identified by it's revision
     * number.
     *
     * @param number The NodeRevisionNumber of the revision
     * 
     * @return a RevisionBean representing the requested revision, or 
     *         <code>null</code> if the revision does not exist
     * 
     * @see #getRevisions
     * @see #getHasRevisions
     * @see #getInitialRevision
     * @see #getLatestRevision
     * @see #getRevision(String)
     */
    public RevisionBean getRevision(NodeRevisionNumber number) {
        
        RevisionBean revision = null;
        try {
            if (revisionDescriptors != null) {
                Content content = nat.getContentHelper();
                NodeRevisionDescriptor nrd =
                    content.retrieve(st, revisionDescriptors, number);
                revision = new RevisionBean(nat, st, revisionDescriptors, nrd);
            }
        }
        catch (SlideException e) {
            // ignore for now
        }
        
        return revision;
    }
    
    
    /**
     * Returns the latest revision of the node in the specified branch.
     * 
     * @param branch name of the branch
     * 
     * @return a RevisionBean representing the latest revision in the branch, 
     *         or null if the node has no revisions or the requested branch 
     *         does not exist
     * 
     * @see #getRevisions
     * @see #getHasRevisions
     * @see #getInitialRevision
     * @see #getLatestRevision
     * @see #getRevision(NodeRevisionNumber)
     */
    public RevisionBean getRevision(String branch) {
        
        RevisionBean revision = null;
        try {
            if (revisionDescriptors != null) {
                Content content = nat.getContentHelper();
                NodeRevisionDescriptor nrd =
                    content.retrieve(st, revisionDescriptors, branch);
                revision = new RevisionBean(nat, st, revisionDescriptors, nrd);
            }
        }
        catch (SlideException e) {
            // ignore for now
        }
        
        return revision;
    }
    
    
    /**
     * Returns a String representation of the node, which is the node's URI.
     *
     * @return the node's URI
     */
    public String toString() {
        
        return getUri();
    }
    
    
}


