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