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 }