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(64); 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(32); 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