0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017: package org.apache.jetspeed.portalsite.impl;
0018:
0019: import java.util.ArrayList;
0020: import java.util.HashSet;
0021: import java.util.Iterator;
0022: import java.util.List;
0023: import java.util.ListIterator;
0024: import java.util.Locale;
0025: import java.util.Set;
0026: import java.util.regex.Matcher;
0027: import java.util.regex.Pattern;
0028:
0029: import org.apache.jetspeed.om.common.GenericMetadata;
0030: import org.apache.jetspeed.om.folder.Folder;
0031: import org.apache.jetspeed.om.folder.MenuDefinition;
0032: import org.apache.jetspeed.om.folder.MenuExcludeDefinition;
0033: import org.apache.jetspeed.om.folder.MenuIncludeDefinition;
0034: import org.apache.jetspeed.om.folder.MenuOptionsDefinition;
0035: import org.apache.jetspeed.om.folder.MenuSeparatorDefinition;
0036: import org.apache.jetspeed.om.page.Page;
0037: import org.apache.jetspeed.page.document.Node;
0038: import org.apache.jetspeed.page.document.NodeNotFoundException;
0039: import org.apache.jetspeed.portalsite.Menu;
0040: import org.apache.jetspeed.portalsite.MenuElement;
0041: import org.apache.jetspeed.portalsite.MenuOption;
0042: import org.apache.jetspeed.portalsite.PortalSiteRequestContext;
0043: import org.apache.jetspeed.portalsite.menu.DefaultMenuDefinition;
0044: import org.apache.jetspeed.portalsite.menu.DefaultMenuOptionsDefinition;
0045: import org.apache.jetspeed.portalsite.view.SiteView;
0046:
0047: /**
0048: * This class implements the portal-site menu elements
0049: * constructed and returned to decorators.
0050: *
0051: * @author <a href="mailto:rwatler@apache.org">Randy Watler</a>
0052: * @version $Id: MenuImpl.java 516448 2007-03-09 16:25:47Z ate $
0053: */
0054: public class MenuImpl extends MenuElementImpl implements Menu,
0055: Cloneable {
0056: /**
0057: * definition - menu definition
0058: */
0059: private MenuDefinition definition;
0060:
0061: /**
0062: * elements - ordered list of menu elements that
0063: * make up this instaniated menu
0064: */
0065: private List elements;
0066:
0067: /**
0068: * elementRelative - flag that indicates whether any relative paths
0069: * dependent on the current page in context were
0070: * referenced while constructing menu elements:
0071: * requires request, not session, caching
0072: */
0073: private boolean elementRelative;
0074:
0075: /**
0076: * MenuImpl - request/session context dependent constructor
0077: *
0078: * @param parent containing menu implementation
0079: * @param definition menu definition
0080: * @param context request context
0081: * @param menus related menu definition names set
0082: */
0083: public MenuImpl(MenuImpl parent, MenuDefinition definition,
0084: PortalSiteRequestContextImpl context, Set menus) {
0085: super (parent);
0086: this .definition = definition;
0087:
0088: // get site view from context
0089: SiteView view = ((PortalSiteSessionContextImpl) context
0090: .getSessionContext()).getSiteView();
0091: if (view != null) {
0092: // define menu node for titles and metadata if options
0093: // specifies a single visible page or folder proxy
0094: String options = definition.getOptions();
0095: Node optionProxy = null;
0096: if ((options != null) && (options.indexOf(',') == -1)) {
0097: try {
0098: optionProxy = view.getNodeProxy(options, context
0099: .getPage(), true, true);
0100: } catch (NodeNotFoundException nnfe) {
0101: } catch (SecurityException se) {
0102: }
0103: if (optionProxy != null) {
0104: setNode(optionProxy);
0105: }
0106: }
0107:
0108: // construct menu elements from menu definition
0109: // or nested menu definition elements; note that
0110: // menu elements override menu options attribute
0111: if ((definition.getMenuElements() == null)
0112: || definition.getMenuElements().isEmpty()) {
0113: // if options optionProxy is a single folder, force
0114: // options to include all folder children if not to
0115: // be expanded with paths and depth inclusion is
0116: // specified
0117: List overrideOptionProxies = null;
0118: if (optionProxy != null) {
0119: if ((optionProxy instanceof Folder)
0120: && !definition.isPaths()
0121: && (definition.getDepth() != 0)) {
0122: // assemble folder children path using wildcard
0123: String folderChildrenPath = null;
0124: if (!options.endsWith(Folder.PATH_SEPARATOR)) {
0125: folderChildrenPath = options
0126: + Folder.PATH_SEPARATOR + "*";
0127: } else {
0128: folderChildrenPath = options + "*";
0129: }
0130:
0131: // override menu options with visible folder contents
0132: // or create empty menu if no contents exist
0133: List folderChildren = null;
0134: try {
0135: folderChildren = view.getNodeProxies(
0136: folderChildrenPath, context
0137: .getPage(), true, true);
0138: } catch (NodeNotFoundException nnfe) {
0139: } catch (SecurityException se) {
0140: }
0141: if ((folderChildren != null)
0142: && !folderChildren.isEmpty()) {
0143: overrideOptionProxies = folderChildren;
0144: } else {
0145: return;
0146: }
0147: } else {
0148: // override menu options with single folder/page/link
0149: overrideOptionProxies = new ArrayList(1);
0150: overrideOptionProxies.add(optionProxy);
0151: }
0152:
0153: // set relative element flag if options path is relative
0154: this .elementRelative = (this .elementRelative || !options
0155: .startsWith(Folder.PATH_SEPARATOR));
0156: }
0157:
0158: // menu defined only with menu definition options
0159: this .elements = constructMenuElements(context, view,
0160: options, overrideOptionProxies, definition
0161: .getDepth(), definition.isPaths(),
0162: definition.isRegexp(), definition.getProfile(),
0163: definition.getOrder());
0164: } else {
0165: // limit cyclic references to this menu if named and
0166: // referencable as root menu instance
0167: boolean menuNameReferenced = false;
0168: if ((definition.getName() != null) && (parent == null)) {
0169: if (menus == null) {
0170: menus = new HashSet(4);
0171: }
0172: menuNameReferenced = menus
0173: .add(definition.getName());
0174: }
0175:
0176: // process menu elements in chunks between separators:
0177: // separators are included only if menu options are
0178: // generated after the separator and include/exclude
0179: // merge/filter operations apply to options bounded
0180: // by separators
0181: MenuSeparatorImpl separator = null;
0182: List separatedElements = null;
0183:
0184: // process each defined menu element
0185: Iterator menuElementsIter = definition
0186: .getMenuElements().iterator();
0187: while (menuElementsIter.hasNext()) {
0188: Object menuElement = menuElementsIter.next();
0189: if (menuElement instanceof MenuOptionsDefinition) {
0190: // construct menu option elements from definition using
0191: // defaults from menu definition as appropriate
0192: MenuOptionsDefinition optionDefinition = (MenuOptionsDefinition) menuElement;
0193: String locatorName = optionDefinition
0194: .getProfile();
0195: if (locatorName == null) {
0196: locatorName = definition.getProfile();
0197: }
0198: String order = optionDefinition.getOrder();
0199: if (order == null) {
0200: order = definition.getOrder();
0201: }
0202: List optionsAndMenus = constructMenuElements(
0203: context, view, optionDefinition
0204: .getOptions(), null,
0205: optionDefinition.getDepth(),
0206: optionDefinition.isPaths(),
0207: optionDefinition.isRegexp(),
0208: locatorName, order);
0209:
0210: // append option and menu elements to current separator
0211: // elements list
0212: if (optionsAndMenus != null) {
0213: if (separatedElements == null) {
0214: separatedElements = optionsAndMenus;
0215: } else {
0216: appendMenuElements(optionsAndMenus,
0217: separatedElements);
0218: }
0219: }
0220: } else if (menuElement instanceof MenuSeparatorDefinition) {
0221: // append current separator and separated option/menu elements
0222: // to menu elements list if at least one option/menu
0223: // element exists: do not include disassociated separators in menu
0224: if ((separatedElements != null)
0225: && !separatedElements.isEmpty()) {
0226: if (this .elements == null) {
0227: int initialSize = separatedElements
0228: .size();
0229: if (separator != null) {
0230: initialSize++;
0231: }
0232: this .elements = new ArrayList(
0233: initialSize);
0234: }
0235: if (separator != null) {
0236: this .elements.add(separator);
0237: }
0238: this .elements.addAll(separatedElements);
0239: }
0240:
0241: // construct new separator and reset separator
0242: // and separator option/menu elements list
0243: MenuSeparatorDefinition separatorDefinition = (MenuSeparatorDefinition) menuElement;
0244: separator = new MenuSeparatorImpl(this ,
0245: separatorDefinition);
0246: if (separatedElements != null) {
0247: separatedElements.clear();
0248: }
0249: } else if (menuElement instanceof MenuDefinition) {
0250: // construct nested menu element from definition
0251: MenuDefinition menuDefinition = (MenuDefinition) menuElement;
0252: MenuImpl nestedMenu = new MenuImpl(this ,
0253: menuDefinition, context, menus);
0254:
0255: // append menu element to current separated elements list
0256: if (separatedElements == null) {
0257: separatedElements = new ArrayList(1);
0258: }
0259: appendMenuElement(nestedMenu, separatedElements);
0260:
0261: // set relative element flag if nested menu is relative
0262: this .elementRelative = (this .elementRelative || nestedMenu
0263: .isElementRelative());
0264: } else if (menuElement instanceof MenuIncludeDefinition) {
0265: // include or nest referenced menu definition
0266: // assuming reference to menu is not cyclic
0267: MenuIncludeDefinition includeDefinition = (MenuIncludeDefinition) menuElement;
0268: if ((menus == null)
0269: || !menus.contains(includeDefinition
0270: .getName())) {
0271: // get named root menu from context, (menu may
0272: // not exist in this context so failure to
0273: // access menu is ignored)
0274: MenuImpl includeMenu = null;
0275: try {
0276: includeMenu = (MenuImpl) context
0277: .getMenu(includeDefinition
0278: .getName());
0279: } catch (NodeNotFoundException nnfe) {
0280: } catch (SecurityException se) {
0281: }
0282: if (includeMenu != null) {
0283: // nest menu or include elements, clone required
0284: // to support reparenting to this menu
0285: if (includeDefinition.isNest()) {
0286: // nest menu instance
0287: try {
0288: // clone menu and reparent
0289: includeMenu = (MenuImpl) includeMenu
0290: .clone();
0291: includeMenu.setParentMenu(this );
0292:
0293: // append menu element to current separated elements list
0294: if (separatedElements == null) {
0295: separatedElements = new ArrayList(
0296: 1);
0297: }
0298: appendMenuElement(includeMenu,
0299: separatedElements);
0300: } catch (CloneNotSupportedException cnse) {
0301: }
0302: } else {
0303: // include menu elements
0304: if (!includeMenu.isEmpty()) {
0305: Iterator elementsIter = includeMenu
0306: .getElements()
0307: .iterator();
0308: while (elementsIter.hasNext()) {
0309: MenuElementImpl includeElement = (MenuElementImpl) elementsIter
0310: .next();
0311: try {
0312: // clone menu element and reparent
0313: includeElement = (MenuElementImpl) includeElement
0314: .clone();
0315: includeElement
0316: .setParentMenu(this );
0317:
0318: // insert separators or options and menus
0319: if (includeElement instanceof MenuSeparatorImpl) {
0320: // append current separator and separated option/menu elements
0321: if ((separatedElements != null)
0322: && !separatedElements
0323: .isEmpty()) {
0324: if (this .elements == null) {
0325: int initialSize = separatedElements
0326: .size();
0327: if (separator != null) {
0328: initialSize++;
0329: }
0330: this .elements = new ArrayList(
0331: initialSize);
0332: }
0333: if (separator != null) {
0334: this .elements
0335: .add(separator);
0336: }
0337: this .elements
0338: .addAll(separatedElements);
0339: }
0340:
0341: // reset separator and separator option/menu elements list
0342: // using separator menu element
0343: separator = (MenuSeparatorImpl) includeElement;
0344: if (separatedElements != null) {
0345: separatedElements
0346: .clear();
0347: }
0348: } else {
0349: // append menu element to current separated elements list
0350: if (separatedElements == null) {
0351: separatedElements = new ArrayList(
0352: includeMenu
0353: .getElements()
0354: .size());
0355: }
0356: appendMenuElement(
0357: includeElement,
0358: separatedElements);
0359: }
0360: } catch (CloneNotSupportedException cnse) {
0361: }
0362: }
0363: }
0364: }
0365:
0366: // set relative element flag if included menu is relative
0367: this .elementRelative = (this .elementRelative || includeMenu
0368: .isElementRelative());
0369: }
0370: }
0371: } else if (menuElement instanceof MenuExcludeDefinition) {
0372: // exclusion requires current separated elements
0373: if ((separatedElements != null)
0374: && !separatedElements.isEmpty()) {
0375: // exclude top level referenced menu definition
0376: // options assuming reference to menu is not cyclic
0377: MenuExcludeDefinition excludeDefinition = (MenuExcludeDefinition) menuElement;
0378: if ((menus == null)
0379: || !menus
0380: .contains(excludeDefinition
0381: .getName())) {
0382: // get named root menu from context, (menu may
0383: // not exist in this context so failure to
0384: // access menu is ignored)
0385: MenuImpl excludeMenu = null;
0386: try {
0387: excludeMenu = (MenuImpl) context
0388: .getMenu(excludeDefinition
0389: .getName());
0390: } catch (NodeNotFoundException nnfe) {
0391: } catch (SecurityException se) {
0392: }
0393: if (excludeMenu != null) {
0394: // remove referenced menu options from current
0395: // separated elements list
0396: removeMenuElements(excludeMenu
0397: .getElements(),
0398: separatedElements);
0399:
0400: // set relative element flag if excluded menu is relative
0401: this .elementRelative = (this .elementRelative || excludeMenu
0402: .isElementRelative());
0403: }
0404: }
0405: }
0406: }
0407: }
0408:
0409: // append last separator and separated option/menu elements
0410: // to menu elements list if at least one option/menu
0411: // element exists: do not include trailing separators
0412: if ((separatedElements != null)
0413: && !separatedElements.isEmpty()) {
0414: if (this .elements == null) {
0415: // use the separated elements as the menu elements
0416: // collection and insert the separator
0417: this .elements = separatedElements;
0418: if (separator != null) {
0419: this .elements.add(0, separator);
0420: }
0421: } else {
0422: // copy into existing menu elements collection
0423: if (separator != null) {
0424: this .elements.add(separator);
0425: }
0426: this .elements.addAll(separatedElements);
0427: }
0428: }
0429:
0430: // restore referencing for this menu if limited
0431: if (menuNameReferenced) {
0432: menus.remove(definition.getName());
0433: }
0434: }
0435: }
0436: }
0437:
0438: /**
0439: * MenuImpl - request/session context dependent constructor
0440: *
0441: * @param definition menu definition
0442: * @param context request context
0443: * @param menus related menu definition names set
0444: */
0445: public MenuImpl(MenuDefinition definition,
0446: PortalSiteRequestContextImpl context, Set menus) {
0447: this (null, definition, context, menus);
0448: }
0449:
0450: /**
0451: * appendMenuElement - append to ordered list of unique menu
0452: * option/menu elements
0453: *
0454: * @param appendMenuElement option/menu element to append
0455: * @param menuElements option/menu element list
0456: */
0457: private void appendMenuElement(MenuElementImpl appendMenuElement,
0458: List menuElements) {
0459: // make sure new menu element is unique and
0460: // add to menu element list
0461: if (appendMenuElement != null) {
0462: if (!menuElements.contains(appendMenuElement)) {
0463: menuElements.add(appendMenuElement);
0464: }
0465: }
0466: }
0467:
0468: /**
0469: * appendMenuElements - append to ordered list of unique menu
0470: * option/menu elements
0471: *
0472: * @param appendMenuElements option/menu element list to append
0473: * @param menuElements option/menu element list
0474: */
0475: private void appendMenuElements(List appendMenuElements,
0476: List menuElements) {
0477: // make sure new menu elements are unique and
0478: // add to menu element list
0479: if (appendMenuElements != null) {
0480: Iterator elementsIter = appendMenuElements.iterator();
0481: while (elementsIter.hasNext()) {
0482: appendMenuElement(
0483: (MenuElementImpl) elementsIter.next(),
0484: menuElements);
0485: }
0486: }
0487: }
0488:
0489: /**
0490: * removeMenuElements - remove from ordered list of unique menu
0491: * option/menu elements
0492: *
0493: * @param removeMenuElements option/menu element list to remove
0494: * @param menuElements option/menu element list
0495: */
0496: private void removeMenuElements(List removeMenuElements,
0497: List menuElements) {
0498: // remove equivalent menu elements from menu
0499: // element list
0500: if (removeMenuElements != null) {
0501: menuElements.removeAll(removeMenuElements);
0502: }
0503: }
0504:
0505: /**
0506: * constructMenuElements - construct ordered list of menu elements in
0507: * context/site view using specified element
0508: * selection parameters; also sets up the
0509: * elementRelative flag while constructing the
0510: * menu elements
0511: *
0512: * @param context request context
0513: * @param view context site view
0514: * @param options option paths specification
0515: * @param overrideElementProxies override menu element node proxies
0516: * @param depth inclusion depth
0517: * @param paths paths elements flag
0518: * @param regexp regexp flag
0519: * @param locatorName profile locator name
0520: * @param order ordering patterns list
0521: */
0522: private List constructMenuElements(
0523: PortalSiteRequestContextImpl context, SiteView view,
0524: String options, List overrideElementProxies, int depth,
0525: boolean paths, boolean regexp, String locatorName,
0526: String order) {
0527: if (options != null) {
0528: // use override element proxies if specified; otherwise
0529: // compute proxy list using specified menu options
0530: List elementProxies = overrideElementProxies;
0531: if (elementProxies == null) {
0532: // split multiple comma separated option paths from specified options
0533: String[] optionPaths = options.split(",");
0534:
0535: // use regexp processing if specified or simple
0536: // path evaluation to retrieve list of proxies from
0537: // the site view for the specified options
0538: for (int i = 0; (i < optionPaths.length); i++) {
0539: String optionPath = optionPaths[i].trim();
0540: if (optionPath.length() > 0) {
0541: // get proxies/proxy for path
0542: if (regexp) {
0543: // get list of visible proxies for path from view and append
0544: // to list if unique and pass profile locator name filter
0545: List pathProxies = null;
0546: try {
0547: pathProxies = view.getNodeProxies(
0548: optionPath, context.getPage(),
0549: true, true);
0550: } catch (NodeNotFoundException nnfe) {
0551: } catch (SecurityException se) {
0552: }
0553: if (pathProxies != null) {
0554: Iterator pathProxiesIter = pathProxies
0555: .iterator();
0556: while (pathProxiesIter.hasNext()) {
0557: Node pathProxy = (Node) pathProxiesIter
0558: .next();
0559: if ((locatorName == null)
0560: || locatorName
0561: .equals(MenuOptionsDefinition.ANY_PROFILE_LOCATOR)
0562: || locatorName
0563: .equals(view
0564: .getProfileLocatorName(pathProxy))) {
0565: if (elementProxies == null) {
0566: elementProxies = new ArrayList();
0567: }
0568: appendMenuElementProxies(
0569: pathProxy,
0570: elementProxies);
0571: }
0572: }
0573: }
0574: } else {
0575: // get visible proxy for path from view and append to
0576: // list if unique and pass profile locator name filter
0577: Node pathProxy = null;
0578: try {
0579: pathProxy = view.getNodeProxy(
0580: optionPath, context.getPage(),
0581: true, true);
0582: } catch (NodeNotFoundException nnfe) {
0583: } catch (SecurityException se) {
0584: }
0585: if ((pathProxy != null)
0586: && ((locatorName == null)
0587: || locatorName
0588: .equals(MenuOptionsDefinition.ANY_PROFILE_LOCATOR) || locatorName
0589: .equals(view
0590: .getProfileLocatorName(pathProxy)))) {
0591: if (elementProxies == null) {
0592: elementProxies = new ArrayList();
0593: }
0594: appendMenuElementProxies(pathProxy,
0595: elementProxies);
0596: }
0597: }
0598:
0599: // set relative element flag if path is relative
0600: elementRelative = (elementRelative || !optionPath
0601: .startsWith(Folder.PATH_SEPARATOR));
0602: }
0603: }
0604:
0605: // return if no proxies available
0606: if (elementProxies == null) {
0607: return null;
0608: }
0609: }
0610:
0611: // sort elements proxies using url and/or names if order
0612: // specified and more than one element proxy in list
0613: if ((order != null) && (elementProxies.size() > 1)) {
0614: // create ordered element proxies
0615: List orderedElementProxies = new ArrayList(
0616: elementProxies.size());
0617:
0618: // split multiple comma separated elements orderings
0619: // after converted to regexp pattern
0620: String[] orderings = orderRegexpPattern(order).split(
0621: ",");
0622:
0623: // copy ordered proxies per ordering
0624: for (int i = 0; ((i < orderings.length) && (elementProxies
0625: .size() > 1)); i++) {
0626: String ordering = orderings[i].trim();
0627: if (ordering.length() > 0) {
0628: // get ordering pattern and matcher
0629: Pattern pattern = Pattern.compile(ordering);
0630: Matcher matcher = null;
0631:
0632: // use regular expression to match urls or names of
0633: // element proxies; matched proxies are removed and
0634: // placed in the ordered elements proxies list
0635: Iterator elementProxiesIter = elementProxies
0636: .iterator();
0637: while (elementProxiesIter.hasNext()) {
0638: Node elementProxy = (Node) elementProxiesIter
0639: .next();
0640:
0641: // get url or name to test ordering match against
0642: String test = null;
0643: if (ordering.charAt(0) == Folder.PATH_SEPARATOR_CHAR) {
0644: test = elementProxy.getUrl();
0645: } else {
0646: test = elementProxy.getName();
0647: }
0648:
0649: // construct or reset ordering matcher
0650: if (matcher == null) {
0651: matcher = pattern.matcher(test);
0652: } else {
0653: matcher.reset(test);
0654: }
0655:
0656: // move proxy to ordered list if matched
0657: if (matcher.matches()) {
0658: orderedElementProxies.add(elementProxy);
0659: elementProxiesIter.remove();
0660: }
0661: }
0662: }
0663: }
0664:
0665: // copy remaining unordered proxies
0666: orderedElementProxies.addAll(elementProxies);
0667:
0668: // replace element proxies with ordered list
0669: elementProxies = orderedElementProxies;
0670: }
0671:
0672: // expand paths if single page or folder element proxy
0673: // has been specified in elements with no depth expansion
0674: if (paths
0675: && (depth == 0)
0676: && (elementProxies.size() == 1)
0677: && ((elementProxies.get(0) instanceof Folder) || (elementProxies
0678: .get(0) instanceof Page))) {
0679: Node parentNode = ((Node) elementProxies.get(0))
0680: .getParent();
0681: while (parentNode != null) {
0682: elementProxies.add(0, parentNode);
0683: parentNode = parentNode.getParent();
0684: }
0685: }
0686:
0687: // convert elements proxies into menu elements
0688: DefaultMenuOptionsDefinition defaultMenuOptionsDefinition = null;
0689: ListIterator elementProxiesIter = elementProxies
0690: .listIterator();
0691: while (elementProxiesIter.hasNext()) {
0692: Node elementProxy = (Node) elementProxiesIter.next();
0693: MenuElement menuElement = null;
0694:
0695: // convert folders into nested menus if depth specified
0696: // with no paths expansion, (negative depth values are
0697: // interpreted as complete menu expansion)
0698: if ((elementProxy instanceof Folder)
0699: && ((depth < 0) || (depth > 1)) && !paths) {
0700: // construct menu definition and associated menu
0701: MenuDefinition nestedMenuDefinition = new DefaultMenuDefinition(
0702: elementProxy.getUrl(), depth - 1,
0703: locatorName);
0704: menuElement = new MenuImpl(this ,
0705: nestedMenuDefinition, context, null);
0706: } else {
0707: // construct shared default menu option definition and menu option
0708: if (defaultMenuOptionsDefinition == null) {
0709: defaultMenuOptionsDefinition = new DefaultMenuOptionsDefinition(
0710: options, depth, paths, regexp,
0711: locatorName, order);
0712: }
0713: menuElement = new MenuOptionImpl(this ,
0714: elementProxy, defaultMenuOptionsDefinition);
0715: }
0716:
0717: // replace element proxy with menu element
0718: elementProxiesIter.set(menuElement);
0719: }
0720: List menuElements = elementProxies;
0721:
0722: // return list of menu elements constructed from element proxies
0723: return menuElements;
0724: }
0725:
0726: // no options specified
0727: return null;
0728: }
0729:
0730: /**
0731: * appendMenuElementProxies - append to ordered list of unique menu
0732: * element proxies
0733: *
0734: * @param pathProxy menu element page, folder, or link proxy at path
0735: * @param elementProxies element proxies list
0736: */
0737: private void appendMenuElementProxies(Node pathProxy,
0738: List elementProxies) {
0739: // make sure new proxy is unique and add
0740: // to element proxies list
0741: if (!elementProxies.contains(pathProxy)) {
0742: elementProxies.add(pathProxy);
0743: }
0744: }
0745:
0746: /**
0747: * clone - clone this instance
0748: *
0749: * @return unparented deep copy
0750: */
0751: public Object clone() throws CloneNotSupportedException {
0752: // clone this object
0753: MenuImpl copy = (MenuImpl) super .clone();
0754:
0755: // clone and reparent copy elements
0756: if (copy.elements != null) {
0757: Iterator elementsIter = copy.elements.iterator();
0758: copy.elements = new ArrayList(copy.elements.size());
0759: while (elementsIter.hasNext()) {
0760: MenuElementImpl elementCopy = (MenuElementImpl) ((MenuElementImpl) elementsIter
0761: .next()).clone();
0762: elementCopy.setParentMenu(copy);
0763: copy.elements.add(elementCopy);
0764: }
0765: }
0766: return copy;
0767: }
0768:
0769: /**
0770: * getElementType - get type of menu element
0771: *
0772: * @return MENU_ELEMENT_TYPE
0773: */
0774: public String getElementType() {
0775: return MENU_ELEMENT_TYPE;
0776: }
0777:
0778: /**
0779: * getName - get name of menu
0780: *
0781: * @return menu name
0782: */
0783: public String getName() {
0784: return definition.getName();
0785: }
0786:
0787: /**
0788: * getTitle - get default title for menu element
0789: *
0790: * @return title text
0791: */
0792: public String getTitle() {
0793: // return definition title
0794: String title = definition.getTitle();
0795: if (title != null) {
0796: return title;
0797: }
0798: // return node or default title
0799: return super .getTitle();
0800: }
0801:
0802: /**
0803: * getShortTitle - get default short title for menu element
0804: *
0805: * @return short title text
0806: */
0807: public String getShortTitle() {
0808: // return definition short title
0809: String title = definition.getShortTitle();
0810: if (title != null) {
0811: return title;
0812: }
0813:
0814: // return node or default short title
0815: return super .getShortTitle();
0816: }
0817:
0818: /**
0819: * getTitle - get locale specific title for menu element
0820: * from metadata
0821: *
0822: * @param locale preferred locale
0823: * @return title text
0824: */
0825: public String getTitle(Locale locale) {
0826: // return definition short title for preferred locale
0827: String title = definition.getTitle(locale);
0828: if (title != null) {
0829: return title;
0830: }
0831:
0832: // return node or default title for preferred locale
0833: return super .getTitle(locale);
0834: }
0835:
0836: /**
0837: * getShortTitle - get locale specific short title for menu
0838: * element from metadata
0839: *
0840: * @param locale preferred locale
0841: * @return short title text
0842: */
0843: public String getShortTitle(Locale locale) {
0844: // return definition short title for preferred locale
0845: String title = definition.getShortTitle(locale);
0846: if (title != null) {
0847: return title;
0848: }
0849:
0850: // return node or default short title for preferred locale
0851: return super .getShortTitle(locale);
0852: }
0853:
0854: /**
0855: * getMetadata - get generic metadata for menu element
0856: *
0857: * @return metadata
0858: */
0859: public GenericMetadata getMetadata() {
0860: // return definition metadata
0861: GenericMetadata metadata = definition.getMetadata();
0862: if ((metadata != null) && (metadata.getFields() != null)
0863: && !metadata.getFields().isEmpty()) {
0864: return metadata;
0865: }
0866:
0867: // return node metadata
0868: return super .getMetadata();
0869: }
0870:
0871: /**
0872: * getSkin - get skin name for menu element
0873: *
0874: * @return skin name
0875: */
0876: public String getSkin() {
0877: // get skin from definition or inherit from parent menu
0878: String skin = definition.getSkin();
0879: if (skin == null) {
0880: skin = super .getSkin();
0881: }
0882: return skin;
0883: }
0884:
0885: /**
0886: * getUrl - get url of top level folder that defined
0887: * menu options; only available for menus
0888: * defined without multiple options, nested
0889: * menus, or separators
0890: *
0891: * @return folder url
0892: */
0893: public String getUrl() {
0894: // return url of node associated with menu
0895: // option if defined
0896: if (getNode() != null) {
0897: return getNode().getUrl();
0898: }
0899: return null;
0900: }
0901:
0902: /**
0903: * isHidden - get hidden state of folder that defined
0904: * menu options; only available for menus
0905: * defined without multiple options, nested
0906: * menus, or separators
0907: *
0908: * @return hidden state
0909: */
0910: public boolean isHidden() {
0911: // return hidden state of node associated with
0912: // menu option if defined
0913: if (getNode() != null) {
0914: return getNode().isHidden();
0915: }
0916: return false;
0917: }
0918:
0919: /**
0920: * isSelected - return true if an option or nested
0921: * menu within this menu are selected by
0922: * the specified request context
0923: *
0924: * @param context request context
0925: * @return selected state
0926: */
0927: public boolean isSelected(PortalSiteRequestContext context) {
0928: // menu is selected if a selected element exists
0929: return (getSelectedElement(context) != null);
0930: }
0931:
0932: /**
0933: * getElements - get ordered list of menu elements that
0934: * are members of this menu; possibly contains
0935: * options, nested menus, or separators
0936: *
0937: * @return menu elements list
0938: */
0939: public List getElements() {
0940: return elements;
0941: }
0942:
0943: /**
0944: * isEmpty - get empty state of list of menu elements
0945: *
0946: * @return menu elements list empty state
0947: */
0948: public boolean isEmpty() {
0949: return ((elements == null) || elements.isEmpty());
0950: }
0951:
0952: /**
0953: * isElementRelative - get flag that indicates whether any relative paths
0954: * dependent on the current page in context were
0955: * referenced while constructing menu elements
0956: *
0957: * @return relative element status
0958: */
0959: public boolean isElementRelative() {
0960: return elementRelative;
0961: }
0962:
0963: /**
0964: * getSelectedElement - return selected option or nested
0965: * menu within this menu selected by
0966: * the specified request context
0967: *
0968: * @return selected menu element
0969: */
0970: public MenuElement getSelectedElement(
0971: PortalSiteRequestContext context) {
0972: // test nested menu and option menu
0973: // elements for selected status
0974: if (elements != null) {
0975: Iterator elementsIter = elements.iterator();
0976: while (elementsIter.hasNext()) {
0977: MenuElement element = (MenuElement) elementsIter.next();
0978:
0979: // test element selected
0980: boolean selected = false;
0981: if (element instanceof MenuOption) {
0982: selected = ((MenuOption) element)
0983: .isSelected(context);
0984: } else if (element instanceof Menu) {
0985: selected = ((Menu) element).isSelected(context);
0986: }
0987:
0988: // return selected element
0989: if (selected) {
0990: return element;
0991: }
0992: }
0993: }
0994: return null;
0995: }
0996:
0997: /**
0998: * orderRegexpPattern - tests for and converts simple order wildcard
0999: * and character class regular exressions to
1000: * perl5/standard java pattern syntax
1001: *
1002: * @param regexp - candidate order regular expression
1003: * @return - converted pattern
1004: */
1005: private static String orderRegexpPattern(String regexp) {
1006: // convert expression to pattern
1007: StringBuffer pattern = null;
1008: for (int i = 0, limit = regexp.length(); (i < limit); i++) {
1009: char regexpChar = regexp.charAt(i);
1010: switch (regexpChar) {
1011: case '*':
1012: case '.':
1013: case '?':
1014: case '[':
1015: if (pattern == null) {
1016: pattern = new StringBuffer(regexp.length() * 2);
1017: pattern.append(regexp.substring(0, i));
1018: }
1019: switch (regexpChar) {
1020: case '*':
1021: pattern.append("[^" + Folder.PATH_SEPARATOR + "]*");
1022: break;
1023: case '.':
1024: pattern.append("\\.");
1025: break;
1026: case '?':
1027: pattern.append("[^" + Folder.PATH_SEPARATOR + "]");
1028: break;
1029: case '[':
1030: pattern.append('[');
1031: break;
1032: }
1033: break;
1034: default:
1035: if (pattern != null) {
1036: pattern.append(regexpChar);
1037: }
1038: break;
1039: }
1040: }
1041:
1042: // return converted pattern
1043: if (pattern != null)
1044: return pattern.toString();
1045: return regexp;
1046: }
1047: }
|