• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
16 package android.databinding.tool;
17 
18 import org.apache.commons.io.FileUtils;
19 import org.apache.commons.io.IOUtils;
20 import org.w3c.dom.Document;
21 
22 import android.databinding.tool.store.ResourceBundle.LayoutFileBundle;
23 import android.databinding.tool.util.GenerationalClassUtil;
24 import android.databinding.tool.writer.JavaFileWriter;
25 
26 import java.io.File;
27 import java.io.FileWriter;
28 import java.io.FilenameFilter;
29 import java.io.IOException;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Map;
35 
36 import javax.xml.parsers.DocumentBuilder;
37 import javax.xml.parsers.DocumentBuilderFactory;
38 import javax.xml.xpath.XPath;
39 import javax.xml.xpath.XPathConstants;
40 import javax.xml.xpath.XPathExpressionException;
41 import javax.xml.xpath.XPathFactory;
42 
43 /**
44  * This class is used by make to copy resources to an intermediate directory and start processing
45  * them. When aapt takes over, this can be easily extracted to a short script.
46  */
47 public class MakeCopy {
48     private static final int MANIFEST_INDEX = 0;
49     private static final int SRC_INDEX = 1;
50     private static final int XML_INDEX = 2;
51     private static final int RES_OUT_INDEX = 3;
52     private static final int RES_IN_INDEX = 4;
53 
54     private static final String APP_SUBPATH = LayoutXmlProcessor.RESOURCE_BUNDLE_PACKAGE
55             .replace('.', File.separatorChar);
56     private static final FilenameFilter LAYOUT_DIR_FILTER = new FilenameFilter() {
57         @Override
58         public boolean accept(File dir, String name) {
59             return name.toLowerCase().startsWith("layout");
60         }
61     };
62 
63     private static final FilenameFilter XML_FILENAME_FILTER = new FilenameFilter() {
64         @Override
65         public boolean accept(File dir, String name) {
66             return name.toLowerCase().endsWith(".xml");
67         }
68     };
69 
main(String[] args)70     public static void main(String[] args) {
71         if (args.length < 5) {
72             System.out.println("required parameters: [-l] manifest adk-dir src-out-dir xml-out-dir " +
73                             "res-out-dir res-in-dir...");
74             System.out.println("Creates an android data binding class and copies resources from");
75             System.out.println("res-source to res-target and modifies binding layout files");
76             System.out.println("in res-target. Binding data is extracted into XML files");
77             System.out.println("and placed in xml-out-dir.");
78             System.out.println("  -l          indicates that this is a library");
79             System.out.println("  manifest    path to AndroidManifest.xml file");
80             System.out.println("  src-out-dir path to where generated source goes");
81             System.out.println("  xml-out-dir path to where generated binding XML goes");
82             System.out.println("  res-out-dir path to the where modified resources should go");
83             System.out.println("  res-in-dir  path to source resources \"res\" directory. One" +
84                     " or more are allowed.");
85             System.exit(1);
86         }
87         final boolean isLibrary = args[0].equals("-l");
88         final int indexOffset = isLibrary ? 1 : 0;
89         final String applicationPackage;
90         final int minSdk;
91         final Document androidManifest = readAndroidManifest(
92                 new File(args[MANIFEST_INDEX + indexOffset]));
93         try {
94             final XPathFactory xPathFactory = XPathFactory.newInstance();
95             final XPath xPath = xPathFactory.newXPath();
96             applicationPackage = xPath.evaluate("string(/manifest/@package)", androidManifest);
97             final Double minSdkNumber = (Double) xPath.evaluate(
98                     "number(/manifest/uses-sdk/@android:minSdkVersion)", androidManifest,
99                     XPathConstants.NUMBER);
100             minSdk = minSdkNumber == null ? 1 : minSdkNumber.intValue();
101         } catch (XPathExpressionException e) {
102             e.printStackTrace();
103             System.exit(6);
104             return;
105         }
106         final File srcDir = new File(args[SRC_INDEX + indexOffset], APP_SUBPATH);
107         if (!makeTargetDir(srcDir)) {
108             System.err.println("Could not create source directory " + srcDir);
109             System.exit(2);
110         }
111         final File resTarget = new File(args[RES_OUT_INDEX + indexOffset]);
112         if (!makeTargetDir(resTarget)) {
113             System.err.println("Could not create resource directory: " + resTarget);
114             System.exit(4);
115         }
116         final File xmlDir = new File(args[XML_INDEX + indexOffset]);
117         if (!makeTargetDir(xmlDir)) {
118             System.err.println("Could not create xml output directory: " + xmlDir);
119             System.exit(5);
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         System.out.println("Library? " + isLibrary);
127 
128         boolean foundSomeResources = false;
129         for (int i = RES_IN_INDEX + indexOffset; i < args.length; i++) {
130             final File resDir = new File(args[i]);
131             if (!resDir.exists()) {
132                 System.out.println("Could not find resource directory: " + resDir);
133             } else {
134                 System.out.println("Source Resources: " + resDir.getAbsolutePath());
135                 try {
136                     FileUtils.copyDirectory(resDir, resTarget);
137                     addFromFile(resDir, resTarget);
138                     foundSomeResources = true;
139                 } catch (IOException e) {
140                     System.err.println("Could not copy resources from " + resDir + " to " + resTarget +
141                             ": " + e.getLocalizedMessage());
142                     System.exit(3);
143                 }
144             }
145         }
146 
147         if (!foundSomeResources) {
148             System.err.println("No resource directories were found.");
149             System.exit(7);
150         }
151         processLayoutFiles(applicationPackage, resTarget, srcDir, xmlDir, minSdk,
152                 isLibrary);
153     }
154 
readAndroidManifest(File manifest)155     private static Document readAndroidManifest(File manifest) {
156         try {
157             DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
158             DocumentBuilder documentBuilder = dbf.newDocumentBuilder();
159             return documentBuilder.parse(manifest);
160         } catch (Exception e) {
161             System.err.println("Could not load Android Manifest from " +
162                     manifest.getAbsolutePath() + ": " + e.getLocalizedMessage());
163             System.exit(8);
164             return null;
165         }
166     }
167 
processLayoutFiles(String applicationPackage, File resTarget, File srcDir, File xmlDir, int minSdk, boolean isLibrary)168     private static void processLayoutFiles(String applicationPackage, File resTarget, File srcDir,
169             File xmlDir, int minSdk, boolean isLibrary) {
170         MakeFileWriter makeFileWriter = new MakeFileWriter(srcDir);
171         LayoutXmlProcessor xmlProcessor = new LayoutXmlProcessor(applicationPackage,
172                 makeFileWriter, minSdk, isLibrary, new LayoutXmlProcessor.OriginalFileLookup() {
173             @Override
174             public File getOriginalFileFor(File file) {
175                 return file;
176             }
177         });
178         try {
179             LayoutXmlProcessor.ResourceInput input = new LayoutXmlProcessor.ResourceInput(
180                     false, resTarget,resTarget
181             );
182             xmlProcessor.processResources(input);
183             xmlProcessor.writeLayoutInfoFiles(xmlDir);
184             // TODO Looks like make does not support excluding from libs ?
185             xmlProcessor.writeInfoClass(null, xmlDir, null);
186             Map<String, List<LayoutFileBundle>> bundles =
187                     xmlProcessor.getResourceBundle().getLayoutBundles();
188             if (isLibrary) {
189                 for (String name : bundles.keySet()) {
190                     LayoutFileBundle layoutFileBundle = bundles.get(name).get(0);
191                     String pkgName = layoutFileBundle.getBindingClassPackage().replace('.', '/');
192                     System.err.println(pkgName + '/' + layoutFileBundle.getBindingClassName() +
193                         ".class");
194                 }
195             }
196             if (makeFileWriter.getErrorCount() > 0) {
197                 System.exit(9);
198             }
199         } catch (Exception e) {
200             System.err.println("Error processing layout files: " + e.getLocalizedMessage());
201             System.exit(10);
202         }
203     }
204 
addFromFile(File resDir, File resTarget)205     private static void addFromFile(File resDir, File resTarget) {
206         for (File layoutDir : resDir.listFiles(LAYOUT_DIR_FILTER)) {
207             if (layoutDir.isDirectory()) {
208                 File targetDir = new File(resTarget, layoutDir.getName());
209                 for (File layoutFile : layoutDir.listFiles(XML_FILENAME_FILTER)) {
210                     File targetFile = new File(targetDir, layoutFile.getName());
211                     FileWriter appender = null;
212                     try {
213                         appender = new FileWriter(targetFile, true);
214                         appender.write("<!-- From: " + layoutFile.toURI().toString() + " -->\n");
215                     } catch (IOException e) {
216                         System.err.println("Could not update " + layoutFile + ": " +
217                                 e.getLocalizedMessage());
218                     } finally {
219                         IOUtils.closeQuietly(appender);
220                     }
221                 }
222             }
223         }
224     }
225 
makeTargetDir(File dir)226     private static boolean makeTargetDir(File dir) {
227         if (dir.exists()) {
228             return dir.isDirectory();
229         }
230 
231         return dir.mkdirs();
232     }
233 
234     private static class MakeFileWriter extends JavaFileWriter {
235         private final File mSourceRoot;
236         private int mErrorCount;
237 
MakeFileWriter(File sourceRoot)238         public MakeFileWriter(File sourceRoot) {
239             mSourceRoot = sourceRoot;
240         }
241 
242         @Override
writeToFile(String canonicalName, String contents)243         public void writeToFile(String canonicalName, String contents) {
244             String fileName = canonicalName.replace('.', File.separatorChar) + ".java";
245             File sourceFile = new File(mSourceRoot, fileName);
246             FileWriter writer = null;
247             try {
248                 sourceFile.getParentFile().mkdirs();
249                 writer = new FileWriter(sourceFile);
250                 writer.write(contents);
251             } catch (IOException e) {
252                 System.err.println("Could not write to " + sourceFile + ": " +
253                         e.getLocalizedMessage());
254                 mErrorCount++;
255             } finally {
256                 IOUtils.closeQuietly(writer);
257             }
258         }
259 
getErrorCount()260         public int getErrorCount() {
261             return mErrorCount;
262         }
263     }
264 }
265