001: /*BEGIN_COPYRIGHT_BLOCK
002: *
003: * Copyright (c) 2001-2007, JavaPLT group at Rice University (javaplt@rice.edu)
004: * All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are met:
008: * * Redistributions of source code must retain the above copyright
009: * notice, this list of conditions and the following disclaimer.
010: * * Redistributions in binary form must reproduce the above copyright
011: * notice, this list of conditions and the following disclaimer in the
012: * documentation and/or other materials provided with the distribution.
013: * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
014: * names of its contributors may be used to endorse or promote products
015: * derived from this software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
018: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
019: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
020: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
021: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
023: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: *
029: * This software is Open Source Initiative approved Open Source Software.
030: * Open Source Initative Approved is a trademark of the Open Source Initiative.
031: *
032: * This file is part of DrJava. Download the current version of this project
033: * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
034: *
035: * END_COPYRIGHT_BLOCK*/
036:
037: package edu.rice.cs.util;
038:
039: import edu.rice.cs.plt.tuple.Pair;
040: import java.io.StringWriter;
041: import java.io.PrintWriter;
042: import java.text.DecimalFormat;
043:
044: /**
045: * A class to provide some convenient String operations as static methods.
046: * It's abstract to prevent (useless) instantiation, though it can be subclassed
047: * to provide convenient namespace importation of its methods.
048: * @version $Id: StringOps.java 4255 2007-08-28 19:17:37Z mgricken $
049: */
050:
051: public abstract class StringOps {
052:
053: public static final String EOL = System
054: .getProperty("line.separator");
055:
056: /** Takes theString fullString and replaces all instances of toReplace with replacement.
057: * TODO: deprecate and used corresponding String method added in Java 5.0.
058: */
059: public static String replace(String fullString, String toReplace,
060: String replacement) {
061: int index = 0;
062: int pos;
063: int fullStringLength = fullString.length();
064: int toReplaceLength = toReplace.length();
065: if (toReplaceLength > 0) {
066: int replacementLength = replacement.length();
067: StringBuilder buff;
068: while (index < fullStringLength
069: && ((pos = fullString.indexOf(toReplace, index)) >= 0)) {
070: buff = new StringBuilder(fullString.substring(0, pos));
071: buff.append(replacement);
072: buff.append(fullString.substring(pos + toReplaceLength,
073: fullStringLength));
074: index = pos + replacementLength;
075: fullString = buff.toString();
076: fullStringLength = fullString.length();
077: }
078: }
079: return fullString;
080: }
081:
082: /**
083: * Converts the given string to a valid Java string literal.
084: * All back slashes, quotes, new-lines, and tabs are converted
085: * to their escap character form, and the sourounding quotes
086: * are added.
087: * @param s the normal string to turn into a string literal
088: * @return the valid Java string literal
089: */
090: public static String convertToLiteral(String s) {
091: String output = s;
092: output = replace(output, "\\", "\\\\"); // convert \ to \\
093: output = replace(output, "\"", "\\\""); // convert " to \"
094: output = replace(output, "\t", "\\t"); // convert [tab] to \t
095: output = replace(output, "\n", "\\n"); // convert [newline] to \n
096: return "\"" + output + "\"";
097: }
098:
099: /**
100: * Verifies that (startRow, startCol) occurs before (endRow, endCol).
101: * @throws IllegalArgumentException if end is before start
102: */
103: private static void _ensureStartBeforeEnd(int startRow,
104: int startCol, int endRow, int endCol) {
105: if (startRow > endRow) {
106: throw new IllegalArgumentException(
107: "end row before start row: " + startRow + " > "
108: + endRow);
109: } else if (startRow == endRow && startCol > endCol) {
110: throw new IllegalArgumentException("end before start: ("
111: + startRow + ", " + startCol + ") > (" + endRow
112: + ", " + endCol + ")");
113: }
114: }
115:
116: /**
117: * Verifies that the given column position is within the row at rowStartIndex
118: * in the given String.
119: * @param fullString the string in which to check the column
120: * @param col the column index that should be within the row
121: * @param rowStartIndex the first index of the row within fullString that col should be in
122: * @throws IllegalArgumentException if col is after the end of the given row
123: */
124: private static void _ensureColInRow(String fullString, int col,
125: int rowStartIndex) {
126: int endOfLine = fullString.indexOf("\n", rowStartIndex);
127: if (endOfLine == -1) {
128: endOfLine = fullString.length();
129: }
130: if (col > (endOfLine - rowStartIndex)) {
131: throw new IllegalArgumentException(
132: "the given column is past the end of its row");
133: }
134: }
135:
136: /**
137: * Gets the offset and length equivalent to the given pairs start and end row-col.
138: * @param fullString the string in which to compute the offset/length
139: * @param startRow the row on which the error starts, starting at one for the first row
140: * @param startCol the col on which the error starts, starting at one for the first column
141: * @param endRow the row on which the error ends. Equals the startRow for one-line errors
142: * @param endCol the character position on which the error ends.
143: * Equals the startCol for one-character errors
144: * @return a Pair of which the first is the offset, the second is the length
145: */
146: public static Pair<Integer, Integer> getOffsetAndLength(
147: String fullString, int startRow, int startCol, int endRow,
148: int endCol) {
149: _ensureStartBeforeEnd(startRow, startCol, endRow, endCol);
150:
151: // find the offset
152: int currentChar = 0;
153: int linesSeen = 1;
154: while (startRow > linesSeen) {
155: currentChar = fullString.indexOf("\n", currentChar);
156: if (currentChar == -1) {
157: throw new IllegalArgumentException(
158: "startRow is beyond the end of the string");
159: }
160: // Must move past the newline
161: currentChar++;
162: linesSeen++;
163: }
164:
165: _ensureColInRow(fullString, startCol, currentChar);
166: int offset = currentChar + startCol - 1; // offset is zero-based
167:
168: // find the length
169: while (endRow > linesSeen) {
170: currentChar = fullString.indexOf("\n", currentChar);
171: if (currentChar == -1) {
172: throw new IllegalArgumentException(
173: "endRow is beyond the end of the string");
174: }
175: currentChar++;
176: linesSeen++;
177: }
178:
179: _ensureColInRow(fullString, endCol, currentChar);
180: int length = currentChar + endCol - offset;
181:
182: // ensure the length is in bounds
183: if (offset + length > fullString.length()) {
184: throw new IllegalArgumentException(
185: "Given positions beyond the end of the string");
186: }
187: return new Pair<Integer, Integer>(new Integer(offset),
188: new Integer(length));
189: }
190:
191: /**
192: * Gets the stack trace of the given Throwable as a String.
193: * @param t the throwable object for which to get the stack trace
194: * @return the stack trace of the given Throwable
195: */
196: public static String getStackTrace(Throwable t) {
197: StringWriter sw = new StringWriter();
198: PrintWriter pw = new PrintWriter(sw);
199: t.printStackTrace(pw);
200: return sw.toString();
201: }
202:
203: /**
204: * Gets the stack trace of the current code. Does not include this method.
205: * @return the stack trace for the current code
206: */
207: public static String getStackTrace() {
208: try {
209: throw new Exception();
210: } // Thread.getStackTrace() might be more efficient, but is new in Java 5.0
211: catch (Exception e) {
212: StringWriter sw = new StringWriter();
213: PrintWriter pw = new PrintWriter(sw);
214: StackTraceElement[] stes = e.getStackTrace();
215: int skip = 1;
216: for (StackTraceElement ste : stes) {
217: if (skip > 0) {
218: --skip;
219: } else {
220: pw.print("at ");
221: pw.println(ste);
222: }
223: }
224: return sw.toString();
225: }
226: }
227:
228: /**
229: * Character.isDigit answers <tt>true</tt> to some non-ascii
230: * digits. This one does not.
231: */
232: public static boolean isAsciiDigit(char c) {
233: return '0' <= c && c <= '9';
234: }
235:
236: /**
237: * Returns true if the class is an anonymous inner class.
238: * This works just like Class.isAnonymousClass() in Java 5.0 but is not version-specific.
239: * @param c class to check
240: * @return true if anonymous inner class
241: */
242: public static boolean isAnonymousClass(Class c) {
243: String simpleName = c.getName();
244: int idx = simpleName.lastIndexOf('$');
245: if (idx >= 0) {
246: // see if we have just numbers after the $
247: for (int pos = idx + 1; pos < simpleName.length(); ++pos) {
248: if (!isAsciiDigit(simpleName.charAt(pos))) {
249: return false;
250: }
251: }
252: return true;
253: }
254: return false;
255: }
256:
257: /**
258: * Returns true if the class is a member class.
259: * This works just like Class.isMemberClass() in Java 5.0 but is not version-specific.
260: * @param c class to check
261: * @return true if member class
262: */
263: public static boolean isMemberClass(Class c) {
264: String simpleName = c.getName();
265: int idx = simpleName.lastIndexOf('$');
266: if (idx == -1) {
267: return false;
268: }
269: return !isAnonymousClass(c);
270: }
271:
272: /**
273: * Returns the simple class name.
274: * This works just like Class.getSimpleName() in Java 5.0 but is not version-specific.
275: * @param c class for which to get the simple name
276: * @return simple name
277: */
278: public static String getSimpleName(Class c) {
279: if (c.isArray())
280: return getSimpleName(c.getComponentType()) + "[]";
281:
282: if (isAnonymousClass(c)) {
283: return "";
284: }
285:
286: String simpleName = c.getName();
287: int idx = Math.max(simpleName.lastIndexOf('.'), simpleName
288: .lastIndexOf('$'));
289: return simpleName.substring(idx + 1); // strip the package name
290: }
291:
292: /**
293: * This works just like java.util.Arrays.toString in Java 5.0 but is not version-specific.
294: */
295: public static String toString(long[] a) {
296: if (a == null)
297: return "null";
298: if (a.length == 0)
299: return "[]";
300:
301: final StringBuilder buf = new StringBuilder();
302: buf.append('[');
303: buf.append(a[0]);
304:
305: for (int i = 1; i < a.length; i++) {
306: buf.append(", ");
307: buf.append(a[i]);
308: }
309:
310: buf.append("]");
311: return buf.toString();
312: }
313:
314: /**
315: * This works just like java.util.Arrays.toString in Java 5.0 but is not version-specific.
316: */
317: public static String toString(int[] a) {
318: if (a == null)
319: return "null";
320: if (a.length == 0)
321: return "[]";
322:
323: final StringBuilder buf = new StringBuilder();
324: buf.append('[');
325: buf.append(a[0]);
326:
327: for (int i = 1; i < a.length; i++) {
328: buf.append(", ");
329: buf.append(a[i]);
330: }
331:
332: buf.append("]");
333: return buf.toString();
334: }
335:
336: /**
337: * This works just like java.util.Arrays.toString in Java 5.0 but is not version-specific.
338: */
339: public static String toString(short[] a) {
340: if (a == null)
341: return "null";
342: if (a.length == 0)
343: return "[]";
344:
345: final StringBuilder buf = new StringBuilder();
346: buf.append('[');
347: buf.append(a[0]);
348:
349: for (int i = 1; i < a.length; i++) {
350: buf.append(", ");
351: buf.append(a[i]);
352: }
353:
354: buf.append("]");
355: return buf.toString();
356: }
357:
358: /**
359: * This works just like java.util.Arrays.toString in Java 5.0 but is not version-specific.
360: */
361: public static String toString(char[] a) {
362: if (a == null)
363: return "null";
364: if (a.length == 0)
365: return "[]";
366:
367: final StringBuilder buf = new StringBuilder();
368: buf.append('[');
369: buf.append(a[0]);
370:
371: for (int i = 1; i < a.length; i++) {
372: buf.append(", ");
373: buf.append(a[i]);
374: }
375:
376: buf.append("]");
377: return buf.toString();
378: }
379:
380: /**
381: * This works just like java.util.Arrays.toString in Java 5.0 but is not version-specific.
382: */
383: public static String toString(byte[] a) {
384: if (a == null)
385: return "null";
386: if (a.length == 0)
387: return "[]";
388:
389: final StringBuilder buf = new StringBuilder();
390: buf.append('[');
391: buf.append(a[0]);
392:
393: for (int i = 1; i < a.length; i++) {
394: buf.append(", ");
395: buf.append(a[i]);
396: }
397:
398: buf.append("]");
399: return buf.toString();
400: }
401:
402: /**
403: * This works just like java.util.Arrays.toString in Java 5.0 but is not version-specific.
404: */
405: public static String toString(boolean[] a) {
406: if (a == null)
407: return "null";
408: if (a.length == 0)
409: return "[]";
410:
411: final StringBuilder buf = new StringBuilder();
412: buf.append('[');
413: buf.append(a[0]);
414:
415: for (int i = 1; i < a.length; i++) {
416: buf.append(", ");
417: buf.append(a[i]);
418: }
419:
420: buf.append("]");
421: return buf.toString();
422: }
423:
424: /**
425: * This works just like java.util.Arrays.toString in Java 5.0 but is not version-specific.
426: */
427: public static String toString(float[] a) {
428: if (a == null)
429: return "null";
430: if (a.length == 0)
431: return "[]";
432:
433: final StringBuilder buf = new StringBuilder();
434: buf.append('[');
435: buf.append(a[0]);
436:
437: for (int i = 1; i < a.length; i++) {
438: buf.append(", ");
439: buf.append(a[i]);
440: }
441:
442: buf.append("]");
443: return buf.toString();
444: }
445:
446: /**
447: * This works just like java.util.Arrays.toString in Java 5.0 but is not version-specific.
448: */
449: public static String toString(double[] a) {
450: if (a == null)
451: return "null";
452: if (a.length == 0)
453: return "[]";
454:
455: final StringBuilder buf = new StringBuilder();
456: buf.append('[');
457: buf.append(a[0]);
458:
459: for (int i = 1; i < a.length; i++) {
460: buf.append(", ");
461: buf.append(a[i]);
462: }
463:
464: buf.append("]");
465: return buf.toString();
466: }
467:
468: /**
469: * This works just like java.util.Arrays.toString in Java 5.0 but is not version-specific.
470: */
471: public static String toString(Object[] a) {
472: if (a == null)
473: return "null";
474: if (a.length == 0)
475: return "[]";
476:
477: final StringBuilder buf = new StringBuilder();
478:
479: for (int i = 0; i < a.length; i++) {
480: if (i == 0)
481: buf.append('[');
482: else
483: buf.append(", ");
484:
485: buf.append(String.valueOf(a[i]));
486: }
487:
488: buf.append("]");
489: return buf.toString();
490: }
491:
492: /**
493: * Encode &, <, > and newlines as HTML entities.
494: * @param s string to encode
495: * @return encoded string
496: */
497: public static String encodeHTML(String s) {
498: s = StringOps.replace(s, "&", "&");
499: s = StringOps.replace(s, "<", "<");
500: s = StringOps.replace(s, ">", ">");
501: s = StringOps.replace(s, EOL, "<br>");
502: s = StringOps.replace(s, "\n", "<br>");
503: return s;
504: }
505:
506: /**
507: * Return a string representing the approximate amount of memory specified in bytes.
508: * @param l memory in bytes
509: * @return string approximating the amount of memory
510: */
511: public static String memSizeToString(long l) {
512: String[] sizes = new String[] { "byte", "kilobyte", "megabyte",
513: "gigabyte" };
514: double d = l;
515: int i = 0;
516: while ((d >= 1024) && (i < sizes.length)) {
517: ++i;
518: d /= 1024;
519: }
520: if (i >= sizes.length) {
521: i = sizes.length - 1;
522: }
523: StringBuilder sb = new StringBuilder();
524: long whole = (long) d;
525: if (whole == d) {
526: if (whole == 1) {
527: sb.append(whole);
528: sb.append(' ');
529: sb.append(sizes[i]);
530: } else {
531: sb.append(whole);
532: sb.append(' ');
533: sb.append(sizes[i]);
534: sb.append('s');
535: }
536: } else {
537: // two decimal digits
538: DecimalFormat df = new DecimalFormat("#.00");
539: sb.append(df.format(d));
540: sb.append(' ');
541: sb.append(sizes[i]);
542: sb.append('s');
543: }
544: return sb.toString();
545: }
546: }
|