001    package org.apache.myfaces.tobago.util;
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_INLINE;
024    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_INNER_HEIGHT;
025    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_INNER_WIDTH;
026    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_LAYOUT_HEIGHT;
027    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_LAYOUT_WIDTH;
028    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_MINIMUM_SIZE;
029    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_WIDTH;
030    import static org.apache.myfaces.tobago.TobagoConstants.FACET_LABEL;
031    import static org.apache.myfaces.tobago.TobagoConstants.RENDERER_TYPE_OUT;
032    import org.apache.myfaces.tobago.component.ComponentUtil;
033    import org.apache.myfaces.tobago.component.UICell;
034    import org.apache.myfaces.tobago.component.UIForm;
035    import org.apache.myfaces.tobago.renderkit.LayoutInformationProvider;
036    
037    import javax.faces.component.UIComponent;
038    import javax.faces.component.UINamingContainer;
039    import javax.faces.context.FacesContext;
040    import java.awt.Dimension;
041    import java.util.ArrayList;
042    import java.util.List;
043    
044    public final class LayoutUtil {
045    
046      private static final Log LOG = LogFactory.getLog(LayoutUtil.class);
047    
048      private LayoutUtil() {
049        // to prevent instantiation
050      }
051    
052      public static int getInnerSpace(FacesContext facesContext,
053          UIComponent component, boolean width) {
054        String attribute;
055        if (width) {
056          attribute = ATTR_INNER_WIDTH;
057        } else {
058          attribute = ATTR_INNER_HEIGHT;
059        }
060        Integer innerSpace = (Integer) component.getAttributes().get(attribute);
061    
062        if (innerSpace == null) {
063          int space = -1;
064    
065          Integer  spaceInteger;
066          if (width) {
067            spaceInteger = getLayoutWidth(component);
068          } else {
069            spaceInteger = getLayoutHeight(component);
070          }
071          if (spaceInteger != null) {
072            space = spaceInteger;
073          }
074    
075    //      if (space < 0 && component.getParent() instanceof UIComponentBase) {
076          if (space < 0 && component.getParent() != null) {
077            space = getInnerSpace(facesContext, component.getParent(), width);
078          }
079    
080          if (space != -1) {
081            innerSpace = getInnerSpace(facesContext, component, space, width);
082          } else {
083            innerSpace = -1;
084          }
085    
086          component.getAttributes().put(attribute, innerSpace);
087        }
088    
089        return innerSpace;
090      }
091    
092      public static int getInnerSpace(FacesContext facesContext, UIComponent component,
093          int outerSpace, boolean width) {
094        int margin = 0;
095        if (component.getRendererType() != null) {
096          try {
097    
098            LayoutInformationProvider renderer = ComponentUtil.getRenderer(facesContext, component);
099    
100            if (width) {
101              margin += renderer.getPaddingWidth(facesContext, component);
102              margin += renderer.getComponentExtraWidth(facesContext, component);
103            } else {
104              margin += renderer.getHeaderHeight(facesContext, component);
105              margin += renderer.getPaddingHeight(facesContext, component);
106              margin += renderer.getComponentExtraHeight(facesContext, component);
107            }
108          } catch (Exception e) {
109            if (LOG.isDebugEnabled()) {
110              LOG.debug("cant find margin", e);
111            }
112          }
113        } else {
114          if (LOG.isDebugEnabled()) {
115            LOG.debug("renderertype = null, component: " + component);
116          }
117        }
118        return outerSpace - margin;
119      }
120    
121    
122      public static int getLabelWidth(UIComponent component) {
123        if (component != null) {
124          UIComponent label = component.getFacet(FACET_LABEL);
125          if (label != null) {
126            String labelWidth = (String) label.getAttributes().get(ATTR_WIDTH);
127            if (labelWidth != null) {
128              try {
129                return Integer.parseInt(labelWidth.replaceAll("\\D", ""));
130              } catch (NumberFormatException e) {
131                LOG.warn("Can't parse label width, using default value", e);
132              }
133            }
134          }
135        }
136        return 0;
137      }
138    
139      //TODO Change this to DimensionUtils.getWidth?
140      public static Integer getLayoutWidth(UIComponent component) {
141        return getLayoutSpace(component, ATTR_WIDTH, ATTR_LAYOUT_WIDTH);
142      }
143      //TODO Change this to DimensionUtils.getHeight?
144      public static Integer getLayoutHeight(UIComponent component) {
145        return getLayoutSpace(component, ATTR_HEIGHT, ATTR_LAYOUT_HEIGHT);
146      }
147    
148      public static Integer getLayoutSpace(UIComponent component,
149          String sizeAttribute, String layoutAttribute) {
150        Object value = ComponentUtil.getAttribute(component, sizeAttribute);
151        if (value != null) {
152          if (value instanceof String) {
153            return new Integer(((String) value).replaceAll("\\D", ""));
154          } else {
155            return (Integer) value;
156          }
157        } else if (!ComponentUtil.getBooleanAttribute(component, ATTR_INLINE)) {
158    
159          value = ComponentUtil.getAttribute(component, layoutAttribute);
160          return (Integer) value;
161        }
162        return null;
163      }
164    
165      public static List<UIComponent> addChildren(List<UIComponent> children, UIComponent panel) {
166        for (Object o : panel.getChildren()) {
167          UIComponent child = (UIComponent) o;
168          if (isTransparentForLayout(child)) {
169            addChildren(children, child);
170          } else {
171            children.add(child);
172          }
173        }
174        return children;
175      }
176    
177      public static boolean isTransparentForLayout(UIComponent component) {
178    
179    //    SubViewTag's component is UINamingContainer with 'null' rendererType
180    //    is transparent for layouting
181    
182        if (component instanceof UINamingContainer
183            && component.getRendererType() == null) {
184          return true;
185        }
186        // TODO find a better way
187        if ("facelets".equals(component.getFamily())) {
188          return !"com.sun.facelets.tag.UIDebug".equals(component.getClass().getName());
189        }
190        /* TODO disable layouting of facelet stuff
191        if (component.getClass().getPackage().getName().equals("com.sun.facelets.compiler")) {
192          return true;
193        } */
194    //  also Forms are transparent for layouting
195    
196        return component instanceof UIForm;
197      }
198    
199      public static UIComponent getLayoutParent(UIComponent component) {
200        UIComponent parent = component.getParent();
201        while (parent != null && isTransparentForLayout(parent)) {
202          parent = parent.getParent();
203        }
204        return parent;
205      }
206    
207      public static void maybeSetLayoutAttribute(UIComponent cell, String attribute,
208          Integer value) {
209        if (RENDERER_TYPE_OUT.equals(cell.getRendererType())) {
210          return;
211        }
212        if (LOG.isDebugEnabled()) {
213          LOG.debug("set " + value + " to " + cell.getRendererType());
214        }
215        cell.getAttributes().put(attribute, value);
216        if (ATTR_LAYOUT_WIDTH.equals(attribute)) {
217          cell.getAttributes().remove(ATTR_INNER_WIDTH);
218        } else if (ATTR_LAYOUT_HEIGHT.equals(attribute)) {
219          cell.getAttributes().remove(ATTR_INNER_HEIGHT);
220        }
221        if (cell instanceof UICell) {
222          List<UIComponent> children = addChildren(new ArrayList<UIComponent>(), cell);
223          for (UIComponent component : children) {
224            maybeSetLayoutAttribute(component, attribute, value);
225          }
226        }
227      }
228    
229      public static int calculateFixedHeightForChildren(FacesContext facesContext, UIComponent component) {
230        int height = 0;
231        for (Object o : component.getChildren()) {
232          UIComponent child = (UIComponent) o;
233          LayoutInformationProvider renderer = ComponentUtil.getRenderer(facesContext, child);
234          if (renderer == null
235              && child instanceof UINamingContainer
236              && child.getChildren().size() > 0) {
237            // this is a subview component ??
238            renderer = ComponentUtil.getRenderer(facesContext, (UIComponent) child.getChildren().get(0));
239          }
240          if (renderer != null) {
241            int h = renderer.getFixedHeight(facesContext, child);
242            if (h > 0) {
243              height += h;
244            }
245          }
246        }
247        return height;
248      }
249    
250      public static Dimension getMinimumSize(
251          FacesContext facesContext, UIComponent component) {
252        Dimension dimension
253            = (Dimension) component.getAttributes().get(ATTR_MINIMUM_SIZE);
254        if (dimension == null) {
255          LayoutInformationProvider renderer = ComponentUtil.getRenderer(facesContext, component);
256          if (renderer != null) {
257            dimension = renderer.getMinimumSize(facesContext, component);
258          }
259        }
260        if (dimension == null) {
261          dimension = new Dimension(-1, -1);
262        }
263        return dimension;
264      }
265    
266    }
267