1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu)
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21package proguard;
22
23import proguard.io.*;
24import proguard.util.*;
25
26import java.util.List;
27
28/**
29 * This class can create DataEntryWriter instances based on class paths. The
30 * writers will wrap the output in the proper apks, jars, wars, ears, and zips.
31 *
32 * @author Eric Lafortune
33 */
34public class DataEntryWriterFactory
35{
36    /**
37     * Creates a DataEntryWriter that can write to the given class path entries.
38     *
39     * @param classPath the output class path.
40     * @param fromIndex the start index in the class path.
41     * @param toIndex   the end index in the class path.
42     * @return a DataEntryWriter for writing to the given class path entries.
43     */
44    public static DataEntryWriter createDataEntryWriter(ClassPath classPath,
45                                                        int       fromIndex,
46                                                        int       toIndex)
47    {
48        DataEntryWriter writer = null;
49
50        // Create a chain of writers, one for each class path entry.
51        for (int index = toIndex - 1; index >= fromIndex; index--)
52        {
53            ClassPathEntry entry = classPath.get(index);
54            writer = createClassPathEntryWriter(entry, writer);
55        }
56
57        return writer;
58    }
59
60
61    /**
62     * Creates a DataEntryWriter that can write to the given class path entry,
63     * or delegate to another DataEntryWriter if its filters don't match.
64     */
65    private static DataEntryWriter createClassPathEntryWriter(ClassPathEntry  classPathEntry,
66                                                              DataEntryWriter alternativeWriter)
67    {
68        boolean isApk = classPathEntry.isApk();
69        boolean isJar = classPathEntry.isJar();
70        boolean isAar = classPathEntry.isAar();
71        boolean isWar = classPathEntry.isWar();
72        boolean isEar = classPathEntry.isEar();
73        boolean isZip = classPathEntry.isZip();
74
75        List filter    = classPathEntry.getFilter();
76        List apkFilter = classPathEntry.getApkFilter();
77        List jarFilter = classPathEntry.getJarFilter();
78        List aarFilter = classPathEntry.getAarFilter();
79        List warFilter = classPathEntry.getWarFilter();
80        List earFilter = classPathEntry.getEarFilter();
81        List zipFilter = classPathEntry.getZipFilter();
82
83        System.out.println("Preparing output " +
84                           (isApk ? "apk" :
85                            isJar ? "jar" :
86                            isAar ? "aar" :
87                            isWar ? "war" :
88                            isEar ? "ear" :
89                            isZip ? "zip" :
90                                    "directory") +
91                           " [" + classPathEntry.getName() + "]" +
92                           (filter    != null ||
93                            apkFilter != null ||
94                            jarFilter != null ||
95                            aarFilter != null ||
96                            warFilter != null ||
97                            earFilter != null ||
98                            zipFilter != null ? " (filtered)" : ""));
99
100        DataEntryWriter writer = new DirectoryWriter(classPathEntry.getFile(),
101                                                     isApk ||
102                                                     isJar ||
103                                                     isAar ||
104                                                     isWar ||
105                                                     isEar ||
106                                                     isZip);
107
108        // Set up the filtered jar writers.
109        writer = wrapInJarWriter(writer, isZip, zipFilter, ".zip", isApk || isJar || isAar || isWar || isEar);
110        writer = wrapInJarWriter(writer, isEar, earFilter, ".ear", isApk || isJar || isAar || isWar);
111        writer = wrapInJarWriter(writer, isWar, warFilter, ".war", isApk || isJar || isAar);
112        writer = wrapInJarWriter(writer, isAar, aarFilter, ".aar", isApk || isJar);
113        writer = wrapInJarWriter(writer, isJar, jarFilter, ".jar", isApk);
114        writer = wrapInJarWriter(writer, isApk, apkFilter, ".apk", false);
115
116        // Add a filter, if specified.
117        writer = filter != null?
118            new FilteredDataEntryWriter(
119            new DataEntryNameFilter(
120            new ListParser(new FileNameParser()).parse(filter)),
121                writer) :
122            writer;
123
124        // Let the writer cascade, if specified.
125        return alternativeWriter != null ?
126            new CascadingDataEntryWriter(writer, alternativeWriter) :
127            writer;
128    }
129
130
131    /**
132     * Wraps the given DataEntryWriter in a JarWriter, filtering if necessary.
133     */
134    private static DataEntryWriter wrapInJarWriter(DataEntryWriter writer,
135                                                   boolean         isJar,
136                                                   List            jarFilter,
137                                                   String          jarExtension,
138                                                   boolean         dontWrap)
139    {
140        // Zip up jars, if necessary.
141        DataEntryWriter jarWriter = dontWrap ?
142            (DataEntryWriter)new ParentDataEntryWriter(writer) :
143            (DataEntryWriter)new JarWriter(writer);
144
145        // Add a filter, if specified.
146        DataEntryWriter filteredJarWriter = jarFilter != null?
147            new FilteredDataEntryWriter(
148            new DataEntryParentFilter(
149            new DataEntryNameFilter(
150            new ListParser(new FileNameParser()).parse(jarFilter))),
151                 jarWriter) :
152            jarWriter;
153
154        // Only zip up jars, unless the output is a jar file itself.
155        return new FilteredDataEntryWriter(
156               new DataEntryParentFilter(
157               new DataEntryNameFilter(
158               new ExtensionMatcher(jarExtension))),
159                   filteredJarWriter,
160                   isJar ? jarWriter : writer);
161    }
162}
163