View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.jetspeed.portalsite.view;
18  
19  import java.util.ArrayList;
20  import java.util.HashSet;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Set;
25  
26  import org.apache.jetspeed.om.folder.Folder;
27  import org.apache.jetspeed.om.folder.FolderNotFoundException;
28  import org.apache.jetspeed.om.folder.proxy.FolderProxy;
29  import org.apache.jetspeed.om.page.Page;
30  import org.apache.jetspeed.om.page.proxy.PageProxy;
31  import org.apache.jetspeed.page.PageManager;
32  import org.apache.jetspeed.page.document.Node;
33  import org.apache.jetspeed.page.document.NodeException;
34  import org.apache.jetspeed.page.document.NodeNotFoundException;
35  import org.apache.jetspeed.page.document.NodeSet;
36  import org.apache.jetspeed.page.document.proxy.NodeProxy;
37  import org.apache.jetspeed.portalsite.menu.StandardBackMenuDefinition;
38  import org.apache.jetspeed.portalsite.menu.StandardBreadcrumbsMenuDefinition;
39  import org.apache.jetspeed.portalsite.menu.StandardNavigationsMenuDefinition;
40  import org.apache.jetspeed.portalsite.menu.StandardPagesMenuDefinition;
41  import org.apache.jetspeed.profiler.ProfileLocator;
42  import org.apache.jetspeed.profiler.ProfileLocatorProperty;
43  
44  /***
45   * This class defines the logical view of site content.
46   * 
47   * @author <a href="mailto:rwatler@apache.org">Randy Watler</a>
48   * @version $Id: SiteView.java 534967 2007-05-03 19:23:06Z taylor $
49   */
50  public class SiteView
51  {
52      /***
53       * CURRENT_PAGE_PATH - expression used to match the current page
54       */
55      public final static String CURRENT_PAGE_PATH = "~";
56  
57      /***
58       * ALT_CURRENT_PAGE_PATH - alternate expression used to match the current page
59       */
60      public final static String ALT_CURRENT_PAGE_PATH = "@";
61  
62      /***
63       * STANDARD_*_MENU_NAME - standard menu names
64       */
65      public final static String STANDARD_BACK_MENU_NAME = "back";
66      public final static String STANDARD_BREADCRUMBS_MENU_NAME = "breadcrumbs";
67      public final static String STANDARD_PAGES_MENU_NAME = "pages";
68      public final static String STANDARD_NAVIGATIONS_MENU_NAME = "navigations";
69  
70      /***
71       * CUSTOM_*_MENU_NAME - custom menu names
72       */
73      public final static String CUSTOM_PAGE_NAVIGATIONS_MENU_NAME = "page-navigations";
74  
75      /***
76       * STANDARD_MENU_NAMES - set of supported standard menu names
77       */
78      private final static Set STANDARD_MENU_NAMES = new HashSet(3);
79      static
80      {
81          STANDARD_MENU_NAMES.add(STANDARD_BACK_MENU_NAME);
82          STANDARD_MENU_NAMES.add(STANDARD_BREADCRUMBS_MENU_NAME);
83          STANDARD_MENU_NAMES.add(STANDARD_PAGES_MENU_NAME);
84          STANDARD_MENU_NAMES.add(STANDARD_NAVIGATIONS_MENU_NAME);
85      }
86  
87      /***
88       * STANDARD_MENU_DEFINITION_LOCATORS - list of standard menu definition locators
89       */
90      private final static List STANDARD_MENU_DEFINITION_LOCATORS = new ArrayList(4);
91      static
92      {
93          STANDARD_MENU_DEFINITION_LOCATORS.add(new SiteViewMenuDefinitionLocator(new StandardBackMenuDefinition()));
94          STANDARD_MENU_DEFINITION_LOCATORS.add(new SiteViewMenuDefinitionLocator(new StandardBreadcrumbsMenuDefinition()));
95          STANDARD_MENU_DEFINITION_LOCATORS.add(new SiteViewMenuDefinitionLocator(new StandardPagesMenuDefinition()));
96          STANDARD_MENU_DEFINITION_LOCATORS.add(new SiteViewMenuDefinitionLocator(new StandardNavigationsMenuDefinition()));
97      }
98  
99      /***
100      * pageManager - PageManager component
101      */
102     private PageManager pageManager;
103 
104     /***
105      * searchPaths - validated list of ordered search path objects
106      *               where paths have no trailing folder separator
107      */
108     private List searchPaths;
109 
110     /***
111      * searchPathsString - search paths as string
112      */
113     private String searchPathsString;
114 
115     /***
116      * rootFolderProxy - root folder proxy instance
117      */
118     private Folder rootFolderProxy;
119 
120     /***
121      * SiteView - validating constructor
122      *
123      * @param pageManager PageManager component instance
124      * @param searchPaths list of search paths in string or search path
125      *                    object form
126      */
127     public SiteView(PageManager pageManager, List searchPaths)
128     {
129         this.pageManager = pageManager;
130         if ((searchPaths != null) && !searchPaths.isEmpty())
131         {
132             // validate search path format and existence
133             this.searchPaths = new ArrayList(searchPaths.size());
134             StringBuffer searchPathsStringBuffer = new StringBuffer();
135             Iterator pathsIter = searchPaths.iterator();
136             while (pathsIter.hasNext())
137             {
138                 Object pathObject = pathsIter.next();
139                 if (!(pathObject instanceof SiteViewSearchPath))
140                 {
141                     String path = pathObject.toString().trim();
142                     if (path.length() > 0)
143                     {
144                         pathObject = new SiteViewSearchPath(ProfileLocator.PAGE_LOCATOR, path);
145                     }
146                 }
147                 SiteViewSearchPath searchPath = (SiteViewSearchPath)pathObject;
148                 if (this.searchPaths.indexOf(searchPath) == -1)
149                 {
150                     try
151                     {
152                         if (this.pageManager.getFolder(searchPath.toString()) != null)
153                         {
154                             this.searchPaths.add(searchPath);
155                             
156                             // construct search paths as string
157                             if (searchPathsStringBuffer.length() == 0)
158                             {
159                                 searchPathsStringBuffer.append(searchPath);
160                             }
161                             else
162                             {
163                                 searchPathsStringBuffer.append(',');
164                                 searchPathsStringBuffer.append(searchPath);
165                             }
166                         }
167                     }
168                     catch (NodeException ne)
169                     {
170                     }
171                     catch (NodeNotFoundException nnfe)
172                     {
173                     }
174                     catch (SecurityException se)
175                     {
176                     }
177                 }
178             }
179 
180             // if no valid paths found, assume root search path
181             // with no aggregation is to be used as default; otherwise,
182             // save search paths as string
183             if (this.searchPaths.isEmpty())
184             {
185                 this.searchPaths.add(new SiteViewSearchPath(ProfileLocator.PAGE_LOCATOR, Folder.PATH_SEPARATOR));
186                 this.searchPathsString = Folder.PATH_SEPARATOR;
187             }
188             else
189             {
190                 this.searchPathsString = searchPathsStringBuffer.toString();
191             }
192         }
193         else
194         {
195             // root search path with no aggregation
196             this.searchPaths = new ArrayList(1);
197             this.searchPaths.add(new SiteViewSearchPath(ProfileLocator.PAGE_LOCATOR, Folder.PATH_SEPARATOR));
198             this.searchPathsString = Folder.PATH_SEPARATOR;
199         }
200     }
201 
202     /***
203      * SiteView - validating constructor
204      *
205      * @param pageManager PageManager component instance
206      * @param searchPaths array of search paths
207      */
208     public SiteView(PageManager pageManager, String [] searchPaths)
209     {
210         this(pageManager, makeSearchPathList(searchPaths));
211     }
212 
213     /***
214      * makeSearchPathList - construct from array
215      *
216      * @param searchPaths array of search paths
217      * @return search path list
218      */
219     private static List makeSearchPathList(String [] searchPaths)
220     {
221         if ((searchPaths != null) && (searchPaths.length > 0))
222         {
223             List searchPathsList = new ArrayList(searchPaths.length);
224             for (int i = 0; (i < searchPaths.length); i++)
225             {
226                 searchPathsList.add(searchPaths[i]);
227             }
228             return searchPathsList;
229         }
230         return null;
231     }
232 
233     /***
234      * SiteView - validating constructor
235      *
236      * @param pageManager PageManager component instance
237      * @param searchPaths string of comma separated search paths
238      */
239     public SiteView(PageManager pageManager, String searchPaths)
240     {
241         this(pageManager, makeSearchPathList(searchPaths));
242     }
243 
244     /***
245      * makeSearchPathList - construct from string
246      *
247      * @param searchPaths string of comma separated search paths
248      * @return search path list
249      */
250     private static List makeSearchPathList(String searchPaths)
251     {
252         return ((searchPaths != null) ? makeSearchPathList(searchPaths.split(",")) : null);
253     }
254 
255     /***
256      * SiteView - validating constructor
257      *
258      * @param pageManager PageManager component instance
259      * @param locator profile locator search specification
260      */
261     public SiteView(PageManager pageManager, ProfileLocator locator)
262     {
263         this(pageManager, makeSearchPathList(locator));
264     }
265     
266     /***
267      * makeSearchPathList - construct from profile locator
268      *
269      * @param locator profile locator search specification
270      * @return search path list
271      */
272     private static List makeSearchPathList(ProfileLocator locator)
273     {
274         if (locator != null)
275         {
276             // generate and return locator search paths
277             return mergeSearchPathList(ProfileLocator.PAGE_LOCATOR, locator, new ArrayList(8));
278         }
279         return null;
280     }
281 
282     /***
283      * SiteView - validating constructor
284      *
285      * @param pageManager PageManager component instance
286      * @param locators map of named profile locator search specifications
287      */
288     public SiteView(PageManager pageManager, Map locators)
289     {
290         this(pageManager, makeSearchPathList(locators));
291     }
292     
293     /***
294      * makeSearchPathList - construct from profile locators
295      *
296      * @param locators map of named profile locator search specifications
297      * @return search path list
298      */
299     private static List makeSearchPathList(Map locators)
300     {
301         if ((locators != null) && !locators.isEmpty())
302         {
303             // generate locators search paths; aggregate individual
304             // profile locator search paths with the 'page' locator
305             // having priority, (all other named locators are processed
306             // subsequent to 'page' in unspecified order).
307             List searchPaths = new ArrayList(8 * locators.size());
308             ProfileLocator pageLocator = (ProfileLocator)locators.get(ProfileLocator.PAGE_LOCATOR);
309             if (pageLocator != null)
310             {
311                 // add priority 'page' locator search paths
312                 mergeSearchPathList(ProfileLocator.PAGE_LOCATOR, pageLocator, searchPaths);
313             }
314             if ((pageLocator == null) || (locators.size() > 1))
315             {
316                 Iterator locatorNameIter = locators.keySet().iterator();
317                 while (locatorNameIter.hasNext())
318                 {
319                     String locatorName = (String)locatorNameIter.next();
320                     if (!locatorName.equals(ProfileLocator.PAGE_LOCATOR))
321                     {
322                         // add alternate locator search paths
323                         mergeSearchPathList(locatorName, (ProfileLocator)locators.get(locatorName), searchPaths);
324                     }
325                 }
326             }
327             return searchPaths;
328         }
329         return null;
330     }
331     
332     /***
333      * mergeSearchPathList - append search paths from profile locator
334      *
335      * @param locatorName name of profile locator
336      * @param locator profile locator search specification
337      * @param searchPaths list of search paths to merge into
338      * @return search path list
339      */
340     private static List mergeSearchPathList(String locatorName, ProfileLocator locator, List searchPaths)
341     {
342         // generate profile locator search paths with locator
343         // names to be used later for node identification and
344         // grouping; note that the profile locator iterator
345         // starts returning a full set of properties and returns
346         // a shorter properties array with each iteration to
347         // create the next search criteria... depending on the
348         // validity of the property values and their cardinality,
349         // (multiple property values are returned sequentially),
350         // profiler locator iterations may be skipped to
351         // generate the proper search paths
352         List locatorSearchPaths = new ArrayList(8);
353         int addLocatorSearchPathsAt = 0;
354         Iterator locatorIter = locator.iterator();
355         while (locatorIter.hasNext())
356         {
357             // initialize path construction variables
358             String pathRoot = Folder.PATH_SEPARATOR;
359             List paths = new ArrayList(8);
360             paths.add(new StringBuffer(pathRoot));
361             int pathDepth = 0;
362             int lastPathsCount = 0;
363             String lastPropertyName = null;
364             int lastPropertyValueLength = 0;
365             boolean navigatedPathRoot = false;
366 
367             // reset advance of the profile locator offset by one
368             // to accomodate automatic iteration within locator loop
369             int skipProfileLocatorIterations = -1;
370 
371             // form locator properties into a complete path
372             ProfileLocatorProperty [] properties = (ProfileLocatorProperty []) locatorIter.next();
373             for (int i = 0; (i < properties.length); i++)
374             {
375                 if (properties[i].isNavigation())
376                 {
377                     // reset search paths to navigation root path, (reset
378                     // only navigation supported), skip null navigation values
379                     if (properties[i].getValue() != null)
380                     {
381                         // TODO: support relative navigation values
382 
383                         // assume navigation value must be a root prefix
384                         // and contains proper path prefix for each subsite
385                         // path folder name
386                         pathRoot = properties[i].getValue();
387                         if (!pathRoot.startsWith(Folder.PATH_SEPARATOR))
388                         {
389                             pathRoot = Folder.PATH_SEPARATOR + pathRoot; 
390                         }
391                         if (!pathRoot.endsWith(Folder.PATH_SEPARATOR))
392                         {
393                             pathRoot += Folder.PATH_SEPARATOR; 
394                         }
395                         if (!pathRoot.equals(Folder.PATH_SEPARATOR))
396                         {
397                             int folderIndex = 1;
398                             do
399                             {
400                                 if (!pathRoot.regionMatches(folderIndex, Folder.RESERVED_SUBSITE_FOLDER_PREFIX, 0, Folder.RESERVED_SUBSITE_FOLDER_PREFIX.length()))
401                                 {
402                                     pathRoot = pathRoot.substring(0, folderIndex) + Folder.RESERVED_SUBSITE_FOLDER_PREFIX + pathRoot.substring(folderIndex);
403                                 }
404                                 folderIndex = pathRoot.indexOf(Folder.PATH_SEPARATOR, folderIndex) + 1;
405                             }
406                             while ((folderIndex != -1) && (folderIndex != pathRoot.length()));
407                         }
408                         
409                         // reset locator paths using new prefix
410                         pathDepth = 0;
411                         paths.clear();
412                         paths.add(new StringBuffer(pathRoot));
413                         lastPathsCount = 0;
414                         lastPropertyName = null;
415                         lastPropertyValueLength = 0;
416                         navigatedPathRoot = true;
417 
418                         // reset advance of the the profile locator iterator
419                         skipProfileLocatorIterations = 0;
420                     }
421                     else
422                     {
423                         // make sure multiple trailing null valued properties are
424                         // ignored if more than one is present by advancing
425                         // the profile locator iterator
426                         skipProfileLocatorIterations++;
427                     }
428                 }
429                 else if (properties[i].isControl())
430                 {
431                     // skip null control values
432                     if (properties[i].getValue() != null)
433                     {
434                         // fold control names to lower case; preserve
435                         // value case as provided by profiler
436                         String propertyName = properties[i].getName().toLowerCase();
437                         String propertyValue = properties[i].getValue();
438                         // detect duplicate control names which indicates multiple
439                         // values: must duplicate locator paths for each value; different
440                         // control values are simply appended to all locator paths
441                         if (propertyName.equals(lastPropertyName))
442                         {
443                             // duplicate last locator paths set, stripping last matching
444                             // control value from each, appending new value, and adding new
445                             // valued set to collection of paths
446                             ArrayList multipleValuePaths = new ArrayList(lastPathsCount);
447                             Iterator pathsIter = paths.iterator();
448                             for (int count = 0; (pathsIter.hasNext() && (count < lastPathsCount)); count++)
449                             {
450                                 StringBuffer path = (StringBuffer) pathsIter.next();
451                                 StringBuffer multipleValuePath = new StringBuffer(path.toString());
452                                 multipleValuePath.setLength(multipleValuePath.length() - lastPropertyValueLength - 1);
453                                 multipleValuePath.append(propertyValue);
454                                 multipleValuePath.append(Folder.PATH_SEPARATOR_CHAR);
455                                 multipleValuePaths.add(multipleValuePath);
456                             }
457                             paths.addAll(multipleValuePaths);
458 
459                             // make sure trailing multiple valued properties are
460                             // ignored by advancing the profile locator iterator
461                             // which is reset for each unique property value sets
462                             skipProfileLocatorIterations++;
463                         }
464                         else
465                         {
466                             // construct locator path folders with control properties
467                             Iterator pathsIter = paths.iterator();
468                             while (pathsIter.hasNext())
469                             {
470                                 StringBuffer path = (StringBuffer) pathsIter.next();
471                                 path.append(Folder.RESERVED_FOLDER_PREFIX);
472                                 path.append(propertyName);
473                                 path.append(Folder.PATH_SEPARATOR_CHAR);
474                                 path.append(propertyValue);
475                                 path.append(Folder.PATH_SEPARATOR_CHAR);
476                             }
477                             
478                             // reset last locator property vars
479                             pathDepth++;
480                             lastPathsCount = paths.size();
481                             lastPropertyValueLength = propertyValue.length();
482                             lastPropertyName = propertyName;
483 
484                             // reset advance of the the profile locator iterator
485                             skipProfileLocatorIterations = 0;
486                         }
487                     }
488                     else
489                     {
490                         // make sure multiple trailing null valued properties are
491                         // ignored along with the last property values if more
492                         // than one is present by advancing the profile locator
493                         // iterator
494                         skipProfileLocatorIterations++;
495                     }
496                 }
497                 else
498                 {
499                     // make sure multiple trailing request path properties are
500                     // ignored if more than one is present by advancing the
501                     // profile locator iterator
502                     skipProfileLocatorIterations++;
503                 }
504             }
505             
506             // if required, advance profile locator iterations
507             for (int skip = skipProfileLocatorIterations; ((skip > 0) && (locatorIter.hasNext())); skip--)
508             {
509                 locatorIter.next();
510             }
511 
512             // append any generated paths to locator search paths and
513             // append root path if at end of locator path group, (locator
514             // path roots are not returned by profiler iterator if not
515             // explicitly navigated)
516             if ((pathDepth > 0) || navigatedPathRoot)
517             {
518                 locatorSearchPaths.addAll(addLocatorSearchPathsAt, paths);
519                 addLocatorSearchPathsAt += paths.size();
520             }
521             if ((pathDepth == 1) && !navigatedPathRoot)
522             {
523                 locatorSearchPaths.add(addLocatorSearchPathsAt++, new StringBuffer(pathRoot));
524             }
525 
526             // reset locator search path ordering since navigated root
527             // paths are generated by the iterator based algorithm above
528             // right to left instead of left to right as expected
529             if ((pathDepth == 0) && navigatedPathRoot)
530             {
531                 addLocatorSearchPathsAt = 0;
532             }
533             
534             // if end of locator path group and have not navigated to
535             // a new root path or there are no more locator iterations,
536             // insert the paths into the search path results
537             if (((pathDepth <= 1) && !navigatedPathRoot) || !locatorIter.hasNext())
538             {
539                 // add locator paths to unique search paths preserving
540                 // search order; move non-unique paths to end of search
541                 // path list to favor more specific paths over common
542                 // root paths, (i.e. '/' should be last)
543                 Iterator locatorSearchPathsIter = locatorSearchPaths.iterator();
544                 while (locatorSearchPathsIter.hasNext())
545                 {
546                     SiteViewSearchPath searchPath = new SiteViewSearchPath(locatorName, locatorSearchPathsIter.next().toString());
547                     // test search path uniqueness
548                     int existsAt = searchPaths.indexOf(searchPath);
549                     if (existsAt != -1)
550                     {
551                         if (existsAt < (searchPaths.size()-1))
552                         {
553                             // push existing search path to end of paths
554                             searchPaths.add(searchPaths.remove(existsAt));
555                         }
556                     }
557                     else
558                     {
559                         // add new unique search path to end of paths
560                         searchPaths.add(searchPath);
561                     }
562                 }
563 
564                 // clear merged locator search paths
565                 locatorSearchPaths.clear();
566                 addLocatorSearchPathsAt = 0;
567             }
568         }
569         return searchPaths;
570     }
571 
572     /***
573      * SiteView - basic constructor
574      *
575      * @param pageManager PageManager component instance
576      */
577     public SiteView(PageManager pageManager)
578     {
579         this(pageManager, (List)null);
580     }
581 
582     /***
583      * getPageManager - return PageManager component instance
584      *
585      * @return PageManager instance
586      */
587     public PageManager getPageManager()
588     {
589         return pageManager;
590     }
591 
592     /***
593      * getSearchPaths - return ordered search paths list that
594      *                  defines this view
595      *
596      * @return search paths list
597      */
598     public List getSearchPaths()
599     {
600         return searchPaths;
601     }
602 
603     /***
604      * getSearchPathsString - return search paths as string
605      *
606      * @return search paths list as comma separated string
607      */
608     public String getSearchPathsString()
609     {
610         return searchPathsString;
611     }
612 
613     /***
614      * getRootFolderProxy - create and return root folder proxy instance
615      *
616      * @return root folder proxy
617      * @throws FolderNotFoundException if not found
618      * @throws SecurityException if view access not granted
619      */
620     public Folder getRootFolderProxy() throws FolderNotFoundException
621     {
622         // latently construct and return root folder proxy
623         if (rootFolderProxy == null)
624         {
625             try
626             {
627                 // the folder and profile locator name of the root
628                 // folder proxy in the view is the locator name of the
629                 // first search path since search paths are valid
630                 SiteViewSearchPath searchPath = (SiteViewSearchPath)searchPaths.get(0);
631                 String path = searchPath.toString();
632                 String locatorName = searchPath.getLocatorName();
633 
634                 // get concrete root folder from page manager
635                 // and construct proxy
636                 Folder rootFolder = pageManager.getFolder(path);
637                 rootFolderProxy = FolderProxy.newInstance(this, locatorName, null, rootFolder);
638             }
639             catch (NodeException ne)
640             {
641                 FolderNotFoundException fnfe = new FolderNotFoundException("Root folder not found");
642                 fnfe.initCause(ne);
643                 throw fnfe;
644             }
645             catch (NodeNotFoundException nnfe)
646             {
647                 FolderNotFoundException fnfe = new FolderNotFoundException("Root folder not found");
648                 fnfe.initCause(nnfe);
649                 throw fnfe;
650             }
651         }
652         return rootFolderProxy;
653     }
654 
655     /***
656      * getNodeProxy - get single folder, page, or link proxy
657      *                at relative or absolute path
658      *
659      * @param path single node path
660      * @param currentNode current folder or page for relative paths or null
661      * @param onlyViewable node required to be viewable
662      * @param onlyVisible node required to be visible, (or current)
663      * @return folder, page, or link node proxy
664      * @throws NodeNotFoundException if not found
665      * @throws SecurityException if view access not granted
666      */
667     public Node getNodeProxy(String path, Node currentNode, boolean onlyViewable, boolean onlyVisible) throws NodeNotFoundException
668     {
669         // determine current folder and page
670         String currentPath = path;
671         Folder currentFolder = null;
672         Page currentPage = null;
673         if (currentNode instanceof Page)
674         {
675             currentPage = (Page)currentNode;
676             currentFolder = (Folder)currentPage.getParent();
677         }
678         else if (currentNode instanceof Folder)
679         {
680             currentFolder = (Folder)currentNode;
681         }
682 
683         // match current page path
684         if (currentPath.equals(CURRENT_PAGE_PATH) || currentPath.equals(ALT_CURRENT_PAGE_PATH))
685         {
686             // return current page if specified, (assume viewable)
687             return currentPage;
688         }
689 
690         // convert absolute path to a root relative search
691         // and default current folder
692         if (currentPath.startsWith(Folder.PATH_SEPARATOR))
693         {
694             currentPath = currentPath.substring(1);
695             currentFolder = null;
696         }
697         if (currentFolder == null)
698         {
699             currentFolder = getRootFolderProxy();
700         }
701 
702         // search for path based on current folder 
703         while ((currentPath.length() > 0) && !currentPath.equals(Folder.PATH_SEPARATOR))
704         {
705             // parse relative sub-folder from path
706             int separatorIndex = currentPath.indexOf(Folder.PATH_SEPARATOR);
707             if (separatorIndex != -1)
708             {
709                 // isolate sub-folder and continue search
710                 // using remaining paths
711                 String subfolder = currentPath.substring(0, separatorIndex);
712                 currentPath = currentPath.substring(separatorIndex+1);
713                 if (subfolder.equals(".."))
714                 {
715                     // adjust current folder if parent exists
716                     if (currentFolder.getParent() != null)
717                     {
718                         currentFolder = (Folder)currentFolder.getParent();
719                     }
720                     else
721                     {
722                         throw new NodeNotFoundException("Specified path " + path + " not found.");
723                     }
724                 }
725                 else if (!subfolder.equals("."))
726                 {
727                     // access sub-folder or return null if nonexistent
728                     // or access forbidden
729                     try
730                     {
731                         currentFolder = currentFolder.getFolder(subfolder);
732                     }
733                     catch (NodeException ne)
734                     {
735                         NodeNotFoundException nnfe = new NodeNotFoundException("Specified path " + path + " not found.");
736                         nnfe.initCause(ne);
737                         throw nnfe;
738                     }
739                     catch (NodeNotFoundException nnfe)
740                     {
741                         NodeNotFoundException nnfeWrapper = new NodeNotFoundException("Specified path " + path + " not found.");
742                         nnfeWrapper.initCause(nnfe);
743                         throw nnfeWrapper;
744                     }
745                 }
746             }
747             else
748             {
749                 // access remaining path as page, folder, or link node
750                 // proxy; return null if not found or not viewable/visible
751                 // and visibility is required
752                 try
753                 {
754                     NodeSet children = currentFolder.getAll();
755                     if (children != null)
756                     {
757                         Node node = children.get(currentPath);
758                         if ((node != null) && (!onlyVisible || !node.isHidden() || (node == currentPage)) &&
759                             (!onlyViewable || isProxyViewable(node, onlyVisible)))
760                         {
761                             return node;
762                         }
763                     }
764                 }
765                 catch (NodeException ne)
766                 {
767                     NodeNotFoundException nnfe = new NodeNotFoundException("Specified path " + path + " not found.");
768                     nnfe.initCause(ne);
769                     throw nnfe;
770                 }
771                 throw new NodeNotFoundException("Specified path " + path + " not found or viewable/visible.");
772             }
773         }
774 
775         // path maps to current folder; return if viewable/visible
776         // or visibility not required
777         if ((!onlyVisible || !currentFolder.isHidden()) &&
778             (!onlyViewable || isProxyViewable(currentFolder, onlyVisible)))
779         {
780             return currentFolder;
781         }
782         throw new NodeNotFoundException("Specified path " + path + " not found or viewable/visible.");
783     }
784 
785     /***
786      * getNodeProxies - get folder, page, or link proxies at
787      *                  relative or absolute path using simple path
788      *                  wildcards and character classes
789      *
790      * @param regexpPath regular expression node path
791      * @param currentNode current folder or page for relative paths or null
792      * @param onlyViewable nodes required to be viewable flag
793      * @param onlyVisible node required to be visible, (or current)
794      * @return list of folder, page, or link node proxies
795      */
796     public List getNodeProxies(String regexpPath, Node currentNode, boolean onlyViewable, boolean onlyVisible)
797     {
798         // determine current folder and page
799         String currentRegexpPath = regexpPath;
800         Folder currentFolder = null;
801         Page currentPage = null;
802         if (currentNode instanceof Page)
803         {
804             currentPage = (Page)currentNode;
805             currentFolder = (Folder)currentPage.getParent();
806         }
807         else if (currentNode instanceof Folder)
808         {
809             currentFolder = (Folder)currentNode;
810         }
811 
812         // match current page path
813         if (currentRegexpPath.equals(CURRENT_PAGE_PATH) || currentRegexpPath.equals(ALT_CURRENT_PAGE_PATH))
814         {
815             if (currentPage != null)
816             {
817                 // return current page, (assume viewable)
818                 List proxies = new ArrayList(1);
819                 proxies.add(currentPage);
820                 return proxies;
821             }
822             else
823             {
824                 // current page not specified
825                 return null;
826             }
827         }
828 
829         // convert absolute path to a root relative search
830         // and default current folder
831         if (currentRegexpPath.startsWith(Folder.PATH_SEPARATOR))
832         {
833             currentRegexpPath = currentRegexpPath.substring(1);
834             currentFolder = null;
835         }
836         if (currentFolder == null)
837         {
838             try
839             {
840                 currentFolder = getRootFolderProxy();
841             }
842             catch (FolderNotFoundException fnfe)
843             {
844                 return null;
845             }
846             catch (SecurityException se)
847             {
848                 return null;
849             }
850         }
851 
852         // search for path based on current folder 
853         while ((currentRegexpPath.length() > 0) && !currentRegexpPath.equals(Folder.PATH_SEPARATOR))
854         {
855             // parse relative sub-folder from path
856             int separatorIndex = currentRegexpPath.indexOf(Folder.PATH_SEPARATOR);
857             if (separatorIndex != -1)
858             {
859                 // isolate sub-folder and continue search
860                 // using remaining paths
861                 String subfolder = currentRegexpPath.substring(0, separatorIndex);
862                 currentRegexpPath = currentRegexpPath.substring(separatorIndex+1);
863                 if (subfolder.equals(".."))
864                 {
865                     // adjust current folder if parent exists
866                     if (currentFolder.getParent() != null)
867                     {
868                         currentFolder = (Folder)currentFolder.getParent();
869                     }
870                     else
871                     {
872                         return null;
873                     }
874                 }
875                 else if (!subfolder.equals("."))
876                 {
877                     try
878                     {
879                         // check for regular expression pattern
880                         String subfolderPattern = pathRegexpPattern(subfolder);
881                         if (subfolderPattern != null)
882                         {
883                             // follow all matching sub-folders
884                             NodeSet subfolders = currentFolder.getFolders();
885                             if (subfolders != null)
886                             {
887                                 subfolders = subfolders.inclusiveSubset(subfolderPattern);
888                                 if (subfolders != null)
889                                 {
890                                     // recursively process sub-folders if more than
891                                     // one match, access single sub-folder, or return
892                                     // null if nonexistent
893                                     if (subfolders.size() > 1)
894                                     {
895                                         // recursively process matching sub-folders
896                                         List proxies = null;
897                                         Iterator subfoldersIter = subfolders.iterator();
898                                         while (subfoldersIter.hasNext())
899                                         {
900                                             currentFolder = (Folder)subfoldersIter.next();
901                                             List subfolderProxies = getNodeProxies(currentRegexpPath, currentFolder, onlyViewable, onlyVisible);
902                                             if ((subfolderProxies != null) && !subfolderProxies.isEmpty())
903                                             {
904                                                 if (proxies == null)
905                                                 {
906                                                     proxies = new ArrayList();
907                                                 }
908                                                 proxies.addAll(subfolderProxies);
909                                             }
910                                         }
911                                         return proxies;
912                                     }
913                                     else if (subfolders.size() == 1)
914                                     {
915                                         // access single sub-folder
916                                         currentFolder = (Folder)subfolders.iterator().next();
917                                     }
918                                     else
919                                     {
920                                         // no matching sub-folders
921                                         return null;
922                                     }
923                                 }
924                                 else
925                                 {
926                                     // no matching sub-folders
927                                     return null;
928                                 }
929                             }
930                             else
931                             {
932                                 // no sub-folders
933                                 return null;
934                             }
935                         }
936                         else
937                         {
938                             // access single sub-folder or return null if
939                             // nonexistent by throwing exception
940                             currentFolder = currentFolder.getFolder(subfolder);
941                         }
942                     }
943                     catch (NodeException ne)
944                     {
945                         // could not access sub-folders
946                         return null;
947                     }
948                     catch (NodeNotFoundException nnfe)
949                     {
950                         // could not access sub-folders
951                         return null;
952                     }
953                     catch (SecurityException se)
954                     {
955                         // could not access sub-folders
956                         return null;
957                     }
958                 }
959             }
960             else
961             {
962                 try
963                 {
964                     // get all children of current folder
965                     NodeSet children = currentFolder.getAll();
966                     if (children != null)
967                     {
968                         // check for regular expression pattern
969                         String pathPattern = pathRegexpPattern(currentRegexpPath);
970                         if (pathPattern != null)
971                         {
972                             // copy children matching remaining path pattern as
973                             // page, folder, or link proxies if viewable/visible or
974                             // visibilty not required
975                             children = children.inclusiveSubset(pathPattern);
976                             if ((children != null) && !children.isEmpty())
977                             {
978                                 List proxies = null;
979                                 Iterator childrenIter = children.iterator();
980                                 while (childrenIter.hasNext())
981                                 {
982                                     Node child = (Node)childrenIter.next(); 
983                                     if ((!onlyVisible || !child.isHidden() || (child == currentPage)) &&
984                                         (!onlyViewable || isProxyViewable(child, onlyVisible)))
985                                     {
986                                         if (proxies == null)
987                                         {
988                                             proxies = new ArrayList(children.size());
989                                         }
990                                         proxies.add(child);
991                                     }
992                                 }
993                                 return proxies;
994                             }
995                         }
996                         else
997                         {
998                             // access remaining path as page, folder, or link
999                             // node proxy; return null if not found or not
1000                             // viewable and visiblity is required
1001                             Node child = children.get(currentRegexpPath);
1002                             if ((child != null) && (!onlyVisible || !child.isHidden() || (child == currentPage)) &&
1003                                 (!onlyViewable || isProxyViewable(child, onlyVisible)))
1004                             {
1005                                 List proxies = new ArrayList(1);
1006                                 proxies.add(currentFolder);
1007                                 return proxies;
1008                             }
1009                         }
1010                     }
1011                     
1012                 }
1013                 catch (NodeException ne)
1014                 {
1015                 }
1016                 catch (SecurityException se)
1017                 {
1018                 }
1019 
1020                 // no children match or available
1021                 return null;
1022             }
1023         }
1024 
1025         // path maps to current folder; return if viewable/visible
1026         // or visibility not required
1027         if ((!onlyVisible || !currentFolder.isHidden()) &&
1028             (!onlyViewable || isProxyViewable(currentFolder, onlyVisible)))
1029         {
1030             List proxies = new ArrayList(1);
1031             proxies.add(currentFolder);
1032             return proxies;
1033         }
1034         return null;
1035     }
1036 
1037     /***
1038      * pathRegexpPattern - tests for and converts simple path wildcard
1039      *                     and character class regular exressions to
1040      *                     perl5/standard java pattern syntax
1041      *
1042      * @param regexp - candidate path regular expression
1043      * @return - converted pattern or null if no regular expression
1044      */
1045     private static String pathRegexpPattern(String regexp)
1046     {
1047         // convert expression to pattern
1048         StringBuffer pattern = null;
1049         for (int i = 0, limit = regexp.length(); (i < limit); i++)
1050         {
1051             char regexpChar = regexp.charAt(i);
1052             switch (regexpChar)
1053             {
1054                 case '*':
1055                 case '.':
1056                 case '?':
1057                 case '[':
1058                     if (pattern == null)
1059                     {
1060                         pattern = new StringBuffer(regexp.length()*2);
1061                         pattern.append(regexp.substring(0, i));
1062                     }
1063                     switch (regexpChar)
1064                     {
1065                         case '*':
1066                             pattern.append(".*");
1067                             break;
1068                         case '.':
1069                             pattern.append("//.");
1070                             break;
1071                         case '?':
1072                             pattern.append('.');
1073                             break;
1074                         case '[':
1075                             pattern.append('[');
1076                             break;
1077                     }
1078                     break;
1079                 default:
1080                     if (pattern != null)
1081                     {
1082                         pattern.append(regexpChar);
1083                     }
1084                     break;
1085             }
1086         }
1087 
1088         // return converted pattern or null if not a regular expression
1089         if (pattern != null)
1090             return pattern.toString();
1091         return null;
1092     }
1093 
1094     /***
1095      * isProxyViewable - tests for node proxy visibility in view
1096      *
1097      * @param nodeProxy test node proxy
1098      * @param onlyVisible nodes required to be visible
1099      * @return - viewable flag
1100      */
1101     private static boolean isProxyViewable(Node nodeProxy, boolean onlyVisible)
1102     {
1103         // pages and links are always considered viewable;
1104         // folders must be tested for viewable and visibile
1105         // child nodes
1106         if (nodeProxy instanceof Folder)
1107         {
1108             try
1109             {
1110                 NodeSet children = ((Folder) nodeProxy).getAll();
1111                 if (children != null)
1112                 {
1113                     Iterator childrenIter = children.iterator();
1114                     while (childrenIter.hasNext())
1115                     {
1116                         Node child = (Node)childrenIter.next();
1117                         if ((!onlyVisible || !child.isHidden()) && isProxyViewable(child, onlyVisible))
1118                         {
1119                             return true;
1120                         }
1121                     }
1122                 }
1123             }
1124             catch (NodeException ne)
1125             {
1126             }
1127             catch (SecurityException se)
1128             {
1129             }
1130             return false;
1131         }
1132         return true;
1133     }
1134 
1135     /***
1136      * getStandardMenuNames - get set of available standard menu names
1137      *  
1138      * @return menu names set
1139      */
1140     public Set getStandardMenuNames()
1141     {
1142         // return constant standard menu names
1143         return STANDARD_MENU_NAMES;
1144     }
1145 
1146     /***
1147      * getStandardMenuDefinitionLocators - get list of available standard
1148      *                                     menu definition locators
1149      *  
1150      * @return menu definition locators list
1151      */
1152     public List getStandardMenuDefinitionLocators()
1153     {
1154         // return constant standard menu definition locators
1155         return STANDARD_MENU_DEFINITION_LOCATORS;
1156     }
1157 
1158     /***
1159      * getMenuDefinitionLocators - get list of view node proxy menu
1160      *                             definition locators; implemented here
1161      *                             to hide view proxy manipulation from
1162      *                             more general portal site implementation
1163      *
1164      * @param node node proxy
1165      * @return definition locator list
1166      */
1167     public List getMenuDefinitionLocators(Node node)
1168     {
1169         // access node proxy from specified node and
1170         // return associated definition locators
1171         NodeProxy nodeProxy = NodeProxy.getNodeProxy(node);
1172         if (nodeProxy != null)
1173         {
1174             return nodeProxy.getMenuDefinitionLocators();
1175         }
1176         return null;
1177     }
1178 
1179     /***
1180      * getMenuDefinitionLocator - get named view node proxy menu
1181      *                            definition locator; implemented here
1182      *                            to hide view proxy manipulation from
1183      *                            more general portal site implementation
1184      *
1185      * @param node node proxy
1186      * @param name menu definition name
1187      * @return menu definition locator
1188      */
1189     public SiteViewMenuDefinitionLocator getMenuDefinitionLocator(Node node, String name)
1190     {
1191         // access node proxy from specified node and
1192         // return associated definition locators
1193         NodeProxy nodeProxy = NodeProxy.getNodeProxy(node);
1194         if (nodeProxy != null)
1195         {
1196             return nodeProxy.getMenuDefinitionLocator(name);
1197         }
1198         return null;
1199     }
1200 
1201     /***
1202      * getProfileLocatorName - get view node proxy profile locator name;
1203      *                         implemented here to hide view proxy manipulation
1204      *                         from more general portal site implementation
1205      *
1206      * @param node node proxy
1207      * @return profile locator name
1208      */
1209     public String getProfileLocatorName(Node node)
1210     {
1211         SiteViewProxy siteViewProxy = SiteViewProxy.getSiteViewProxy(node);
1212         if (siteViewProxy != null)
1213         {
1214             return siteViewProxy.getLocatorName();
1215         }
1216         return null;
1217     }
1218 
1219     /***
1220      * getManagedPage - get concrete page instance from page proxy;
1221      *                  implemented here to hide view proxy manipulation
1222      *                  from more general portal site implementation
1223      *  
1224      * @param page page proxy
1225      * @return managed page
1226      */
1227     public Page getManagedPage(Page page)
1228     {
1229         // access page proxy from specified page and
1230         // return associated delegate managed page
1231         PageProxy pageProxy = (PageProxy)NodeProxy.getNodeProxy(page);
1232         if (pageProxy != null)
1233         {
1234             return pageProxy.getPage();
1235         }
1236         return null;
1237     }
1238 }