DependencyGraph.java revision 2c8cced2d84e8e93214a976be7afd6f5913fc74f
1065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin/*
2065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin * Copyright (C) 2011 The Android Open Source Project
3065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin *
4065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin * Licensed under the Apache License, Version 2.0 (the "License");
5065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin * you may not use this file except in compliance with the License.
6065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin * You may obtain a copy of the License at
7065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin *
8065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin *      http://www.apache.org/licenses/LICENSE-2.0
9065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin *
10065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin * Unless required by applicable law or agreed to in writing, software
11065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin * distributed under the License is distributed on an "AS IS" BASIS,
12065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin * See the License for the specific language governing permissions and
14065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin * limitations under the License.
15065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin */
16065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin
17065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskinpackage com.android.ant;
18065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin
19891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohetimport org.apache.tools.ant.BuildException;
20891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet
21065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskinimport java.io.BufferedReader;
22065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskinimport java.io.File;
23065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskinimport java.io.FileInputStream;
24065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskinimport java.io.IOException;
25065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskinimport java.io.InputStreamReader;
26065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskinimport java.util.Collections;
27891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohetimport java.util.HashSet;
2850b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohetimport java.util.List;
29065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskinimport java.util.Set;
30065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin
31065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin/**
32065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin *  This class takes care of dependency tracking for all targets and prerequisites listed in
33065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin *  a single dependency file. A dependency graph always has a dependency file associated with it
34065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin *  for the duration of its lifetime
35065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin */
36065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskinpublic class DependencyGraph {
37065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin
38891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet    private static enum DependencyStatus {
39891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        NONE, NEW_FILE, UPDATED_FILE, MISSING_FILE, ERROR;
40891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet    }
41891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet
42065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin    // Files that we know about from the dependency file
43891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet    private Set<File> mTargets = Collections.emptySet();
44891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet    private Set<File> mPrereqs = mTargets;
45891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet    private File mFirstPrereq = null;
4650b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet    private boolean mMissingDepFile = false;
4750b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet    private long mDepFileLastModified;
480fd7983848915b9424501afee8d8f0ea6f08f858Xavier Ducrohet    private final List<InputPath> mNewInputs;
490fd7983848915b9424501afee8d8f0ea6f08f858Xavier Ducrohet
500fd7983848915b9424501afee8d8f0ea6f08f858Xavier Ducrohet    public DependencyGraph(String dependencyFilePath, List<InputPath> newInputPaths) {
51891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        mNewInputs = newInputPaths;
52065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        parseDependencyFile(dependencyFilePath);
53065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin    }
54065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin
55065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin    /**
56065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin     * Check all the dependencies to see if anything has changed.
570fd7983848915b9424501afee8d8f0ea6f08f858Xavier Ducrohet     *
5850b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet     * @param printStatus will print to {@link System#out} the dependencies status.
59065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin     * @return true if new prerequisites have appeared, target files are missing or if
60065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin     *         prerequisite files have been modified since the last target generation.
61065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin     */
620fd7983848915b9424501afee8d8f0ea6f08f858Xavier Ducrohet    public boolean dependenciesHaveChanged(boolean printStatus) {
63891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        // If no dependency file has been set up, then we'll just return true
64891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        // if we have a dependency file, we'll check to see what's been changed
65891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        if (mMissingDepFile) {
66891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet            System.out.println("No Dependency File Found");
67891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet            return true;
68891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        }
69891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet
70891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        // check for missing output first
71891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        if (missingTargetFile()) {
72891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet            if (printStatus) {
7350b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet                System.out.println("Found Deleted Target File");
7450b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet            }
75891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet            return true;
76065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        }
7750b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet
780fd7983848915b9424501afee8d8f0ea6f08f858Xavier Ducrohet        // get the time stamp of the oldest target.
79891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        long oldestTarget = getOutputLastModified();
80891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet
81891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        // first look through the input folders and look for new files or modified files.
820fd7983848915b9424501afee8d8f0ea6f08f858Xavier Ducrohet        DependencyStatus status = checkInputs(oldestTarget);
83891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet
84891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        // this can't find missing files. This is done later.
85891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        switch (status) {
86891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet            case ERROR:
87891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                throw new BuildException();
88891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet            case NEW_FILE:
89891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                if (printStatus) {
90891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                    System.out.println("Found new input file");
91891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                }
92891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                return true;
93891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet            case UPDATED_FILE:
94891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                if (printStatus) {
95891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                    System.out.println("Found modified input file");
96891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                }
97891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                return true;
98891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        }
99891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet
100891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        // now do a full check on the remaining files.
1010fd7983848915b9424501afee8d8f0ea6f08f858Xavier Ducrohet        status = checkPrereqFiles(oldestTarget);
102891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        // this can't find new input files. This is done above.
103891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        switch (status) {
104891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet            case ERROR:
105891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                throw new BuildException();
106891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet            case MISSING_FILE:
107891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                if (printStatus) {
108891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                    System.out.println("Found deleted input file");
109891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                }
110891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                return true;
111891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet            case UPDATED_FILE:
112891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                if (printStatus) {
113891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                    System.out.println("Found modified input file");
114891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                }
115891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                return true;
116891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        }
117891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet
118891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        return false;
11950b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet    }
12050b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet
121891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet    public Set<File> getTargets() {
122891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        return Collections.unmodifiableSet(mTargets);
12350b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet    }
12450b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet
125891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet    public File getFirstPrereq() {
126891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        return mFirstPrereq;
127065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin    }
128065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin
129065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin    /**
130065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin     * Parses the given dependency file and stores the file paths
131065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin     *
132065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin     * @param dependencyFilePath the dependency file
133065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin     */
134065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin    private void parseDependencyFile(String dependencyFilePath) {
13550b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet        // first check if the dependency file is here.
13650b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet        File depFile = new File(dependencyFilePath);
13750b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet        if (depFile.isFile() == false) {
13850b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet            mMissingDepFile = true;
13950b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet            return;
14050b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet        }
14150b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet
14250b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet        // get the modification time of the dep file as we may need it later
14350b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet        mDepFileLastModified = depFile.lastModified();
14450b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet
145065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        // Read in our dependency file
146065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        String content = readFile(dependencyFilePath);
147065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        if (content == null) {
148065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin            System.err.println("ERROR: Couldn't read " + dependencyFilePath);
149065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin            return;
150065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        }
151065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin
152065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        // The format is something like:
153065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        // output1 output2 [...]: dep1 dep2 [...]
154065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        // expect it's likely split on several lines. So let's move it back on a single line
155065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        // first
156065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        String[] lines = content.toString().split("\n");
157065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        StringBuilder sb = new StringBuilder(content.length());
158065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        for (String line : lines) {
159065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin            line = line.trim();
160065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin            if (line.endsWith("\\")) {
161065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin                line = line.substring(0, line.length() - 1);
162065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin            }
163065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin            sb.append(line);
164065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        }
165065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin
166065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        // split the left and right part
167065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        String[] files = sb.toString().split(":");
168065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin
169065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        // get the target files:
170065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        String[] targets = files[0].trim().split(" ");
171065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin
172065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        String[] prereqs = {};
173065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        // Check to make sure our dependency file is okay
174065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        if (files.length < 1) {
175065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin            System.err.println(
176065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin                    "Warning! Dependency file does not list any prerequisites after ':' ");
177065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        } else {
178065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin            // and the prerequisite files:
179065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin            prereqs = files[1].trim().split(" ");
180065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        }
181065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin
182891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        mTargets = new HashSet<File>(targets.length);
183065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        for (String path : targets) {
18450b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet            if (path.length() > 0) {
18550b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet                mTargets.add(new File(path));
18650b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet            }
187065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        }
188891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet
189891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        mPrereqs = new HashSet<File>(prereqs.length);
190065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        for (String path : prereqs) {
19150b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet            if (path.length() > 0) {
192891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                File f = new File(path);
193891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                if (mFirstPrereq == null) {
194891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                    mFirstPrereq = f;
195891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                }
196891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                mPrereqs.add(f);
19750b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet            }
198065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        }
199065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin    }
200065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin
201065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin    /**
202891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     * Check all the input files and folders to see if there have been new
203891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     * files added to them or if any of the existing files have been modified.
204891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     *
205891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     * This looks at the input paths, not at the list of known prereq. Therefore this
206891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     * will not find missing files. It will however remove processed files from the
207891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     * prereq file list so that we can process those in a 2nd step.
208891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     *
209891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     * This should be followed by a call to {@link #checkPrereqFiles(long)} which
210891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     * will process the remaining files in the prereq list.
211891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     *
212a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet     * If a change is found, this will return immediately with either
213891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     * {@link DependencyStatus#NEW_FILE} or {@link DependencyStatus#UPDATED_FILE}.
214891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     *
215891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     * @param oldestTarget the timestamp of the oldest output file to compare against.
216891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     *
217891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     * @return the status of the file in the watched folders.
218891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     *
219065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin     */
2200fd7983848915b9424501afee8d8f0ea6f08f858Xavier Ducrohet    private DependencyStatus checkInputs(long oldestTarget) {
221891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        if (mNewInputs != null) {
2220fd7983848915b9424501afee8d8f0ea6f08f858Xavier Ducrohet            for (InputPath input : mNewInputs) {
223a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                File file = input.getFile();
224a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                if (file.isDirectory()) {
225a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                    DependencyStatus status = checkInputFolder(file, input, oldestTarget);
226891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                    if (status != DependencyStatus.NONE) {
227891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                        return status;
228891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                    }
229a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                } else if (file.isFile()) {
230a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                    DependencyStatus status = checkInputFile(file, input, oldestTarget);
231891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                    if (status != DependencyStatus.NONE) {
232891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                        return status;
233891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                    }
23450b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet                }
235065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin            }
236065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        }
237891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet
238065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        // If we make it all the way through our directories we're good.
239891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        return DependencyStatus.NONE;
240065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin    }
241065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin
242065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin    /**
243065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin     * Check all the files in the tree under root and check to see if the files are
244891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     * listed under the dependencies, or if they have been modified. Recurses into subdirs.
245891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     *
246a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet     * @param folder the folder to search through.
247a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet     * @param inputFolder the root level inputFolder
2480fd7983848915b9424501afee8d8f0ea6f08f858Xavier Ducrohet     * @param oldestTarget the time stamp of the oldest output file to compare against.
249891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     *
250891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     * @return the status of the file in the folder.
251065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin     */
252a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet    private DependencyStatus checkInputFolder(File folder, InputPath inputFolder,
253891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet            long oldestTarget) {
254a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet        if (inputFolder.ignores(folder)) {
255a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet            return DependencyStatus.NONE;
256a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet        }
257a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet
258a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet        File[] files = folder.listFiles();
259065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        if (files == null) {
260a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet            System.err.println("ERROR " + folder.toString() + " is not a dir or can't be read");
261891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet            return DependencyStatus.ERROR;
262065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        }
263065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        // Loop through files in this folder
264065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        for (File file : files) {
265065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin            // If this is a directory, recurse into it
266065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin            if (file.isDirectory()) {
267a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                DependencyStatus status = checkInputFolder(file, inputFolder, oldestTarget);
268891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                if (status != DependencyStatus.NONE) {
269891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                    return status;
270891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                }
271891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet            } else if (file.isFile()) {
272a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                DependencyStatus status = checkInputFile(file, inputFolder, oldestTarget);
273891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                if (status != DependencyStatus.NONE) {
274891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                    return status;
275065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin                }
276065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin            }
277065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        }
278065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        // If we got to here then we didn't find anything interesting
279891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        return DependencyStatus.NONE;
280891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet    }
281891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet
282a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet    private DependencyStatus checkInputFile(File file, InputPath inputFolder,
283891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet            long oldestTarget) {
284a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet        if (inputFolder.ignores(file)) {
285a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet            return DependencyStatus.NONE;
286a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet        }
287a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet
288891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        // if it's a file, remove it from the list of prereqs.
289891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        // This way if files in this folder don't trigger a build we'll have less
290891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        // files to go through manually
291891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        if (mPrereqs.remove(file) == false) {
292891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet            // turns out this is a new file!
293891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet            return DependencyStatus.NEW_FILE;
294891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        } else {
295a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet            // check the time stamp on this file if it's a file we care about based what the
296a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet            // input folder decides.
297a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet            if (inputFolder.checksForModification(file)) {
298891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                if (file.lastModified() > oldestTarget) {
299891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                    return DependencyStatus.UPDATED_FILE;
300891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                }
301891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet            }
302891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        }
303891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet
304891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        return DependencyStatus.NONE;
305065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin    }
306065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin
307065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin    /**
308891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     * Check all the prereq files we know about to make sure they're still there, or that they
309891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     * haven't been modified since the last build.
310891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     *
3110fd7983848915b9424501afee8d8f0ea6f08f858Xavier Ducrohet     * @param oldestTarget the time stamp of the oldest output file to compare against.
312891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     *
313891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     * @return the status of the files
314065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin     */
3150fd7983848915b9424501afee8d8f0ea6f08f858Xavier Ducrohet    private DependencyStatus checkPrereqFiles(long oldestTarget) {
316a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet        // TODO: Optimize for the case of a specific file as inputPath.
317a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet        //       We should have a map of filepath to inputpath to quickly search through them?
318a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet
319065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        // Loop through our prereq files and make sure they still exist
320065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        for (File prereq : mPrereqs) {
321065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin            if (prereq.exists() == false) {
322891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet                return DependencyStatus.MISSING_FILE;
323891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet            }
324891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet
325a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet            // check the time stamp on this file if it's a file we care about.
326a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet            // To know if we care about the file we have to find the matching input.
3270fd7983848915b9424501afee8d8f0ea6f08f858Xavier Ducrohet            if (mNewInputs != null) {
3280fd7983848915b9424501afee8d8f0ea6f08f858Xavier Ducrohet                String filePath = prereq.getAbsolutePath();
3290fd7983848915b9424501afee8d8f0ea6f08f858Xavier Ducrohet                for (InputPath input : mNewInputs) {
330a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                    File inputFile = input.getFile();
331a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                    // if the input path is a directory, check if the prereq file is in it,
332a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                    // otherwise check if the prereq file match exactly the input path.
333a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                    if (inputFile.isDirectory()) {
334a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                        if (filePath.startsWith(inputFile.getAbsolutePath())) {
335a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                            // ok file is inside a directory type input folder.
336a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                            // check if we need to check this type of file, and if yes, check it.
337a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                            if (input.checksForModification(prereq)) {
338a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                                if (prereq.lastModified() > oldestTarget) {
339a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                                    return DependencyStatus.UPDATED_FILE;
340a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                                }
341a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                            }
342a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                        }
343a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                    } else {
344a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                        // this is a file input path, we must check if the match is exact.
345a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                        if (prereq.equals(inputFile)) {
346a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                            if (input.checksForModification(prereq)) {
347a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                                if (prereq.lastModified() > oldestTarget) {
348a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                                    return DependencyStatus.UPDATED_FILE;
349a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                                }
350a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                            }
351a9a28238f7b20d7c0a05c95afad02fcdb34e0d0eXavier Ducrohet                        }
3520fd7983848915b9424501afee8d8f0ea6f08f858Xavier Ducrohet                    }
3530fd7983848915b9424501afee8d8f0ea6f08f858Xavier Ducrohet                }
3542c8cced2d84e8e93214a976be7afd6f5913fc74fXavier Ducrohet            } else {
3552c8cced2d84e8e93214a976be7afd6f5913fc74fXavier Ducrohet                // no input? we consider all files.
3562c8cced2d84e8e93214a976be7afd6f5913fc74fXavier Ducrohet                if (prereq.lastModified() > oldestTarget) {
3572c8cced2d84e8e93214a976be7afd6f5913fc74fXavier Ducrohet                    return DependencyStatus.UPDATED_FILE;
3582c8cced2d84e8e93214a976be7afd6f5913fc74fXavier Ducrohet                }
3590fd7983848915b9424501afee8d8f0ea6f08f858Xavier Ducrohet            }
360065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        }
361891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet
362891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        // If we get this far, then all our prereq are okay
363891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        return DependencyStatus.NONE;
364065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin    }
365065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin
366065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin    /**
367065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin     * Check all the target files we know about to make sure they're still there
368065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin     * @return true if any of the target files are missing.
369065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin     */
370065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin    private boolean missingTargetFile() {
371065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        // Loop through our target files and make sure they still exist
372065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        for (File target : mTargets) {
373065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin            if (target.exists() == false) {
374065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin                return true;
375065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin            }
376065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        }
377065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        // If we get this far, then all our targets are okay
378065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        return false;
379065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin    }
380065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin
381065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin    /**
382891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     * Returns the earliest modification time stamp from all the output targets. If there
383891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet     * are no known target, the dependency file time stamp is returned.
384065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin     */
385891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet    private long getOutputLastModified() {
386065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        // Find the oldest target
387065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        long oldestTarget = Long.MAX_VALUE;
38850b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet        // if there's no output, then compare to the time of the dependency file.
38950b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet        if (mTargets.size() == 0) {
39050b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet            oldestTarget = mDepFileLastModified;
39150b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet        } else {
39250b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet            for (File target : mTargets) {
39350b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet                if (target.lastModified() < oldestTarget) {
39450b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet                    oldestTarget = target.lastModified();
39550b3e57fa887dc7182facfb178ef842103aeeaf3Xavier Ducrohet                }
396065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin            }
397065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        }
398065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin
399891cbf7552f98af2f0baaed069b5eb64de50c556Xavier Ducrohet        return oldestTarget;
400065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin    }
401065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin
402065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin    /**
403065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin     * Reads and returns the content of a text file.
404065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin     * @param filepath the file path to the text file
405065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin     * @return null if the file could not be read
406065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin     */
407065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin    private static String readFile(String filepath) {
408065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        try {
409065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin            FileInputStream fStream = new FileInputStream(filepath);
410065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin            if (fStream != null) {
411065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin                BufferedReader reader = new BufferedReader(new InputStreamReader(fStream));
412065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin
413065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin                String line;
414065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin                StringBuilder total = new StringBuilder(reader.readLine());
415065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin                while ((line = reader.readLine()) != null) {
416065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin                    total.append('\n');
417065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin                    total.append(line);
418065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin                }
419065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin                return total.toString();
420065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin            }
421065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        } catch (IOException e) {
422065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin            // we'll just return null
423065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        }
424065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin        return null;
425065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin    }
426065dfacbf0c6e46c80f53d9207fac17afcb1fca7Josiah Gaskin}
427