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 //TODO Change this to DimensionUtils.getHeight? 149 public static Integer getLayoutHeight(UIComponent component) { 150 return getLayoutSpace(component, ATTR_HEIGHT, ATTR_LAYOUT_HEIGHT); 151 } 152 153 public static Integer getLayoutSpace(UIComponent component, 154 String sizeAttribute, String layoutAttribute) { 155 Object value = ComponentUtil.getAttribute(component, sizeAttribute); 156 if (value != null) { 157 if (value instanceof String) { 158 return new Integer(((String) value).replaceAll("\\D", "")); 159 } else { 160 return (Integer) value; 161 } 162 } else if (!ComponentUtil.getBooleanAttribute(component, ATTR_INLINE)) { 163 164 value = ComponentUtil.getAttribute(component, layoutAttribute); 165 return (Integer) value; 166 } 167 return null; 168 } 169 170 public static List<UIComponent> addChildren(List<UIComponent> children, UIComponent panel) { 171 for (Object o : panel.getChildren()) { 172 UIComponent child = (UIComponent) o; 173 if (isTransparentForLayout(child)) { 174 addChildren(children, child); 175 } else { 176 children.add(child); 177 } 178 } 179 return children; 180 } 181 182 public static boolean isTransparentForLayout(UIComponent component) { 183 184 // SubViewTag's component is UINamingContainer with 'null' rendererType 185 // is transparent for layouting 186 187 if (component instanceof UINamingContainer 188 && component.getRendererType() == null) { 189 return true; 190 } 191 // TODO find a better way 192 if ("facelets".equals(component.getFamily())) { 193 return !"com.sun.facelets.tag.UIDebug".equals(component.getClass().getName()); 194 } 195 /* TODO disable layouting of facelet stuff 196 if (component.getClass().getPackage().getName().equals("com.sun.facelets.compiler")) { 197 return true; 198 } */ 199 // also Forms are transparent for layouting 200 201 return component instanceof UIForm; 202 } 203 204 public static UIComponent getLayoutParent(UIComponent component) { 205 UIComponent parent = component.getParent(); 206 while (parent != null && isTransparentForLayout(parent)) { 207 parent = parent.getParent(); 208 } 209 return parent; 210 } 211 212 public static void maybeSetLayoutAttribute(UIComponent cell, String attribute, 213 Integer value) { 214 if (RENDERER_TYPE_OUT.equals(cell.getRendererType())) { 215 return; 216 } 217 if (LOG.isDebugEnabled()) { 218 LOG.debug("set " + value + " to " + cell.getRendererType()); 219 } 220 cell.getAttributes().put(attribute, value); 221 if (ATTR_LAYOUT_WIDTH.equals(attribute)) { 222 cell.getAttributes().remove(ATTR_INNER_WIDTH); 223 } else if (ATTR_LAYOUT_HEIGHT.equals(attribute)) { 224 cell.getAttributes().remove(ATTR_INNER_HEIGHT); 225 } 226 if (cell instanceof UICell) { 227 List<UIComponent> children = addChildren(new ArrayList<UIComponent>(), cell); 228 for (UIComponent component : children) { 229 maybeSetLayoutAttribute(component, attribute, value); 230 } 231 } 232 } 233 234 public static int calculateFixedHeightForChildren(FacesContext facesContext, UIComponent component) { 235 int height = 0; 236 for (Object o : component.getChildren()) { 237 UIComponent child = (UIComponent) o; 238 LayoutInformationProvider renderer = ComponentUtil.getRenderer(facesContext, child); 239 if (renderer == null 240 && child instanceof UINamingContainer 241 && child.getChildren().size() > 0) { 242 // this is a subview component ?? 243 renderer = ComponentUtil.getRenderer(facesContext, (UIComponent) child.getChildren().get(0)); 244 } 245 if (renderer != null) { 246 int h = renderer.getFixedHeight(facesContext, child); 247 if (h > 0) { 248 height += h; 249 } 250 } 251 } 252 return height; 253 } 254 255 public static Dimension getMinimumSize( 256 FacesContext facesContext, UIComponent component) { 257 Dimension dimension 258 = (Dimension) component.getAttributes().get(ATTR_MINIMUM_SIZE); 259 if (dimension == null) { 260 LayoutInformationProvider renderer = ComponentUtil.getRenderer(facesContext, component); 261 if (renderer != null) { 262 dimension = renderer.getMinimumSize(facesContext, component); 263 } 264 } 265 if (dimension == null) { 266 dimension = new Dimension(-1, -1); 267 } 268 return dimension; 269 } 270 271 public static boolean checkTokens(String columns) { 272 StringTokenizer st = new StringTokenizer(columns, ";"); 273 while (st.hasMoreTokens()) { 274 String token = st.nextToken(); 275 if (!TOKEN_PATTERN.matcher(token).matches()) { 276 return false; 277 } 278 } 279 return true; 280 } 281 } 282