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.util;
029
030import org.opencms.file.CmsFile;
031import org.opencms.file.CmsObject;
032import org.opencms.file.CmsProperty;
033import org.opencms.file.CmsResource;
034import org.opencms.file.history.I_CmsHistoryResource;
035import org.opencms.main.CmsException;
036import org.opencms.main.CmsRuntimeException;
037import org.opencms.util.CmsCollectionsGenericWrapper;
038
039import java.util.Map;
040
041import org.apache.commons.collections.Transformer;
042
043/**
044 * Allows access to the attributes and properties of a resource, usually used inside a loop of a
045 * <code>&lt;cms:resourceload&gt;</code> tag.<p>
046 *
047 * The implementation is optimized for performance and uses lazy initializing of the
048 * requested values as much as possible.<p>
049 *
050 * @since 8.0
051 *
052 * @see org.opencms.jsp.CmsJspTagResourceAccess
053 */
054public class CmsJspResourceAccessBean {
055
056    /**
057     * Transformer that reads a history resource property,
058     * the input is used as String for the history property name to read.<p>
059     */
060    private class CmsHistoryPropertyLoaderTransformer implements Transformer {
061
062        /** The resource where the properties are read from. */
063        private I_CmsHistoryResource m_res;
064
065        /**
066         * Creates a new property loading Transformer.<p>
067         *
068         * @param resource the resource where the properties are read from
069         */
070        public CmsHistoryPropertyLoaderTransformer(CmsResource resource) {
071
072            m_res = (I_CmsHistoryResource)resource;
073
074        }
075
076        /**
077         * @see org.apache.commons.collections.Transformer#transform(java.lang.Object)
078         */
079        public Object transform(Object input) {
080
081            String result;
082            try {
083                // read the requested history property
084                result = CmsProperty.get(
085                    String.valueOf(input),
086                    getCmsObject().readHistoryPropertyObjects(m_res)).getValue();
087            } catch (@SuppressWarnings("unused") CmsException e) {
088                // unable to read history property, return null
089                result = null;
090            }
091            return result;
092        }
093    }
094
095    /** The OpenCms context of the current user. */
096    private CmsObject m_cms;
097
098    /** The file that can be accessed. */
099    private CmsFile m_file;
100
101    /** The history properties of the resource. */
102    private Map<String, String> m_historyProperties;
103
104    /** The properties of the resource. */
105    private Map<String, String> m_properties;
106
107    /**
108     * The properties of the resource according to the provided locale.
109     * The map goes from locale -> property -> value.
110     */
111    private Map<String, Map<String, String>> m_localeProperties;
112
113    /** The resource that can be accessed. */
114    private CmsResource m_resource;
115
116    /**
117     * No argument constructor, required for a JavaBean.<p>
118     *
119     * You must call {@link #init(CmsObject, CmsResource)} and provide the
120     * required values when you use this constructor.<p>
121     *
122     * @see #init(CmsObject, CmsResource)
123     */
124    public CmsJspResourceAccessBean() {
125
126        // must call init() manually later
127    }
128
129    /**
130     * Creates a content access bean based on a Resource.<p>
131     *
132     * @param cms the OpenCms context of the current user
133     * @param resource the resource to create the content from
134     */
135    public CmsJspResourceAccessBean(CmsObject cms, CmsResource resource) {
136
137        init(cms, resource);
138    }
139
140    /**
141     * Returns the OpenCms user context this bean was initialized with.<p>
142     *
143     * @return the OpenCms user context this bean was initialized with
144     */
145    public CmsObject getCmsObject() {
146
147        return m_cms;
148    }
149
150    /**
151     * Returns the raw VFS file object of the current resource.<p>
152     *
153     * This can be used to access information from the raw file on a JSP.<p>
154     *
155     * Usage example on a JSP with the JSTL:<pre>
156     * &lt;cms:resourceload ... &gt;
157     *     &lt;cms:resourceaccess var="res" /&gt;
158     *     Root path of the resource: ${res.file.rootPath}
159     * &lt;/cms:resourceload&gt;</pre>
160     *
161     * @return the raw VFS file object the content accessed by this bean was created from
162     */
163    public CmsFile getFile() {
164
165        if (m_file == null) {
166            try {
167                m_file = m_cms.readFile(m_resource);
168            } catch (CmsException e) {
169                // this usually should not happen, as the resource already has been read by the current user
170                // and we just upgrade it to a file
171                throw new CmsRuntimeException(
172                    Messages.get().container(Messages.ERR_FILE_READ_1, m_resource.getRootPath()),
173                    e);
174            }
175        }
176        return m_file;
177    }
178
179    /**
180     * Returns the file contents of the raw VFS file object as String.<p>
181     *
182     * Usage example on a JSP with the JSTL:<pre>
183     * &lt;cms:resourceload ... &gt;
184     *     &lt;cms:resourceaccess var="res" /&gt;
185     *     String content of the resource: ${res.fileContentAsString}
186     * &lt;/cms:resourceload&gt;</pre>
187     *
188     * @return the file contents of the raw VFS file object as String
189     */
190    public String getFileContentAsString() {
191
192        return new String(getFile().getContents());
193    }
194
195    /**
196     * Returns the site path of the current resource, that is the result of
197     * {@link CmsObject#getSitePath(CmsResource)} with the resource
198     * obtained by {@link #getFile()}.<p>
199     *
200     * Usage example on a JSP with the JSTL:<pre>
201     * &&lt;cms:resourceload ... &gt;
202     *     &lt;cms:resourceaccess var="res" /&gt;
203     *     Site path of the resource: "${res.filename}";
204     * &lt;/cms:resourceload&gt;</pre>
205     *
206     * @return the site path of the current resource
207     *
208     * @see CmsObject#getSitePath(CmsResource)
209     */
210    public String getFilename() {
211
212        return m_cms.getSitePath(m_resource);
213    }
214
215    /**
216     * Short form for {@link #getReadHistoryProperties()}.<p>
217     *
218     * This works only if the current resource is implementing {@link I_CmsHistoryResource}.<p>
219     *
220     * Usage example on a JSP with the <code>&lt;cms:resourceaccess&gt;</code> tag:<pre>
221     * &lt;cms:resourceload ... &gt;
222     *     &lt;cms:resourceaccess var="res" /&gt;
223     *     History "Title" property value of the resource: ${res.historyProperty['Title']}
224     * &lt;/cms:resourceload&gt;</pre>
225     *
226     * @return a map that lazily reads history properties of the resource
227     *
228     * @see #getReadHistoryProperties()
229     */
230    public Map<String, String> getHistoryProperty() {
231
232        return getReadHistoryProperties();
233    }
234
235    /**
236     * Short form for {@link #getReadProperties()}.<p>
237     *
238     * Usage example on a JSP with the <code>&lt;cms:resourceaccess&gt;</code> tag:<pre>
239     * &lt;cms:resourceload ... &gt;
240     *     &lt;cms:resourceaccess var="res" /&gt;
241     *     "Title" property value of the resource: ${res.property['Title']}
242     * &lt;/cms:resourceload&gt;</pre>
243     *
244     * @return a map that lazily reads properties of the resource
245     *
246     * @see #getReadProperties()
247     */
248    public Map<String, String> getProperty() {
249
250        return getReadProperties();
251    }
252
253    /**
254     * Short form for {@link #getReadPropertiesLocale()}.<p>
255     *
256     * Usage example on a JSP with the <code>&lt;cms:resourceaccess&gt;</code> tag:<pre>
257     * &lt;cms:resourceload ... &gt;
258     *     &lt;cms:resourceaccess var="res" /&gt;
259     *     "Title" property value of the resource: ${res.property['de']['Title']}
260     * &lt;/cms:resourceload&gt;</pre>
261     *
262     * @return a map that lazily reads properties of the resource and accesses them wrt. to the specified locale.
263     *
264     * @see #getReadPropertiesLocale()
265     */
266    public Map<String, Map<String, String>> getPropertyLocale() {
267
268        return getReadPropertiesLocale();
269    }
270
271    /**
272     * Returns a map that lazily reads history properties of the resource.<p>
273     *
274     * This works only if the current resource is implementing {@link I_CmsHistoryResource}.<p>
275     *
276     * Usage example on a JSP with the <code>&lt;cms:resourceaccess&gt;</code> tag:<pre>
277     * &lt;cms:resourceload ... &gt;
278     *     &lt;cms:resourceaccess var="res" /&gt;
279     *     History "Title" property value of the resource: ${res.readHistoryProperties['Title']}
280     * &lt;/cms:resourceload&gt;</pre>
281     *
282     * @return a map that lazily reads properties of the resource
283     *
284     * @see #getProperty() for a short form of this method
285     */
286    public Map<String, String> getReadHistoryProperties() {
287
288        if (m_historyProperties == null) {
289            // create lazy map only on demand
290            m_historyProperties = CmsCollectionsGenericWrapper.createLazyMap(
291                new CmsHistoryPropertyLoaderTransformer(m_resource));
292        }
293        return m_historyProperties;
294    }
295
296    /**
297     * Returns a map that lazily reads properties of the resource.<p>
298     *
299     * Usage example on a JSP with the <code>&lt;cms:resourceaccess&gt;</code> tag:<pre>
300     * &lt;cms:resourceload ... &gt;
301     *     &lt;cms:resourceaccess var="res" /&gt;
302     *     "Title" property value of the resource: ${res.readProperties['Title']}
303     * &lt;/cms:resourceload&gt;</pre>
304     *
305     * @return a map that lazily reads properties of the resource
306     *
307     * @see #getProperty() for a short form of this method
308     */
309    public Map<String, String> getReadProperties() {
310
311        if (m_properties == null) {
312            // create lazy map only on demand
313            m_properties = CmsCollectionsGenericWrapper.createLazyMap(
314                new CmsJspValueTransformers.CmsPropertyLoaderTransformer(m_cms, m_resource, false));
315        }
316        return m_properties;
317    }
318
319    /**
320     * Returns a map that lazily reads properties of the resource and makes the accessible according to the specified locale.<p>
321     *
322     * Usage example on a JSP with the <code>&lt;cms:resourceaccess&gt;</code> tag:<pre>
323     * &lt;cms:resourceload ... &gt;
324     *     &lt;cms:resourceaccess var="res" /&gt;
325     *     "Title" property value of the resource: ${res.readProperties['de']['Title']}
326     * &lt;/cms:resourceload&gt;</pre>
327     *
328     * @return a map that lazily reads properties of the resource and makes the accessible according to the specified locale.
329     *
330     * @see #getPropertyLocale() for a short form of this method
331     */
332    public Map<String, Map<String, String>> getReadPropertiesLocale() {
333
334        if (m_localeProperties == null) {
335            m_localeProperties = CmsCollectionsGenericWrapper.createLazyMap(
336                new CmsJspValueTransformers.CmsLocalePropertyLoaderTransformer(m_cms, m_resource, false));
337        }
338        return m_localeProperties;
339
340    }
341
342    /**
343     * Returns the current resource.<p>
344     *
345     * Usage example on a JSP with the JSTL:<pre>
346     * &&lt;cms:resourceload ... &gt;
347     *     &lt;cms:resourceaccess var="res" /&gt;
348     *     Root path of the resource: "${res.resource.rootPath}";
349     * &lt;/cms:resourceload&gt;</pre>
350     *
351     * @return the current resource
352     */
353    public CmsResource getResource() {
354
355        return m_resource;
356    }
357
358    /**
359     * Returns an instance of a VFS access bean,
360     * initialized with the OpenCms user context this bean was created with.<p>
361     *
362     * @return an instance of a VFS access bean,
363     *      initialized with the OpenCms user context this bean was created with
364     */
365    public CmsJspVfsAccessBean getVfs() {
366
367        return CmsJspVfsAccessBean.create(m_cms);
368    }
369
370    /**
371     * Initialize this instance.<p>
372     *
373     * @param cms the OpenCms context of the current user
374     * @param resource the resource to create the content from
375     */
376    public void init(CmsObject cms, CmsResource resource) {
377
378        m_cms = cms;
379        m_resource = resource;
380    }
381}