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