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