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 }