1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.service.impl;
24  
25  import com.liferay.portal.SystemException;
26  import com.liferay.portal.kernel.image.SpriteProcessorUtil;
27  import com.liferay.portal.kernel.log.Log;
28  import com.liferay.portal.kernel.log.LogFactoryUtil;
29  import com.liferay.portal.kernel.plugin.PluginPackage;
30  import com.liferay.portal.kernel.servlet.ServletContextUtil;
31  import com.liferay.portal.kernel.util.GetterUtil;
32  import com.liferay.portal.kernel.util.ListUtil;
33  import com.liferay.portal.kernel.util.ReleaseInfo;
34  import com.liferay.portal.kernel.util.ServerDetector;
35  import com.liferay.portal.kernel.util.StringPool;
36  import com.liferay.portal.kernel.util.StringUtil;
37  import com.liferay.portal.kernel.util.Validator;
38  import com.liferay.portal.kernel.xml.Document;
39  import com.liferay.portal.kernel.xml.Element;
40  import com.liferay.portal.kernel.xml.SAXReaderUtil;
41  import com.liferay.portal.model.ColorScheme;
42  import com.liferay.portal.model.PluginSetting;
43  import com.liferay.portal.model.PortletConstants;
44  import com.liferay.portal.model.Theme;
45  import com.liferay.portal.model.impl.ColorSchemeImpl;
46  import com.liferay.portal.model.impl.ThemeImpl;
47  import com.liferay.portal.plugin.PluginUtil;
48  import com.liferay.portal.service.base.ThemeLocalServiceBaseImpl;
49  import com.liferay.portal.theme.ThemeCompanyId;
50  import com.liferay.portal.theme.ThemeCompanyLimit;
51  import com.liferay.portal.theme.ThemeGroupId;
52  import com.liferay.portal.theme.ThemeGroupLimit;
53  import com.liferay.portal.util.PortalUtil;
54  import com.liferay.util.ContextReplace;
55  import com.liferay.util.Version;
56  
57  import java.io.File;
58  
59  import java.util.ArrayList;
60  import java.util.HashSet;
61  import java.util.Iterator;
62  import java.util.List;
63  import java.util.Map;
64  import java.util.Properties;
65  import java.util.Set;
66  import java.util.concurrent.ConcurrentHashMap;
67  
68  import javax.servlet.ServletContext;
69  
70  /**
71   * <a href="ThemeLocalServiceImpl.java.html"><b><i>View Source</i></b></a>
72   *
73   * @author Brian Wing Shun Chan
74   * @author Jorge Ferrer
75   *
76   */
77  public class ThemeLocalServiceImpl extends ThemeLocalServiceBaseImpl {
78  
79      public ColorScheme getColorScheme(
80          long companyId, String themeId, String colorSchemeId,
81          boolean wapTheme) {
82  
83          colorSchemeId = GetterUtil.getString(colorSchemeId);
84  
85          Theme theme = getTheme(companyId, themeId, wapTheme);
86  
87          Map<String, ColorScheme> colorSchemesMap = theme.getColorSchemesMap();
88  
89          ColorScheme colorScheme = colorSchemesMap.get(colorSchemeId);
90  
91          if (colorScheme == null) {
92              List<ColorScheme> colorSchemes = theme.getColorSchemes();
93  
94              if (colorSchemes.size() > 0) {
95                  for (int i = (colorSchemes.size() - 1); i >= 0; i--) {
96                      colorScheme = colorSchemes.get(i);
97  
98                      if (colorScheme.isDefaultCs()) {
99                          break;
100                     }
101                 }
102             }
103         }
104 
105         if (colorScheme == null) {
106             if (wapTheme) {
107                 colorSchemeId = ColorSchemeImpl.getDefaultWapColorSchemeId();
108             }
109             else {
110                 colorSchemeId =
111                     ColorSchemeImpl.getDefaultRegularColorSchemeId();
112             }
113         }
114 
115         if (colorScheme == null) {
116             colorScheme = ColorSchemeImpl.getNullColorScheme();
117         }
118 
119         return colorScheme;
120     }
121 
122     public Theme getTheme(long companyId, String themeId, boolean wapTheme) {
123         themeId = GetterUtil.getString(themeId);
124 
125         Theme theme = _getThemes(companyId).get(themeId);
126 
127         if (theme == null) {
128             if (_log.isWarnEnabled()) {
129                 _log.warn(
130                     "No theme found for specified theme id " + themeId +
131                         ". Returning the default theme.");
132             }
133 
134             if (wapTheme) {
135                 themeId = ThemeImpl.getDefaultWapThemeId();
136             }
137             else {
138                 themeId = ThemeImpl.getDefaultRegularThemeId();
139             }
140 
141             theme = _themes.get(themeId);
142         }
143 
144         if (theme == null) {
145             if (_themes.isEmpty()) {
146                 if (_log.isDebugEnabled()) {
147                     _log.debug("No themes are installed");
148                 }
149 
150                 return null;
151             }
152 
153             _log.error(
154                 "No theme found for default theme id " + themeId +
155                     ". Returning a random theme.");
156 
157             Iterator<Map.Entry<String, Theme>> itr =
158                 _themes.entrySet().iterator();
159 
160             while (itr.hasNext()) {
161                 Map.Entry<String, Theme> entry = itr.next();
162 
163                 theme = entry.getValue();
164             }
165         }
166 
167         return theme;
168     }
169 
170     public List<Theme> getThemes(long companyId) {
171         List<Theme> themes = ListUtil.fromCollection(
172             _getThemes(companyId).values());
173 
174         return ListUtil.sort(themes);
175     }
176 
177     public List<Theme> getThemes(
178             long companyId, long groupId, long userId, boolean wapTheme)
179         throws SystemException {
180 
181         List<Theme> themes = getThemes(companyId);
182 
183         themes = PluginUtil.restrictPlugins(themes, companyId, userId);
184 
185         Iterator<Theme> itr = themes.iterator();
186 
187         while (itr.hasNext()) {
188             Theme theme = itr.next();
189 
190             if ((!theme.isGroupAvailable(groupId)) ||
191                 (theme.isWapTheme() != wapTheme)) {
192 
193                 itr.remove();
194             }
195         }
196 
197         return themes;
198     }
199 
200     public List<Theme> getWARThemes() {
201         List<Theme> themes = ListUtil.fromCollection(_themes.values());
202 
203         Iterator<Theme> itr = themes.iterator();
204 
205         while (itr.hasNext()) {
206             Theme theme = itr.next();
207 
208             if (!theme.isWARFile()) {
209                 itr.remove();
210             }
211         }
212 
213         return themes;
214     }
215 
216     public List<String> init(
217         ServletContext servletContext, String themesPath,
218         boolean loadFromServletContext, String[] xmls,
219         PluginPackage pluginPackage) {
220 
221         return init(
222             null, servletContext, themesPath, loadFromServletContext, xmls,
223             pluginPackage);
224     }
225 
226     public List<String> init(
227         String servletContextName, ServletContext servletContext,
228         String themesPath, boolean loadFromServletContext, String[] xmls,
229         PluginPackage pluginPackage) {
230 
231         List<String> themeIds = new ArrayList<String>();
232 
233         try {
234             for (int i = 0; i < xmls.length; i++) {
235                 Set<String> themes = _readThemes(
236                     servletContextName, servletContext, themesPath,
237                     loadFromServletContext, xmls[i], pluginPackage);
238 
239                 Iterator<String> itr = themes.iterator();
240 
241                 while (itr.hasNext()) {
242                     String themeId = itr.next();
243 
244                     if (!themeIds.contains(themeId)) {
245                         themeIds.add(themeId);
246                     }
247                 }
248             }
249         }
250         catch (Exception e) {
251             e.printStackTrace();
252         }
253 
254         _themesPool.clear();
255 
256         return themeIds;
257     }
258 
259     public void uninstallThemes(List<String> themeIds) {
260         for (int i = 0; i < themeIds.size(); i++) {
261             String themeId = themeIds.get(i);
262 
263             _themes.remove(themeId);
264 
265             layoutTemplateLocalService.uninstallLayoutTemplates(themeId);
266         }
267 
268         _themesPool.clear();
269     }
270 
271     private List<ThemeCompanyId> _getCompanyLimitExcludes(Element el) {
272         List<ThemeCompanyId> includes = new ArrayList<ThemeCompanyId>();
273 
274         if (el != null) {
275             List<Element> companyIds = el.elements("company-id");
276 
277             for (int i = 0; i < companyIds.size(); i++) {
278                 Element companyIdEl = companyIds.get(i);
279 
280                 String name = companyIdEl.attributeValue("name");
281                 String pattern = companyIdEl.attributeValue("pattern");
282 
283                 ThemeCompanyId themeCompanyId = null;
284 
285                 if (Validator.isNotNull(name)) {
286                     themeCompanyId = new ThemeCompanyId(name, false);
287                 }
288                 else if (Validator.isNotNull(pattern)) {
289                     themeCompanyId = new ThemeCompanyId(pattern, true);
290                 }
291 
292                 if (themeCompanyId != null) {
293                     includes.add(themeCompanyId);
294                 }
295             }
296         }
297 
298         return includes;
299     }
300 
301     private List<ThemeCompanyId> _getCompanyLimitIncludes(Element el) {
302         return _getCompanyLimitExcludes(el);
303     }
304 
305     private List<ThemeGroupId> _getGroupLimitExcludes(Element el) {
306         List<ThemeGroupId> includes = new ArrayList<ThemeGroupId>();
307 
308         if (el != null) {
309             List<Element> groupIds = el.elements("group-id");
310 
311             for (int i = 0; i < groupIds.size(); i++) {
312                 Element groupIdEl = groupIds.get(i);
313 
314                 String name = groupIdEl.attributeValue("name");
315                 String pattern = groupIdEl.attributeValue("pattern");
316 
317                 ThemeGroupId themeGroupId = null;
318 
319                 if (Validator.isNotNull(name)) {
320                     themeGroupId = new ThemeGroupId(name, false);
321                 }
322                 else if (Validator.isNotNull(pattern)) {
323                     themeGroupId = new ThemeGroupId(pattern, true);
324                 }
325 
326                 if (themeGroupId != null) {
327                     includes.add(themeGroupId);
328                 }
329             }
330         }
331 
332         return includes;
333     }
334 
335     private List<ThemeGroupId> _getGroupLimitIncludes(Element el) {
336         return _getGroupLimitExcludes(el);
337     }
338 
339     private Map<String, Theme> _getThemes(long companyId) {
340         Map<String, Theme> themes = _themesPool.get(companyId);
341 
342         if (themes == null) {
343             themes = new ConcurrentHashMap<String, Theme>();
344 
345             Iterator<Map.Entry<String, Theme>> itr =
346                 _themes.entrySet().iterator();
347 
348             while (itr.hasNext()) {
349                 Map.Entry<String, Theme> entry = itr.next();
350 
351                 String themeId = entry.getKey();
352                 Theme theme = entry.getValue();
353 
354                 if (theme.isCompanyAvailable(companyId)) {
355                     themes.put(themeId, theme);
356                 }
357             }
358 
359             _themesPool.put(companyId, themes);
360         }
361 
362         return themes;
363     }
364 
365     private Version _getVersion(String version) {
366         if (version.equals("${current-version}")) {
367             version = ReleaseInfo.getVersion();
368         }
369 
370         return Version.getInstance(version);
371     }
372 
373     private void _readColorSchemes(
374         Element theme, Map<String, ColorScheme> colorSchemes,
375         ContextReplace themeContextReplace) {
376 
377         Iterator<Element> itr = theme.elements("color-scheme").iterator();
378 
379         while (itr.hasNext()) {
380             Element colorScheme = itr.next();
381 
382             ContextReplace colorSchemeContextReplace =
383                 (ContextReplace)themeContextReplace.clone();
384 
385             String id = colorScheme.attributeValue("id");
386 
387             colorSchemeContextReplace.addValue("color-scheme-id", id);
388 
389             ColorScheme colorSchemeModel = colorSchemes.get(id);
390 
391             if (colorSchemeModel == null) {
392                 colorSchemeModel = new ColorSchemeImpl(id);
393             }
394 
395             String name = GetterUtil.getString(
396                 colorScheme.attributeValue("name"), colorSchemeModel.getName());
397 
398             name = colorSchemeContextReplace.replace(name);
399 
400             boolean defaultCs = GetterUtil.getBoolean(
401                 colorScheme.elementText("default-cs"),
402                 colorSchemeModel.isDefaultCs());
403 
404             String cssClass = GetterUtil.getString(
405                 colorScheme.elementText("css-class"),
406                 colorSchemeModel.getCssClass());
407 
408             cssClass = colorSchemeContextReplace.replace(cssClass);
409 
410             colorSchemeContextReplace.addValue("css-class", cssClass);
411 
412             String colorSchemeImagesPath = GetterUtil.getString(
413                 colorScheme.elementText("color-scheme-images-path"),
414                 colorSchemeModel.getColorSchemeImagesPath());
415 
416             colorSchemeImagesPath = colorSchemeContextReplace.replace(
417                 colorSchemeImagesPath);
418 
419             colorSchemeContextReplace.addValue(
420                 "color-scheme-images-path", colorSchemeImagesPath);
421 
422             colorSchemeModel.setName(name);
423             colorSchemeModel.setDefaultCs(defaultCs);
424             colorSchemeModel.setCssClass(cssClass);
425             colorSchemeModel.setColorSchemeImagesPath(colorSchemeImagesPath);
426 
427             colorSchemes.put(id, colorSchemeModel);
428         }
429     }
430 
431     private Set<String> _readThemes(
432             String servletContextName, ServletContext servletContext,
433             String themesPath, boolean loadFromServletContext, String xml,
434             PluginPackage pluginPackage)
435         throws Exception {
436 
437         Set<String> themeIds = new HashSet<String>();
438 
439         if (xml == null) {
440             return themeIds;
441         }
442 
443         Document doc = SAXReaderUtil.read(xml, true);
444 
445         Element root = doc.getRootElement();
446 
447         Version portalVersion = _getVersion(ReleaseInfo.getVersion());
448 
449         boolean compatible = false;
450 
451         Element compatibilityEl = root.element("compatibility");
452 
453         if (compatibilityEl != null) {
454             Iterator<Element> itr = compatibilityEl.elements(
455                 "version").iterator();
456 
457             while (itr.hasNext()) {
458                 Element versionEl = itr.next();
459 
460                 Version version = _getVersion(versionEl.getTextTrim());
461 
462                 if (version.includes(portalVersion)) {
463                     compatible = true;
464 
465                     break;
466                 }
467             }
468         }
469 
470         if (!compatible) {
471             _log.error(
472                 "Themes in this WAR are not compatible with " +
473                     ReleaseInfo.getServerInfo());
474 
475             return themeIds;
476         }
477 
478         ThemeCompanyLimit companyLimit = null;
479 
480         Element companyLimitEl = root.element("company-limit");
481 
482         if (companyLimitEl != null) {
483             companyLimit = new ThemeCompanyLimit();
484 
485             Element companyIncludesEl =
486                 companyLimitEl.element("company-includes");
487 
488             if (companyIncludesEl != null) {
489                 companyLimit.setIncludes(
490                     _getCompanyLimitIncludes(companyIncludesEl));
491             }
492 
493             Element companyExcludesEl =
494                 companyLimitEl.element("company-excludes");
495 
496             if (companyExcludesEl != null) {
497                 companyLimit.setExcludes(
498                     _getCompanyLimitExcludes(companyExcludesEl));
499             }
500         }
501 
502         ThemeGroupLimit groupLimit = null;
503 
504         Element groupLimitEl = root.element("group-limit");
505 
506         if (groupLimitEl != null) {
507             groupLimit = new ThemeGroupLimit();
508 
509             Element groupIncludesEl = groupLimitEl.element("group-includes");
510 
511             if (groupIncludesEl != null) {
512                 groupLimit.setIncludes(_getGroupLimitIncludes(groupIncludesEl));
513             }
514 
515             Element groupExcludesEl =
516                 groupLimitEl.element("group-excludes");
517 
518             if (groupExcludesEl != null) {
519                 groupLimit.setExcludes(_getGroupLimitExcludes(groupExcludesEl));
520             }
521         }
522 
523         long timestamp = ServletContextUtil.getLastModified(servletContext);
524 
525         Iterator<Element> itr1 = root.elements("theme").iterator();
526 
527         while (itr1.hasNext()) {
528             Element theme = itr1.next();
529 
530             ContextReplace themeContextReplace = new ContextReplace();
531 
532             themeContextReplace.addValue("themes-path", themesPath);
533 
534             String themeId = theme.attributeValue("id");
535 
536             if (servletContextName != null) {
537                 themeId =
538                     themeId + PortletConstants.WAR_SEPARATOR +
539                         servletContextName;
540             }
541 
542             themeId = PortalUtil.getJsSafePortletId(themeId);
543 
544             themeContextReplace.addValue("theme-id", themeId);
545 
546             themeIds.add(themeId);
547 
548             Theme themeModel = _themes.get(themeId);
549 
550             if (themeModel == null) {
551                 themeModel = new ThemeImpl(themeId);
552 
553                 _themes.put(themeId, themeModel);
554             }
555 
556             themeModel.setTimestamp(timestamp);
557 
558             PluginSetting pluginSetting =
559                 pluginSettingLocalService.getDefaultPluginSetting();
560 
561             themeModel.setPluginPackage(pluginPackage);
562             themeModel.setDefaultPluginSetting(pluginSetting);
563 
564             themeModel.setThemeCompanyLimit(companyLimit);
565             themeModel.setThemeGroupLimit(groupLimit);
566 
567             if (servletContextName != null) {
568                 themeModel.setServletContextName(servletContextName);
569             }
570 
571             themeModel.setLoadFromServletContext(loadFromServletContext);
572 
573             String name = GetterUtil.getString(
574                 theme.attributeValue("name"), themeModel.getName());
575 
576             String rootPath = GetterUtil.getString(
577                 theme.elementText("root-path"), themeModel.getRootPath());
578 
579             rootPath = themeContextReplace.replace(rootPath);
580 
581             themeContextReplace.addValue("root-path", rootPath);
582 
583             String templatesPath = GetterUtil.getString(
584                 theme.elementText("templates-path"),
585                 themeModel.getTemplatesPath());
586 
587             templatesPath = themeContextReplace.replace(templatesPath);
588             templatesPath = StringUtil.safePath(templatesPath);
589 
590             themeContextReplace.addValue("templates-path", templatesPath);
591 
592             String cssPath = GetterUtil.getString(
593                 theme.elementText("css-path"), themeModel.getCssPath());
594 
595             cssPath = themeContextReplace.replace(cssPath);
596             cssPath = StringUtil.safePath(cssPath);
597 
598             themeContextReplace.addValue("css-path", cssPath);
599 
600             String imagesPath = GetterUtil.getString(
601                 theme.elementText("images-path"),
602                 themeModel.getImagesPath());
603 
604             imagesPath = themeContextReplace.replace(imagesPath);
605             imagesPath = StringUtil.safePath(imagesPath);
606 
607             themeContextReplace.addValue("images-path", imagesPath);
608 
609             String javaScriptPath = GetterUtil.getString(
610                 theme.elementText("javascript-path"),
611                 themeModel.getJavaScriptPath());
612 
613             javaScriptPath = themeContextReplace.replace(javaScriptPath);
614             javaScriptPath = StringUtil.safePath(javaScriptPath);
615 
616             themeContextReplace.addValue("javascript-path", javaScriptPath);
617 
618             String virtualPath = GetterUtil.getString(
619                 theme.elementText("virtual-path"), themeModel.getVirtualPath());
620 
621             String templateExtension = GetterUtil.getString(
622                 theme.elementText("template-extension"),
623                 themeModel.getTemplateExtension());
624 
625             themeModel.setName(name);
626             themeModel.setRootPath(rootPath);
627             themeModel.setTemplatesPath(templatesPath);
628             themeModel.setCssPath(cssPath);
629             themeModel.setImagesPath(imagesPath);
630             themeModel.setJavaScriptPath(javaScriptPath);
631             themeModel.setVirtualPath(virtualPath);
632             themeModel.setTemplateExtension(templateExtension);
633 
634             Element settingsEl = theme.element("settings");
635 
636             if (settingsEl != null) {
637                 Iterator<Element> itr2 = settingsEl.elements(
638                     "setting").iterator();
639 
640                 while (itr2.hasNext()) {
641                     Element settingEl = itr2.next();
642 
643                     String key = settingEl.attributeValue("key");
644                     String value = settingEl.attributeValue("value");
645 
646                     themeModel.setSetting(key, value);
647                 }
648             }
649 
650             themeModel.setWapTheme(GetterUtil.getBoolean(
651                 theme.elementText("wap-theme"), themeModel.isWapTheme()));
652 
653             Element rolesEl = theme.element("roles");
654 
655             if (rolesEl != null) {
656                 Iterator<Element> itr2 = rolesEl.elements(
657                     "role-name").iterator();
658 
659                 while (itr2.hasNext()) {
660                     Element roleNameEl = itr2.next();
661 
662                     pluginSetting.addRole(roleNameEl.getText());
663                 }
664             }
665 
666             _readColorSchemes(
667                 theme, themeModel.getColorSchemesMap(), themeContextReplace);
668             _readColorSchemes(
669                 theme, themeModel.getColorSchemesMap(), themeContextReplace);
670 
671             Element layoutTemplatesEl = theme.element("layout-templates");
672 
673             if (layoutTemplatesEl != null) {
674                 Element standardEl = layoutTemplatesEl.element("standard");
675 
676                 if (standardEl != null) {
677                     layoutTemplateLocalService.readLayoutTemplate(
678                         servletContextName, servletContext, null,
679                         standardEl, true, themeId, pluginPackage);
680                 }
681 
682                 Element customEl = layoutTemplatesEl.element("custom");
683 
684                 if (customEl != null) {
685                     layoutTemplateLocalService.readLayoutTemplate(
686                         servletContextName, servletContext, null,
687                         customEl, false, themeId, pluginPackage);
688                 }
689             }
690 
691             if (!themeModel.isWapTheme()) {
692                 _setSpriteImages(servletContext, themeModel, imagesPath);
693             }
694         }
695 
696         return themeIds;
697     }
698 
699     private void _setSpriteImages(
700             ServletContext servletContext, Theme theme, String resourcePath)
701         throws Exception {
702 
703         Set<String> resourcePaths = servletContext.getResourcePaths(
704             resourcePath);
705 
706         if (resourcePaths == null) {
707             return;
708         }
709 
710         List<File> images = new ArrayList<File>(resourcePaths.size());
711 
712         for (String curResourcePath : resourcePaths) {
713             if (curResourcePath.endsWith(StringPool.SLASH)) {
714                 _setSpriteImages(servletContext, theme, curResourcePath);
715             }
716             else if (curResourcePath.endsWith(".png")) {
717                 String realPath = ServletContextUtil.getRealPath(
718                     servletContext, curResourcePath);
719 
720                 if (realPath != null) {
721                     images.add(new File(realPath));
722                 }
723                 else {
724                     if (ServerDetector.isTomcat()) {
725                         if (_log.isInfoEnabled()) {
726                             _log.info(ServletContextUtil.LOG_INFO_SPRITES);
727                         }
728                     }
729                     else {
730                         _log.error(
731                             "Real path for " + curResourcePath + " is null");
732                     }
733                 }
734             }
735         }
736 
737         String spriteFileName = ".sprite.png";
738         String spritePropertiesFileName = ".sprite.properties";
739         String spritePropertiesRootPath = ServletContextUtil.getRealPath(
740             servletContext, theme.getImagesPath());
741 
742         Properties spriteProperties = SpriteProcessorUtil.generate(
743             images, spriteFileName, spritePropertiesFileName,
744             spritePropertiesRootPath, 16, 16, 10240);
745 
746         if (spriteProperties == null) {
747             return;
748         }
749 
750         spriteFileName =
751             resourcePath.substring(
752                 theme.getImagesPath().length(), resourcePath.length()) +
753             spriteFileName;
754 
755         theme.setSpriteImages(spriteFileName, spriteProperties);
756     }
757 
758     private static Log _log =
759          LogFactoryUtil.getLog(ThemeLocalServiceImpl.class);
760 
761     private static Map<String, Theme> _themes =
762         new ConcurrentHashMap<String, Theme>();
763     private static Map<Long, Map<String, Theme>> _themesPool =
764         new ConcurrentHashMap<Long, Map<String, Theme>>();
765 
766 }