All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.day.cq.wcm.foundation.Navigation Maven / Gradle / Ivy

/*
 * Copyright 1997-2010 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */
package com.day.cq.wcm.foundation;

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

import org.apache.commons.lang3.StringEscapeUtils;

import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageFilter;
import com.day.text.Text;

/**
 * Provides a generic utility class that can be used to draw a navigation.
 * It specifically does this by providing an iterator over navigation elements.
 *
 * A navigation element reflects a page and can have different {@link Element.Type}s.
 * Note that the same page might be returned 4 times for the different element
 * types. this offers maximal flexibility when drawing the navigation.
 */
public class Navigation implements Iterable {

    /**
     * page filter
     */
    private final PageFilter filter;

    /**
     * navigation depth
     */
    private final int depth;

    /**
     * root page
     */
    private final Page root;

    /**
     * current path
     */
    private final String currentPath;

    /**
     * current path with "/" suffix
     */
    private final String currentPathS;

    /**
     * Creates a new navigation object.
     * @param current the current page
     * @param absParent the abs level of the navigation root
     * @param filter a page filter for filtering the navigation pages
     * @param depth navigation depth.
     */
    public Navigation(Page current, int absParent, PageFilter filter, int depth) {
        currentPath = current.getPath();
        currentPathS = currentPath + "/";
        Page r = current.getAbsoluteParent(absParent);
        root = r == null ? current : r;
        this.filter = filter;
        this.depth = depth;
    }

    /**
     * Returns an iterator over the navigation elements.
     * @return an iterator.
     */
    public Iterator iterator() {
        List list = new ArrayList();
        if (root != null) {
            fillList(list, root.listChildren(filter), depth-1);
        }
        return list.iterator();
    }

    /**
     * Internally fills the list of navigation elements.
     * @param list the list to fill
     * @param pages current iterator over the pages
     * @param limit current recursion limit
     */
    private void fillList(List list, Iterator pages, int limit) {
        boolean first = true;
        while (pages.hasNext()) {
            Page p = pages.next();
            Element e = new Element(p);
            e.first = first;
            first = false;
            e.last = !pages.hasNext();
            String path = p.getPath();
            if (path.equals(currentPath)) {
                e.current = true;
                e.onTrail = true;
            } else if (currentPathS.startsWith(path + "/")) {
                e.onTrail = true;
            }
            Iterator cs = null;
            if (limit > 0) {
                cs = p.listChildren(filter);
            }
            if (cs != null && cs.hasNext()) {
                e.children = true;
            }
            e.type = Element.Type.ITEM_BEGIN;
            list.add(e);
            if (e.children) {
                list.add(new Element(e, Element.Type.NODE_OPEN));
                fillList(list, cs, limit-1);
                list.add(new Element(e, Element.Type.NODE_CLOSE));
            }
            list.add(new Element(e, Element.Type.ITEM_END));
        }
    }

    /**
     * Navigation element.
     */
    public static class Element {

        /**
         * Type of the navigation element
         */
        public static enum Type {

            /**
             * Denotes that is element is an open node, e.g. an <UL> tag.
             */
            NODE_OPEN,

            /**
             * Denotes that this element is the beginning of an item, e.g. an <LI> tag.
             */
            ITEM_BEGIN,

            /**
             * Denotes that this element is the end of an item, e.g. an </LI> tag.
             */
            ITEM_END,

            /**
             * Denotes that is element is a closed node, e.g. an </UL> tag.
             */
            NODE_CLOSE
        }

        /**
         * element type
         */
        private Type type = Type.ITEM_BEGIN;

        /**
         * underlying page
         */
        private final Page page;

        /**
         * the navigation title
         */
        private final String title;

        /**
         * true if this element has children
         */
        private boolean children;

        /**
         * true if this element is on the trail
         */
        private boolean onTrail;

        /**
         * true if this element is the current one
         */
        private boolean current;

        /**
         * true if this is the first element of it's siblings
         */
        private boolean first;

        /**
         * true if this is the last element of it's siblings
         */
        private boolean last;

        /**
         * Creates a new element based on a given one, but with a different type.
         * @param e base element
         * @param type new type
         */
        private Element(Element e, Type type) {
            this.type = type;
            page = e.page;
            title = e.title;
            children = e.children;
            onTrail = e.onTrail;
            current = e.current;
            first = e.first;
            last = e.last;
        }

        /**
         * Creates a new element
         * @param page the underlying page
         */
        private Element(Page page) {
            this.page = page;
            String t = page.getNavigationTitle();
            if (t == null) {
                t = page.getTitle();
            }
            title = t == null ? page.getName() : t;
        }

        /**
         * Returns the element type.
         * @return the element type.
         */
        public Type getType() {
            return type;
        }

        /**
         * Returns the underlying page.
         * @return the underlying page.
         */
        public Page getPage() {
            return page;
        }

        /**
         * Returns the escaped path of the underlying page.
         *
         * Node that the path is escaped using {@link Text#escape(String, char, boolean)}
         *
         * @return the escaped path.
         */
        public String getPath() {
            return Text.escape(page.getPath(), '%', true);
        }

        /**
         * Returns the unescaped navigation title of the underlying page.
         * if the page does not specific a navigation title, the title is used
         * and ultimately it's name.
         *
         * @return the navigation title
         *
         * @see Page#getNavigationTitle()
         * @see Page#getTitle()
         * @see Page#getName()
         */
        public String getRawTitle() {
            return title;
        }

        /**
         * Returns the escaped navigation title of the underlying page. if the
         * page does not specific a navigation title, the title is used and
         * ultimately it's name.
         *
         * Note that the title is escaped using {@link StringEscapeUtils#escapeHtml4(String)}
         *
         * @return the navigation title
         *
         * @see Page#getNavigationTitle()
         * @see Page#getTitle()
         * @see Page#getName()
         */
        public String getTitle() {
            return StringEscapeUtils.escapeHtml4(title);
        }

        /**
         * Checks if the this element has children.
         *
         * Note that this is always true for
         * {@link Type#NODE_OPEN} and {@link Type#NODE_CLOSE} elements.
         *
         * @return true if the current element has children.
         */
        public boolean hasChildren() {
            return children;
        }

        /**
         * Checks if this element is on the trail. i.e. if the underlying page
         * is the same or an ancestor of any degree of the current page, passed
         * in the constructor of the navigation object.
         *
         * @return true if this element is on the trail.
         */
        public boolean isOnTrail() {
            return onTrail;
        }

        /**
         * Checks if this element is the current one. i.e. if the underlying page
         * is the same as current one, passed in the constructor of the navigation
         * object.
         *
         * @return true if this element is the current one.
         */
        public boolean isCurrent() {
            return current;
        }

        /**
         * Checks if this element is the first of it's siblings.
         * @return true if this element is the first of it's siblings.
         */
        public boolean isFirst() {
            return first;
        }

        /**
         * Checks if this element is the last of it's siblings.
         * @return true if this element is the last of it's siblings.
         */
        public boolean isLast() {
            return last;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy