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.page.document.impl;
18  
19  import java.util.Collection;
20  import java.util.List;
21  import java.util.Locale;
22  import java.util.StringTokenizer;
23  
24  import org.apache.jetspeed.om.common.GenericMetadata;
25  import org.apache.jetspeed.om.folder.Folder;
26  import org.apache.jetspeed.om.page.PageMetadataImpl;
27  import org.apache.jetspeed.om.page.PageSecurity;
28  import org.apache.jetspeed.om.page.impl.BaseElementImpl;
29  import org.apache.jetspeed.om.page.impl.SecurityConstraintsImpl;
30  import org.apache.jetspeed.page.document.Node;
31  import org.apache.jetspeed.page.impl.DatabasePageManagerUtils;
32  import org.apache.ojb.broker.core.proxy.ProxyHelper;
33  
34  /***
35   * NodeImpl
36   *
37   * @author <a href="mailto:rwatler@apache.org">Randy Watler</a>
38   * @version $Id$
39   */
40  public abstract class NodeImpl extends BaseElementImpl implements Node
41  {
42      private Node parent;
43      private boolean hidden;
44      private Collection metadataFields;
45      private String path = Folder.PATH_SEPARATOR;
46      private String subsite;
47      private String user;
48      private String role;
49      private String group;
50      private String mediatype;
51      private String locale;
52      private String extendedAttributeName;
53      private String extendedAttributeValue;
54  
55      private PageMetadataImpl pageMetadata;
56      private String logicalPath;
57  
58      public NodeImpl(SecurityConstraintsImpl constraints)
59      {
60          super(constraints);
61      }
62  
63      /***
64       * getCanonicalNodePath
65       *
66       * Format paths used to set and query NodeImpl instances.
67       *
68       * @param path specified path
69       * @return canonical path
70       */
71      public static String getCanonicalNodePath(String path)
72      {
73          // validate and format path
74          if ((path == null) || (path.length() == 0))
75          {
76              path = Folder.PATH_SEPARATOR;
77          }
78          if (!path.startsWith(Folder.PATH_SEPARATOR))
79          {
80              path = Folder.PATH_SEPARATOR + path;
81          }
82          if (path.endsWith(Folder.PATH_SEPARATOR) && !path.equals(Folder.PATH_SEPARATOR))
83          {
84              path = path.substring(0, path.length() - 1);
85          }
86          return path;
87      }
88  
89      /***
90       * newPageMetadata
91       *
92       * Construct page manager specific metadata implementation.
93       *
94       * @param fields mutable fields collection
95       * @return page metadata
96       */
97      public PageMetadataImpl newPageMetadata(Collection fields)
98      {
99          // no metadata available by default
100         return null;
101     }
102 
103     /***
104      * getPageMetadata
105      *
106      * Get page manager specific metadata implementation.
107      *
108      * @return page metadata
109      */
110     public PageMetadataImpl getPageMetadata()
111     {
112         if (pageMetadata == null)
113         {
114             if (metadataFields == null)
115             {
116                 metadataFields = DatabasePageManagerUtils.createList();
117             }
118             pageMetadata = newPageMetadata(metadataFields);
119         }
120         return pageMetadata;
121     }
122 
123     /***
124      * defaultTitleFromName
125      *
126      * Compute default title from name.
127      *
128      * @return default title
129      */
130     protected String defaultTitleFromName()
131     {
132         // transform name to title
133         String title = getName();
134         if (title != null)
135         {
136             // strip extensions and default root folder name
137             if ((getType() != null) && title.endsWith(getType()))
138             {
139                 title = title.substring(0, title.length()-getType().length());
140             }
141             else if (title.equals(Folder.PATH_SEPARATOR))
142             {
143                 title = "top";
144             }
145             // use space as word separator
146             title = title.replace('_', ' ');
147             title = title.replace('-', ' ');
148             // use title case for title words
149             int wordIndex = -1;
150             do
151             {
152                 if (!Character.isTitleCase(title.charAt(wordIndex+1)))
153                 {
154                     StringBuffer makeTitle = new StringBuffer();
155                     makeTitle.append(title.substring(0, wordIndex+1));
156                     makeTitle.append(Character.toTitleCase(title.charAt(wordIndex+1)));
157                     makeTitle.append(title.substring(wordIndex+2));
158                     title = makeTitle.toString();
159                 }
160                 wordIndex = title.indexOf(' ', wordIndex+1);
161             }
162             while (wordIndex != -1);
163         }
164         return title;
165     }
166 
167     /* (non-Javadoc)
168      * @see org.apache.jetspeed.om.page.impl.BaseElementImpl#getName()
169      */
170     public String getName()
171     {
172         // get name or compute from path
173         String name = super.getName();
174         if (name == null)
175         {
176             if (path != null)
177             {
178                 if (!path.equals(Folder.PATH_SEPARATOR))
179                 {
180                     name = path.substring(path.lastIndexOf(Folder.PATH_SEPARATOR) + 1);
181                 }
182                 else
183                 {
184                     name = Folder.PATH_SEPARATOR;
185                 }
186                 super.setName(name);
187             }
188         }
189         return name;
190     }
191 
192     /* (non-Javadoc)
193      * @see org.apache.jetspeed.om.page.impl.BaseElementImpl#setName(java.lang.String)
194      */
195     public void setName(String name)
196     {
197         // set path based on name
198         if (name != null)
199         {
200             if (path != null)
201             {
202                 // set path
203                 if (!name.equals(Folder.PATH_SEPARATOR))
204                 {
205                     path = path.substring(0, path.lastIndexOf(Folder.PATH_SEPARATOR) + 1) + name;
206                 }
207                 else
208                 {
209                     path = Folder.PATH_SEPARATOR;
210                 }
211 
212                 // reset logicalPath
213                 logicalPath = null;
214             }
215             super.setName(name);
216         }
217     }
218 
219     /* (non-Javadoc)
220      * @see org.apache.jetspeed.om.page.impl.BaseElementImpl#getEffectivePageSecurity()
221      */
222     public PageSecurity getEffectivePageSecurity()
223     {
224         // by default, delegate to real parent node implementation
225         NodeImpl parentNodeImpl = (NodeImpl)ProxyHelper.getRealObject(parent);
226         if (parentNodeImpl != null)
227         {
228             return parentNodeImpl.getEffectivePageSecurity();
229         }
230         return null;
231     }
232 
233     /* (non-Javadoc)
234      * @see org.apache.jetspeed.om.page.impl.BaseElementImpl#checkConstraints(java.util.List, java.util.List, java.util.List, java.util.List, boolean, boolean)
235      */
236     public void checkConstraints(List actions, List userPrincipals, List rolePrincipals, List groupPrincipals, boolean checkNodeOnly, boolean checkParentsOnly) throws SecurityException
237     {
238         // check constraints in node hierarchy
239         if (checkNodeOnly)
240         {
241             // check node constraints if available; otherwise,
242             // recursively check parent constraints until
243             // default constraints for node are checked
244             SecurityConstraintsImpl constraintsImpl = (SecurityConstraintsImpl)getSecurityConstraints();
245             if ((constraintsImpl != null) && !constraintsImpl.isEmpty())
246             {
247                 constraintsImpl.checkConstraints(actions, userPrincipals, rolePrincipals, groupPrincipals, getEffectivePageSecurity());
248             }
249             else
250             {
251                 NodeImpl parentNodeImpl = (NodeImpl)ProxyHelper.getRealObject(parent);
252                 if (parentNodeImpl != null)
253                 {
254                     parentNodeImpl.checkConstraints(actions, userPrincipals, rolePrincipals, groupPrincipals, checkNodeOnly, false);
255                 }
256             }
257         }
258         else
259         {
260             // check node constraints if available and not
261             // to be skipped due to explicity granted access
262             if (!checkParentsOnly)
263             {
264                 SecurityConstraintsImpl constraintsImpl = (SecurityConstraintsImpl)getSecurityConstraints();
265                 if ((constraintsImpl != null) && !constraintsImpl.isEmpty())
266                 {
267                     constraintsImpl.checkConstraints(actions, userPrincipals, rolePrincipals, groupPrincipals, getEffectivePageSecurity());
268                 }
269             }
270 
271             // recursively check all parent constraints in hierarchy
272             NodeImpl parentNodeImpl = (NodeImpl)ProxyHelper.getRealObject(parent);
273             if (parentNodeImpl != null)
274             {
275                 parentNodeImpl.checkConstraints(actions, userPrincipals, rolePrincipals, groupPrincipals, false, false);
276             }
277         }
278     }
279 
280     /* (non-Javadoc)
281      * @see org.apache.jetspeed.om.page.impl.BaseElementImpl#checkPermissions(java.lang.String, int, boolean, boolean)
282      */
283     public void checkPermissions(String path, int mask, boolean checkNodeOnly, boolean checkParentsOnly) throws SecurityException
284     {
285         // check granted node permissions unless the check is
286         // to be skipped due to explicity granted access
287         if (!checkParentsOnly)
288         {
289             super.checkPermissions(path, mask, true, false);
290         }
291         
292         // if not checking node only, recursively check
293         // all parent permissions in hierarchy
294         if (!checkNodeOnly)
295         {
296             NodeImpl parentNodeImpl = (NodeImpl)ProxyHelper.getRealObject(parent);
297             if (parentNodeImpl != null)
298             {
299                 parentNodeImpl.checkPermissions(mask, false, false);
300             }
301         }
302     }
303 
304     /* (non-Javadoc)
305      * @see org.apache.jetspeed.om.page.impl.BaseElementImpl#getLogicalPermissionPath()
306      */
307     public String getLogicalPermissionPath()
308     {
309         // compute logical path if required
310         if (logicalPath == null)
311         {
312             // check for path attributes
313             if ((subsite != null) || (user != null) || (role != null) || (group != null) || (mediatype != null) ||
314                 (locale != null) || (extendedAttributeName != null) || (extendedAttributeValue != null))
315             {
316                 // parse path, stripping reserved folders from path
317                 boolean skipAttribute = false;
318                 StringBuffer logicalPathBuffer = new StringBuffer();
319                 StringTokenizer pathElements = new StringTokenizer(path, Folder.PATH_SEPARATOR);
320                 while (pathElements.hasMoreTokens())
321                 {
322                     // classify path element
323                     String pathElement = pathElements.nextToken();
324                     if (!skipAttribute)
325                     {
326                         if (!pathElement.startsWith(Folder.RESERVED_SUBSITE_FOLDER_PREFIX))
327                         {
328                             if (!pathElement.startsWith(Folder.RESERVED_FOLDER_PREFIX))
329                             {
330                                 // append to logical path
331                                 logicalPathBuffer.append(Folder.PATH_SEPARATOR);
332                                 logicalPathBuffer.append(pathElement);
333                             }
334                             else
335                             {
336                                 // skip next attribute path element
337                                 skipAttribute = true;
338                             }
339                         }
340                     }
341                     else
342                     {
343                         // attribute path element skipped
344                         skipAttribute = false;
345                     }
346                 }
347                 
348                 // set logical path
349                 if (logicalPathBuffer.length() > 0)
350                 {
351                     logicalPath = logicalPathBuffer.toString();
352                 }
353                 else
354                 {
355                     logicalPath = Folder.PATH_SEPARATOR;
356                 }
357             }
358             else
359             {
360                 // no path attributes: logical path and physical path equivalent
361                 logicalPath = path;
362             }
363         }
364 
365         return logicalPath;
366     }
367 
368     /* (non-Javadoc)
369      * @see org.apache.jetspeed.om.page.impl.BaseElementImpl#getPhysicalPermissionPath()
370      */
371     public String getPhysicalPermissionPath()
372     {
373         // return path
374         return path;
375     }
376 
377     /* (non-Javadoc)
378      * @see org.apache.jetspeed.page.document.Node#getParent()
379      */
380     public Node getParent()
381     {
382         return parent;
383     }
384     
385     /* (non-Javadoc)
386      * @see org.apache.jetspeed.page.document.Node#setParent(org.apache.jetspeed.page.document.Node)
387      */
388     public void setParent(Node parent)
389     {
390         // set node parent
391         this.parent = parent;
392 
393         // update path if required
394         if (parent != null)
395         {
396             String parentPath = parent.getPath();
397             if ((parentPath.equals(Folder.PATH_SEPARATOR) &&
398                  (path.lastIndexOf(Folder.PATH_SEPARATOR) > 0)) ||
399                 (!parentPath.equals(Folder.PATH_SEPARATOR) &&
400                  !parentPath.equals(path.substring(0, path.lastIndexOf(Folder.PATH_SEPARATOR)))))
401             {
402                 // set path
403                 path = parentPath + Folder.PATH_SEPARATOR + getName();
404 
405                 // reset logicalPath
406                 logicalPath = null;
407             }
408         }
409     }
410 
411     /* (non-Javadoc)
412      * @see org.apache.jetspeed.page.document.Node#getPath()
413      */
414     public String getPath()
415     {
416         // return path from attributes and base path
417         return path;
418     }
419     
420     /* (non-Javadoc)
421      * @see org.apache.jetspeed.page.document.Node#setPath(java.lang.String)
422      */
423     public void setPath(String path)
424     {
425         // set canonical node path
426         this.path = getCanonicalNodePath(path);
427 
428         // reset logical path
429         logicalPath = null;
430 
431         // parse and set informational attributes from path
432         String attributeName = null;
433         StringTokenizer pathElements = new StringTokenizer(this.path, Folder.PATH_SEPARATOR);
434         while (pathElements.hasMoreTokens())
435         {
436             String pathElement = pathElements.nextToken();
437             if (attributeName != null)
438             {
439                 // set last attribute name with attribute value
440                 if (attributeName.startsWith(Folder.RESERVED_USER_FOLDER_NAME))
441                 {
442                     user = pathElement.toLowerCase();
443                 }
444                 else if (attributeName.startsWith(Folder.RESERVED_ROLE_FOLDER_NAME))
445                 {
446                     role = pathElement.toLowerCase();
447                 }
448                 else if (attributeName.startsWith(Folder.RESERVED_GROUP_FOLDER_NAME))
449                 {
450                     group = pathElement.toLowerCase();
451                 }
452                 else if (attributeName.startsWith(Folder.RESERVED_MEDIATYPE_FOLDER_NAME))
453                 {
454                     mediatype = pathElement.toLowerCase();
455                 }
456                 else if (attributeName.startsWith(Folder.RESERVED_LANGUAGE_FOLDER_NAME))
457                 {
458                     if (locale != null)
459                     {
460                         // compose locale from language + country
461                         locale = pathElement.toLowerCase() + "_" + locale;
462                     }
463                     else
464                     {
465                         locale = pathElement.toLowerCase();
466                     }
467                 }
468                 else if (attributeName.startsWith(Folder.RESERVED_COUNTRY_FOLDER_NAME))
469                 {
470                     if (locale != null)
471                     {
472                         // compose locale from language + country
473                         locale = locale + "_" + pathElement.toLowerCase() ;
474                     }
475                     else
476                     {
477                         locale = pathElement.toLowerCase();
478                     }
479                 }
480                 else if (attributeName.startsWith(Folder.RESERVED_FOLDER_PREFIX))
481                 {
482                     extendedAttributeName = attributeName.substring(Folder.RESERVED_FOLDER_PREFIX.length());
483                     extendedAttributeValue = pathElement.toLowerCase();
484                 }
485 
486                 // reset attribute name
487                 attributeName = null;
488             }
489             else if (pathElement.startsWith(Folder.RESERVED_SUBSITE_FOLDER_PREFIX))
490             {
491                 subsite = pathElement.substring(Folder.RESERVED_SUBSITE_FOLDER_PREFIX.length()).toLowerCase();
492             }
493             else if (pathElement.startsWith(Folder.RESERVED_FOLDER_PREFIX))
494             {
495                 // save attribute name
496                 attributeName = pathElement.toLowerCase();
497             }
498         }
499 
500         // set name based on path
501         if (!this.path.equals(Folder.PATH_SEPARATOR))
502         {
503             super.setName(this.path.substring(this.path.lastIndexOf(Folder.PATH_SEPARATOR) + 1));
504         }
505         else
506         {
507             super.setName(Folder.PATH_SEPARATOR);
508         }
509 
510         // reset parent if required
511         if (parent != null)
512         {
513             String parentPath = parent.getPath();
514             if ((parentPath.equals(Folder.PATH_SEPARATOR) &&
515                  (this.path.lastIndexOf(Folder.PATH_SEPARATOR) > 0)) ||
516                 (!parentPath.equals(Folder.PATH_SEPARATOR) &&
517                  !parentPath.equals(this.path.substring(0, this.path.lastIndexOf(Folder.PATH_SEPARATOR)))))
518             {
519                 parent = null;
520             }
521         }
522     }
523 
524     /* (non-Javadoc)
525      * @see org.apache.jetspeed.page.document.Node#getMetadata()
526      */
527     public GenericMetadata getMetadata()
528     {
529         return getPageMetadata();
530     }
531     
532     /* (non-Javadoc)
533      * @see org.apache.jetspeed.page.document.Node#getTitle(java.util.Locale)
534      */
535     public String getTitle(Locale locale)
536     {
537         // get title from metadata or use default title
538         String title = getPageMetadata().getText("title", locale);
539         if (title == null)
540         {
541             title = getTitle();
542         }
543         return title;
544     }
545     
546     /* (non-Javadoc)
547      * @see org.apache.jetspeed.page.document.Node#getShortTitle(java.util.Locale)
548      */
549     public String getShortTitle(Locale locale)
550     {
551         // get short title from metadata or use title from metadata,
552         // default short title, or default title
553         String shortTitle = getPageMetadata().getText("short-title", locale);
554         if (shortTitle == null)
555         {
556             shortTitle = getPageMetadata().getText("title", locale);
557             if (shortTitle == null)
558             {
559                 shortTitle = getShortTitle();
560                 if (shortTitle == null)
561                 {
562                     shortTitle = getTitle();
563                 }
564             }
565         }
566         return shortTitle;
567     }
568     
569     /* (non-Javadoc)
570      * @see org.apache.jetspeed.page.document.Node#getType()
571      */
572     public abstract String getType();
573     
574     /* (non-Javadoc)
575      * @see org.apache.jetspeed.page.document.Node#getUrl()
576      */
577     public String getUrl()
578     {
579         return path;
580     }
581     
582     /* (non-Javadoc)
583      * @see org.apache.jetspeed.page.document.Node#isHidden()
584      */
585     public boolean isHidden()
586     {
587         return hidden;
588     }    
589 
590     /* (non-Javadoc)
591      * @see org.apache.jetspeed.page.document.Node#setHidden(boolean)
592      */
593     public void setHidden(boolean hidden)
594     {
595         this.hidden = hidden;
596     }    
597 }