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) { 158 return createLayoutTokens(columnLayout, count, "1*"); 159 } 160 161 public static String[] createLayoutTokens(String columnLayout, int count, String defaultToken) { 162 String[] tokens; 163 if (columnLayout != null) { 164 List<String> list = new ArrayList<String>(); 165 StringTokenizer tokenizer = new StringTokenizer(columnLayout, ";"); 166 while (tokenizer.hasMoreTokens()) { 167 String token = tokenizer.nextToken().trim(); 168 if ("*".equals(token)) { 169 token = "1*"; 170 } 171 list.add(token); 172 } 173 tokens = list.toArray(new String[list.size()]); 174 } else { 175 defaultToken = "*".equals(defaultToken) ? "1*" : defaultToken; 176 tokens = new String[count]; 177 for (int i = 0; i < tokens.length; i++) { 178 tokens[i] = defaultToken; 179 } 180 } 181 if (LOG.isDebugEnabled()) { 182 LOG.debug("created Tokens : " + tokensToString(tokens)); 183 } 184 return tokens; 185 } 186 187 public static String listToTokenString(List list) { 188 String[] tokens = new String[list.size()]; 189 for (int i = 0; i < list.size(); i++) { 190 tokens[i] = list.get(i).toString(); 191 } 192 return tokensToString(tokens); 193 } 194 195 public static String tokensToString(int[] tokens) { 196 String[] strings = new String[tokens.length]; 197 for (int i = 0; i < tokens.length; i++) { 198 strings[i] = Integer.toString(tokens[i]); 199 } 200 return tokensToString(strings); 201 } 202 203 public static String tokensToString(String[] tokens) { 204 StringBuilder sb = new StringBuilder(); 205 for (String token : tokens) { 206 if (sb.length() != 0) { 207 sb.append(";"); 208 } 209 sb.append(token); 210 } 211 sb.insert(0, "\""); 212 sb.append("\""); 213 return sb.toString(); 214 } 215 216 public boolean isFree(int column) { 217 return spaces[column] == FREE; 218 } 219 220 public int getSpaceForColumn(int column) { 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 //TODO replace with Arrays.asList .. 280 private String arrayAsString(int[] currentSpaces) { 281 StringBuilder sb = new StringBuilder("["); 282 for (int currentSpace : currentSpaces) { 283 sb.append(currentSpace); 284 sb.append(", "); 285 } 286 sb.replace(sb.lastIndexOf(", "), sb.length(), "]"); 287 return sb.toString(); 288 } 289 290 private void addSpace(int space, int i) { 291 if (spaces[i] > HIDE) { 292 if (spaces[i] == FREE) { 293 spaces[i] = space; 294 } else { 295 spaces[i] += space; 296 } 297 spaceLeft -= space; 298 } 299 } 300 301 302 public void parseHides(int padding) { 303 for (int i = 0; i < layoutTokens.getSize(); i++) { 304 if (layoutTokens.get(i) instanceof HideLayoutToken) { 305 update(0, i); 306 spaces[i] = HIDE; 307 if (LOG.isDebugEnabled()) { 308 LOG.debug("set column " + i + " from " + layoutTokens.get(i) 309 + " to hide " + " clientId='" + clientIdForLogging + "'"); 310 } 311 } 312 } 313 } 314 315 public void parsePixels() { 316 for (int i = 0; i < layoutTokens.getSize(); i++) { 317 LayoutToken token = layoutTokens.get(i); 318 if (token instanceof PixelLayoutToken) { 319 int w = ((PixelLayoutToken) token).getPixel(); 320 update(w, i, true); 321 if (LOG.isDebugEnabled()) { 322 LOG.debug("set column " + i + " from " + token 323 + " to with " + w + " clientId='" + clientIdForLogging + "'"); 324 } 325 } 326 } 327 } 328 329 330 public void parsePercent(double innerWidth) { 331 if (columnsLeft()) { 332 for (int i = 0; i < layoutTokens.getSize(); i++) { 333 LayoutToken token = layoutTokens.get(i); 334 if (isFree(i) && token instanceof PercentLayoutToken) { 335 int percent = ((PercentLayoutToken) token).getPercent(); 336 int w = (int) (innerWidth / 100 * percent); 337 update(w, i); 338 if (LOG.isDebugEnabled()) { 339 LOG.debug("set column " + i + " from " + token 340 + " to with " + w + " clientId='" + clientIdForLogging + "'"); 341 } 342 } 343 } 344 } 345 } 346 347 public void parsePortions() { 348 if (columnsLeft()) { 349 // 1. count portions 350 int portions = 0; 351 for (int i = 0; i < layoutTokens.getSize(); i++) { 352 LayoutToken token = layoutTokens.get(i); 353 if (isFree(i) && token instanceof RelativeLayoutToken) { 354 portions += ((RelativeLayoutToken) token).getFactor(); 355 } 356 } 357 // 2. calc and set portion 358 if (portions > 0) { 359 int widthForPortions = getSpaceLeft(); 360 for (int i = 0; i < layoutTokens.getSize(); i++) { 361 LayoutToken token = layoutTokens.get(i); 362 if (isFree(i) && token instanceof RelativeLayoutToken) { 363 int portion = ((RelativeLayoutToken) token).getFactor(); 364 float w = (float) widthForPortions / portions * portion; 365 if (w < 0) { 366 update(0, i); 367 if (LOG.isDebugEnabled()) { 368 LOG.debug("set column " + i + " from " + token 369 + " to with " + w + " == 0px" + " clientId='" + clientIdForLogging + "'"); 370 } 371 } else { 372 update(Math.round(w), i); 373 if (LOG.isDebugEnabled()) { 374 LOG.debug("set column " + i + " from " + token 375 + " to with " + w + " == " + Math.round(w) + "px" + " clientId='" + clientIdForLogging + "'"); 376 } 377 } 378 } 379 } 380 } 381 } 382 } 383 384 /* 385 public void parseAsterisks() { 386 String[] tokens = getLayoutTokens(); 387 if (columnsLeft()) { 388 // 1. count unset columns 389 int portions = 0; 390 for (int i = 0; i < tokens.length; i++) { 391 if (isFree(i) && tokens[i].equals("*")) { 392 portions++; 393 } 394 } 395 // 2. calc and set portion 396 int widthPerPortion; 397 if (portions > 0) { 398 widthPerPortion = getSpaceLeft() / portions; 399 for (int i = 0; i < tokens.length; i++) { 400 if (isFree(i) && tokens[i].equals("*")) { 401 int w = widthPerPortion; 402 update(w, i); 403 if (LOG.isDebugEnabled()) { 404 LOG.debug("set column " + i + " from " + tokens[i] 405 + " to with " + w); 406 } 407 } 408 } 409 } 410 } 411 } 412 */ 413 414 public void parseColumnLayout(double space) { 415 parseColumnLayout(space, 0); 416 } 417 418 public void parseColumnLayout(double space, int padding) { 419 420 if (hasLayoutTokens()) { 421 parseHides(padding); 422 parsePixels(); 423 parsePercent(space); 424 parsePortions(); 425 // parseAsterisks(); 426 handleSpaceLeft(); 427 } 428 429 if (columnsLeft() && LOG.isWarnEnabled()) { 430 handleIllegalTokens(); 431 } 432 } 433 434 public String toString() { 435 return new ToStringBuilder(this). 436 append("cellLeft", cellsLeft). 437 append("spaceLeft", spaceLeft). 438 append("spaces", spaces). 439 append("layoutTokens", layoutTokens). 440 toString(); 441 } 442 } 443