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