Remove Most of dx-tests
Strip out the dx-tests that no longer compile. The build doesn't
break, because the compilation occurs in a script. These tests
haven't been running as part of CTS and are not maintained at all.
Keep the cfassembler module since that is needed by the vm-tests
which are still maintained. Rename the dx-tests directory to
cfassembler, since that is all that is in it now.
Change-Id: I038054ad0703bdd00af3b7b1012ea9ad3eabd8a6
diff --git a/tools/cfassembler/Android.mk b/tools/cfassembler/Android.mk
new file mode 100644
index 0000000..8e0f351
--- /dev/null
+++ b/tools/cfassembler/Android.mk
@@ -0,0 +1,47 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# cfassembler host module
+#============================================================
+
+include $(CLEAR_VARS)
+
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := cfassembler
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(HOST_OUT_JAVA_LIBRARIES)/cfassembler$(COMMON_JAVA_PACKAGE_SUFFIX)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/cfassembler | $(ACP)
+ @echo "Copy: $(PRIVATE_MODULE) ($@)"
+ $(copy-file-to-new-target)
+ $(hide) chmod 755 $@
+
+INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
+
+# cfassembler java library
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := src/dxconvext/ClassFileAssembler.java src/dxconvext/util/FileUtils.java
+LOCAL_JAR_MANIFEST := etc/cfassembler_manifest.txt
+
+LOCAL_MODULE:= cfassembler
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
diff --git a/tools/cfassembler/etc/cfassembler b/tools/cfassembler/etc/cfassembler
new file mode 100644
index 0000000..e05f4ae
--- /dev/null
+++ b/tools/cfassembler/etc/cfassembler
@@ -0,0 +1,66 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+jarfile=cfassembler.jar
+libdir="$progdir"
+if [ ! -r "$libdir/$jarfile" ]
+then
+ libdir=`dirname "$progdir"`/tools/lib
+fi
+if [ ! -r "$libdir/$jarfile" ]
+then
+ libdir=`dirname "$progdir"`/framework
+fi
+if [ ! -r "$libdir/$jarfile" ]
+then
+ echo `basename "$prog"`": can't find $jarfile"
+ exit 1
+fi
+
+javaOpts=""
+while expr "x$1" : 'x-J' >/dev/null; do
+ opt=`expr "$1" : '-J\(.*\)'`
+ javaOpts="${javaOpts} -${opt}"
+ shift
+done
+
+if [ "$OSTYPE" = "cygwin" ] ; then
+ jarpath=`cygpath -w "$libdir/$jarfile"`
+else
+ jarpath="$libdir/$jarfile"
+fi
+
+exec java $javaOpts -jar "$jarpath" "$@"
diff --git a/tools/cfassembler/etc/cfassembler_manifest.txt b/tools/cfassembler/etc/cfassembler_manifest.txt
new file mode 100644
index 0000000..382adc6
--- /dev/null
+++ b/tools/cfassembler/etc/cfassembler_manifest.txt
@@ -0,0 +1 @@
+Main-Class: dxconvext.ClassFileAssembler
diff --git a/tools/cfassembler/src/dxconvext/ClassFileAssembler.java b/tools/cfassembler/src/dxconvext/ClassFileAssembler.java
new file mode 100644
index 0000000..09eec27
--- /dev/null
+++ b/tools/cfassembler/src/dxconvext/ClassFileAssembler.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dxconvext;
+
+import dxconvext.util.FileUtils;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.zip.Adler32;
+
+public class ClassFileAssembler {
+
+ /**
+ * @param args
+ */
+ public static void main(String[] args) {
+ ClassFileAssembler cfa = new ClassFileAssembler();
+ cfa.run(args);
+ }
+
+ private void run(String[] args) {
+ // this class can be used to generate .class files that are somehow
+ // damaged in order to test the dalvik vm verifier.
+ // The input is a .cfh (class file hex) file.
+ // The output is a java vm .class file.
+ // The .cfh files can be generated as follows:
+ // 1. create the initial .cfh file from an existing .class files by using
+ // the ClassFileParser
+ // 2. modify some bytes to damage the structure of the .class file in a
+ // way that would not be possible with e.g. jasmin (otherwise you are
+ // better off using jasmin).
+ // Uncomment the original bytes, and write "MOD:" meaning a modified
+ // entry (with the original commented out)
+ //
+ // Use the ClassFileAssembler to generate the .class file.
+ // this class here simply takes all non-comment lines from the .cfh
+ // file, parses them as hex values and writes the bytes to the class file
+ File cfhF = new File(args[0]);
+ if (!cfhF.getName().endsWith(".cfh") &&
+ !cfhF.getName().endsWith(".dfh")) {
+ System.out.println("file must be a .cfh or .dfh file, and its filename end with .cfh or .dfh");
+ return;
+ }
+
+ String outBase = args[1];
+
+ boolean isDex = cfhF.getName().endsWith(".dfh");
+
+ byte[] cfhbytes = FileUtils.readFile(cfhF);
+ ByteArrayInputStream bais = new ByteArrayInputStream(cfhbytes);
+ // encoding should not matter, since we are skipping comment lines and parsing
+ try {
+ // get the package name
+ BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(cfhF)));
+ String firstLine = br.readLine();
+ br.close();
+ String classHdr = "//@class:";
+ String dexHdr = "// Processing '";
+ String hdr;
+ if(isDex)
+ hdr = dexHdr;
+ else
+ hdr = classHdr;
+
+ if (!firstLine.startsWith(hdr)) throw new RuntimeException("wrong format:"+firstLine +" isDex=" + isDex);
+ String tFile;
+ if(isDex) {
+ tFile = outBase + "/classes.dex";
+ } else {
+ String classO = firstLine.substring(hdr.length()).trim();
+ tFile = outBase +"/"+classO+".class";
+ }
+ File outFile = new File(tFile);
+ System.out.println("outfile:" + outFile);
+ String mkdir = tFile.substring(0, tFile.lastIndexOf("/"));
+ new File(mkdir).mkdirs();
+
+ Reader r = new InputStreamReader(bais,"utf-8");
+ OutputStream os = new FileOutputStream(outFile);
+ BufferedOutputStream bos = new BufferedOutputStream(os);
+ writeClassFile(r, bos, isDex);
+ bos.close();
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException("problem while parsing .dfh or .cfh file: "+cfhF.getAbsolutePath(), e);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException("problem while parsing .dfh or .cfh file: "+cfhF.getAbsolutePath(), e);
+ } catch (IOException e) {
+ throw new RuntimeException("problem while parsing .dfh or .cfh file: "+cfhF.getAbsolutePath(), e);
+ }
+ }
+
+ /**
+ * Calculates the signature for the <code>.dex</code> file in the
+ * given array, and modify the array to contain it.
+ *
+ * Originally from com.android.dx.dex.file.DexFile.
+ *
+ * @param bytes non-null; the bytes of the file
+ */
+ private void calcSignature(byte[] bytes) {
+ MessageDigest md;
+
+ try {
+ md = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException ex) {
+ throw new RuntimeException(ex);
+ }
+
+ md.update(bytes, 32, bytes.length - 32);
+
+ try {
+ int amt = md.digest(bytes, 12, 20);
+ if (amt != 20) {
+ throw new RuntimeException("unexpected digest write: " + amt +
+ " bytes");
+ }
+ } catch (DigestException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * Calculates the checksum for the <code>.dex</code> file in the
+ * given array, and modify the array to contain it.
+ *
+ * Originally from com.android.dx.dex.file.DexFile.
+ *
+ * @param bytes non-null; the bytes of the file
+ */
+ private void calcChecksum(byte[] bytes) {
+ Adler32 a32 = new Adler32();
+
+ a32.update(bytes, 12, bytes.length - 12);
+
+ int sum = (int) a32.getValue();
+
+ bytes[8] = (byte) sum;
+ bytes[9] = (byte) (sum >> 8);
+ bytes[10] = (byte) (sum >> 16);
+ bytes[11] = (byte) (sum >> 24);
+ }
+
+ public void writeClassFile(Reader r, OutputStream rOs, boolean isDex) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(8192);
+ BufferedReader br = new BufferedReader(r);
+ String line;
+ String secondLine = null;
+ int lineCnt = 0;
+ try {
+ while ((line = br.readLine()) != null) {
+ if (isDex && lineCnt++ == 1) {
+ secondLine = line;
+ }
+ // skip it if it is a comment
+ if (!line.trim().startsWith("//")) {
+ // we have a row like " ae 08 21 ff" etc.
+ String[] parts = line.split("\\s+");
+ for (int i = 0; i < parts.length; i++) {
+ String part = parts[i].trim();
+ if (!part.equals("")) {
+ int res = Integer.parseInt(part, 16);
+ baos.write(res);
+ }
+ }
+ }
+ }
+
+ // now for dex, update the checksum and the signature.
+ // special case:
+ // for two tests (currently T_f1_9.dfh and T_f1_10.dfh), we need
+ // to keep the checksum or the signature, respectively.
+ byte[] outBytes = baos.toByteArray();
+ if (isDex) {
+ boolean leaveChecksum = secondLine.contains("//@leaveChecksum");
+ boolean leaveSignature= secondLine.contains("//@leaveSignature");
+ // update checksum and signature for dex file
+ if(!leaveSignature)
+ calcSignature(outBytes);
+ if(!leaveChecksum)
+ calcChecksum(outBytes);
+ }
+ rOs.write(outBytes);
+ rOs.close();
+ } catch (IOException e) {
+ throw new RuntimeException("problem while writing file",e);
+ }
+ }
+
+}
diff --git a/tools/cfassembler/src/dxconvext/ClassFileParser.java b/tools/cfassembler/src/dxconvext/ClassFileParser.java
new file mode 100644
index 0000000..8a43396
--- /dev/null
+++ b/tools/cfassembler/src/dxconvext/ClassFileParser.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dxconvext;
+
+import com.android.dx.cf.direct.ClassPathOpener;
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.FileUtils;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+public class ClassFileParser {
+
+ private BufferedWriter bw; // the writer to write the result to.
+
+ /**
+ * Parses a .class file and outputs a .cfh (class file in hex format) file.
+ *
+ * args[0] is the absolute path to the java src directory e.g.
+ * /home/fjost/android/workspace/dxconverter/src
+ *
+ * args[1] is the absolute path to the classes directory e.g.
+ * /home/fjost/android/workspace/out/classes_javac this is the place where
+ *
+ * args[2] is the absolute path to the java source file, e.g.
+ * /home/fjost/android/workspace/dxconverter/src/test/MyTest.java
+ *
+ *
+ *
+ * @param args
+ */
+ public static void main(String[] args) throws IOException {
+ ClassFileParser cfp = new ClassFileParser();
+ cfp.process(args[0], args[1], args[2]);
+ }
+
+ private void process(final String srcDir, final String classesDir,
+ final String absSrcFilePath) throws IOException {
+ ClassPathOpener opener;
+
+ String fileName = absSrcFilePath;
+ // e.g. test/p1/MyTest.java
+ String pckPath = fileName.substring(srcDir.length() + 1);
+ // e.g. test/p1
+ String pck = pckPath.substring(0, pckPath.lastIndexOf("/"));
+ // e.g. MyTest
+ String cName = pckPath.substring(pck.length() + 1);
+ cName = cName.substring(0, cName.lastIndexOf("."));
+ String cfName = pck+"/"+cName+".class";
+ // 2. calculate the target file name:
+ // e.g. <out-path>/test/p1/MyTest.class
+ String inFile = classesDir + "/" + pck + "/" + cName + ".class";
+ if (!new File(inFile).exists()) {
+ throw new RuntimeException("cannot read:" + inFile);
+ }
+ byte[] bytes = FileUtils.readFile(inFile);
+ // write the outfile to the same directory as the corresponding .java
+ // file
+ String outFile = absSrcFilePath.substring(0, absSrcFilePath
+ .lastIndexOf("/"))+ "/" + cName + ".cfh";
+ Writer w;
+ try {
+ w = new OutputStreamWriter(new FileOutputStream(new File(outFile)));
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException("cannot write to file:"+outFile, e);
+ }
+ // Writer w = new OutputStreamWriter(System.out);
+ ClassFileParser.this.processFileBytes(w, cfName, bytes);
+
+ }
+
+ /**
+ *
+ * @param w the writer to write the generated .cfh file to
+ * @param name the relative name of the java src file, e.g.
+ * dxc/util/Util.java
+ * @param allbytes the bytes of this java src file
+ * @return true if everthing went alright
+ */
+ void processFileBytes(Writer w, String name, final byte[] allbytes) throws IOException {
+ String fixedPathName = fixPath(name);
+ DirectClassFile cf = new DirectClassFile(allbytes, fixedPathName, true);
+ bw = new BufferedWriter(w);
+ String className = fixedPathName.substring(0, fixedPathName.lastIndexOf("."));
+ out("//@class:" + className, 0);
+ cf.setObserver(new ParseObserver() {
+ private int cur_indent = 0;
+ private int checkpos = 0;
+
+ /**
+ * Indicate that the level of indentation for a dump should increase
+ * or decrease (positive or negative argument, respectively).
+ *
+ * @param indentDelta the amount to change indentation
+ */
+ public void changeIndent(int indentDelta) {
+ cur_indent += indentDelta;
+ }
+
+ /**
+ * Indicate that a particular member is now being parsed.
+ *
+ * @param bytes non-null; the source that is being parsed
+ * @param offset offset into <code>bytes</code> for the start of
+ * the member
+ * @param name non-null; name of the member
+ * @param descriptor non-null; descriptor of the member
+ */
+ public void startParsingMember(ByteArray bytes, int offset,
+ String name, String descriptor) {
+ // ByteArray ba = bytes.slice(offset, bytes.size());
+ out("// ========== start-ParseMember:" + name + ", offset "
+ + offset + ", len:" + (bytes.size() - offset)
+ + ",desc: " + descriptor);
+ // out("// "+dumpReadableString(ba));
+ // out(" "+dumpBytes(ba));
+ }
+
+ /**
+ * Indicate that a particular member is no longer being parsed.
+ *
+ * @param bytes non-null; the source that was parsed
+ * @param offset offset into <code>bytes</code> for the end of the
+ * member
+ * @param name non-null; name of the member
+ * @param descriptor non-null; descriptor of the member
+ * @param member non-null; the actual member that was parsed
+ */
+ public void endParsingMember(ByteArray bytes, int offset,
+ String name, String descriptor, Member member) {
+ ByteArray ba = bytes.slice(offset, bytes.size());
+ out("// ========== end-ParseMember:" + name + ", desc: "
+ + descriptor);
+ // out("// "+dumpReadableString(ba));
+ // out(" "+dumpBytes(ba));
+ }
+
+ /**
+ * Indicate that some parsing happened.
+ *
+ * @param bytes non-null; the source that was parsed
+ * @param offset offset into <code>bytes</code> for what was
+ * parsed
+ * @param len number of bytes parsed
+ * @param human non-null; human form for what was parsed
+ */
+ public void parsed(ByteArray bytes, int offset, int len,
+ String human) {
+ human = human.replace('\n', ' ');
+ out("// parsed:" + ", offset " + offset + ", len " + len
+ + ", h: " + human);
+ if (len > 0) {
+ ByteArray ba = bytes.slice(offset, offset + len);
+ check(ba);
+ out("// " + dumpReadableString(ba));
+ out(" " + dumpBytes(ba));
+ }
+ }
+
+ private void out(String msg) {
+ ClassFileParser.this.out(msg, cur_indent);
+
+ }
+
+ private void check(ByteArray ba) {
+ int len = ba.size();
+ int offset = checkpos;
+ for (int i = 0; i < len; i++) {
+ int b = ba.getByte(i);
+ byte b2 = allbytes[i + offset];
+ if (b != b2)
+ throw new RuntimeException("byte dump mismatch at pos "
+ + (i + offset));
+ }
+ checkpos += len;
+ }
+
+
+
+ private String dumpBytes(ByteArray ba) {
+ String s = "";
+ for (int i = 0; i < ba.size(); i++) {
+ int byt = ba.getUnsignedByte(i);
+ String hexVal = Integer.toHexString(byt);
+ if (hexVal.length() == 1) {
+ hexVal = "0" + hexVal;
+ }
+ s += hexVal + " ";
+ }
+ return s;
+ }
+
+ private String dumpReadableString(ByteArray ba) {
+ String s = "";
+ for (int i = 0; i < ba.size(); i++) {
+ int bb = ba.getUnsignedByte(i);
+ if (bb > 31 && bb < 127) {
+ s += (char) bb;
+ } else {
+ s += ".";
+ }
+ s += " ";
+ }
+ return s;
+ }
+
+
+ });
+ cf.setAttributeFactory(StdAttributeFactory.THE_ONE);
+ // what is needed to force parsing to the end?
+ cf.getMagic();
+ // cf.getFields();
+ // cf.getAttributes();
+ // cf.getMethods();
+ bw.close();
+ }
+
+
+ private String getIndent(int indent) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < indent * 4; i++) {
+ sb.append(' ');
+ }
+ return sb.toString();
+ }
+
+ private void out(String msg, int cur_indent) {
+ try {
+ bw.write(getIndent(cur_indent) + msg);
+ bw.newLine();
+ } catch (IOException ioe) {
+ throw new RuntimeException("error while writing to the writer", ioe);
+ }
+ }
+
+ private static String fixPath(String path) {
+ /*
+ * If the path separator is \ (like on windows), we convert the path to
+ * a standard '/' separated path.
+ */
+ if (File.separatorChar == '\\') {
+ path = path.replace('\\', '/');
+ }
+
+ int index = path.lastIndexOf("/./");
+
+ if (index != -1) {
+ return path.substring(index + 3);
+ }
+
+ if (path.startsWith("./")) {
+ return path.substring(2);
+ }
+
+ return path;
+ }
+
+
+
+}
diff --git a/tools/cfassembler/src/dxconvext/util/FileUtils.java b/tools/cfassembler/src/dxconvext/util/FileUtils.java
new file mode 100644
index 0000000..7b7003d
--- /dev/null
+++ b/tools/cfassembler/src/dxconvext/util/FileUtils.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dxconvext.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/**
+ * File I/O utilities.
+ */
+public final class FileUtils {
+ /**
+ * This class is uninstantiable.
+ */
+ private FileUtils() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Reads the named file, translating {@link IOException} to a
+ * {@link RuntimeException} of some sort.
+ *
+ * @param fileName non-null; name of the file to read
+ * @return non-null; contents of the file
+ */
+ public static byte[] readFile(String fileName) {
+ File file = new File(fileName);
+ return readFile(file);
+ }
+
+ /**
+ * Reads the given file, translating {@link IOException} to a
+ * {@link RuntimeException} of some sort.
+ *
+ * @param file non-null; the file to read
+ * @return non-null; contents of the file
+ */
+ public static byte[] readFile(File file) {
+ if (!file.exists()) {
+ throw new RuntimeException(file + ": file not found");
+ }
+
+ if (!file.isFile()) {
+ throw new RuntimeException(file + ": not a file");
+ }
+
+ if (!file.canRead()) {
+ throw new RuntimeException(file + ": file not readable");
+ }
+
+ long longLength = file.length();
+ int length = (int) longLength;
+ if (length != longLength) {
+ throw new RuntimeException(file + ": file too long");
+ }
+
+ byte[] result = new byte[length];
+
+ try {
+ FileInputStream in = new FileInputStream(file);
+ int at = 0;
+ while (length > 0) {
+ int amt = in.read(result, at, length);
+ if (amt == -1) {
+ throw new RuntimeException(file + ": unexpected EOF");
+ }
+ at += amt;
+ length -= amt;
+ }
+ in.close();
+ } catch (IOException ex) {
+ throw new RuntimeException(file + ": trouble reading", ex);
+ }
+
+ return result;
+ }
+}