001    /* 
002        Licensed to the Apache Software Foundation (ASF) under one
003        or more contributor license agreements.  See the NOTICE file
004        distributed with this work for additional information
005        regarding copyright ownership.  The ASF licenses this file
006        to you under the Apache License, Version 2.0 (the
007        "License"); you may not use this file except in compliance
008        with the License.  You may obtain a copy of the License at
009    
010           http://www.apache.org/licenses/LICENSE-2.0
011    
012        Unless required by applicable law or agreed to in writing,
013        software distributed under the License is distributed on an
014        "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015        KIND, either express or implied.  See the License for the
016        specific language governing permissions and limitations
017        under the License.  
018     */
019    package org.apache.wiki.tags;
020    
021    import org.apache.wiki.WikiContext;
022    
023    import javax.servlet.http.HttpSession;
024    import javax.servlet.jsp.JspWriter;
025    
026    import org.apache.log4j.Logger;
027    
028    import java.io.IOException;
029    import java.io.Serializable;
030    import java.util.LinkedList;
031    
032    /**
033     * Implement a "breadcrumb" (most recently visited) trail.  This tag can be added to any view jsp page.
034     * Separate breadcrumb trails are not tracked across multiple browser windows.<br>
035     * The optional attributes are:
036     * <p>
037     * <b>maxpages</b>, the number of pages to store, 10 by default<br>
038     * <b>separator</b>, the separator string to use between pages, " | " by default<br>
039     * </p>
040     *
041     * <p>
042     * This class is implemented by storing a breadcrumb trail, which is a
043     * fixed size queue, into a session variable "breadCrumbTrail".
044     * This queue is displayed as a series of links separated by a separator
045     * character.
046     * </p>
047     */
048    public class BreadcrumbsTag extends WikiTagBase
049    {
050        private static final long serialVersionUID = 0L;
051    
052        private static final Logger log = Logger.getLogger(BreadcrumbsTag.class);
053        /** The name of the session attribute representing the breadcrumbtrail */
054        public static final String BREADCRUMBTRAIL_KEY = "breadCrumbTrail";
055        private int m_maxQueueSize = 11;
056        private String m_separator = ", ";
057    
058        /**
059         *  {@inheritDoc}
060         */
061        @Override
062        public void initTag()
063        {
064            super.initTag();
065            m_maxQueueSize = 11;
066            m_separator = ", ";
067        }
068    
069        /**
070         *  Returns the maxpages.  This may differ from what was set by setMaxpages().
071         *  
072         *  @return The current size of the pages.
073         */
074        public int getMaxpages()
075        {
076            return m_maxQueueSize;
077        }
078    
079        /**
080         *  Sets how many pages to show.
081         *  
082         *  @param maxpages The amount.
083         */
084        public void setMaxpages(int maxpages)
085        {
086            m_maxQueueSize = maxpages + 1;
087        }
088    
089        /**
090         *  Get the separator string.
091         *  
092         *  @return The string set in setSeparator()
093         */
094        public String getSeparator()
095        {
096            return m_separator;
097        }
098    
099        /**
100         *  Set the separator string.
101         *  
102         *  @param separator A string which separates the page names.
103         */
104        public void setSeparator(String separator)
105        {
106            m_separator = separator;
107        }
108    
109        /**
110         *  {@inheritDoc}
111         */
112        @Override
113        public int doWikiStartTag() throws IOException
114        {
115            HttpSession session = pageContext.getSession();
116            FixedQueue  trail = (FixedQueue) session.getAttribute(BREADCRUMBTRAIL_KEY);
117    
118            String page = m_wikiContext.getPage().getName();
119    
120            if( trail == null )
121            {
122                trail = new FixedQueue(m_maxQueueSize);
123            } else {
124                //  check if page still exists (could be deleted/renamed by another user)
125                for (int i = 0;i<trail.size();i++) {
126                    if (!m_wikiContext.getEngine().pageExists(trail.get(i))) {
127                        trail.remove(i);
128                    }
129                }
130            }
131    
132            if (m_wikiContext.getRequestContext().equals(WikiContext.VIEW))
133            {
134                if (m_wikiContext.getEngine().pageExists(page))
135                {
136                    if (trail.isEmpty())
137                    {
138                        trail.pushItem(page);
139                    }
140                    else
141                    {
142                        //
143                        // Don't add the page to the queue if the page was just refreshed
144                        //
145                        if (!trail.getLast().equals(page))
146                        {
147                            trail.pushItem(page);
148                        }
149                    }
150                }
151                else
152                {
153                    log.debug("didn't add page because it doesn't exist: " + page);
154                }
155            }
156    
157            session.setAttribute(BREADCRUMBTRAIL_KEY, trail);
158    
159            //
160            //  Print out the breadcrumb trail
161            //
162    
163            // FIXME: this code would be much simpler if we could just output the [pagename] and then use the
164            // wiki engine to output the appropriate wikilink
165    
166            JspWriter out     = pageContext.getOut();
167            int queueSize     = trail.size();
168            String linkclass  = "wikipage";
169            String curPage    = null;
170    
171            for( int i = 0; i < queueSize - 1; i++ )
172            {
173                curPage = trail.get(i);
174    
175                //FIXME: I can't figure out how to detect the appropriate jsp page to put here, so I hard coded Wiki.jsp
176                //This breaks when you view an attachment metadata page
177                out.print("<a class=\"" + linkclass + "\" href=\"" + m_wikiContext.getViewURL(curPage)+ "\">" + curPage + "</a>");
178    
179                if( i < queueSize - 2 )
180                {
181                    out.print(m_separator);
182                }
183            }
184    
185            return SKIP_BODY;
186        }
187    
188        /**
189         * Extends the LinkedList class to provide a fixed-size queue implementation
190         */
191        public static class FixedQueue
192            extends LinkedList<String>
193            implements Serializable
194        {
195            private int m_size;
196            private static final long serialVersionUID = 0L;
197    
198            FixedQueue(int size)
199            {
200                m_size = size;
201            }
202    
203            String pushItem(String o)
204            {
205                add(o);
206                if( size() > m_size )
207                {
208                    return removeFirst();
209                }
210    
211                return null;
212            }
213            
214            /**
215             * @param pageName
216             *            the page to be deleted from the breadcrumb
217             */
218            public void removeItem(String pageName)
219            {
220                for (int i = 0; i < size(); i++)
221                {
222                    String page = get(i);
223                    if (page != null && page.equals(pageName))
224                    {
225                        remove(page);
226                    }
227                }
228            }
229    
230        }
231    }
232