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    }