Index: third_party/jmake/src/org/pantsbuild/jmake/Utils.java |
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/Utils.java b/third_party/jmake/src/org/pantsbuild/jmake/Utils.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..38566a977847df84b9427e3a71c5eaeb4acf6dbb |
--- /dev/null |
+++ b/third_party/jmake/src/org/pantsbuild/jmake/Utils.java |
@@ -0,0 +1,355 @@ |
+/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved |
+ * |
+ * This program is distributed under the terms of |
+ * the GNU General Public License Version 2. See the LICENSE file |
+ * at the top of the source tree. |
+ */ |
+package org.pantsbuild.jmake; |
+ |
+import java.io.File; |
+import java.io.FileInputStream; |
+import java.io.IOException; |
+import java.io.InputStream; |
+import java.io.OutputStream; |
+import java.io.PrintStream; |
+import java.util.zip.ZipEntry; |
+import java.util.zip.ZipFile; |
+ |
+/** |
+ * Utility functions used by other classes from this package. |
+ * |
+ * @author Misha Dmitriev |
+ * 23 January 2003 |
+ */ |
+public class Utils { |
+ |
+ static final String REPORT_PROBLEM = |
+ "Please report this problem to Mikhail.Dmitriev@sun.com"; |
+ static final byte[] MAGIC = {'J', 'a', 'v', 'a', 'm', 'a', 'k', 'e', ' ', 'P', 'r', 'o', 'j', 'e', 'c', 't', ' ', 'D', 'a', 't', 'a', 'b', 'a', 's', 'e', ' ', 'F', 'i', 'l', 'e'}; |
+ static final int magicLength = MAGIC.length; |
+ static final int PDB_FORMAT_CODE_OLD = 1; |
+ static final int PDB_FORMAT_CODE_133 = 0x01030300; |
+ static final int PDB_FORMAT_CODE_LATEST = PDB_FORMAT_CODE_133; |
+ static final int JAVAC_TARGET_RELEASE_OLDEST = 0x01040000; // 1.4 and previous versions |
+ static final int JAVAC_TARGET_RELEASE_15 = 0x01050000; // if class is compiled with -target 1.5 |
+ static final int JAVAC_TARGET_RELEASE_16 = 0x01060000; // if class is compiled with -target 1.6 |
+ static final int JAVAC_TARGET_RELEASE_17 = 0x01070000; // if class is compiled with -target 1.7 |
+ static final int JAVAC_TARGET_RELEASE_18 = 0x01080000; // if class is compiled with -target 1.8 |
+ static int warningLimit = 20; // Maximum number of warnings to print |
+ static final int TIMING_TOTAL = 0; |
+ static final int TIMING_PDBREAD = 1; |
+ static final int TIMING_SYNCHRO = 2; |
+ static final int TIMING_SYNCHRO_CHECK_JAVA_FILES = 3; |
+ static final int TIMING_FIND_UPDATED_JAVA_FILES = 4; |
+ static final int TIMING_CLASS_FILE_OBSOLETE_OR_DELETED = 5; |
+ static final int TIMING_COMPILE = 6; |
+ static final int TIMING_FIND_UPDATED_CLASSES = 7; |
+ static final int TIMING_CHECK_UPDATED_CLASSES = 8; |
+ static final int TIMING_PDBWRITE = 9; |
+ static final int TIMING_SYNCHRO_CHECK_TMP = 10; |
+ static final int TIMING_CLASS_FILE_OBSOLETE_TMP = 11; |
+ static final int TIMING_PDBUPDATE = 12; |
+ static final int TIMING_ARRAY_LENGTH = 13; |
+ private static long timings[] = new long[TIMING_ARRAY_LENGTH]; |
+ private static boolean timingOn = false; |
+ |
+ |
+ // ------------------------------------------------------------------------------- |
+ // Name manipulation stuff |
+ // ------------------------------------------------------------------------------- |
+ /** |
+ * Returns package name for the given class. In case of no package, returns an |
+ * empty, but non-null string. Returned string is interned. |
+ */ |
+ public static String getPackageName(String clazzName) { |
+ int ldi = clazzName.lastIndexOf('/'); // For convenience, we use system-internal slashes, not dots |
+ if (ldi == -1) { |
+ return ""; |
+ } else { |
+ return clazzName.substring(0, ldi).intern(); |
+ } |
+ } |
+ |
+ /** |
+ * Returns directly enclosing class name for the given class. If the given class is not a |
+ * nested class, returns empty, but non-null string. Returned string is interned. |
+ * NOTE FOR JDK 1.5: this function has to work with both old (1.4 and before) and new (1.5) ways |
+ * of naming non-member classes. javacTargetRelease determines the javac version for this class; |
+ * however on rare occasions (when checking a deleted non-project class) it may be 0, denoting |
+ * that javac version is not known. |
+ * In that case, we use the old algorithm, which is error-prone due to a bug in nested class |
+ * naming that existed prior to JDK 1.5, where both a non-member local nested class B of A, and a |
+ * member nested class B of anonymous class A$1, are named A$1$B. |
+ */ |
+ public static String getDirectlyEnclosingClass(String clazzName, int javacTargetRelease) { |
+ int ldi = clazzName.lastIndexOf('$'); |
+ if (ldi == -1) { |
+ return ""; |
+ } |
+ |
+ if (javacTargetRelease >= JAVAC_TARGET_RELEASE_15) { |
+ return clazzName.substring(0, ldi).intern(); |
+ } else { // JAVAC_TARGET_RELEASE_OLDEST or unknown |
+ // Take into account local classes which are named like "EncClass$1$LocalClass", where EncClass |
+ // is directly enclosing class. |
+ int lldi = clazzName.lastIndexOf('$', ldi - 1); |
+ if (lldi == -1 || !Character.isDigit(clazzName.charAt(lldi + 1))) { |
+ return clazzName.substring(0, ldi).intern(); |
+ } else { |
+ return clazzName.substring(0, lldi).intern(); |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Returns top-level enclosing class name for the given class. If the given class is not a |
+ * nested class, returns empty, but non-null string. Returned string is interned. |
+ */ |
+ public static String getTopLevelEnclosingClass(String clazzName) { |
+ int fdi = clazzName.indexOf('$'); |
+ if (fdi == -1) { |
+ return ""; |
+ } |
+ |
+ return clazzName.substring(0, fdi).intern(); |
+ } |
+ |
+ /** |
+ * Given the full path for the enclosing class file and the full name for the nested class, return the supposed |
+ * full path for the nested class. |
+ */ |
+ public static String getClassFileFullPathForNestedClass(String enclosingClassFileFullPath, String nestedClassFullName) { |
+ String enclosingClassDir = enclosingClassFileFullPath; |
+ int cutIndex = enclosingClassDir.lastIndexOf(File.separatorChar); |
+ enclosingClassDir = enclosingClassDir.substring(0, cutIndex + 1); // If slash is present, it's included, otherwise we get "" |
+ cutIndex = nestedClassFullName.lastIndexOf('/'); |
+ String nestedClassLocalName; |
+ if (cutIndex < 0) { |
+ nestedClassLocalName = nestedClassFullName; |
+ } else { |
+ nestedClassLocalName = nestedClassFullName.substring(cutIndex + 1); |
+ } |
+ return enclosingClassDir + nestedClassLocalName + ".class"; |
+ } |
+ |
+ /** |
+ * For two strings representing signatures, check if the number of parameters in |
+ * both is the same. |
+ */ |
+ public static boolean sameParamNumber(String sig1, String sig2) { |
+ return getParamNumber(sig1) == getParamNumber(sig2); |
+ } |
+ |
+ private static int getParamNumber(String sig) { |
+ char ch; |
+ int parNo = 0, pos = 0; |
+ do { |
+ ch = sig.charAt(++pos); |
+ if (ch == ')') { |
+ break; |
+ } |
+ while (ch == '[') { |
+ ch = sig.charAt(++pos); |
+ } |
+ parNo++; |
+ if (ch == '@') { |
+ // We replaced all "Lclassname;" in signatures with "@classname#" |
+ while (ch != '#') { |
+ ch = sig.charAt(++pos); |
+ } |
+ } |
+ } while (ch != ')'); |
+ return parNo; |
+ } |
+ |
+ |
+ // ------------------------------------------------------------------------------- |
+ // File related stuff |
+ // ------------------------------------------------------------------------------- |
+ public static File checkFileForName(String name) { |
+ // For each .java file, a File object is created two times when jmake executes: first when we synchronise the PCD |
+ // and the supplied .java file list (we make sure that the .java file exists), and second time when we check if a class |
+ // file was updated (we compare time stamps of the .java and the .class file). I tried to call this routine for a .java |
+ // class both times, and cached File objects, but it looks as if this does not bring any real speed-up (and in fact may |
+ // even slow down the application). Most of the time seems to go to the underlying code creating internal File |
+ // representation; once it is created, it takes little time to execute another "new File()" for it. Also, all operations |
+ // on files like getCanonicalPath() or lastModified() seem to be quite expensive, so their unnecessary repetition should |
+ // be avoided as much as possible. |
+ if (name == null) { |
+ return null; |
+ } |
+ File file = new File(name); |
+ if (file.exists()) { |
+ return file; |
+ } |
+ return null; |
+ } |
+ |
+ public static File checkOrCreateDirForName(String name) { |
+ File file = new File(name); |
+ if (!file.exists()) { |
+ file.mkdirs(); |
+ } |
+ if (file.exists()) { |
+ if (!file.isDirectory()) { |
+ throw new PrivateException(new PublicExceptions.InternalException(file + " is not a directory.")); |
+ } |
+ return file; |
+ } |
+ return null; |
+ } |
+ |
+ public static byte[] readFileIntoBuffer(File file) { |
+ try { |
+ InputStream in = new FileInputStream(file); |
+ int len = (int) file.length(); |
+ return readInputStreamIntoBuffer(in, len); |
+ } catch (IOException e) { |
+ throw new PrivateException(e); |
+ } |
+ } |
+ |
+ public static byte[] readZipEntryIntoBuffer(ZipFile file, ZipEntry entry) { |
+ try { |
+ InputStream in = file.getInputStream(entry); |
+ int len = (int) entry.getSize(); |
+ return Utils.readInputStreamIntoBuffer(in, len); |
+ } catch (IOException e) { |
+ throw new PrivateException(e); |
+ } |
+ } |
+ |
+ public static byte[] readInputStreamIntoBuffer(InputStream in, int len) throws IOException { |
+ byte buf[] = new byte[len]; |
+ int readBytes, ofs = 0, remBytes = len; |
+ do { |
+ readBytes = in.read(buf, ofs, remBytes); |
+ ofs += readBytes; |
+ remBytes -= readBytes; |
+ } while (ofs < len); |
+ in.close(); |
+ return buf; |
+ } |
+ |
+ public static void readAndPrintBytesFromStream(InputStream in, OutputStream out) throws IOException { |
+ int avail = in.available(); |
+ if (avail > 0) { |
+ byte outbytes[] = new byte[avail]; |
+ int realOutBytes = in.read(outbytes); |
+ out.write(outbytes, 0, realOutBytes); |
+ } |
+ } |
+ |
+ /** For a Windows path, convert the drive letter to the lower case */ |
+ public static String convertDriveLetterToLowerCase(String path) { |
+ if (path.charAt(1) != ':') { |
+ return path; |
+ } |
+ char drive = path.charAt(0); |
+ if (Character.isUpperCase(drive)) { |
+ drive = Character.toLowerCase(drive); |
+ char[] chars = path.toCharArray(); |
+ chars[0] = drive; |
+ path = new String(chars); |
+ } |
+ return path; |
+ } |
+ |
+ public static void ignore(Exception e) { |
+ // Ignore this exception |
+ } |
+ |
+ /** Used when invoking a third-party executable compiler app */ |
+ public static void delay(int ms) { |
+ Object o = new Object(); |
+ synchronized (o) { |
+ try { |
+ o.wait(ms); |
+ } catch (InterruptedException e) { |
+ } |
+ } |
+ } |
+ // ------------------------------------------------------------------------------- |
+ // Custom printing stuff |
+ // ------------------------------------------------------------------------------- |
+ private static PrintStream out = System.out; |
+ private static PrintStream warn = System.out; |
+ private static PrintStream err = System.err; |
+ private static boolean printInfoMessages = true; |
+ private static boolean printWarningMessages = true; |
+ private static boolean printErrorMessages = true; |
+ private static int warningNo; |
+ |
+ public static void setOutputStreams(PrintStream out, PrintStream warn, PrintStream err) { |
+ Utils.out = out; |
+ Utils.warn = warn; |
+ Utils.err = err; |
+ } |
+ |
+ public static void customizeOutput(boolean printInfoMessages, boolean printWarningMessages, boolean printErrorMessages) { |
+ Utils.printInfoMessages = printInfoMessages; |
+ Utils.printWarningMessages = printWarningMessages; |
+ Utils.printErrorMessages = printErrorMessages; |
+ } |
+ |
+ public static void printInfoMessage(String message) { |
+ if (printInfoMessages) { |
+ out.println(message); |
+ } |
+ } |
+ |
+ public static void printInfoMessageNoEOL(String message) { |
+ if (printInfoMessages) { |
+ out.print(message); |
+ } |
+ } |
+ |
+ public static void printWarningMessage(String message) { |
+ if (!printWarningMessages) { |
+ return; |
+ } |
+ if (warningNo < warningLimit) { |
+ warn.println(message); |
+ } else if (warningNo == warningLimit) { |
+ warn.println("jmake: more than " + warningLimit + " warnings."); |
+ } |
+ warningNo++; |
+ } |
+ |
+ public static void printErrorMessage(String message) { |
+ if (printErrorMessages) { |
+ err.println("jmake: " + message); |
+ } |
+ } |
+ |
+ // ------------------------------------------------------------------------------- |
+ // Measuring stuff |
+ // ------------------------------------------------------------------------------- |
+ public static void setTimingOn() { |
+ timingOn = true; |
+ } |
+ |
+ public static void startTiming(int slot) { |
+ timings[slot] = System.currentTimeMillis(); |
+ } |
+ |
+ public static void stopAndPrintTiming(String message, int slot) { |
+ if (timingOn) { |
+ long time = System.currentTimeMillis() - timings[slot]; |
+ printInfoMessage("========== " + message + " time = " + time); |
+ } |
+ } |
+ |
+ public static void printTiming(String message, int slot) { |
+ if (timingOn) { |
+ printInfoMessage("========== " + message + " time = " + timings[slot]); |
+ } |
+ } |
+ |
+ public static void stopAndAddTiming(int slot1, int slot2) { |
+ if (timingOn) { |
+ long time = System.currentTimeMillis() - timings[slot1]; |
+ timings[slot2] += time; |
+ } |
+ } |
+} |