001 package org.apache.myfaces.tobago.config;
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.lang.ClassUtils;
021 import org.apache.commons.logging.Log;
022 import org.apache.commons.logging.LogFactory;
023 import org.apache.myfaces.tobago.component.ComponentUtil;
024 import org.apache.myfaces.tobago.context.ClientProperties;
025 import org.apache.myfaces.tobago.context.ResourceManager;
026 import org.apache.myfaces.tobago.context.ResourceManagerFactory;
027 import org.apache.myfaces.tobago.renderkit.RendererBase;
028
029 import javax.faces.component.UIComponent;
030 import javax.faces.component.UIInput;
031 import javax.faces.component.UIViewRoot;
032 import javax.faces.context.FacesContext;
033 import javax.faces.render.Renderer;
034 import javax.servlet.ServletContext;
035 import java.util.Locale;
036 import java.util.Map;
037 import java.util.concurrent.ConcurrentHashMap;
038
039 import static org.apache.myfaces.tobago.TobagoConstants.RENDERER_TYPE_IN;
040
041 public class ThemeConfig {
042
043 private static final Log LOG = LogFactory.getLog(ThemeConfig.class);
044
045 public static final String THEME_CONFIG_CACHE = "org.apache.myfaces.tobago.config.ThemeConfig.CACHE";
046
047 private static final Integer NULL_VALUE = Integer.MIN_VALUE;
048
049 public static int getValue(FacesContext facesContext, UIComponent component, String name) {
050
051 CacheKey key = new CacheKey(facesContext.getViewRoot(), component, name);
052 Map<CacheKey, Integer> cache
053 = (Map<CacheKey, Integer>) facesContext.getExternalContext().getApplicationMap().get(THEME_CONFIG_CACHE);
054
055 Integer value = cache.get(key);
056 if (value == null) {
057 value = createValue(facesContext, component, name);
058 if (value == null) {
059 value = NULL_VALUE;
060 }
061 cache.put(key, value);
062 }
063 if (!NULL_VALUE.equals(value)) {
064 return value;
065 } else {
066 // todo: remove condition, is only temporary to ignore wml errors.
067 if (!ClientProperties.getInstance(facesContext.getViewRoot()).getContentType().equals("wml")) {
068 throw new NullPointerException("No value configured");
069 }
070 // todo: remove, is only temporary to ignore wml errors.
071 return 0;
072 }
073 }
074
075 public static boolean hasValue(FacesContext facesContext, UIComponent component,
076 String name) {
077 try {
078 getValue(facesContext, component, name);
079 return true;
080 } catch (NullPointerException e) {
081 return false;
082 }
083 }
084
085 private static Integer createValue(FacesContext facesContext, UIComponent component, String name) {
086 String family;
087 String rendererType;
088 if (component != null) {
089 family = component.getFamily();
090 rendererType = component.getRendererType();
091 } else {
092 family = UIInput.COMPONENT_FAMILY;
093 rendererType = RENDERER_TYPE_IN;
094 }
095 Renderer renderer = ComponentUtil.getRenderer(facesContext, family, rendererType);
096
097 Class clazz = renderer.getClass();
098 if (LOG.isDebugEnabled()) {
099 LOG.debug("search for '" + name + "' in '" + clazz.getName() + "'");
100 }
101 ResourceManager resourceManager
102 = ResourceManagerFactory.getResourceManager(facesContext);
103 UIViewRoot viewRoot = facesContext.getViewRoot();
104 while (clazz != null) {
105 String tag = getTagName(clazz);
106 if (LOG.isDebugEnabled()) {
107 LOG.debug("try " + tag);
108 }
109
110 String property = resourceManager.getThemeProperty(viewRoot, "tobago-theme-config", tag + "." + name);
111
112 if (property != null && property.length() > 0) {
113 if (LOG.isDebugEnabled()) {
114 LOG.debug("found " + property);
115 }
116 return new Integer(property);
117 }
118 clazz = clazz.getSuperclass();
119 }
120 // todo: remove condition, is only temporary to ignore wml errors.
121 if (!ClientProperties.getInstance(viewRoot).getContentType().equals("wml")) {
122 LOG.error("Theme property '" + name + "' not found for renderer: " + renderer.getClass()
123 + " with clientProperties='" + ClientProperties.getInstance(viewRoot).getId() + "'"
124 + " and locale='" + viewRoot.getLocale() + "'");
125 }
126 return null;
127 }
128
129 private static String getTagName(Class clazz) {
130 String className = ClassUtils.getShortClassName(clazz);
131 if (className.equals(ClassUtils.getShortClassName(RendererBase.class))) {
132 return "Tobago";
133 } else if (className.endsWith("Renderer")) {
134 return className.substring(0, className.lastIndexOf("Renderer"));
135 } else if (className.endsWith("RendererBase")) {
136 return className.substring(0, className.lastIndexOf("RendererBase")) + "Base";
137 }
138 return null;
139 }
140
141 public static void init(ServletContext servletContext) {
142 servletContext.setAttribute(THEME_CONFIG_CACHE, new ConcurrentHashMap<CacheKey, Integer>(100, 0.75f, 1));
143 }
144
145 public static void shutdown(ServletContext servletContext) {
146 Map<CacheKey, Integer> cache = (Map<CacheKey, Integer>) servletContext.getAttribute(THEME_CONFIG_CACHE);
147 cache.clear();
148 servletContext.removeAttribute(THEME_CONFIG_CACHE);
149 }
150
151 private static class CacheKey {
152 private String clientProperties;
153 private Locale locale;
154 private String rendererType;
155 private String name;
156
157 public CacheKey(UIViewRoot viewRoot, UIComponent component, String name) {
158 this.clientProperties = ClientProperties.getInstance(viewRoot).getId();
159 this.locale = viewRoot.getLocale();
160 if (component != null) {
161 rendererType = component.getRendererType();
162 } else {
163 rendererType = "DEFAULT";
164 }
165 this.name = name;
166 }
167
168 public boolean equals(Object o) {
169 if (this == o) {
170 return true;
171 }
172 if (o == null || getClass() != o.getClass()) {
173 return false;
174 }
175
176 final CacheKey cacheKey = (CacheKey) o;
177
178 if (!clientProperties.equals(cacheKey.clientProperties)) {
179 return false;
180 }
181 if (!locale.equals(cacheKey.locale)) {
182 return false;
183 }
184 if (!name.equals(cacheKey.name)) {
185 return false;
186 }
187 if (!rendererType.equals(cacheKey.rendererType)) {
188 return false;
189 }
190
191 return true;
192 }
193
194 public int hashCode() {
195 int result;
196 result = clientProperties.hashCode();
197 result = 29 * result + locale.hashCode();
198 result = 29 * result + rendererType.hashCode();
199 result = 29 * result + name.hashCode();
200 return result;
201 }
202 }
203
204 }