aboutsummaryrefslogtreecommitdiffhomepage
path: root/third_party
diff options
context:
space:
mode:
authorGravatar Liam Miller-Cushon <cushon@google.com>2018-03-29 12:41:15 -0700
committerGravatar Philipp Wollermann <philwo@google.com>2018-04-13 10:32:52 +0200
commitc7e7232c8a58d3bdf8f7423645521a8b94c81bbd (patch)
tree8f394876dc6ca0b2acda39a15900ab310fa32f0d /third_party
parent2ef1ebfb65bdb183abb0ece3d0eccb4319f7e27f (diff)
This is based on the original jarjar sources, with updates to support JDK 9. I added it to a different packages (third_party/jarjar vs. third_party/java/jarjar) because Bazel uses the first 'java/' component of the path as the resource root. Change-Id: Icdf10b1a71edf4e59f5524c1e169000b9f090ed9
Diffstat (limited to 'third_party')
-rw-r--r--third_party/BUILD1
-rw-r--r--third_party/jarjar/BUILD48
-rw-r--r--third_party/jarjar/BUILD.tools14
-rw-r--r--third_party/jarjar/LICENSE202
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/AbstractDepHandler.java56
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/DepFind.java78
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/DepFindVisitor.java74
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/DepHandler.java30
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/EmptyClassVisitor.java51
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/ExcludeProcessor.java39
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/JarJarMojo.java53
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/JarJarTask.java63
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/Keep.java19
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/KeepProcessor.java116
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/Main.java96
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/MainProcessor.java113
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/MainUtil.java86
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/ManifestProcessor.java36
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/PackageRemapper.java136
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/PathClass.java39
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/PatternElement.java44
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/ResourceProcessor.java36
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/Rule.java29
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/RulesFileParser.java82
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/StringDumper.java108
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/StringReader.java114
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/TextDepHandler.java33
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/Wildcard.java160
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/Zap.java19
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/ZapProcessor.java47
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/help.txt75
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/util/AntJarProcessor.java110
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassHeaderReader.java192
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassPathEntry.java28
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassPathIterator.java242
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/util/EntryStruct.java35
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/util/GetNameClassWriter.java48
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/util/IoUtil.java119
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/util/JarProcessor.java34
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/util/JarProcessorChain.java42
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/util/JarTransformer.java46
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/util/JarTransformerChain.java35
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/util/RemappingClassTransformer.java32
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/util/RuntimeIOException.java27
-rw-r--r--third_party/jarjar/java/com/tonicsystems/jarjar/util/StandaloneJarProcessor.java68
45 files changed, 3155 insertions, 0 deletions
diff --git a/third_party/BUILD b/third_party/BUILD
index dc774eed5c..cf939fb7ea 100644
--- a/third_party/BUILD
+++ b/third_party/BUILD
@@ -9,6 +9,7 @@ filegroup(
"//third_party/ijar:srcs",
"//third_party/iossim:srcs",
"//third_party/grpc:srcs",
+ "//third_party/jarjar:srcs",
"//third_party/java/android_databinding:srcs",
"//third_party/java/aosp_gradle_core:srcs",
"//third_party/java/apkbuilder:srcs",
diff --git a/third_party/jarjar/BUILD b/third_party/jarjar/BUILD
new file mode 100644
index 0000000000..4f4f223b08
--- /dev/null
+++ b/third_party/jarjar/BUILD
@@ -0,0 +1,48 @@
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"]) # Apache 2.0
+
+filegroup(
+ name = "srcs",
+ srcs = glob(["**"]),
+)
+
+filegroup(
+ name = "embedded_tools_srcs",
+ srcs = [
+ "BUILD.tools",
+ "LICENSE",
+ ":jarjar_command_deploy.jar",
+ ],
+)
+
+java_binary(
+ name = "jarjar_command",
+ srcs = glob(
+ ["java/**/*.java"],
+ exclude = [
+ "java/com/tonicsystems/jarjar/util/AntJarProcessor.java",
+ "java/com/tonicsystems/jarjar/JarJarMojo.java",
+ "java/com/tonicsystems/jarjar/JarJarTask.java",
+ ],
+ ),
+ main_class = "com.tonicsystems.jarjar.Main",
+ resources = ["java/com/tonicsystems/jarjar/help.txt"],
+ deps = [
+ "//third_party:asm",
+ "//third_party:asm-commons",
+ "//third_party:asm-util",
+ "//third_party:jsr305",
+ ],
+)
+
+java_import(
+ name = "jarjar_import",
+ jars = [":jarjar_command_deploy.jar"],
+)
+
+java_binary(
+ name = "jarjar_bin",
+ main_class = "com.tonicsystems.jarjar.Main",
+ runtime_deps = [":jarjar_import"],
+)
diff --git a/third_party/jarjar/BUILD.tools b/third_party/jarjar/BUILD.tools
new file mode 100644
index 0000000000..2319e6bcd5
--- /dev/null
+++ b/third_party/jarjar/BUILD.tools
@@ -0,0 +1,14 @@
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"]) # Apache 2.0
+
+java_import(
+ name = "jarjar_import",
+ jars = [":jarjar_command_deploy.jar"],
+)
+
+java_binary(
+ name = "jarjar_bin",
+ main_class = "com.tonicsystems.jarjar.Main",
+ runtime_deps = [":jarjar_import"],
+)
diff --git a/third_party/jarjar/LICENSE b/third_party/jarjar/LICENSE
new file mode 100644
index 0000000000..d645695673
--- /dev/null
+++ b/third_party/jarjar/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/AbstractDepHandler.java b/third_party/jarjar/java/com/tonicsystems/jarjar/AbstractDepHandler.java
new file mode 100644
index 0000000000..c13283eb4d
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/AbstractDepHandler.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+import com.tonicsystems.jarjar.util.*;
+import java.io.*;
+import java.util.*;
+
+public abstract class AbstractDepHandler implements DepHandler {
+ protected final int level;
+ private final Set<List<Object>> seenIt = new HashSet<List<Object>>();
+
+ protected AbstractDepHandler(int level) {
+ this.level = level;
+ }
+
+ public void handle(PathClass from, PathClass to) throws IOException {
+ List<Object> pair;
+ if (level == LEVEL_JAR) {
+ pair = createPair(from.getClassPath(), to.getClassPath());
+ } else {
+ pair = createPair(from.getClassName(), to.getClassName());
+ }
+ if (!seenIt.contains(pair)) {
+ seenIt.add(pair);
+ handle(pair.get(0).toString(), pair.get(1).toString());
+ }
+ }
+
+ protected abstract void handle(String from, String to) throws IOException;
+
+ public void handleStart() throws IOException {}
+
+ public void handleEnd() throws IOException {}
+
+ private static List<Object> createPair(Object o1, Object o2) {
+ List<Object> list = new ArrayList<Object>(2);
+ list.add(o1);
+ list.add(o2);
+ return list;
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/DepFind.java b/third_party/jarjar/java/com/tonicsystems/jarjar/DepFind.java
new file mode 100644
index 0000000000..088ff38ded
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/DepFind.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+import com.tonicsystems.jarjar.util.*;
+import java.io.*;
+import java.util.*;
+import org.objectweb.asm.ClassReader;
+
+public class DepFind {
+ private File curDir = new File(System.getProperty("user.dir"));
+
+ public void setCurrentDirectory(File curDir) {
+ this.curDir = curDir;
+ }
+
+ public void run(String from, String to, DepHandler handler) throws IOException {
+ try {
+ ClassHeaderReader header = new ClassHeaderReader();
+ Map<String, String> classes = new HashMap<String, String>();
+ ClassPathIterator cp = new ClassPathIterator(curDir, to, null);
+ try {
+ while (cp.hasNext()) {
+ ClassPathEntry entry = cp.next();
+ InputStream in = entry.openStream();
+ try {
+ header.read(in);
+ classes.put(header.getClassName(), entry.getSource());
+ } catch (Exception e) {
+ System.err.println("Error reading " + entry.getName() + ": " + e.getMessage());
+ } finally {
+ in.close();
+ }
+ }
+ } finally {
+ cp.close();
+ }
+
+ handler.handleStart();
+ cp = new ClassPathIterator(curDir, from, null);
+ try {
+ while (cp.hasNext()) {
+ ClassPathEntry entry = cp.next();
+ InputStream in = entry.openStream();
+ try {
+ new ClassReader(in)
+ .accept(
+ new DepFindVisitor(classes, entry.getSource(), handler),
+ ClassReader.SKIP_DEBUG);
+ } catch (Exception e) {
+ System.err.println("Error reading " + entry.getName() + ": " + e.getMessage());
+ } finally {
+ in.close();
+ }
+ }
+ } finally {
+ cp.close();
+ }
+ handler.handleEnd();
+ } catch (RuntimeIOException e) {
+ throw (IOException) e.getCause();
+ }
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/DepFindVisitor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/DepFindVisitor.java
new file mode 100644
index 0000000000..af36dfee63
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/DepFindVisitor.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+import com.tonicsystems.jarjar.util.*;
+import java.io.*;
+import java.util.*;
+import org.objectweb.asm.*;
+import org.objectweb.asm.commons.*;
+
+class DepFindVisitor extends ClassRemapper {
+ public DepFindVisitor(Map<String, String> classes, String source, DepHandler handler)
+ throws IOException {
+ super(null, new DepFindRemapper(classes, source, handler));
+ }
+
+ public void visit(
+ int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ ((DepFindRemapper) remapper).setClassName(name);
+ super.visit(version, access, name, signature, superName, interfaces);
+ }
+
+ private static class DepFindRemapper extends Remapper {
+ private final Map<String, String> classes;
+ private final String source;
+ private final DepHandler handler;
+ private PathClass curPathClass;
+
+ public DepFindRemapper(Map<String, String> classes, String source, DepHandler handler)
+ throws IOException {
+ this.classes = classes;
+ this.source = source;
+ this.handler = handler;
+ }
+
+ public void setClassName(String name) {
+ curPathClass = new PathClass(source, name);
+ }
+
+ public String map(String key) {
+ try {
+ if (classes.containsKey(key)) {
+ String otherSource = classes.get(key);
+ if (!source.equals(otherSource)) {
+ // TODO: some escape mechanism?
+ handler.handle(curPathClass, new PathClass(otherSource, key));
+ }
+ }
+ } catch (IOException e) {
+ throw new RuntimeIOException(e);
+ }
+ return null;
+ }
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/DepHandler.java b/third_party/jarjar/java/com/tonicsystems/jarjar/DepHandler.java
new file mode 100644
index 0000000000..46847304b1
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/DepHandler.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+import java.io.IOException;
+
+public interface DepHandler {
+ public static final int LEVEL_CLASS = 0;
+ public static final int LEVEL_JAR = 1;
+
+ void handleStart() throws IOException;
+
+ void handle(PathClass from, PathClass to) throws IOException;
+
+ void handleEnd() throws IOException;
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/EmptyClassVisitor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/EmptyClassVisitor.java
new file mode 100644
index 0000000000..901e237992
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/EmptyClassVisitor.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2003-2010 the original author or authors.
+ *
+ * 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.tonicsystems.jarjar;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * An ASM3 EmptyVisitor replacement
+ *
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a>
+ */
+public class EmptyClassVisitor extends ClassVisitor {
+
+ public EmptyClassVisitor() {
+ super(Opcodes.ASM6);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(
+ int access, String name, String desc, String signature, String[] exceptions) {
+ return new MethodVisitor(Opcodes.ASM6) {};
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ return new AnnotationVisitor(Opcodes.ASM6) {};
+ }
+
+ @Override
+ public FieldVisitor visitField(
+ int access, String name, String desc, String signature, Object value) {
+ return new FieldVisitor(Opcodes.ASM6) {};
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/ExcludeProcessor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/ExcludeProcessor.java
new file mode 100644
index 0000000000..79ec041e03
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/ExcludeProcessor.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+import com.tonicsystems.jarjar.util.*;
+import java.io.IOException;
+import java.util.*;
+
+class ExcludeProcessor implements JarProcessor {
+ private final Set<String> excludes;
+ private final boolean verbose;
+
+ public ExcludeProcessor(Set<String> excludes, boolean verbose) {
+ this.excludes = excludes;
+ this.verbose = verbose;
+ }
+
+ public boolean process(EntryStruct struct) throws IOException {
+ boolean toKeep = !excludes.contains(struct.name);
+ if (verbose && !toKeep) {
+ System.err.println("Excluding " + struct.name);
+ }
+ return toKeep;
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/JarJarMojo.java b/third_party/jarjar/java/com/tonicsystems/jarjar/JarJarMojo.java
new file mode 100644
index 0000000000..2cec5fc65b
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/JarJarMojo.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+import com.tonicsystems.jarjar.util.*;
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+
+public class JarJarMojo extends AbstractMojo {
+ private File fromJar;
+ private File toJar;
+ private File rulesFile;
+ private String rules;
+ private boolean verbose;
+
+ public void execute() throws MojoExecutionException {
+ if (!((rulesFile == null || !rulesFile.exists()) ^ (rules == null))) {
+ throw new MojoExecutionException("Exactly one of rules or rulesFile is required");
+ }
+
+ try {
+ List<PatternElement> patterns;
+ if (rules != null) {
+ patterns = RulesFileParser.parse(rules);
+ } else {
+ patterns = RulesFileParser.parse(rulesFile);
+ }
+ // TODO: refactor with Main.java
+ MainProcessor proc = new MainProcessor(patterns, verbose, true);
+ StandaloneJarProcessor.run(fromJar, toJar, proc);
+ proc.strip(toJar);
+ } catch (IOException e) {
+ throw new MojoExecutionException(e.getMessage(), e);
+ }
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/JarJarTask.java b/third_party/jarjar/java/com/tonicsystems/jarjar/JarJarTask.java
new file mode 100644
index 0000000000..39e1677895
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/JarJarTask.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+import com.tonicsystems.jarjar.util.*;
+import java.io.IOException;
+import java.util.*;
+import org.apache.tools.ant.BuildException;
+
+public class JarJarTask extends AntJarProcessor {
+ private List<PatternElement> patterns = new ArrayList<PatternElement>();
+
+ public void addConfiguredRule(Rule rule) {
+ if (rule.getPattern() == null || rule.getResult() == null) {
+ throw new IllegalArgumentException(
+ "The <rule> element requires both \"pattern\" and \"result\" attributes.");
+ }
+ patterns.add(rule);
+ }
+
+ public void addConfiguredZap(Zap zap) {
+ if (zap.getPattern() == null) {
+ throw new IllegalArgumentException("The <zap> element requires a \"pattern\" attribute.");
+ }
+ patterns.add(zap);
+ }
+
+ public void addConfiguredKeep(Keep keep) {
+ if (keep.getPattern() == null) {
+ throw new IllegalArgumentException("The <keep> element requires a \"pattern\" attribute.");
+ }
+ patterns.add(keep);
+ }
+
+ public void execute() throws BuildException {
+ MainProcessor proc = new MainProcessor(patterns, verbose, false);
+ execute(proc);
+ try {
+ proc.strip(getDestFile());
+ } catch (IOException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ protected void cleanHelper() {
+ super.cleanHelper();
+ patterns.clear();
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/Keep.java b/third_party/jarjar/java/com/tonicsystems/jarjar/Keep.java
new file mode 100644
index 0000000000..090340e345
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/Keep.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+public class Keep extends PatternElement {}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/KeepProcessor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/KeepProcessor.java
new file mode 100644
index 0000000000..5bdd6aec00
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/KeepProcessor.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+import com.tonicsystems.jarjar.util.*;
+import java.io.*;
+import java.util.*;
+import org.objectweb.asm.*;
+import org.objectweb.asm.commons.*;
+
+// TODO: this can probably be refactored into JarClassVisitor, etc.
+class KeepProcessor extends Remapper implements JarProcessor {
+ private final ClassVisitor cv = new ClassRemapper(new EmptyClassVisitor(), this);
+ private final List<Wildcard> wildcards;
+ private final List<String> roots = new ArrayList<String>();
+ private final Map<String, Set<String>> depend = new HashMap<String, Set<String>>();
+
+ public KeepProcessor(List<Keep> patterns) {
+ wildcards = PatternElement.createWildcards(patterns);
+ }
+
+ public boolean isEnabled() {
+ return !wildcards.isEmpty();
+ }
+
+ public Set<String> getExcludes() {
+ Set<String> closure = new HashSet<String>();
+ closureHelper(closure, roots);
+ Set<String> removable = new HashSet<String>(depend.keySet());
+ removable.removeAll(closure);
+ return removable;
+ }
+
+ private void closureHelper(Set<String> closure, Collection<String> process) {
+ if (process == null) {
+ return;
+ }
+ for (String name : process) {
+ if (closure.add(name)) {
+ closureHelper(closure, depend.get(name));
+ }
+ }
+ }
+
+ private Set<String> curSet;
+ private byte[] buf = new byte[0x2000];
+
+ public boolean process(EntryStruct struct) throws IOException {
+ try {
+ if (struct.name.endsWith(".class")) {
+ String name = struct.name.substring(0, struct.name.length() - 6);
+ for (Wildcard wildcard : wildcards)
+ if (wildcard.matches(name)) {
+ roots.add(name);
+ }
+ depend.put(name, curSet = new HashSet<String>());
+ new ClassReader(new ByteArrayInputStream(struct.data))
+ .accept(cv, ClassReader.EXPAND_FRAMES);
+ curSet.remove(name);
+ }
+ } catch (Exception e) {
+ System.err.println("Error reading " + struct.name + ": " + e.getMessage());
+ }
+ return true;
+ }
+
+ public String map(String key) {
+ if (key.startsWith("java/") || key.startsWith("javax/")) {
+ return null;
+ }
+ curSet.add(key);
+ return null;
+ }
+
+ public Object mapValue(Object value) {
+ if (value instanceof String) {
+ String s = (String) value;
+ if (PackageRemapper.isArrayForName(s)) {
+ mapDesc(s.replace('.', '/'));
+ } else if (isForName(s)) {
+ map(s.replace('.', '/'));
+ }
+ return value;
+ } else {
+ return super.mapValue(value);
+ }
+ }
+
+ // TODO: use this for package remapping too?
+ private static boolean isForName(String value) {
+ if (value.equals("")) {
+ return false;
+ }
+ for (int i = 0, len = value.length(); i < len; i++) {
+ char c = value.charAt(i);
+ if (c != '.' && !Character.isJavaIdentifierPart(c)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/Main.java b/third_party/jarjar/java/com/tonicsystems/jarjar/Main.java
new file mode 100644
index 0000000000..55b7d24a60
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/Main.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+import com.tonicsystems.jarjar.util.*;
+import java.io.*;
+import java.util.*;
+
+public class Main {
+
+ private static final String LINE_SEPARATOR = System.getProperty("line.separator");
+ private static final String HELP;
+
+ static {
+ try {
+ HELP = readIntoString(Main.class.getResourceAsStream("help.txt"));
+ } catch (IOException e) {
+ throw new RuntimeIOException(e);
+ }
+ }
+
+ private static String readIntoString(InputStream in) throws IOException {
+ StringBuilder sb = new StringBuilder();
+ BufferedReader r = new BufferedReader(new InputStreamReader(in, "UTF-8"));
+ String line = null;
+ while ((line = r.readLine()) != null) sb.append(line).append(LINE_SEPARATOR);
+ return sb.toString();
+ }
+
+ private boolean verbose;
+ private List patterns;
+ private int level = DepHandler.LEVEL_CLASS;
+
+ public static void main(String[] args) throws Exception {
+ MainUtil.runMain(new Main(), args, "help");
+ }
+
+ public void help() {
+ System.err.print(HELP);
+ }
+
+ public void strings(String cp) throws IOException {
+ if (cp == null) {
+ throw new IllegalArgumentException("cp is required");
+ }
+ new StringDumper().run(cp, new PrintWriter(System.out));
+ }
+
+ // TODO: make level an enum
+ public void find(String level, String cp1, String cp2) throws IOException {
+ if (level == null || cp1 == null) {
+ throw new IllegalArgumentException("level and cp1 are required");
+ }
+ if (cp2 == null) {
+ cp2 = cp1;
+ }
+ int levelFlag;
+ if ("class".equals(level)) {
+ levelFlag = DepHandler.LEVEL_CLASS;
+ } else if ("jar".equals(level)) {
+ levelFlag = DepHandler.LEVEL_JAR;
+ } else {
+ throw new IllegalArgumentException("unknown level " + level);
+ }
+ PrintWriter w = new PrintWriter(System.out);
+ DepHandler handler = new TextDepHandler(w, levelFlag);
+ new DepFind().run(cp1, cp2, handler);
+ w.flush();
+ }
+
+ public void process(File rulesFile, File inJar, File outJar) throws IOException {
+ if (rulesFile == null || inJar == null || outJar == null) {
+ throw new IllegalArgumentException("rulesFile, inJar, and outJar are required");
+ }
+ List<PatternElement> rules = RulesFileParser.parse(rulesFile);
+ boolean verbose = Boolean.getBoolean("verbose");
+ boolean skipManifest = Boolean.getBoolean("skipManifest");
+ MainProcessor proc = new MainProcessor(rules, verbose, skipManifest);
+ StandaloneJarProcessor.run(inJar, outJar, proc);
+ proc.strip(outJar);
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/MainProcessor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/MainProcessor.java
new file mode 100644
index 0000000000..48ccf9c8ec
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/MainProcessor.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+import com.tonicsystems.jarjar.util.*;
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+
+class MainProcessor implements JarProcessor {
+ private final boolean verbose;
+ private final JarProcessorChain chain;
+ private final KeepProcessor kp;
+ private final Map<String, String> renames = new HashMap<String, String>();
+
+ public MainProcessor(List<PatternElement> patterns, boolean verbose, boolean skipManifest) {
+ this.verbose = verbose;
+ List<Zap> zapList = new ArrayList<Zap>();
+ List<Rule> ruleList = new ArrayList<Rule>();
+ List<Keep> keepList = new ArrayList<Keep>();
+ for (PatternElement pattern : patterns) {
+ if (pattern instanceof Zap) {
+ zapList.add((Zap) pattern);
+ } else if (pattern instanceof Rule) {
+ ruleList.add((Rule) pattern);
+ } else if (pattern instanceof Keep) {
+ keepList.add((Keep) pattern);
+ }
+ }
+
+ PackageRemapper pr = new PackageRemapper(ruleList, verbose);
+ kp = keepList.isEmpty() ? null : new KeepProcessor(keepList);
+
+ List<JarProcessor> processors = new ArrayList<JarProcessor>();
+ if (skipManifest) {
+ processors.add(ManifestProcessor.getInstance());
+ }
+ if (kp != null) {
+ processors.add(kp);
+ }
+ processors.add(new ZapProcessor(zapList));
+ processors.add(
+ new JarTransformerChain(
+ new RemappingClassTransformer[] {new RemappingClassTransformer(pr)}));
+ processors.add(new ResourceProcessor(pr));
+ chain = new JarProcessorChain(processors.toArray(new JarProcessor[processors.size()]));
+ }
+
+ public void strip(File file) throws IOException {
+ if (kp == null) {
+ return;
+ }
+ Set<String> excludes = getExcludes();
+ if (!excludes.isEmpty()) {
+ StandaloneJarProcessor.run(file, file, new ExcludeProcessor(excludes, verbose));
+ }
+ }
+
+ /**
+ * Returns the <code>.class</code> files to delete. As well the root-parameter as the rename ones
+ * are taken in consideration, so that the concerned files are not listed in the result.
+ *
+ * @return the paths of the files in the jar-archive, including the <code>.class</code> suffix
+ */
+ private Set<String> getExcludes() {
+ Set<String> result = new HashSet<String>();
+ for (String exclude : kp.getExcludes()) {
+ String name = exclude + ".class";
+ String renamed = renames.get(name);
+ result.add((renamed != null) ? renamed : name);
+ }
+ return result;
+ }
+
+ /**
+ * @param struct
+ * @return <code>true</code> if the entry is to include in the output jar
+ * @throws IOException
+ */
+ public boolean process(EntryStruct struct) throws IOException {
+ String name = struct.name;
+ boolean keepIt = chain.process(struct);
+ if (keepIt) {
+ if (!name.equals(struct.name)) {
+ if (kp != null) {
+ renames.put(name, struct.name);
+ }
+ if (verbose) {
+ System.err.println("Renamed " + name + " -> " + struct.name);
+ }
+ }
+ } else {
+ if (verbose) {
+ System.err.println("Removed " + name);
+ }
+ }
+ return keepIt;
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/MainUtil.java b/third_party/jarjar/java/com/tonicsystems/jarjar/MainUtil.java
new file mode 100644
index 0000000000..dabed739c6
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/MainUtil.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+class MainUtil {
+ public static void runMain(Object main, String[] args, String defCommand) throws Exception {
+ if (args.length > 0) {
+ String command = args[0];
+ Method[] methods = main.getClass().getMethods();
+ for (int i = 0; i < methods.length; i++) {
+ Method method = methods[i];
+ if (method.getName().equals(command)) {
+ String[] remaining = new String[args.length - 1];
+ System.arraycopy(args, 1, remaining, 0, remaining.length);
+ try {
+ method.invoke(main, bindParameters(method, remaining));
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof IllegalArgumentException) {
+ System.err.println("Syntax error: " + cause.getMessage());
+ } else if (cause instanceof Exception) {
+ throw (Exception) cause;
+ } else {
+ throw e;
+ }
+ }
+ return;
+ }
+ }
+ }
+ if (defCommand != null) {
+ runMain(main, new String[] {defCommand}, null);
+ }
+ }
+
+ private static Object[] bindParameters(Method method, String[] args) {
+ List<Object> parameters = new ArrayList<Object>();
+ Class[] parameterTypes = method.getParameterTypes();
+ for (int i = 0, len = parameterTypes.length; i < len; i++) {
+ Class type = parameterTypes[i];
+ int remaining = Math.max(0, args.length - i);
+ if (type.equals(String[].class)) {
+ String[] rest = new String[remaining];
+ System.arraycopy(args, 1, rest, 0, remaining);
+ parameters.add(rest);
+ } else if (remaining > 0) {
+ parameters.add(convertParameter(args[i], parameterTypes[i]));
+ } else {
+ parameters.add(null);
+ }
+ }
+ return parameters.toArray();
+ }
+
+ private static Object convertParameter(String arg, Class type) {
+ if (type.equals(String.class)) {
+ return arg;
+ } else if (type.equals(Integer.class)) {
+ return Integer.valueOf(arg, 10);
+ } else if (type.equals(File.class)) {
+ return new File(arg);
+ } else {
+ throw new UnsupportedOperationException("Unknown type " + type);
+ }
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/ManifestProcessor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/ManifestProcessor.java
new file mode 100644
index 0000000000..c8903a9db8
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/ManifestProcessor.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+import com.tonicsystems.jarjar.util.*;
+import java.io.IOException;
+import java.util.*;
+
+class ManifestProcessor implements JarProcessor {
+ private static final String MANIFEST_PATH = "META-INF/MANIFEST.MF";
+ private static final ManifestProcessor INSTANCE = new ManifestProcessor();
+
+ public static ManifestProcessor getInstance() {
+ return INSTANCE;
+ }
+
+ private ManifestProcessor() {}
+
+ public boolean process(EntryStruct struct) throws IOException {
+ return !struct.name.equalsIgnoreCase(MANIFEST_PATH);
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/PackageRemapper.java b/third_party/jarjar/java/com/tonicsystems/jarjar/PackageRemapper.java
new file mode 100644
index 0000000000..3506366a81
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/PackageRemapper.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+import java.util.*;
+import java.util.regex.Pattern;
+import org.objectweb.asm.*;
+import org.objectweb.asm.commons.*;
+
+class PackageRemapper extends Remapper {
+ private static final String RESOURCE_SUFFIX = "RESOURCE";
+
+ private static final Pattern ARRAY_FOR_NAME_PATTERN =
+ Pattern.compile("\\[L[\\p{javaJavaIdentifierPart}\\.]+?;");
+
+ private final List<Wildcard> wildcards;
+ private final Map<String, String> typeCache = new HashMap<String, String>();
+ private final Map<String, String> pathCache = new HashMap<String, String>();
+ private final Map<Object, String> valueCache = new HashMap<Object, String>();
+ private final boolean verbose;
+
+ public PackageRemapper(List<Rule> ruleList, boolean verbose) {
+ this.verbose = verbose;
+ wildcards = PatternElement.createWildcards(ruleList);
+ }
+
+ // also used by KeepProcessor
+ static boolean isArrayForName(String value) {
+ return ARRAY_FOR_NAME_PATTERN.matcher(value).matches();
+ }
+
+ public String map(String key) {
+ String s = typeCache.get(key);
+ if (s == null) {
+ s = replaceHelper(key);
+ if (key.equals(s)) {
+ s = null;
+ }
+ typeCache.put(key, s);
+ }
+ return s;
+ }
+
+ public String mapPath(String path) {
+ String s = pathCache.get(path);
+ if (s == null) {
+ s = path;
+ int slash = s.lastIndexOf('/');
+ String end;
+ if (slash < 0) {
+ end = s;
+ s = RESOURCE_SUFFIX;
+ } else {
+ end = s.substring(slash + 1);
+ s = s.substring(0, slash + 1) + RESOURCE_SUFFIX;
+ }
+ boolean absolute = s.startsWith("/");
+ if (absolute) {
+ s = s.substring(1);
+ }
+
+ s = replaceHelper(s);
+
+ if (absolute) {
+ s = "/" + s;
+ }
+ if (s.indexOf(RESOURCE_SUFFIX) < 0) {
+ return path;
+ }
+ s = s.substring(0, s.length() - RESOURCE_SUFFIX.length()) + end;
+ pathCache.put(path, s);
+ }
+ return s;
+ }
+
+ public Object mapValue(Object value) {
+ if (value instanceof String) {
+ String s = valueCache.get(value);
+ if (s == null) {
+ s = (String) value;
+ if (isArrayForName(s)) {
+ String desc1 = s.replace('.', '/');
+ String desc2 = mapDesc(desc1);
+ if (!desc2.equals(desc1)) {
+ return desc2.replace('/', '.');
+ }
+ } else {
+ s = mapPath(s);
+ if (s.equals(value)) {
+ boolean hasDot = s.indexOf('.') >= 0;
+ boolean hasSlash = s.indexOf('/') >= 0;
+ if (!(hasDot && hasSlash)) {
+ if (hasDot) {
+ s = replaceHelper(s.replace('.', '/')).replace('/', '.');
+ } else {
+ s = replaceHelper(s);
+ }
+ }
+ }
+ }
+ valueCache.put(value, s);
+ }
+ // TODO: add back class name to verbose message
+ if (verbose && !s.equals(value)) {
+ System.err.println("Changed \"" + value + "\" -> \"" + s + "\"");
+ }
+ return s;
+ } else {
+ return super.mapValue(value);
+ }
+ }
+
+ private String replaceHelper(String value) {
+ for (Wildcard wildcard : wildcards) {
+ String test = wildcard.replace(value);
+ if (test != null) {
+ return test;
+ }
+ }
+ return value;
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/PathClass.java b/third_party/jarjar/java/com/tonicsystems/jarjar/PathClass.java
new file mode 100644
index 0000000000..fbfa24fd7d
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/PathClass.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+public class PathClass {
+ private String classPath;
+ private String className;
+
+ public PathClass(String classPath, String className) {
+ this.classPath = classPath;
+ this.className = className;
+ }
+
+ public String getClassPath() {
+ return classPath;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public String toString() {
+ return classPath + "!" + className;
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/PatternElement.java b/third_party/jarjar/java/com/tonicsystems/jarjar/PatternElement.java
new file mode 100644
index 0000000000..2b87cff8a3
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/PatternElement.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+import java.util.*;
+
+public abstract class PatternElement {
+ private String pattern;
+
+ public void setPattern(String pattern) {
+ this.pattern = pattern;
+ }
+
+ public String getPattern() {
+ return pattern;
+ }
+
+ static List<Wildcard> createWildcards(List<? extends PatternElement> patterns) {
+ List<Wildcard> wildcards = new ArrayList<Wildcard>();
+ for (PatternElement pattern : patterns) {
+ String result = (pattern instanceof Rule) ? ((Rule) pattern).getResult() : "";
+ String expr = pattern.getPattern();
+ if (expr.indexOf('/') >= 0) {
+ throw new IllegalArgumentException("Patterns cannot contain slashes");
+ }
+ wildcards.add(new Wildcard(expr.replace('.', '/'), result));
+ }
+ return wildcards;
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/ResourceProcessor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/ResourceProcessor.java
new file mode 100644
index 0000000000..76f9189c11
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/ResourceProcessor.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+import com.tonicsystems.jarjar.util.*;
+import java.io.IOException;
+import java.util.*;
+
+class ResourceProcessor implements JarProcessor {
+ private PackageRemapper pr;
+
+ public ResourceProcessor(PackageRemapper pr) {
+ this.pr = pr;
+ }
+
+ public boolean process(EntryStruct struct) throws IOException {
+ if (!struct.isClass()) {
+ struct.name = pr.mapPath(struct.name);
+ }
+ return true;
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/Rule.java b/third_party/jarjar/java/com/tonicsystems/jarjar/Rule.java
new file mode 100644
index 0000000000..96de530331
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/Rule.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+public class Rule extends PatternElement {
+ private String result;
+
+ public void setResult(String result) {
+ this.result = result;
+ }
+
+ public String getResult() {
+ return result;
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/RulesFileParser.java b/third_party/jarjar/java/com/tonicsystems/jarjar/RulesFileParser.java
new file mode 100644
index 0000000000..f737e494fb
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/RulesFileParser.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+import java.io.*;
+import java.util.*;
+
+class RulesFileParser {
+ private RulesFileParser() {}
+
+ public static List<PatternElement> parse(File file) throws IOException {
+ return parse(new FileReader(file));
+ }
+
+ public static List<PatternElement> parse(String value) throws IOException {
+ return parse(new java.io.StringReader(value));
+ }
+
+ private static String stripComment(String in) {
+ int p = in.indexOf("#");
+ return p < 0 ? in : in.substring(0, p);
+ }
+
+ private static List<PatternElement> parse(Reader r) throws IOException {
+ try {
+ List<PatternElement> patterns = new ArrayList<PatternElement>();
+ BufferedReader br = new BufferedReader(r);
+ int c = 1;
+ String line;
+ while ((line = br.readLine()) != null) {
+ line = stripComment(line);
+ if ("".equals(line)) {
+ continue;
+ }
+ String[] parts = line.split("\\s+");
+ if (parts.length < 2) {
+ error(c, parts);
+ }
+ String type = parts[0];
+ PatternElement element = null;
+ if (type.equals("rule")) {
+ if (parts.length < 3) {
+ error(c, parts);
+ }
+ Rule rule = new Rule();
+ rule.setResult(parts[2]);
+ element = rule;
+ } else if (type.equals("zap")) {
+ element = new Zap();
+ } else if (type.equals("keep")) {
+ element = new Keep();
+ } else {
+ error(c, parts);
+ }
+ element.setPattern(parts[1]);
+ patterns.add(element);
+ c++;
+ }
+ return patterns;
+ } finally {
+ r.close();
+ }
+ }
+
+ private static void error(int line, String[] parts) {
+ throw new IllegalArgumentException("Error on line " + line + ": " + Arrays.asList(parts));
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/StringDumper.java b/third_party/jarjar/java/com/tonicsystems/jarjar/StringDumper.java
new file mode 100644
index 0000000000..75c83b5d7c
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/StringDumper.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+import com.tonicsystems.jarjar.util.*;
+import java.io.*;
+import org.objectweb.asm.*;
+
+class StringDumper {
+ public StringDumper() {}
+
+ public void run(String classPath, PrintWriter pw) throws IOException {
+ StringReader stringReader = new DumpStringReader(pw);
+ ClassPathIterator cp = new ClassPathIterator(classPath);
+ try {
+ while (cp.hasNext()) {
+ ClassPathEntry entry = cp.next();
+ InputStream in = entry.openStream();
+ try {
+ new ClassReader(in).accept(stringReader, 0);
+ } catch (Exception e) {
+ System.err.println("Error reading " + entry.getName() + ": " + e.getMessage());
+ } finally {
+ in.close();
+ }
+ pw.flush();
+ }
+ } catch (RuntimeIOException e) {
+ throw (IOException) e.getCause();
+ } finally {
+ cp.close();
+ }
+ }
+
+ private static class DumpStringReader extends StringReader {
+ private final PrintWriter pw;
+ private String className;
+
+ public DumpStringReader(PrintWriter pw) {
+ this.pw = pw;
+ }
+
+ public void visitString(String className, String value, int line) {
+ if (value.length() > 0) {
+ if (!className.equals(this.className)) {
+ this.className = className;
+ pw.println(className.replace('/', '.'));
+ }
+ pw.print("\t");
+ if (line >= 0) {
+ pw.print(line + ": ");
+ }
+ pw.print(escapeStringLiteral(value));
+ pw.println();
+ }
+ }
+ };
+
+ private static String escapeStringLiteral(String value) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("\"");
+ char[] chars = value.toCharArray();
+ for (int i = 0, size = chars.length; i < size; i++) {
+ char ch = chars[i];
+ switch (ch) {
+ case '\n':
+ sb.append("\\n");
+ break;
+ case '\r':
+ sb.append("\\r");
+ break;
+ case '\b':
+ sb.append("\\b");
+ break;
+ case '\f':
+ sb.append("\\f");
+ break;
+ case '\t':
+ sb.append("\\t");
+ break;
+ case '\"':
+ sb.append("\\\"");
+ break;
+ case '\\':
+ sb.append("\\\\");
+ break;
+ default:
+ sb.append(ch);
+ }
+ }
+ sb.append("\"");
+ return sb.toString();
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/StringReader.java b/third_party/jarjar/java/com/tonicsystems/jarjar/StringReader.java
new file mode 100644
index 0000000000..d1c11088e1
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/StringReader.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+import org.objectweb.asm.*;
+
+abstract class StringReader extends ClassVisitor {
+ private int line = -1;
+ private String className;
+
+ public StringReader() {
+ super(Opcodes.ASM6);
+ }
+
+ public abstract void visitString(String className, String value, int line);
+
+ private void handleObject(Object value) {
+ if (value instanceof String) {
+ visitString(className, (String) value, line);
+ }
+ }
+
+ @Override
+ public void visit(
+ int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ className = name;
+ line = -1;
+ }
+
+ public FieldVisitor visitField(
+ int access, String name, String desc, String signature, Object value) {
+ handleObject(value);
+ return new FieldVisitor(Opcodes.ASM6) {
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ return StringReader.this.visitAnnotation(desc, visible);
+ }
+ };
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ return new AnnotationVisitor(Opcodes.ASM6) {
+ @Override
+ public void visit(String name, Object value) {
+ handleObject(value);
+ }
+
+ @Override
+ public void visitEnum(String name, String desc, String value) {
+ handleObject(value);
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String name, String desc) {
+ return this;
+ }
+ };
+ }
+
+ @Override
+ public MethodVisitor visitMethod(
+ int access, String name, String desc, String signature, String[] exceptions) {
+ MethodVisitor mv =
+ new MethodVisitor(Opcodes.ASM6) {
+ @Override
+ public void visitLdcInsn(Object cst) {
+ handleObject(cst);
+ }
+
+ @Override
+ public void visitLineNumber(int line, Label start) {
+ StringReader.this.line = line;
+ }
+
+ @Override
+ public void visitInvokeDynamicInsn(
+ String name, String desc, Handle bsm, Object... bsmArgs) {
+ for (Object bsmArg : bsmArgs) handleObject(bsmArg);
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ return StringReader.this.visitAnnotation(desc, visible);
+ }
+
+ @Override
+ public AnnotationVisitor visitParameterAnnotation(
+ int parameter, String desc, boolean visible) {
+ return StringReader.this.visitAnnotation(desc, visible);
+ }
+ };
+ return mv;
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/TextDepHandler.java b/third_party/jarjar/java/com/tonicsystems/jarjar/TextDepHandler.java
new file mode 100644
index 0000000000..a84286cdd3
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/TextDepHandler.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+import java.io.*;
+import java.util.*;
+
+public class TextDepHandler extends AbstractDepHandler {
+ private PrintWriter w;
+
+ public TextDepHandler(PrintWriter w, int level) {
+ super(level);
+ this.w = w;
+ }
+
+ protected void handle(String from, String to) throws IOException {
+ w.println(from + " -> " + to);
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/Wildcard.java b/third_party/jarjar/java/com/tonicsystems/jarjar/Wildcard.java
new file mode 100644
index 0000000000..8deb3518d0
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/Wildcard.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+class Wildcard {
+ private static Pattern dstar = Pattern.compile("\\*\\*");
+ private static Pattern star = Pattern.compile("\\*");
+ private static Pattern estar = Pattern.compile("\\+\\??\\)\\Z");
+
+ private final Pattern pattern;
+ private final int count;
+ private final ArrayList<Object> parts = new ArrayList<Object>(16); // kept for debugging
+ private final String[] strings;
+ private final int[] refs;
+
+ public Wildcard(String pattern, String result) {
+ if (pattern.equals("**")) {
+ throw new IllegalArgumentException("'**' is not a valid pattern");
+ }
+ if (!checkIdentifierChars(pattern, "/*-")) {
+ throw new IllegalArgumentException("Not a valid package pattern: " + pattern);
+ }
+ if (pattern.indexOf("***") >= 0) {
+ throw new IllegalArgumentException("The sequence '***' is invalid in a package pattern");
+ }
+
+ String regex = pattern;
+ regex = replaceAllLiteral(dstar, regex, "(.+?)");
+ regex = replaceAllLiteral(star, regex, "([^/]+)");
+ regex = replaceAllLiteral(estar, regex, "*)");
+ this.pattern = Pattern.compile("\\A" + regex + "\\Z");
+ this.count = this.pattern.matcher("foo").groupCount();
+
+ // TODO: check for illegal characters
+ char[] chars = result.toCharArray();
+ int max = 0;
+ for (int i = 0, mark = 0, state = 0, len = chars.length; i < len + 1; i++) {
+ char ch = (i == len) ? '@' : chars[i];
+ if (state == 0) {
+ if (ch == '@') {
+ parts.add(new String(chars, mark, i - mark));
+ mark = i + 1;
+ state = 1;
+ }
+ } else {
+ switch (ch) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ break;
+ default:
+ if (i == mark) {
+ throw new IllegalArgumentException("Backslash not followed by a digit");
+ }
+ int n = Integer.parseInt(new String(chars, mark, i - mark));
+ if (n > max) {
+ max = n;
+ }
+ parts.add(new Integer(n));
+ mark = i--;
+ state = 0;
+ }
+ }
+ }
+ int size = parts.size();
+ strings = new String[size];
+ refs = new int[size];
+ Arrays.fill(refs, -1);
+ for (int i = 0; i < size; i++) {
+ Object v = parts.get(i);
+ if (v instanceof String) {
+ strings[i] = ((String) v).replace('.', '/');
+ } else {
+ refs[i] = ((Integer) v).intValue();
+ }
+ }
+ if (count < max) {
+ throw new IllegalArgumentException(
+ "Result includes impossible placeholder \"@" + max + "\": " + result);
+ }
+ // System.err.println(this);
+ }
+
+ public boolean matches(String value) {
+ return getMatcher(value) != null;
+ }
+
+ public String replace(String value) {
+ Matcher matcher = getMatcher(value);
+ if (matcher != null) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < strings.length; i++)
+ sb.append((refs[i] >= 0) ? matcher.group(refs[i]) : strings[i]);
+ return sb.toString();
+ }
+ return null;
+ }
+
+ private Matcher getMatcher(String value) {
+ Matcher matcher = pattern.matcher(value);
+ if (matcher.matches() && checkIdentifierChars(value, "/-")) {
+ return matcher;
+ }
+ return null;
+ }
+
+ private static boolean checkIdentifierChars(String expr, String extra) {
+ // package-info violates the spec for Java Identifiers.
+ // Nevertheless, expressions that end with this string are still legal.
+ // See 7.4.1.1 of the Java language spec for discussion.
+ if (expr.endsWith("package-info")) {
+ expr = expr.substring(0, expr.length() - "package-info".length());
+ }
+ for (int i = 0, len = expr.length(); i < len; i++) {
+ char c = expr.charAt(i);
+ if (extra.indexOf(c) >= 0) {
+ continue;
+ }
+ if (!Character.isJavaIdentifierPart(c)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static String replaceAllLiteral(Pattern pattern, String value, String replace) {
+ replace = replace.replaceAll("([$\\\\])", "\\\\$0");
+ return pattern.matcher(value).replaceAll(replace);
+ }
+
+ public String toString() {
+ return "Wildcard{pattern=" + pattern + ",parts=" + parts + "}";
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/Zap.java b/third_party/jarjar/java/com/tonicsystems/jarjar/Zap.java
new file mode 100644
index 0000000000..30a341ddc2
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/Zap.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+public class Zap extends PatternElement {}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/ZapProcessor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/ZapProcessor.java
new file mode 100644
index 0000000000..7431ec3120
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/ZapProcessor.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar;
+
+import com.tonicsystems.jarjar.util.*;
+import java.io.IOException;
+import java.util.*;
+
+class ZapProcessor implements JarProcessor {
+ private List<Wildcard> wildcards;
+
+ public ZapProcessor(List<Zap> zapList) {
+ wildcards = PatternElement.createWildcards(zapList);
+ }
+
+ public boolean process(EntryStruct struct) throws IOException {
+ String name = struct.name;
+ if (struct.isClass()) {
+ return !zap(name.substring(0, name.length() - 6));
+ }
+ return true;
+ }
+
+ private boolean zap(String desc) {
+ // TODO: optimize
+ for (Wildcard wildcard : wildcards) {
+ if (wildcard.matches(desc)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/help.txt b/third_party/jarjar/java/com/tonicsystems/jarjar/help.txt
new file mode 100644
index 0000000000..8410909164
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/help.txt
@@ -0,0 +1,75 @@
+Jar Jar Links - A utility to repackage and embed Java libraries
+Copyright 2007 Google Inc.
+
+Command-line usage:
+
+ java -jar jarjar.jar [help]
+
+ Prints this help message.
+
+ java -jar jarjar.jar strings <cp>
+
+ Dumps all string literals in classpath <cp>. Line numbers will be
+ included if the classes have debug information.
+
+ java -jar jarjar.jar find <level> <cp1> [<cp2>]
+
+ Prints dependencies on classpath <cp2> in classpath <cp1>. If <cp2>
+ is omitted, <cp1> is used for both arguments.
+
+ The level argument must be "class" or "jar". The former prints
+ dependencies between individual classes, while the latter only
+ prints jar->jar dependencies. A "jar" in this context is actually
+ any classpath component, which can be a jar file, a zip file, or a
+ parent directory (see below).
+
+ java -jar jarjar.jar process <rulesFile> <inJar> <outJar>
+
+ Transform the <inJar> jar file, writing a new jar file to <outJar>.
+ Any existing file named by <outJar> will be deleted.
+
+ The transformation is defined by a set of rules in the file specified
+ by the rules argument (see below).
+
+Classpath format:
+
+ The classpath argument is a colon or semi-colon delimited set
+ (depending on platform) of directories, jar files, or zip files. See
+ the following page for more details:
+ http://java.sun.com/j2se/1.5.0/docs/tooldocs/solaris/classpath.html
+
+ Mustang-style wildcards are also supported:
+ http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6268383
+
+Rules file format:
+
+ The rules file is a text file, one rule per line. Leading and trailing
+ whitespace is ignored. There are three types of rules:
+
+ rule <pattern> <result>
+ zap <pattern>
+ keep <pattern>
+
+ The standard rule ("rule") is used to rename classes. All references
+ to the renamed classes will also be updated. If a class name is
+ matched by more than one rule, only the first one will apply.
+
+ <pattern> is a class name with optional wildcards. "**" will
+ match against any valid class name substring. To match a single
+ package component (by excluding "." from the match), a single "*" may
+ be used instead.
+
+ <result> is a class name which can optionally reference the
+ substrings matched by the wildcards. A numbered reference is available
+ for every "*" or "**" in the <pattern>, starting from left to
+ right: "@1", "@2", etc. A special "@0" reference contains the entire
+ matched class name.
+
+ The "zap" rule causes any matched class to be removed from the resulting
+ jar file. All zap rules are processed before renaming rules.
+
+ The "keep" rule marks all matched classes as "roots". If any keep
+ rules are defined all classes which are not reachable from the roots
+ via dependency analysis are discarded when writing the output
+ jar. This is the last step in the process, after renaming and zapping.
+
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/AntJarProcessor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/AntJarProcessor.java
new file mode 100644
index 0000000000..85bbe6cf8d
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/AntJarProcessor.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar.util;
+
+import java.io.*;
+import java.util.*;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Jar;
+import org.apache.tools.ant.types.ZipFileSet;
+import org.apache.tools.zip.JarMarker;
+import org.apache.tools.zip.ZipExtraField;
+import org.apache.tools.zip.ZipOutputStream;
+
+public abstract class AntJarProcessor extends Jar {
+ private EntryStruct struct = new EntryStruct();
+ private JarProcessor proc;
+ private byte[] buf = new byte[0x2000];
+
+ private Set<String> dirs = new HashSet<String>();
+ private boolean filesOnly;
+
+ protected boolean verbose;
+
+ private static final ZipExtraField[] JAR_MARKER = new ZipExtraField[] {JarMarker.getInstance()};
+
+ public void setVerbose(boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ public abstract void execute() throws BuildException;
+
+ public void execute(JarProcessor proc) throws BuildException {
+ this.proc = proc;
+ super.execute();
+ }
+
+ public void setFilesonly(boolean f) {
+ super.setFilesonly(f);
+ filesOnly = f;
+ }
+
+ protected void zipDir(File dir, ZipOutputStream zOut, String vPath, int mode)
+ throws IOException {}
+
+ protected void zipFile(
+ InputStream is,
+ ZipOutputStream zOut,
+ String vPath,
+ long lastModified,
+ File fromArchive,
+ int mode)
+ throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ IoUtil.pipe(is, baos, buf);
+ struct.data = baos.toByteArray();
+ struct.name = vPath;
+ struct.time = lastModified;
+ if (proc.process(struct)) {
+ if (mode == 0) {
+ mode = ZipFileSet.DEFAULT_FILE_MODE;
+ }
+ if (!filesOnly) {
+ addParentDirs(struct.name, zOut);
+ }
+ super.zipFile(
+ new ByteArrayInputStream(struct.data), zOut, struct.name, struct.time, fromArchive, mode);
+ }
+ }
+
+ private void addParentDirs(String file, ZipOutputStream zOut) throws IOException {
+ int slash = file.lastIndexOf('/');
+ if (slash >= 0) {
+ String dir = file.substring(0, slash);
+ if (dirs.add(dir)) {
+ addParentDirs(dir, zOut);
+ super.zipDir((File) null, zOut, dir + "/", ZipFileSet.DEFAULT_DIR_MODE, JAR_MARKER);
+ }
+ }
+ }
+
+ public void reset() {
+ super.reset();
+ cleanHelper();
+ }
+
+ protected void cleanUp() {
+ super.cleanUp();
+ cleanHelper();
+ }
+
+ protected void cleanHelper() {
+ verbose = false;
+ filesOnly = false;
+ dirs.clear();
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassHeaderReader.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassHeaderReader.java
new file mode 100644
index 0000000000..8e58951143
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassHeaderReader.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar.util;
+
+import java.io.*;
+import java.lang.reflect.Array;
+import java.util.*;
+
+public class ClassHeaderReader {
+ private int access;
+ private String thisClass;
+ private String superClass;
+ private String[] interfaces;
+
+ private InputStream in;
+ private byte[] b = new byte[0x2000];
+ private int[] items = new int[1000];
+ private int bsize = 0;
+ private MyByteArrayInputStream bin = new MyByteArrayInputStream();
+ private DataInputStream data = new DataInputStream(bin);
+
+ public int getAccess() {
+ return access;
+ }
+
+ public String getClassName() {
+ return thisClass;
+ }
+
+ public String getSuperName() {
+ return superClass;
+ }
+
+ public String[] getInterfaces() {
+ return interfaces;
+ }
+
+ public void read(InputStream in) throws IOException {
+ try {
+ this.in = in;
+ bsize = 0;
+ access = 0;
+ thisClass = superClass = null;
+ interfaces = null;
+
+ try {
+ buffer(4);
+ } catch (IOException e) {
+ // ignore
+ }
+ if (b[0] != (byte) 0xCA
+ || b[1] != (byte) 0xFE
+ || b[2] != (byte) 0xBA
+ || b[3] != (byte) 0xBE) {
+ throw new ClassFormatError("Bad magic number");
+ }
+
+ buffer(6);
+ readUnsignedShort(4); // minorVersion
+ readUnsignedShort(6); // majorVersion
+ // TODO: check version
+ int constant_pool_count = readUnsignedShort(8);
+ items = (int[]) resizeArray(items, constant_pool_count);
+
+ int index = 10;
+ for (int i = 1; i < constant_pool_count; i++) {
+ int size;
+ buffer(index + 3); // TODO: reduce calls to buffer
+ int tag = b[index];
+ items[i] = index + 1;
+ switch (tag) {
+ case 9: // Fieldref
+ case 10: // Methodref
+ case 11: // InterfaceMethodref
+ case 3: // Integer
+ case 4: // Float
+ case 12: // NameAndType
+ size = 4;
+ break;
+ case 5: // Long
+ case 6: // Double
+ size = 8;
+ i++;
+ break;
+ case 1: // Utf8
+ size = 2 + readUnsignedShort(index + 1);
+ break;
+ case 7: // Class
+ case 8: // String
+ size = 2;
+ break;
+ default:
+ throw new IllegalStateException("Unknown constant pool tag " + tag);
+ }
+ index += size + 1;
+ }
+ buffer(index + 8);
+ access = readUnsignedShort(index);
+ thisClass = readClass(index + 2);
+ superClass = readClass(index + 4);
+ int interfaces_count = readUnsignedShort(index + 6);
+
+ index += 8;
+ buffer(index + interfaces_count * 2);
+ interfaces = new String[interfaces_count];
+ for (int i = 0; i < interfaces_count; i++) {
+ interfaces[i] = readClass(index);
+ index += 2;
+ }
+ } finally {
+ in.close();
+ }
+ }
+
+ private String readClass(int index) throws IOException {
+ index = readUnsignedShort(index);
+ if (index == 0) {
+ return null;
+ }
+ index = readUnsignedShort(items[index]);
+ bin.readFrom(b, items[index]);
+ return data.readUTF();
+ }
+
+ private int readUnsignedShort(int index) {
+ byte[] b = this.b;
+ return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF);
+ }
+
+ private static final int CHUNK = 2048;
+
+ private void buffer(int amount) throws IOException {
+ if (amount > b.length) {
+ b = (byte[]) resizeArray(b, b.length * 2);
+ }
+ if (amount > bsize) {
+ int rounded = (int) (CHUNK * Math.ceil((float) amount / CHUNK));
+ bsize += read(in, b, bsize, rounded - bsize);
+ if (amount > bsize) {
+ throw new EOFException();
+ }
+ }
+ }
+
+ private static int read(InputStream in, byte[] b, int off, int len) throws IOException {
+ int total = 0;
+ while (total < len) {
+ int result = in.read(b, off + total, len - total);
+ if (result == -1) {
+ break;
+ }
+ total += result;
+ }
+ return total;
+ }
+
+ private static Object resizeArray(Object array, int length) {
+ if (Array.getLength(array) < length) {
+ Object newArray = Array.newInstance(array.getClass().getComponentType(), length);
+ System.arraycopy(array, 0, newArray, 0, Array.getLength(array));
+ return newArray;
+ } else {
+ return array;
+ }
+ }
+
+ private static class MyByteArrayInputStream extends ByteArrayInputStream {
+ public MyByteArrayInputStream() {
+ super(new byte[0]);
+ }
+
+ public void readFrom(byte[] buf, int pos) {
+ this.buf = buf;
+ this.pos = pos;
+ count = buf.length;
+ }
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassPathEntry.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassPathEntry.java
new file mode 100644
index 0000000000..e90fd8cfa0
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassPathEntry.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * 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.tonicsystems.jarjar.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public interface ClassPathEntry {
+ String getSource() throws IOException;
+
+ String getName();
+
+ InputStream openStream() throws IOException;
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassPathIterator.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassPathIterator.java
new file mode 100644
index 0000000000..d2210a4ac4
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/ClassPathIterator.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar.util;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+import java.util.zip.*;
+
+public class ClassPathIterator implements Iterator<ClassPathEntry> {
+ private static final FileFilter CLASS_FILTER =
+ new FileFilter() {
+ public boolean accept(File file) {
+ return file.isDirectory() || isClass(file.getName());
+ }
+ };
+
+ private static final FileFilter JAR_FILTER =
+ new FileFilter() {
+ public boolean accept(File file) {
+ return hasExtension(file.getName(), ".jar");
+ }
+ };
+
+ private final Iterator<File> files;
+ private Iterator<ClassPathEntry> entries = Collections.<ClassPathEntry>emptyList().iterator();
+ private ClassPathEntry next;
+ private List<ZipFile> zips = new ArrayList<ZipFile>();
+
+ public ClassPathIterator(String classPath) throws IOException {
+ this(new File(System.getProperty("user.dir")), classPath, null);
+ }
+
+ public ClassPathIterator(File parent, String classPath, String delim) throws IOException {
+ if (delim == null) {
+ delim = System.getProperty("path.separator");
+ }
+ StringTokenizer st = new StringTokenizer(classPath, delim);
+ List<File> fileList = new ArrayList<File>();
+ while (st.hasMoreTokens()) {
+ String part = (String) st.nextElement();
+ boolean wildcard = false;
+ if (part.endsWith("/*")) {
+ part = part.substring(0, part.length() - 1);
+ if (part.indexOf('*') >= 0) {
+ throw new IllegalArgumentException("Multiple wildcards are not allowed: " + part);
+ }
+ wildcard = true;
+ } else if (part.indexOf('*') >= 0) {
+ throw new IllegalArgumentException("Incorrect wildcard usage: " + part);
+ }
+
+ File file = new File(part);
+ if (!file.isAbsolute()) {
+ file = new File(parent, part);
+ }
+ if (!file.exists()) {
+ throw new IllegalArgumentException("File " + file + " does not exist");
+ }
+
+ if (wildcard) {
+ if (!file.isDirectory()) {
+ throw new IllegalArgumentException("File " + file + " + is not a directory");
+ }
+ fileList.addAll(findFiles(file, JAR_FILTER, false, new ArrayList<File>()));
+ } else {
+ fileList.add(file);
+ }
+ }
+ this.files = fileList.iterator();
+ advance();
+ }
+
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ /** Closes all zip files opened by this iterator. */
+ public void close() throws IOException {
+ next = null;
+ for (ZipFile zip : zips) {
+ zip.close();
+ }
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ public ClassPathEntry next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ ClassPathEntry result = next;
+ try {
+ advance();
+ } catch (IOException e) {
+ throw new RuntimeIOException(e);
+ }
+ return result;
+ }
+
+ private void advance() throws IOException {
+ if (!entries.hasNext()) {
+ if (!files.hasNext()) {
+ next = null;
+ return;
+ }
+ File file = files.next();
+ if (hasExtension(file.getName(), ".jar")) {
+ ZipFile zip = new JarFile(file);
+ zips.add(zip);
+ entries = new ZipIterator(zip);
+ } else if (hasExtension(file.getName(), ".zip")) {
+ ZipFile zip = new ZipFile(file);
+ zips.add(zip);
+ entries = new ZipIterator(zip);
+ } else if (file.isDirectory()) {
+ entries = new FileIterator(file);
+ } else {
+ throw new IllegalArgumentException("Do not know how to handle " + file);
+ }
+ }
+
+ boolean foundClass = false;
+ while (!foundClass && entries.hasNext()) {
+ next = entries.next();
+ foundClass = isClass(next.getName());
+ }
+ if (!foundClass) {
+ advance();
+ }
+ }
+
+ private static class ZipIterator implements Iterator<ClassPathEntry> {
+ private final ZipFile zip;
+ private final Enumeration<? extends ZipEntry> entries;
+
+ ZipIterator(ZipFile zip) {
+ this.zip = zip;
+ this.entries = zip.entries();
+ }
+
+ public boolean hasNext() {
+ return entries.hasMoreElements();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ public ClassPathEntry next() {
+ final ZipEntry entry = entries.nextElement();
+ return new ClassPathEntry() {
+ public String getSource() {
+ return zip.getName();
+ }
+
+ public String getName() {
+ return entry.getName();
+ }
+
+ public InputStream openStream() throws IOException {
+ return zip.getInputStream(entry);
+ }
+ };
+ }
+ }
+
+ private static class FileIterator implements Iterator<ClassPathEntry> {
+ private final File dir;
+ private final Iterator<File> entries;
+
+ FileIterator(File dir) {
+ this.dir = dir;
+ this.entries = findFiles(dir, CLASS_FILTER, true, new ArrayList<File>()).iterator();
+ }
+
+ public boolean hasNext() {
+ return entries.hasNext();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ public ClassPathEntry next() {
+ final File file = entries.next();
+ return new ClassPathEntry() {
+ public String getSource() throws IOException {
+ return dir.getCanonicalPath();
+ }
+
+ public String getName() {
+ return file.getName();
+ }
+
+ public InputStream openStream() throws IOException {
+ return new BufferedInputStream(new FileInputStream(file));
+ }
+ };
+ }
+ }
+
+ private static List<File> findFiles(
+ File dir, FileFilter filter, boolean recurse, List<File> collect) {
+ for (File file : dir.listFiles(filter)) {
+ if (recurse && file.isDirectory()) {
+ findFiles(file, filter, recurse, collect);
+ } else {
+ collect.add(file);
+ }
+ }
+ return collect;
+ }
+
+ private static boolean isClass(String name) {
+ return hasExtension(name, ".class");
+ }
+
+ private static boolean hasExtension(String name, String ext) {
+ if (name.length() < ext.length()) {
+ return false;
+ }
+ String actual = name.substring(name.length() - ext.length());
+ return actual.equals(ext) || actual.equals(ext.toUpperCase());
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/EntryStruct.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/EntryStruct.java
new file mode 100644
index 0000000000..bf72ab5d69
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/EntryStruct.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar.util;
+
+public class EntryStruct {
+ public byte[] data;
+ public String name;
+ public long time;
+
+ /** Returns true if the entry is a class file. */
+ public boolean isClass() {
+ if (!name.endsWith(".class")) {
+ return false;
+ }
+ if (name.startsWith("META-INF/version")) {
+ // TODO(b/69678527): handle multi-release jar files
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/GetNameClassWriter.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/GetNameClassWriter.java
new file mode 100644
index 0000000000..19011be5ca
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/GetNameClassWriter.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar.util;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Opcodes;
+
+public class GetNameClassWriter extends ClassVisitor {
+ private String className;
+
+ public GetNameClassWriter(int flags) {
+ super(Opcodes.ASM6, new ClassWriter(flags));
+ }
+
+ public void visit(
+ int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ className = name;
+ super.visit(version, access, name, signature, superName, interfaces);
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public byte[] toByteArray() {
+ return ((ClassWriter) cv).toByteArray();
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/IoUtil.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/IoUtil.java
new file mode 100644
index 0000000000..988a85cafd
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/IoUtil.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * 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.tonicsystems.jarjar.util;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+class IoUtil {
+ private IoUtil() {}
+
+ public static void pipe(InputStream is, OutputStream out, byte[] buf) throws IOException {
+ for (; ; ) {
+ int amt = is.read(buf);
+ if (amt < 0) {
+ break;
+ }
+ out.write(buf, 0, amt);
+ }
+ }
+
+ public static void copy(File from, File to, byte[] buf) throws IOException {
+ InputStream in = new FileInputStream(from);
+ try {
+ OutputStream out = new FileOutputStream(to);
+ try {
+ pipe(in, out, buf);
+ } finally {
+ out.close();
+ }
+ } finally {
+ in.close();
+ }
+ }
+
+ /**
+ * Create a copy of an zip file without its empty directories.
+ *
+ * @param inputFile
+ * @param outputFile
+ * @throws IOException
+ */
+ public static void copyZipWithoutEmptyDirectories(final File inputFile, final File outputFile)
+ throws IOException {
+ final byte[] buf = new byte[0x2000];
+
+ final ZipFile inputZip = new ZipFile(inputFile);
+ final ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(outputFile));
+ try {
+ // read a the entries of the input zip file and sort them
+ final Enumeration<? extends ZipEntry> e = inputZip.entries();
+ final ArrayList<ZipEntry> sortedList = new ArrayList<ZipEntry>();
+ while (e.hasMoreElements()) {
+ final ZipEntry entry = e.nextElement();
+ sortedList.add(entry);
+ }
+
+ Collections.sort(
+ sortedList,
+ new Comparator<ZipEntry>() {
+ public int compare(ZipEntry o1, ZipEntry o2) {
+ return o1.getName().compareTo(o2.getName());
+ }
+ });
+
+ // treat them again and write them in output, wenn they not are empty directories
+ for (int i = sortedList.size() - 1; i >= 0; i--) {
+ final ZipEntry inputEntry = sortedList.get(i);
+ final String name = inputEntry.getName();
+ final boolean isEmptyDirectory;
+ if (inputEntry.isDirectory()) {
+ if (i == sortedList.size() - 1) {
+ // no item afterwards; it was an empty directory
+ isEmptyDirectory = true;
+ } else {
+ final String nextName = sortedList.get(i + 1).getName();
+ isEmptyDirectory = !nextName.startsWith(name);
+ }
+ } else {
+ isEmptyDirectory = false;
+ }
+
+ // write the entry
+ if (isEmptyDirectory) {
+ sortedList.remove(inputEntry);
+ } else {
+ final ZipEntry outputEntry = new ZipEntry(inputEntry);
+ outputStream.putNextEntry(outputEntry);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ final InputStream is = inputZip.getInputStream(inputEntry);
+ IoUtil.pipe(is, baos, buf);
+ is.close();
+ outputStream.write(baos.toByteArray());
+ }
+ }
+ } finally {
+ outputStream.close();
+ }
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarProcessor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarProcessor.java
new file mode 100644
index 0000000000..7624080b47
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarProcessor.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar.util;
+
+import java.io.IOException;
+
+public interface JarProcessor {
+ /**
+ * Process the entry (p.ex. rename the file)
+ *
+ * <p>Returns <code>true</code> if the processor has has changed the entry. In this case, the
+ * entry can be removed from the jar file in a future time. Return <code>false</code> for the
+ * entries which do not have been changed and there fore are not to be deleted
+ *
+ * @param struct
+ * @return <code>true</code> if he process chain can continue after this process
+ * @throws IOException
+ */
+ boolean process(EntryStruct struct) throws IOException;
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarProcessorChain.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarProcessorChain.java
new file mode 100644
index 0000000000..6360a3fed7
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarProcessorChain.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar.util;
+
+import java.io.IOException;
+
+public class JarProcessorChain implements JarProcessor {
+ private final JarProcessor[] chain;
+
+ public JarProcessorChain(JarProcessor[] chain) {
+ this.chain = chain.clone();
+ }
+
+ /**
+ * @param struct
+ * @return <code>true</code> if the entry has run the complete chain
+ * @throws IOException
+ */
+ public boolean process(EntryStruct struct) throws IOException {
+
+ for (JarProcessor aChain : chain) {
+ if (!aChain.process(struct)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarTransformer.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarTransformer.java
new file mode 100644
index 0000000000..2b07a88bcc
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarTransformer.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar.util;
+
+import java.io.*;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
+public abstract class JarTransformer implements JarProcessor {
+ public boolean process(EntryStruct struct) throws IOException {
+ if (struct.isClass()) {
+ ClassReader reader;
+ try {
+ reader = new ClassReader(struct.data);
+ } catch (Exception e) {
+ return true; // TODO?
+ }
+ GetNameClassWriter w = new GetNameClassWriter(ClassWriter.COMPUTE_MAXS);
+ reader.accept(transform(w), ClassReader.EXPAND_FRAMES);
+ struct.data = w.toByteArray();
+ struct.name = pathFromName(w.getClassName());
+ }
+ return true;
+ }
+
+ protected abstract ClassVisitor transform(ClassVisitor v);
+
+ private static String pathFromName(String className) {
+ return className.replace('.', '/') + ".class";
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarTransformerChain.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarTransformerChain.java
new file mode 100644
index 0000000000..824115840b
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/JarTransformerChain.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar.util;
+
+import org.objectweb.asm.ClassVisitor;
+
+public class JarTransformerChain extends JarTransformer {
+ private final RemappingClassTransformer[] chain;
+
+ public JarTransformerChain(RemappingClassTransformer[] chain) {
+ this.chain = chain.clone();
+ for (int i = chain.length - 1; i > 0; i--) {
+ chain[i - 1].setTarget(chain[i]);
+ }
+ }
+
+ protected ClassVisitor transform(ClassVisitor v) {
+ chain[chain.length - 1].setTarget(v);
+ return chain[0];
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/RemappingClassTransformer.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/RemappingClassTransformer.java
new file mode 100644
index 0000000000..664ccc12b2
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/RemappingClassTransformer.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar.util;
+
+import com.tonicsystems.jarjar.EmptyClassVisitor;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.commons.ClassRemapper;
+import org.objectweb.asm.commons.Remapper;
+
+public class RemappingClassTransformer extends ClassRemapper {
+ public RemappingClassTransformer(Remapper pr) {
+ super(new EmptyClassVisitor(), pr);
+ }
+
+ public void setTarget(ClassVisitor target) {
+ cv = target;
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/RuntimeIOException.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/RuntimeIOException.java
new file mode 100644
index 0000000000..0cf0a15d77
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/RuntimeIOException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar.util;
+
+import java.io.IOException;
+
+public class RuntimeIOException extends RuntimeException {
+ private static final long serialVersionUID = 0L;
+
+ public RuntimeIOException(IOException e) {
+ super(e);
+ }
+}
diff --git a/third_party/jarjar/java/com/tonicsystems/jarjar/util/StandaloneJarProcessor.java b/third_party/jarjar/java/com/tonicsystems/jarjar/util/StandaloneJarProcessor.java
new file mode 100644
index 0000000000..3d2e424a76
--- /dev/null
+++ b/third_party/jarjar/java/com/tonicsystems/jarjar/util/StandaloneJarProcessor.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.tonicsystems.jarjar.util;
+
+import java.io.*;
+import java.util.*;
+import java.util.Enumeration;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+
+public class StandaloneJarProcessor {
+ public static void run(File from, File to, JarProcessor proc) throws IOException {
+ byte[] buf = new byte[0x2000];
+
+ JarFile in = new JarFile(from);
+ final File tmpTo = File.createTempFile("jarjar", ".jar");
+ JarOutputStream out = new JarOutputStream(new FileOutputStream(tmpTo));
+ Set<String> entries = new HashSet<String>();
+ try {
+ EntryStruct struct = new EntryStruct();
+ Enumeration<JarEntry> e = in.entries();
+ while (e.hasMoreElements()) {
+ JarEntry entry = e.nextElement();
+ struct.name = entry.getName();
+ struct.time = entry.getTime();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ IoUtil.pipe(in.getInputStream(entry), baos, buf);
+ struct.data = baos.toByteArray();
+ if (proc.process(struct)) {
+ if (entries.add(struct.name)) {
+ entry = new JarEntry(struct.name);
+ entry.setTime(struct.time);
+ entry.setCompressedSize(-1);
+ out.putNextEntry(entry);
+ out.write(struct.data);
+ } else if (struct.name.endsWith("/")) {
+ // TODO(chrisn): log
+ } else {
+ throw new IllegalArgumentException("Duplicate jar entries: " + struct.name);
+ }
+ }
+ }
+
+ } finally {
+ in.close();
+ out.close();
+ }
+
+ // delete the empty directories
+ IoUtil.copyZipWithoutEmptyDirectories(tmpTo, to);
+ tmpTo.delete();
+ }
+}