UpToDateChecker.java revision b9cc48a43ed984587c939d02fba5316bf5c0df6e
1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2013 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 java.io.File;
24
25/**
26 * This class checks whether the output is up to date.
27 *
28 * @author Eric Lafortune
29 */
30public class UpToDateChecker
31{
32    private final Configuration configuration;
33
34
35    /**
36     * Creates a new UpToDateChecker with the given configuration.
37     */
38    public UpToDateChecker(Configuration configuration)
39    {
40        this.configuration = configuration;
41    }
42
43
44    /**
45     * Returns whether the output is up to date, based on the modification times
46     * of the input jars, output jars, and library jars (or directories).
47     */
48    public boolean check()
49    {
50        try
51        {
52            ModificationTimeChecker checker = new ModificationTimeChecker();
53
54            checker.updateInputModificationTime(configuration.lastModified);
55
56            ClassPath programJars = configuration.programJars;
57            ClassPath libraryJars = configuration.libraryJars;
58
59            // Check the dates of the program jars, if any.
60            if (programJars != null)
61            {
62                for (int index = 0; index < programJars.size(); index++)
63                {
64                    // Update the input and output modification times.
65                    ClassPathEntry classPathEntry = programJars.get(index);
66
67                    checker.updateModificationTime(classPathEntry.getFile(),
68                                                   classPathEntry.isOutput());
69                }
70            }
71
72            // Check the dates of the library jars, if any.
73            if (libraryJars != null)
74            {
75                for (int index = 0; index < libraryJars.size(); index++)
76                {
77                    // Update the input modification time.
78                    ClassPathEntry classPathEntry = libraryJars.get(index);
79
80                    checker.updateModificationTime(classPathEntry.getFile(),
81                                                   false);
82                }
83            }
84
85            // Check the dates of the auxiliary input files.
86            checker.updateInputModificationTime(configuration.applyMapping);
87            checker.updateInputModificationTime(configuration.obfuscationDictionary);
88            checker.updateInputModificationTime(configuration.classObfuscationDictionary);
89            checker.updateInputModificationTime(configuration.packageObfuscationDictionary);
90
91            // Check the dates of the auxiliary output files.
92            checker.updateOutputModificationTime(configuration.printSeeds);
93            checker.updateOutputModificationTime(configuration.printUsage);
94            checker.updateOutputModificationTime(configuration.printMapping);
95            checker.updateOutputModificationTime(configuration.printConfiguration);
96            checker.updateOutputModificationTime(configuration.dump);
97        }
98        catch (IllegalStateException e)
99        {
100            // The output is outdated.
101            return false;
102        }
103
104        System.out.println("The output seems up to date");
105
106        return true;
107    }
108
109
110    /**
111     * This class maintains the modification times of input and output.
112     * The methods throw an IllegalStateException if the output appears
113     * outdated.
114     */
115    private static class ModificationTimeChecker {
116
117        private long inputModificationTime  = Long.MIN_VALUE;
118        private long outputModificationTime = Long.MAX_VALUE;
119
120
121        /**
122         * Updates the input modification time based on the given file or
123         * directory (recursively).
124         */
125        public void updateInputModificationTime(File file)
126        {
127            if (file != null)
128            {
129                updateModificationTime(file, false);
130            }
131        }
132
133
134        /**
135         * Updates the input modification time based on the given file or
136         * directory (recursively).
137         */
138        public void updateOutputModificationTime(File file)
139        {
140            if (file != null && file.getName().length() > 0)
141            {
142                updateModificationTime(file, true);
143            }
144        }
145
146
147        /**
148         * Updates the specified modification time based on the given file or
149         * directory (recursively).
150         */
151        public void updateModificationTime(File file, boolean isOutput)
152        {
153            // Is it a directory?
154            if (file.isDirectory())
155            {
156                // Ignore the directory's modification time; just recurse on
157                // its files.
158                File[] files = file.listFiles();
159
160                // Still, an empty output directory is probably a sign that it
161                // is not up to date.
162                if (files.length == 0 && isOutput)
163                {
164                    updateOutputModificationTime(Long.MIN_VALUE);
165                }
166
167                for (int index = 0; index < files.length; index++)
168                {
169                    updateModificationTime(files[index], isOutput);
170                }
171            }
172            else
173            {
174                // Update with the file's modification time.
175                updateModificationTime(file.lastModified(), isOutput);
176            }
177        }
178
179
180        /**
181         * Updates the specified modification time.
182         */
183        public void updateModificationTime(long time, boolean isOutput)
184        {
185            if (isOutput)
186            {
187                updateOutputModificationTime(time);
188            }
189            else
190            {
191                updateInputModificationTime(time);
192            }
193        }
194
195
196        /**
197         * Updates the input modification time.
198         */
199        public void updateInputModificationTime(long time)
200        {
201            if (inputModificationTime < time)
202            {
203                inputModificationTime = time;
204
205                checkModificationTimes();
206            }
207        }
208
209
210        /**
211         * Updates the output modification time.
212         */
213        public void updateOutputModificationTime(long time)
214        {
215            if (outputModificationTime > time)
216            {
217                outputModificationTime = time;
218
219                checkModificationTimes();
220            }
221        }
222
223
224        private void checkModificationTimes()
225        {
226            if (inputModificationTime > outputModificationTime)
227            {
228                throw new IllegalStateException("The output is outdated");
229            }
230        }
231    }
232}
233