001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.jsp;
029
030import org.opencms.file.CmsFile;
031import org.opencms.file.CmsFolder;
032import org.opencms.file.CmsObject;
033import org.opencms.file.CmsProperty;
034import org.opencms.file.CmsPropertyDefinition;
035import org.opencms.file.CmsRequestContext;
036import org.opencms.file.CmsResource;
037import org.opencms.file.CmsResourceFilter;
038import org.opencms.file.types.CmsResourceTypeXmlContent;
039import org.opencms.file.types.CmsResourceTypeXmlPage;
040import org.opencms.i18n.CmsLocaleGroup;
041import org.opencms.jsp.util.CmsJspCategoryAccessBean;
042import org.opencms.jsp.util.CmsJspContentAccessBean;
043import org.opencms.jsp.util.CmsJspImageBean;
044import org.opencms.jsp.util.CmsJspValueTransformers.CmsLocalePropertyLoaderTransformer;
045import org.opencms.main.CmsException;
046import org.opencms.main.CmsLog;
047import org.opencms.main.OpenCms;
048import org.opencms.security.CmsSecurityException;
049import org.opencms.util.CmsCollectionsGenericWrapper;
050import org.opencms.util.CmsUUID;
051
052import java.util.ArrayList;
053import java.util.Collections;
054import java.util.HashMap;
055import java.util.List;
056import java.util.Locale;
057import java.util.Map;
058
059import org.apache.commons.logging.Log;
060
061import com.google.common.collect.Maps;
062
063/**
064 * Wrapper subclass of CmsResource with some convenience methods.<p>
065 */
066public class CmsJspResourceWrapper extends CmsResource {
067
068    /** Serial version id. */
069    private static final long serialVersionUID = 1L;
070
071    /** Logger instance for this class. */
072    @SuppressWarnings("unused")
073    private static final Log LOG = CmsLog.getLog(CmsJspResourceWrapper.class);
074
075    /** The CMS context. */
076    private CmsObject m_cms;
077
078    /** The set of locale variants. */
079    private Map<String, CmsJspResourceWrapper> m_localeResources;
080
081    /** The main locale. */
082    private Locale m_mainLocale;
083
084    /** The file object for this resource. */
085    private CmsFile m_file;
086
087    /** The resource / file content as a String. */
088    private String m_content;
089
090    /** The calculated site path of the resource. */
091    private String m_sitePath;
092
093    /** Properties of this resource. */
094    private Map<String, String> m_properties;
095
096    /** Properties of this resource with search. */
097    private Map<String, String> m_propertiesSearch;
098
099    /** Locale properties of this resource. */
100    private Map<String, Map<String, String>> m_propertiesLocale;
101
102    /** Locale properties of this resource with search. */
103    private Map<String, Map<String, String>> m_propertiesLocaleSearch;
104
105    /** The XML content access bean. */
106    private CmsJspContentAccessBean m_xml;
107
108    /** Stores if this resource is an XML content or not. */
109    private Boolean m_isXml;
110
111    /** All parent folder of this resource in the current site as a list. */
112    public List<CmsJspResourceWrapper> m_parentFolders;
113
114    /** The parent folder of this resource in the current site. */
115    private CmsJspResourceWrapper m_parentFolder;
116
117    /** The default file of this resource, assumed that this resource is a folder. */
118    CmsJspResourceWrapper m_navigationDefaultFile;
119
120    /** The navigation builder for this resource. */
121    CmsJspNavBuilder m_navBuilder;
122
123    /** The navigation info elements in this resource, assuming that this resource is a folder. */
124    private List<CmsJspNavElement> m_navigationForFolder;
125
126    /** The navigation info element for this resource. */
127    private CmsJspNavElement m_navigation;
128
129    /** Image bean instance created from this resource. */
130    private CmsJspImageBean m_imageBean;
131
132    /** The category access bean for this resource. */
133    private CmsJspCategoryAccessBean m_categories;
134
135    /**
136     * Creates a new instance.<p>
137     *
138     * @param cms the current CMS context
139     * @param res the resource to wrap
140     */
141    private CmsJspResourceWrapper(CmsObject cms, CmsResource res) {
142
143        super(
144            res.getStructureId(),
145            res.getResourceId(),
146            res.getRootPath(),
147            res.getTypeId(),
148            res.isFolder(),
149            res.getFlags(),
150            res.getProjectLastModified(),
151            res.getState(),
152            res.getDateCreated(),
153            res.getUserCreated(),
154            res.getDateLastModified(),
155            res.getUserLastModified(),
156            res.getDateReleased(),
157            res.getDateExpired(),
158            res.getSiblingCount(),
159            res.getLength(),
160            res.getDateContent(),
161            res.getVersion());
162        m_cms = cms;
163        m_file = null;
164        m_content = "";
165    }
166
167    /**
168     * Factory method to create a new {@link CmsJspResourceWrapper} instance from a {@link CmsResource}.<p>
169     *
170     * In case the parameter resource already is a wrapped resource AND the OpenCms request context is
171     * the same as the provided context, the parameter object is returned.<p>
172     *
173     * @param cms the current CMS context
174     * @param res the resource to wrap
175     *
176     * @return a new instance of a {@link CmsJspResourceWrapper}
177     */
178    public static CmsJspResourceWrapper wrap(CmsObject cms, CmsResource res) {
179
180        CmsJspResourceWrapper result = null;
181        if ((cms != null) && (res != null)) {
182            if (res instanceof CmsJspResourceWrapper) {
183                CmsJspResourceWrapper wrapper = (CmsJspResourceWrapper)res;
184                if (cms.getRequestContext().getSiteRoot().equals(wrapper.getRequestContext().getSiteRoot())) {
185                    result = wrapper;
186                } else {
187                    result = new CmsJspResourceWrapper(cms, res);
188                }
189            } else {
190                result = new CmsJspResourceWrapper(cms, res);
191            }
192        }
193        return result;
194    }
195
196    /**
197     * Two resources are considered equal in case their structure id is equal.<p>
198     *
199     * @see CmsResource#equals(java.lang.Object)
200     */
201    @Override
202    public boolean equals(Object obj) {
203
204        if (obj == null) {
205            return false;
206        }
207
208        if (obj == this) {
209            return true;
210        }
211        if (obj instanceof CmsResource) {
212            return ((CmsResource)obj).getStructureId().equals(getStructureId());
213        }
214        return false;
215    }
216
217    /**
218     * Returns the categories assigned to this resource.<p>
219     *
220     * @return the categories assigned to this resource
221     */
222    public CmsJspCategoryAccessBean getCategories() {
223
224        if (m_categories == null) {
225            m_categories = new CmsJspCategoryAccessBean(m_cms, this);
226        }
227        return m_categories;
228    }
229
230    /**
231     * Returns the OpenCms user context this resource was initialized with.<p>
232     *
233     * @return the OpenCms user context this resource was initialized with
234     */
235    public CmsObject getCmsObject() {
236
237        return m_cms;
238    }
239
240    /**
241     * Returns the content of the file as a String.<p>
242     *
243     * @return the content of the file as a String
244     */
245    public String getContent() {
246
247        if ((m_content.length() == 0) && (getFile() != null)) {
248            m_content = new String(getFile().getContents());
249        }
250        return m_content;
251    }
252
253    /**
254     * Returns this resources name extension (if present).<p>
255     *
256     * The extension will always be lower case.<p>
257     *
258     * @return the extension or <code>null</code> if not available
259     *
260     * @see CmsResource#getExtension(String)
261     * @see org.opencms.jsp.util.CmsJspVfsAccessBean#getResourceExtension(Object)
262     */
263    public String getExtension() {
264
265        return getExtension(getRootPath());
266    }
267
268    /**
269     * Returns the full file object for this resource.<p>
270     *
271     * @return the full file object for this resource
272     */
273    public CmsFile getFile() {
274
275        if ((m_file == null) && !isFolder()) {
276            try {
277                m_file = m_cms.readFile(this);
278            } catch (CmsException e) {
279                // this should not happen since we are updating from a resource object
280            }
281        }
282        return m_file;
283    }
284
285    /**
286     * Returns the folder of this resource.<p>
287     *
288     * In case this resource already is a {@link CmsFolder}, it is returned without modification.
289     * In case it is a {@link CmsFile}, the parent folder of the file is returned.<p>
290     *
291     * @return the folder of this resource
292     *
293     * @see #getSitePathFolder()
294     */
295    public CmsJspResourceWrapper getFolder() {
296
297        CmsJspResourceWrapper result;
298        if (isFolder()) {
299            result = this;
300        } else {
301            result = readResource(getSitePathFolder());
302        }
303        return result;
304    }
305
306    /**
307     * Returns <code>true</code> in case this resource is an image in the VFS.<p>
308     *
309     * @return <code>true</code> in case this resource is an image in the VFS
310     */
311    public boolean getIsImage() {
312
313        return getToImage().isImage();
314    }
315
316    /**
317     * Returns <code>true</code> in case this resource is an XML content.<p>
318     *
319     * @return <code>true</code> in case this resource is an XML content
320     */
321    public boolean getIsXml() {
322
323        if (m_isXml == null) {
324            m_isXml = Boolean.valueOf(
325                CmsResourceTypeXmlPage.isXmlPage(this) || CmsResourceTypeXmlContent.isXmlContent(this));
326        }
327        return m_isXml.booleanValue();
328    }
329
330    /**
331     * Returns a substituted link to this resource.<p>
332     *
333     * @return the link
334     */
335    public String getLink() {
336
337        return OpenCms.getLinkManager().substituteLinkForUnknownTarget(
338            m_cms,
339            m_cms.getRequestContext().getSitePath(this));
340    }
341
342    /**
343     * Returns a map of the locale group for the current resource, with locale strings as keys.<p>
344     *
345     * @return a map with locale strings as keys and resource wrappers for the corresponding locale variants
346     */
347    public Map<String, CmsJspResourceWrapper> getLocaleResource() {
348
349        if (m_localeResources != null) {
350            return m_localeResources;
351        }
352        try {
353            CmsLocaleGroup localeGroup = m_cms.getLocaleGroupService().readLocaleGroup(this);
354            Map<Locale, CmsResource> resourcesByLocale = localeGroup.getResourcesByLocale();
355            Map<String, CmsJspResourceWrapper> result = Maps.newHashMap();
356            for (Map.Entry<Locale, CmsResource> entry : resourcesByLocale.entrySet()) {
357                result.put(entry.getKey().toString(), CmsJspResourceWrapper.wrap(m_cms, entry.getValue()));
358            }
359            m_localeResources = result;
360            return result;
361        } catch (CmsException e) {
362            return new HashMap<String, CmsJspResourceWrapper>();
363        }
364    }
365
366    /**
367     * Returns the main locale for this resource.<p>
368     *
369     * @return the main locale for this resource
370     */
371    public Locale getMainLocale() {
372
373        if (m_mainLocale != null) {
374            return m_mainLocale;
375        }
376        try {
377            CmsLocaleGroup localeGroup = m_cms.getLocaleGroupService().readLocaleGroup(this);
378            m_mainLocale = localeGroup.getMainLocale();
379            return m_mainLocale;
380        } catch (CmsException e) {
381            return null;
382        }
383    }
384
385    /**
386     * Returns the mime type for this resource.<p>
387     *
388     * In case no valid mime type can be determined from the file extension, <code>text/plain</code> is returned.<p>
389     *
390     * @return the mime type for this resource
391     */
392    public String getMimeType() {
393
394        return OpenCms.getResourceManager().getMimeType(getRootPath(), null, "text/plain");
395    }
396
397    /**
398     * Returns the navigation builder for this resource.<p>
399     *
400     * This will be initialized with this resource as default URI.<p>
401     *
402     * @return the navigation builder for this resource
403     */
404    public CmsJspNavBuilder getNavBuilder() {
405
406        if (m_navBuilder == null) {
407            m_navBuilder = new CmsJspNavBuilder();
408            m_navBuilder.init(m_cms, null, getSitePath());
409        }
410        return m_navBuilder;
411    }
412
413    /**
414     * Returns the navigation info element for this resource.<p>
415     *
416     * @return the navigation info element for this resource
417     */
418    public CmsJspNavElement getNavigation() {
419
420        if (m_navigation == null) {
421            m_navigation = getNavBuilder().getNavigationForResource();
422        }
423        return m_navigation;
424    }
425
426    /**
427     * Returns the default resource for this resource.<p>
428     *
429     * If this resource is a file, then this file is returned.<p>
430     *
431     * Otherwise, in case this resource is a folder:<br>
432     * <ol>
433     *   <li>the {@link CmsPropertyDefinition#PROPERTY_DEFAULT_FILE} is checked, and
434     *   <li>if still no file could be found, the configured default files in the
435     *       <code>opencms-vfs.xml</code> configuration are iterated until a match is
436     *       found, and
437     *   <li>if still no file could be found, <code>null</code> is returned
438     * </ol>
439     *
440     * @return the default file for the given folder
441     *
442     * @see CmsObject#readDefaultFile(CmsResource, CmsResourceFilter)
443     */
444    public CmsJspResourceWrapper getNavigationDefaultFile() {
445
446        if (m_navigationDefaultFile == null) {
447            if (isFolder()) {
448                try {
449                    m_navigationDefaultFile = wrap(m_cms, m_cms.readDefaultFile(this, CmsResourceFilter.DEFAULT));
450                } catch (CmsSecurityException e) {
451                    if (LOG.isDebugEnabled()) {
452                        LOG.debug(e.getMessage(), e);
453                    }
454                }
455            }
456        } else {
457            m_navigationDefaultFile = this;
458        }
459        return m_navigationDefaultFile;
460    }
461
462    /**
463     * Returns the navigation info elements in this resource, assuming that this resource is a folder.<p>
464     *
465     * @return the navigation info elements in this resource, assuming that this resource is a folder
466     */
467    public List<CmsJspNavElement> getNavigationForFolder() {
468
469        if (m_navigationForFolder == null) {
470            m_navigationForFolder = getNavBuilder().getNavigationForFolder();
471        }
472        return m_navigationForFolder;
473    }
474
475    /**
476     * Returns the parent folder of this resource in the current site.<p>
477     *
478     * In case this resource represents the site root folder, <code>null</code> is returned.<p>
479     *
480     * @return the parent folder of this resource in the current site
481     *
482     * @see #getSitePathParentFolder()
483     * @see CmsResource#getParentFolder(String)
484     * @see org.opencms.jsp.util.CmsJspVfsAccessBean#getParentFolder(Object)
485     */
486    public CmsJspResourceWrapper getParentFolder() {
487
488        if (m_parentFolder == null) {
489            String parentFolder = getSitePathParentFolder();
490            if (parentFolder != null) {
491                m_parentFolder = readResource(getSitePathParentFolder());
492            }
493        }
494        return m_parentFolder;
495    }
496
497    /**
498     * Returns all parent folder of this resource in the current site as a list.<p>
499     *
500     * First resource in the list will be the direct parent folder of this resource,
501     * the last element will be the site root folder.<p>
502     *
503     * @return all parent folder of this resource in the current site as a list
504     */
505    public List<CmsJspResourceWrapper> getParentFolders() {
506
507        if (m_parentFolders == null) {
508            m_parentFolders = new ArrayList<CmsJspResourceWrapper>();
509            CmsJspResourceWrapper parentFolder = getParentFolder();
510            while (parentFolder != null) {
511                m_parentFolders.add(parentFolder);
512                parentFolder = parentFolder.getParentFolder();
513            }
514        }
515        return m_parentFolders;
516    }
517
518    /**
519     * Returns the direct properties of this resource in a map.<p>
520     *
521     * This is without "search", so it will not include inherited properties from the parent folders.<p>
522     *
523     * @return the direct properties of this resource in a map
524     */
525    public Map<String, String> getProperty() {
526
527        if (m_properties == null) {
528            try {
529                List<CmsProperty> properties = m_cms.readPropertyObjects(this, false);
530                m_properties = CmsProperty.toMap(properties);
531            } catch (CmsException e) {
532                if (LOG.isDebugEnabled()) {
533                    LOG.debug(e.getMessage(), e);
534                }
535            }
536        }
537        return m_properties;
538    }
539
540    /**
541     * Returns the direct properties of this resource in a map for a given locale.<p>
542     *
543     * This is without "search", so it will not include inherited properties from the parent folders.<p>
544     *
545     * @return the direct properties of this resource in a map for  a given locale
546     */
547    public Map<String, Map<String, String>> getPropertyLocale() {
548
549        if (m_propertiesLocale == null) {
550            m_propertiesLocale = CmsCollectionsGenericWrapper.createLazyMap(
551                new CmsLocalePropertyLoaderTransformer(getCmsObject(), this, false));
552            // result may still be null
553            return (m_propertiesLocale == null) ? Collections.EMPTY_MAP : m_propertiesLocale;
554        }
555        return m_propertiesLocale;
556    }
557
558    /**
559     * Returns the searched properties of this resource in a map for a given locale.<p>
560     *
561     * This is with "search", so it will include inherited properties from the parent folders.<p>
562     *
563     * @return the direct properties of this resource in a map for a given locale
564     */
565    public Map<String, Map<String, String>> getPropertyLocaleSearch() {
566
567        if (m_propertiesLocaleSearch == null) {
568            m_propertiesLocaleSearch = CmsCollectionsGenericWrapper.createLazyMap(
569                new CmsLocalePropertyLoaderTransformer(getCmsObject(), this, true));
570            // result may still be null
571            return (m_propertiesLocaleSearch == null) ? Collections.EMPTY_MAP : m_propertiesLocaleSearch;
572        }
573        return m_propertiesLocaleSearch;
574    }
575
576    /**
577     * Returns the searched properties of this resource in a map.<p>
578     *
579     * This is with "search", so it will include inherited properties from the parent folders.<p>
580     *
581     * @return the direct properties of this resource in a map
582     */
583    public Map<String, String> getPropertySearch() {
584
585        if (m_propertiesSearch == null) {
586            try {
587                List<CmsProperty> properties = m_cms.readPropertyObjects(this, true);
588                m_propertiesSearch = CmsProperty.toMap(properties);
589            } catch (CmsException e) {
590                if (LOG.isDebugEnabled()) {
591                    LOG.debug(e.getMessage(), e);
592                }
593            }
594        }
595        return m_propertiesSearch;
596    }
597
598    /**
599     * Returns the OpenCms user request context this resource was initialized with.<p>
600     *
601     * @return the OpenCms user request context this resource was initialized with
602     */
603    public CmsRequestContext getRequestContext() {
604
605        return m_cms.getRequestContext();
606    }
607
608    /**
609     * Returns this resources name extension (if present).<p>
610     *
611     * The extension will always be lower case.<p>
612     *
613     * @return the extension or <code>null</code> if not available
614     *
615     * @see CmsResource#getExtension(String)
616     * @see org.opencms.jsp.util.CmsJspVfsAccessBean#getResourceExtension(Object)
617     */
618    public String getResourceExtension() {
619
620        return getExtension();
621    }
622
623    /**
624     * Returns the name of this resource without the path information.<p>
625     *
626     * The resource name of a file is the name of the file.
627     * The resource name of a folder is the folder name with trailing "/".
628     * The resource name of the root folder is <code>/</code>.<p>
629     *
630     * @return the name of this resource without the path information
631     *
632     * @see CmsResource#getName()
633     * @see org.opencms.jsp.util.CmsJspVfsAccessBean#getResourceName(Object)
634     */
635    public String getResourceName() {
636
637        return getName();
638    }
639
640    /**
641     * Returns the folder name of this resource from the root site.<p>
642     *
643     * In case this resource already is a {@link CmsFolder}, the folder path is returned without modification.
644     * In case it is a {@link CmsFile}, the parent folder name of the file is returned.<p>
645     *
646     * @return  the folder name of this resource from the root site
647     */
648    public String getRootPathFolder() {
649
650        String result;
651        if (isFile()) {
652            result = getRootPathParentFolder();
653        } else {
654            result = getRootPath();
655        }
656        return result;
657    }
658
659    /**
660     * Returns the directory level of a resource from the root site.<p>
661     *
662     * The root folder "/" has level 0,
663     * a folder "/foo/" would have level 1,
664     * a folder "/foo/bar/" level 2 etc.<p>
665     *
666     * @return the directory level of a resource from the root site
667     *
668     * @see CmsResource#getPathLevel(String)
669     */
670    public int getRootPathLevel() {
671
672        return getPathLevel(getRootPath());
673    }
674
675    /**
676     * Returns the parent folder of this resource from the root site.<p>
677     *
678     * @return the parent folder of this resource from the root site
679     *
680     * @see CmsResource#getParentFolder(String)
681     */
682    public String getRootPathParentFolder() {
683
684        return getParentFolder(getRootPath());
685    }
686
687    /**
688     * Returns the current site path to this resource.<p>
689     *
690     * @return the current site path to this resource
691     *
692     * @see org.opencms.file.CmsRequestContext#getSitePath(CmsResource)
693     */
694    public String getSitePath() {
695
696        if (m_sitePath == null) {
697            m_sitePath = m_cms.getRequestContext().getSitePath(this);
698        }
699
700        return m_sitePath;
701    }
702
703    /**
704     * Returns the folder name of this resource in the current site.<p>
705     *
706     * In case this resource already is a {@link CmsFolder}, the folder path is returned without modification.
707     * In case it is a {@link CmsFile}, the parent folder name of the file is returned.<p>
708     *
709     * @return  the folder name of this resource in the current site
710     */
711    public String getSitePathFolder() {
712
713        String result;
714        if (isFile()) {
715            result = getSitePathParentFolder();
716        } else {
717            result = getSitePath();
718        }
719        return result;
720    }
721
722    /**
723     * Returns the directory level of a resource in the current site.<p>
724     *
725     * The root folder "/" has level 0,
726     * a folder "/foo/" would have level 1,
727     * a folder "/foo/bar/" level 2 etc.<p>
728     *
729     * @return the directory level of a resource in the current site
730     *
731     * @see CmsResource#getPathLevel(String)
732     * @see org.opencms.jsp.util.CmsJspVfsAccessBean#getPathLevel(Object)
733     */
734    public int getSitePathLevel() {
735
736        return getPathLevel(getSitePath());
737    }
738
739    /**
740     * Returns the parent folder of this resource in the current site.<p>
741     *
742     * @return the parent folder of this resource in the current site
743     *
744     * @see CmsResource#getParentFolder(String)
745     * @see org.opencms.jsp.util.CmsJspVfsAccessBean#getParentFolder(Object)
746     */
747    public String getSitePathParentFolder() {
748
749        return getParentFolder(getSitePath());
750    }
751
752    /**
753     * Returns a scaled image bean from the wrapped value.<p>
754     *
755     * In case the value does not point to an image resource, <code>null</code> is returned.
756     *
757     * @return the scaled image bean
758     */
759    public CmsJspImageBean getToImage() {
760
761        if (m_imageBean == null) {
762            m_imageBean = new CmsJspImageBean(getCmsObject(), this, null);
763        }
764        return m_imageBean;
765    }
766
767    /**
768     * Returns an XML content access bean created for this resource.<p>
769     *
770     * In case this resource is not an XML content, <code>null</code> is returned.<p>
771     *
772     * @return an XML content access bean created for this resource
773     *
774     * @see #getIsXml()
775     */
776    public CmsJspContentAccessBean getToXml() {
777
778        if ((m_xml == null) && getIsXml()) {
779            m_xml = new CmsJspContentAccessBean(m_cms, this);
780        }
781        return m_xml;
782    }
783
784    /**
785     * Returns an XML content access bean created for this resource.<p>
786     *
787     * In case this resource is not an XML content, <code>null</code> is returned.<p>
788     *
789     * @return an XML content access bean created for this resource
790     *
791     * @see #getToXml()
792     * @see #getIsXml()
793     */
794    public CmsJspContentAccessBean getXml() {
795
796        return getToXml();
797    }
798
799    /**
800     * @see CmsResource#hashCode()
801     * @see java.lang.Object#hashCode()
802     */
803    @Override
804    public int hashCode() {
805
806        if (getStructureId() != null) {
807            return getStructureId().hashCode();
808        }
809
810        return CmsUUID.getNullUUID().hashCode();
811    }
812
813    /**
814     * Returns <code>true</code> in case this resource is child resource of the provided resource which is assumed to be a folder.<p>
815     *
816     * @param resource the resource to check
817     *
818     * @return <code>true</code> in case this resource is child resource of the provided resource which is assumed to be a folder
819     */
820    public boolean isChildResourceOf(CmsResource resource) {
821
822        return (resource != null)
823            && resource.isFolder()
824            && !(getStructureId().equals(resource.getStructureId()))
825            && ((getRootPath().indexOf(resource.getRootPath()) == 0));
826    }
827
828    /**
829     * Returns <code>true</code> in case this resource is child resource of the provided resource path which is assumed to be a folder in the current site.<p>
830     *
831     * No check is performed to see if the provided site path resource actually exists.<p>
832     *
833     * @param sitePath the resource to check
834     *
835     * @return <code>true</code> in case this resource is child resource of the provided resource path which is assumed to be a folder in the current site
836     */
837    public boolean isChildResourceOf(String sitePath) {
838
839        return (sitePath != null)
840            && ((getSitePath().indexOf(sitePath) == 0))
841            && (sitePath.length() < getSitePath().length());
842    }
843
844    /**
845     * Returns <code>true</code> in case this resource is a parent folder of the provided resource.<p>
846     *
847     * @param resource the resource to check
848     *
849     * @return <code>true</code> in case this resource is a parent folder of the provided resource
850     */
851    public boolean isParentFolderOf(CmsResource resource) {
852
853        return (resource != null)
854            && isFolder()
855            && !(getStructureId().equals(resource.getStructureId()))
856            && ((resource.getRootPath().indexOf(getRootPath()) == 0));
857    }
858
859    /**
860     * Returns <code>true</code> in case this resource is a parent folder of the provided resource path in the current site.<p>
861     *
862     * No check is performed to see if the provided site path resource actually exists.<p>
863     *
864     * @param sitePath the path to check
865     *
866     * @return <code>true</code> in case this resource is a parent folder of the provided resource path in the current site
867     */
868    public boolean isParentFolderOf(String sitePath) {
869
870        return (sitePath != null)
871            && isFolder()
872            && ((sitePath.indexOf(getSitePath()) == 0))
873            && (sitePath.length() > getSitePath().length());
874    }
875
876    /**
877     * Reads a resource, suppressing possible exceptions.<p>
878     *
879     * @param sitePath the site path of the resource to read.
880     *
881     * @return the resource of <code>null</code> on case an exception occurred while reading
882     */
883    private CmsJspResourceWrapper readResource(String sitePath) {
884
885        CmsJspResourceWrapper result = null;
886        try {
887            result = new CmsJspResourceWrapper(m_cms, m_cms.readResource(sitePath));
888        } catch (CmsException e) {
889            if (LOG.isDebugEnabled()) {
890                LOG.debug(e.getMessage(), e);
891            }
892        }
893        return result;
894    }
895}