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    }