001: /*
002: * Copyright (c) 2007, intarsys consulting GmbH
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * - Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * - Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * - Neither the name of intarsys nor the names of its contributors may be used
015: * to endorse or promote products derived from this software without specific
016: * prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028: * POSSIBILITY OF SUCH DAMAGE.
029: */
030: package de.intarsys.tools.string;
031:
032: import java.io.IOException;
033: import java.io.StringReader;
034: import java.io.StringWriter;
035: import java.text.DateFormat;
036: import java.text.SimpleDateFormat;
037: import java.util.ArrayList;
038: import java.util.Date;
039: import java.util.List;
040:
041: /**
042: * A tool class for the handling of strings.
043: *
044: */
045: public class StringTools {
046: public static final String LF = "\n"; //$NON-NLS-1$
047:
048: public static final String CRLF = "\r\n"; //$NON-NLS-1$
049:
050: public static final String CR = "\r"; //$NON-NLS-1$
051:
052: public static final String DATEFORMAT_SIMPLE = "yy-MM-dd HH:mm:ss:SSSS"; //$NON-NLS-1$
053:
054: public static final String FORMAT_P = "p"; //$NON-NLS-1$
055:
056: public static final String FORMAT_D = "d"; //$NON-NLS-1$
057:
058: public static final String FORMAT_I = "i"; //$NON-NLS-1$
059:
060: public static final String FORMAT_F = "f"; //$NON-NLS-1$
061:
062: public static final String FORMAT_S = "s"; //$NON-NLS-1$
063:
064: private static DateFormat DEFAULT_DATEFORMAT = new SimpleDateFormat(
065: "yyyy_MM_dd-HH_mm_ss_SSS"); //$NON-NLS-1$
066:
067: /** An empty string constant */
068: public static final String EMPTY = ""; //$NON-NLS-1$
069:
070: /** An single space constant */
071: public static final String SPACE = " "; //$NON-NLS-1$
072:
073: /**
074: * Create a string representation and format <code>value</code> according
075: * to the instructions in <code>format</code>.
076: * <p>
077: * If <code>value</code> is null, the empty string is returned.
078: * <p>
079: */
080: public static String format(Object value, String format) {
081: if (value == null) {
082: return EMPTY;
083: }
084: if (format.startsWith(FORMAT_S)) {
085: return formatString(value, format.substring(1));
086: }
087: if (format.startsWith(FORMAT_F)) {
088: return formatFloat(value, format.substring(1));
089: }
090: if (format.startsWith(FORMAT_I)) {
091: return formatInteger(value, format.substring(1));
092: }
093: if (format.startsWith(FORMAT_D)) {
094: return formatDate(value, format.substring(1));
095: }
096: if (format.startsWith(FORMAT_P)) {
097: return formatPath(value, format.substring(1));
098: }
099:
100: // the default formattings
101: if (value instanceof Date) {
102: return new SimpleDateFormat(DATEFORMAT_SIMPLE)
103: .format(value);
104: }
105: if (value instanceof Long) {
106: return Long.toString(((Long) value).longValue(), 32);
107: }
108: return value.toString();
109: }
110:
111: /**
112: */
113: protected static String formatDate(Object value, String format) {
114: Date date = null;
115: if (value instanceof Date) {
116: date = (Date) value;
117: } else if (value instanceof Number) {
118: date = new Date(((Number) value).longValue());
119: } else if (value instanceof String) {
120: return (String) value;
121: } else {
122: return EMPTY;
123: }
124: if ((format == null) || (format.length() == 0)) {
125: return formatDateDefault(date);
126: }
127: char dateTime = '+';
128: char command = format.charAt(0);
129: if ((command == 'd') || (command == 't')) {
130: // date or time only
131: dateTime = command;
132: if (format.length() > 1) {
133: command = format.charAt(1);
134: } else {
135: command = 'f';
136: }
137: }
138: if (dateTime == 'd') {
139: if (command == 's') {
140: DateFormat dateFormat = DateFormat
141: .getDateInstance(DateFormat.SHORT);
142: return dateFormat.format(date);
143: } else if (command == 'm') {
144: DateFormat dateFormat = DateFormat
145: .getDateInstance(DateFormat.MEDIUM);
146: return dateFormat.format(date);
147: } else if (command == 'f') {
148: DateFormat dateFormat = DateFormat
149: .getDateInstance(DateFormat.FULL);
150: return dateFormat.format(date);
151: }
152: } else if (dateTime == 't') {
153: if (command == 's') {
154: DateFormat dateFormat = DateFormat
155: .getTimeInstance(DateFormat.SHORT);
156: return dateFormat.format(date);
157: } else if (command == 'm') {
158: DateFormat dateFormat = DateFormat
159: .getTimeInstance(DateFormat.MEDIUM);
160: return dateFormat.format(date);
161: } else if (command == 'f') {
162: DateFormat dateFormat = DateFormat
163: .getTimeInstance(DateFormat.FULL);
164: return dateFormat.format(date);
165: }
166: } else {
167: if (command == 's') {
168: DateFormat dateFormat = DateFormat.getDateTimeInstance(
169: DateFormat.SHORT, DateFormat.SHORT);
170: return dateFormat.format(date);
171: } else if (command == 'm') {
172: DateFormat dateFormat = DateFormat.getDateTimeInstance(
173: DateFormat.MEDIUM, DateFormat.MEDIUM);
174: return dateFormat.format(date);
175: } else if (command == 'f') {
176: DateFormat dateFormat = DateFormat.getDateTimeInstance(
177: DateFormat.FULL, DateFormat.FULL);
178: return dateFormat.format(date);
179: }
180: }
181: return formatDateDefault(date);
182: }
183:
184: /**
185: * @param value
186: */
187: protected static synchronized String formatDateDefault(Date date) {
188: return DEFAULT_DATEFORMAT.format(date);
189: }
190:
191: /**
192: */
193: protected static String formatFloat(Object value, String format) {
194: String result = null;
195: if (value == null) {
196: result = EMPTY;
197: }
198: return result.toString();
199: }
200:
201: /**
202: */
203: protected static String formatInteger(Object value, String format) {
204: if (value == null) {
205: return EMPTY;
206: }
207: char base = 'd';
208: if (format.length() > 1) {
209: base = format.charAt(1);
210: }
211: long number = 0;
212: if (value instanceof Number) {
213: number = ((Number) value).longValue();
214: } else {
215: if (value instanceof Date) {
216: number = ((Date) value).getTime();
217: } else {
218: if (value instanceof String) {
219: try {
220: number = Long.parseLong((String) value);
221: } catch (NumberFormatException e) {
222: return (String) value;
223: }
224: } else {
225: return value.toString();
226: }
227: }
228: }
229: try {
230: if (base == 'b') {
231: return Long.toString(number, 2);
232: }
233: if (base == 'o') {
234: return Long.toString(number, 8);
235: }
236: if (base == 'd') {
237: return Long.toString(number, 10);
238: }
239: if (base == 'x') {
240: return Long.toString(number, 16);
241: }
242: return Long.toString(number, 10);
243: } catch (Exception e) {
244: return value.toString();
245: }
246: }
247:
248: /**
249: * @param value
250: * @param string
251: * @return
252: */
253: protected static String formatPath(Object value, String string) {
254: return value.toString();
255: }
256:
257: /**
258: */
259: protected static String formatString(Object value, String format) {
260: String result = null;
261: if (value == null) {
262: result = EMPTY;
263: } else {
264: result = value.toString();
265: }
266: String range;
267: int from = format.indexOf('(');
268: int to = format.indexOf(')');
269: if ((from == -1) || (to == -1) || (from > to)) {
270: return result;
271: }
272: range = format.substring(from + 1, to);
273: int rangeStart = 0;
274: int rangeStop = result.length() - 1;
275: from = range.indexOf(',');
276: try {
277: if (from != -1) {
278: rangeStart = Integer.parseInt(range.substring(0, from));
279: rangeStop = Integer.parseInt(range.substring(from + 1));
280: } else {
281: rangeStart = Integer.parseInt(range);
282: }
283: } catch (NumberFormatException e) {
284: return result;
285: }
286: if (rangeStart < 0) {
287: rangeStart = result.length() + rangeStart;
288: }
289: if (rangeStop < 0) {
290: rangeStop = result.length() + rangeStop;
291: }
292: if (rangeStart < 0) {
293: rangeStart = 0;
294: }
295: if (rangeStop < 0) {
296: rangeStop = 0;
297: }
298: if (rangeStart > rangeStop) {
299: rangeStop = rangeStart - 1;
300: }
301: if (rangeStart > result.length()) {
302: rangeStart = result.length();
303: }
304: if (rangeStop >= result.length()) {
305: rangeStop = result.length() - 1;
306: }
307: return result.substring(rangeStart, rangeStop + 1);
308: }
309:
310: /**
311: * The first line of text (all characters up to the first occurence of
312: * either "\n" or "\r".
313: *
314: * @param text
315: * The text where the first line is looked up.
316: *
317: * @return The first line of text
318: */
319: public static String getFirstLine(String text) {
320: if (text == null) {
321: return EMPTY;
322: }
323:
324: int indexCR = text.indexOf(CR);
325: if (indexCR == -1) {
326: indexCR = text.length();
327: }
328: int indexLF = text.indexOf(LF);
329: if (indexLF == -1) {
330: indexLF = text.length();
331: }
332:
333: return text.substring(0, (indexCR > indexLF) ? indexLF
334: : indexCR);
335: }
336:
337: /**
338: * The number of lines in <code>text</code>. This is 1 + the number of
339: * "\n" in <code>text</code>.
340: *
341: * @param text
342: * The text where the lines are counted.
343: * @return The number of lines in <code>text</code>. This is 1 + the
344: * number of "\n" in <code>text</code>.
345: */
346: public static int getLineCount(String text) {
347: int count = 1;
348: char[] chars = text.toCharArray();
349: for (int i = 0; i < chars.length; i++) {
350: if (chars[i] == '\n') {
351: count++;
352: }
353: }
354: return count;
355: }
356:
357: /**
358: * <code>true</code> if <code>value</code> is "empty" in any respect.
359: * <p>
360: * This is the case when value == null, value has no characters or only
361: * whitespace.
362: *
363: * @param value
364: * The value to be inspected for emptyness.
365: * @return <code>true</code> if <code>value</code> is "empty" in any
366: * respect.
367: */
368: static public boolean isEmpty(String value) {
369: return (value == null) || (value.length() == 0)
370: || (value.trim().length() == 0);
371: }
372:
373: /**
374: * Parse a commandline string.
375: *
376: * @param line
377: * The commandline string.
378: * @return The array of string tokens in the commandline string.
379: */
380: static public String[] parseCommandline(String line) {
381: List result = new ArrayList();
382: StringBuilder sb = new StringBuilder();
383: boolean escaped = false;
384: boolean quoted = false;
385: boolean commented = false;
386: for (int i = 0; i < line.length(); i++) {
387: char c = line.charAt(i);
388: if (commented) {
389: if (c == '\n') {
390: commented = false;
391: }
392: continue;
393: }
394: if (escaped) {
395: escaped = false;
396: if (c != '"') {
397: sb.append('\\');
398: }
399: sb.append(c);
400: continue;
401: }
402: if (c == '\\') {
403: escaped = true;
404: continue;
405: }
406: if (c == '"') {
407: quoted = !quoted;
408: continue;
409: }
410: if (!quoted && (c == '#')) {
411: if (sb.length() > 0) {
412: result.add(sb.toString());
413: sb.setLength(0);
414: }
415: commented = true;
416: continue;
417: }
418: if (!quoted && Character.isWhitespace(c)) {
419: if (sb.length() > 0) {
420: result.add(sb.toString());
421: sb.setLength(0);
422: }
423: continue;
424: }
425: sb.append(c);
426: }
427: if (sb.length() > 0) {
428: result.add(sb.toString());
429: sb.setLength(0);
430: }
431: return (String[]) result.toArray(new String[result.size()]);
432: }
433:
434: /**
435: * Create a string representation of <code>object</code> that is
436: * guaranteed not to fail in any case.
437: *
438: * @param object
439: * The object to be printed.
440: * @return Create a string representation of <code>object</code> that is
441: * guaranteed not to fail in any case.
442: */
443: public static String safeString(Object object) {
444: try {
445: return String.valueOf(object);
446: } catch (RuntimeException e) {
447: return "<unprintable>"; //$NON-NLS-1$
448: }
449: }
450:
451: /**
452: * Create a byte array from the string. This is simply a fast version of
453: * getBytes, ignoring any encoding.
454: * <p>
455: * If you use this, you should be sure you will encounter valid ascii
456: * characters only!
457: *
458: * @param value
459: * @return
460: */
461: static public byte[] toByteArray(String value) {
462: byte[] result = new byte[value.length()];
463: value.getBytes(0, result.length, result, 0);
464: return result;
465: }
466:
467: /**
468: * Create a string from the byte array. This is simply a fast version of new
469: * String(), ignoring any encoding.
470: * <p>
471: * If you use this, you should be sure you will encounter valid ascii
472: * characters only!
473: *
474: * @param value
475: * @return
476: */
477: static public String toString(byte[] value) {
478: return new String(value, 0);
479: }
480:
481: /**
482: * Create a new string from <code>value</code> without leading whitespace.
483: *
484: * @param value
485: * The string to be stripped.
486: *
487: * @return A copy of <code>value</code> with leading whitespace stripped.
488: */
489: public static String trimLeft(String value) {
490: int i = 0;
491: int len = value.length();
492: while (i < len) {
493: if (Character.isWhitespace(value.charAt(i))) {
494: i++;
495: continue;
496: }
497: break;
498: }
499: return value.substring(i);
500: }
501:
502: /**
503: * Create a new string from <code>value</code> without trailing
504: * whitespace.
505: *
506: * @param value
507: * The string to be stripped.
508: *
509: * @return A copy of <code>value</code> with trailing whitespace stripped.
510: */
511: public static String trimRight(String value) {
512: int i = value.length() - 1;
513: while (i >= 0) {
514: if (Character.isWhitespace(value.charAt(i))) {
515: i--;
516: continue;
517: }
518: break;
519: }
520: return value.substring(0, i + 1);
521: }
522:
523: /**
524: * Create a string containing only the alphanumeric content of
525: * <code>value</code>.
526: *
527: * @param value
528: * The string to be trimmed.
529: * @return A string containing only the alphanumeric content of
530: * <code>value</code>.
531: */
532: static public String trimAlphaNumeric(String value) {
533: StringReader reader;
534: StringWriter writer;
535: char[] buffer;
536:
537: reader = new StringReader(value);
538: buffer = new char[1];
539: writer = new StringWriter();
540:
541: try {
542: while (reader.read(buffer) != -1) {
543: if (Character.isLetterOrDigit(buffer[0])) {
544: writer.write(buffer);
545: }
546: }
547: } catch (IOException ex) {
548: // working in memory; ignore
549: }
550: return writer.toString();
551: }
552: }
|