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