001: /*
002: * ====================================================================
003: * JAFFA - Java Application Framework For All
004: *
005: * Copyright (C) 2002 JAFFA Development Group
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: *
021: * Redistribution and use of this software and associated documentation ("Software"),
022: * with or without modification, are permitted provided that the following conditions are met:
023: * 1. Redistributions of source code must retain copyright statements and notices.
024: * Redistributions must also contain a copy of this document.
025: * 2. Redistributions in binary form must reproduce the above copyright notice,
026: * this list of conditions and the following disclaimer in the documentation
027: * and/or other materials provided with the distribution.
028: * 3. The name "JAFFA" must not be used to endorse or promote products derived from
029: * this Software without prior written permission. For written permission,
030: * please contact mail to: jaffagroup@yahoo.com.
031: * 4. Products derived from this Software may not be called "JAFFA" nor may "JAFFA"
032: * appear in their names without prior written permission.
033: * 5. Due credit should be given to the JAFFA Project (http://jaffa.sourceforge.net).
034: *
035: * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: */
049:
050: /*
051: * StringHelper.java
052: *
053: * Created on September 5, 2001, 10:06 AM
054: */
055:
056: package org.jaffa.util;
057:
058: import java.util.StringTokenizer;
059: import java.util.ArrayList;
060: import java.io.*;
061: import org.apache.log4j.Logger;
062: import java.lang.reflect.Field;
063: import org.jaffa.metadata.FieldMetaData;
064: import org.jaffa.util.BeanHelper;
065: import org.jaffa.datatypes.Formatter;
066: import org.jaffa.datatypes.DateTime;
067: import org.jaffa.persistence.util.PersistentHelper;
068: import org.jaffa.security.SecurityManager;
069:
070: /** Utility Class for Common String Manipulation routines.
071: *
072: * @author PaulE
073: * @version 1.0
074: */
075: public class StringHelper {
076:
077: private static final Logger log = Logger
078: .getLogger(StringHelper.class);
079: private static final int DESCRIPTION_LIMIT = 25;
080: private static final String DESCRIPTION_TRUNCATE_INDICATOR = "...";
081: private static final String DESCRIPTION_BEGIN_MARKER = " (";
082: private static final String DESCRIPTION_END_MARKER = ")";
083: private static final String LINE_DELIMITER = "<BR>";
084:
085: /**
086: * Pad a number to take up at least 2 characters
087: * @param number the number to pad
088: * @return string representation of number padded to 2 characters
089: */
090: public static String pad(int number) {
091: return pad(number, 2);
092: }
093:
094: /**
095: * Pad a number to take up a specified length
096: * @param number the number to pad
097: * @param length length of resulting string
098: * @return string representation of number padded to specified length
099: */
100: public static String pad(int number, int length) {
101: String zeros = "00000000000000000000";
102: if (length > 20)
103: length = 20;
104:
105: String s = String.valueOf(number);
106: if (s.length() < length) {
107: s = zeros.substring(0, (length - s.length())) + s;
108: }
109: return s;
110: }
111:
112: /**
113: * Pad a number to take up a specified length
114: * @param number the number to pad
115: * @param len length of resulting string
116: * @param padVal Character to be used for Padding
117: * @return string representation of number padded to specified length
118: */
119: public static String pad(int number, int len, String padVal) {
120: String padString = padVal;
121: for (int i = 0; i < len; i++)
122: padString += padVal;
123:
124: String s = String.valueOf(number);
125: if (s.length() < len) {
126: s = padString.substring(0, (len - s.length())) + s;
127: }
128: return s;
129: }
130:
131: /**
132: * Tokenize the Input String with the default tokenizer (whitespace)
133: * @param s String to tokenize
134: * @return string[] representation tokenized String
135: */
136: public static String[] parseString(String s) {
137: StringTokenizer tokens = new StringTokenizer(s);
138: String[] SOut = new String[tokens.countTokens()];
139: int i = 0;
140: while (tokens.hasMoreTokens()) {
141: SOut[i++] = tokens.nextToken();
142: }
143: return SOut;
144: }
145:
146: /**
147: * Tokenize the Input String with the given delim
148: * @param s String to tokenize
149: * @param delim Deliminator to use
150: * @return string[] representation tokenized String
151: */
152: public static String[] parseString(String s, String delim) {
153: StringTokenizer tokens = new StringTokenizer(s, delim);
154: String[] SOut = new String[tokens.countTokens()];
155: int i = 0;
156: while (tokens.hasMoreTokens()) {
157: SOut[i++] = tokens.nextToken();
158: }
159: return SOut;
160: }
161:
162: /** Pad each new line of the supplied string with the
163: * specified number of spaces
164: * @param text String to pad
165: * @param indent number of spaces to indent by
166: * @return the indented string */
167: public static String linePad(String text, int indent) {
168: return linePad(text, indent, " ", false);
169: }
170:
171: /** Pad each new line of the supplied string with the
172: * specified number of 'indent Strings'
173: * @param text String to pad
174: * @param indent number of spaces to indent by
175: * @param indentWith String to use for indenting
176: * @return the indented string */
177: public static String linePad(String text, int indent,
178: String indentWith) {
179: return linePad(text, indent, indentWith, false);
180: }
181:
182: /** Pad each new line of the supplied string with the specified number of the indent string,
183: * but allows the suppression of the first line from indentation
184: * @param text String to pad
185: * @param indent number of spaces to indent by
186: * @param indentWith String to use for indenting
187: * @param supressFirst true, if the first line should be skipped when indenting
188: * @return the indented string
189: */
190: public static String linePad(String text, int indent,
191: String indentWith, boolean supressFirst) {
192: String pad = replicate(indentWith, indent);
193: String prefix = "";
194: if (!supressFirst)
195: prefix = pad;
196: if (text != null) {
197: text = prefix + replace(text, "\n", "\n" + pad);
198: }
199: return text;
200: }
201:
202: /** Produce a string by replicating the specific string the specified
203: * number of times
204: * @param text String to replicate
205: * @param count number of times to replicate string
206: * @return final replicated string
207: */
208: public static String replicate(String text, int count) {
209: StringBuffer sb = new StringBuffer();
210: for (int i = 0; i < count; i++)
211: sb.append(text);
212: return sb.toString();
213: }
214:
215: /** Converts a comma delimmitered string into an array of strings
216: * @param commaList Source string
217: * @return List of strings
218: */
219: public static ArrayList convertToList(String commaList) {
220:
221: ArrayList outputList = new ArrayList();
222:
223: StringTokenizer str = new StringTokenizer(commaList, ",");
224: while (str.hasMoreTokens()) {
225: String value = str.nextToken();
226: outputList.add(value);
227: }
228:
229: return outputList;
230: }
231:
232: /** Convert a string that has XML (and HTML) entities in it, into a regular string
233: *
234: * It will convert the following...
235: * & to &
236: * < to <
237: * > to >
238: * ' to '
239: * " to "
240: * @param s String to convert
241: * @return converted string
242: */
243: public static String convertFromHTML(String s) {
244: if (s == null)
245: return "";
246: else
247: return replace(replace(replace(replace(replace(s, "&",
248: "&"), "<", "<"), ">", ">"), "'", "'"),
249: """, "\"");
250: }
251:
252: /** Convert a regular string into an XML (and HTML) based string
253: *
254: * It will convert the following...
255: * & to &
256: * < to <
257: * > to >
258: * ' to '
259: * " to "
260: * @param s String to convert
261: * @return converted string
262: */
263: public static String convertToHTML(String s) {
264: if (s == null)
265: return "";
266: else
267: return replace(replace(replace(replace(s, "&", "&"),
268: "<", "<"), ">", ">"), "\"", """);
269: }
270:
271: /** This method will add HTML line breaks in the input String at the specified limits.
272: * This is useful for wrapping long lines of text.
273: * @param s String to modify
274: * @param lineLimit The interval at which the line breaks will be added.
275: */
276: public static String addHTMLLineBreak(String s, int lineLimit) {
277: if (s != null && s.length() > lineLimit) {
278: int i = lineLimit;
279: while (s.length() > i) {
280: s = s.substring(0, i) + LINE_DELIMITER + s.substring(i);
281: i += lineLimit + LINE_DELIMITER.length();
282: }
283: }
284: return s;
285: }
286:
287: /** Basic find and replace on a string, it will replace ALL occurences of the string
288: * @param source original string
289: * @param find string to search for
290: * @param replace string to replace found strings with
291: * @return resultant string from the replace
292: */
293: public static String replace(String source, String find,
294: String replace) {
295: if (source == null)
296: return null;
297: StringBuffer sb = null;
298: int pos;
299: do {
300: if ((pos = source.indexOf(find)) < 0)
301: break; // No more matches
302: if (sb == null)
303: sb = new StringBuffer();
304: if (pos > 0)
305: sb.append(source.substring(0, pos));
306: sb.append(replace);
307: source = source.substring(pos + find.length());
308: } while (source.length() > 0);
309: if (sb == null)
310: return source;
311: else {
312: sb.append(source);
313: return sb.toString();
314: }
315: }
316:
317: /**
318: * A convenience method to translate the input String's 1st character to LowerCase,
319: * the rest of the string is left the same
320: *
321: * @param input the string to process
322: * @return the modified string
323: */
324: public static String getLower1(String input) {
325: if (input != null && input.length() > 0) {
326: return Character.toLowerCase(input.charAt(0))
327: + (input.length() == 1 ? "" : input.substring(1));
328: } else
329: return input;
330: }
331:
332: /**
333: * A convenience method to translate the input String's 1st character to UpperCase,
334: * the rest of the string is left the same
335: *
336: * @param input the string to process
337: * @return the modified string
338: */
339: public static String getUpper1(String input) {
340: if (input != null && input.length() > 0) {
341: return Character.toUpperCase(input.charAt(0))
342: + (input.length() == 1 ? "" : input.substring(1));
343: } else
344: return input;
345: }
346:
347: /**
348: * A convenience method to translate the input String to LowerCase
349: *
350: * @param input the string to process
351: * @return the modified string
352: */
353: public static String getLower(String input) {
354: if (input != null && input.length() > 0) {
355: return input.toLowerCase();
356: } else
357: return input;
358: }
359:
360: /**
361: * A convenience method to translate the input String to UpperCase
362: *
363: * @param input the string to process
364: * @return the modified string
365: */
366: public static String getUpper(String input) {
367: if (input != null && input.length() > 0) {
368: return input.toUpperCase();
369: } else
370: return input;
371: }
372:
373: /**
374: * A convenience method to translate the input String to all UpperCase with underscores separating the words
375: * For eg: "abcDef" would be translated to "ABC_DEF"
376: *
377: * @param input the string to process
378: * @return the modified string
379: */
380: public static String getStatic(String input) {
381: if (input != null && input.length() > 0) {
382: return replace(input, true, '_');
383: } else
384: return input;
385: }
386:
387: /**
388: * A convenience method to translate the input String to words separated by spaces
389: * For eg: "abcDef" would be translated to "abc Def"
390: *
391: * @param input the string to process
392: * @return the modified string
393: */
394: public static String getSpace(String input) {
395: if (input != null && input.length() > 0) {
396: return replace(input, false, ' ');
397: } else
398: return input;
399: }
400:
401: /** This returns the contents of a Reader in a StringBuffer.
402: * @param reader the input source.
403: * @throws IOException if any error occurs in reading the contents of the input.
404: * @return a StringBuffer with the contents of the source. A null is returned if the input is null.
405: */
406: public static StringBuffer getStringBuffer(Reader reader)
407: throws IOException {
408: StringBuffer buf = null;
409: if (reader != null) {
410: buf = new StringBuffer();
411: BufferedReader bReader = new BufferedReader(reader);
412: String str = bReader.readLine();
413: if (str != null) {
414: buf.append(str);
415: while ((str = bReader.readLine()) != null) {
416: buf.append('\n');
417: buf.append(str);
418: }
419: }
420: }
421: return buf;
422: }
423:
424: /** This returns the contents of a Reader as a String.
425: * @param reader the input source.
426: * @throws IOException if any error occurs in reading the contents of the input.
427: * @return a String with the contents of the source. A null is returned if the input is null.
428: */
429: public static String getString(Reader reader) throws IOException {
430: StringBuffer buf = getStringBuffer(reader);
431: if (buf == null)
432: return null;
433: else
434: return buf.toString();
435: }
436:
437: private static String replace(String input, boolean toUpper,
438: char separator) {
439: if (input != null && input.length() > 0) {
440: StringBuffer out = new StringBuffer();
441: for (int i = 0; i < input.length(); i++) {
442: char ch = input.charAt(i);
443: if (Character.isUpperCase(ch)) {
444: // do not append the separator for the 1st character !!!
445: if (i > 0)
446: out.append(separator);
447:
448: out.append(ch);
449: } else {
450: if (toUpper)
451: out.append(Character.toUpperCase(ch));
452: else
453: out.append(ch);
454: }
455: }
456: return out.toString();
457: } else
458: return input;
459: }
460:
461: /** This method is invoked in cases where 'description' field is appended to a 'code' field.
462: * This method formats the input dexcriptionField based on the passed layout.
463: * The layout can be passed directly or can be determined from the parameters domainClassWithPackage and domainField, through the appropriate FieldMetaData object.
464: * It then truncates the formatted String to the input limit. If the String is truncated, then the truncateIndicator will be appended.
465: * Finally the String will be packaged between the beginMarker and endMarker.
466: * If the toHtml flag is true, then the result will made HTML safe.
467: * This method will use the default values beginMarker=' (', endMarker=')', limit=25, truncateIndicator='...'
468: * @param field The description of the field.
469: * @param layout The layout, if any to be used for formatting
470: * @param domainClassWithPackage The domainClass to determine the FieldMetaData object, to get a handle on the layout.
471: * @param domainField The domainField to determine the FieldMetaData object, to get a handle on the layout.
472: * @param toHtml if true, then the output will be converted to HTML.
473: * @return the formatted string packed between the markers. An empty String will be returned, in case the input field is null.
474: */
475: public static String formatDescription(Object field, String layout,
476: String domainClassWithPackage, String domainField,
477: boolean toHtml) {
478: return formatDescription(field, layout, domainClassWithPackage,
479: domainField, toHtml, DESCRIPTION_BEGIN_MARKER,
480: DESCRIPTION_END_MARKER);
481: }
482:
483: /** This method is invoked in cases where 'description' field is appended to a 'code' field.
484: * This method formats the input dexcriptionField based on the passed layout.
485: * The layout can be passed directly or can be determined from the parameters domainClassWithPackage and domainField, through the appropriate FieldMetaData object.
486: * It then truncates the formatted String to the input limit. If the String is truncated, then the truncateIndicator will be appended.
487: * Finally the String will be packaged between the beginMarker and endMarker.
488: * If the toHtml flag is true, then the result will made HTML safe.
489: * This method will use the default values limit=25, truncateIndicator='...'
490: * @param field The description of the field.
491: * @param layout The layout, if any to be used for formatting
492: * @param domainClassWithPackage The domainClass to determine the FieldMetaData object, to get a handle on the layout.
493: * @param domainField The domainField to determine the FieldMetaData object, to get a handle on the layout.
494: * @param toHtml if true, then the output will be converted to HTML.
495: * @param beginMarker The marker at the start of the output. Default is ' ('
496: * @param endMarker The marker at the end of the output. Default is ')'
497: * @return the formatted string packed between the markers. An empty String will be returned, in case the input field is null.
498: */
499: public static String formatDescription(Object field, String layout,
500: String domainClassWithPackage, String domainField,
501: boolean toHtml, String beginMarker, String endMarker) {
502: return formatDescription(field, layout, domainClassWithPackage,
503: domainField, toHtml, beginMarker, endMarker,
504: DESCRIPTION_LIMIT, DESCRIPTION_TRUNCATE_INDICATOR);
505: }
506:
507: /** This method is invoked in cases where 'description' field is appended to a 'code' field.
508: * This method formats the input dexcriptionField based on the passed layout.
509: * The layout can be passed directly or can be determined from the parameters domainClassWithPackage and domainField, through the appropriate FieldMetaData object.
510: * It then truncates the formatted String to the input limit. If the String is truncated, then the truncateIndicator will be appended.
511: * No truncation will be performed if the limit <= 0
512: * Finally the String will be packaged between the beginMarker and endMarker.
513: * If the toHtml flag is true, then the result will made HTML safe.
514: * @param field The description of the field.
515: * @param layout The layout, if any to be used for formatting
516: * @param domainClassWithPackage The domainClass to determine the FieldMetaData object, to get a handle on the layout.
517: * @param domainField The domainField to determine the FieldMetaData object, to get a handle on the layout.
518: * @param toHtml if true, then the output will be converted to HTML.
519: * @param beginMarker The marker at the start of the output. Default is ' ('
520: * @param endMarker The marker at the end of the output. Default is ')'
521: * @param limit The limit for the formatted decription. Default is 25.
522: * @param truncateIndicator The string to append tot he formatted description, if exceeds the limit and is truncated.
523: * @return the formatted string packed between the markers. An empty String will be returned, in case the input field is null.
524: */
525: public static String formatDescription(Object field, String layout,
526: String domainClassWithPackage, String domainField,
527: boolean toHtml, String beginMarker, String endMarker,
528: int limit, String truncateIndicator) {
529: String out = null;
530: if (field == null)
531: out = "";
532: else {
533: StringBuffer buf = new StringBuffer();
534:
535: // determine the layout to use
536: if (layout == null && domainClassWithPackage != null
537: && domainField != null) {
538: try {
539: FieldMetaData meta = PersistentHelper
540: .getFieldMetaData(domainClassWithPackage,
541: domainField);
542: try {
543: layout = (String) BeanHelper.getField(meta,
544: "layout");
545: } catch (NoSuchMethodException e) {
546: // do nothing.. since the meta object may not have the getLayout() method !!!
547: }
548: } catch (Exception e) {
549: String str = "Exception thrown while determining the LabelToken for the MetaField "
550: + domainField;
551: log.error(str, e);
552: throw new RuntimeException(str, e);
553: }
554: }
555:
556: // Format the field
557: String str = null;
558: if (layout != null)
559: str = Formatter.format(field, layout);
560: else
561: str = Formatter.format(field);
562:
563: // Build the Output
564: if (str != null && str.length() > 0) {
565: if (beginMarker != null)
566: buf.append(beginMarker);
567:
568: // Truncate the formatted string, if needed, and append the truncateIndicator
569: if (limit > 0 && str.length() > limit) {
570: buf.append(str.substring(0, limit - 1));
571: if (truncateIndicator != null)
572: buf.append(truncateIndicator);
573: } else {
574: buf.append(str);
575: }
576:
577: if (endMarker != null)
578: buf.append(endMarker);
579: }
580:
581: out = buf.toString();
582: if (toHtml)
583: out = org.jaffa.util.StringHelper.convertToHTML(out);
584: }
585: return out;
586: }
587:
588: /** Returns the default String used in the formatDescription() methods placed at the beginning.
589: * @return the default String used in the formatDescription() methods placed at the beginning.
590: */
591: public static String getDefaultDescriptionBeginMarker() {
592: return DESCRIPTION_BEGIN_MARKER;
593: }
594:
595: /** Returns the default String used in the formatDescription() methods placed at the end.
596: * @return the default String used in the formatDescription() methods placed at the end.
597: */
598: public static String getDefaultDescriptionEndMarker() {
599: return DESCRIPTION_END_MARKER;
600: }
601:
602: /** This is a helper for combining old and additional comments.
603: * A stamp containing the userId and current time will be inserted before the additional comment.
604: * The additional comment will be inserted before the old comment if 'lifo' is true, otherwise it'll be appended.
605: * If the input userId is null, then the userId will be obtained from the SecurityManager.
606: * @param originalComment The original comment.
607: * @param additionalComment The additional comment.
608: * @param lifo This determines if the additional comment is inserted before or appended after the old comment.
609: * @param userId The userId.
610: * @return The combination of the old comment and the additional comment.
611: */
612: public static String addCommentWithStamp(String originalComment,
613: String additionalComment, boolean lifo, String userId) {
614: if (additionalComment == null
615: || additionalComment.trim().length() == 0)
616: return originalComment;
617:
618: // create a buffer for the generated comment, by estimating an initial capacity
619: int capacity = (originalComment != null ? originalComment
620: .length() : 0)
621: + (additionalComment != null ? additionalComment
622: .length() : 0) + 100;
623: StringBuffer buffer = new StringBuffer(capacity);
624:
625: // create the stamp
626: if (originalComment != null)
627: buffer.append("------- Additional ");
628: else
629: buffer.append("------- ");
630: buffer.append("Comments ");
631: if (userId == null && SecurityManager.getPrincipal() != null)
632: userId = SecurityManager.getPrincipal().getName();
633: if (userId != null) {
634: buffer.append("From ");
635: buffer.append(userId);
636: buffer.append(' ');
637: }
638: buffer.append(Formatter.format(new DateTime()));
639: buffer.append(" -------");
640: buffer.append('\n');
641:
642: // add the new comments
643: buffer.append(additionalComment);
644:
645: // add the original comments before or after the new comments, based on the 'lifo' flag
646: if (originalComment != null) {
647: if (lifo) {
648: buffer.append('\n');
649: buffer.append('\n');
650: buffer.append(originalComment);
651: } else {
652: buffer.insert(0, '\n');
653: buffer.insert(0, '\n');
654: buffer.insert(0, originalComment);
655: }
656: }
657:
658: return buffer.toString();
659: }
660:
661: /** Returns the EOL character for the input String, if any.
662: * The EOL character can be '\r', '\n' or '\r\n'.
663: * @return the EOL character for the input String. A null string will be returned in case no EOL charcater is found.
664: */
665: public static String findEol(String input) {
666: String output = null;
667: if (input != null) {
668: if (input.endsWith("\r\n"))
669: output = "\r\n";
670: else if (input.endsWith("\n"))
671: output = "\n";
672: else if (input.endsWith("\r"))
673: output = "\r";
674: }
675: return output;
676: }
677:
678: /** Reads a line from the input reader.
679: * It'll return an instance of StringHelper.Line object, which has the contents and EOL of the line.
680: * The EOL character can be '\r', '\n' or '\r\n'.
681: * This method is used instead of the readLine() method of the BufferedReader, since there is no other way of finding out if the last line in a file ends with an EOL.
682: * This also has the added benefit of knowing the exact EOL character.
683: * A null will be returned if the EOF is reached.
684: * @return a StringHelper.Line object containing the line contents and the EOL character. A null is returned if the end of file is reached.
685: * @throws IOException if any IO error occurs.
686: */
687: public static Line readLine(PushbackReader reader)
688: throws IOException {
689: Line line = null;
690: StringBuffer contents = new StringBuffer();
691: StringBuffer eol = new StringBuffer();
692: int i;
693: while ((i = reader.read()) != -1) {
694: char ch = (char) i;
695: if (ch == '\r') {
696: eol.append(ch);
697: if ((i = reader.read()) != -1) {
698: ch = (char) i;
699: if (ch == '\n')
700: eol.append(ch);
701: else
702: reader.unread(i);
703: }
704: break;
705: } else if (ch == '\n') {
706: eol.append(ch);
707: break;
708: } else {
709: contents.append(ch);
710: }
711: }
712: if (contents.length() > 0 || eol.length() > 0) {
713: line = new Line();
714: line.m_contents = contents.toString();
715: line.m_eol = eol.toString();
716: }
717: return line;
718: }
719:
720: /** This class contains the contents of a line and the EOL character.
721: * The EOL character can be '\n', '\r' or '\r\n'.
722: */
723: public static class Line {
724: private String m_contents;
725: private String m_eol;
726:
727: /** Returns the contents of the line.
728: * @return The contents.
729: */
730: public String getContents() {
731: return m_contents;
732: }
733:
734: /** Returns the EOL (End of Line) character of the line.
735: * @return The EOL.
736: */
737: public String getEol() {
738: return m_eol;
739: }
740:
741: /** Returns a String by concatenating the contents and the EOL character.
742: * @return a String by concatenating the contents and the EOL character.
743: */
744: public String toString() {
745: StringBuffer buf = new StringBuffer();
746: if (m_contents != null)
747: buf.append(m_contents);
748: if (m_eol != null)
749: buf.append(m_eol);
750: return buf.toString();
751: }
752: }
753:
754: /** Test Routine
755: * @param args no args required
756: */
757: public static void main(String[] args) {
758: // @todo: Main method used to run simple tests, should be moved to unit test package
759: String source, find, replace;
760:
761: System.out.println("\nSearch/Replace Test\n");
762: source = "To be or not to be, that is the question";
763: find = "be";
764: replace = "exist";
765: System.out.println("Source:" + source);
766: System.out.println("Find:" + find);
767: System.out.println("Replace:" + replace);
768: System.out.println("Result:" + replace(source, find, replace));
769:
770: System.out.println("\nXML Decode Test\n");
771: source = "< '1' & '2' = "e;3"e; >";
772: System.out.println("Source:" + source);
773: System.out.println("Result:" + convertFromHTML(source));
774:
775: System.out.println("\nHTML Encode Test\n");
776: source = "< '1' & '2' = \"3\">";
777: System.out.println("Source:" + source);
778: System.out.println("Result:" + convertToHTML(source));
779:
780: System.out.println("\nLine Pad Test 1\n");
781: source = "Every Rose Has A Thorn\nEvery Cowboy\nSings A Sad, Sad Song";
782: System.out.println(linePad(source, 3));
783: System.out.println(linePad(source, 2, "\t"));
784: System.out.println(linePad(source, 4, "-", true));
785: }
786: }
|