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.lang.builder.ToStringBuilder; 021 import org.apache.commons.logging.Log; 022 import org.apache.commons.logging.LogFactory; 023 import org.apache.myfaces.tobago.component.HideLayoutToken; 024 import org.apache.myfaces.tobago.component.LayoutToken; 025 import org.apache.myfaces.tobago.component.LayoutTokens; 026 import org.apache.myfaces.tobago.component.PercentLayoutToken; 027 import org.apache.myfaces.tobago.component.PixelLayoutToken; 028 import org.apache.myfaces.tobago.component.RelativeLayoutToken; 029 030 import java.util.ArrayList; 031 import java.util.List; 032 import java.util.StringTokenizer; 033 034 public class LayoutInfo { 035 036 private static final Log LOG = LogFactory.getLog(LayoutInfo.class); 037 038 private static final int FREE = -1; 039 public static final int HIDE = -2; 040 041 private int cellsLeft; 042 private int spaceLeft; 043 private int[] spaces; 044 private LayoutTokens layoutTokens; 045 private String clientIdForLogging; 046 047 public LayoutInfo(int cellCount, int space, LayoutTokens layoutTokens, String clientIdForLogging) { 048 this(cellCount, space, layoutTokens, clientIdForLogging, false); 049 } 050 051 public LayoutInfo(int cellCount, int space, LayoutTokens layoutTokens, 052 String clientIdForLogging, boolean ignoreMismatch) { 053 054 this.cellsLeft = cellCount; 055 this.spaceLeft = space; 056 this.layoutTokens = layoutTokens; 057 this.clientIdForLogging = clientIdForLogging; 058 /*if (layoutTokens.length == cellCount) { 059 this.layoutTokens = layoutTokens; 060 } else */ 061 if (layoutTokens.getSize() > cellCount) { 062 if (!ignoreMismatch) { 063 LOG.warn("More tokens (" + layoutTokens.getSize() 064 + ") for layout than cells (" + cellCount + ") found! Ignoring" 065 + " redundant tokens. Token string was: " 066 + layoutTokens 067 + " clientId='" + clientIdForLogging + "'"); 068 } 069 070 layoutTokens.shrinkSizeTo(cellCount); 071 } else { 072 if (!ignoreMismatch && LOG.isWarnEnabled() && (cellCount - layoutTokens.getSize()) != 0) { 073 LOG.warn("More cells (" + cellCount + ") than tokens (" + layoutTokens.getSize() 074 + ") for layout found! Setting missing tokens to '1*'." 075 + " Token string was: " + layoutTokens 076 + " clientId='" + clientIdForLogging + "'"); 077 } 078 layoutTokens.ensureSize(cellCount, new RelativeLayoutToken(1)); 079 //this.layoutTokens = new String[cellCount]; 080 //for (int i = 0; i < cellCount; i++) { 081 // if (i < layoutTokens.length) { 082 // this.layoutTokens[i] = layoutTokens[i]; 083 // } else { 084 // this.layoutTokens[i] = "1*"; 085 // } 086 //} 087 } 088 createAndInitSpaces(cellCount, FREE); 089 } 090 091 private void createAndInitSpaces(int columns, int initValue) { 092 spaces = new int[columns]; 093 for (int j = 0; j < spaces.length; j++) { 094 spaces[j] = initValue; 095 } 096 } 097 098 public void update(int space, int index) { 099 update(space, index, false); 100 } 101 102 public void update(int space, int index, boolean force) { 103 if (space > spaceLeft) { 104 if (LOG.isDebugEnabled()) { 105 LOG.debug("More space (" + space + ") needed than available (" + spaceLeft + ")!" 106 + " clientId='" + clientIdForLogging + "'"); 107 } 108 if (!force) { 109 if (LOG.isDebugEnabled()) { 110 LOG.debug("Cutting to fit. " + " clientId='" + clientIdForLogging + "'"); 111 } 112 if (spaceLeft < 0) { 113 space = 0; 114 } else { 115 space = spaceLeft; 116 } 117 } 118 } 119 120 spaceLeft -= space; 121 cellsLeft--; 122 if (index < spaces.length) { 123 spaces[index] = space; 124 if (spaceLeft < 1 && columnsLeft()) { 125 if (LOG.isWarnEnabled()) { 126 LOG.warn("There are columns left but no more space! cellsLeft=" 127 + cellsLeft + ", tokens=" + layoutTokens 128 + " clientId='" + clientIdForLogging + "'"); 129 LOG.warn("calculated spaces = " + tokensToString(spaces) 130 + " clientId='" + clientIdForLogging + "'"); 131 } 132 } 133 } else { 134 LOG.warn("More space to assign (" + space + "px) but no more columns!" 135 + " More layout tokens than column tags?" + " clientId='" + clientIdForLogging + "'"); 136 } 137 } 138 139 public boolean columnsLeft() { 140 return cellsLeft > 0; 141 } 142 143 144 public void handleIllegalTokens() { 145 for (int i = 0; i < spaces.length; i++) { 146 if (isFree(i)) { 147 if (LOG.isWarnEnabled()) { 148 LOG.warn("Illegal layout token pattern \"" + layoutTokens.get(i) 149 + "\" ignored, set to 0px !" + " clientId='" + clientIdForLogging + "'"); 150 } 151 spaces[i] = 0; 152 } 153 } 154 } 155 156 157 public static String[] createLayoutTokens(String columnLayout, int count, String defaultToken) { 158 String[] tokens; 159 if (columnLayout != null) { 160 List<String> list = new ArrayList<String>(); 161 StringTokenizer tokenizer = new StringTokenizer(columnLayout, ";"); 162 while (tokenizer.hasMoreTokens()) { 163 String token = tokenizer.nextToken().trim(); 164 if ("*".equals(token)) { 165 token = "1*"; 166 } 167 list.add(token); 168 } 169 tokens = list.toArray(new String[list.size()]); 170 } else { 171 defaultToken = "*".equals(defaultToken) ? "1*" : defaultToken; 172 tokens = new String[count]; 173 for (int i = 0; i < tokens.length; i++) { 174 tokens[i] = defaultToken; 175 } 176 } 177 if (LOG.isDebugEnabled()) { 178 LOG.debug("created Tokens : " + tokensToString(tokens)); 179 } 180 return tokens; 181 } 182 183 public static String listToTokenString(List list) { 184 String[] tokens = new String[list.size()]; 185 for (int i = 0; i < list.size(); i++) { 186 tokens[i] = list.get(i).toString(); 187 } 188 return tokensToString(tokens); 189 } 190 191 public static String tokensToString(int[] tokens) { 192 String[] strings = new String[tokens.length]; 193 for (int i = 0; i < tokens.length; i++) { 194 strings[i] = Integer.toString(tokens[i]); 195 } 196 return tokensToString(strings); 197 } 198 199 public static String tokensToString(String[] tokens) { 200 StringBuilder sb = new StringBuilder(); 201 for (String token : tokens) { 202 if (sb.length() != 0) { 203 sb.append(";"); 204 } 205 sb.append(token); 206 } 207 sb.insert(0, "\""); 208 sb.append("\""); 209 return sb.toString(); 210 } 211 212 public boolean isFree(int column) { 213 return spaces[column] == FREE; 214 } 215 216 public int getSpaceForColumn(int column) { 217 if (column >= spaces.length) { 218 LOG.error("spaces length " + spaces.length + " column " + column); 219 return 0; 220 } 221 return spaces[column]; 222 } 223 224 public int getSpaceLeft() { 225 return spaceLeft; 226 } 227 228 public LayoutTokens getLayoutTokens() { 229 return layoutTokens; 230 } 231 232 public boolean hasLayoutTokens() { 233 return !layoutTokens.isEmpty(); 234 } 235 236 public List<Integer> getSpaceList() { 237 List<Integer> list = new ArrayList<Integer>(spaces.length); 238 for (int space : spaces) { 239 list.add(space); 240 } 241 return list; 242 } 243 244 public void handleSpaceLeft() { 245 if (spaceLeft > 0) { 246 if (LOG.isDebugEnabled()) { 247 LOG.debug("spread spaceLeft (" + spaceLeft + "px) to columns" + " clientId='" + clientIdForLogging + "'"); 248 LOG.debug("spaces before spread :" + arrayAsString(spaces) + " clientId='" + clientIdForLogging + "'"); 249 } 250 251 for (int i = 0; i < layoutTokens.getSize(); i++) { 252 if (layoutTokens.get(i) instanceof RelativeLayoutToken) { 253 addSpace(spaceLeft, i); 254 break; 255 } 256 } 257 boolean found = false; 258 while (spaceLeft > 0) { 259 // for (int i = 0; i < layoutTokens.length; i++) { 260 for (int i = layoutTokens.getSize() - 1; i > -1; i--) { 261 //String layoutToken = layoutTokens[i]; 262 if (spaceLeft > 0 && layoutTokens.get(i) instanceof RelativeLayoutToken) { 263 found = true; 264 addSpace(1, i); 265 } 266 } 267 if (!found) { 268 break; 269 } 270 } 271 } 272 if (spaceLeft > 0 && LOG.isWarnEnabled()) { 273 LOG.warn("Space left after spreading : " + spaceLeft + "px!" + " clientId='" + clientIdForLogging + "'"); 274 } 275 if (LOG.isDebugEnabled()) { 276 LOG.debug("spaces after spread :" + arrayAsString(spaces) + " clientId='" + clientIdForLogging + "'"); 277 } 278 } 279 280 //TODO replace with Arrays.asList .. 281 private String arrayAsString(int[] currentSpaces) { 282 StringBuilder sb = new StringBuilder("["); 283 for (int currentSpace : currentSpaces) { 284 sb.append(currentSpace); 285 sb.append(", "); 286 } 287 sb.replace(sb.lastIndexOf(", "), sb.length(), "]"); 288 return sb.toString(); 289 } 290 291 private void addSpace(int space, int i) { 292 if (spaces[i] > HIDE) { 293 if (spaces[i] == FREE) { 294 spaces[i] = space; 295 } else { 296 spaces[i] += space; 297 } 298 spaceLeft -= space; 299 } 300 } 301 302 303 private void parsePortions(int portions) { 304 if (columnsLeft()) { 305 306 // 2. calc and set portion 307 if (portions > 0) { 308 int widthForPortions = getSpaceLeft(); 309 for (int i = 0; i < layoutTokens.getSize(); i++) { 310 LayoutToken token = layoutTokens.get(i); 311 if (isFree(i) && token instanceof RelativeLayoutToken) { 312 int portion = ((RelativeLayoutToken) token).getFactor(); 313 float w = (float) widthForPortions / portions * portion; 314 if (w < 0) { 315 update(0, i); 316 if (LOG.isDebugEnabled()) { 317 LOG.debug("set column " + i + " from " + token 318 + " to with " + w + " == 0px" + " clientId='" + clientIdForLogging + "'"); 319 } 320 } else { 321 update(Math.round(w), i); 322 if (LOG.isDebugEnabled()) { 323 LOG.debug("set column " + i + " from " + token 324 + " to with " + w + " == " + Math.round(w) + "px" + " clientId='" + clientIdForLogging + "'"); 325 } 326 } 327 } 328 } 329 } 330 } 331 } 332 333 /* 334 public void parseAsterisks() { 335 String[] tokens = getLayoutTokens(); 336 if (columnsLeft()) { 337 // 1. count unset columns 338 int portions = 0; 339 for (int i = 0; i < tokens.length; i++) { 340 if (isFree(i) && tokens[i].equals("*")) { 341 portions++; 342 } 343 } 344 // 2. calc and set portion 345 int widthPerPortion; 346 if (portions > 0) { 347 widthPerPortion = getSpaceLeft() / portions; 348 for (int i = 0; i < tokens.length; i++) { 349 if (isFree(i) && tokens[i].equals("*")) { 350 int w = widthPerPortion; 351 update(w, i); 352 if (LOG.isDebugEnabled()) { 353 LOG.debug("set column " + i + " from " + tokens[i] 354 + " to with " + w); 355 } 356 } 357 } 358 } 359 } 360 } 361 */ 362 363 public void parseColumnLayout(double space) { 364 parseColumnLayout(space, 0); 365 } 366 367 public void parseColumnLayout(double space, int padding) { 368 369 if (hasLayoutTokens()) { 370 int portions = 0; 371 for (int i = 0; i < layoutTokens.getSize(); i++) { 372 LayoutToken token = layoutTokens.get(i); 373 if (token instanceof HideLayoutToken) { 374 update(0, i); 375 spaces[i] = HIDE; 376 if (LOG.isDebugEnabled()) { 377 LOG.debug("set column " + i + " from " + layoutTokens.get(i) 378 + " to hide " + " clientId='" + clientIdForLogging + "'"); 379 } 380 } else if (token instanceof PixelLayoutToken) { 381 int w = ((PixelLayoutToken) token).getPixel(); 382 update(w, i, true); 383 if (LOG.isDebugEnabled()) { 384 LOG.debug("set column " + i + " from " + token 385 + " to with " + w + " clientId='" + clientIdForLogging + "'"); 386 } 387 } else if (token instanceof RelativeLayoutToken) { 388 portions += ((RelativeLayoutToken) token).getFactor(); 389 } else if (token instanceof PercentLayoutToken) { 390 int percent = ((PercentLayoutToken) token).getPercent(); 391 int w = (int) (space / 100 * percent); 392 update(w, i); 393 if (LOG.isDebugEnabled()) { 394 LOG.debug("set column " + i + " from " + token 395 + " to with " + w + " clientId='" + clientIdForLogging + "'"); 396 } 397 } 398 } 399 parsePortions(portions); 400 // parseAsterisks(); 401 handleSpaceLeft(); 402 } 403 404 if (columnsLeft() && LOG.isWarnEnabled()) { 405 handleIllegalTokens(); 406 } 407 } 408 409 public String toString() { 410 return new ToStringBuilder(this). 411 append("cellLeft", cellsLeft). 412 append("spaceLeft", spaceLeft). 413 append("spaces", spaces). 414 append("layoutTokens", layoutTokens). 415 toString(); 416 } 417 } 418