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