diff options
author | Marcin Kosiba <mkosiba@google.com> | 2014-06-17 09:56:21 +0100 |
---|---|---|
committer | Marcin Kosiba <mkosiba@google.com> | 2014-06-20 11:55:22 +0100 |
commit | ab61347a2cb3254688c42c993278cefd43e5d99d (patch) | |
tree | de9bb96cdb2bc7a710c8222edcdd3e3a33e7086e | |
parent | fe7bad2c42f39829e8d4fd990ac1da776bcaeee2 (diff) | |
download | platform_external_jarjar-ab61347a2cb3254688c42c993278cefd43e5d99d.tar.gz platform_external_jarjar-ab61347a2cb3254688c42c993278cefd43e5d99d.tar.bz2 platform_external_jarjar-ab61347a2cb3254688c42c993278cefd43e5d99d.zip |
Add JarJar 1.4
Change-Id: Iadcec27828223189f579b70d050cd7246cb488c2
62 files changed, 3847 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..e7656fd --- /dev/null +++ b/Android.mk @@ -0,0 +1,44 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-java-files-under, src/main) + +LOCAL_JAVA_LIBRARIES := \ + asm-tools \ + asm-commons \ + maven-plugin-api \ + apache-ant + +LOCAL_MODULE := jarjar +LOCAL_MODULE_TAGS := optional +LOCAL_JAR_MANIFEST := manifest.txt +LOCAL_JAVA_RESOURCE_DIRS := res + +include $(BUILD_HOST_JAVA_LIBRARY) + +################################################## +include $(CLEAR_VARS) + +LOCAL_PREBUILT_JAVA_LIBRARIES := \ + asm-tools:lib/asm-4.0$(COMMON_JAVA_PACKAGE_SUFFIX) \ + asm-commons:lib/asm-commons-4.0$(COMMON_JAVA_PACKAGE_SUFFIX) \ + maven-plugin-api:lib/maven-plugin-api$(COMMON_JAVA_PACKAGE_SUFFIX) \ + apache-ant:lib/apache-ant-1.9.4$(COMMON_JAVA_PACKAGE_SUFFIX) + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_HOST_PREBUILT) @@ -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/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE.txt @@ -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/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/MODULE_LICENSE_APACHE2 diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..d0d73f5 --- /dev/null +++ b/build.xml @@ -0,0 +1,271 @@ +<?xml version='1.0' encoding='UTF-8'?> +<project name="Jar Jar Links" default="jar" xmlns:artifact="antlib:org.apache.maven.artifact.ant"> + <property file="${user.home}/build.properties" /> + <property file="build.properties" /> + + <property name="javadoc.access" value="public"/> + + <property name="compile.source" value="1.5"/> + <property name="compile.target" value="1.5"/> + <property name="compile.bootclasspath" value=""/> + <property name="compile.extdirs" value=""/> + + <property name="compile.debug" value="true"/> + <property name="compile.deprecation" value="false"/> + <property name="compile.optimize" value="false"/> + + <property name="src" location="src" /> + <property name="build" location="build" /> + <property name="dist" location="dist" /> + + <!-- define Maven coordinates --> + <property name="groupId" value="com.googlecode.jarjar" /> + <property name="artifactId" value="jarjar" /> + <property name="version" value="1.4"/> + + <!-- define artifacts' name, which follows the convention of Maven --> + <property name="maven-jar" value="${dist}/${artifactId}-${version}.jar" /> + <property name="maven-javadoc-jar" value="${dist}/${artifactId}-${version}-javadoc.jar" /> + <property name="maven-sources-jar" value="${dist}/${artifactId}-${version}-sources.jar" /> + + <!-- defined maven snapshots and staging repository id and url --> + <property name="maven-snapshots-repository-id" value="sonatype-nexus-snapshots" /> + <property name="maven-snapshots-repository-url" value="https://oss.sonatype.org/content/repositories/snapshots/" /> + <property name="maven-staging-repository-id" value="sonatype-nexus-staging" /> + <property name="maven-staging-repository-url" value="https://oss.sonatype.org/service/local/staging/deploy/maven2/" /> + + + <property name="jarfile" value="dist/jarjar-${version}.jar"/> + <property name="jarfile.util" value="dist/jarjar-util-${version}.jar"/> + <property name="jarfile.mojo" value="dist/jarjar-plugin-${version}.jar"/> + + <property name="test.jar" value="build/rejar.jar"/> + + <target name="init"> + <echo>bootclasspath ${compile.bootclasspath}</echo> + <tstamp/> + <path id="path.build"> + <fileset dir="lib" includes="**/*.jar"/> + </path> + <path id="path.run"> + <path refid="path.build"/> + <pathelement path="build/main"/> + </path> + <path id="path.test"> + <path refid="path.run"/> + <pathelement path="build/test"/> + </path> + </target> + + <macrodef name="compile"> + <attribute name="module"/> + <attribute name="refid"/> + <attribute name="bootclasspath" default=""/> + <sequential> + <mkdir dir="build/@{module}"/> + <copy todir="build/@{module}"> + <fileset dir="src/@{module}" excludes="**/*.java"/> + </copy> + <javac includeantruntime="true" srcdir="src/@{module}" destdir="build/@{module}" + source="${compile.source}" + target="${compile.target}" bootclasspath="@{bootclasspath}" extdirs="${compile.extdirs}" + debug="${compile.debug}" optimize="${compile.optimize}" deprecation="${compile.deprecation}"> + <classpath refid="@{refid}"/> + <compilerarg value="-Xlint"/> + </javac> + </sequential> + </macrodef> + + <target name="compile" depends="init" description="Compile"> + <compile module="main" refid="path.build" bootclasspath="${compile.bootclasspath}"/> + <taskdef name="jarjar" classname="com.tonicsystems.jarjar.JarJarTask" classpathref="path.run"/> + </target> + + <target name="compile-test" depends="compile" description="Compile tests"> + <compile module="test" refid="path.run"/> + </target> + + <target name="clean" description="Clean"> + <delete dir="build"/> + <delete dir="dist"/> + <delete dir="dist/javadoc"/> + </target> + + <target name="jar" depends="compile" description="Create Jar"> + <mkdir dir="dist"/> + <jarjar jarfile="${jarfile}"> + <fileset dir="build/main"/> + <zipfileset src="lib/asm-4.0.jar"/> + <zipfileset src="lib/asm-commons-4.0.jar"> + <include name="org/objectweb/asm/commons/Remap*.class"/> + <include name="org/objectweb/asm/commons/LocalVariablesSorter.class"/> + </zipfileset> + <keep pattern="com.tonicsystems.jarjar.Main"/> + <keep pattern="com.tonicsystems.jarjar.JarJarTask"/> + <rule pattern="com.tonicsystems.jarjar.util.**" result="com.tonicsystems.jarjar.ext_util.@1"/> + <rule pattern="org.objectweb.asm.**" result="com.tonicsystems.jarjar.asm.@1"/> + <manifest> + <attribute name="Main-Class" value="com.tonicsystems.jarjar.Main"/> + <attribute name="Implementation-Version" value="${version}"/> + </manifest> + </jarjar> + </target> + + <target name="jar-util" depends="compile" description="Create utility Jar"> + <mkdir dir="dist"/> + <jar jarfile="${jarfile.util}"> + <fileset dir="build/main" includes="com/tonicsystems/jarjar/util/**"/> + </jar> + </target> + + <target name="dist" depends="jar,dist-src,javadoc" description="generate the distribution"> + <copy file="${dist}/jarjar-src-${version}.zip" tofile="${maven-sources-jar}" /> + <jar jarfile="${maven-javadoc-jar}"> + <fileset dir="${dist}/javadoc" /> + </jar> + </target> + + <!-- before this, update project version (both build.xml and pom.xml) from SNAPSHOT to RELEASE --> + <target name="stage" depends="dist" description="deploy release version to Maven staging repository"> + <!-- sign and deploy the main artifact --> + <artifact:mvn> + <arg value="org.apache.maven.plugins:maven-gpg-plugin:1.3:sign-and-deploy-file" /> + <arg value="-Durl=${maven-staging-repository-url}" /> + <arg value="-DrepositoryId=${maven-staging-repository-id}" /> + <arg value="-DpomFile=pom.xml" /> + <arg value="-Dfile=${maven-jar}" /> + <arg value="-Pgpg" /> + </artifact:mvn> + + <!-- sign and deploy the sources artifact --> + <artifact:mvn> + <arg value="org.apache.maven.plugins:maven-gpg-plugin:1.3:sign-and-deploy-file" /> + <arg value="-Durl=${maven-staging-repository-url}" /> + <arg value="-DrepositoryId=${maven-staging-repository-id}" /> + <arg value="-DpomFile=pom.xml" /> + <arg value="-Dfile=${maven-sources-jar}" /> + <arg value="-Dclassifier=sources" /> + <arg value="-Pgpg" /> + </artifact:mvn> + + <!-- sign and deploy the javadoc artifact --> + <artifact:mvn> + <arg value="org.apache.maven.plugins:maven-gpg-plugin:1.3:sign-and-deploy-file" /> + <arg value="-Durl=${maven-staging-repository-url}" /> + <arg value="-DrepositoryId=${maven-staging-repository-id}" /> + <arg value="-DpomFile=pom.xml" /> + <arg value="-Dfile=${maven-javadoc-jar}" /> + <arg value="-Dclassifier=javadoc" /> + <arg value="-Pgpg" /> + </artifact:mvn> + </target> + + <target name="dist-src" depends="compile" description="Create source distribution"> + <mkdir dir="${dist}"/> + <zip destfile="${dist}/jarjar-src-${version}.zip"> + <zipfileset dir="src" excludes="**/*.save" prefix="jarjar-${version}/src"/> + <zipfileset dir="lib" prefix="jarjar-${version}/lib"/> + <zipfileset dir="maven" prefix="jarjar-${version}/maven"/> + <zipfileset dir="." includes="build.xml,COPYING" prefix="jarjar-${version}"/> + </zip> + </target> + + <target name="mojo" depends="jar" description="Create Maven plugin"> + <mkdir dir="dist"/> + <copy todir="dist/maven"> + <fileset dir="maven"/> + <filterset> + <filter token="VERSION" value="${version}"/> + </filterset> + </copy> + <jar jarfile="${jarfile.mojo}"> + <zipfileset src="${jarfile}"/> + <zipfileset dir="dist/maven" prefix="META-INF/maven" includes="plugin.xml"/> + <zipfileset + dir="dist/maven" + prefix="META-INF/maven/com.tonicsystems.jarjar/jarjar-plugin" + includes="pom.xml,pom.properties"/> + </jar> + </target> + + <target name="test" depends="junit,test-rejar,test-manifest,test-enum" description="Run tests"/> + + <target name="junit" depends="compile-test"> + <junit haltonfailure="on"> + <!-- <jvmarg value="-Xint"/> --> + <!-- <jvmarg value="-Xprof"/> --> + <!-- <jvmarg value="-server"/> --> + <formatter type="plain" usefile="false"/> + <classpath refid="path.test"/> + <batchtest> + <fileset dir="src/test" includes="**/*Test.java"/> + </batchtest> + </junit> + </target> + + <target name="test-rejar" depends="jar"> + <taskdef name="jarjar2" classname="com.tonicsystems.jarjar.JarJarTask" classpath="${jarfile}"/> + <delete file="${test.jar}"/> + <jarjar2 jarfile="${test.jar}"> + <fileset dir="build/main"/> + <zipfileset src="lib/asm-4.0.jar"/> + <zipfileset src="lib/asm-commons-4.0.jar"/> + <rule pattern="org.objectweb.asm.**" result="com.tonicsystems.jarjar.asm.@1"/> + </jarjar2> + <delete file="${test.jar}"/> + </target> + + <target name="test-enum" depends="compile"> + <delete file="build/enumtest.jar"/> + <jarjar jarfile="build/enumtest.jar" verbose="true"> + <zipfileset src="src/test/enumtest.jar"/> + <rule pattern="foo.**" result="bar.@1"/> + </jarjar> + <java classpath="build/enumtest.jar" classname="bar.EnumTest"/> + <!-- TODO: check output --> + <!-- <delete file="build/enumtest.jar"/> --> + </target> + + <target name="test-manifest" depends="compile"> + <delete> + <fileset dir="build" includes="manifest*.jar"/> + </delete> + <jar jarfile="build/manifest1.jar"> + <manifest> + <attribute name="Built-By" value="Chris"/> + <attribute name="Specification-Title" value="Example"/> + </manifest> + </jar> + <jar jarfile="build/manifest2.jar"> + <manifest> + <attribute name="Built-By" value="CJN"/> + <attribute name="Implementation-Title" value="common"/> + </manifest> + </jar> + <jar jarfile="build/manifest3.jar" filesetmanifest="merge"> + <zipfileset src="build/manifest1.jar"/> + <zipfileset src="build/manifest2.jar"/> + </jar> + <jarjar jarfile="build/manifest4.jar" filesetmanifest="merge"> + <zipfileset src="build/manifest1.jar"/> + <zipfileset src="build/manifest2.jar"/> + </jarjar> + <!-- TODO: check manifest contents here --> + </target> + + <!-- TODO: reference ant javadocs --> + <target name="javadoc" depends="compile" description="Generate the javadoc"> + <mkdir dir="dist/javadoc"/> + <javadoc + sourcepath="${src}/main" + destdir="${dist}/javadoc" + access="${javadoc.access}" + failonerror="true" + packagenames="com.tonicsystems.jarjar.*" + classpathref="path.run" + Author="true" + Version="true" + Use="true" + /> + </target> +</project> diff --git a/lib/apache-ant-1.9.4.jar b/lib/apache-ant-1.9.4.jar Binary files differnew file mode 100644 index 0000000..24641e7 --- /dev/null +++ b/lib/apache-ant-1.9.4.jar diff --git a/lib/asm-4.0.jar b/lib/asm-4.0.jar Binary files differnew file mode 100644 index 0000000..cca0d9c --- /dev/null +++ b/lib/asm-4.0.jar diff --git a/lib/asm-commons-4.0.jar b/lib/asm-commons-4.0.jar Binary files differnew file mode 100644 index 0000000..169400e --- /dev/null +++ b/lib/asm-commons-4.0.jar diff --git a/lib/maven-plugin-api.jar b/lib/maven-plugin-api.jar Binary files differnew file mode 100644 index 0000000..379b2c8 --- /dev/null +++ b/lib/maven-plugin-api.jar diff --git a/manifest.txt b/manifest.txt new file mode 100644 index 0000000..63c68e0 --- /dev/null +++ b/manifest.txt @@ -0,0 +1 @@ +Main-Class: com.tonicsystems.jarjar.Main diff --git a/maven/plugin.xml b/maven/plugin.xml new file mode 100644 index 0000000..9f59300 --- /dev/null +++ b/maven/plugin.xml @@ -0,0 +1,63 @@ +<plugin> + <description></description> + <groupId>com.tonicsystems.jarjar</groupId> + <artifactId>jarjar-plugin</artifactId> + <version>@VERSION@</version> + <goalPrefix>jarjar</goalPrefix> + <isolatedRealm>false</isolatedRealm> + <inheritedByDefault>true</inheritedByDefault> + <mojos> + <mojo> + <goal>jarjar</goal> + <description>Repackage class files into a new namespace.</description> + <requiresDirectInvocation>false</requiresDirectInvocation> + <requiresProject>true</requiresProject> + <requiresReports>false</requiresReports> + <aggregator>false</aggregator> + <requiresOnline>false</requiresOnline> + <inheritedByDefault>true</inheritedByDefault> + <implementation>com.tonicsystems.jarjar.JarJarMojo</implementation> + <language>java</language> + <instantiationStrategy>per-lookup</instantiationStrategy> + <executionStrategy>once-per-session</executionStrategy> + <parameters> + <parameter> + <name>fromJar</name> + <type>java.io.File</type> + <required>true</required> + <editable>true</editable> + <description>Jar file to process</description> + </parameter> + <parameter> + <name>toJar</name> + <type>java.io.File</type> + <required>true</required> + <editable>true</editable> + <description>Output jar file</description> + </parameter> + <parameter> + <name>rules</name> + <type>java.lang.String</type> + <required>false</required> + <editable>true</editable> + <description>External rules file</description> + </parameter> + <parameter> + <name>rulesFile</name> + <type>java.io.File</type> + <required>false</required> + <editable>true</editable> + <description>Rules</description> + </parameter> + <parameter> + <name>verbose</name> + <type>boolean</type> + <required>false</required> + <editable>true</editable> + <description>Verbose</description> + </parameter> + </parameters> + </mojo> + </mojos> + <dependencies/> +</plugin> diff --git a/maven/pom.properties b/maven/pom.properties new file mode 100644 index 0000000..4aa8403 --- /dev/null +++ b/maven/pom.properties @@ -0,0 +1,3 @@ +version=@VERSION@ +groupId=com.tonicsystems.jarjar +artifactId=jarjar-plugin diff --git a/maven/pom.xml b/maven/pom.xml new file mode 100644 index 0000000..9521fc4 --- /dev/null +++ b/maven/pom.xml @@ -0,0 +1,17 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>com.tonicsystems.jarjar</groupId> + <artifactId>jarjar-plugin</artifactId> + <packaging>maven-plugin</packaging> + <version>@VERSION@</version> + <name>Jar Jar Links Maven Plugin</name> + <url>http://tonicsystems.com/products/jarjar/</url> + <dependencies> + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-plugin-api</artifactId> + <version>2.0</version> + </dependency> + </dependencies> +</project> diff --git a/res/com/tonicsystems/jarjar/help.txt b/res/com/tonicsystems/jarjar/help.txt new file mode 100644 index 0000000..8410909 --- /dev/null +++ b/res/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/src/Jarjar.iml b/src/Jarjar.iml new file mode 100644 index 0000000..70f1faf --- /dev/null +++ b/src/Jarjar.iml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="true"> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/main" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="library" name="lib" level="project" /> + <orderEntry type="module-library"> + <library> + <CLASSES> + <root url="jar://$MAVEN_REPOSITORY$/org/apache/ant/ant/1.7.1/ant-1.7.1.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> + diff --git a/src/main/com/tonicsystems/jarjar/AbstractDepHandler.java b/src/main/com/tonicsystems/jarjar/AbstractDepHandler.java new file mode 100644 index 0000000..5a54737 --- /dev/null +++ b/src/main/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.*; + +abstract public 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()); + } + } + + abstract protected 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/src/main/com/tonicsystems/jarjar/DepFind.java b/src/main/com/tonicsystems/jarjar/DepFind.java new file mode 100644 index 0000000..40f68ff --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/DepFind.java @@ -0,0 +1,80 @@ +/** + * 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 java.util.zip.ZipEntry; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; + +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/src/main/com/tonicsystems/jarjar/DepFindVisitor.java b/src/main/com/tonicsystems/jarjar/DepFindVisitor.java new file mode 100644 index 0000000..9d6611e --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/DepFindVisitor.java @@ -0,0 +1,71 @@ +/** + * 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 java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import org.objectweb.asm.*; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.*; + +class DepFindVisitor extends RemappingClassAdapter +{ + 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/src/main/com/tonicsystems/jarjar/DepHandler.java b/src/main/com/tonicsystems/jarjar/DepHandler.java new file mode 100644 index 0000000..33107d4 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/DepHandler.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; + +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/src/main/com/tonicsystems/jarjar/EmptyClassVisitor.java b/src/main/com/tonicsystems/jarjar/EmptyClassVisitor.java new file mode 100644 index 0000000..348e03d --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/EmptyClassVisitor.java @@ -0,0 +1,50 @@ +/* + * 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.ASM4); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, + String signature, String[] exceptions) { + return new MethodVisitor(Opcodes.ASM4) {}; + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return new AnnotationVisitor(Opcodes.ASM4) {}; + } + + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + return new FieldVisitor(Opcodes.ASM4) {}; + } + +} diff --git a/src/main/com/tonicsystems/jarjar/ExcludeProcessor.java b/src/main/com/tonicsystems/jarjar/ExcludeProcessor.java new file mode 100644 index 0000000..191803f --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/ExcludeProcessor.java @@ -0,0 +1,40 @@ +/** + * 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/src/main/com/tonicsystems/jarjar/JarJarMojo.java b/src/main/com/tonicsystems/jarjar/JarJarMojo.java new file mode 100644 index 0000000..9d37b77 --- /dev/null +++ b/src/main/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/src/main/com/tonicsystems/jarjar/JarJarTask.java b/src/main/com/tonicsystems/jarjar/JarJarTask.java new file mode 100644 index 0000000..2c183c9 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/JarJarTask.java @@ -0,0 +1,61 @@ +/** + * 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.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/src/main/com/tonicsystems/jarjar/Keep.java b/src/main/com/tonicsystems/jarjar/Keep.java new file mode 100644 index 0000000..14b719e --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/Keep.java @@ -0,0 +1,21 @@ +/** + * 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/src/main/com/tonicsystems/jarjar/KeepProcessor.java b/src/main/com/tonicsystems/jarjar/KeepProcessor.java new file mode 100644 index 0000000..0176b84 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/KeepProcessor.java @@ -0,0 +1,112 @@ +/** + * 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.Type; +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 RemappingClassAdapter(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/src/main/com/tonicsystems/jarjar/Main.java b/src/main/com/tonicsystems/jarjar/Main.java new file mode 100644 index 0000000..117bdbb --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/Main.java @@ -0,0 +1,97 @@ +/** + * 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/src/main/com/tonicsystems/jarjar/MainProcessor.java b/src/main/com/tonicsystems/jarjar/MainProcessor.java new file mode 100644 index 0000000..de15924 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/MainProcessor.java @@ -0,0 +1,106 @@ +/** + * 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/src/main/com/tonicsystems/jarjar/MainUtil.java b/src/main/com/tonicsystems/jarjar/MainUtil.java new file mode 100644 index 0000000..068ece6 --- /dev/null +++ b/src/main/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/src/main/com/tonicsystems/jarjar/ManifestProcessor.java b/src/main/com/tonicsystems/jarjar/ManifestProcessor.java new file mode 100644 index 0000000..91f1f76 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/ManifestProcessor.java @@ -0,0 +1,38 @@ +/** + * 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/src/main/com/tonicsystems/jarjar/PackageRemapper.java b/src/main/com/tonicsystems/jarjar/PackageRemapper.java new file mode 100644 index 0000000..4d102be --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/PackageRemapper.java @@ -0,0 +1,128 @@ +/** + * 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.*; +import org.objectweb.asm.commons.*; +import java.util.*; +import java.util.regex.Pattern; + +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/src/main/com/tonicsystems/jarjar/PathClass.java b/src/main/com/tonicsystems/jarjar/PathClass.java new file mode 100644 index 0000000..fbbb7ed --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/PathClass.java @@ -0,0 +1,40 @@ +/** + * 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/src/main/com/tonicsystems/jarjar/PatternElement.java b/src/main/com/tonicsystems/jarjar/PatternElement.java new file mode 100644 index 0000000..6ccd9ea --- /dev/null +++ b/src/main/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.*; + +abstract public 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/src/main/com/tonicsystems/jarjar/ResourceProcessor.java b/src/main/com/tonicsystems/jarjar/ResourceProcessor.java new file mode 100644 index 0000000..c07f664 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/ResourceProcessor.java @@ -0,0 +1,37 @@ +/** + * 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.name.endsWith(".class")) + struct.name = pr.mapPath(struct.name); + return true; + } +} + diff --git a/src/main/com/tonicsystems/jarjar/Rule.java b/src/main/com/tonicsystems/jarjar/Rule.java new file mode 100644 index 0000000..2e76ca8 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/Rule.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; + +public class Rule extends PatternElement +{ + private String result; + + public void setResult(String result) { + this.result = result; + } + + public String getResult() { + return result; + } +} diff --git a/src/main/com/tonicsystems/jarjar/RulesFileParser.java b/src/main/com/tonicsystems/jarjar/RulesFileParser.java new file mode 100644 index 0000000..f54f3b9 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/RulesFileParser.java @@ -0,0 +1,81 @@ +/** + * 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 (line.isEmpty()) + 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/src/main/com/tonicsystems/jarjar/StringDumper.java b/src/main/com/tonicsystems/jarjar/StringDumper.java new file mode 100644 index 0000000..5086314 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/StringDumper.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 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/src/main/com/tonicsystems/jarjar/StringReader.java b/src/main/com/tonicsystems/jarjar/StringReader.java new file mode 100644 index 0000000..ba7bb29 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/StringReader.java @@ -0,0 +1,100 @@ +/** + * 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.ASM4); + } + + abstract public 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.ASM4){ + @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.ASM4) { + @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.ASM4){ + @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/src/main/com/tonicsystems/jarjar/TextDepHandler.java b/src/main/com/tonicsystems/jarjar/TextDepHandler.java new file mode 100644 index 0000000..3551395 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/TextDepHandler.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; + +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/src/main/com/tonicsystems/jarjar/Wildcard.java b/src/main/com/tonicsystems/jarjar/Wildcard.java new file mode 100644 index 0000000..723c6f5 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/Wildcard.java @@ -0,0 +1,143 @@ +/** + * 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.regex.Matcher; +import java.util.regex.Pattern; +import java.util.ArrayList; +import java.util.Arrays; + +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/src/main/com/tonicsystems/jarjar/Zap.java b/src/main/com/tonicsystems/jarjar/Zap.java new file mode 100644 index 0000000..ed5c828 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/Zap.java @@ -0,0 +1,21 @@ +/** + * 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/src/main/com/tonicsystems/jarjar/ZapProcessor.java b/src/main/com/tonicsystems/jarjar/ZapProcessor.java new file mode 100644 index 0000000..5b6b680 --- /dev/null +++ b/src/main/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 (name.endsWith(".class")) + 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/src/main/com/tonicsystems/jarjar/util/AntJarProcessor.java b/src/main/com/tonicsystems/jarjar/util/AntJarProcessor.java new file mode 100644 index 0000000..ac30418 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/util/AntJarProcessor.java @@ -0,0 +1,107 @@ +/** + * 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; + +abstract public 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/src/main/com/tonicsystems/jarjar/util/ClassHeaderReader.java b/src/main/com/tonicsystems/jarjar/util/ClassHeaderReader.java new file mode 100644 index 0000000..6e448be --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/util/ClassHeaderReader.java @@ -0,0 +1,186 @@ +/** + * 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/src/main/com/tonicsystems/jarjar/util/ClassPathEntry.java b/src/main/com/tonicsystems/jarjar/util/ClassPathEntry.java new file mode 100644 index 0000000..d8de708 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/util/ClassPathEntry.java @@ -0,0 +1,26 @@ +/** + * 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.InputStream; +import java.io.IOException; + +public interface ClassPathEntry { + String getSource() throws IOException; + String getName(); + InputStream openStream() throws IOException; +}
\ No newline at end of file diff --git a/src/main/com/tonicsystems/jarjar/util/ClassPathIterator.java b/src/main/com/tonicsystems/jarjar/util/ClassPathIterator.java new file mode 100644 index 0000000..f4f54f0 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/util/ClassPathIterator.java @@ -0,0 +1,234 @@ +/** + * 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.util.*; +import java.util.zip.*; +import java.io.*; +import java.util.jar.*; + +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/src/main/com/tonicsystems/jarjar/util/EntryStruct.java b/src/main/com/tonicsystems/jarjar/util/EntryStruct.java new file mode 100644 index 0000000..93f7622 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/util/EntryStruct.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.InputStream; +import java.io.File; + +public class EntryStruct +{ + public byte[] data; + public String name; + public long time; +} diff --git a/src/main/com/tonicsystems/jarjar/util/GetNameClassWriter.java b/src/main/com/tonicsystems/jarjar/util/GetNameClassWriter.java new file mode 100644 index 0000000..bbb8590 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/util/GetNameClassWriter.java @@ -0,0 +1,43 @@ +/** + * 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.ASM4,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/src/main/com/tonicsystems/jarjar/util/IoUtil.java b/src/main/com/tonicsystems/jarjar/util/IoUtil.java new file mode 100644 index 0000000..aef7ade --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/util/IoUtil.java @@ -0,0 +1,131 @@ +/** + * 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/src/main/com/tonicsystems/jarjar/util/JarProcessor.java b/src/main/com/tonicsystems/jarjar/util/JarProcessor.java new file mode 100644 index 0000000..1560696 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/util/JarProcessor.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 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/src/main/com/tonicsystems/jarjar/util/JarProcessorChain.java b/src/main/com/tonicsystems/jarjar/util/JarProcessorChain.java new file mode 100644 index 0000000..06ac85d --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/util/JarProcessorChain.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 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/src/main/com/tonicsystems/jarjar/util/JarTransformer.java b/src/main/com/tonicsystems/jarjar/util/JarTransformer.java new file mode 100644 index 0000000..95f1216 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/util/JarTransformer.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.util; + +import java.io.*; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; + +abstract public class JarTransformer implements JarProcessor +{ + public boolean process(EntryStruct struct) throws IOException { + if (struct.name.endsWith(".class")) { + 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; + } + + abstract protected ClassVisitor transform(ClassVisitor v); + + private static String pathFromName(String className) { + return className.replace('.', '/') + ".class"; + } +} diff --git a/src/main/com/tonicsystems/jarjar/util/JarTransformerChain.java b/src/main/com/tonicsystems/jarjar/util/JarTransformerChain.java new file mode 100644 index 0000000..be16d9b --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/util/JarTransformerChain.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.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/src/main/com/tonicsystems/jarjar/util/RemappingClassTransformer.java b/src/main/com/tonicsystems/jarjar/util/RemappingClassTransformer.java new file mode 100644 index 0000000..fd1b9d7 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/util/RemappingClassTransformer.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 org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.commons.Remapper; +import org.objectweb.asm.commons.RemappingClassAdapter; + +import com.tonicsystems.jarjar.EmptyClassVisitor; + +public class RemappingClassTransformer extends RemappingClassAdapter +{ + public RemappingClassTransformer(Remapper pr) { + super(new EmptyClassVisitor(), pr); + } + + public void setTarget(ClassVisitor target) { + cv = target; + } +} diff --git a/src/main/com/tonicsystems/jarjar/util/RuntimeIOException.java b/src/main/com/tonicsystems/jarjar/util/RuntimeIOException.java new file mode 100644 index 0000000..9b5d4a8 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/util/RuntimeIOException.java @@ -0,0 +1,28 @@ +/** + * 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/src/main/com/tonicsystems/jarjar/util/StandaloneJarProcessor.java b/src/main/com/tonicsystems/jarjar/util/StandaloneJarProcessor.java new file mode 100644 index 0000000..70b169b --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/util/StandaloneJarProcessor.java @@ -0,0 +1,71 @@ +/** + * 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.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.Enumeration; +import java.io.*; +import java.util.*; + +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(); + + } +} diff --git a/src/test/Generics.class b/src/test/Generics.class Binary files differnew file mode 100644 index 0000000..827519a --- /dev/null +++ b/src/test/Generics.class diff --git a/src/test/com/tonicsystems/jarjar/GenericsTest.java b/src/test/com/tonicsystems/jarjar/GenericsTest.java new file mode 100644 index 0000000..1996a24 --- /dev/null +++ b/src/test/com/tonicsystems/jarjar/GenericsTest.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 com.tonicsystems.jarjar.util.*; +import junit.framework.*; +import java.util.*; +import org.objectweb.asm.ClassReader; + +public class GenericsTest +extends TestCase +{ + public void testTransform() throws Exception { + Rule rule = new Rule(); + rule.setPattern("java.lang.String"); + rule.setResult("com.tonicsystems.String"); + RemappingClassTransformer t = new RemappingClassTransformer(new PackageRemapper(Arrays.asList(rule), false)); + t.setTarget(new EmptyClassVisitor()); + ClassReader reader = new ClassReader(getClass().getResourceAsStream("/Generics.class")); + reader.accept(t, 0); + } + + public GenericsTest(String name) { + super(name); + } + + public static Test suite() { + return new TestSuite(GenericsTest.class); + } +} diff --git a/src/test/com/tonicsystems/jarjar/PackageRemapperTest.java b/src/test/com/tonicsystems/jarjar/PackageRemapperTest.java new file mode 100644 index 0000000..aea9d46 --- /dev/null +++ b/src/test/com/tonicsystems/jarjar/PackageRemapperTest.java @@ -0,0 +1,59 @@ +/** + * 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 junit.framework.*; + +import java.util.Collections; + +public class PackageRemapperTest +extends TestCase +{ + protected PackageRemapper remapper; + + protected void setUp() { + Rule rule = new Rule(); + rule.setPattern("org.**"); + rule.setResult("foo.@1"); + remapper = new PackageRemapper(Collections.singletonList(rule), false); + } + + public void testMapValue() { + assertUnchangedValue("[^\\s;/@&=,.?:+$]"); + assertUnchangedValue("[Ljava/lang/Object;"); + assertUnchangedValue("[Lorg/example/Object;"); + assertUnchangedValue("[Ljava.lang.Object;"); + assertUnchangedValue("[Lorg.example/Object;"); + assertUnchangedValue("[L;"); + assertUnchangedValue("[Lorg.example.Object;;"); + assertUnchangedValue("[Lorg.example.Obj ct;"); + assertUnchangedValue("org.example/Object"); + + assertEquals("[Lfoo.example.Object;", remapper.mapValue("[Lorg.example.Object;")); + assertEquals("foo.example.Object", remapper.mapValue("org.example.Object")); + assertEquals("foo/example/Object", remapper.mapValue("org/example/Object")); + assertEquals("foo/example.Object", remapper.mapValue("org/example.Object")); // path match + + assertEquals("foo.example.package-info", remapper.mapValue("org.example.package-info")); + assertEquals("foo/example/package-info", remapper.mapValue("org/example/package-info")); + assertEquals("foo/example.package-info", remapper.mapValue("org/example.package-info")); + } + + private void assertUnchangedValue(String value) { + assertEquals(value, remapper.mapValue(value)); + } +} diff --git a/src/test/com/tonicsystems/jarjar/RulesFileParserTest.java b/src/test/com/tonicsystems/jarjar/RulesFileParserTest.java new file mode 100644 index 0000000..4a4c371 --- /dev/null +++ b/src/test/com/tonicsystems/jarjar/RulesFileParserTest.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 junit.framework.*; +import java.io.*; + +public class RulesFileParserTest +extends TestCase +{ + public void testSimple() throws Exception { + // TODO + } + + public RulesFileParserTest(String name) { + super(name); + } + + public static Test suite() { + return new TestSuite(RulesFileParserTest.class); + } +} diff --git a/src/test/com/tonicsystems/jarjar/WildcardTest.java b/src/test/com/tonicsystems/jarjar/WildcardTest.java new file mode 100644 index 0000000..7cdcdf7 --- /dev/null +++ b/src/test/com/tonicsystems/jarjar/WildcardTest.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; + +import junit.framework.*; + +public class WildcardTest +extends TestCase +{ + public void testWildcards() { + wildcard("net/sf/cglib/**", "foo/@1", "net/sf/cglib/proxy/Mixin$Generator", + "foo/proxy/Mixin$Generator"); + wildcard("net/sf/cglib/**", "foo/@1", "net/sf/cglib/Bar", "foo/Bar"); + wildcard("net/sf/cglib/**", "foo/@1", "net/sf/cglib/Bar/Baz", "foo/Bar/Baz"); + wildcard("net/sf/cglib/**", "foo/@1", "net/sf/cglib/", "foo/"); + wildcard("net/sf/cglib/**", "foo/@1", "net/sf/cglib/!", null); + wildcard("net/sf/cglib/*", "foo/@1", "net/sf/cglib/Bar", "foo/Bar"); + wildcard("net/sf/cglib/*/*", "foo/@2/@1", "net/sf/cglib/Bar/Baz", "foo/Baz/Bar"); + } + + private void wildcard(String pattern, String result, String value, String expect) { + Wildcard wc = new Wildcard(pattern, result); + // System.err.println(wc); + assertEquals(expect, wc.replace(value)); + } + + public WildcardTest(String name) { + super(name); + } + + public static Test suite() { + return new TestSuite(WildcardTest.class); + } +} diff --git a/src/test/com/tonicsystems/jarjar/example/Example.java b/src/test/com/tonicsystems/jarjar/example/Example.java new file mode 100644 index 0000000..2129962 --- /dev/null +++ b/src/test/com/tonicsystems/jarjar/example/Example.java @@ -0,0 +1,5 @@ +package com.tonicsystems.jarjar.example; + +public class Example +{ +} diff --git a/src/test/enumtest.jar b/src/test/enumtest.jar Binary files differnew file mode 100644 index 0000000..df34d16 --- /dev/null +++ b/src/test/enumtest.jar |