001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020 package org.apache.myfaces.tobago.config; 021 022 import org.apache.commons.lang.ClassUtils; 023 import org.apache.commons.logging.Log; 024 import org.apache.commons.logging.LogFactory; 025 import org.apache.myfaces.tobago.component.ComponentUtil; 026 import org.apache.myfaces.tobago.context.ClientProperties; 027 import org.apache.myfaces.tobago.context.ResourceManager; 028 import org.apache.myfaces.tobago.context.ResourceManagerFactory; 029 import org.apache.myfaces.tobago.renderkit.RendererBase; 030 031 import javax.faces.component.UIComponent; 032 import javax.faces.component.UIInput; 033 import javax.faces.component.UIViewRoot; 034 import javax.faces.context.FacesContext; 035 import javax.faces.render.Renderer; 036 import javax.servlet.ServletContext; 037 import java.util.Locale; 038 import java.util.Map; 039 import java.util.concurrent.ConcurrentHashMap; 040 041 import static org.apache.myfaces.tobago.TobagoConstants.RENDERER_TYPE_IN; 042 043 public class ThemeConfig { 044 045 private static final Log LOG = LogFactory.getLog(ThemeConfig.class); 046 047 public static final String THEME_CONFIG_CACHE = "org.apache.myfaces.tobago.config.ThemeConfig.CACHE"; 048 049 private static final Integer NULL_VALUE = Integer.MIN_VALUE; 050 051 public static int getValue(FacesContext facesContext, UIComponent component, String name) { 052 053 CacheKey key = new CacheKey(facesContext.getViewRoot(), component, name); 054 Map<CacheKey, Integer> cache 055 = (Map<CacheKey, Integer>) facesContext.getExternalContext().getApplicationMap().get(THEME_CONFIG_CACHE); 056 057 Integer value = cache.get(key); 058 if (value == null) { 059 value = createValue(facesContext, component, name); 060 if (value == null) { 061 value = NULL_VALUE; 062 } 063 cache.put(key, value); 064 } 065 if (!NULL_VALUE.equals(value)) { 066 return value; 067 } else { 068 // todo: remove condition, is only temporary to ignore wml errors. 069 if (!ClientProperties.getInstance(facesContext.getViewRoot()).getContentType().equals("wml")) { 070 throw new NullPointerException("No value configured"); 071 } 072 // todo: remove, is only temporary to ignore wml errors. 073 return 0; 074 } 075 } 076 077 public static boolean hasValue(FacesContext facesContext, UIComponent component, 078 String name) { 079 try { 080 getValue(facesContext, component, name); 081 return true; 082 } catch (NullPointerException e) { 083 return false; 084 } 085 } 086 087 private static Integer createValue(FacesContext facesContext, UIComponent component, String name) { 088 String family; 089 String rendererType; 090 if (component != null) { 091 family = component.getFamily(); 092 rendererType = component.getRendererType(); 093 } else { 094 family = UIInput.COMPONENT_FAMILY; 095 rendererType = RENDERER_TYPE_IN; 096 } 097 Renderer renderer = ComponentUtil.getRenderer(facesContext, family, rendererType); 098 099 Class clazz = renderer.getClass(); 100 if (LOG.isDebugEnabled()) { 101 LOG.debug("search for '" + name + "' in '" + clazz.getName() + "'"); 102 } 103 ResourceManager resourceManager 104 = ResourceManagerFactory.getResourceManager(facesContext); 105 UIViewRoot viewRoot = facesContext.getViewRoot(); 106 while (clazz != null) { 107 String tag = getTagName(clazz); 108 if (LOG.isDebugEnabled()) { 109 LOG.debug("try " + tag); 110 } 111 112 String property = resourceManager.getThemeProperty(viewRoot, "tobago-theme-config", tag + "." + name); 113 114 if (property != null && property.length() > 0) { 115 if (LOG.isDebugEnabled()) { 116 LOG.debug("found " + property); 117 } 118 return new Integer(property); 119 } 120 clazz = clazz.getSuperclass(); 121 } 122 // todo: remove condition, is only temporary to ignore wml errors. 123 if (!ClientProperties.getInstance(viewRoot).getContentType().equals("wml")) { 124 LOG.error("Theme property '" + name + "' not found for renderer: " + renderer.getClass() 125 + " with clientProperties='" + ClientProperties.getInstance(viewRoot).getId() + "'" 126 + " and locale='" + viewRoot.getLocale() + "'"); 127 } 128 return null; 129 } 130 131 private static String getTagName(Class clazz) { 132 String className = ClassUtils.getShortClassName(clazz); 133 if (className.equals(ClassUtils.getShortClassName(RendererBase.class))) { 134 return "Tobago"; 135 } else if (className.endsWith("Renderer")) { 136 return className.substring(0, className.lastIndexOf("Renderer")); 137 } else if (className.endsWith("RendererBase")) { 138 return className.substring(0, className.lastIndexOf("RendererBase")) + "Base"; 139 } 140 return null; 141 } 142 143 public static void init(ServletContext servletContext) { 144 servletContext.setAttribute(THEME_CONFIG_CACHE, new ConcurrentHashMap<CacheKey, Integer>(100, 0.75f, 1)); 145 } 146 147 public static void shutdown(ServletContext servletContext) { 148 Map<CacheKey, Integer> cache = (Map<CacheKey, Integer>) servletContext.getAttribute(THEME_CONFIG_CACHE); 149 cache.clear(); 150 servletContext.removeAttribute(THEME_CONFIG_CACHE); 151 } 152 153 private static class CacheKey { 154 private String clientProperties; 155 private Locale locale; 156 private String rendererType; 157 private String name; 158 159 public CacheKey(UIViewRoot viewRoot, UIComponent component, String name) { 160 this.clientProperties = ClientProperties.getInstance(viewRoot).getId(); 161 this.locale = viewRoot.getLocale(); 162 if (component != null) { 163 rendererType = component.getRendererType(); 164 } else { 165 rendererType = "DEFAULT"; 166 } 167 this.name = name; 168 } 169 170 public boolean equals(Object o) { 171 if (this == o) { 172 return true; 173 } 174 if (o == null || getClass() != o.getClass()) { 175 return false; 176 } 177 178 final CacheKey cacheKey = (CacheKey) o; 179 180 if (!clientProperties.equals(cacheKey.clientProperties)) { 181 return false; 182 } 183 if (!locale.equals(cacheKey.locale)) { 184 return false; 185 } 186 if (!name.equals(cacheKey.name)) { 187 return false; 188 } 189 if (!rendererType.equals(cacheKey.rendererType)) { 190 return false; 191 } 192 193 return true; 194 } 195 196 public int hashCode() { 197 int result; 198 result = clientProperties.hashCode(); 199 result = 29 * result + locale.hashCode(); 200 result = 29 * result + rendererType.hashCode(); 201 result = 29 * result + name.hashCode(); 202 return result; 203 } 204 } 205 206 }