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