OSDN Git Service

Created command line method to be invoked by make.
authorGeorge Mount <mount@google.com>
Wed, 11 Mar 2015 23:17:31 +0000 (16:17 -0700)
committerGeorge Mount <mount@google.com>
Fri, 20 Mar 2015 18:20:19 +0000 (11:20 -0700)
make needs some means for copying resource files, stripping them,
and generating the intermediate java/xml files. This calls into
the same code used by the gradle plugin to do it. When aapt
work has completed, this can be removed entirely and replaced
by a simple shell script.

Change-Id: Ie6e1fda8fa27892cc74a13f1404ab064f6210270

tools/data-binding/compiler/src/main/java/com/android/databinding/MakeCopy.java [new file with mode: 0644]

diff --git a/tools/data-binding/compiler/src/main/java/com/android/databinding/MakeCopy.java b/tools/data-binding/compiler/src/main/java/com/android/databinding/MakeCopy.java
new file mode 100644 (file)
index 0000000..141018e
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.databinding;
+
+import com.android.databinding.writer.JavaFileWriter;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.UUID;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+/**
+ * This class is used by make to copy resources to an intermediate directory and start processing
+ * them. When aapt takes over, this can be easily extracted to a short script.
+ */
+public class MakeCopy {
+    private static final int MANIFEST_INDEX = 0;
+    private static final int ADK_INDEX = 1;
+    private static final int SRC_INDEX = 2;
+    private static final int XML_INDEX = 3;
+    private static final int RES_OUT_INDEX = 4;
+    private static final int RES_IN_INDEX = 5;
+
+    private static final String APP_SUBPATH = "com.android.databinding.layouts"
+            .replace('.', File.separatorChar);
+    private static final FilenameFilter LAYOUT_DIR_FILTER = new FilenameFilter() {
+        @Override
+        public boolean accept(File dir, String name) {
+            return name.toLowerCase().startsWith("layout");
+        }
+    };
+
+    private static final FilenameFilter XML_FILENAME_FILTER = new FilenameFilter() {
+        @Override
+        public boolean accept(File dir, String name) {
+            return name.toLowerCase().endsWith(".xml");
+        }
+    };
+
+    public static void main(String[] args) {
+        if (args.length < 6) {
+            System.out.println("required parameters: manifest adk-dir src-out-dir xml-out-dir " +
+                            "res-out-dir res-in-dir...");
+            System.out.println("Creates an android data binding class and copies resources from");
+            System.out.println("res-source to res-target and modifies binding layout files");
+            System.out.println("in res-target. Binding data is extracted into XML files");
+            System.out.println("and placed in xml-out-dir.");
+            System.out.println("  manifest    path to AndroidManifest.xml file");
+            System.out.println("  adk-dir     path to Android SDK home");
+            System.out.println("  src-out-dir path to where generated source goes");
+            System.out.println("  xml-out-dir path to where generated binding XML goes");
+            System.out.println("  res-out-dir path to the where modified resources should go");
+            System.out.println("  res-in-dir  path to source resources \"res\" directory. One" +
+                    " or more are allowed.");
+            System.exit(1);
+        }
+        final boolean isLibrary;
+        final String applicationPackage;
+        final int minSdk;
+        final Document androidManifest = readAndroidManifest(new File(args[MANIFEST_INDEX]));
+        try {
+            final XPathFactory xPathFactory = XPathFactory.newInstance();
+            final XPath xPath = xPathFactory.newXPath();
+            isLibrary = (Boolean) xPath.evaluate("boolean(/manifest/application)", androidManifest,
+                    XPathConstants.BOOLEAN);
+            applicationPackage = xPath.evaluate("string(/manifest/@package)", androidManifest);
+            final Double minSdkNumber = (Double) xPath.evaluate(
+                    "number(/manifest/uses-sdk/@android:minSdkVersion)", androidManifest,
+                    XPathConstants.NUMBER);
+            minSdk = minSdkNumber == null ? 1 : minSdkNumber.intValue();
+        } catch (XPathExpressionException e) {
+            e.printStackTrace();
+            System.exit(6);
+            return;
+        }
+        final File srcDir = new File(args[SRC_INDEX], APP_SUBPATH);
+        if (!makeTargetDir(srcDir)) {
+            System.err.println("Could not create source directory " + srcDir);
+            System.exit(2);
+        }
+        final File resTarget = new File(args[RES_OUT_INDEX]);
+        if (!makeTargetDir(resTarget)) {
+            System.err.println("Could not create resource directory: " + resTarget);
+            System.exit(4);
+        }
+        final File xmlDir = new File(args[XML_INDEX]);
+        if (!makeTargetDir(xmlDir)) {
+            System.err.println("Could not create xml output directory: " + xmlDir);
+            System.exit(5);
+        }
+        final File adkDir = new File(args[ADK_INDEX]);
+        if (!adkDir.exists()) {
+            System.err.println("Could not find android SDK directory: " + adkDir);
+            System.exit(6);
+        }
+        System.out.println("Application Package: " + applicationPackage);
+        System.out.println("Minimum SDK: " + minSdk);
+        System.out.println("Target Resources: " + resTarget.getAbsolutePath());
+        System.out.println("Target Source Dir: " + srcDir.getAbsolutePath());
+        System.out.println("Target XML Dir: " + xmlDir.getAbsolutePath());
+
+        boolean foundSomeResources = false;
+        for (int i = RES_IN_INDEX; i < args.length; i++) {
+            final File resDir = new File(args[i]);
+            if (!resDir.exists()) {
+                System.err.println("Could not find resource directory: " + resDir);
+            } else {
+                System.out.println("Source Resources: " + resDir.getAbsolutePath());
+                try {
+                    FileUtils.copyDirectory(resDir, resTarget);
+                    addFromFile(resDir, resTarget);
+                    foundSomeResources = true;
+                } catch (IOException e) {
+                    System.err.println("Could not copy resources from " + resDir + " to " + resTarget +
+                            ": " + e.getLocalizedMessage());
+                    System.exit(3);
+                }
+            }
+        }
+
+        if (!foundSomeResources) {
+            System.err.println("No resource directories were found.");
+            System.exit(7);
+        }
+        processLayoutFiles(applicationPackage, resTarget, srcDir, xmlDir, adkDir, minSdk,
+                isLibrary);
+    }
+
+    private static Document readAndroidManifest(File manifest) {
+        try {
+            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+            DocumentBuilder documentBuilder = dbf.newDocumentBuilder();
+            return documentBuilder.parse(manifest);
+        } catch (Exception e) {
+            System.err.println("Could not load Android Manifest from " +
+                    manifest.getAbsolutePath() + ": " + e.getLocalizedMessage());
+            System.exit(8);
+            return null;
+        }
+    }
+
+    private static void processLayoutFiles(String applicationPackage, File resTarget, File srcDir,
+            File xmlDir, File adkDir, int minSdk, boolean isLibrary) {
+        ArrayList<File> resourceFolders = new ArrayList<File>();
+        resourceFolders.add(resTarget);
+        MakeFileWriter makeFileWriter = new MakeFileWriter(srcDir);
+        LayoutXmlProcessor xmlProcessor = new LayoutXmlProcessor(applicationPackage,
+                resourceFolders, makeFileWriter, minSdk, isLibrary);
+        try {
+            xmlProcessor.processResources();
+            xmlProcessor.writeIntermediateFile(adkDir, xmlDir);
+            if (makeFileWriter.getErrorCount() > 0) {
+                System.exit(9);
+            }
+        } catch (Exception e) {
+            System.err.println("Error processing layout files: " + e.getLocalizedMessage());
+            System.exit(10);
+        }
+    }
+
+    private static void addFromFile(File resDir, File resTarget) {
+        for (File layoutDir : resDir.listFiles(LAYOUT_DIR_FILTER)) {
+            if (layoutDir.isDirectory()) {
+                File targetDir = new File(resTarget, layoutDir.getName());
+                for (File layoutFile : layoutDir.listFiles(XML_FILENAME_FILTER)) {
+                    File targetFile = new File(targetDir, layoutFile.getName());
+                    FileWriter appender = null;
+                    try {
+                        appender = new FileWriter(targetFile, true);
+                        appender.write("<!-- From: " + layoutFile.toURI().toString() + " -->\n");
+                    } catch (IOException e) {
+                        System.err.println("Could not update " + layoutFile + ": " +
+                                e.getLocalizedMessage());
+                    } finally {
+                        IOUtils.closeQuietly(appender);
+                    }
+                }
+            }
+        }
+    }
+
+    private static boolean makeTargetDir(File dir) {
+        if (dir.exists()) {
+            return dir.isDirectory();
+        }
+
+        return dir.mkdirs();
+    }
+
+    private static class MakeFileWriter extends JavaFileWriter {
+        private final File mSourceRoot;
+        private int mErrorCount;
+
+        public MakeFileWriter(File sourceRoot) {
+            mSourceRoot = sourceRoot;
+        }
+
+        @Override
+        public void writeToFile(String canonicalName, String contents) {
+            String fileName = canonicalName.replace('.', File.separatorChar) + ".java";
+            File sourceFile = new File(mSourceRoot, fileName);
+            FileWriter writer = null;
+            try {
+                sourceFile.getParentFile().mkdirs();
+                writer = new FileWriter(sourceFile);
+                writer.write(contents);
+            } catch (IOException e) {
+                System.err.println("Could not write to " + sourceFile + ": " +
+                        e.getLocalizedMessage());
+                mErrorCount++;
+            } finally {
+                IOUtils.closeQuietly(writer);
+            }
+        }
+
+        public int getErrorCount() {
+            return mErrorCount;
+        }
+    }
+}