Package org.apache.lenya.cms.publication

Source Code of org.apache.lenya.cms.publication.DefaultSiteTree

/*
* Copyright  1999-2004 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.
*
*/

/* @version $Id: DefaultSiteTree.java 160864 2005-04-11 11:46:24Z michi $ */

package org.apache.lenya.cms.publication;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.apache.lenya.xml.DocumentHelper;
import org.apache.lenya.xml.NamespaceHelper;
import org.apache.log4j.Logger;
import org.apache.xpath.XPathAPI;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
* Default Sitetree implementation
*/
public class DefaultSiteTree implements SiteTree, LastModified {
    private static Logger log = Logger.getLogger(DefaultSiteTree.class);
   
    private static Object lock = new Object();

    public static final String SITE_TREE_FILENAME = "sitetree.xml";

    private Document document = null;
    private File treefile = null;
    // the area is only retained to provide some more info when raising an exception.
    private String area = "";
   
    private long lastModified = 0;

    /**
     * Create a DefaultSiteTree
     *
     * @param pubDir the publication directory
     * @param area the area
     *
     * @throws SiteTreeException if an error occurs
     */
    protected DefaultSiteTree(File pubDir, String area) throws SiteTreeException {
        this(
            new File(
                pubDir,
                Publication.CONTENT_PATH
                    + File.separator
                    + area
                    + File.separator
                    + SITE_TREE_FILENAME));
        this.area = area;
    }

    /**
     * Create a DefaultSiteTree from a filename.
     *
     * @param treefilename file name of the tree
     *
     * @throws SiteTreeException if an error occurs
     * 
     * @deprecated use the DefaultSiteTree(File pubDir, String area) constructor instead.
     */
    public DefaultSiteTree(String treefilename) throws SiteTreeException {
        this(new File(treefilename));
    }

    /**
     * Create a DefaultSiteTree from a file.
     *
     * @param treefile the file containing the tree
     *
     * @throws SiteTreeException if an error occurs
     *
     * @deprecated this constructor will be private in the future
     */
    public DefaultSiteTree(File treefile) throws SiteTreeException {
        this.treefile = treefile;

        try {
            if (!treefile.isFile()) {
                //the treefile doesn't exist, so create it

                document = createDocument();
            } else {
                // Read tree
                document = DocumentHelper.readDocument(treefile);
            }
        } catch (ParserConfigurationException e) {
            throw new SiteTreeException(e);
        } catch (SAXException e) {
            throw new SiteTreeException(e);
        } catch (IOException e) {
            throw new SiteTreeException(e);
        }

    }

    /**
     * Checks if the tree file has been modified externally and reloads the site tree.
     * @throws SiteTreeException when something went wrong.
     */
    protected synchronized void checkModified() {
        if (area.equals(Publication.LIVE_AREA)
            && treefile.isFile()
            && treefile.lastModified() > lastModified) {
               
            if (log.isDebugEnabled()) {
                log.debug("Sitetree [" + treefile + "] has changed: reloading.");
            }
               
            try {
                document = DocumentHelper.readDocument(treefile);
            } catch (Exception e) {
                throw new IllegalStateException(e.getMessage());
            }
            lastModified = treefile.lastModified();
        }
    }

    /**
     * Create a new DefaultSiteTree xml document.
     *
     * @return the new site document
     *
     * @throws ParserConfigurationException if an error occurs
     */
    public synchronized Document createDocument() throws ParserConfigurationException {
        document = DocumentHelper.createDocument(NAMESPACE_URI, "site", null);

        Element root = document.getDocumentElement();
        root.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
        root.setAttribute(
            "xsi:schemaLocation",
            "http://apache.org/cocoon/lenya/sitetree/1.0  ../../../../resources/entities/sitetree.xsd");

        return document;
    }

    /**
     * Find a node in a subtree. The search is started at the
     * given node. The list of ids contains the document-id
     * split by "/".
     *
     * @param node where to start the search
     * @param ids list of node ids
     *
     * @return the node that matches the path given in the list of ids
     */
    protected synchronized Node findNode(Node node, List ids) {

        checkModified();

        if (ids.size() < 1) {
            return node;
        } else {
            NodeList nodes = node.getChildNodes();

            for (int i = 0; i < nodes.getLength(); i++) {
                NamedNodeMap attributes = nodes.item(i).getAttributes();

                if (attributes != null) {
                    Node idAttribute = attributes.getNamedItem("id");

                    if (idAttribute != null
                        && !"".equals(idAttribute.getNodeValue())
                        && idAttribute.getNodeValue().equals(ids.get(0))) {
                        return findNode(nodes.item(i), ids.subList(1, ids.size()));
                    }
                }
            }
        }

        // node wasn't found
        return null;
    }

    /** (non-Javadoc)
     * @see org.apache.lenya.cms.publication.SiteTree#addNode(org.apache.lenya.cms.publication.SiteTreeNode, java.lang.String)
     */
    public synchronized void addNode(SiteTreeNode node, String refDocumentId) throws SiteTreeException {
        this.addNode(
            node.getAbsoluteParentId(),
            node.getId(),
            node.getLabels(),
            node.visibleInNav(),
            node.getHref(),
            node.getSuffix(),
            node.hasLink(),
            refDocumentId);
    }

    /** (non-Javadoc)
     * @see org.apache.lenya.cms.publication.SiteTree#addNode(java.lang.String, java.lang.String, org.apache.lenya.cms.publication.Label[])
     */
    public synchronized void addNode(String parentid, String id, Label[] labels, boolean visibleInNav ) throws SiteTreeException {
        addNode(parentid, id, labels, visibleInNav, null, null, false);
    }

    /** (non-Javadoc)
     * @see org.apache.lenya.cms.publication.SiteTree#addNode(java.lang.String, java.lang.String, org.apache.lenya.cms.publication.Label[], boolean)
     */
    public synchronized void addNode(String parentid, String id, Label[] labels) throws SiteTreeException {
        addNode(parentid, id, labels, true);
    }

    /** (non-Javadoc)
     * @see org.apache.lenya.cms.publication.SiteTree#addNode(org.apache.lenya.cms.publication.SiteTreeNode)
     */
    public synchronized void addNode(SiteTreeNode node) throws SiteTreeException {
        this.addNode(node, null);
    }

    /** (non-Javadoc)
     * @see org.apache.lenya.cms.publication.SiteTree#addNode(java.lang.String, org.apache.lenya.cms.publication.Label[], java.lang.String, java.lang.String, boolean, java.lang.String)
     */
    public synchronized void addNode(
        String documentid,
        Label[] labels,
        boolean visibleInNav,
        String href,
        String suffix,
        boolean link,
        String refDocumentId)
        throws SiteTreeException {
        String parentid = "";
        StringTokenizer st = new StringTokenizer(documentid, "/");
        int length = st.countTokens();

        for (int i = 0; i < (length - 1); i++) {
            parentid = parentid + "/" + st.nextToken();
        }

        String id = st.nextToken();
        this.addNode(parentid, id, labels, visibleInNav, href, suffix, link, refDocumentId);
    }

    /** (non-Javadoc)
     * @see org.apache.lenya.cms.publication.SiteTree#addNode(java.lang.String, org.apache.lenya.cms.publication.Label[], java.lang.String, java.lang.String, boolean)
     */
    public synchronized void addNode(
        String documentid,
        Label[] labels,
        boolean visibleInNav,
        String href,
        String suffix,
        boolean link)
        throws SiteTreeException {
        this.addNode(documentid, labels, visibleInNav, href, suffix, link, null);
    }
    /** (non-Javadoc)
     * @see org.apache.lenya.cms.publication.SiteTree#addNode(java.lang.String, java.lang.String, org.apache.lenya.cms.publication.Label[], java.lang.String, java.lang.String, boolean)
     */
    public synchronized void addNode(
        String parentid,
        String id,
        Label[] labels,
        boolean visibleInNav,
        String href,
        String suffix,
        boolean link)
        throws SiteTreeException {
        this.addNode(parentid, id, labels, visibleInNav, href, suffix, link, null);
    }

    /** (non-Javadoc)
     * @see org.apache.lenya.cms.publication.SiteTree#addNode(java.lang.String, java.lang.String, org.apache.lenya.cms.publication.Label[], java.lang.String, java.lang.String, boolean)
     */
    public synchronized void addNode(
        String parentid,
        String id,
        Label[] labels,
        boolean visibleInNav,
        String href,
        String suffix,
        boolean link,
        String refDocumentId)
        throws SiteTreeException {

        Node parentNode = getNodeInternal(parentid);

        if (parentNode == null) {
            throw new SiteTreeException(
                "Parentid: " + parentid + " in " + area + " tree not found");
        }

        log.debug("PARENT ELEMENT: " + parentNode);

        // Check if child already exists
        Node childNode = getNodeInternal(parentid + "/" + id);

        if (childNode != null) {
            log.info("This node: " + parentid + "/" + id + " has already been inserted");

            return;
        }

        // Create node
        NamespaceHelper helper = new NamespaceHelper(NAMESPACE_URI, "", document);
        Element child = helper.createElement(SiteTreeNodeImpl.NODE_NAME);
        child.setAttribute(SiteTreeNodeImpl.ID_ATTRIBUTE_NAME, id);
       
        if (visibleInNav) {
            child.setAttribute(SiteTreeNodeImpl.VISIBLEINNAV_ATTRIBUTE_NAME, "true");
        } else {
            child.setAttribute(SiteTreeNodeImpl.VISIBLEINNAV_ATTRIBUTE_NAME, "false");
        }

        if ((href != null) && (href.length() > 0)) {
            child.setAttribute(SiteTreeNodeImpl.HREF_ATTRIBUTE_NAME, href);
        }

        if ((suffix != null) && (suffix.length() > 0)) {
            child.setAttribute(SiteTreeNodeImpl.SUFFIX_ATTRIBUTE_NAME, suffix);
        }

        if (link) {
            child.setAttribute(SiteTreeNodeImpl.LINK_ATTRIBUTE_NAME, "true");
        }

        for (int i = 0; i < labels.length; i++) {
            String labelName = labels[i].getLabel();
            Element label = helper.createElement(SiteTreeNodeImpl.LABEL_NAME, labelName);
            String labelLanguage = labels[i].getLanguage();

            if ((labelLanguage != null) && (labelLanguage.length() > 0)) {
                label.setAttribute(SiteTreeNodeImpl.LANGUAGE_ATTRIBUTE_NAME, labelLanguage);
            }

            child.appendChild(label);
        }

        // Add Node
        if (refDocumentId != null && !refDocumentId.equals("")) {
            Node nextSibling = getNodeInternal(refDocumentId);
            if (nextSibling != null) {
                parentNode.insertBefore(child, nextSibling);
            } else {
                parentNode.appendChild(child);
            }
        } else {
            parentNode.appendChild(child);
        }
        log.debug("Tree has been modified: " + document.getDocumentElement());
    }
    /**
     *  (non-Javadoc)
     * @see org.apache.lenya.cms.publication.SiteTree#addLabel(java.lang.String, org.apache.lenya.cms.publication.Label)
     */
    public synchronized void addLabel(String documentId, Label label) {
        SiteTreeNode node = getNode(documentId);
        if (node != null) {
            node.addLabel(label);
        }
    }

    /**
     *  (non-Javadoc)
     * @see org.apache.lenya.cms.publication.SiteTree#removeLabel(java.lang.String, org.apache.lenya.cms.publication.Label)
     */
    public synchronized void removeLabel(String documentId, Label label) {
        SiteTreeNode node = getNode(documentId);
        if (node != null) {
            node.removeLabel(label);
        }
    }

    /** (non-Javadoc)
     * @see org.apache.lenya.cms.publication.SiteTree#removeNode(java.lang.String)
     */
    public synchronized SiteTreeNode removeNode(String documentId) {
        assert documentId != null;

        Node node = removeNodeInternal(documentId);

        if (node == null) {
            return null;
        }

        return new SiteTreeNodeImpl(node, this);
    }
   
    /* (non-Javadoc)
     * @see org.apache.lenya.cms.publication.SiteTree#deleteNode(java.lang.String)
     */
    public void deleteNode(String documentId) throws SiteTreeException {
        Node node = this.getNodeInternal(documentId);
        Node parentNode = node.getParentNode();
        Node newNode = parentNode.removeChild(node);
    }


    /**
     * removes the node corresponding to the given document-id
     * and returns it
     *
     * @param documentId the document-id of the Node to be removed
     *
     * @return the <code>Node</code> that was removed
     */
    private synchronized Node removeNodeInternal(String documentId) {
        Node node = this.getNodeInternal(documentId);
        Node parentNode = node.getParentNode();
        Node newNode = parentNode.removeChild(node);

        return newNode;
    }

    /**
     * Find a node for a given document-id
     *
     * @param documentId the document-id of the Node that we're trying to get
     *
     * @return the Node if there is a Node for the given document-id, null otherwise
     */
    private synchronized Node getNodeInternal(String documentId) {
        StringTokenizer st = new StringTokenizer(documentId, "/");
        ArrayList ids = new ArrayList();

        while (st.hasMoreTokens()) {
            ids.add(st.nextToken());
        }

        Node node = findNode(document.getDocumentElement(), ids);
        return node;
    }

    /**
     * @see org.apache.lenya.cms.publication.SiteTree#getNode(java.lang.String)
     */
    public synchronized SiteTreeNode getNode(String documentId) {
        assert documentId != null;

        SiteTreeNode treeNode = null;

        Node node = getNodeInternal(documentId);
        if (node != null) {
            treeNode = new SiteTreeNodeImpl(node, this);
        }

        return treeNode;
    }

    /**
     * @see org.apache.lenya.cms.publication.SiteTree#getTopNodes()
     */
    public SiteTreeNode[] getTopNodes() {
        List childElements = new ArrayList();

        NamespaceHelper helper = new NamespaceHelper(NAMESPACE_URI, "", document);
        Element[] elements = helper.getChildren((Element) document.getDocumentElement(), SiteTreeNodeImpl.NODE_NAME);

        for (int i = 0; i < elements.length; i++) {
            SiteTreeNode newNode = new SiteTreeNodeImpl(elements[i], this);
            childElements.add(newNode);
        }

        return (SiteTreeNode[]) childElements.toArray(new SiteTreeNode[childElements.size()]);
    }
   
    /**
     * Move up the node amongst its siblings.
     *
     * @param documentid The document id for the node.
     * @throws SiteTreeException if the moving failed.
     */
    public synchronized void moveUp(String documentid) throws SiteTreeException {
        Node node = this.getNodeInternal(documentid);
        if (node == null) {
            throw new SiteTreeException("Node to move: " + documentid + " not found");
        }
        Node parentNode = node.getParentNode();
        if (parentNode == null) {
            throw new SiteTreeException(
                "Parentid of node with documentid: " + documentid + " not found");
        }

        Node previousNode;
        try {
            previousNode =
                XPathAPI.selectSingleNode(
                    node,
                    "(preceding-sibling::*[local-name() = 'node'])[last()]");
        } catch (TransformerException e) {
            throw new SiteTreeException(e);
        }

        if (previousNode == null) {
            log.warn("Couldn't found a preceding sibling");
            return;
        }
        Node insertNode = parentNode.removeChild(node);
        parentNode.insertBefore(insertNode, previousNode);
    }

    /**
     * Move down the node amongst its siblings.
     *
     * @param documentid The document id for the node.
     * @throws SiteTreeException if the moving failed.
     */
    public synchronized void moveDown(String documentid) throws SiteTreeException {
        Node node = this.getNodeInternal(documentid);
        if (node == null) {
            throw new SiteTreeException("Node to move: " + documentid + " not found");
        }
        Node parentNode = node.getParentNode();
        if (parentNode == null) {
            throw new SiteTreeException(
                "Parentid of node with documentid: " + documentid + " not found");
        }
        Node nextNode;
        try {
            nextNode =
                XPathAPI.selectSingleNode(
                    node,
                    "following-sibling::*[local-name() = 'node'][position()=2]");
        } catch (TransformerException e) {
            throw new SiteTreeException(e);
        }

        Node insertNode = parentNode.removeChild(node);

        if (nextNode == null) {
            log.warn("Couldn't found the second following sibling");
            parentNode.appendChild(insertNode);
        } else {
            parentNode.insertBefore(insertNode, nextNode);
        }
    }

    /** (non-Javadoc)
     * @see org.apache.lenya.cms.publication.SiteTree#importSubtree(org.apache.lenya.cms.publication.SiteTreeNode, org.apache.lenya.cms.publication.SiteTreeNode, java.lang.String)
     */
    public synchronized void importSubtree(
        SiteTreeNode newParent,
        SiteTreeNode subtreeRoot,
        String newid,
        String refDocumentId)
        throws SiteTreeException {
        assert subtreeRoot != null;
        assert newParent != null;
        String parentId = newParent.getAbsoluteId();
        String id = newid;

        this.addNode(
            parentId,
            id,
            subtreeRoot.getLabels(),
            subtreeRoot.visibleInNav(),
            subtreeRoot.getHref(),
            subtreeRoot.getSuffix(),
            subtreeRoot.hasLink(),
            refDocumentId);
        newParent = this.getNode(parentId + "/" + id);
        if (newParent == null) {
            throw new SiteTreeException("The added node was not found.");
        }
        SiteTreeNode[] children = subtreeRoot.getChildren();
        if (children == null) {
            log.info("The node " + subtreeRoot.toString() + " has no children");
            return;
        } else {            for (int i = 0; i < children.length; i++) {
                importSubtree(newParent, children[i], children[i].getId(), null);
            }
        }
    }

    /** (non-Javadoc)
     * @see org.apache.lenya.cms.publication.SiteTree#save()
     */
    public synchronized void save() throws SiteTreeException {
        try {
            DocumentHelper.writeDocument(document, treefile);
        } catch (TransformerException e) {
            throw new SiteTreeException(
                "The document [" + document.getLocalName() + "] could not be transformed");
        } catch (IOException e) {
            throw new SiteTreeException(
                "The saving of document [" + document.getLocalName() + "] failed");
        }
        lastModified = new Date().getTime();   
    }

    /**
     * @see org.apache.lenya.cms.publication.SiteTree#setLabel(java.lang.String, org.apache.lenya.cms.publication.Label)
     */
    public synchronized void setLabel(String documentId, Label label) {
        SiteTreeNode node = getNode(documentId);
        if (node != null) {
            node.setLabel(label);
        }
    }

    /**
     * @see org.apache.lenya.cms.publication.SiteTree#copy(org.apache.lenya.cms.publication.SiteTreeNode, org.apache.lenya.cms.publication.SiteTreeNode, java.lang.String, java.lang.String)
     */
    public void copy(SiteTreeNode src, SiteTreeNode dst, String newId, String followingSibling) throws SiteTreeException {
        assert dst instanceof SiteTreeNodeImpl;
       
        SiteTreeNodeImpl dstNode = (SiteTreeNodeImpl)dst;
        if (this.equals(dstNode.getDefaultSiteTree())) {
            // Copy if this sitetree is the destination sitetree.
            // Acquire global sitetree lock to establish lock hierarchy in case of copy operation
            synchronized(DefaultSiteTree.lock) {
                DefaultSiteTree srcSiteTree = ((SiteTreeNodeImpl)src).getDefaultSiteTree();
                synchronized(srcSiteTree) {
                    synchronized(this) {
                        String parentId = dst.getAbsoluteId();
                        String id = newId;
           
                        this.addNode(
                            parentId,
                            id,
                            src.getLabels(),
                            src.visibleInNav(),
                            src.getHref(),
                            src.getSuffix(),
                            src.hasLink(),
                            followingSibling);
                        SiteTreeNode node = this.getNode(parentId + "/" + id);
                        if (node == null) {
                            throw new SiteTreeException("The added node was not found.");
                        }
                        SiteTreeNode[] children = src.getChildren();
                        if (children == null) {
                            log.debug("The node " + src.toString() + " has no children");
                            return;
                        } else {
                            for (int i = 0; i < children.length; i++) {
                                copy(children[i], node, children[i].getId(), null);
                            }
                        }
                    }
                }
            }
        } else {
            // Delegate copy operation to destination sitetree.
            dstNode.getDefaultSiteTree().copy(src, dst, newId, followingSibling);
        }
    }

    /**
     * @see org.apache.lenya.cms.publication.SiteTree#move(org.apache.lenya.cms.publication.SiteTreeNode, org.apache.lenya.cms.publication.SiteTreeNode, java.lang.String, java.lang.String)
     */
    public void move(SiteTreeNode src, SiteTreeNode dst, String newId, String followingSibling) throws SiteTreeException {
        assert dst != null;
        assert src instanceof SiteTreeNodeImpl;
       
        // Acquire global sitetree lock to establish lock hierarchy in case of move operation
        synchronized(DefaultSiteTree.lock) {
            // Lock both source and destination sitetree.
            synchronized(((SiteTreeNodeImpl)src).getDefaultSiteTree()) {
                synchronized(((SiteTreeNodeImpl)dst).getDefaultSiteTree()) {
                    copy(src, dst, newId, followingSibling);
                    DefaultSiteTree sitetree = ((SiteTreeNodeImpl)src).getDefaultSiteTree();
                    sitetree.deleteNode(src.getAbsoluteId());
                }
            }
        }
    }

    /**
     * @see org.apache.lenya.cms.publication.LastModified#getLastModified()
     */
    public long getLastModified() {
        return lastModified;
    }
}
TOP

Related Classes of org.apache.lenya.cms.publication.DefaultSiteTree

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.