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.util.ListUtil;
24
25import java.io.*;
26import java.util.List;
27
28
29/**
30 * This class represents an entry from a class path: an apk, a jar, an aar, a
31 * war, a zip, an ear, or a directory, with a name and a flag to indicates
32 * whether the entry is an input entry or an output entry. Optional filters can
33 * be specified for the names of the contained resource/classes, apks, jars,
34 * aars, wars, ears, and zips.
35 *
36 * @author Eric Lafortune
37 */
38public class ClassPathEntry
39{
40    private File    file;
41    private boolean output;
42    private List    filter;
43    private List    apkFilter;
44    private List    jarFilter;
45    private List    aarFilter;
46    private List    warFilter;
47    private List    earFilter;
48    private List    zipFilter;
49
50    private String cachedName;
51
52
53    /**
54     * Creates a new ClassPathEntry with the given file and output flag.
55     */
56    public ClassPathEntry(File file, boolean isOutput)
57    {
58        this.file   = file;
59        this.output = isOutput;
60    }
61
62
63    /**
64     * Returns the path name of the entry.
65     */
66    public String getName()
67    {
68        if (cachedName == null)
69        {
70            cachedName = getUncachedName();
71        }
72
73        return cachedName;
74    }
75
76
77    /**
78     * Returns the uncached path name of the entry.
79     */
80    private String getUncachedName()
81    {
82        try
83        {
84            return file.getCanonicalPath();
85        }
86        catch (IOException ex)
87        {
88            return file.getPath();
89        }
90    }
91
92
93    /**
94     * Returns the file.
95     */
96    public File getFile()
97    {
98        return file;
99    }
100
101
102    /**
103     * Sets the file.
104     */
105    public void setFile(File file)
106    {
107        this.file       = file;
108        this.cachedName = null;
109    }
110
111
112    /**
113     * Returns whether this data entry is an output entry.
114     */
115    public boolean isOutput()
116    {
117        return output;
118    }
119
120
121    /**
122     * Specifies whether this data entry is an output entry.
123     */
124    public void setOutput(boolean output)
125    {
126        this.output = output;
127    }
128
129
130    /**
131     * Returns whether this data entry is a dex file.
132     */
133    public boolean isDex()
134    {
135        return hasExtension(".dex");
136    }
137
138
139    /**
140     * Returns whether this data entry is an apk file.
141     */
142    public boolean isApk()
143    {
144        return hasExtension(".apk") ||
145               hasExtension(".ap_");
146    }
147
148
149    /**
150     * Returns whether this data entry is a jar file.
151     */
152    public boolean isJar()
153    {
154        return hasExtension(".jar");
155    }
156
157
158    /**
159     * Returns whether this data entry is an aar file.
160     */
161    public boolean isAar()
162    {
163        return hasExtension(".aar");
164    }
165
166
167    /**
168     * Returns whether this data entry is a war file.
169     */
170    public boolean isWar()
171    {
172        return hasExtension(".war");
173    }
174
175
176    /**
177     * Returns whether this data entry is a ear file.
178     */
179    public boolean isEar()
180    {
181        return hasExtension(".ear");
182    }
183
184
185    /**
186     * Returns whether this data entry is a zip file.
187     */
188    public boolean isZip()
189    {
190        return hasExtension(".zip");
191    }
192
193
194    /**
195     * Returns whether this data entry has the given extension.
196     */
197    private boolean hasExtension(String extension)
198    {
199        return endsWithIgnoreCase(file.getPath(), extension);
200    }
201
202
203    /**
204     * Returns whether the given string ends with the given suffix, ignoring
205     * its case.
206     */
207    private static boolean endsWithIgnoreCase(String string, String suffix)
208    {
209        int stringLength = string.length();
210        int suffixLength = suffix.length();
211
212        return string.regionMatches(true, stringLength -
213                                          suffixLength, suffix, 0, suffixLength);
214    }
215
216
217    /**
218     * Returns whether this data entry has any kind of filter.
219     */
220    public boolean isFiltered()
221    {
222        return filter    != null ||
223               apkFilter != null ||
224               jarFilter != null ||
225               aarFilter != null ||
226               warFilter != null ||
227               earFilter != null ||
228               zipFilter != null;
229    }
230
231
232    /**
233     * Returns the name filter that is applied to bottom-level files in this entry.
234     */
235    public List getFilter()
236    {
237        return filter;
238    }
239
240    /**
241     * Sets the name filter that is applied to bottom-level files in this entry.
242     */
243    public void setFilter(List filter)
244    {
245        this.filter = filter == null || filter.size() == 0 ? null : filter;
246    }
247
248
249    /**
250     * Returns the name filter that is applied to apk files in this entry, if any.
251     */
252    public List getApkFilter()
253    {
254        return apkFilter;
255    }
256
257    /**
258     * Sets the name filter that is applied to apk files in this entry, if any.
259     */
260    public void setApkFilter(List filter)
261    {
262        this.apkFilter = filter == null || filter.size() == 0 ? null : filter;
263    }
264
265
266    /**
267     * Returns the name filter that is applied to jar files in this entry, if any.
268     */
269    public List getJarFilter()
270    {
271        return jarFilter;
272    }
273
274    /**
275     * Sets the name filter that is applied to jar files in this entry, if any.
276     */
277    public void setJarFilter(List filter)
278    {
279        this.jarFilter = filter == null || filter.size() == 0 ? null : filter;
280    }
281
282
283    /**
284     * Returns the name filter that is applied to aar files in this entry, if any.
285     */
286    public List getAarFilter()
287    {
288        return aarFilter;
289    }
290
291    /**
292     * Sets the name filter that is applied to aar files in this entry, if any.
293     */
294    public void setAarFilter(List filter)
295    {
296        this.aarFilter = filter == null || filter.size() == 0 ? null : filter;
297    }
298
299
300    /**
301     * Returns the name filter that is applied to war files in this entry, if any.
302     */
303    public List getWarFilter()
304    {
305        return warFilter;
306    }
307
308    /**
309     * Sets the name filter that is applied to war files in this entry, if any.
310     */
311    public void setWarFilter(List filter)
312    {
313        this.warFilter = filter == null || filter.size() == 0 ? null : filter;
314    }
315
316
317    /**
318     * Returns the name filter that is applied to ear files in this entry, if any.
319     */
320    public List getEarFilter()
321    {
322        return earFilter;
323    }
324
325    /**
326     * Sets the name filter that is applied to ear files in this entry, if any.
327     */
328    public void setEarFilter(List filter)
329    {
330        this.earFilter = filter == null || filter.size() == 0 ? null : filter;
331    }
332
333
334    /**
335     * Returns the name filter that is applied to zip files in this entry, if any.
336     */
337    public List getZipFilter()
338    {
339        return zipFilter;
340    }
341
342    /**
343     * Sets the name filter that is applied to zip files in this entry, if any.
344     */
345    public void setZipFilter(List filter)
346    {
347        this.zipFilter = filter == null || filter.size() == 0 ? null : filter;
348    }
349
350
351    // Implementations for Object.
352
353    public String toString()
354    {
355        String string = getName();
356
357        if (filter    != null ||
358            jarFilter != null ||
359            aarFilter != null ||
360            warFilter != null ||
361            earFilter != null ||
362            zipFilter != null)
363        {
364            string +=
365                ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD +
366                (aarFilter != null ? ListUtil.commaSeparatedString(aarFilter, true) : "")  +
367                ConfigurationConstants.SEPARATOR_KEYWORD +
368                (apkFilter != null ? ListUtil.commaSeparatedString(apkFilter, true) : "")  +
369                ConfigurationConstants.SEPARATOR_KEYWORD +
370                (zipFilter != null ? ListUtil.commaSeparatedString(zipFilter, true) : "")  +
371                ConfigurationConstants.SEPARATOR_KEYWORD +
372                (earFilter != null ? ListUtil.commaSeparatedString(earFilter, true) : "")  +
373                ConfigurationConstants.SEPARATOR_KEYWORD +
374                (warFilter != null ? ListUtil.commaSeparatedString(warFilter, true) : "")  +
375                ConfigurationConstants.SEPARATOR_KEYWORD +
376                (jarFilter != null ? ListUtil.commaSeparatedString(jarFilter, true) : "")  +
377                ConfigurationConstants.SEPARATOR_KEYWORD +
378                (filter    != null ? ListUtil.commaSeparatedString(filter, true)    : "")  +
379                ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD;
380        }
381
382        return string;
383    }
384}
385