001    package org.apache.myfaces.tobago.renderkit;
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.logging.Log;
021    import org.apache.commons.logging.LogFactory;
022    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_HEIGHT;
023    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_WIDTH;
024    import static org.apache.myfaces.tobago.TobagoConstants.FACET_MENUBAR;
025    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_INNER_WIDTH;
026    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_INNER_HEIGHT;
027    import org.apache.myfaces.tobago.util.ComponentUtil;
028    import org.apache.myfaces.tobago.component.Cell;
029    import org.apache.myfaces.tobago.util.LayoutUtil;
030    import org.apache.myfaces.tobago.renderkit.html.StyleClasses;
031    import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
032    import org.apache.myfaces.tobago.renderkit.html.HtmlStyleMap;
033    
034    import javax.faces.component.UIComponent;
035    import javax.faces.context.FacesContext;
036    import java.awt.Dimension;
037    import java.io.IOException;
038    import java.util.ArrayList;
039    import java.util.List;
040    import java.util.Map;
041    
042    public abstract class LayoutableRendererBase
043        extends RendererBase implements LayoutableRenderer {
044    
045      private static final Log LOG = LogFactory.getLog(LayoutableRendererBase.class);
046    
047      public int getHeaderHeight(
048          FacesContext facesContext, UIComponent component) {
049        int height = getConfiguredValue(facesContext, component, "headerHeight");
050        final UIComponent menubar = component.getFacet(FACET_MENUBAR);
051        if (menubar != null) {
052          height += getConfiguredValue(facesContext, menubar, "headerHeight");
053        }
054        return height;
055      }
056    
057      public int getPaddingWidth(FacesContext facesContext, UIComponent component) {
058        return getConfiguredValue(facesContext, component, "paddingWidth");
059      }
060    
061      public int getPaddingHeight(
062          FacesContext facesContext, UIComponent component) {
063        return getConfiguredValue(facesContext, component, "paddingHeight");
064      }
065    
066      public int getComponentExtraWidth(
067          FacesContext facesContext,
068          UIComponent component) {
069        return getConfiguredValue(facesContext, component, "componentExtraWidth");
070      }
071    
072      public int getComponentExtraHeight(
073          FacesContext facesContext,
074          UIComponent component) {
075        return getConfiguredValue(facesContext, component, "componentExtraHeight");
076      }
077    
078      public Dimension getMinimumSize(
079          FacesContext facesContext, UIComponent component) {
080        int width = getMinimumWidth(facesContext, component);
081        int height = getMinimumHeight(facesContext, component);
082        return new Dimension(width, height);
083      }
084    
085      public int getMinimumHeight(FacesContext facesContext, UIComponent component) {
086        int height = getConfiguredValue(facesContext, component, "minimumHeight");
087        if (height == -1) {
088          height = getConfiguredValue(facesContext, component, "fixedHeight");
089        }
090        return height;
091      }
092    
093      public int getMinimumWidth(FacesContext facesContext, UIComponent component) {
094        int width = getConfiguredValue(facesContext, component, "minimumWidth");
095        if (width == -1) {
096          width = getConfiguredValue(facesContext, component, "fixedWidth");
097        }
098        return width;
099      }
100    
101      public int getFixedWidth(FacesContext facesContext, UIComponent component) {
102        return getFixedSpace(facesContext, component, true);
103      }
104    
105      public int getFixedHeight(FacesContext facesContext, UIComponent component) {
106        return getFixedSpace(facesContext, component, false);
107      }
108    
109      public int getFixedSpace(FacesContext facesContext, UIComponent component, boolean width) {
110    
111        int fixedSpace = 0;
112    
113        if (component instanceof Cell) {
114          List children = LayoutUtil.addChildren(new ArrayList(), component);
115          for (Object aChildren : children) {
116            UIComponent child = (UIComponent) aChildren;
117    
118            LayoutInformationProvider renderer = ComponentUtil.getRenderer(facesContext, child);
119            if (renderer != null) {
120              if (width) {
121                fixedSpace = Math.max(fixedSpace, renderer.getFixedWidth(facesContext, child));
122              } else {
123                fixedSpace = Math.max(fixedSpace, renderer.getFixedHeight(facesContext, child));
124              }
125            }
126          }
127        } else {
128          if (width) {
129            fixedSpace = getFixedSpace(facesContext, component, ATTR_WIDTH, "fixedWidth");
130          } else {
131            fixedSpace = getFixedSpace(facesContext, component, ATTR_HEIGHT, "fixedHeight");
132          }
133        }
134        return fixedSpace;
135      }
136    
137      private int getFixedSpace(FacesContext facesContext, UIComponent component,
138                                String attr, String attrFixed) {
139        int intSpace = -1;
140        Object space = null;
141        if (component != null) {
142          space = ComponentUtil.getObjectAttribute(component, attr);
143        }
144        if (space != null && space instanceof String) {
145          try {
146            intSpace = Integer.parseInt(((String) space).replaceAll("\\D", ""));
147          } catch (NumberFormatException e) {
148            LOG.error("Caught: " + e.getMessage(), e);
149          }
150        }
151        if (space != null && space instanceof Integer) {
152          intSpace = (Integer) space;
153        }
154        if (intSpace == -1) {
155          return getConfiguredValue(facesContext, component, attrFixed);
156        } else {
157          return intSpace;
158        }
159      }
160    
161      public void layoutBegin(FacesContext context, UIComponent component) throws IOException {
162      }
163    
164      public void layoutEnd(FacesContext context, UIComponent component) throws IOException {
165      }
166    
167      public void prepareRender(FacesContext facesContext, UIComponent component) throws IOException {
168        
169        prepareDimension(facesContext, component);
170        final String rendererType = component.getRendererType();
171        if (rendererType != null) {
172          final String rendererName = getRendererName(rendererType);
173          StyleClasses classes = StyleClasses.ensureStyleClasses(component);
174          classes.updateClassAttributeAndMarkup(component, rendererName);
175        } else {
176          LOG.error("No renderType for " + component); 
177        }
178        layoutSpace(facesContext, component, true);
179        layoutSpace(facesContext, component, false);
180      }
181    
182       void prepareDimension(FacesContext facesContext, UIComponent component) {
183    //    LOG.info("prepareDimension for " + component.getClientId(facesContext) + " is " + component.getRendererType());
184        setInnerWidth(facesContext, component);
185        setInnerHeight(facesContext, component);
186      }
187    
188      void setInnerWidth(FacesContext facesContext, UIComponent component) {
189        Integer layoutWidth = LayoutUtil.getLayoutWidth(component);
190        if (layoutWidth != null) {
191          int space = layoutWidth.intValue();
192          int innerSpace = LayoutUtil.getInnerSpace(facesContext, component, space, true);
193          component.getAttributes().put(ATTR_INNER_WIDTH, Integer.valueOf(innerSpace));
194        }
195      }
196    
197      void setInnerHeight(FacesContext facesContext, UIComponent component) {
198        Integer layoutHeight = LayoutUtil.getLayoutHeight(component);
199        if (layoutHeight != null) {
200          int space = layoutHeight.intValue();
201          int innerSpace = LayoutUtil.getInnerSpace(facesContext, component, space, false);
202          component.getAttributes().put(ATTR_INNER_HEIGHT, Integer.valueOf(innerSpace));
203        }
204      }
205    
206      private void layoutSpace(FacesContext facesContext, UIComponent component, boolean width) {
207    
208        // prepare html 'style' attribute
209        Integer layoutSpace;
210        String layoutAttribute;
211        String styleAttribute;
212        if (width) {
213          layoutSpace = LayoutUtil.getLayoutWidth(component);
214          layoutAttribute = org.apache.myfaces.tobago.TobagoConstants.ATTR_LAYOUT_WIDTH;
215          styleAttribute = HtmlAttributes.WIDTH;
216        } else {
217          layoutSpace = LayoutUtil.getLayoutHeight(component);
218          layoutAttribute = org.apache.myfaces.tobago.TobagoConstants.ATTR_LAYOUT_HEIGHT;
219          styleAttribute = HtmlAttributes.HEIGHT;
220        }
221        int space = -1;
222        if (layoutSpace != null) {
223          space = layoutSpace.intValue();
224        }
225        if (space == -1
226            && (!org.apache.myfaces.tobago.TobagoConstants.RENDERER_TYPE_OUT.equals(component.getRendererType()))) {
227          UIComponent parent = component.getParent();
228          space = LayoutUtil.getInnerSpace(facesContext, parent, width);
229          if (space > 0 && !ComponentUtil.isFacetOf(component, parent)) {
230            component.getAttributes().put(layoutAttribute, Integer.valueOf(space));
231            if (width) {
232              component.getAttributes().remove(org.apache.myfaces.tobago.TobagoConstants.ATTR_INNER_WIDTH);
233            } else {
234              component.getAttributes().remove(org.apache.myfaces.tobago.TobagoConstants.ATTR_INNER_HEIGHT);
235            }
236          }
237        }
238        if (space > 0) {
239          if (layoutSpace != null
240              || !ComponentUtil.getBooleanAttribute(component, org.apache.myfaces.tobago.TobagoConstants.ATTR_INLINE)) {
241            int styleSpace = space;
242              if (width) {
243                styleSpace -= getComponentExtraWidth(facesContext, component);
244              } else {
245                styleSpace -= getComponentExtraHeight(facesContext, component);
246              }
247    
248            replaceStyleAttribute(component, styleAttribute, styleSpace);
249    
250          }
251          UIComponent layout = component.getFacet(org.apache.myfaces.tobago.TobagoConstants.FACET_LAYOUT);
252          if (layout != null) {
253            int layoutSpace2 = LayoutUtil.getInnerSpace(facesContext, component,
254                width);
255            if (layoutSpace2 > 0) {
256              layout.getAttributes().put(layoutAttribute, Integer.valueOf(layoutSpace2));
257            }
258          }
259        }
260      }
261    
262      private void replaceStyleAttribute(UIComponent component, String styleAttribute, int value) {
263          HtmlStyleMap style = ensureStyleAttributeMap(component);
264          style.put(styleAttribute, value);
265        }
266    
267      private HtmlStyleMap ensureStyleAttributeMap(UIComponent component) {
268        return ensureStyleAttributeMap(component, org.apache.myfaces.tobago.TobagoConstants.ATTR_STYLE);
269      }
270    
271      private HtmlStyleMap ensureStyleAttributeMap(UIComponent component, String attribute) {
272        final Map attributes = component.getAttributes();
273        HtmlStyleMap style = (HtmlStyleMap) attributes.get(attribute);
274        if (style == null) {
275          style = new HtmlStyleMap();
276          attributes.put(attribute, style);
277        }
278        return style;
279      }
280    }