001: /*
002: * Copyright 2001-2006 C:1 Financial Services GmbH
003: *
004: * This software is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License Version 2.1, as published by the Free Software Foundation.
007: *
008: * This software is distributed in the hope that it will be useful,
009: * but WITHOUT ANY WARRANTY; without even the implied warranty of
010: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
011: * Lesser General Public License for more details.
012: *
013: * You should have received a copy of the GNU Lesser General Public
014: * License along with this library; if not, write to the Free Software
015: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
016: */
017:
018: package de.finix.contelligent.components.website;
019:
020: import java.io.IOException;
021: import java.io.StringWriter;
022: import java.io.Writer;
023: import java.util.Collection;
024: import java.util.HashMap;
025: import java.util.Iterator;
026: import java.util.LinkedList;
027: import java.util.List;
028: import java.util.Map;
029:
030: import de.finix.contelligent.CallData;
031: import de.finix.contelligent.Component;
032: import de.finix.contelligent.ComponentManager;
033: import de.finix.contelligent.ComponentNotFoundException;
034: import de.finix.contelligent.ComponentPath;
035: import de.finix.contelligent.Container;
036: import de.finix.contelligent.ExternalRelationSource;
037: import de.finix.contelligent.HTTPCallData;
038: import de.finix.contelligent.ModificationVetoException;
039: import de.finix.contelligent.components.CBoolean;
040: import de.finix.contelligent.components.Folder;
041: import de.finix.contelligent.content.BooleanContent;
042: import de.finix.contelligent.content.ContelligentStringContent;
043: import de.finix.contelligent.content.ContentProvider;
044: import de.finix.contelligent.content.StringContent;
045: import de.finix.contelligent.core.security.ComponentPermission;
046: import de.finix.contelligent.event.ComponentEvent;
047: import de.finix.contelligent.event.ContelligentEvent;
048: import de.finix.contelligent.event.EventQueue;
049: import de.finix.contelligent.event.EventQueueListener;
050: import de.finix.contelligent.exception.ContelligentException;
051: import de.finix.contelligent.logging.LoggingService;
052: import de.finix.contelligent.render.ParameterDescription;
053: import de.finix.contelligent.render.Renderable;
054: import de.finix.contelligent.render.Renderer;
055: import de.finix.contelligent.render.Template;
056:
057: public class MenuComposer extends Folder implements Renderable,
058: Renderer, ExternalRelationSource {
059:
060: private final static org.apache.log4j.Logger log = LoggingService
061: .getLogger(MenuComposer.class);
062:
063: private static int FOLDER_OPEN = 0;
064:
065: private static int FOLDER_CLOSED = 1;
066:
067: private static int FOLDER_SELECTED = 2;
068:
069: private static int ITEM_SELECTED = 3;
070:
071: private static int ITEM_UNSELECTED = 4;
072:
073: /**
074: * initialDetectionDepth wird verwendet, um im modus "detectSiteFolder" eine
075: * Starttiefe festzulegen ab der der Pfad "erraten" werden soll. Ausgehend
076: * von der aktuellen Seite wird der rootFolder für die Menüerzeugung ab dem
077: * <initialDetectionDepth>-ten Pfadbestandteil ermittelt. Der erste
078: * PagesFolder da gefunden wird, wird der rootFolder. Standard ist hier 0.
079: * Für Sitemaps in Teilbäumen kann man hier eine Tiefe vorgeben.
080: */
081: private int initialDetectionDepth = 0;
082:
083: private static int MAX_LEVELS = 10; // max 10 levels supported
084:
085: private ComponentPath siteFolder;
086:
087: final private static ComponentPath INDEX = new ComponentPath(
088: "index");
089:
090: final private static ComponentPath PRE = new ComponentPath("pre");
091:
092: final private static ComponentPath POST = new ComponentPath("post");
093:
094: final private static ComponentPath OPEN_FOLDER = new ComponentPath(
095: "openFolder");
096:
097: final private static ComponentPath CLOSED_FOLDER = new ComponentPath(
098: "closedFolder");
099:
100: final private static ComponentPath SELECTED_FOLDER = new ComponentPath(
101: "selectedFolder");
102:
103: final private static ComponentPath SELECTED_ITEM = new ComponentPath(
104: "selectedItem");
105:
106: final private static ComponentPath UNSELECTED_ITEM = new ComponentPath(
107: "unselectedItem");
108:
109: final private static ComponentPath CREATE_MENU_ENTRY = new ComponentPath(
110: "createMenuEntry");
111:
112: final private static ComponentPath MENU_TITLE = new ComponentPath(
113: "menuTitle");
114:
115: final private static ComponentPath MENU_TOOLTIP = new ComponentPath(
116: "menuTooltip");
117:
118: final private static String TITLE = "menu_title";
119:
120: final private static String TOOLTIP = "menu_tooltip";
121:
122: final private static String TARGET = "menu_target";
123:
124: final private static String NUMBER = "menu_number";
125:
126: final private static String ID = "menu_id";
127:
128: final private static int MENU = 0;
129:
130: final private static int PATH = 1;
131:
132: final private static int SITEMAP = 2;
133:
134: private boolean cacheOutput = false;
135:
136: private Map cachedOutput = new HashMap();
137:
138: private boolean createSitemap = false;
139:
140: private boolean useFirstPageAsIndex = true;
141:
142: private boolean detectSiteFolder = false;
143:
144: private int mode = MENU;
145:
146: private String modeAsString = "Normal";
147:
148: private EventQueueListener listener;
149:
150: private String namespacePrefix = "";
151:
152: public void postCreate() throws Exception {
153: listener = new EventListener(siteFolder, this
154: .getComponentContext().getPath());
155: EventQueue.getInstance().addListener(listener);
156: }
157:
158: public void finalize() {
159: EventQueue.getInstance().removeListener(listener);
160: }
161:
162: public boolean getCacheOutput() {
163: return cacheOutput;
164: }
165:
166: public void setCacheOutput(boolean cacheOutput) {
167: this .cacheOutput = cacheOutput;
168: }
169:
170: public int getInitialDetectionDepth() {
171: return this .initialDetectionDepth;
172: }
173:
174: public void setInitialDetectionDepth(int depth) {
175: this .initialDetectionDepth = depth;
176: }
177:
178: public boolean getUseFirstPageAsIndex() {
179: return useFirstPageAsIndex;
180: }
181:
182: public void setUseFirstPageAsIndex(boolean useFirstPageAsIndex) {
183: this .useFirstPageAsIndex = useFirstPageAsIndex;
184: }
185:
186: public boolean getDetectSiteFolder() {
187: return detectSiteFolder;
188: }
189:
190: public void setDetectSiteFolder(boolean detectSiteFolder) {
191: this .detectSiteFolder = detectSiteFolder;
192: }
193:
194: public void setSiteFolder(ComponentPath siteFolder) {
195: this .siteFolder = siteFolder;
196: }
197:
198: public ComponentPath getSiteFolder() {
199: return detectSiteFolder ? ComponentPath.EMPTY_PATH
200: : this .siteFolder;
201: }
202:
203: public void setMode(String modeAsString) {
204: this .modeAsString = modeAsString;
205: if (modeAsString.equals("Path")) {
206: mode = PATH;
207: } else if (modeAsString.equals("Sitemap")) {
208: mode = SITEMAP;
209: createSitemap = true;
210: } else {
211: mode = MENU;
212: }
213: }
214:
215: public String getMode() {
216: return modeAsString;
217: }
218:
219: public void render(Writer writer, Map parameterMap,
220: CallData callData) throws ContelligentException,
221: IOException {
222: ComponentPath siteFolder = this .siteFolder; // Local copy for autodetection
223: final boolean debugEnabled = log.isDebugEnabled();
224: if (debugEnabled) {
225: log.debug("Start composing menu");
226: }
227: String output = null;
228: String environment = callData.getEnvironment();
229: ComponentPath currentPage = callData.getContelligentSession()
230: .getHistoryPage(environment, 0);
231: if (cacheOutput) {
232: synchronized (this ) {
233: output = (String) cachedOutput.get(currentPage);
234: }
235: }
236: if (output == null) {
237: Writer menuWriter = new StringWriter(1024);
238: // collect fragments
239: int maxDepth = 0;
240: int minDepth = 99;
241: try {
242: ComponentManager manager = callData.getActualManager();
243: if (detectSiteFolder) {
244: siteFolder = detectSiteFolder(manager, currentPage,
245: callData);
246: }
247: if (siteFolder == null) {
248: log
249: .error("Site folder not set, abort printing menu!");
250: } else {
251: Container site = (Container) manager.getComponent(
252: siteFolder, callData);
253: Template[][] fragment = new Template[5][MAX_LEVELS];
254: Template[] pre = new Template[MAX_LEVELS];
255: Template[] post = new Template[MAX_LEVELS];
256: boolean[] levelDefined = new boolean[MAX_LEVELS];
257: for (Iterator i = this .getSubcomponentNames(); i
258: .hasNext();) {
259: Container levelFolder = (Container) manager
260: .getSubcomponent(this , (String) i
261: .next(), callData);
262: int level = Integer.parseInt(levelFolder
263: .getComponentContext().getPath()
264: .getName());
265: if (debugEnabled) {
266: log
267: .debug("Load menu definition for level: "
268: + level);
269: }
270: try {
271: fragment[ITEM_UNSELECTED][level] = loadTemplate(
272: manager, levelFolder,
273: UNSELECTED_ITEM, callData);
274: levelDefined[level] = true;
275: minDepth = Math.min(minDepth, level);
276: maxDepth = Math.max(maxDepth, level);
277: } catch (ContelligentException e) {
278: log
279: .error(
280: "Menu not completely defined, at least unselectedItem must be defined in folder "
281: + levelFolder
282: .getComponentContext()
283: .getPath(),
284: e);
285: }
286: try {
287: fragment[ITEM_SELECTED][level] = loadTemplate(
288: manager, levelFolder,
289: SELECTED_ITEM, callData);
290: } catch (ContelligentException e) {
291: // use unselected item as default
292: log
293: .warn("Selected item not defined, will use unselected item to display selected items.");
294: fragment[ITEM_SELECTED][level] = fragment[ITEM_UNSELECTED][level];
295: }
296: try {
297: pre[level] = loadTemplate(manager,
298: levelFolder, PRE, callData);
299: post[level] = loadTemplate(manager,
300: levelFolder, POST, callData);
301: } catch (ContelligentException e) {
302: log
303: .info("Pre and/or post fragment not found. Will not be displayed in level "
304: + level);
305: }
306: try {
307: fragment[FOLDER_OPEN][level] = loadTemplate(
308: manager, levelFolder, OPEN_FOLDER,
309: callData);
310: } catch (ContelligentException e) {
311: // level not completely defined
312: log
313: .info("(Optional) open folder not defined, will use selected item instead. ");
314: fragment[FOLDER_OPEN][level] = fragment[ITEM_SELECTED][level];
315: }
316: try {
317: fragment[FOLDER_CLOSED][level] = loadTemplate(
318: manager, levelFolder,
319: CLOSED_FOLDER, callData);
320: } catch (ContelligentException e) {
321: // level not completely defined
322: log
323: .info("(Optional) closed folder not defined, will use unselected item instead. ");
324: fragment[FOLDER_CLOSED][level] = fragment[ITEM_UNSELECTED][level];
325: }
326: try {
327: fragment[FOLDER_SELECTED][level] = loadTemplate(
328: manager, levelFolder,
329: SELECTED_FOLDER, callData);
330: } catch (ContelligentException e) {
331: // level not completely defined
332: log
333: .info("(Optional) selected folder not defined, will use selected item instead. ");
334: fragment[FOLDER_SELECTED][level] = fragment[ITEM_SELECTED][level];
335: }
336: }
337: if (debugEnabled) {
338: log.debug("Min level: " + minDepth);
339: log.debug("Max level: " + maxDepth);
340: }
341: if (currentPage == null) {
342: log.warn("Current page is not set!");
343: } else {
344: int currentLevel = currentPage.getParsedPath().length
345: - siteFolder.getParsedPath().length;
346: if (debugEnabled) {
347: log.debug("Current level is: "
348: + currentLevel);
349: }
350: if (levelDefined[0] && pre[0] != null) {
351: pre[0].write(menuWriter, this , callData);
352: }
353: renderMenu(callData, menuWriter, currentPage,
354: site, site.getComponentContext()
355: .getPath().getName(), 0,
356: currentLevel, pre, post, fragment,
357: minDepth, maxDepth, levelDefined, "ID");
358: if (levelDefined[0] && post[0] != null) {
359: post[0].write(menuWriter, this , callData);
360: }
361: }
362: }
363: } catch (ComponentNotFoundException e) {
364: log.error("Could not resolve component for menu!" + e);
365: }
366: output = menuWriter.toString();
367: if (cacheOutput) {
368: synchronized (this ) {
369: cachedOutput.put(currentPage, output);
370: }
371: }
372: }
373: if (debugEnabled) {
374: log.debug("Menu composed");
375: }
376: writer.write(output);
377: }
378:
379: private ComponentPath detectSiteFolder(ComponentManager manager,
380: ComponentPath currentPage, CallData callData) {
381: String[] parsedPath = currentPage.getParsedPath();
382: ComponentPath folder = ComponentPath.EMPTY_PATH;
383:
384: for (int i = 0; i < initialDetectionDepth; i++) {
385: folder = folder.append(parsedPath[i]);
386: }
387:
388: log.debug("Inital path '" + folder + "'");
389:
390: for (int i = initialDetectionDepth; i < parsedPath.length; i++) {
391: log.debug("Appending path element '" + parsedPath[i] + "'");
392: folder = folder.append(parsedPath[i]);
393: log.debug("Check for '" + folder + "' as SiteFolder");
394: try {
395: Component component = manager.getComponent(folder,
396: callData);
397: if (component instanceof PagesFolder) {
398: return folder;
399: }
400: } catch (ComponentNotFoundException e) {
401: log.error(
402: "Exception while trying to find site folder!",
403: e);
404: }
405: }
406: return null;
407: }
408:
409: private Template loadTemplate(ComponentManager manager,
410: Container folder, ComponentPath subcomponent,
411: CallData callData) throws ContelligentException {
412: Component component = manager.getSubcomponent(folder,
413: subcomponent, callData);
414: if (component instanceof ContentProvider
415: && ((ContentProvider) component).getContent() instanceof ContelligentStringContent) {
416: return ((ContelligentStringContent) ((ContentProvider) component)
417: .getContent()).getTemplate(callData);
418: }
419: throw new ContelligentException(
420: "Could not load template of menu item '" + subcomponent
421: + "'");
422: }
423:
424: private Page findFirstPageAsIndex(Container container,
425: ComponentManager manager, CallData callData) {
426: for (Iterator i = container.getSubcomponentNames(); i.hasNext();) {
427: ComponentPath localIndex = new ComponentPath((String) i
428: .next());
429: try {
430: Component child = manager.getSubcomponent(container,
431: localIndex, callData);
432: if (child instanceof Page) {
433: return (Page) child;
434: }
435: } catch (ComponentNotFoundException e) {
436: log.error("Child component with path '" + localIndex
437: + "' not found!");
438: }
439: }
440: return null;
441: }
442:
443: private boolean pageIsIndex(CallData callData,
444: ComponentPath pagePath, boolean useFirstPageAsIndex) {
445: try {
446: Component comp = callData.getActualManager().getComponent(
447: pagePath, callData);
448: if (comp instanceof Page) {
449: return pageIsIndex(callData, (Page) comp,
450: useFirstPageAsIndex);
451: } else {
452: return false;
453: }
454: } catch (ComponentNotFoundException cne) {
455: log.error("Component not found.", cne);
456: return false;
457: }
458: }
459:
460: private boolean pageIsIndex(CallData callData, Page page,
461: boolean useFirstPageAsIndex) {
462: boolean isIndex = false;
463: if (useFirstPageAsIndex) {
464: Component parentIndexPage = findFirstPageAsIndex(callData
465: .getActualManager().parentContainer(page), callData
466: .getActualManager(), callData);
467: if (parentIndexPage.getComponentContext().getPath().equals(
468: page.getComponentContext().getPath())) {
469: isIndex = true;
470: }
471: } else {
472: if (page.getComponentContext().getPath().getName().equals(
473: INDEX.toString())) {
474: isIndex = true;
475: }
476: }
477: return isIndex;
478: }
479:
480: private int renderMenu(CallData callData, Writer menuWriter,
481: ComponentPath currentPage, Component menuItem,
482: String menuItemName, int level, int currentLevel,
483: Template[] pre, Template[] post, Template[][] fragment,
484: int minDepth, int maxDepth, boolean[] levelDefined,
485: String id) {
486:
487: final boolean debugEnabled = log.isDebugEnabled();
488: ComponentManager manager = callData.getActualManager();
489: ComponentPath menuItemPath = menuItem.getComponentContext()
490: .getPath();
491:
492: // skip index pages...
493: boolean createMenuEntry = false;
494: if (menuItem instanceof Page) {
495: try {
496: createMenuEntry = ((BooleanContent) ((CBoolean) manager
497: .getSubcomponent((Page) menuItem,
498: getNamespacePrefix()
499: + CREATE_MENU_ENTRY, callData))
500: .getContent()).getBooleanValue(callData);
501: } catch (IOException e) {
502: log
503: .error("Could not get content for createMenuEntry in page '"
504: + menuItemPath + "'" + e.toString());
505: } catch (ComponentNotFoundException e) {
506: log
507: .error("Could not resolve createMenuEntry subcomponent for page '"
508: + menuItemPath + "'" + e.toString());
509: }
510: } else if (menuItem instanceof PagesFolder) {
511: try {
512: Component indexPage;
513: if (useFirstPageAsIndex) {
514: indexPage = findFirstPageAsIndex(
515: (Container) menuItem, manager, callData);
516: } else {
517: indexPage = manager.getSubcomponent(
518: (Container) menuItem, INDEX, callData);
519: }
520: if (indexPage instanceof Page) {
521: createMenuEntry = ((BooleanContent) ((CBoolean) manager
522: .getSubcomponent((Page) indexPage,
523: getNamespacePrefix()
524: + CREATE_MENU_ENTRY,
525: callData)).getContent())
526: .getBooleanValue(callData);
527: } else {
528: log.error("Index page '"
529: + indexPage.getComponentContext().getPath()
530: .toPath()
531: + "' must be of type page!");
532: }
533: } catch (IOException e) {
534: log
535: .error("Could not get content for createMenuEntry under "
536: + menuItem.getComponentContext()
537: .getPath().toPath()
538: + e.toString());
539: } catch (ComponentNotFoundException e) {
540: log.error("Could not resolve index page "
541: + menuItem.getComponentContext().getPath()
542: .toPath());
543: }
544: }
545: if (debugEnabled) {
546: log.debug("Current page is: " + currentPage);
547: log.debug("Level is: " + level);
548: log.debug("Menu item is: " + menuItemPath);
549: log.debug("Menu item name is: " + menuItemName);
550: }
551: boolean menuItemIsIndex = false;
552: if (menuItem instanceof Page) {
553: menuItemIsIndex = pageIsIndex(callData, (Page) menuItem,
554: useFirstPageAsIndex);
555: }
556: if (((menuItem instanceof Page) && (!menuItemIsIndex && createMenuEntry))
557: || ((menuItem instanceof PagesFolder) && createMenuEntry)) {
558: // modify components in data folder to give fragments a chance to
559: // display items...
560:
561: String target = "[no target found]";
562: String title = "[no title found]";
563: String tooltip = "[no tooltip found]";
564:
565: // check if current icon is folder and contains items...
566: boolean isFolder = false;
567: boolean isFolderWithInvisibleSubpages = false;
568:
569: Page page = null;
570: if (menuItem instanceof Page) {
571: page = (Page) menuItem;
572: if (debugEnabled)
573: log.debug("Page to compare is: " + page);
574: } else if (menuItem instanceof PagesFolder) {
575: // get subcomponent jsp page with same name as folder as target
576: try {
577: if (useFirstPageAsIndex) {
578: page = findFirstPageAsIndex(
579: (Container) menuItem, manager, callData);
580: } else {
581: page = (Page) manager.getSubcomponent(
582: (Container) menuItem, INDEX, callData);
583: }
584: String pagePath = page.getComponentContext()
585: .getPath().getPathWithEndToken();
586: menuItemPath = new ComponentPath(pagePath)
587: .parentPath();
588: } catch (ComponentNotFoundException e) {
589: log
590: .error("Could not resolve index page in folder '"
591: + menuItemPath + "': " + e);
592: }
593: for (Iterator i = ((Container) menuItem)
594: .getSubcomponentNames(); i.hasNext();) {
595: String childName = (String) i.next();
596: ComponentPath childPath = menuItem
597: .getComponentContext().getPath().append(
598: childName);
599: try {
600: Component child = manager.getComponent(
601: childPath, callData);
602: boolean includeChild = false;
603: if (child instanceof PagesFolder) {
604: includeChild = true;
605: }
606: if (child instanceof Page) {
607: includeChild = !pageIsIndex(callData,
608: (Page) child, useFirstPageAsIndex);
609: }
610: if (includeChild) {
611: if (child instanceof Page) {
612: try {
613: createMenuEntry = ((BooleanContent) ((CBoolean) manager
614: .getSubcomponent(
615: (Page) child,
616: getNamespacePrefix()
617: + CREATE_MENU_ENTRY,
618: callData))
619: .getContent())
620: .getBooleanValue(callData);
621: } catch (ComponentNotFoundException e) {
622: log
623: .error("Could not resolve createMenuEntry in folder '"
624: + menuItemPath
625: + "'" + e);
626: } catch (IOException e) {
627: log
628: .error("Could not get content for createMenuEntry in folder '"
629: + menuItemPath
630: + "'" + e);
631: }
632: if (!createMenuEntry) {
633: if (debugEnabled)
634: log
635: .debug("Item is folder containing only invisible pages");
636: isFolderWithInvisibleSubpages = true;
637: } else {
638: if (debugEnabled)
639: log.debug("Item is folder");
640: isFolderWithInvisibleSubpages = false;
641: isFolder = true;
642: break;
643: }
644: } else {
645: if (debugEnabled)
646: log.debug("Item is folder");
647: isFolderWithInvisibleSubpages = false;
648: isFolder = true;
649: break;
650: }
651: }
652: } catch (ComponentNotFoundException e) {
653: log.error("Could not resolve subcomponent '"
654: + childPath.getName()
655: + "' of menu-item '" + menuItem
656: + "' (ignored): " + e);
657: }
658: }
659: }
660: if (debugEnabled)
661: log.debug("Menu item path is: " + menuItemPath);
662:
663: if (page != null
664: && manager.callerHasPermission(callData, page,
665: ComponentPermission.READ)) {
666: try {
667: title = ((StringContent) ((ContentProvider) manager
668: .getSubcomponent(page, getNamespacePrefix()
669: + MENU_TITLE, callData))
670: .getContent()).getString(callData);
671: tooltip = ((StringContent) ((ContentProvider) manager
672: .getSubcomponent(page, getNamespacePrefix()
673: + MENU_TOOLTIP, callData))
674: .getContent()).getString(callData);
675: } catch (IOException e) {
676: log
677: .error("Could not get content for title or tooltip in page '"
678: + menuItemPath + "'" + e);
679: } catch (ContelligentException e) {
680: log
681: .error("Could not resolve title or tooltip subcomponent for page '"
682: + menuItemPath + "'" + e);
683: }
684:
685: StringBuffer url = new StringBuffer(256);
686:
687: if (callData instanceof HTTPCallData) {
688: HTTPCallData httpCallData = (HTTPCallData) callData;
689: url.append(httpCallData.getCurrentBaseURL())
690: .append(
691: page.getComponentContext()
692: .getPath()).append(
693: page.getExtension());
694: }
695: target = callData.encodeURL(url.toString());
696: if (log.isDebugEnabled())
697: log.debug("Target is: " + target);
698: if (level == maxDepth) {
699: isFolder = false;
700: }
701: callData.setRequestAttribute(TITLE, title);
702: callData.setRequestAttribute(TOOLTIP, tooltip);
703: callData.setRequestAttribute(TARGET, target);
704:
705: // calculate state of menu item
706: int state = ITEM_UNSELECTED;
707: if (!createSitemap
708: && (currentPage.equals(menuItemPath) || (currentPage
709: .isSubPathOf(menuItemPath) && level == maxDepth))) {
710: if (debugEnabled)
711: log.debug("Item is selected");
712: state = ITEM_SELECTED;
713: } else if (!createSitemap
714: && pageIsIndex(callData, currentPage,
715: useFirstPageAsIndex)
716: && currentPage.getDir().equals(
717: menuItemPath.getPathWithEndToken())) {
718: if (isFolder) {
719: if (debugEnabled)
720: log.debug("Item is selected folder");
721: state = FOLDER_SELECTED;
722: } else {
723: if (debugEnabled)
724: log
725: .debug("Item is selected (empty folder)");
726: state = ITEM_SELECTED;
727: }
728: } else if (createSitemap
729: || currentPage.isSubPathOf(menuItemPath)) {
730: if (isFolder) {
731: if (debugEnabled)
732: log.debug("Item is open folder");
733: state = FOLDER_OPEN;
734: } else if (isFolderWithInvisibleSubpages) {
735: if (debugEnabled)
736: log
737: .debug("Item is selected (folder with invisible pages)");
738: state = ITEM_SELECTED;
739: }
740: } else {
741: if (isFolder) {
742: if (debugEnabled)
743: log.debug("Item is closed folder");
744: state = FOLDER_CLOSED;
745: }
746: }
747: if (debugEnabled)
748: log.debug("Item state: " + state);
749: try {
750: if (level >= minDepth
751: && (mode != PATH
752: || state == FOLDER_SELECTED
753: || state == ITEM_SELECTED
754: || state == FOLDER_OPEN || level == currentLevel)) {
755: if (debugEnabled) {
756: log.debug("Render fragment with state "
757: + state + " for level " + level);
758: }
759: if (levelDefined[level]) {
760: fragment[state][level].write(menuWriter,
761: this , callData);
762: } else {
763: if (debugEnabled) {
764: log.warn("Fragment with state " + state
765: + " for level " + level
766: + " is not defined!!");
767: }
768: }
769: }
770: if ((state == FOLDER_OPEN || state == FOLDER_SELECTED)
771: && menuItem instanceof PagesFolder
772: && level < maxDepth) {
773: if (level + 1 >= minDepth) {
774: if (debugEnabled)
775: log
776: .debug("Render pre-fragment for level "
777: + (level + 1));
778: if (levelDefined[level + 1]
779: && pre[level + 1] != null) {
780: pre[level + 1].write(menuWriter, this ,
781: callData);
782: }
783: }
784: int number = 1;
785: for (Iterator i = ((Container) menuItem)
786: .getSubcomponentNames(); i.hasNext();) {
787: callData.setRequestAttribute(NUMBER, String
788: .valueOf(number));
789: String nextID = id + "-"
790: + String.valueOf(number);
791: callData.setRequestAttribute(ID, nextID);
792:
793: String subcomponentName = (String) i.next();
794: Component subcomponent = manager
795: .getSubcomponent(
796: (Container) menuItem,
797: subcomponentName, callData);
798: number += renderMenu(callData, menuWriter,
799: currentPage, subcomponent,
800: subcomponentName, level + 1,
801: currentLevel, pre, post, fragment,
802: minDepth, maxDepth, levelDefined,
803: nextID);
804: }
805: if (level + 1 >= minDepth) {
806: if (debugEnabled)
807: log
808: .debug("Render post-fragment for level "
809: + (level + 1));
810: if (levelDefined[level + 1]
811: && post[level + 1] != null) {
812: post[level + 1].write(menuWriter, this ,
813: callData);
814: }
815: }
816: }
817: } catch (ContelligentException e) {
818: log
819: .error("Could not eval template for menu state ["
820: + state
821: + "] level ["
822: + level
823: + "]"
824: + e);
825: } catch (IOException e) {
826: log
827: .error("Could not eval template for menu state ["
828: + state
829: + "] level ["
830: + level
831: + "]"
832: + e);
833: }
834: }
835: return 1;
836: } else {
837: log.debug("Skipping...");
838: return 0;
839: }
840: }
841:
842: public Renderer getRenderer() {
843: return this ;
844: }
845:
846: public ParameterDescription[] getParameterDescription() {
847: return new ParameterDescription[0];
848: }
849:
850: public Collection getSensitiveCategories() {
851: return null;
852: }
853:
854: class EventListener implements EventQueueListener {
855: private ComponentPath siteFolder, menuComposerPath;
856:
857: public EventListener(ComponentPath siteFolder,
858: ComponentPath menuComposerPath) {
859: this .siteFolder = siteFolder;
860: this .menuComposerPath = menuComposerPath;
861: }
862:
863: public void onEvents(List eventList) {
864: for (Iterator i = eventList.iterator(); i.hasNext();) {
865: ContelligentEvent event = (ContelligentEvent) i.next();
866: if (event instanceof ComponentEvent) {
867: ComponentPath targetPath = ((ComponentEvent) event)
868: .getTargetPath();
869: if (targetPath.isSubPathOf(siteFolder)
870: || targetPath.isSubPathOf(menuComposerPath)
871: || targetPath.equals(siteFolder)
872: || targetPath.equals(menuComposerPath)) {
873: synchronized (this ) {
874: cachedOutput.clear();
875: }
876: }
877: }
878: }
879: }
880: }
881:
882: /*
883: * (non-Javadoc)
884: *
885: * @see de.finix.contelligent.ExternalRelationSource#relatedPaths()
886: */
887: public List relatedPaths() {
888: LinkedList list = new LinkedList();
889: list.addLast(siteFolder);
890: return list;
891: }
892:
893: /*
894: * (non-Javadoc)
895: *
896: * @see de.finix.contelligent.ExternalRelationSource#relatedPaths(java.util.List)
897: */
898: public void relatedPaths(List newTargetPaths)
899: throws ModificationVetoException {
900: if (newTargetPaths == null || newTargetPaths.size() == 0
901: || newTargetPaths.size() > 1) {
902: throw new ModificationVetoException(
903: "illegal state - newTargetPaths:'" + newTargetPaths
904: + "'");
905: }
906: setSiteFolder((ComponentPath) newTargetPaths.get(0));
907: }
908:
909: public String getNamespacePrefix() {
910: return namespacePrefix;
911: }
912:
913: public void setNamespacePrefix(String namespacePrefix) {
914: if (namespacePrefix == null) {
915: namespacePrefix = "";
916: }
917: this.namespacePrefix = namespacePrefix;
918: }
919: }
|