001: package ru.emdev.EmForge.web.bean;
002:
003: import java.util.Collection;
004: import java.util.LinkedList;
005: import java.util.List;
006:
007: import javax.faces.context.FacesContext;
008:
009: import org.apache.commons.lang.StringUtils;
010: import org.apache.commons.logging.Log;
011: import org.apache.commons.logging.LogFactory;
012:
013: import ru.emdev.EmForge.wiki.web.bean.Crumb;
014:
015: /**
016: * Trail Controller This class is implemented by storing a breadcrumb trail,
017: * which is a fixed size queue. This queue is displayed as a series of links
018: * separated by a separator character.
019: *
020: * @author spopov
021: */
022: public class TrailController {
023: protected final Log logger = LogFactory.getLog(getClass());
024:
025: public static final int MAX_TRAIL_SIZE = 5;
026:
027: private LinkedList<Crumb> m_trail = null;
028:
029: private LinkedList<Crumb> m_trailRecycle = null;
030:
031: public TrailController() {
032:
033: if (m_trail == null) {
034: m_trail = new LinkedList<Crumb>();
035: }
036: if (m_trailRecycle == null) {
037: m_trailRecycle = new LinkedList<Crumb>();
038: }
039: }
040:
041: public Collection<Crumb> getTrailHeadList() {
042: return m_trail;
043: }
044:
045: public Collection<Crumb> getTrailRecycleList() {
046: return m_trailRecycle;
047: }
048:
049: /**
050: * push "crumb" to trail list
051: *
052: * @param i_requestServletPath
053: * @param i_pageName
054: * @param i_displayName
055: * if null it equals i_pageName
056: */
057: public void push(String i_displayName, String i_url) {
058: push(new Crumb(i_displayName, i_url));
059: }
060:
061: public void push(Crumb i_crumb) {
062:
063: if (StringUtils.isEmpty(i_crumb.getFullDisplayName())
064: || StringUtils.isEmpty(i_crumb.getUrl())) {
065: return;
066: }
067:
068: if (m_trail.isEmpty()) {
069: m_trail.add(i_crumb);
070: } else if (!m_trail.getLast().equals(i_crumb)) {
071: // add a crumb to the queue if the page was NOT just refreshed
072:
073: int indexInTrail = getIndexOfInHead(i_crumb);
074: int indexInRecycle = getIndexOfInTail(i_crumb);
075:
076: if (indexInTrail != -1) {
077: removeTailToRecycle(indexInTrail + 1);
078: } else if (indexInRecycle != -1) {
079: restoreTailFromRecycle(indexInRecycle);
080: } else {
081: m_trail.add(i_crumb);
082: if (m_trail.size() > MAX_TRAIL_SIZE) {
083: m_trail.removeFirst();
084: }
085: m_trailRecycle.clear();
086: }
087: }
088: }
089:
090: /**
091: * gets index of object
092: *
093: * @return -1 if not exists
094: */
095: protected int getIndexOfInHead(Crumb i_crumb) {
096: return m_trail.indexOf(i_crumb);
097: }
098:
099: /**
100: * gets index of object
101: *
102: * @return -1 if not exists
103: */
104: protected int getIndexOfInTail(Crumb i_crumb) {
105: return m_trailRecycle.indexOf(i_crumb);
106: }
107:
108: /**
109: * moves the elements of trail from i_index to last element, exclusive
110: *
111: * @param i_index
112: */
113: protected void removeTailToRecycle(int i_indexFrom) {
114: List<Crumb> removingList = getTail(m_trail, i_indexFrom);
115: List<Crumb> remainder = getHaed(m_trail, i_indexFrom - 1);
116:
117: // refresh trail
118: m_trail.clear();
119: if (remainder != null) {
120: m_trail.addAll(remainder);
121: }
122: // remove trail's tail to recycle
123: // m_trailRecycle.clear();
124: if (removingList != null) {
125: m_trailRecycle.addAll(0, removingList);
126: }
127: }
128:
129: /**
130: * restores tail from recycle
131: *
132: * @param i_toIndex
133: * index of the elements to resotered
134: */
135: protected void restoreTailFromRecycle(int i_indexTo) {
136: List<Crumb> res = getHaed(m_trailRecycle, i_indexTo);
137: List<Crumb> remainder = getTail(m_trailRecycle, i_indexTo + 1);
138:
139: // refresh the recycle
140: m_trailRecycle.clear();
141: if (remainder != null) {
142: m_trailRecycle.addAll(remainder);
143: }
144: if (res != null) {
145: m_trail.addAll(res);
146: }
147: }
148:
149: @SuppressWarnings("unchecked")
150: protected List<Crumb> getTail(LinkedList i_list, int i_indexFrom) {
151: List res = null;
152: int lastIndex = i_list.size() - 1;
153: //
154: // If fromIndex and toIndex are equal, method subList() returns empty
155: // list.
156: // So in this case we have to return via methos get()
157: //
158: if (i_indexFrom < lastIndex) {
159: res = new LinkedList(i_list.subList(i_indexFrom,
160: lastIndex + 1));
161: } else if (i_indexFrom == lastIndex) {
162: res = new LinkedList();
163: res.add(i_list.getLast());
164: }
165: return res;
166: }
167:
168: @SuppressWarnings("unchecked")
169: protected List<Crumb> getHaed(LinkedList i_list, int indexTo) {
170: List res = null;
171: //
172: // If fromIndex and toIndex are equal, method subList() returns empty
173: // list.
174: // So in this case we have to return the first element via method
175: // getFirst()
176: //
177: if (indexTo > 0 && indexTo < i_list.size()) {
178: res = new LinkedList(i_list.subList(0, indexTo + 1));
179: } else {
180: res = new LinkedList();
181: res.add(i_list.getFirst());
182: }
183: return res;
184: }
185:
186: public void backToPreviousUrl() {
187: int previousIndex = m_trail.size() - 2;
188: if (previousIndex >= 0) {
189: try {
190: FacesContext.getCurrentInstance().getExternalContext()
191: .redirect(m_trail.get(previousIndex).getUrl());
192: } catch (Exception e) {
193: logger.error("Cannot back to previous page: ", e);
194: }
195: }
196: }
197:
198: /** Returns URL currently placed on the top of stack
199: *
200: * @return
201: */
202: public String getCurrentUrl() {
203: if (m_trail.size() > 0) {
204: return m_trail.get(m_trail.size() - 1).getUrl();
205: } else {
206: return null;
207: }
208: }
209: }
|