MakeCopy.java revision ee7586713d68806b556a425cbebf007a56261ff3
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 SRC_INDEX = 1; 44 private static final int XML_INDEX = 2; 45 private static final int RES_OUT_INDEX = 3; 46 private static final int RES_IN_INDEX = 4; 47 48 private static final String APP_SUBPATH = LayoutXmlProcessor.RESOURCE_BUNDLE_PACKAGE 49 .replace('.', File.separatorChar); 50 private static final FilenameFilter LAYOUT_DIR_FILTER = new FilenameFilter() { 51 @Override 52 public boolean accept(File dir, String name) { 53 return name.toLowerCase().startsWith("layout"); 54 } 55 }; 56 57 private static final FilenameFilter XML_FILENAME_FILTER = new FilenameFilter() { 58 @Override 59 public boolean accept(File dir, String name) { 60 return name.toLowerCase().endsWith(".xml"); 61 } 62 }; 63 64 public static void main(String[] args) { 65 if (args.length < 5) { 66 System.out.println("required parameters: manifest adk-dir src-out-dir xml-out-dir " + 67 "res-out-dir res-in-dir..."); 68 System.out.println("Creates an android data binding class and copies resources from"); 69 System.out.println("res-source to res-target and modifies binding layout files"); 70 System.out.println("in res-target. Binding data is extracted into XML files"); 71 System.out.println("and placed in xml-out-dir."); 72 System.out.println(" manifest path to AndroidManifest.xml file"); 73 System.out.println(" src-out-dir path to where generated source goes"); 74 System.out.println(" xml-out-dir path to where generated binding XML goes"); 75 System.out.println(" res-out-dir path to the where modified resources should go"); 76 System.out.println(" res-in-dir path to source resources \"res\" directory. One" + 77 " or more are allowed."); 78 System.exit(1); 79 } 80 final boolean isLibrary; 81 final String applicationPackage; 82 final int minSdk; 83 final Document androidManifest = readAndroidManifest(new File(args[MANIFEST_INDEX])); 84 try { 85 final XPathFactory xPathFactory = XPathFactory.newInstance(); 86 final XPath xPath = xPathFactory.newXPath(); 87 isLibrary = (Boolean) xPath.evaluate("boolean(/manifest/application)", androidManifest, 88 XPathConstants.BOOLEAN); 89 applicationPackage = xPath.evaluate("string(/manifest/@package)", androidManifest); 90 final Double minSdkNumber = (Double) xPath.evaluate( 91 "number(/manifest/uses-sdk/@android:minSdkVersion)", androidManifest, 92 XPathConstants.NUMBER); 93 minSdk = minSdkNumber == null ? 1 : minSdkNumber.intValue(); 94 } catch (XPathExpressionException e) { 95 e.printStackTrace(); 96 System.exit(6); 97 return; 98 } 99 final File srcDir = new File(args[SRC_INDEX], APP_SUBPATH); 100 if (!makeTargetDir(srcDir)) { 101 System.err.println("Could not create source directory " + srcDir); 102 System.exit(2); 103 } 104 final File resTarget = new File(args[RES_OUT_INDEX]); 105 if (!makeTargetDir(resTarget)) { 106 System.err.println("Could not create resource directory: " + resTarget); 107 System.exit(4); 108 } 109 final File xmlDir = new File(args[XML_INDEX]); 110 if (!makeTargetDir(xmlDir)) { 111 System.err.println("Could not create xml output directory: " + xmlDir); 112 System.exit(5); 113 } 114 System.out.println("Application Package: " + applicationPackage); 115 System.out.println("Minimum SDK: " + minSdk); 116 System.out.println("Target Resources: " + resTarget.getAbsolutePath()); 117 System.out.println("Target Source Dir: " + srcDir.getAbsolutePath()); 118 System.out.println("Target XML Dir: " + xmlDir.getAbsolutePath()); 119 120 boolean foundSomeResources = false; 121 for (int i = RES_IN_INDEX; i < args.length; i++) { 122 final File resDir = new File(args[i]); 123 if (!resDir.exists()) { 124 System.err.println("Could not find resource directory: " + resDir); 125 } else { 126 System.out.println("Source Resources: " + resDir.getAbsolutePath()); 127 try { 128 FileUtils.copyDirectory(resDir, resTarget); 129 addFromFile(resDir, resTarget); 130 foundSomeResources = true; 131 } catch (IOException e) { 132 System.err.println("Could not copy resources from " + resDir + " to " + resTarget + 133 ": " + e.getLocalizedMessage()); 134 System.exit(3); 135 } 136 } 137 } 138 139 if (!foundSomeResources) { 140 System.err.println("No resource directories were found."); 141 System.exit(7); 142 } 143 processLayoutFiles(applicationPackage, resTarget, srcDir, xmlDir, minSdk, 144 isLibrary); 145 } 146 147 private static Document readAndroidManifest(File manifest) { 148 try { 149 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 150 DocumentBuilder documentBuilder = dbf.newDocumentBuilder(); 151 return documentBuilder.parse(manifest); 152 } catch (Exception e) { 153 System.err.println("Could not load Android Manifest from " + 154 manifest.getAbsolutePath() + ": " + e.getLocalizedMessage()); 155 System.exit(8); 156 return null; 157 } 158 } 159 160 private static void processLayoutFiles(String applicationPackage, File resTarget, File srcDir, 161 File xmlDir, int minSdk, boolean isLibrary) { 162 ArrayList<File> resourceFolders = new ArrayList<File>(); 163 resourceFolders.add(resTarget); 164 MakeFileWriter makeFileWriter = new MakeFileWriter(srcDir); 165 LayoutXmlProcessor xmlProcessor = new LayoutXmlProcessor(applicationPackage, 166 resourceFolders, makeFileWriter, minSdk, isLibrary); 167 try { 168 xmlProcessor.processResources(); 169 xmlProcessor.writeIntermediateFile(null, xmlDir); 170 if (makeFileWriter.getErrorCount() > 0) { 171 System.exit(9); 172 } 173 } catch (Exception e) { 174 System.err.println("Error processing layout files: " + e.getLocalizedMessage()); 175 System.exit(10); 176 } 177 } 178 179 private static void addFromFile(File resDir, File resTarget) { 180 for (File layoutDir : resDir.listFiles(LAYOUT_DIR_FILTER)) { 181 if (layoutDir.isDirectory()) { 182 File targetDir = new File(resTarget, layoutDir.getName()); 183 for (File layoutFile : layoutDir.listFiles(XML_FILENAME_FILTER)) { 184 File targetFile = new File(targetDir, layoutFile.getName()); 185 FileWriter appender = null; 186 try { 187 appender = new FileWriter(targetFile, true); 188 appender.write("<!-- From: " + layoutFile.toURI().toString() + " -->\n"); 189 } catch (IOException e) { 190 System.err.println("Could not update " + layoutFile + ": " + 191 e.getLocalizedMessage()); 192 } finally { 193 IOUtils.closeQuietly(appender); 194 } 195 } 196 } 197 } 198 } 199 200 private static boolean makeTargetDir(File dir) { 201 if (dir.exists()) { 202 return dir.isDirectory(); 203 } 204 205 return dir.mkdirs(); 206 } 207 208 private static class MakeFileWriter extends JavaFileWriter { 209 private final File mSourceRoot; 210 private int mErrorCount; 211 212 public MakeFileWriter(File sourceRoot) { 213 mSourceRoot = sourceRoot; 214 } 215 216 @Override 217 public void writeToFile(String canonicalName, String contents) { 218 String fileName = canonicalName.replace('.', File.separatorChar) + ".java"; 219 File sourceFile = new File(mSourceRoot, fileName); 220 FileWriter writer = null; 221 try { 222 sourceFile.getParentFile().mkdirs(); 223 writer = new FileWriter(sourceFile); 224 writer.write(contents); 225 } catch (IOException e) { 226 System.err.println("Could not write to " + sourceFile + ": " + 227 e.getLocalizedMessage()); 228 mErrorCount++; 229 } finally { 230 IOUtils.closeQuietly(writer); 231 } 232 } 233 234 public int getErrorCount() { 235 return mErrorCount; 236 } 237 } 238} 239