MakeCopy.java revision fead9ca09b117136b35bc5bf137340a754f9eddd
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package android.databinding.tool; 17 18import org.apache.commons.io.FileUtils; 19import org.apache.commons.io.IOUtils; 20import org.w3c.dom.Document; 21 22import android.databinding.tool.writer.JavaFileWriter; 23 24import java.io.File; 25import java.io.FileWriter; 26import java.io.FilenameFilter; 27import java.io.IOException; 28import java.util.ArrayList; 29 30import javax.xml.parsers.DocumentBuilder; 31import javax.xml.parsers.DocumentBuilderFactory; 32import javax.xml.xpath.XPath; 33import javax.xml.xpath.XPathConstants; 34import javax.xml.xpath.XPathExpressionException; 35import javax.xml.xpath.XPathFactory; 36 37/** 38 * This class is used by make to copy resources to an intermediate directory and start processing 39 * them. When aapt takes over, this can be easily extracted to a short script. 40 */ 41public class MakeCopy { 42 private static final int MANIFEST_INDEX = 0; 43 private static final int ADK_INDEX = 1; 44 private static final int SRC_INDEX = 2; 45 private static final int XML_INDEX = 3; 46 private static final int RES_OUT_INDEX = 4; 47 private static final int RES_IN_INDEX = 5; 48 49 private static final String APP_SUBPATH = LayoutXmlProcessor.RESOURCE_BUNDLE_PACKAGE 50 .replace('.', File.separatorChar); 51 private static final FilenameFilter LAYOUT_DIR_FILTER = new FilenameFilter() { 52 @Override 53 public boolean accept(File dir, String name) { 54 return name.toLowerCase().startsWith("layout"); 55 } 56 }; 57 58 private static final FilenameFilter XML_FILENAME_FILTER = new FilenameFilter() { 59 @Override 60 public boolean accept(File dir, String name) { 61 return name.toLowerCase().endsWith(".xml"); 62 } 63 }; 64 65 public static void main(String[] args) { 66 if (args.length < 6) { 67 System.out.println("required parameters: manifest adk-dir src-out-dir xml-out-dir " + 68 "res-out-dir res-in-dir..."); 69 System.out.println("Creates an android data binding class and copies resources from"); 70 System.out.println("res-source to res-target and modifies binding layout files"); 71 System.out.println("in res-target. Binding data is extracted into XML files"); 72 System.out.println("and placed in xml-out-dir."); 73 System.out.println(" manifest path to AndroidManifest.xml file"); 74 System.out.println(" adk-dir path to Android SDK home"); 75 System.out.println(" src-out-dir path to where generated source goes"); 76 System.out.println(" xml-out-dir path to where generated binding XML goes"); 77 System.out.println(" res-out-dir path to the where modified resources should go"); 78 System.out.println(" res-in-dir path to source resources \"res\" directory. One" + 79 " or more are allowed."); 80 System.exit(1); 81 } 82 final boolean isLibrary; 83 final String applicationPackage; 84 final int minSdk; 85 final Document androidManifest = readAndroidManifest(new File(args[MANIFEST_INDEX])); 86 try { 87 final XPathFactory xPathFactory = XPathFactory.newInstance(); 88 final XPath xPath = xPathFactory.newXPath(); 89 isLibrary = (Boolean) xPath.evaluate("boolean(/manifest/application)", androidManifest, 90 XPathConstants.BOOLEAN); 91 applicationPackage = xPath.evaluate("string(/manifest/@package)", androidManifest); 92 final Double minSdkNumber = (Double) xPath.evaluate( 93 "number(/manifest/uses-sdk/@android:minSdkVersion)", androidManifest, 94 XPathConstants.NUMBER); 95 minSdk = minSdkNumber == null ? 1 : minSdkNumber.intValue(); 96 } catch (XPathExpressionException e) { 97 e.printStackTrace(); 98 System.exit(6); 99 return; 100 } 101 final File srcDir = new File(args[SRC_INDEX], APP_SUBPATH); 102 if (!makeTargetDir(srcDir)) { 103 System.err.println("Could not create source directory " + srcDir); 104 System.exit(2); 105 } 106 final File resTarget = new File(args[RES_OUT_INDEX]); 107 if (!makeTargetDir(resTarget)) { 108 System.err.println("Could not create resource directory: " + resTarget); 109 System.exit(4); 110 } 111 final File xmlDir = new File(args[XML_INDEX]); 112 if (!makeTargetDir(xmlDir)) { 113 System.err.println("Could not create xml output directory: " + xmlDir); 114 System.exit(5); 115 } 116 final File adkDir = new File(args[ADK_INDEX]); 117 if (!adkDir.exists()) { 118 System.err.println("Could not find android SDK directory: " + adkDir); 119 System.exit(6); 120 } 121 System.out.println("Application Package: " + applicationPackage); 122 System.out.println("Minimum SDK: " + minSdk); 123 System.out.println("Target Resources: " + resTarget.getAbsolutePath()); 124 System.out.println("Target Source Dir: " + srcDir.getAbsolutePath()); 125 System.out.println("Target XML Dir: " + xmlDir.getAbsolutePath()); 126 127 boolean foundSomeResources = false; 128 for (int i = RES_IN_INDEX; i < args.length; i++) { 129 final File resDir = new File(args[i]); 130 if (!resDir.exists()) { 131 System.err.println("Could not find resource directory: " + resDir); 132 } else { 133 System.out.println("Source Resources: " + resDir.getAbsolutePath()); 134 try { 135 FileUtils.copyDirectory(resDir, resTarget); 136 addFromFile(resDir, resTarget); 137 foundSomeResources = true; 138 } catch (IOException e) { 139 System.err.println("Could not copy resources from " + resDir + " to " + resTarget + 140 ": " + e.getLocalizedMessage()); 141 System.exit(3); 142 } 143 } 144 } 145 146 if (!foundSomeResources) { 147 System.err.println("No resource directories were found."); 148 System.exit(7); 149 } 150 processLayoutFiles(applicationPackage, resTarget, srcDir, xmlDir, adkDir, minSdk, 151 isLibrary); 152 } 153 154 private static Document readAndroidManifest(File manifest) { 155 try { 156 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 157 DocumentBuilder documentBuilder = dbf.newDocumentBuilder(); 158 return documentBuilder.parse(manifest); 159 } catch (Exception e) { 160 System.err.println("Could not load Android Manifest from " + 161 manifest.getAbsolutePath() + ": " + e.getLocalizedMessage()); 162 System.exit(8); 163 return null; 164 } 165 } 166 167 private static void processLayoutFiles(String applicationPackage, File resTarget, File srcDir, 168 File xmlDir, File adkDir, int minSdk, boolean isLibrary) { 169 ArrayList<File> resourceFolders = new ArrayList<File>(); 170 resourceFolders.add(resTarget); 171 MakeFileWriter makeFileWriter = new MakeFileWriter(srcDir); 172 LayoutXmlProcessor xmlProcessor = new LayoutXmlProcessor(applicationPackage, 173 resourceFolders, makeFileWriter, minSdk, isLibrary); 174 try { 175 xmlProcessor.processResources(); 176 xmlProcessor.writeIntermediateFile(adkDir, xmlDir); 177 if (makeFileWriter.getErrorCount() > 0) { 178 System.exit(9); 179 } 180 } catch (Exception e) { 181 System.err.println("Error processing layout files: " + e.getLocalizedMessage()); 182 System.exit(10); 183 } 184 } 185 186 private static void addFromFile(File resDir, File resTarget) { 187 for (File layoutDir : resDir.listFiles(LAYOUT_DIR_FILTER)) { 188 if (layoutDir.isDirectory()) { 189 File targetDir = new File(resTarget, layoutDir.getName()); 190 for (File layoutFile : layoutDir.listFiles(XML_FILENAME_FILTER)) { 191 File targetFile = new File(targetDir, layoutFile.getName()); 192 FileWriter appender = null; 193 try { 194 appender = new FileWriter(targetFile, true); 195 appender.write("<!-- From: " + layoutFile.toURI().toString() + " -->\n"); 196 } catch (IOException e) { 197 System.err.println("Could not update " + layoutFile + ": " + 198 e.getLocalizedMessage()); 199 } finally { 200 IOUtils.closeQuietly(appender); 201 } 202 } 203 } 204 } 205 } 206 207 private static boolean makeTargetDir(File dir) { 208 if (dir.exists()) { 209 return dir.isDirectory(); 210 } 211 212 return dir.mkdirs(); 213 } 214 215 private static class MakeFileWriter extends JavaFileWriter { 216 private final File mSourceRoot; 217 private int mErrorCount; 218 219 public MakeFileWriter(File sourceRoot) { 220 mSourceRoot = sourceRoot; 221 } 222 223 @Override 224 public void writeToFile(String canonicalName, String contents) { 225 String fileName = canonicalName.replace('.', File.separatorChar) + ".java"; 226 File sourceFile = new File(mSourceRoot, fileName); 227 FileWriter writer = null; 228 try { 229 sourceFile.getParentFile().mkdirs(); 230 writer = new FileWriter(sourceFile); 231 writer.write(contents); 232 } catch (IOException e) { 233 System.err.println("Could not write to " + sourceFile + ": " + 234 e.getLocalizedMessage()); 235 mErrorCount++; 236 } finally { 237 IOUtils.closeQuietly(writer); 238 } 239 } 240 241 public int getErrorCount() { 242 return mErrorCount; 243 } 244 } 245} 246