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.gwt;
029
030import org.opencms.file.CmsObject;
031import org.opencms.file.CmsResource;
032import org.opencms.file.types.CmsResourceTypeUnknownFile;
033import org.opencms.file.types.CmsResourceTypeUnknownFolder;
034import org.opencms.file.types.CmsResourceTypeXmlContainerPage;
035import org.opencms.gwt.shared.CmsGwtConstants;
036import org.opencms.jsp.CmsJspNavBuilder;
037import org.opencms.main.CmsEvent;
038import org.opencms.main.I_CmsEventListener;
039import org.opencms.main.OpenCms;
040import org.opencms.util.CmsStringUtil;
041import org.opencms.workplace.CmsWorkplace;
042import org.opencms.workplace.explorer.CmsExplorerTypeSettings;
043import org.opencms.workplace.explorer.CmsIconRule;
044
045import java.util.ArrayList;
046import java.util.HashMap;
047import java.util.List;
048import java.util.Map;
049import java.util.Map.Entry;
050
051/**
052 * Utility class to generate the resource icon CSS.<p>
053 *
054 * @since 8.0.0
055 */
056public final class CmsIconUtil implements I_CmsEventListener {
057
058    /**
059     * Inner helper class for building the CSS rules.<p>
060     */
061    static class CssBuilder {
062
063        /** The buffer into which the CSS is written. */
064        private StringBuffer m_buffer = new StringBuffer(1024);
065
066        /**
067         * Builds the CSS for all resource types.<p>
068         *
069         * @return a string containing the CSS rules for all resource types
070         */
071        public String buildResourceIconCss() {
072
073            for (CmsExplorerTypeSettings type : OpenCms.getWorkplaceManager().getExplorerTypeSettings()) {
074                addCssForType(type);
075            }
076            return m_buffer.toString();
077        }
078
079        /**
080         * Writes the CSS for a single icon rule to a buffer.<p>
081         *
082         * @param typeName the name of the resource type
083         * @param rule the icon rule
084         */
085        private void addCssForIconRule(String typeName, CmsIconRule rule) {
086
087            String extension = rule.getExtension();
088            if (rule.getBigIcon() != null) {
089                IconCssRuleBuilder cssBig = new IconCssRuleBuilder();
090                cssBig.addSelectorForSubType(typeName, extension, false);
091                cssBig.setImageUri(getIconUri(rule.getBigIcon()));
092                cssBig.writeCss(m_buffer);
093
094                IconCssRuleBuilder cssSmall = new IconCssRuleBuilder();
095                cssSmall.addSelectorForSubType(typeName, extension, true);
096                cssSmall.setImageUri(getIconUri(rule.getIcon()));
097                cssSmall.writeCss(m_buffer);
098
099            } else {
100                IconCssRuleBuilder css = new IconCssRuleBuilder();
101                css.addSelectorForSubType(typeName, extension, false);
102                css.addSelectorForSubType(typeName, extension, true);
103                css.setImageUri(getIconUri(rule.getIcon()));
104                css.writeCss(m_buffer);
105
106            }
107        }
108
109        /**
110         * Helper method for appending the CSS for a single resource type to a buffer.<p>
111         *
112         * @param explorerType the explorer type for which the CSS should be generated
113         */
114        private void addCssForType(CmsExplorerTypeSettings explorerType) {
115
116            String typeName = explorerType.getName();
117            if (explorerType.getBigIconStyle() == null) {
118                if (explorerType.getBigIcon() != null) {
119                    IconCssRuleBuilder css = new IconCssRuleBuilder();
120                    css.setImageUri(getIconUri(explorerType.getBigIcon()));
121                    css.addSelectorForType(typeName, false);
122                    css.writeCss(m_buffer);
123
124                    IconCssRuleBuilder cssSmall = new IconCssRuleBuilder();
125                    cssSmall.setImageUri(getIconUri(explorerType.getIcon()));
126                    cssSmall.addSelectorForType(typeName, true);
127                    cssSmall.writeCss(m_buffer);
128                } else if (explorerType.getOriginalIcon() != null) {
129                    IconCssRuleBuilder css = new IconCssRuleBuilder();
130                    css.setImageUri(getIconUri(explorerType.getIcon()));
131                    css.addSelectorForType(typeName, true);
132                    css.addSelectorForType(typeName, false);
133                    css.writeCss(m_buffer);
134                }
135            }
136            Map<String, CmsIconRule> iconRules = explorerType.getIconRules();
137            for (Map.Entry<String, CmsIconRule> entry : iconRules.entrySet()) {
138                CmsIconRule rule = entry.getValue();
139                addCssForIconRule(typeName, rule);
140            }
141        }
142
143        /**
144         * Converts an icon file name to a full icon URI.<p>
145         *
146         * @param icon the file name of the icon
147         *
148         * @return the full icon uri
149         */
150        private String getIconUri(String icon) {
151
152            return CmsWorkplace.getResourceUri(CmsWorkplace.RES_PATH_FILETYPES + icon);
153        }
154
155    }
156
157    /**
158     * Helper class for creating the text of the CSS rule for a single icon based on resource type and file suffix.<p>
159     */
160    static class IconCssRuleBuilder {
161
162        /** The uri of the icon image. */
163        private String m_imageUri = "INVALID_ICON";
164
165        /** The list of selector strings. */
166        private List<String> m_selectors = new ArrayList<String>();
167
168        /**
169         * Adds a selector for a resource type and a file suffix.<p>
170         *
171         * @param type the resource type name
172         * @param suffix the file suffix
173         * @param small true if the selector should be for the small icon
174         */
175        public void addSelectorForSubType(String type, String suffix, boolean small) {
176
177            String template = " .%1$s.%2$s.%3$s";
178            String selector = String.format(
179                template,
180                CmsGwtConstants.TYPE_ICON_CLASS,
181                getResourceTypeIconClass(type, small),
182                getResourceSubTypeIconClass(type, suffix, small));
183            m_selectors.add(selector);
184        }
185
186        /**
187         * Adds a selector for a resource type.<p>
188         *
189         * @param type the name of the resource type
190         * @param small true if the selector should be for the small icon
191         */
192        public void addSelectorForType(String type, boolean small) {
193
194            String template = " div.%1$s.%2$s, span.%1$s.%2$s";
195            String selector = String.format(
196                template,
197                CmsGwtConstants.TYPE_ICON_CLASS,
198                getResourceTypeIconClass(type, small));
199            m_selectors.add(selector);
200        }
201
202        /**
203         * Sets the URI of the icon image file.<p>
204         *
205         * @param imageUri the URI of the icon image file
206         */
207        public void setImageUri(String imageUri) {
208
209            m_imageUri = imageUri;
210        }
211
212        /**
213         * Writes the CSS to a string buffer.<p>
214         *
215         * @param buffer the string buffer to which the
216         */
217        public void writeCss(StringBuffer buffer) {
218
219            buffer.append(CmsStringUtil.listAsString(m_selectors, ", "));
220            buffer.append(" { background-image: url(\"");
221            buffer.append(m_imageUri);
222            buffer.append("\");} ");
223        }
224    }
225
226    /** Pseudo type icon. */
227    public static final String ICON_MODEL_GROUP_BIG = CmsGwtConstants.TYPE_ICON_CLASS + " oc-icon-24-modelgroup_copy";
228
229    /** Pseudo type icon. */
230    public static final String ICON_MODEL_GROUP_COPY_BIG = CmsGwtConstants.TYPE_ICON_CLASS
231        + " "
232        + CmsExplorerTypeSettings.ICON_STYLE_MODEL_GROUP_COPY_BIG;
233
234    /** Pseudo type icon. */
235    public static final String ICON_MODEL_GROUP_COPY_SMALL = CmsGwtConstants.TYPE_ICON_CLASS
236        + " "
237        + CmsExplorerTypeSettings.ICON_STYLE_MODEL_GROUP_COPY_SMALL;
238
239    /** Pseudo type icon. */
240    public static final String ICON_NAV_LEVEL_BIG = CmsGwtConstants.TYPE_ICON_CLASS
241        + " "
242        + CmsExplorerTypeSettings.ICON_STYLE_NAV_LEVEL_BIG;
243
244    /** Pseudo type icon. */
245    public static final String ICON_NAV_LEVEL_SMALL = CmsGwtConstants.TYPE_ICON_CLASS
246        + " "
247        + CmsExplorerTypeSettings.ICON_STYLE_NAV_LEVEL_SMALL;
248
249    /** The big resource not found icon name. */
250    public static final String NOT_FOUND_ICON_BIG = CmsGwtConstants.TYPE_ICON_CLASS + " oc-icon-24-warning";
251
252    /** The small resource not found icon name. */
253    public static final String NOT_FOUND_ICON_SMALL = CmsGwtConstants.TYPE_ICON_CLASS + " oc-icon-16-warning";
254
255    /** The suffix for the CSS classes for small icons. */
256    public static final String SMALL_SUFFIX = "_small";
257
258    /** Type for resource not found. */
259    public static final String TYPE_RESOURCE_NOT_FOUND = "cms_resource_not_found";
260
261    /** The cached CSS. */
262    private static String m_cachedCss;
263
264    /** The extension icon mapping. */
265    private static Map<String, String> m_extensionIconMapping;
266
267    /** Flag indicating the 'clear caches' event listener has been registered. */
268    private static boolean m_listenerRegistered;
269
270    /**
271     * Constructor.<p>
272     */
273    private CmsIconUtil() {
274
275    }
276
277    /**
278     * Builds the CSS for all resource types.<p>
279     *
280     * @return a string containing the CSS rules for all resource types
281     */
282    public static String buildResourceIconCss() {
283
284        if (!m_listenerRegistered) {
285            registerListener();
286        }
287        if (m_cachedCss == null) {
288            rebuildCss();
289        }
290        return m_cachedCss;
291    }
292
293    /**
294     * Returns the resource type name used to display the resource icon.
295     * This may differ from the actual resource type in case of navigation level folders and model groups.<p>
296     *
297     * @param cms the cms context
298     * @param resource the resource
299     *
300     * @return the display type name
301     */
302    public static String getDisplayType(CmsObject cms, CmsResource resource) {
303
304        String result;
305        if (CmsJspNavBuilder.isNavLevelFolder(cms, resource)) {
306            result = CmsGwtConstants.TYPE_NAVLEVEL;
307        } else if (CmsResourceTypeXmlContainerPage.isModelCopyGroup(cms, resource)) {
308            result = CmsGwtConstants.TYPE_MODELGROUP_COPY;
309        } else {
310            result = OpenCms.getResourceManager().getResourceType(resource).getTypeName();
311        }
312        return result;
313    }
314
315    /**
316     * Returns the extension icon mapping used when uploading files.<p>
317     *
318     * @return the extension icon mapping
319     */
320    public static Map<String, String> getExtensionIconMapping() {
321
322        if (m_extensionIconMapping == null) {
323            m_extensionIconMapping = new HashMap<String, String>();
324            for (Entry<String, String> entry : OpenCms.getResourceManager().getExtensionMapping().entrySet()) {
325                m_extensionIconMapping.put(
326                    entry.getKey(),
327                    getIconClasses(entry.getValue(), "_." + entry.getKey(), false));
328            }
329            m_extensionIconMapping.put("", getIconClasses("plain", null, false));
330        }
331        // returning a copy of the icon map, as GWT will not work with unmodifiable maps
332        return new HashMap<String, String>(m_extensionIconMapping);
333    }
334
335    /**
336     * Returns the resource type icon CSS classes for the given type.<p>
337     * Use within ADE context only.<p>
338     *
339     * @param typeSettings the explorer type settings
340     * @param resourceName the resource name
341     * @param small <code>true</code> to get the small icon classes
342     *
343     * @return the icon CSS classes
344     */
345    public static String getIconClasses(CmsExplorerTypeSettings typeSettings, String resourceName, boolean small) {
346
347        String result = null;
348        if (typeSettings == null) {
349            typeSettings = OpenCms.getWorkplaceManager().getExplorerTypeSetting(
350                (resourceName != null) && CmsResource.isFolder(resourceName)
351                ? CmsResourceTypeUnknownFile.RESOURCE_TYPE_NAME
352                : CmsResourceTypeUnknownFolder.RESOURCE_TYPE_NAME);
353        }
354        if (!typeSettings.getIconRules().isEmpty() && (resourceName != null)) {
355            String extension = CmsResource.getExtension(resourceName);
356            if (extension != null) {
357                // check for a matching sub type icon rule
358                CmsIconRule rule = typeSettings.getIconRules().get(extension);
359                if (rule != null) {
360                    result = small ? rule.getSmallIconStyle() : rule.getBigIconStyle();
361                }
362            }
363        }
364        if (result == null) {
365            if (small && (typeSettings.getSmallIconStyle() != null)) {
366                result = typeSettings.getSmallIconStyle();
367            } else if (small && (typeSettings.getIcon() == null)) {
368                result = CmsExplorerTypeSettings.ICON_STYLE_DEFAULT_SMALL;
369            } else if (!small && (typeSettings.getBigIconStyle() != null)) {
370                result = typeSettings.getBigIconStyle();
371            } else if (!small && (typeSettings.getBigIcon() == null)) {
372                result = CmsExplorerTypeSettings.ICON_STYLE_DEFAULT_BIG;
373            }
374
375            if (result != null) {
376                result = CmsGwtConstants.TYPE_ICON_CLASS + " " + result;
377            } else {
378                result = getResourceIconClasses(typeSettings.getName(), resourceName, small);
379            }
380        }
381        return result;
382    }
383
384    /**
385     * Returns the resource type icon CSS classes for the given type.<p>
386     * Use within ADE context only.<p>
387     *
388     * @param resourceType the resource type name
389     * @param resourceName the resource name
390     * @param small <code>true</code> to get the small icon classes
391     *
392     * @return the icon CSS classes
393     */
394    public static String getIconClasses(String resourceType, String resourceName, boolean small) {
395
396        String result;
397        if (resourceType.equals(CmsGwtConstants.TYPE_NAVLEVEL)) {
398            if (small) {
399                result = ICON_NAV_LEVEL_SMALL;
400            } else {
401                result = ICON_NAV_LEVEL_BIG;
402            }
403        } else if (resourceType.equals(CmsGwtConstants.TYPE_MODELGROUP_COPY)) {
404            if (small) {
405                result = ICON_MODEL_GROUP_COPY_SMALL;
406            } else {
407                result = ICON_MODEL_GROUP_COPY_BIG;
408            }
409        } else if (resourceType.equals(TYPE_RESOURCE_NOT_FOUND)) {
410            if (small) {
411                result = NOT_FOUND_ICON_SMALL;
412            } else {
413                result = NOT_FOUND_ICON_BIG;
414            }
415        } else {
416            result = getIconClasses(
417                OpenCms.getWorkplaceManager().getExplorerTypeSetting(resourceType),
418                resourceName,
419                small);
420        }
421        return result;
422    }
423
424    /**
425     * Returns the CSS class for a given resource type name and file name extension.<p>
426     *
427     * @param resourceTypeName the resource type name
428     * @param suffix the file name extension
429     * @param small if true, get the icon class for the small icon, else for the biggest one available
430     *
431     * @return the CSS class for the type and extension
432     */
433    static String getResourceSubTypeIconClass(String resourceTypeName, String suffix, boolean small) {
434
435        StringBuffer buffer = new StringBuffer(CmsGwtConstants.TYPE_ICON_CLASS).append("_").append(
436            resourceTypeName.hashCode()).append("_").append(suffix);
437        if (small) {
438            buffer.append(SMALL_SUFFIX);
439        }
440        return buffer.toString();
441    }
442
443    /**
444     * Returns the CSS class for the given resource type.<p>
445     *
446     * @param resourceTypeName the resource type name
447     * @param small if true, get the icon class for the small icon, else for the biggest one available
448     *
449     * @return the CSS class
450     */
451    static String getResourceTypeIconClass(String resourceTypeName, boolean small) {
452
453        StringBuffer sb = new StringBuffer(CmsGwtConstants.TYPE_ICON_CLASS);
454        sb.append("_").append(resourceTypeName.hashCode());
455        if (small) {
456            sb.append(SMALL_SUFFIX);
457        }
458        return sb.toString();
459    }
460
461    /**
462     * Returns the CSS class for the given filename.<p>
463     *
464     * @param resourceTypeName the resource type name
465     * @param fileName the filename
466     * @param small if true, get the CSS class for the small icon, else for the biggest one available
467     *
468     * @return the CSS class
469     */
470    private static String getFileTypeIconClass(String resourceTypeName, String fileName, boolean small) {
471
472        if ((fileName != null) && fileName.contains(".")) {
473            int last = fileName.lastIndexOf(".");
474            if (fileName.length() > (last + 1)) {
475                String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
476                return getResourceSubTypeIconClass(resourceTypeName, suffix, small);
477            }
478        }
479        return "";
480
481    }
482
483    /**
484     * Returns the CSS classes of the resource icon for the given resource type and filename.<p>
485     *
486     * Use this the resource type and filename is known.<p>
487     *
488     * @param resourceTypeName the resource type name
489     * @param fileName the filename
490     * @param small if true, get the icon classes for the small icon, else for the biggest one available
491     *
492     * @return the CSS classes
493     */
494    private static String getResourceIconClasses(String resourceTypeName, String fileName, boolean small) {
495
496        StringBuffer sb = new StringBuffer(CmsGwtConstants.TYPE_ICON_CLASS);
497        sb.append(" ").append(getResourceTypeIconClass(resourceTypeName, small)).append(" ").append(
498            getFileTypeIconClass(resourceTypeName, fileName, small));
499        return sb.toString();
500    }
501
502    /**
503     * Rebuilds the icon CSS.<p>
504     */
505    private static synchronized void rebuildCss() {
506
507        if (m_cachedCss == null) {
508            CssBuilder builder = new CssBuilder();
509            m_cachedCss = builder.buildResourceIconCss();
510        }
511    }
512
513    /**
514     * Registers the 'clear caches' event listener.<p>
515     */
516    private static synchronized void registerListener() {
517
518        if (!m_listenerRegistered) {
519            OpenCms.getEventManager().addCmsEventListener(
520                new CmsIconUtil(),
521                new int[] {I_CmsEventListener.EVENT_CLEAR_CACHES});
522            m_listenerRegistered = true;
523        }
524    }
525
526    /**
527     * @see org.opencms.main.I_CmsEventListener#cmsEvent(org.opencms.main.CmsEvent)
528     */
529    public void cmsEvent(CmsEvent event) {
530
531        m_cachedCss = null;
532    }
533}