001 package org.apache.myfaces.tobago.context;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one or more
005 * contributor license agreements. See the NOTICE file distributed with
006 * this work for additional information regarding copyright ownership.
007 * The ASF licenses this file to You under the Apache License, Version 2.0
008 * (the "License"); you may not use this file except in compliance with
009 * the License. You may obtain a copy of the License at
010 *
011 * http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019
020 import org.apache.commons.logging.Log;
021 import org.apache.commons.logging.LogFactory;
022 import static org.apache.myfaces.tobago.TobagoConstants.RENDERER_TYPE_OUT;
023 import org.apache.myfaces.tobago.config.TobagoConfig;
024
025 import javax.faces.component.UIViewRoot;
026 import javax.faces.render.Renderer;
027 import java.util.ArrayList;
028 import java.util.HashMap;
029 import java.util.List;
030 import java.util.Locale;
031 import java.util.StringTokenizer;
032
033 // FIXME: is this class thread-safe?
034
035 public class ResourceManagerImpl implements ResourceManager {
036
037 private static final Log LOG = LogFactory.getLog(ResourceManagerImpl.class);
038 private static final String PROPERTY = "property";
039 private static final String JSP = "jsp";
040 private static final String TAG = "tag";
041
042 private final HashMap<String, String> resourceList;
043
044 private final HashMap<RendererCacheKey, Renderer> rendererCache = new HashMap<RendererCacheKey, Renderer>();
045 private final HashMap<ImageCacheKey, String> imageCache = new HashMap<ImageCacheKey, String>();
046 private final HashMap<JspCacheKey, String> jspCache = new HashMap<JspCacheKey, String>();
047 private final HashMap<MiscCacheKey, String[]> miscCache = new HashMap<MiscCacheKey, String[]>();
048 private final HashMap<PropertyCacheKey, String> propertyCache = new HashMap<PropertyCacheKey, String>();
049
050 private TobagoConfig tobagoConfig;
051
052 public ResourceManagerImpl(TobagoConfig tobagoConfig) {
053 resourceList = new HashMap<String, String>();
054 this.tobagoConfig = tobagoConfig;
055 }
056
057 public void add(String resourceKey) {
058 if (LOG.isDebugEnabled()) {
059 LOG.debug("adding resourceKey = '" + resourceKey + "'");
060 }
061 resourceList.put(resourceKey, "");
062 }
063
064 public void add(String resourceKey, String value) {
065 if (LOG.isDebugEnabled()) {
066 LOG.debug(
067 "adding resourceKey = '" + resourceKey + "' value='" + value + "'");
068 }
069 resourceList.put(resourceKey, value);
070 }
071
072
073 public String getImage(UIViewRoot viewRoot, String name) {
074 return getImage(viewRoot, name, false);
075 }
076
077 public String getImage(UIViewRoot viewRoot, String name,
078 boolean ignoreMissing) {
079 String result = null;
080 if (name != null) {
081 int dot = name.lastIndexOf('.');
082 if (dot == -1) {
083 dot = name.length();
084 }
085 CacheKey key = getCacheKey(viewRoot);
086
087 ImageCacheKey imageKey = new ImageCacheKey(key, name);
088
089 result = imageCache.get(imageKey);
090 if (result == null) {
091 // TODO: cache null values
092 try {
093 List paths = getPaths(key.getClientPropertyId(), key.getLocale(), "", null, name.substring(0, dot),
094 name.substring(dot), false, true, true, null, true, ignoreMissing);
095 if (paths != null) {
096 result = (String) paths.get(0);
097 }
098 // TODO: cache null values
099 imageCache.put(imageKey, result);
100 } catch (Exception e) {
101 LOG.error("name = '" + name + "' clientProperties = '" + key.getClientPropertyId() + "'", e);
102 }
103 }
104 }
105
106 if (result == null) {
107 if (LOG.isDebugEnabled()) {
108 LOG.debug("Can't find image for \"" + name + "\"");
109 }
110 }
111
112 return result;
113 }
114
115 private CacheKey getCacheKey(UIViewRoot viewRoot) {
116 CacheKey key;
117 if (viewRoot instanceof org.apache.myfaces.tobago.component.UIViewRoot) {
118 key = ((org.apache.myfaces.tobago.component.UIViewRoot) viewRoot).getRendererCacheKey();
119 } else {
120 String clientPropertyId = ClientProperties.getInstance(viewRoot).getId();
121 Locale locale = viewRoot.getLocale();
122 key = new CacheKey(clientPropertyId, locale);
123 }
124 return key;
125 }
126
127 public String getJsp(UIViewRoot viewRoot, String name) {
128 String result = null;
129 if (name != null) {
130 CacheKey key = getCacheKey(viewRoot);
131
132 JspCacheKey jspKey = new JspCacheKey(key, name);
133
134 result = jspCache.get(jspKey);
135 if (result != null) {
136 return result;
137 }
138 try {
139 result = (String) getPaths(key.getClientPropertyId(), key.getLocale(), "",
140 JSP, name, "", false, true, true, null, true, false).get(0);
141 jspCache.put(jspKey, result);
142 } catch (Exception e) {
143 LOG.error("name = '" + name + "' clientProperties = '" + key.getClientPropertyId() + "'", e);
144 }
145 }
146 return result;
147 }
148
149 public String getProperty(
150 UIViewRoot viewRoot, String bundle, String propertyKey) {
151 String result = null;
152 if (bundle != null && propertyKey != null) {
153 CacheKey key = getCacheKey(viewRoot);
154
155 PropertyCacheKey propertyCacheKey = new PropertyCacheKey(key, bundle, propertyKey);
156 result = propertyCache.get(propertyCacheKey);
157 if (result != null) {
158 return result;
159 }
160 List properties = getPaths(key.getClientPropertyId(), key.getLocale(), "", PROPERTY, bundle,
161 "", false, true, false, propertyKey, true, false);
162 if (properties != null) {
163 result = (String) properties.get(0);
164 } else {
165 result = null;
166 }
167 propertyCache.put(propertyCacheKey, result);
168 }
169 return result;
170 }
171
172 private List getPaths(String clientProperties, Locale locale, String prefix,
173 String subDir, String name, String suffix,
174 boolean reverseOrder, boolean single, boolean returnKey,
175 String key, boolean returnStrings, boolean ignoreMissing) {
176 List matches = new ArrayList();
177
178 StringTokenizer tokenizer = new StringTokenizer(clientProperties, "/");
179 String contentType = tokenizer.nextToken();
180 Theme theme = tobagoConfig.getTheme(tokenizer.nextToken());
181 UserAgent browser = UserAgent.getInstanceForId(tokenizer.nextToken());
182 List<String> locales = ClientProperties.getLocaleList(locale, false);
183
184 String path;
185
186 // e.g. 1. application, 2. library or renderkit
187 for (Theme themeName : theme.getFallbackList()) { // theme loop
188 for (String resourceDirectory : tobagoConfig.getResourceDirs()) {
189 for (String browserType : browser.getFallbackList()) { // browser loop
190 for (String localeSuffix : locales) { // locale loop
191 path = makePath(resourceDirectory,
192 contentType,
193 themeName,
194 browserType,
195 subDir,
196 name,
197 localeSuffix,
198 suffix,
199 key);
200 if (LOG.isDebugEnabled()) {
201 LOG.debug("testing path: " + path);
202 }
203 if (returnStrings && resourceList.containsKey(path)) {
204 String result =
205 returnKey
206 ? prefix + path
207 : prefix + resourceList.get(path);
208
209 if (reverseOrder) {
210 matches.add(0, result);
211 } else {
212 matches.add(result);
213 }
214
215 if (single) {
216 return matches;
217 }
218 } else if (!returnStrings) {
219 try {
220 path = path.substring(1).replace('/', '.');
221 Class clazz = Class.forName(path);
222 matches.add(clazz);
223 return matches;
224 } catch (ClassNotFoundException e) {
225 // not found
226 }
227 }
228 }
229 }
230 }
231 }
232 for (String localeSuffix : locales) { // locale loop
233 path = makePath(name, localeSuffix, suffix, key);
234 if (LOG.isDebugEnabled()) {
235 LOG.debug("testing path: " + path);
236 }
237 if (returnStrings && resourceList.containsKey(path)) {
238 String result =
239 returnKey
240 ? prefix + path
241 : prefix + resourceList.get(path);
242
243 if (reverseOrder) {
244 matches.add(0, result);
245 } else {
246 matches.add(result);
247 }
248
249 if (single) {
250 return matches;
251 }
252 } else if (!returnStrings) {
253 try {
254 path = path.substring(1).replace('/', '.');
255 Class clazz = Class.forName(path);
256 matches.add(clazz);
257 return matches;
258 } catch (ClassNotFoundException e) {
259 // not found
260 }
261 }
262 }
263 if (matches.isEmpty()) {
264 if (!ignoreMissing) {
265 LOG.error("Path not found, and no fallback. Using empty string.\n"
266 + "resourceDirs = '" + tobagoConfig.getResourceDirs()
267 + "' contentType = '" + contentType
268 + "' theme = '" + theme
269 + "' browser = '" + browser
270 + "' subDir = '" + subDir
271 + "' name = '" + name
272 + "' suffix = '" + suffix
273 + "' key = '" + key
274 + "'"/*, new Exception()*/);
275 }
276 return null;
277 } else {
278 return matches;
279 }
280 }
281
282 private String makePath(String project,
283 String language, Theme theme, String browser,
284 String subDir, String name, String localeSuffix, String extension,
285 String key) {
286 StringBuilder searchtext = new StringBuilder();
287
288 searchtext.append('/');
289 searchtext.append(project);
290 searchtext.append('/');
291 searchtext.append(language);
292 searchtext.append('/');
293 searchtext.append(theme.getName());
294 searchtext.append('/');
295 searchtext.append(browser);
296 if (subDir != null) {
297 searchtext.append('/');
298 searchtext.append(subDir);
299 }
300 searchtext.append('/');
301 searchtext.append(name);
302 searchtext.append(localeSuffix);
303 searchtext.append(extension);
304 if (key != null) {
305 searchtext.append('/');
306 searchtext.append(key);
307 }
308
309 return searchtext.toString();
310 }
311
312 private String makePath(
313 String name, String localeSuffix, String extension, String key) {
314 StringBuilder searchtext = new StringBuilder();
315
316 searchtext.append('/');
317 searchtext.append(name);
318 searchtext.append(localeSuffix);
319 searchtext.append(extension);
320 if (key != null) {
321 searchtext.append('/');
322 searchtext.append(key);
323 }
324
325 return searchtext.toString();
326 }
327
328 public Renderer getRenderer(UIViewRoot viewRoot, String name) {
329 Renderer renderer = null;
330
331 if (name != null) {
332 CacheKey key = getCacheKey(viewRoot);
333
334 RendererCacheKey rendererKey = new RendererCacheKey(key, name);
335 renderer = rendererCache.get(rendererKey);
336 if (renderer != null) {
337 return renderer;
338 }
339 try {
340 name = getRendererClassName(name);
341 List<Class> classes = getPaths(key.getClientPropertyId(), key.getLocale(), "", TAG, name, "",
342 false, true, true, null, false, false);
343 if (classes != null && !classes.isEmpty()) {
344 Class clazz = classes.get(0);
345 renderer = (Renderer) clazz.newInstance();
346 rendererCache.put(rendererKey, renderer);
347 } else {
348 LOG.error("Don't find any RendererClass for " + name + ". Please check you configuration.");
349 }
350 } catch (InstantiationException e) {
351 LOG.error("name = '" + name + "' clientProperties = '" + key.getClientPropertyId() + "'", e);
352 } catch (IllegalAccessException e) {
353 LOG.error("name = '" + name + "' clientProperties = '" + key.getClientPropertyId() + "'", e);
354 }
355 }
356 return renderer;
357 }
358
359
360 private String getRendererClassName(String rendererType) {
361 String name;
362 if (LOG.isDebugEnabled()) {
363 LOG.debug("rendererType = '" + rendererType + "'");
364 }
365 if ("javax.faces.Text".equals(rendererType)) { // TODO: find a better way
366 name = RENDERER_TYPE_OUT;
367 } else {
368 name = rendererType;
369 }
370 name = name + "Renderer";
371 if (name.startsWith("javax.faces.")) { // FIXME: this is a hotfix from jsf1.0beta to jsf1.0fr
372 LOG.warn("patching renderer from " + name);
373 name = name.substring("javax.faces.".length());
374 LOG.warn("patching renderer to " + name);
375 }
376 return name;
377 }
378
379 public String[] getScripts(UIViewRoot viewRoot, String name) {
380 return getStrings(viewRoot, name, null);
381 }
382
383 public String[] getStyles(UIViewRoot viewRoot, String name) {
384 return getStrings(viewRoot, name, null);
385 }
386
387 private String[] getStrings(UIViewRoot viewRoot, String name, String type) {
388 String[] result = null;
389 if (name != null) {
390 int dot = name.lastIndexOf('.');
391 if (dot == -1) {
392 dot = name.length();
393 }
394 CacheKey key = getCacheKey(viewRoot);
395 MiscCacheKey miscKey = new MiscCacheKey(key, name);
396 result = miscCache.get(miscKey);
397 if (result != null) {
398 return result;
399 }
400 try {
401 List matches = getPaths(key.getClientPropertyId(), key.getLocale(), "", type,
402 name.substring(0, dot), name.substring(dot), true, false, true, null, true, false);
403 result = (String[]) matches.toArray(new String[matches.size()]);
404 miscCache.put(miscKey, result);
405 } catch (Exception e) {
406 LOG.error("name = '" + name + "' clientProperties = '" + key.getClientPropertyId() + "'", e);
407 }
408 }
409 return result;
410 }
411
412 public String getThemeProperty(UIViewRoot viewRoot,
413 String bundle, String propertyKey) {
414 String result = null;
415 if (bundle != null && propertyKey != null) {
416 CacheKey key = getCacheKey(viewRoot);
417
418 PropertyCacheKey propertyCacheKey = new PropertyCacheKey(key, bundle, propertyKey);
419 result = propertyCache.get(propertyCacheKey);
420 if (result != null) {
421 return result;
422 }
423 List properties = getPaths(key.getClientPropertyId(), key.getLocale(), "", PROPERTY,
424 bundle, "", false, true, false, propertyKey, true, true);
425 if (properties != null) {
426 result = (String) properties.get(0);
427 } else {
428 result = null;
429 }
430 propertyCache.put(propertyCacheKey, result);
431 }
432 return result;
433 }
434
435 public static CacheKey getRendererCacheKey(String clientPropertyId, Locale locale) {
436 return new CacheKey(clientPropertyId, locale);
437 }
438
439
440 private static final class ImageCacheKey {
441 private CacheKey cacheKey;
442 private String name;
443 private int hashCode;
444
445 private ImageCacheKey(CacheKey cacheKey, String name) {
446 this.name = name;
447 this.cacheKey = cacheKey;
448 hashCode = calcHashCode();
449 }
450
451 public boolean equals(Object o) {
452 if (this == o) {
453 return true;
454 }
455 if (o == null || getClass() != o.getClass()) {
456 return false;
457 }
458
459 ImageCacheKey that = (ImageCacheKey) o;
460
461 return cacheKey.equals(that.cacheKey) && name.equals(that.name);
462
463 }
464
465 private int calcHashCode() {
466 int result;
467 result = cacheKey.hashCode();
468 result = 31 * result + name.hashCode();
469 return result;
470 }
471
472 public int hashCode() {
473 return hashCode;
474 }
475 }
476
477 private static final class JspCacheKey {
478 private final CacheKey cacheKey;
479 private final String name;
480 private final int hashCode;
481
482 private JspCacheKey(CacheKey cacheKey, String name) {
483 this.cacheKey = cacheKey;
484 this.name = name;
485 hashCode = calcHashCode();
486 }
487
488 public boolean equals(Object o) {
489 if (this == o) {
490 return true;
491 }
492 if (o == null || getClass() != o.getClass()) {
493 return false;
494 }
495
496 JspCacheKey that = (JspCacheKey) o;
497
498 return cacheKey.equals(that.cacheKey) && name.equals(that.name);
499
500 }
501
502 private int calcHashCode() {
503 int result;
504 result = cacheKey.hashCode();
505 result = 31 * result + name.hashCode();
506 return result;
507 }
508
509 public int hashCode() {
510 return hashCode;
511 }
512 }
513
514 private static final class PropertyCacheKey {
515 private final CacheKey cacheKey;
516 private final String name;
517 private final String key;
518 private final int hashCode;
519
520 private PropertyCacheKey(CacheKey cacheKey, String name, String key) {
521 this.cacheKey = cacheKey;
522 this.name = name;
523 this.key = key;
524 hashCode = calcHashCode();
525 }
526
527 public boolean equals(Object o) {
528 if (this == o) {
529 return true;
530 }
531 if (o == null || getClass() != o.getClass()) {
532 return false;
533 }
534
535 PropertyCacheKey that = (PropertyCacheKey) o;
536
537 return cacheKey.equals(that.cacheKey) && key.equals(that.key) && name.equals(that.name);
538
539 }
540
541 private int calcHashCode() {
542 int result;
543 result = cacheKey.hashCode();
544 result = 31 * result + name.hashCode();
545 result = 31 * result + key.hashCode();
546 return result;
547 }
548
549 public int hashCode() {
550 return hashCode;
551 }
552 }
553
554 private static final class MiscCacheKey {
555 private final CacheKey cacheKey;
556 private final String name;
557 private final int hashCode;
558
559 private MiscCacheKey(CacheKey cacheKey, String name) {
560 this.cacheKey = cacheKey;
561 this.name = name;
562 hashCode = calcHashCode();
563 }
564
565 public boolean equals(Object o) {
566 if (this == o) {
567 return true;
568 }
569 if (o == null || getClass() != o.getClass()) {
570 return false;
571 }
572
573 MiscCacheKey that = (MiscCacheKey) o;
574
575 return cacheKey.equals(that.cacheKey) && name.equals(that.name);
576
577 }
578
579 private int calcHashCode() {
580 int result;
581 result = cacheKey.hashCode();
582 result = 31 * result + name.hashCode();
583 return result;
584 }
585
586 public int hashCode() {
587 return hashCode;
588 }
589 }
590
591 private static final class RendererCacheKey {
592 private final CacheKey cacheKey;
593 private final String name;
594 private final int hashCode;
595
596 private RendererCacheKey(CacheKey cacheKey, String name) {
597 this.cacheKey = cacheKey;
598 this.name = name;
599 hashCode = calcHashCode();
600 }
601
602 public boolean equals(Object o) {
603 if (this == o) {
604 return true;
605 }
606 if (o == null || getClass() != o.getClass()) {
607 return false;
608 }
609
610 RendererCacheKey that = (RendererCacheKey) o;
611
612 return cacheKey.equals(that.cacheKey) && name.equals(that.name);
613
614 }
615
616 private int calcHashCode() {
617 int result;
618 result = cacheKey.hashCode();
619 result = 31 * result + name.hashCode();
620 return result;
621 }
622
623 public int hashCode() {
624 return hashCode;
625 }
626 }
627
628 public static final class CacheKey {
629 private final String clientPropertyId;
630 private final Locale locale;
631 private final int hashCode;
632
633 private CacheKey(String clientPropertyId, Locale locale) {
634 this.clientPropertyId = clientPropertyId;
635 if (locale == null) { // FIXME: should not happen, but does.
636 LOG.warn("locale == null");
637 locale = Locale.getDefault();
638 }
639 this.locale = locale;
640 hashCode = calcHashCode();
641 }
642
643 public String getClientPropertyId() {
644 return clientPropertyId;
645 }
646
647 public Locale getLocale() {
648 return locale;
649 }
650
651 public boolean equals(Object o) {
652 if (this == o) {
653 return true;
654 }
655 if (o == null || getClass() != o.getClass()) {
656 return false;
657 }
658
659 CacheKey cacheKey = (CacheKey) o;
660
661 return clientPropertyId.equals(cacheKey.clientPropertyId) && locale.equals(cacheKey.locale);
662
663 }
664
665 private int calcHashCode() {
666 int result;
667 result = clientPropertyId.hashCode();
668 result = 31 * result + locale.hashCode();
669 return result;
670 }
671
672 public int hashCode() {
673 return hashCode;
674 }
675 }
676 }
677