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