Add dexgen helper classes and Android.mk

This commit adds the very first classes to the dexgen project together with
its Android.mk file. These are the helper classes needed by dex class builder.

Change-Id: I47f8132443f43881826d24a854ab6bafb14181bd
diff --git a/Android.mk b/Android.mk
index e70e5f5..7a950a1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -18,6 +18,7 @@
 		libdex \
 		vm \
 		dalvikvm \
+		dexgen \
 		dexlist \
 		dexopt \
 		dexdump \
diff --git a/dexgen/Android.mk b/dexgen/Android.mk
new file mode 100644
index 0000000..c4d84a0
--- /dev/null
+++ b/dexgen/Android.mk
@@ -0,0 +1,25 @@
+# Copyright (C) 2010 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)
+
+include $(CLEAR_VARS)
+
+LOCAL_SDK_VERSION := 4
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_MODULE := dexgen
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/dexgen/src/com/android/dexgen/util/DexClassLoaderHelper.java b/dexgen/src/com/android/dexgen/util/DexClassLoaderHelper.java
new file mode 100644
index 0000000..92a2eb6
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/util/DexClassLoaderHelper.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 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 com.android.dexgen.util;
+
+import dalvik.system.DexClassLoader;
+
+/**
+ * Class used indirectly for loading generated dex classes. It allows the caller
+ * to obtain appropriate {@code DexClassLoader} instance, which can be then used for
+ * loading classes.
+ */
+public class DexClassLoaderHelper {
+
+    private DexClassLoaderHelper() {
+        // intentionally empty to disable direct instantiation
+    }
+
+    /**
+     * Returns the sole instance of {@code DexClassLoaderHelper}.
+     *
+     * @return dex {@code DexClassLoaderHelper} sole instance
+     */
+    public static DexClassLoaderHelper getInstance() {
+        return DexClassLoaderHelperHolder.INSTANCE;
+    }
+
+    /**
+     * Creates and returns DexClassLoader instance with its classpath
+     * set to {@code pathHolder}.
+     *
+     * @param pathHolder {@code non-null;} location of jar archive containing dex
+     * classes canned into a working PathHolder instance.
+     * @return dex class loader instance with its classpath set to location
+     * indicated by {@code pathHolder}
+     */
+    public ClassLoader getDexClassLoader(PathHolder pathHolder) {
+        ClassLoader myLoader = DexClassLoaderHelper.class.getClassLoader();
+        return new DexClassLoader(pathHolder.getJarFilePath(), pathHolder.getDirLocation(),
+                null, myLoader);
+    }
+
+    private static class DexClassLoaderHelperHolder {
+        private static final DexClassLoaderHelper INSTANCE = new DexClassLoaderHelper();
+    }
+}
\ No newline at end of file
diff --git a/dexgen/src/com/android/dexgen/util/DexClassLoadingException.java b/dexgen/src/com/android/dexgen/util/DexClassLoadingException.java
new file mode 100644
index 0000000..ba4d350
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/util/DexClassLoadingException.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2010 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 com.android.dexgen.util;
+
+/**
+ * An exception type used to aggregate all the unexpected situations while
+ * trying to save dex file with generated class and load the class afterwards.
+ */
+public class DexClassLoadingException extends Exception {
+
+    /**
+     * Encapsulates any checked exception being thrown in time between saving
+     * generated dex class to a file and loading it via DexClassLoader with
+     * an user-friendly message and passing the original exception as well.
+     *
+     * @param thr {@code non-null;} lower level exception with more detailed
+     * error message
+     */
+    public DexClassLoadingException(Throwable thr) {
+        super("Loading generated dex class has failed", thr);
+    }
+}
\ No newline at end of file
diff --git a/dexgen/src/com/android/dexgen/util/DexJarMaker.java b/dexgen/src/com/android/dexgen/util/DexJarMaker.java
new file mode 100644
index 0000000..4fe5a56
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/util/DexJarMaker.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2010 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 com.android.dexgen.util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+/**
+ * Helper class used to encapsulate generated .dex file into .jar
+ * so that it fits {@code DexClassLoader} constructor.
+ */
+public class DexJarMaker {
+
+    /** indicates name of the dex file added to jar */
+    public static final String DEX_FILE_NAME_IN_JAR = "classes" + PathHolder.DEX_FILE_EXTENSION;
+
+    /** {@code non-null;} storage for all the paths related to current dex file */
+    private final PathHolder pathHolder;
+
+    public DexJarMaker(PathHolder pathHolder) {
+        this.pathHolder = pathHolder;
+    }
+
+    /** Packs previously added files into a single jar archive. */
+    public void create() throws DexClassLoadingException {
+        Manifest manifest = new Manifest();
+        manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
+        JarOutputStream target = null;
+        try {
+            target = new JarOutputStream(
+                    new BufferedOutputStream(new FileOutputStream(pathHolder.getJarFilePath())),
+                    manifest);
+            add(new File(pathHolder.getDexFilePath()), target);
+
+        } catch (IOException e) {
+            throw new DexClassLoadingException(e);
+        }
+        finally {
+            try {
+                if (target != null) {
+                    target.close();
+                }
+            } catch(IOException e) {
+                // Ignoring deliberately in order to keep the original exception clear.
+            }
+        }
+    }
+
+    /**
+     * Adds indicated file to the requested archive.
+     *
+     * @param source {@code non-null;} dex file to add
+     * @param target {@code non-null;} target jar archive
+     * @throws IOException
+     */
+    private void add(File source, JarOutputStream target) throws IOException {
+
+        if (!source.isFile()) {
+            throw new IllegalArgumentException("Wrong source dex file provided");
+        }
+
+        BufferedInputStream in = new BufferedInputStream(new FileInputStream(source));
+        JarEntry entry = new JarEntry(DEX_FILE_NAME_IN_JAR);
+        entry.setTime(source.lastModified());
+        target.putNextEntry(entry);
+
+        int curr = -1;
+        while ((curr = in.read()) != -1) {
+            target.write(curr);
+        }
+        target.closeEntry();
+    }
+}
\ No newline at end of file
diff --git a/dexgen/src/com/android/dexgen/util/PathHolder.java b/dexgen/src/com/android/dexgen/util/PathHolder.java
new file mode 100644
index 0000000..b23ce66
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/util/PathHolder.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 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 com.android.dexgen.util;
+
+import java.io.File;
+
+/**
+ *  Helper class used primarily for holding path on the device of different
+ *  files arising in the dex class generation process.
+ */
+public class PathHolder {
+
+    public static final String DEX_FILE_EXTENSION = ".dex";
+
+    public static final String JAR_FILE_EXTENSION = ".jar";
+
+    /** {@code non-null;} directory location of the dex-related files */
+    private final String dirLocation;
+
+    /** {@code non-null;} common file name prefix of the created files */
+    private final String fileNamePrefix;
+
+    /**
+     * Creates an instance of {@code PathHolder} initialized with the directory
+     * location for storage of temporary files and common file name prefix for these
+     * files.
+     *
+     * @param dirLocation {@code non-null;} path to directory used for storage of temporary files
+     * @param fileNamePrefix {@code non-null;} common file name prefix across all the temporary
+     * files involved in the dex class generation and loading process
+     */
+    public PathHolder(String dirLocation, String fileNamePrefix) {
+        if (dirLocation == null) {
+            throw new NullPointerException("dirLocation == null");
+        }
+        if (fileNamePrefix == null) {
+            throw new NullPointerException("fileNamePrefix == null");
+        }
+
+        this.dirLocation = dirLocation;
+        this.fileNamePrefix = fileNamePrefix;
+    }
+
+    public String getFileName() {
+        return fileNamePrefix;
+    }
+
+    public String getDexFilePath() {
+        return dirLocation + File.separator + fileNamePrefix + DEX_FILE_EXTENSION;
+    }
+
+    public String getDexFileName() {
+        return fileNamePrefix + DEX_FILE_EXTENSION;
+    }
+
+    public String getJarFilePath() {
+        return dirLocation + File.separator + fileNamePrefix + JAR_FILE_EXTENSION;
+    }
+
+    public String getJarFileName() {
+        return fileNamePrefix + JAR_FILE_EXTENSION;
+    }
+
+    public String getDirLocation() {
+        return dirLocation;
+    }
+}