001: /***************************************************************
002: * This file is part of the [fleXive](R) project.
003: *
004: * Copyright (c) 1999-2008
005: * UCS - unique computing solutions gmbh (http://www.ucs.at)
006: * All rights reserved
007: *
008: * The [fleXive](R) project is free software; you can redistribute
009: * it and/or modify it under the terms of the GNU General Public
010: * License as published by the Free Software Foundation;
011: * either version 2 of the License, or (at your option) any
012: * later version.
013: *
014: * The GNU General Public License can be found at
015: * http://www.gnu.org/copyleft/gpl.html.
016: * A copy is found in the textfile GPL.txt and important notices to the
017: * license from the author are found in LICENSE.txt distributed with
018: * these libraries.
019: *
020: * This library is distributed in the hope that it will be useful,
021: * but WITHOUT ANY WARRANTY; without even the implied warranty of
022: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
023: * GNU General Public License for more details.
024: *
025: * For further information about UCS - unique computing solutions gmbh,
026: * please see the company website: http://www.ucs.at
027: *
028: * For further information about [fleXive](R), please see the
029: * project website: http://www.flexive.org
030: *
031: *
032: * This copyright notice MUST APPEAR in all copies of the file!
033: ***************************************************************/package com.flexive.shared;
034:
035: import com.flexive.shared.exceptions.FxApplicationException;
036: import com.flexive.shared.exceptions.FxInvalidParameterException;
037: import com.flexive.shared.exceptions.FxRuntimeException;
038: import com.flexive.shared.structure.FxSelectListItem;
039: import com.flexive.shared.value.FxString;
040: import com.flexive.shared.value.SelectMany;
041: import com.flexive.shared.value.FxValue;
042: import org.apache.commons.lang.StringUtils;
043:
044: import java.text.ParseException;
045: import java.text.SimpleDateFormat;
046: import java.util.Date;
047: import java.util.Formatter;
048: import java.util.List;
049: import java.util.Locale;
050:
051: /**
052: * Miscellaneous formatting utility functions.
053: *
054: * @author Daniel Lichtenberger (daniel.lichtenberger@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
055: * @author Markus Plesser (markus.plesser@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
056: */
057: public final class FxFormatUtils {
058: /**
059: * Private constructor to avoid instantiation
060: */
061: private FxFormatUtils() {
062: }
063:
064: /**
065: * Returns true if the char is a valid RGB value.
066: *
067: * @param ch the character to check
068: * @return true if the char is a valid RGB value
069: */
070: private static boolean isValidRGBChar(final char ch) {
071: return (Character.isDigit(ch) || ch == 'A' || ch == 'B'
072: || ch == 'C' || ch == 'D' || ch == 'E' || ch == 'F');
073: }
074:
075: /**
076: * Checks if a value is a RGB color code.
077: * <p/>
078: * The RGB color code my start with a '#', but is also recognized without it.
079: *
080: * @param value the string to check
081: * @return true if the value is a valid RGB color code
082: */
083: public static boolean isRGBCode(String value) {
084: if (value == null || value.length() < 6)
085: return false;
086: String rgbValue = value.trim();
087: rgbValue = rgbValue.toUpperCase();
088: if (rgbValue.charAt(0) == '#' && rgbValue.length() != 7)
089: return false;
090: if (rgbValue.charAt(0) != '#' && rgbValue.length() != 6)
091: return false;
092: // Cut away the leading'#'
093: if ((rgbValue.charAt(0)) == '#') {
094: rgbValue = rgbValue.substring(1);
095: }
096: // Check all digits
097: for (int i = 0; i < rgbValue.length(); i++) {
098: if (!isValidRGBChar(rgbValue.charAt(i)))
099: return false;
100: }
101: // Passed all tests
102: return true;
103: }
104:
105: /**
106: * Converts "#FF0000" or "FF0000" rgb values to style="color:#FF0000" and all other style values
107: * to class="XXX".
108: *
109: * @param code the color code
110: * @return the style or class value
111: */
112: public static String colorCodeToStyle(String code) {
113: if (code == null)
114: return "";
115: String colorCode = code.trim();
116: if (colorCode != null && colorCode.length() > 0) {
117: if (isRGBCode(colorCode)) {
118: if (colorCode.charAt(0) != '#')
119: colorCode = '#' + colorCode;
120: colorCode = "style=\"color:" + colorCode + "\" ";
121: } else {
122: colorCode = "class=\"" + colorCode + "\" ";
123: }
124: }
125: return colorCode;
126: }
127:
128: /**
129: * Checks the email.
130: *
131: * @param email the email to check
132: * @return the email without whitespaces
133: * @throws com.flexive.shared.exceptions.FxInvalidParameterException
134: * if the email is invalid
135: */
136: public static String isEmail(String email)
137: throws FxInvalidParameterException {
138: boolean valid = true;
139: if (email == null) {
140: valid = false;
141: } else {
142: int pointPos = email.lastIndexOf('.');
143: int affenPos = email.indexOf('@');
144: if (pointPos == -1)
145: valid = false;
146: if (affenPos == -1)
147: valid = false;
148: if (valid) {
149: try {
150: // string between '@' und '.' ...
151: String subStr1 = email.substring(affenPos + 1,
152: pointPos);
153: // and string after '.' ....
154: String substr2 = email.substring(pointPos + 1);
155: // and string before '@' ...
156: String substr3 = email.substring(0, affenPos);
157: // must all must contain characters
158: if (subStr1.length() == 0 || substr2.length() == 0
159: || substr3.length() == 0)
160: valid = false;
161: } catch (Exception exc) {
162: // array index exception = invalid
163: valid = false;
164: }
165: }
166: }
167: if (!valid) {
168: throw new FxInvalidParameterException("EMAIL",
169: "ex.account.email.invalid", email);
170: }
171: return email.trim();
172: }
173:
174: /**
175: * Checks the password and encodes it.
176: *
177: * @param accountId the account ID (needed for computing the hash)
178: * @param password unencoded the password
179: * @return the encoded password
180: * @throws FxInvalidParameterException if the password is invalid (too short, too simple, ..)
181: */
182: public static String encodePassword(long accountId,
183: final String password) throws FxInvalidParameterException {
184: if (password.length() < 6)
185: throw new FxInvalidParameterException("PASSWORD",
186: "ex.account.password.tooShort");
187: return FxSharedUtils.hashPassword(accountId, password);
188: }
189:
190: /**
191: * Check if a color value is valid.
192: * <p/>
193: * The color may be a RGB value (recognized by a starting '#') or a css class name.<br>
194: * The function returns the default color if value is empty.<br>
195: * If the value defines a invalid RGB value a FxInvalidParameterException is thrown.<br>
196: *
197: * @param paramName the name of the parameter that is used when a FxInvalidParameterException is thrown
198: * @param value the color alue
199: * @return the (corrected) color
200: * @throws FxInvalidParameterException if the color is invalid
201: */
202: public static String processColorString(String paramName,
203: String value) throws FxInvalidParameterException {
204: if (value == null || value.length() == 0) {
205: return "#000000";
206: }
207: if (value.charAt(0) == '#') {
208: if (value.length() != 7) {
209: throw new FxInvalidParameterException(
210: "Invalid color for property [" + paramName
211: + "]", paramName);
212: }
213: for (int i = 1; i < 7; i++) {
214: char aChar = value.charAt(i);
215: if (!Character.isDigit(aChar) && (aChar != 'A')
216: && (aChar != 'B') && (aChar != 'C')
217: && (aChar != 'D') && (aChar != 'E')
218: && (aChar != 'F')) {
219: throw new FxInvalidParameterException(
220: "Invalid color for property [" + paramName
221: + "]", paramName);
222: }
223: }
224: }
225: return value;
226: }
227:
228: /**
229: * Escape a path to allow only a-zA-Z0-9/ and replace all other letters with underscores (_)
230: *
231: * @param path the path to escape
232: * @return escaped path
233: */
234: public static String escapeTreePath(String path) {
235: StringBuilder sb = new StringBuilder(path.length());
236: char c;
237: for (int i = 0; i < path.length(); i++) {
238: c = path.charAt(i);
239: if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')
240: || (c >= 'a' && c <= 'z') || c == '/')
241: sb.append(c);
242: else
243: sb.append('_');
244: }
245: return sb.toString();
246: }
247:
248: public static String toString(Date date, Locale locale) {
249: return getGermanDateFormat().format(date);
250: }
251:
252: public static Date toDate(String date, Locale locale)
253: throws ParseException {
254: return getGermanDateFormat().parse(date);
255: }
256:
257: private static SimpleDateFormat getGermanDateFormat() {
258: return new SimpleDateFormat("dd.MM.yyyy", Locale.ENGLISH);
259: }
260:
261: /**
262: * Is the value quoted by ' or " ?
263: *
264: * @param value value to check
265: * @return if quoted by ' or " ?
266: */
267: public static boolean isQuoted(String value) {
268: return !StringUtils.isEmpty(value)
269: && ((value.endsWith("'") && value.startsWith("'")) || (value
270: .endsWith("\"") && value.startsWith("\"")));
271: }
272:
273: /**
274: * Unquote a quoted value
275: *
276: * @param value value to unquote
277: * @return unquoted calue
278: */
279: public static String unquote(String value) {
280: if (!isQuoted(value))
281: return value;
282: return value.substring(1, value.length() - 1);
283: }
284:
285: /**
286: * Turns an array of bytes into a String representing each byte as an unsigned
287: * hex number.
288: *
289: * @param bytes an array of bytes to convert to a hex-string
290: * @return generated hex string
291: */
292: public static String encodeHex(byte[] bytes) {
293: StringBuffer buf = new StringBuffer(bytes.length * 2);
294: int i;
295: for (i = 0; i < bytes.length; i++) {
296: if (((int) bytes[i] & 0xff) < 0x10) {
297: buf.append("0");
298: }
299: buf.append(Long.toString((int) bytes[i] & 0xff, 16));
300: }
301: return buf.toString();
302: }
303:
304: /**
305: * Format a resource, replacing all {x} by the appropriate value in values.
306: * If values is an instance of FxString the given localeId is used
307: *
308: * @param resource String containing {0}..{n} placeholders
309: * @param languageId used locale if values contain FxString instances
310: * @param values either FxString, FxException or any Object using toString()
311: * @return formatted resource
312: */
313: public static String formatResource(String resource,
314: long languageId, Object... values) {
315: if (resource == null)
316: return "";
317: StringBuffer msg = new StringBuffer(
318: (int) (resource.length() * 1.5));
319: int pos = 0;
320: boolean inRep = false;
321: int index;
322: StringBuffer sbIdx = new StringBuffer(5);
323: while (pos < resource.length()) {
324: if (resource.charAt(pos) == '{') {
325: inRep = true;
326: sbIdx.setLength(0);
327: } else if (resource.charAt(pos) == '}' && inRep) {
328: inRep = false;
329: try {
330: index = Integer.parseInt(sbIdx.toString());
331: } catch (NumberFormatException e) {
332: index = -1;
333: }
334: if (index >= 0 && index < values.length) {
335: final Object value = values[index];
336: if (value instanceof FxString) {
337: if (((FxString) value)
338: .translationExists(languageId))
339: msg.append(((FxString) value)
340: .getTranslation(languageId));
341: else
342: msg.append(((FxString) value)
343: .getDefaultTranslation());
344: } else if (value instanceof FxApplicationException) {
345: msg.append(((FxApplicationException) value)
346: .getMessage(languageId));
347: } else if (value instanceof FxRuntimeException) {
348: msg.append(((FxRuntimeException) value)
349: .getMessage(languageId));
350: } else
351: msg.append(String.valueOf(value));
352: } else {
353: // append unset parameter
354: msg.append("{").append(index).append("}");
355: }
356: } else if (!inRep)
357: msg.append(resource.charAt(pos));
358: if (inRep && resource.charAt(pos) >= '0'
359: && resource.charAt(pos) <= '9')
360: sbIdx.append(resource.charAt(pos));
361: pos++;
362: }
363: return msg.toString();
364: }
365:
366: /**
367: * Escape text content for output in a javascript function call parameter,
368: * based on the Struts TagUtils#filter function.
369: *
370: * @param value the text content
371: * @param replaceNewlines replaces linebreaks with html <br> tags if set to true,
372: * or simply removes them (replaces them with spaces) if set to false.
373: * @param filterHtml filter html?
374: * @return the escaped text
375: */
376: public static String escapeForJavaScript(String value,
377: boolean replaceNewlines, boolean filterHtml) {
378: if (value != null) {
379: // based on org.apache.struts.util.ResponseUtils.filter, custom handling of HTML identifiers
380: StringBuffer result = null;
381: String filtered;
382: for (int i = 0; i < value.length(); i++) {
383: char lookAhead = i < value.length() - 1 ? value
384: .charAt(i + 1) : '\0';
385: filtered = null;
386: switch (value.charAt(i)) {
387: case '<':
388: if (filterHtml)
389: filtered = "<";
390: break;
391: case '>':
392: if (filterHtml)
393: filtered = ">";
394: break;
395: case '&':
396: if (filterHtml && lookAhead != '#')
397: filtered = "&";
398: break;
399: case '"':
400: filtered = """;
401: break;
402: case '\'':
403: filtered = "'";
404: break;
405: case '\r':
406: filtered = (replaceNewlines && lookAhead != '\n') ? "<br/>"
407: : " ";
408: break;
409: case '\n':
410: filtered = replaceNewlines ? "<br/>" : " ";
411: break;
412: }
413:
414: if (result == null) {
415: if (filtered != null) {
416: result = new StringBuffer(value.length() + 50);
417: if (i > 0) {
418: result.append(value.substring(0, i));
419: }
420: result.append(filtered);
421: }
422: } else {
423: if (filtered == null) {
424: result.append(value.charAt(i));
425: } else {
426: result.append(filtered);
427: }
428: }
429: }
430: return result == null ? value : result.toString();
431: } else {
432: return "";
433: }
434: }
435:
436: /**
437: * Escape text content for output in a javascript function call parameter.
438: *
439: * @param value the text content
440: * @param replaceNewlines replaces linebreaks with html <br> tags if set to true,
441: * or simply removes them (replaces them with spaces) if set to false.
442: * @return the escaped text
443: */
444: public static String escapeForJavaScript(String value,
445: boolean replaceNewlines) {
446: return escapeForJavaScript(value, replaceNewlines, false);
447: }
448:
449: /**
450: * Escape text content for output in a javascript function call parameter.
451: *
452: * @param value the text content
453: * @return the escaped text
454: */
455: public static String escapeForJavaScript(String value) {
456: return escapeForJavaScript(value, false, false);
457: }
458:
459: /**
460: * Generic SQL escape method.
461: *
462: * @param value the value to be formatted
463: * @return the formatted value
464: */
465: public static String escapeForSql(Object value) {
466: if (value instanceof FxValue) {
467: return ((FxValue) value).getSqlValue();
468: } else if (value instanceof String) {
469: return "'" + StringUtils.replace((String) value, "'", "''")
470: + "'";
471: } else if (value instanceof Date) {
472: return "'" + new Formatter().format("%tF", (Date) value)
473: + "'";
474: } else if (value instanceof FxSelectListItem) {
475: return String.valueOf(((FxSelectListItem) value).getId());
476: } else if (value instanceof SelectMany) {
477: final SelectMany selectMany = (SelectMany) value;
478: final List<Long> selectedIds = selectMany.getSelectedIds();
479: if (selectedIds.size() > 1) {
480: return "("
481: + StringUtils.join(selectedIds.iterator(), ',')
482: + ")";
483: } else if (selectedIds.size() == 1) {
484: return String.valueOf(selectedIds.get(0));
485: } else {
486: return "-1";
487: }
488: } else if (value != null) {
489: return value.toString();
490: } else {
491: return "null";
492: }
493: }
494:
495: /**
496: * Returns a basic date/time format that is readable but not localized.
497: *
498: * @return a basic date/time format that is readable but not localized.
499: */
500: public static SimpleDateFormat getDateTimeFormat() {
501: return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
502: }
503:
504: /**
505: * Returns a basic date format that is readable but not localized.
506: *
507: * @return a basic date format that is readable but not localized.
508: */
509: public static SimpleDateFormat getDateFormat() {
510: return new SimpleDateFormat("yyyy-MM-dd");
511: }
512: }
|