001 // If you edit this file, you must also edit its tests.
002 // For tests of this and the entire plume package, see class TestPlume.
003
004 package daikon.util;
005
006 import java.io.*;
007 import java.util.*;
008 import java.util.zip.*;
009 import java.lang.reflect.*;
010 // import Assert;
011
012 /** Utility functions that do not belong elsewhere in the plume package. */
013 public final class UtilMDE {
014 private UtilMDE() { throw new Error("do not instantiate"); }
015
016 @SuppressWarnings("nullness") // line.separator property always exists
017 private static final String lineSep = System.getProperty("line.separator");
018
019 ///////////////////////////////////////////////////////////////////////////
020 /// Array
021 ///
022
023 // For arrays, see ArraysMDE.java.
024
025 ///////////////////////////////////////////////////////////////////////////
026 /// BitSet
027 ///
028
029 /**
030 * Returns true if the cardinality of the intersection of the two
031 * BitSets is at least the given value.
032 **/
033 public static boolean intersectionCardinalityAtLeast(BitSet a, BitSet b, int i) {
034 // Here are three implementation strategies to determine the
035 // cardinality of the intersection:
036 // 1. a.clone().and(b).cardinality()
037 // 2. do the above, but copy only a subset of the bits initially -- enough
038 // that it should exceed the given number -- and if that fails, do the
039 // whole thing. Unfortunately, bits.get(int, int) isn't optimized
040 // for the case where the indices line up, so I'm not sure at what
041 // point this approach begins to dominate #1.
042 // 3. iterate through both sets with nextSetBit()
043
044 int size = Math.min(a.length(), b.length());
045 if (size > 10*i) {
046 // The size is more than 10 times the limit. So first try processing
047 // just a subset of the bits (4 times the limit).
048 BitSet intersection = a.get(0, 4*i);
049 intersection.and(b);
050 if (intersection.cardinality() >= i) {
051 return true;
052 }
053 }
054 return (intersectionCardinality(a, b) >= i);
055 }
056
057 /**
058 * Returns true if the cardinality of the intersection of the two
059 * BitSets is at least the given value.
060 **/
061 public static boolean intersectionCardinalityAtLeast(BitSet a, BitSet b, BitSet c, int i) {
062 // See comments in intersectionCardinalityAtLeast(BitSet, BitSet, int).
063 // This is a copy of that.
064
065 int size = Math.min(a.length(), b.length());
066 size = Math.min(size, c.length());
067 if (size > 10*i) {
068 // The size is more than 10 times the limit. So first try processing
069 // just a subset of the bits (4 times the limit).
070 BitSet intersection = a.get(0, 4*i);
071 intersection.and(b);
072 intersection.and(c);
073 if (intersection.cardinality() >= i) {
074 return true;
075 }
076 }
077 return (intersectionCardinality(a, b, c) >= i);
078 }
079
080 /** Returns the cardinality of the intersection of the two BitSets. **/
081 public static int intersectionCardinality(BitSet a, BitSet b) {
082 BitSet intersection = (BitSet) a.clone();
083 intersection.and(b);
084 return intersection.cardinality();
085 }
086
087 /** Returns the cardinality of the intersection of the three BitSets. **/
088 public static int intersectionCardinality(BitSet a, BitSet b, BitSet c) {
089 BitSet intersection = (BitSet) a.clone();
090 intersection.and(b);
091 intersection.and(c);
092 return intersection.cardinality();
093 }
094
095
096 ///////////////////////////////////////////////////////////////////////////
097 /// BufferedFileReader
098 ///
099
100
101 // Convenience methods for creating InputStreams, Readers, BufferedReaders, and LineNumberReaders.
102
103 /**
104 * Returns an InputStream for the file, accounting for the possibility
105 * that the file is compressed.
106 * (A file whose name ends with ".gz" is treated as compressed.)
107 * <p>
108 * Warning: The "gzip" program writes and reads files containing
109 * concatenated gzip files. As of Java 1.4, Java reads
110 * just the first one: it silently discards all characters (including
111 * gzipped files) after the first gzipped file.
112 */
113 public static InputStream fileInputStream(File file) throws IOException {
114 InputStream in;
115 if (file.getName().endsWith(".gz")) {
116 in = new GZIPInputStream(new FileInputStream(file));
117 } else {
118 in = new FileInputStream(file);
119 }
120 return in;
121 }
122
123 /**
124 * Returns a Reader for the file, accounting for the possibility
125 * that the file is compressed.
126 * (A file whose name ends with ".gz" is treated as compressed.)
127 * <p>
128 * Warning: The "gzip" program writes and reads files containing
129 * concatenated gzip files. As of Java 1.4, Java reads
130 * just the first one: it silently discards all characters (including
131 * gzipped files) after the first gzipped file.
132 **/
133 public static InputStreamReader fileReader(String filename) throws FileNotFoundException, IOException {
134 // return fileReader(filename, "ISO-8859-1");
135 return fileReader(new File(filename), null);
136 }
137
138 /**
139 * Returns a Reader for the file, accounting for the possibility
140 * that the file is compressed.
141 * (A file whose name ends with ".gz" is treated as compressed.)
142 * <p>
143 * Warning: The "gzip" program writes and reads files containing
144 * concatenated gzip files. As of Java 1.4, Java reads
145 * just the first one: it silently discards all characters (including
146 * gzipped files) after the first gzipped file.
147 **/
148 public static InputStreamReader fileReader(File file) throws FileNotFoundException, IOException {
149 return fileReader(file, null);
150 }
151
152
153 /**
154 * Returns a Reader for the file, accounting for the possibility
155 * that the file is compressed.
156 * (A file whose name ends with ".gz" is treated as compressed.)
157 * @param charsetName may be null, or the name of a Charset
158 * <p>
159 * Warning: The "gzip" program writes and reads files containing
160 * concatenated gzip files. As of Java 1.4, Java reads
161 * just the first one: it silently discards all characters (including
162 * gzipped files) after the first gzipped file.
163 **/
164 public static InputStreamReader fileReader(File file, /*@Nullable*/ String charsetName) throws FileNotFoundException, IOException {
165 InputStream in = new FileInputStream(file);
166 InputStreamReader file_reader;
167 if (charsetName == null) {
168 file_reader = new InputStreamReader(in);
169 } else {
170 file_reader = new InputStreamReader(in, charsetName);
171 }
172 return file_reader;
173 }
174
175 /**
176 * Returns a BufferedReader for the file, accounting for the possibility
177 * that the file is compressed.
178 * (A file whose name ends with ".gz" is treated as compressed.)
179 * <p>
180 * Warning: The "gzip" program writes and reads files containing
181 * concatenated gzip files. As of Java 1.4, Java reads
182 * just the first one: it silently discards all characters (including
183 * gzipped files) after the first gzipped file.
184 **/
185 public static BufferedReader bufferedFileReader(String filename) throws FileNotFoundException, IOException {
186 return bufferedFileReader(new File(filename));
187 }
188
189 /**
190 * Returns a BufferedReader for the file, accounting for the possibility
191 * that the file is compressed.
192 * (A file whose name ends with ".gz" is treated as compressed.)
193 * <p>
194 * Warning: The "gzip" program writes and reads files containing
195 * concatenated gzip files. As of Java 1.4, Java reads
196 * just the first one: it silently discards all characters (including
197 * gzipped files) after the first gzipped file.
198 **/
199 public static BufferedReader bufferedFileReader(File file) throws FileNotFoundException, IOException {
200 return(bufferedFileReader(file, null));
201 }
202
203 /**
204 * Returns a BufferedReader for the file, accounting for the possibility
205 * that the file is compressed.
206 * (A file whose name ends with ".gz" is treated as compressed.)
207 * <p>
208 * Warning: The "gzip" program writes and reads files containing
209 * concatenated gzip files. As of Java 1.4, Java reads
210 * just the first one: it silently discards all characters (including
211 * gzipped files) after the first gzipped file.
212 **/
213 public static BufferedReader bufferedFileReader(String filename, /*@Nullable*/ String charsetName) throws FileNotFoundException, IOException {
214 return bufferedFileReader(new File(filename), charsetName);
215 }
216
217 /**
218 * Returns a BufferedReader for the file, accounting for the possibility
219 * that the file is compressed.
220 * (A file whose name ends with ".gz" is treated as compressed.)
221 * <p>
222 * Warning: The "gzip" program writes and reads files containing
223 * concatenated gzip files. As of Java 1.4, Java reads
224 * just the first one: it silently discards all characters (including
225 * gzipped files) after the first gzipped file.
226 **/
227 public static BufferedReader bufferedFileReader(File file, /*@Nullable*/ String charsetName) throws FileNotFoundException, IOException {
228 Reader file_reader = fileReader(file, charsetName);
229 return new BufferedReader(file_reader);
230 }
231
232
233 /**
234 * Returns a LineNumberReader for the file, accounting for the possibility
235 * that the file is compressed.
236 * (A file whose name ends with ".gz" is treated as compressed.)
237 * <p>
238 * Warning: The "gzip" program writes and reads files containing
239 * concatenated gzip files. As of Java 1.4, Java reads
240 * just the first one: it silently discards all characters (including
241 * gzipped files) after the first gzipped file.
242 **/
243 public static LineNumberReader lineNumberFileReader(String filename) throws FileNotFoundException, IOException {
244 return lineNumberFileReader(new File(filename));
245 }
246
247 /**
248 * Returns a LineNumberReader for the file, accounting for the possibility
249 * that the file is compressed.
250 * (A file whose name ends with ".gz" is treated as compressed.)
251 * <p>
252 * Warning: The "gzip" program writes and reads files containing
253 * concatenated gzip files. As of Java 1.4, Java reads
254 * just the first one: it silently discards all characters (including
255 * gzipped files) after the first gzipped file.
256 **/
257 public static LineNumberReader lineNumberFileReader(File file) throws FileNotFoundException, IOException {
258 Reader file_reader;
259 if (file.getName().endsWith(".gz")) {
260 file_reader = new InputStreamReader(new GZIPInputStream(new FileInputStream(file)),
261 "ISO-8859-1");
262 } else {
263 file_reader = new InputStreamReader(new FileInputStream(file),
264 "ISO-8859-1");
265 }
266 return new LineNumberReader(file_reader);
267 }
268
269 /**
270 * Returns a BufferedWriter for the file, accounting for the possibility
271 * that the file is compressed.
272 * (A file whose name ends with ".gz" is treated as compressed.)
273 * <p>
274 * Warning: The "gzip" program writes and reads files containing
275 * concatenated gzip files. As of Java 1.4, Java reads
276 * just the first one: it silently discards all characters (including
277 * gzipped files) after the first gzipped file.
278 **/
279 public static BufferedWriter bufferedFileWriter(String filename) throws IOException {
280 return bufferedFileWriter (filename, false);
281 }
282
283 /**
284 * Returns a BufferedWriter for the file, accounting for the possibility
285 * that the file is compressed.
286 * (A file whose name ends with ".gz" is treated as compressed.)
287 * <p>
288 * Warning: The "gzip" program writes and reads files containing
289 * concatenated gzip files. As of Java 1.4, Java reads
290 * just the first one: it silently discards all characters (including
291 * gzipped files) after the first gzipped file.
292 * @param append if true, the resulting BufferedWriter appends to the end
293 * of the file instead of the beginning.
294 **/
295 public static BufferedWriter bufferedFileWriter(String filename, boolean append) throws IOException {
296 Writer file_writer;
297 if (filename.endsWith(".gz")) {
298 file_writer = new OutputStreamWriter(new GZIPOutputStream(new FileOutputStream(filename, append)));
299 } else {
300 file_writer = new FileWriter(filename, append);
301 }
302 return new BufferedWriter(file_writer);
303 }
304
305
306 /** @deprecated use bufferedFileReader (note lowercase first letter) */
307 @Deprecated // since June 2005
308 public static BufferedReader BufferedFileReader(String filename) throws FileNotFoundException, IOException {
309 return bufferedFileReader(filename);
310 }
311 /** @deprecated use lineNumberFileReader (note lowercase first letter) */
312 @Deprecated // since June 2005
313 public static LineNumberReader LineNumberFileReader(String filename) throws FileNotFoundException, IOException {
314 return lineNumberFileReader(filename);
315 }
316 /** @deprecated use lineNumberFileReader (note lowercase first letter) */
317 @Deprecated // since June 2005
318 public static LineNumberReader LineNumberFileReader(File file) throws FileNotFoundException, IOException {
319 return lineNumberFileReader(file);
320 }
321 /** @deprecated use bufferedFileWriter (note lowercase first letter) */
322 @Deprecated // since June 2005
323 public static BufferedWriter BufferedFileWriter(String filename) throws IOException {
324 return bufferedFileWriter(filename);
325 }
326 /** @deprecated use bufferedFileWriter (note lowercase first letter) */
327 @Deprecated // since June 2005
328 public static BufferedWriter BufferedFileWriter(String filename, boolean append) throws IOException {
329 return bufferedFileWriter(filename, append);
330 }
331
332
333 ///////////////////////////////////////////////////////////////////////////
334 /// Class
335 ///
336
337 private static HashMap<String,Class<?>> primitiveClasses = new HashMap<String,Class<?>>(8);
338 static {
339 primitiveClasses.put("boolean", Boolean.TYPE);
340 primitiveClasses.put("byte", Byte.TYPE);
341 primitiveClasses.put("char", Character.TYPE);
342 primitiveClasses.put("double", Double.TYPE);
343 primitiveClasses.put("float", Float.TYPE);
344 primitiveClasses.put("int", Integer.TYPE);
345 primitiveClasses.put("long", Long.TYPE);
346 primitiveClasses.put("short", Short.TYPE);
347 }
348
349 /**
350 * Like @link{Class.forName(String)}, but works when the string
351 * represents a primitive type, too. If the original name can't
352 * be found, it also tries looking for the name with the last '.'
353 * changed to a dollar sign ($). This accounts for inner classes
354 * which are sometimes separated with '.'.
355 **/
356 public static Class<?> classForName(String className) throws ClassNotFoundException {
357 Class<?> result = primitiveClasses.get(className);
358 if (result != null)
359 return result;
360 else {
361 try {
362 return Class.forName(className);
363 } catch (ClassNotFoundException e) {
364 int pos = className.lastIndexOf('.');
365 String inner_name = className.substring (0, pos) + "$"
366 + className.substring (pos+1);
367 try {
368 return Class.forName (inner_name);
369 } catch (ClassNotFoundException ee) {
370 throw e;
371 }
372 }
373 }
374 }
375
376 private static HashMap<String,String> primitiveClassesJvm = new HashMap<String,String>(8);
377 static {
378 primitiveClassesJvm.put("boolean", "Z");
379 primitiveClassesJvm.put("byte", "B");
380 primitiveClassesJvm.put("char", "C");
381 primitiveClassesJvm.put("double", "D");
382 primitiveClassesJvm.put("float", "F");
383 primitiveClassesJvm.put("int", "I");
384 primitiveClassesJvm.put("long", "J");
385 primitiveClassesJvm.put("short", "S");
386 }
387
388 /**
389 * Convert a fully-qualified classname from Java format to JVML format.
390 * For example, convert "java.lang.Object[]" to "[Ljava/lang/Object;".
391 **/
392 public static String classnameToJvm(String classname) {
393 int dims = 0;
394 while (classname.endsWith("[]")) {
395 dims++;
396 classname = classname.substring(0, classname.length()-2);
397 }
398 String result = primitiveClassesJvm.get(classname);
399 if (result == null) {
400 result = "L" + classname + ";";
401 }
402 for (int i=0; i<dims; i++) {
403 result = "[" + result;
404 }
405 return result.replace('.', '/');
406 }
407
408 /**
409 * Convert a primitive java type name (e.g., "int", "double", etc.) to
410 * the single character JVM name (e.g., "I", "D", etc.).
411 * Throws IllegalArgumentException if primitive_name is not a valid name.
412 */
413 public static String primitive_name_to_jvm (String primitive_name) {
414 String result = primitiveClassesJvm.get (primitive_name);
415 if (result == null) {
416 throw new IllegalArgumentException("Not the name of a primitive type: " + primitive_name);
417 }
418 return result;
419 }
420
421 /**
422 * Convert a fully-qualified argument list from Java format to JVML format.
423 * For example, convert "(java.lang.Integer[], int, java.lang.Integer[][])"
424 * to "([Ljava/lang/Integer;I[[Ljava/lang/Integer;)".
425 **/
426 public static String arglistToJvm(String arglist) {
427 if (! (arglist.startsWith("(") && arglist.endsWith(")"))) {
428 throw new Error("Malformed arglist: " + arglist);
429 }
430 String result = "(";
431 String comma_sep_args = arglist.substring(1, arglist.length()-1);
432 StringTokenizer args_tokenizer
433 = new StringTokenizer(comma_sep_args, ",", false);
434 for ( ; args_tokenizer.hasMoreTokens(); ) {
435 String arg = args_tokenizer.nextToken().trim();
436 result += classnameToJvm(arg);
437 }
438 result += ")";
439 // System.out.println("arglistToJvm: " + arglist + " => " + result);
440 return result;
441 }
442
443 private static HashMap<String,String> primitiveClassesFromJvm = new HashMap<String,String>(8);
444 static {
445 primitiveClassesFromJvm.put("Z", "boolean");
446 primitiveClassesFromJvm.put("B", "byte");
447 primitiveClassesFromJvm.put("C", "char");
448 primitiveClassesFromJvm.put("D", "double");
449 primitiveClassesFromJvm.put("F", "float");
450 primitiveClassesFromJvm.put("I", "int");
451 primitiveClassesFromJvm.put("J", "long");
452 primitiveClassesFromJvm.put("S", "short");
453 }
454
455 // does not convert "V" to "void". Should it?
456 /**
457 * Convert a classname from JVML format to Java format.
458 * For example, convert "[Ljava/lang/Object;" to "java.lang.Object[]".
459 **/
460 public static String classnameFromJvm(String classname) {
461 int dims = 0;
462 while (classname.startsWith("[")) {
463 dims++;
464 classname = classname.substring(1);
465 }
466 String result;
467 if (classname.startsWith("L") && classname.endsWith(";")) {
468 result = classname.substring(1, classname.length() - 1);
469 } else {
470 result = primitiveClassesFromJvm.get(classname);
471 if (result == null) {
472 throw new Error("Malformed base class: " + classname);
473 }
474 }
475 for (int i=0; i<dims; i++) {
476 result += "[]";
477 }
478 return result.replace('/', '.');
479 }
480
481 /**
482 * Convert an argument list from JVML format to Java format.
483 * For example, convert "([Ljava/lang/Integer;I[[Ljava/lang/Integer;)"
484 * to "(java.lang.Integer[], int, java.lang.Integer[][])".
485 **/
486 public static String arglistFromJvm(String arglist) {
487 if (! (arglist.startsWith("(") && arglist.endsWith(")"))) {
488 throw new Error("Malformed arglist: " + arglist);
489 }
490 String result = "(";
491 int pos = 1;
492 while (pos < arglist.length()-1) {
493 if (pos > 1)
494 result += ", ";
495 int nonarray_pos = pos;
496 while (arglist.charAt(nonarray_pos) == '[') {
497 nonarray_pos++;
498 }
499 char c = arglist.charAt(nonarray_pos);
500 if (c == 'L') {
501 int semi_pos = arglist.indexOf(";", nonarray_pos);
502 result += classnameFromJvm(arglist.substring(pos, semi_pos+1));
503 pos = semi_pos + 1;
504 } else {
505 String maybe = classnameFromJvm(arglist.substring(pos, nonarray_pos+1));
506 if (maybe == null) {
507 // return null;
508 throw new Error("Malformed arglist: " + arglist);
509 }
510 result += maybe;
511 pos = nonarray_pos+1;
512 }
513 }
514 return result + ")";
515 }
516
517
518 ///////////////////////////////////////////////////////////////////////////
519 /// ClassLoader
520 ///
521
522 /**
523 * This class has no purpose but to define loadClassFromFile.
524 * ClassLoader.defineClass is protected, so I subclass ClassLoader in
525 * order to call defineClass.
526 **/
527 private static class PromiscuousLoader extends ClassLoader {
528 public Class<?> loadClassFromFile(String className, String pathname) throws FileNotFoundException, IOException {
529 FileInputStream fi = new FileInputStream(pathname);
530 int numbytes = fi.available();
531 byte[] classBytes = new byte[numbytes];
532 fi.read(classBytes);
533 fi.close();
534 Class<?> return_class = defineClass(className, classBytes, 0, numbytes);
535 resolveClass(return_class);
536 return return_class;
537 }
538 }
539
540 private static PromiscuousLoader thePromiscuousLoader = new PromiscuousLoader();
541
542 /**
543 * @param pathname the pathname of a .class file
544 * @return a Java Object corresponding to the Class defined in the .class file
545 **/
546 public static Class<?> loadClassFromFile(String className, String pathname) throws FileNotFoundException, IOException {
547 return thePromiscuousLoader.loadClassFromFile(className, pathname);
548 }
549
550
551 ///////////////////////////////////////////////////////////////////////////
552 /// Classpath
553 ///
554
555 // Perhaps abstract out the simpler addToPath from this
556 /** Add the directory to the system classpath. */
557 public static void addToClasspath(String dir) {
558 // If the dir isn't on CLASSPATH, add it.
559 String pathSep = System.getProperty("path.separator");
560 // what is the point of the "replace()" call?
561 String cp = System.getProperty("java.class.path",".").replace('\\', '/');
562 StringTokenizer tokenizer = new StringTokenizer(cp, pathSep, false);
563 boolean found = false;
564 while (tokenizer.hasMoreTokens() && !found) {
565 found = tokenizer.nextToken().equals(dir);
566 }
567 if (!found) {
568 System.setProperty("java.class.path", dir + pathSep + cp);
569 }
570 }
571
572
573 ///////////////////////////////////////////////////////////////////////////
574 /// File
575 ///
576
577
578 /** Count the number of lines in the specified file **/
579 public static long count_lines(String filename) throws IOException {
580 LineNumberReader reader = UtilMDE.lineNumberFileReader(filename);
581 long count = 0;
582 while (reader.readLine() != null)
583 count++;
584 return count;
585 }
586
587 /** Tries to infer the line separator used in a file. **/
588 public static String inferLineSeparator(String filename) throws IOException {
589 return inferLineSeparator(new File(filename));
590 }
591
592 /** Tries to infer the line separator used in a file. **/
593 public static String inferLineSeparator(File filename) throws IOException {
594 BufferedReader r = UtilMDE.bufferedFileReader(filename);
595 int unix = 0;
596 int dos = 0;
597 int mac = 0;
598 while (true) {
599 String s = r.readLine();
600 if (s == null) {
601 break;
602 }
603 if (s.endsWith("\r\n")) {
604 dos++;
605 } else if (s.endsWith("\r")) {
606 mac++;
607 } else if (s.endsWith("\n")) {
608 unix++;
609 } else {
610 // This can happen only if the last line is not terminated.
611 }
612 }
613 if ((dos > mac && dos > unix)
614 || (lineSep.equals("\r\n") && dos >= unix && dos >= mac)) {
615 return "\r\n";
616 }
617 if ((mac > dos && mac > unix)
618 || (lineSep.equals("\r") && mac >= dos && mac >= unix)) {
619 return "\n";
620 }
621 if ((unix > dos && unix > mac)
622 || (lineSep.equals("\n") && unix >= dos && unix >= mac)) {
623 return "\n";
624 }
625 // The two non-preferred line endings are tied and have more votes than
626 // the preferred line ending. Give up.
627 return lineSep;
628 }
629
630
631
632 /**
633 * Returns true iff files have the same contents.
634 */
635 public static boolean equalFiles(String file1, String file2) {
636 return equalFiles(file1, file2, false);
637 }
638
639 /**
640 * Returns true iff files have the same contents.
641 * @param trimLines if true, call String.trim on each line before comparing
642 */
643 public static boolean equalFiles(String file1, String file2, boolean trimLines) {
644 try {
645 LineNumberReader reader1 = UtilMDE.lineNumberFileReader(file1);
646 LineNumberReader reader2 = UtilMDE.lineNumberFileReader(file2);
647 String line1 = reader1.readLine();
648 String line2 = reader2.readLine();
649 while (line1 != null && line2 != null) {
650 if (trimLines) {
651 line1 = line1.trim();
652 line2 = line2.trim();
653 }
654 if (! (line1.equals(line2))) {
655 return false;
656 }
657 line1 = reader1.readLine();
658 line2 = reader2.readLine();
659 }
660 if (line1 == null && line2 == null) {
661 return true;
662 }
663 return false;
664 } catch (IOException e) {
665 throw new RuntimeException(e);
666 }
667 }
668
669
670 /**
671 * Returns true
672 * if the file exists and is writable, or
673 * if the file can be created.
674 **/
675 public static boolean canCreateAndWrite(File file) {
676 if (file.exists()) {
677 return file.canWrite();
678 } else {
679 File directory = file.getParentFile();
680 if (directory == null) {
681 directory = new File(".");
682 }
683 // Does this test need "directory.canRead()" also?
684 return directory.canWrite();
685 }
686
687 /// Old implementation; is this equivalent to the new one, above??
688 // try {
689 // if (file.exists()) {
690 // return file.canWrite();
691 // } else {
692 // file.createNewFile();
693 // file.delete();
694 // return true;
695 // }
696 // } catch (IOException e) {
697 // return false;
698 // }
699 }
700
701
702 ///
703 /// Directories
704 ///
705
706 /**
707 * Creates an empty directory in the default temporary-file directory,
708 * using the given prefix and suffix to generate its name. For example
709 * calling createTempDir("myPrefix", "mySuffix") will create the following
710 * directory: temporaryFileDirectory/myUserName/myPrefix_someString_suffix.
711 * someString is internally generated to ensure no temporary files of the
712 * same name are generated.
713 * @param prefix The prefix string to be used in generating the file's
714 * name; must be at least three characters long
715 * @param suffix The suffix string to be used in generating the file's
716 * name; may be null, in which case the suffix ".tmp" will be used Returns:
717 * An abstract pathname denoting a newly-created empty file
718 * @throws IllegalArgumentException If the prefix argument contains fewer
719 * than three characters
720 * @throws IOException If a file could not be created
721 * @throws SecurityException If a security manager exists and its
722 * SecurityManager.checkWrite(java.lang.String) method does not allow a
723 * file to be created
724 * @see java.io.File#createTempFile(String, String, File)
725 **/
726 public static File createTempDir(String prefix, String suffix)
727 throws IOException {
728 String fs = File.separator;
729 String path = System.getProperty("java.io.tmpdir") + fs +
730 System.getProperty("user.name") + fs;
731 File pathFile = new File(path);
732 pathFile.mkdirs();
733 File tmpfile = File.createTempFile(prefix + "_", "_", pathFile);
734 String tmpDirPath = tmpfile.getPath() + suffix;
735 tmpfile.delete();
736 File tmpDir = new File(tmpDirPath);
737 tmpDir.mkdirs();
738 return tmpDir;
739 }
740
741
742 /**
743 * Deletes the directory at dirName and all its files.
744 * Fails if dirName has any subdirectories.
745 */
746 public static void deleteDir(String dirName) {
747 deleteDir(new File(dirName));
748 }
749
750 /**
751 * Deletes the directory at dirName and all its files.
752 * Fails if dirName has any subdirectories.
753 */
754 public static void deleteDir(File dir) {
755 File[] files = dir.listFiles();
756 if (files == null) {
757 return;
758 }
759 for (int i = 0; i < files.length; i++) {
760 files[i].delete();
761 }
762 dir.delete();
763 }
764
765
766 ///
767 /// File names (aka filenames)
768 ///
769
770 // Someone must have already written this. Right?
771
772 // Deals with exactly one "*" in name.
773 public static final class WildcardFilter implements FilenameFilter {
774 String prefix;
775 String suffix;
776 public WildcardFilter(String filename) {
777 int astloc = filename.indexOf("*");
778 if (astloc == -1)
779 throw new Error("No asterisk in wildcard argument: " + filename);
780 prefix = filename.substring(0, astloc);
781 suffix = filename.substring(astloc+1);
782 if (filename.indexOf("*") != -1)
783 throw new Error("Multiple asterisks in wildcard argument: " + filename);
784 }
785 public boolean accept(File dir, String name) {
786 return name.startsWith(prefix) && name.endsWith(suffix);
787 }
788 }
789
790 @SuppressWarnings("nullness") // user.home property always exists
791 static final /*@NonNull*/ String userHome = System.getProperty ("user.home");
792
793 /**
794 * Does tilde expansion on a file name (to the user's home directory).
795 */
796 public static File expandFilename (File name) {
797 String path = name.getPath();
798 String newname = expandFilename (path);
799 @SuppressWarnings("interning")
800 boolean changed = (newname != path);
801 if (changed)
802 return new File (newname);
803 else
804 return name;
805 }
806
807 /**
808 * Does tilde expansion on a file name (to the user's home directory).
809 */
810 public static String expandFilename (String name) {
811 if (name.contains ("~"))
812 return (name.replace ("~", userHome));
813 else
814 return name;
815 }
816
817
818 /**
819 * Returns a string version of the name that can be used in Java source.
820 * On Windows, the file will return a backslash separated string. Since
821 * backslash is an escape character, it must be quoted itself inside
822 * the string.
823 *
824 * The current implementation presumes that backslashes don't appear
825 * in filenames except as windows path separators. That seems like a
826 * reasonable assumption.
827 */
828 public static String java_source (File name) {
829
830 return name.getPath().replace ("\\", "\\\\");
831 }
832
833 ///
834 /// Reading and writing
835 ///
836
837 /**
838 * Writes an Object to a File.
839 **/
840 public static void writeObject(Object o, File file) throws IOException {
841 // 8192 is the buffer size in BufferedReader
842 OutputStream bytes =
843 new BufferedOutputStream(new FileOutputStream(file), 8192);
844 if (file.getName().endsWith(".gz")) {
845 bytes = new GZIPOutputStream(bytes);
846 }
847 ObjectOutputStream objs = new ObjectOutputStream(bytes);
848 objs.writeObject(o);
849 objs.close();
850 }
851
852
853 /**
854 * Reads an Object from a File.
855 **/
856 public static Object readObject(File file) throws
857 IOException, ClassNotFoundException {
858 // 8192 is the buffer size in BufferedReader
859 InputStream istream =
860 new BufferedInputStream(new FileInputStream(file), 8192);
861 if (file.getName().endsWith(".gz")) {
862 istream = new GZIPInputStream(istream);
863 }
864 ObjectInputStream objs = new ObjectInputStream(istream);
865 return objs.readObject();
866 }
867
868 /**
869 * Reads the entire contents of the reader and returns it as a string.
870 * Any IOException encountered will be turned into an Error.
871 */
872 public static String readerContents(Reader r) {
873 try {
874 StringBuilder contents = new StringBuilder();
875 int ch;
876 while ((ch = r.read()) != -1) {
877 contents.append((char) ch);
878 }
879 r.close();
880 return contents.toString();
881 } catch (Exception e) {
882 throw new Error ("Unexpected error in readerContents(" + r + ")", e);
883 }
884 }
885
886 // an alternate name would be "fileContents".
887 /**
888 * Reads the entire contents of the file and returns it as a string.
889 * Any IOException encountered will be turned into an Error.
890 */
891 public static String readFile (File file) {
892
893 try {
894 BufferedReader reader = UtilMDE.bufferedFileReader (file);
895 StringBuilder contents = new StringBuilder();
896 String line = reader.readLine();
897 while (line != null) {
898 contents.append (line);
899 // Note that this converts line terminators!
900 contents.append (lineSep);
901 line = reader.readLine();
902 }
903 reader.close();
904 return contents.toString();
905 } catch (Exception e) {
906 throw new Error ("Unexpected error in readFile(" + file + ")", e);
907 }
908 }
909
910 /**
911 * Creates a file with the given name and writes the specified string
912 * to it. If the file currently exists (and is writable) it is overwritten
913 * Any IOException encountered will be turned into an Error.
914 */
915 public static void writeFile (File file, String contents) {
916
917 try {
918 FileWriter writer = new FileWriter (file);
919 writer.write (contents, 0, contents.length());
920 writer.close();
921 } catch (Exception e) {
922 throw new Error ("Unexpected error in writeFile(" + file + ")", e);
923 }
924 }
925
926
927 ///////////////////////////////////////////////////////////////////////////
928 /// Hashing
929 ///
930
931 // In hashing, there are two separate issues. First, one must convert
932 // the input datum into an integer. Then, one must transform the
933 // resulting integer in a pseudorandom way so as to result in a number
934 // that is far separated from other values that may have been near it to
935 // begin with. Often these two steps are combined, particularly if
936 // one wishes to avoid creating too large an integer (losing information
937 // off the top bits).
938
939 // http://burtleburtle.net/bob/hash/hashfaq.html says (of combined methods):
940 // * for (h=0, i=0; i<len; ++i) { h += key[i]; h += (h<<10); h ^= (h>>6); }
941 // h += (h<<3); h ^= (h>>11); h += (h<<15);
942 // is good.
943 // * for (h=0, i=0; i<len; ++i) h = tab[(h^key[i])&0xff]; may be good.
944 // * for (h=0, i=0; i<len; ++i) h = (h>>8)^tab[(key[i]+h)&0xff]; may be good.
945
946 // In this part of the file, perhaps I will eventually write good hash
947 // functions. For now, write cheesy ones that primarily deal with the
948 // first issue, transforming a data structure into a single number. This
949 // is also known as fingerprinting.
950
951 // Note that this differs from the result of Double.hashCode (which see).
952 public static final int hash(double x) {
953 return hash(Double.doubleToLongBits(x));
954 }
955
956 public static final int hash(double a, double b) {
957 double result = 17;
958 result = result * 37 + a;
959 result = result * 37 + b;
960 return hash(result);
961 }
962
963 public static final int hash(double a, double b, double c) {
964 double result = 17;
965 result = result * 37 + a;
966 result = result * 37 + b;
967 result = result * 37 + c;
968 return hash(result);
969 }
970
971 public static final int hash(double /*@Nullable*/ [] a) {
972 double result = 17;
973 if (a != null) {
974 result = result * 37 + a.length;
975 for (int i = 0; i < a.length; i++) {
976 result = result * 37 + a[i];
977 }
978 }
979 return hash(result);
980 }
981
982 public static final int hash(double /*@Nullable*/ [] a, double /*@Nullable*/ [] b) {
983 return hash(hash(a), hash(b));
984 }
985
986
987 // Don't define hash with int args; use the long versions instead.
988
989 // Note that this differs from the result of Long.hashCode (which see)
990 // But it doesn't map -1 and 0 to the same value.
991 public static final int hash(long l) {
992 // If possible, use the value itself.
993 if (l >= Integer.MIN_VALUE && l <= Integer.MAX_VALUE) {
994 return (int) l;
995 }
996
997 int result = 17;
998 int hibits = (int) (l >> 32);
999 int lobits = (int) l;
1000 result = result * 37 + hibits;
1001 result = result * 37 + lobits;
1002 return result;
1003 }
1004
1005 public static final int hash(long a, long b) {
1006 long result = 17;
1007 result = result * 37 + a;
1008 result = result * 37 + b;
1009 return hash(result);
1010 }
1011
1012 public static final int hash(long a, long b, long c) {
1013 long result = 17;
1014 result = result * 37 + a;
1015 result = result * 37 + b;
1016 result = result * 37 + c;
1017 return hash(result);
1018 }
1019
1020 public static final int hash(long /*@Nullable*/ [] a) {
1021 long result = 17;
1022 if (a != null) {
1023 result = result * 37 + a.length;
1024 for (int i = 0; i < a.length; i++) {
1025 result = result * 37 + a[i];
1026 }
1027 }
1028 return hash(result);
1029 }
1030
1031 public static final int hash(long /*@Nullable*/ [] a, long /*@Nullable*/ [] b) {
1032 return hash(hash(a), hash(b));
1033 }
1034
1035 public static final int hash(/*@Nullable*/ String a) {
1036 return (a == null) ? 0 : a.hashCode();
1037 }
1038
1039 public static final int hash(/*@Nullable*/ String a, /*@Nullable*/ String b) {
1040 long result = 17;
1041 result = result * 37 + hash(a);
1042 result = result * 37 + hash(b);
1043 return hash(result);
1044 }
1045
1046 public static final int hash(/*@Nullable*/ String a, /*@Nullable*/ String b, /*@Nullable*/ String c) {
1047 long result = 17;
1048 result = result * 37 + hash(a);
1049 result = result * 37 + hash(b);
1050 result = result * 37 + hash(c);
1051 return hash(result);
1052 }
1053
1054 public static final int hash(/*@Nullable*/ String /*@Nullable*/ [] a) {
1055 long result = 17;
1056 if (a != null) {
1057 result = result * 37 + a.length;
1058 for (int i = 0; i < a.length; i++) {
1059 result = result * 37 + hash(a[i]);
1060 }
1061 }
1062 return hash(result);
1063 }
1064
1065
1066 ///////////////////////////////////////////////////////////////////////////
1067 /// Iterator
1068 ///
1069
1070 // Making these classes into functions didn't work because I couldn't get
1071 // their arguments into a scope that Java was happy with.
1072
1073 /** Converts an Enumeration into an Iterator. */
1074 public static final class EnumerationIterator<T> implements Iterator<T> {
1075 Enumeration<T> e;
1076 public EnumerationIterator(Enumeration<T> e) { this.e = e; }
1077 public boolean hasNext() { return e.hasMoreElements(); }
1078 public T next() { return e.nextElement(); }
1079 public void remove() { throw new UnsupportedOperationException(); }
1080 }
1081
1082 /** Converts an Iterator into an Enumeration. */
1083 public static final class IteratorEnumeration<T> implements Enumeration<T> {
1084 Iterator<T> itor;
1085 public IteratorEnumeration(Iterator<T> itor) { this.itor = itor; }
1086 public boolean hasMoreElements() { return itor.hasNext(); }
1087 public T nextElement() { return itor.next(); }
1088 }
1089
1090 // This must already be implemented someplace else. Right??
1091 /**
1092 * An Iterator that returns first the elements returned by its first
1093 * argument, then the elements returned by its second argument.
1094 * Like MergedIterator, but specialized for the case of two arguments.
1095 **/
1096 public static final class MergedIterator2<T> implements Iterator<T> {
1097 Iterator<T> itor1, itor2;
1098 public MergedIterator2(Iterator<T> itor1_, Iterator<T> itor2_) {
1099 this.itor1 = itor1_; this.itor2 = itor2_;
1100 }
1101 public boolean hasNext() {
1102 return (itor1.hasNext() || itor2.hasNext());
1103 }
1104 public T next() {
1105 if (itor1.hasNext())
1106 return itor1.next();
1107 else if (itor2.hasNext())
1108 return itor2.next();
1109 else
1110 throw new NoSuchElementException();
1111 }
1112 public void remove() {
1113 throw new UnsupportedOperationException();
1114 }
1115 }
1116
1117 // This must already be implemented someplace else. Right??
1118 /**
1119 * An Iterator that returns the elements in each of its argument
1120 * Iterators, in turn. The argument is an Iterator of Iterators.
1121 * Like MergedIterator2, but generalized to arbitrary number of iterators.
1122 **/
1123 public static final class MergedIterator<T> implements Iterator<T> {
1124 Iterator<Iterator<T>> itorOfItors;
1125 public MergedIterator(Iterator<Iterator<T>> itorOfItors) { this.itorOfItors = itorOfItors; }
1126
1127 // an empty iterator to prime the pump
1128 Iterator<T> current = new ArrayList<T>().iterator();
1129
1130 public boolean hasNext() {
1131 while ((!current.hasNext()) && (itorOfItors.hasNext())) {
1132 current = itorOfItors.next();
1133 }
1134 return current.hasNext();
1135 }
1136
1137 public T next() {
1138 hasNext(); // for side effect
1139 return current.next();
1140 }
1141
1142 public void remove() {
1143 throw new UnsupportedOperationException();
1144 }
1145 }
1146
1147 public static final class FilteredIterator<T> implements Iterator<T> {
1148 Iterator<T> itor;
1149 Filter<T> filter;
1150
1151 public FilteredIterator(Iterator<T> itor, Filter<T> filter) {
1152 this.itor = itor; this.filter = filter;
1153 }
1154
1155 @SuppressWarnings("unchecked")
1156 T invalid_t = (T) new Object();
1157
1158 T current = invalid_t;
1159 boolean current_valid = false;
1160
1161 public boolean hasNext() {
1162 while ((!current_valid) && itor.hasNext()) {
1163 current = itor.next();
1164 current_valid = filter.accept(current);
1165 }
1166 return current_valid;
1167 }
1168
1169 public T next() {
1170 if (hasNext()) {
1171 current_valid = false;
1172 @SuppressWarnings("interning")
1173 boolean ok = (current != invalid_t);
1174 assert ok;
1175 return current;
1176 } else {
1177 throw new NoSuchElementException();
1178 }
1179 }
1180
1181 public void remove() {
1182 throw new UnsupportedOperationException();
1183 }
1184 }
1185
1186 /**
1187 * Returns an iterator just like its argument, except that the first and
1188 * last elements are removed. They can be accessed via the getFirst and
1189 * getLast methods.
1190 **/
1191 public static final class RemoveFirstAndLastIterator<T> implements Iterator<T> {
1192 Iterator<T> itor;
1193 // I don't think this works, because the iterator might itself return null
1194 // /*@Nullable*/ T nothing = (/*@Nullable*/ T) null;
1195 @SuppressWarnings("unchecked")
1196 T nothing = (T) new Object();
1197 T first = nothing;
1198 T current = nothing;
1199
1200 public RemoveFirstAndLastIterator(Iterator<T> itor) {
1201 this.itor = itor;
1202 if (itor.hasNext()) {
1203 first = itor.next();
1204 }
1205 if (itor.hasNext()) {
1206 current = itor.next();
1207 }
1208 }
1209
1210 public boolean hasNext() {
1211 return itor.hasNext();
1212 }
1213
1214 public T next() {
1215 if (! itor.hasNext()) {
1216 throw new NoSuchElementException();
1217 }
1218 T tmp = current;
1219 current = itor.next();
1220 return tmp;
1221 }
1222
1223 public T getFirst() {
1224 @SuppressWarnings("interning")
1225 boolean invalid = (first == nothing);
1226 if (invalid) {
1227 throw new NoSuchElementException();
1228 }
1229 return first;
1230 }
1231
1232 // Throws an error unless the RemoveFirstAndLastIterator has already
1233 // been iterated all the way to its end (so the delegate is pointing to
1234 // the last element). Also, this is buggy when the delegate is empty.
1235 public T getLast() {
1236 if (itor.hasNext()) {
1237 throw new Error();
1238 }
1239 return current;
1240 }
1241
1242 public void remove() {
1243 throw new UnsupportedOperationException();
1244 }
1245 }
1246
1247
1248 /**
1249 * Return an List containing num_elts randomly chosen
1250 * elements from the iterator, or all the elements of the iterator if
1251 * there are fewer. It examines every element of the iterator, but does
1252 * not keep them all in memory.
1253 **/
1254 public static <T> List<T> randomElements(Iterator<T> itor, int num_elts) {
1255 return randomElements(itor, num_elts, r);
1256 }
1257 private static Random r = new Random();
1258
1259 /**
1260 * Return an List containing num_elts randomly chosen
1261 * elements from the iterator, or all the elements of the iterator if
1262 * there are fewer. It examines every element of the iterator, but does
1263 * not keep them all in memory.
1264 **/
1265 public static <T> List<T> randomElements(Iterator<T> itor, int num_elts, Random random) {
1266 // The elements are chosen with the following probabilities,
1267 // where n == num_elts:
1268 // n n/2 n/3 n/4 n/5 ...
1269
1270 RandomSelector<T> rs = new RandomSelector<T> (num_elts, random);
1271
1272 while (itor.hasNext()) {
1273 rs.accept (itor.next());
1274 }
1275 return rs.getValues();
1276
1277
1278 /*
1279 ArrayList<T> result = new ArrayList<T>(num_elts);
1280 int i=1;
1281 for (int n=0; n<num_elts && itor.hasNext(); n++, i++) {
1282 result.add(itor.next());
1283 }
1284 for (; itor.hasNext(); i++) {
1285 T o = itor.next();
1286 // test random < num_elts/i
1287 if (random.nextDouble() * i < num_elts) {
1288 // This element will replace one of the existing elements.
1289 result.set(random.nextInt(num_elts), o);
1290 }
1291 }
1292 return result;
1293 */
1294 }
1295
1296
1297 ///////////////////////////////////////////////////////////////////////////
1298 /// Map
1299 ///
1300
1301 // In Python, inlining this gave a 10x speed improvement.
1302 // Will the same be true for Java?
1303 /**
1304 * Increment the Integer which is indexed by key in the Map.
1305 * If the key isn't in the Map, it is added.
1306 * Throws an error if the key is in the Map but maps to a non-Integer.
1307 **/
1308 public static <T> /*@Nullable*/ Integer incrementMap(Map<T,Integer> m, T key, int count) {
1309 Integer old = m.get(key);
1310 int new_total;
1311 if (old == null) {
1312 new_total = count;
1313 } else {
1314 new_total = old.intValue() + count;
1315 }
1316 return m.put(key, new Integer(new_total));
1317 }
1318
1319 public static <K,V> String mapToString(Map<K,V> m) {
1320 StringBuilder sb = new StringBuilder();
1321 mapToString(sb, m, "");
1322 return sb.toString();
1323 }
1324
1325 /**
1326 * Write a multi-line representation of the map into the given Appendable
1327 * (e.g., a StringBuilder).
1328 */
1329 public static <K,V> void mapToString(Appendable sb, Map<K,V> m, String linePrefix) {
1330 try {
1331 for (Map.Entry<K, V> entry : m.entrySet()) {
1332 sb.append(linePrefix);
1333 sb.append(entry.getKey().toString());
1334 sb.append(" => ");
1335 sb.append(entry.getValue().toString());
1336 sb.append(lineSep);
1337 }
1338 } catch (IOException e) {
1339 throw new RuntimeException(e);
1340 }
1341 }
1342
1343 /** Returns a sorted version of m.keySet(). */
1344 public static <K extends Comparable<? super K>,V> Collection</*@KeyFor("#0")*/ K> sortedKeySet(Map<K,V> m) {
1345 ArrayList<K> theKeys = new ArrayList<K> (m.keySet());
1346 Collections.sort (theKeys);
1347 return theKeys;
1348 }
1349
1350 /** Returns a sorted version of m.keySet(). */
1351 public static <K,V> Collection</*@KeyFor("#0")*/ K> sortedKeySet(Map<K,V> m, Comparator<K> comparator) {
1352 ArrayList<K> theKeys = new ArrayList<K> (m.keySet());
1353 Collections.sort (theKeys, comparator);
1354 return theKeys;
1355 }
1356
1357
1358 ///////////////////////////////////////////////////////////////////////////
1359 /// Method
1360 ///
1361
1362 // maps from a string of arg names to an array of Class objects.
1363 static HashMap<String,Class<?>[]> args_seen = new HashMap<String,Class<?>[]>();
1364
1365 public static Method methodForName(String method)
1366 throws ClassNotFoundException, NoSuchMethodException, SecurityException {
1367
1368 int oparenpos = method.indexOf('(');
1369 int dotpos = method.lastIndexOf('.', oparenpos);
1370 int cparenpos = method.indexOf(')', oparenpos);
1371 if ((dotpos == -1) || (oparenpos == -1) || (cparenpos == -1)) {
1372 throw new Error("malformed method name should contain a period, open paren, and close paren: " + method + " <<" + dotpos + "," + oparenpos + "," + cparenpos + ">>");
1373 }
1374 for (int i=cparenpos+1; i<method.length(); i++) {
1375 if (! Character.isWhitespace(method.charAt(i))) {
1376 throw new Error("malformed method name should contain only whitespace following close paren");
1377 }
1378 }
1379
1380 String classname = method.substring(0,dotpos);
1381 String methodname = method.substring(dotpos+1, oparenpos);
1382 String all_argnames = method.substring(oparenpos+1, cparenpos).trim();
1383 Class<?>[] argclasses = args_seen.get(all_argnames);
1384 if (argclasses == null) {
1385 String[] argnames;
1386 if (all_argnames.equals("")) {
1387 argnames = new String[0];
1388 } else {
1389 argnames = split(all_argnames, ',');
1390 }
1391
1392 argclasses = new Class<?>[argnames.length];
1393 for (int i=0; i<argnames.length; i++) {
1394 String argname = argnames[i].trim();
1395 int numbrackets = 0;
1396 while (argname.endsWith("[]")) {
1397 argname = argname.substring(0, argname.length()-2);
1398 numbrackets++;
1399 }
1400 if (numbrackets > 0) {
1401 argname = "L" + argname + ";";
1402 while (numbrackets>0) {
1403 argname = "[" + argname;
1404 numbrackets--;
1405 }
1406 }
1407 // System.out.println("argname " + i + " = " + argname + " for method " + method);
1408 argclasses[i] = classForName(argname);
1409 }
1410 args_seen.put(all_argnames, argclasses);
1411 }
1412 return methodForName(classname, methodname, argclasses);
1413 }
1414
1415 public static Method methodForName(String classname, String methodname, Class<?>[] params)
1416 throws ClassNotFoundException, NoSuchMethodException, SecurityException {
1417
1418 Class<?> c = Class.forName(classname);
1419 Method m = c.getDeclaredMethod(methodname, params);
1420 return m;
1421 }
1422
1423
1424
1425 ///////////////////////////////////////////////////////////////////////////
1426 /// ProcessBuilder
1427 ///
1428
1429 /**
1430 * Execute the given command, and return all its output as a string.
1431 */
1432 public static String backticks(String... command) {
1433 return backticks(Arrays.asList(command));
1434 }
1435
1436 /**
1437 * Execute the given command, and return all its output as a string.
1438 */
1439 public static String backticks(List<String> command) {
1440 ProcessBuilder pb = new ProcessBuilder(command);
1441 pb.redirectErrorStream(true);
1442 // TimeLimitProcess p = new TimeLimitProcess(pb.start(), TIMEOUT_SEC * 1000);
1443 try {
1444 Process p = pb.start();
1445 String output = UtilMDE.streamString(p.getInputStream());
1446 return output;
1447 } catch (IOException e) {
1448 return "IOException: " + e.getMessage();
1449 }
1450 }
1451
1452
1453 ///////////////////////////////////////////////////////////////////////////
1454 /// Properties
1455 ///
1456
1457 /**
1458 * Determines whether a property has value "true", "yes", or "1".
1459 * @see Properties#getProperty
1460 **/
1461 public static boolean propertyIsTrue(Properties p, String key) {
1462 String pvalue = p.getProperty(key);
1463 if (pvalue == null) {
1464 return false;
1465 }
1466 pvalue = pvalue.toLowerCase();
1467 return (pvalue.equals("true") || pvalue.equals("yes") || pvalue.equals("1"));
1468 }
1469
1470 /**
1471 * Set the property to its previous value concatenated to the given value.
1472 * Returns the previous value.
1473 * @see Properties#getProperty
1474 * @see Properties#setProperty
1475 **/
1476 public static /*@Nullable*/ String appendProperty(Properties p, String key, String value) {
1477 return (String)p.setProperty(key, p.getProperty(key, "") + value);
1478 }
1479
1480 /**
1481 * Set the property only if it was not previously set.
1482 * @deprecated use setDefaultMaybe
1483 * @see Properties#getProperty
1484 * @see Properties#setProperty
1485 **/
1486 @Deprecated
1487 public static /*@Nullable*/ String setDefault(Properties p, String key, String value) {
1488 String currentValue = p.getProperty(key);
1489 if (currentValue == null) {
1490 p.setProperty(key, value);
1491 }
1492 return currentValue;
1493 }
1494
1495 /**
1496 * Set the property only if it was not previously set.
1497 * @see Properties#getProperty
1498 * @see Properties#setProperty
1499 **/
1500 public static /*@Nullable*/ String setDefaultMaybe(Properties p, String key, String value) {
1501 String currentValue = p.getProperty(key);
1502 if (currentValue == null) {
1503 p.setProperty(key, value);
1504 }
1505 return currentValue;
1506 }
1507
1508
1509 ///////////////////////////////////////////////////////////////////////////
1510 /// Regexp (regular expression)
1511 ///
1512
1513 // Stolen from JDK 1.5. Intended for use (and viewing) only by JRL
1514 // licensees (if you are not one, proceed no further). To be removed
1515 // as soon as we migrate to Java 1.5.
1516 /**
1517 * Returns a literal pattern <code>String</code> for the specified
1518 * <code>String</code>.
1519 *
1520 * <p>This method produces a <code>String</code> that can be used to
1521 * create a <code>Pattern</code> that would match the string
1522 * <code>s</code> as if it were a literal pattern.</p> Metacharacters
1523 * or escape sequences in the input sequence will be given no special
1524 * meaning.
1525 *
1526 * @param s The string to be literalized
1527 * @return A literal string replacement
1528 * @since 1.5
1529 */
1530 public static String patternQuote(String s) {
1531 int slashEIndex = s.indexOf("\\E");
1532 if (slashEIndex == -1)
1533 return "\\Q" + s + "\\E";
1534
1535 StringBuffer sb = new StringBuffer(s.length() * 2);
1536 sb.append("\\Q");
1537 slashEIndex = 0;
1538 int current = 0;
1539 while ((slashEIndex = s.indexOf("\\E", current)) != -1) {
1540 sb.append(s.substring(current, slashEIndex));
1541 current = slashEIndex + 2;
1542 sb.append("\\E\\\\E\\Q");
1543 }
1544 sb.append(s.substring(current, s.length()));
1545 sb.append("\\E");
1546 return sb.toString();
1547 }
1548
1549
1550 ///////////////////////////////////////////////////////////////////////////
1551 /// Reflection
1552 ///
1553
1554 /**
1555 * Sets the given field, which may be final.
1556 * Intended for use in readObject and nowhere else!
1557 */
1558 public static void setFinalField(Object o, String fieldName, Object value)
1559 throws NoSuchFieldException, IllegalAccessException {
1560 Class<?> c = o.getClass();
1561 while (c != Object.class) { // Class is interned
1562 // System.out.printf ("Setting field %s in %s%n", fieldName, c);
1563 try {
1564 Field f = c.getDeclaredField(fieldName);
1565 f.setAccessible(true);
1566 f.set(o, value);
1567 return;
1568 } catch (NoSuchFieldException e) {
1569 if (c.getSuperclass() == Object.class) // Class is interned
1570 throw e;
1571 }
1572 c = c.getSuperclass();
1573 assert c != null : "@SuppressWarnings(nullness): c was not Object, so is not null now";
1574 }
1575 throw new NoSuchFieldException (fieldName);
1576 }
1577
1578
1579 ///////////////////////////////////////////////////////////////////////////
1580 /// Set
1581 ///
1582
1583 /**
1584 * Returns the object in this set that is equal to key.
1585 * The Set abstraction doesn't provide this; it only provides "contains".
1586 * Returns null if the argument is null, or if it isn't in the set.
1587 **/
1588 public static /*@Nullable*/ Object getFromSet(Set<?> set, Object key) {
1589 if (key == null) {
1590 return null;
1591 }
1592 for (Object elt : set) {
1593 if (key.equals(elt)) {
1594 return elt;
1595 }
1596 }
1597 return null;
1598 }
1599
1600
1601 ///////////////////////////////////////////////////////////////////////////
1602 /// Stream
1603 ///
1604
1605 /** Copy the contents of the input stream to the output stream. */
1606 public static void streamCopy(java.io.InputStream from, java.io.OutputStream to) {
1607 byte[] buffer = new byte[1024];
1608 int bytes;
1609 try {
1610 while (true) {
1611 bytes = from.read(buffer);
1612 if (bytes == -1) {
1613 return;
1614 }
1615 to.write(buffer, 0, bytes);
1616 }
1617 } catch (java.io.IOException e) {
1618 e.printStackTrace();
1619 throw new Error(e);
1620 }
1621 }
1622
1623 /** Return a String containing all the characters from the input stream. **/
1624 public static String streamString(java.io.InputStream is) {
1625 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1626 streamCopy(is, baos);
1627 return baos.toString();
1628 }
1629
1630
1631 ///////////////////////////////////////////////////////////////////////////
1632 /// String
1633 ///
1634
1635 /**
1636 * Return a new string which is the text of target with all instances of
1637 * oldStr replaced by newStr.
1638 **/
1639 public static String replaceString(String target, String oldStr, String newStr) {
1640 if (oldStr.equals("")) throw new IllegalArgumentException();
1641
1642 StringBuffer result = new StringBuffer();
1643 int lastend = 0;
1644 int pos;
1645 while ((pos = target.indexOf(oldStr, lastend)) != -1) {
1646 result.append(target.substring(lastend, pos));
1647 result.append(newStr);
1648 lastend = pos + oldStr.length();
1649 }
1650 result.append(target.substring(lastend));
1651 return result.toString();
1652 }
1653
1654 /**
1655 * Return an array of Strings representing the characters between
1656 * successive instances of the delimiter character.
1657 * Always returns an array of length at least 1 (it might contain only the
1658 * empty string).
1659 * @see #split(String s, String delim)
1660 **/
1661 public static String[] split(String s, char delim) {
1662 Vector<String> result = new Vector<String>();
1663 for (int delimpos = s.indexOf(delim); delimpos != -1; delimpos = s.indexOf(delim)) {
1664 result.add(s.substring(0, delimpos));
1665 s = s.substring(delimpos+1);
1666 }
1667 result.add(s);
1668 String[] result_array = new String[result.size()];
1669 result.copyInto(result_array);
1670 return result_array;
1671 }
1672
1673 /**
1674 * Return an array of Strings representing the characters between
1675 * successive instances of the delimiter String.
1676 * Always returns an array of length at least 1 (it might contain only the
1677 * empty string).
1678 * @see #split(String s, char delim)
1679 **/
1680 public static String[] split(String s, String delim) {
1681 int delimlen = delim.length();
1682 if (delimlen == 0) {
1683 throw new Error("Second argument to split was empty.");
1684 }
1685 Vector<String> result = new Vector<String>();
1686 for (int delimpos = s.indexOf(delim); delimpos != -1; delimpos = s.indexOf(delim)) {
1687 result.add(s.substring(0, delimpos));
1688 s = s.substring(delimpos+delimlen);
1689 }
1690 result.add(s);
1691 String[] result_array = new String[result.size()];
1692 result.copyInto(result_array);
1693 return result_array;
1694 }
1695
1696 /**
1697 * Return an array of Strings, one for each line in the argument.
1698 * Always returns an array of length at least 1 (it might contain only the
1699 * empty string). All common line separators (cr, lf, cr-lf, or lf-cr)
1700 * are supported. Note that a string that ends with a line separator
1701 * will return an empty string as the last element of the array.
1702 * @see #split(String s, char delim)
1703 **/
1704 public static String[] splitLines(String s) {
1705 return s.split ("\r\n?|\n\r?", -1);
1706 }
1707
1708 /**
1709 * Concatenate the string representations of the objects, placing the
1710 * delimiter between them.
1711 * @see plume.ArraysMDE#toString(int[])
1712 **/
1713 public static String join(Object[] a, String delim) {
1714 if (a.length == 0) return "";
1715 if (a.length == 1) return String.valueOf(a[0]);
1716 StringBuffer sb = new StringBuffer(String.valueOf(a[0]));
1717 for (int i=1; i<a.length; i++)
1718 sb.append(delim).append(a[i]);
1719 return sb.toString();
1720 }
1721
1722 /**
1723 * Concatenate the string representations of the objects, placing the
1724 * system-specific line separator between them.
1725 * @see plume.ArraysMDE#toString(int[])
1726 **/
1727 public static String joinLines(Object... a) {
1728 return join(a, lineSep);
1729 }
1730
1731 /**
1732 * Concatenate the string representations of the objects, placing the
1733 * delimiter between them.
1734 * @see java.util.AbstractCollection#toString()
1735 **/
1736 public static String join(List<?> v, String delim) {
1737 if (v.size() == 0) return "";
1738 if (v.size() == 1) return v.get(0).toString();
1739 // This should perhaps use an iterator rather than get().
1740 StringBuffer sb = new StringBuffer(v.get(0).toString());
1741 for (int i=1; i<v.size(); i++)
1742 sb.append(delim).append(v.get(i));
1743 return sb.toString();
1744 }
1745
1746 /**
1747 * Concatenate the string representations of the objects, placing the
1748 * system-specific line separator between them.
1749 * @see java.util.AbstractCollection#toString()
1750 **/
1751 public static String joinLines(List<String> v, String delim) {
1752 return join(v, lineSep);
1753 }
1754
1755 // Inspired by the 'quote' function in Ajax (but independent code).
1756 /**
1757 * Escape \, ", newline, and carriage-return characters in the
1758 * target as \\, \\", \n, and \r; return a new string if any
1759 * modifications were necessary. The intent is that by surrounding
1760 * the return value with double quote marks, the result will be a
1761 * Java string literal denoting the original string. Previously
1762 * known as quote().
1763 **/
1764 public static String escapeNonJava(String orig) {
1765 StringBuffer sb = new StringBuffer();
1766 // The previous escape character was seen right before this position.
1767 int post_esc = 0;
1768 int orig_len = orig.length();
1769 for (int i=0; i<orig_len; i++) {
1770 char c = orig.charAt(i);
1771 switch (c) {
1772 case '\"':
1773 case '\\':
1774 if (post_esc < i) {
1775 sb.append(orig.substring(post_esc, i));
1776 }
1777 sb.append('\\');
1778 post_esc = i;
1779 break;
1780 case '\n': // not lineSep
1781 if (post_esc < i) {
1782 sb.append(orig.substring(post_esc, i));
1783 }
1784 sb.append("\\n"); // not lineSep
1785 post_esc = i+1;
1786 break;
1787 case '\r':
1788 if (post_esc < i) {
1789 sb.append(orig.substring(post_esc, i));
1790 }
1791 sb.append("\\r");
1792 post_esc = i+1;
1793 break;
1794 default:
1795 // Nothing to do: i gets incremented
1796 }
1797 }
1798 if (sb.length() == 0)
1799 return orig;
1800 sb.append(orig.substring(post_esc));
1801 return sb.toString();
1802 }
1803
1804 // The overhead of this is too high to call in escapeNonJava(String)
1805 public static String escapeNonJava(Character ch) {
1806 char c = ch.charValue();
1807 switch (c) {
1808 case '\"':
1809 return "\\\"";
1810 case '\\':
1811 return "\\\\";
1812 case '\n': // not lineSep
1813 return "\\n"; // not lineSep
1814 case '\r':
1815 return "\\r";
1816 default:
1817 return new String(new char[] { c });
1818 }
1819 }
1820
1821 /**
1822 * Escape unprintable characters in the target, following the usual
1823 * Java backslash conventions, so that the result is sure to be
1824 * printable ASCII. Returns a new string.
1825 **/
1826 public static String escapeNonASCII(String orig) {
1827 StringBuffer sb = new StringBuffer();
1828 int orig_len = orig.length();
1829 for (int i=0; i<orig_len; i++) {
1830 char c = orig.charAt(i);
1831 sb.append(escapeNonASCII(c));
1832 }
1833 return sb.toString();
1834 }
1835
1836 /**
1837 * Like escapeNonJava(), but quote more characters so that the
1838 * result is sure to be printable ASCII. Not particularly optimized.
1839 **/
1840 private static String escapeNonASCII(char c) {
1841 if (c == '"') {
1842 return "\\\"";
1843 } else if (c == '\\') {
1844 return "\\\\";
1845 } else if (c == '\n') { // not lineSep
1846 return "\\n"; // not lineSep
1847 } else if (c == '\r') {
1848 return "\\r";
1849 } else if (c == '\t') {
1850 return "\\t";
1851 } else if (c >= ' ' && c <= '~') {
1852 return new String(new char[] { c });
1853 } else if (c < 256) {
1854 String octal = Integer.toOctalString(c);
1855 while (octal.length() < 3)
1856 octal = '0' + octal;
1857 return "\\" + octal;
1858 } else {
1859 String hex = Integer.toHexString(c);
1860 while (hex.length() < 4)
1861 hex = "0" + hex;
1862 return "\\u" + hex;
1863 }
1864 }
1865
1866 /**
1867 * Replace "\\", "\"", "\n", and "\r" sequences by their
1868 * one-character equivalents. All other backslashes are removed
1869 * (for instance, octal/hex escape sequences are not turned into
1870 * their respective characters). This is the inverse operation of
1871 * escapeNonJava(). Previously known as unquote().
1872 **/
1873 public static String unescapeNonJava(String orig) {
1874 StringBuffer sb = new StringBuffer();
1875 // The previous escape character was seen just before this position.
1876 int post_esc = 0;
1877 int this_esc = orig.indexOf('\\');
1878 while (this_esc != -1) {
1879 if (this_esc == orig.length()-1) {
1880 sb.append(orig.substring(post_esc, this_esc+1));
1881 post_esc = this_esc+1;
1882 break;
1883 }
1884 switch (orig.charAt(this_esc+1)) {
1885 case 'n':
1886 sb.append(orig.substring(post_esc, this_esc));
1887 sb.append('\n'); // not lineSep
1888 post_esc = this_esc+2;
1889 break;
1890 case 'r':
1891 sb.append(orig.substring(post_esc, this_esc));
1892 sb.append('\r');
1893 post_esc = this_esc+2;
1894 break;
1895 case '\\':
1896 // This is not in the default case because the search would find
1897 // the quoted backslash. Here we incluce the first backslash in
1898 // the output, but not the first.
1899 sb.append(orig.substring(post_esc, this_esc+1));
1900 post_esc = this_esc+2;
1901 break;
1902
1903 case '0': case '1': case '2': case '3': case '4':
1904 case '5': case '6': case '7': case '8': case '9':
1905 sb.append(orig.substring(post_esc, this_esc));
1906 char octal_char = 0;
1907 int ii = this_esc+1;
1908 while (ii < orig.length()) {
1909 char ch = orig.charAt(ii++);
1910 if ((ch < '0') || (ch > '8'))
1911 break;
1912 octal_char = (char) ((octal_char * 8)+ Character.digit (ch, 8));
1913 }
1914 sb.append (octal_char);
1915 post_esc = ii-1;
1916 break;
1917
1918 default:
1919 // In the default case, retain the character following the backslash,
1920 // but discard the backslash itself. "\*" is just a one-character string.
1921 sb.append(orig.substring(post_esc, this_esc));
1922 post_esc = this_esc+1;
1923 break;
1924 }
1925 this_esc = orig.indexOf('\\', post_esc);
1926 }
1927 if (post_esc == 0)
1928 return orig;
1929 sb.append(orig.substring(post_esc));
1930 return sb.toString();
1931 }
1932
1933 // Use the built-in String.trim()!
1934 // /** Return the string with all leading and trailing whitespace stripped. */
1935 // public static String trimWhitespace(String s) {
1936 // int len = s.length();
1937 // if (len == 0)
1938 // return s;
1939 // int first_non_ws = 0;
1940 // int last_non_ws = len-1;
1941 // while ((first_non_ws < len) && Character.isWhitespace(s.charAt(first_non_ws)))
1942 // first_non_ws++;
1943 // if (first_non_ws == len)
1944 // return "";
1945 // while (Character.isWhitespace(s.charAt(last_non_ws)))
1946 // last_non_ws--;
1947 // if ((first_non_ws == 0) && (last_non_ws == len))
1948 // return s;
1949 // else
1950 // return s.substring(first_non_ws, last_non_ws+1);
1951 // }
1952 // // // Testing:
1953 // // assert(UtilMDE.trimWhitespace("foo").equals("foo"));
1954 // // assert(UtilMDE.trimWhitespace(" foo").equals("foo"));
1955 // // assert(UtilMDE.trimWhitespace(" foo").equals("foo"));
1956 // // assert(UtilMDE.trimWhitespace("foo ").equals("foo"));
1957 // // assert(UtilMDE.trimWhitespace("foo ").equals("foo"));
1958 // // assert(UtilMDE.trimWhitespace(" foo ").equals("foo"));
1959 // // assert(UtilMDE.trimWhitespace(" foo bar ").equals("foo bar"));
1960 // // assert(UtilMDE.trimWhitespace("").equals(""));
1961 // // assert(UtilMDE.trimWhitespace(" ").equals(""));
1962
1963
1964 /** Remove all whitespace before or after instances of delimiter. **/
1965 public static String removeWhitespaceAround(String arg, String delimiter) {
1966 arg = removeWhitespaceBefore(arg, delimiter);
1967 arg = removeWhitespaceAfter(arg, delimiter);
1968 return arg;
1969 }
1970
1971 /** Remove all whitespace after instances of delimiter. **/
1972 public static String removeWhitespaceAfter(String arg, String delimiter) {
1973 // String orig = arg;
1974 int delim_len = delimiter.length();
1975 int delim_index = arg.indexOf(delimiter);
1976 while (delim_index > -1) {
1977 int non_ws_index = delim_index+delim_len;
1978 while ((non_ws_index < arg.length())
1979 && (Character.isWhitespace(arg.charAt(non_ws_index)))) {
1980 non_ws_index++;
1981 }
1982 // if (non_ws_index == arg.length()) {
1983 // System.out.println("No nonspace character at end of: " + arg);
1984 // } else {
1985 // System.out.println("'" + arg.charAt(non_ws_index) + "' not a space character at " + non_ws_index + " in: " + arg);
1986 // }
1987 if (non_ws_index != delim_index+delim_len) {
1988 arg = arg.substring(0, delim_index + delim_len) + arg.substring(non_ws_index);
1989 }
1990 delim_index = arg.indexOf(delimiter, delim_index+1);
1991 }
1992 return arg;
1993 }
1994
1995 /** Remove all whitespace before instances of delimiter. **/
1996 public static String removeWhitespaceBefore(String arg, String delimiter) {
1997 // System.out.println("removeWhitespaceBefore(\"" + arg + "\", \"" + delimiter + "\")");
1998 // String orig = arg;
1999 int delim_len = delimiter.length();
2000 int delim_index = arg.indexOf(delimiter);
2001 while (delim_index > -1) {
2002 int non_ws_index = delim_index-1;
2003 while ((non_ws_index >= 0)
2004 && (Character.isWhitespace(arg.charAt(non_ws_index)))) {
2005 non_ws_index--;
2006 }
2007 // if (non_ws_index == -1) {
2008 // System.out.println("No nonspace character at front of: " + arg);
2009 // } else {
2010 // System.out.println("'" + arg.charAt(non_ws_index) + "' not a space character at " + non_ws_index + " in: " + arg);
2011 // }
2012 if (non_ws_index != delim_index-1) {
2013 arg = arg.substring(0, non_ws_index + 1) + arg.substring(delim_index);
2014 }
2015 delim_index = arg.indexOf(delimiter, non_ws_index+2);
2016 }
2017 return arg;
2018 }
2019
2020
2021 // @return either "n noun" or "n nouns" depending on n
2022 public static String nplural(int n, String noun) {
2023 if (n == 1)
2024 return n + " " + noun;
2025 else if (noun.endsWith("s") || noun.endsWith("x") ||
2026 noun.endsWith("ch") || noun.endsWith("sh"))
2027 return n + " " + noun + "es";
2028 else
2029 return n + " " + noun + "s";
2030 }
2031
2032
2033 // Returns a string of the specified length, truncated if necessary,
2034 // and padded with spaces to the left if necessary.
2035 public static String lpad(String s, int length) {
2036 if (s.length() < length) {
2037 StringBuffer buf = new StringBuffer();
2038 for (int i = s.length(); i < length; i++) {
2039 buf.append(' ');
2040 }
2041 return buf.toString() + s;
2042 } else {
2043 return s.substring(0, length);
2044 }
2045 }
2046
2047 // Returns a string of the specified length, truncated if necessary,
2048 // and padded with spaces to the right if necessary.
2049 public static String rpad(String s, int length) {
2050 if (s.length() < length) {
2051 StringBuffer buf = new StringBuffer(s);
2052 for (int i = s.length(); i < length; i++) {
2053 buf.append(' ');
2054 }
2055 return buf.toString();
2056 } else {
2057 return s.substring(0, length);
2058 }
2059 }
2060
2061 // Converts the int to a String, then formats it using rpad
2062 public static String rpad(int num, int length) {
2063 return rpad(String.valueOf(num), length);
2064 }
2065
2066 // Converts the double to a String, then formats it using rpad
2067 public static String rpad(double num, int length) {
2068 return rpad(String.valueOf(num), length);
2069 }
2070
2071 // Same as built-in String comparison, but accept null arguments,
2072 // and place them at the beginning.
2073 public static class NullableStringComparator
2074 implements Comparator<String>
2075 {
2076 public int compare(String s1, String s2) {
2077 if (s1 == null && s2 == null) return 0;
2078 if (s1 == null && s2 != null) return 1;
2079 if (s1 != null && s2 == null) return -1;
2080 return s1.compareTo(s2);
2081 }
2082 }
2083
2084 /** Return the number of times the character appears in the string. **/
2085 public static int count(String s, int ch) {
2086 int result = 0;
2087 int pos = s.indexOf(ch);
2088 while (pos > -1) {
2089 result++;
2090 pos = s.indexOf(ch, pos+1);
2091 }
2092 return result;
2093 }
2094
2095 /** Return the number of times the second string appears in the first. **/
2096 public static int count(String s, String sub) {
2097 int result = 0;
2098 int pos = s.indexOf(sub);
2099 while (pos > -1) {
2100 result++;
2101 pos = s.indexOf(sub, pos+1);
2102 }
2103 return result;
2104 }
2105
2106
2107 ///////////////////////////////////////////////////////////////////////////
2108 /// StringTokenizer
2109 ///
2110
2111 /**
2112 * Return a Vector of the Strings returned by
2113 * {@link java.util.StringTokenizer#StringTokenizer(String,String,boolean)} with the given arguments.
2114 * <p>
2115 * The static type is Vector<Object> because StringTokenizer extends
2116 * Enumeration<Object> instead of Enumeration<String> as it should
2117 * (probably due to backward-compatibility).
2118 **/
2119 public static Vector<Object> tokens(String str, String delim, boolean returnTokens) {
2120 return makeVector(new StringTokenizer(str, delim, returnTokens));
2121 }
2122
2123 /**
2124 * Return a Vector of the Strings returned by
2125 * {@link java.util.StringTokenizer#StringTokenizer(String,String)} with the given arguments.
2126 **/
2127 public static Vector<Object> tokens(String str, String delim) {
2128 return makeVector(new StringTokenizer(str, delim));
2129 }
2130
2131 /**
2132 * Return a Vector of the Strings returned by
2133 * {@link java.util.StringTokenizer#StringTokenizer(String)} with the given arguments.
2134 **/
2135 public static Vector<Object> tokens(String str) {
2136 return makeVector(new StringTokenizer(str));
2137 }
2138
2139
2140
2141 ///////////////////////////////////////////////////////////////////////////
2142 /// Throwable
2143 ///
2144
2145 /** For the current backtrace, do "backtrace(new Throwable())". **/
2146 public static String backTrace(Throwable t) {
2147 StringWriter sw = new StringWriter();
2148 PrintWriter pw = new PrintWriter(sw);
2149 t.printStackTrace(pw);
2150 pw.close();
2151 String result = sw.toString();
2152 return result;
2153 }
2154
2155 // Deprecated as of 2/1/2004.
2156 /**
2157 * @deprecated use "backtrace(new Throwable())" instead, to avoid
2158 * spurious "at plume.UtilMDE.backTrace(UtilMDE.java:1491)" in output.
2159 * @see #backTrace(Throwable)
2160 **/
2161 @Deprecated
2162 public static String backTrace() {
2163 StringWriter sw = new StringWriter();
2164 PrintWriter pw = new PrintWriter(sw);
2165 new Throwable().printStackTrace(pw);
2166 pw.close();
2167 String result = sw.toString();
2168 // TODO: should remove "at plume.UtilMDE.backTrace(UtilMDE.java:1491)"
2169 return result;
2170 }
2171
2172
2173 ///////////////////////////////////////////////////////////////////////////
2174 /// Collections
2175 ///
2176
2177 /**
2178 * Returns the sorted version of the list. Does not alter the list.
2179 * Simply calls Collections.sort(List<T>, Comparator<? super T>).
2180 **/
2181 public static <T> List<T> sortList (List<T> l, Comparator<? super T> c) {
2182 List<T> result = new ArrayList<T>(l);
2183 Collections.sort(result, c);
2184 return result;
2185 }
2186
2187
2188 /**
2189 * Returns a copy of the list with duplicates removed.
2190 * Retains the original order.
2191 **/
2192 public static <T> List<T> removeDuplicates(List<T> l) {
2193 // There are shorter solutions that do not maintain order.
2194 HashSet<T> hs = new HashSet<T>(l.size());
2195 List<T> result = new ArrayList<T>();
2196 for (T t : l) {
2197 if (hs.add(t)) {
2198 result.add(t);
2199 }
2200 }
2201 return result;
2202 }
2203
2204
2205 ///////////////////////////////////////////////////////////////////////////
2206 /// Vector
2207 ///
2208
2209 /** Returns a vector containing the elements of the enumeration. */
2210 public static <T> Vector<T> makeVector(Enumeration<T> e) {
2211 Vector<T> result = new Vector<T>();
2212 while (e.hasMoreElements()) {
2213 result.addElement(e.nextElement());
2214 }
2215 return result;
2216 }
2217
2218 // Rather than writing something like VectorToStringArray, use
2219 // v.toArray(new String[0])
2220
2221
2222 /**
2223 * Returns a list of lists of each combination (with repetition, but
2224 * not permutations) of the specified objects starting at index
2225 * start over dims dimensions, for dims > 0.
2226 *
2227 * For example, create_combinations (1, 0, {a, b, c}) returns:
2228 * {a}, {b}, {c}
2229 *
2230 * And create_combinations (2, 0, {a, b, c}) returns:
2231 *
2232 * {a, a}, {a, b}, {a, c}
2233 * {b, b}, {b, c},
2234 * {c, c}
2235 */
2236 public static <T> List<List<T>> create_combinations (int dims, int start, List<T> objs) {
2237
2238 if (dims < 1) throw new IllegalArgumentException();
2239
2240 List<List<T>> results = new ArrayList<List<T>>();
2241
2242 for (int i = start; i < objs.size(); i++) {
2243 if (dims == 1) {
2244 List<T> simple = new ArrayList<T>();
2245 simple.add (objs.get(i));
2246 results.add (simple);
2247 } else {
2248 List<List<T>> combos = create_combinations (dims-1, i, objs);
2249 for (Iterator<List<T>> j = combos.iterator(); j.hasNext(); ) {
2250 List<T> simple = new ArrayList<T>();
2251 simple.add (objs.get(i));
2252 simple.addAll (j.next());
2253 results.add (simple);
2254 }
2255 }
2256 }
2257
2258 return (results);
2259 }
2260
2261 /**
2262 * Returns a list of lists of each combination (with repetition, but
2263 * not permutations) of integers from start to cnt (inclusive) over
2264 * arity dimensions.
2265 *
2266 * For example, create_combinations (1, 0, 2) returns:
2267 * {0}, {1}, {2}
2268 *
2269 * And create_combinations (2, 0, 2) returns:
2270 *
2271 * {0, 0}, {0, 1}, {0, 2}
2272 * {1, 1} {1, 2},
2273 * {2, 2}
2274 */
2275 public static ArrayList<ArrayList<Integer>> create_combinations (int arity, int start, int cnt) {
2276
2277 ArrayList<ArrayList<Integer>> results = new ArrayList<ArrayList<Integer>>();
2278
2279 // Return a list with one zero length element if arity is zero
2280 if (arity == 0) {
2281 results.add (new ArrayList<Integer>());
2282 return (results);
2283 }
2284
2285 for (int i = start; i <= cnt; i++) {
2286 ArrayList<ArrayList<Integer>> combos = create_combinations (arity-1, i, cnt);
2287 for (Iterator<ArrayList<Integer>> j = combos.iterator(); j.hasNext(); ) {
2288 ArrayList<Integer> simple = new ArrayList<Integer>();
2289 simple.add (new Integer(i));
2290 simple.addAll (j.next());
2291 results.add (simple);
2292 }
2293 }
2294
2295 return (results);
2296
2297 }
2298
2299 /**
2300 * Returns the simple unqualified class name that corresponds to the
2301 * specified fully qualified name. For example if qualified name
2302 * is java.lang.String, String will be returned.
2303 **/
2304 public static String unqualified_name (String qualified_name) {
2305
2306 int offset = qualified_name.lastIndexOf ('.');
2307 if (offset == -1)
2308 return (qualified_name);
2309 return (qualified_name.substring (offset+1));
2310 }
2311
2312 /**
2313 * Returns the simple unqualified class name that corresponds to the
2314 * specified class. For example if qualified name of the class
2315 * is java.lang.String, String will be returned.
2316 **/
2317 public static String unqualified_name (Class<?> cls) {
2318
2319 return (unqualified_name (cls.getName()));
2320 }
2321
2322
2323 // This name "human_readable" is terrible.
2324 /**
2325 * Convert a number into an abbreviation such as "5.00K" for 5000 or
2326 * "65.0M" for 65000000. K stands for 1000, not 1024; M stands for
2327 * 1000000, not 1048576, etc. There are always exactly 3 decimal digits
2328 * of precision in the result (counting both sides of the decimal point).
2329 */
2330 public static String human_readable (long val) {
2331
2332 double dval = (double) val;
2333 String mag = "";
2334
2335 if (val < 1000)
2336 ;
2337 else if (val < 1000000) {
2338 dval = val / 1000.0;
2339 mag = "K";
2340 } else if (val < 1000000000) {
2341 dval = val / 1000000.0;
2342 mag = "M";
2343 } else {
2344 dval = val / 1000000000.0;
2345 mag = "G";
2346 }
2347
2348 String precision = "0";
2349 if (dval < 10)
2350 precision = "2";
2351 else if (dval < 100)
2352 precision = "1";
2353
2354 return String.format ("%,1." + precision + "f" + mag, dval);
2355
2356 }
2357
2358 }