18e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount/*
28e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * Copyright (C) 2015 The Android Open Source Project
38e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * Licensed under the Apache License, Version 2.0 (the "License");
48e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * you may not use this file except in compliance with the License.
58e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * You may obtain a copy of the License at
68e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount *      http://www.apache.org/licenses/LICENSE-2.0
78e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * Unless required by applicable law or agreed to in writing, software
88e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * distributed under the License is distributed on an "AS IS" BASIS,
98e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
108e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * See the License for the specific language governing permissions and
118e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * limitations under the License.
128e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount */
138e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
14fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountpackage android.databinding.tool;
158e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
164ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Guptaimport com.google.common.escape.Escaper;
174ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta
189784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport org.apache.commons.io.FileUtils;
198e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport org.xml.sax.SAXException;
208e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
21fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.BindingBuildInfo;
22fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.store.LayoutFileParser;
23fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.store.ResourceBundle;
2489e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyarimport android.databinding.tool.util.L;
259784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport android.databinding.tool.util.Preconditions;
264ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Guptaimport android.databinding.tool.util.SourceCodeEscapers;
27fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.writer.JavaFileWriter;
28a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
298e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport java.io.File;
308e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport java.io.FilenameFilter;
318e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport java.io.IOException;
329784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport java.net.URI;
33a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.util.ArrayList;
348e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport java.util.List;
358e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport java.util.UUID;
368e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
378e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport javax.xml.bind.JAXBException;
388e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport javax.xml.parsers.ParserConfigurationException;
398e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport javax.xml.xpath.XPathExpressionException;
408e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
418e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount/**
428e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * Processes the layout XML, stripping the binding attributes and elements
438e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * and writes the information into an annotated class file for the annotation
448e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * processor to work with.
458e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount */
468e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountpublic class LayoutXmlProcessor {
47a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    // hardcoded in baseAdapters
48fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount    public static final String RESOURCE_BUNDLE_PACKAGE = "android.databinding.layouts";
49a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    public static final String CLASS_NAME = "DataBindingInfo";
508e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    private final JavaFileWriter mFileWriter;
518e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    private final ResourceBundle mResourceBundle;
5297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    private final int mMinSdk;
5397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
548e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    private boolean mProcessingComplete;
558e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    private boolean mWritten;
56a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private final boolean mIsLibrary;
578e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    private final String mBuildId = UUID.randomUUID().toString();
589784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    private final OriginalFileLookup mOriginalFileLookup;
598e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
609784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public LayoutXmlProcessor(String applicationPackage,
619784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            JavaFileWriter fileWriter, int minSdk, boolean isLibrary,
629784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            OriginalFileLookup originalFileLookup) {
638e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        mFileWriter = fileWriter;
648e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        mResourceBundle = new ResourceBundle(applicationPackage);
6597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        mMinSdk = minSdk;
66a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        mIsLibrary = isLibrary;
679784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        mOriginalFileLookup = originalFileLookup;
68a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
69a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
709784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    private static void processIncrementalInputFiles(ResourceInput input,
719784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            ProcessFileCallback callback)
729784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            throws IOException, ParserConfigurationException, XPathExpressionException,
739784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            SAXException {
749784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        processExistingIncrementalFiles(input.getRootInputFolder(), input.getAdded(), callback);
759784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        processExistingIncrementalFiles(input.getRootInputFolder(), input.getChanged(), callback);
769784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        processRemovedIncrementalFiles(input.getRootInputFolder(), input.getRemoved(), callback);
77890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar    }
78890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar
799784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    private static void processExistingIncrementalFiles(File inputRoot, List<File> files,
809784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            ProcessFileCallback callback)
819784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            throws IOException, XPathExpressionException, SAXException,
829784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            ParserConfigurationException {
839784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        for (File file : files) {
849784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            File parent = file.getParentFile();
859784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            if (inputRoot.equals(parent)) {
869784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                callback.processOtherRootFile(file);
879784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            } else if (layoutFolderFilter.accept(parent, parent.getName())) {
889784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                callback.processLayoutFile(file);
899784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            } else {
909784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                callback.processOtherFile(parent, file);
910c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar            }
929784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
939784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
949784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
959784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    private static void processRemovedIncrementalFiles(File inputRoot, List<File> files,
969784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            ProcessFileCallback callback)
979784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            throws IOException {
989784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        for (File file : files) {
999784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            File parent = file.getParentFile();
1009784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            if (inputRoot.equals(parent)) {
1019784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                callback.processRemovedOtherRootFile(file);
1029784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            } else if (layoutFolderFilter.accept(parent, parent.getName())) {
1039784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                callback.processRemovedLayoutFile(file);
1049784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            } else {
1059784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                callback.processRemovedOtherFile(parent, file);
1069784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            }
1079784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
1089784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
109a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
1109784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    private static void processAllInputFiles(ResourceInput input, ProcessFileCallback callback)
1119784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            throws IOException, XPathExpressionException, SAXException,
1129784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            ParserConfigurationException {
1139784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        FileUtils.deleteDirectory(input.getRootOutputFolder());
1149784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        Preconditions.check(input.getRootOutputFolder().mkdirs(), "out dir should be re-created");
1159784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        Preconditions.check(input.getRootInputFolder().isDirectory(), "it must be a directory");
1169784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        for (File firstLevel : input.getRootInputFolder().listFiles()) {
1179784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            if (firstLevel.isDirectory()) {
1189784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                if (layoutFolderFilter.accept(firstLevel, firstLevel.getName())) {
1199784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    callback.processLayoutFolder(firstLevel);
1209784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    for (File xmlFile : firstLevel.listFiles(xmlFileFilter)) {
1219784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                        callback.processLayoutFile(xmlFile);
1229784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    }
1239784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                } else {
1249784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    callback.processOtherFolder(firstLevel);
1259784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    for (File file : firstLevel.listFiles()) {
1269784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                        callback.processOtherFile(firstLevel, file);
1279784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    }
128a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                }
1299784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            } else {
1309784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                callback.processOtherRootFile(firstLevel);
131a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            }
1329784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
133a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
134a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
135a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
136a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    /**
137a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar     * used by the studio plugin
138a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar     */
139a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    public ResourceBundle getResourceBundle() {
140a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        return mResourceBundle;
1418e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    }
1428e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
1439784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public boolean processResources(final ResourceInput input)
1448e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            throws ParserConfigurationException, SAXException, XPathExpressionException,
1458e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            IOException {
1468e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        if (mProcessingComplete) {
1478e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            return false;
1488e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        }
1499784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        final LayoutFileParser layoutFileParser = new LayoutFileParser();
1509784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        final URI inputRootUri = input.getRootInputFolder().toURI();
1519784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        ProcessFileCallback callback = new ProcessFileCallback() {
1529784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            private File convertToOutFile(File file) {
1539784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                final String subPath = toSystemDependentPath(inputRootUri
1549784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                        .relativize(file.toURI()).getPath());
1559784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                return new File(input.getRootOutputFolder(), subPath);
1569784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            }
1579784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            @Override
1589784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            public void processLayoutFile(File file)
1599784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    throws ParserConfigurationException, SAXException, XPathExpressionException,
1609784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    IOException {
16189e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar                final File output = convertToOutFile(file);
1629784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                final ResourceBundle.LayoutFileBundle bindingLayout = layoutFileParser
1639784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                        .parseXml(file, output, mResourceBundle.getAppPackage(), mOriginalFileLookup);
1649784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                if (bindingLayout != null && !bindingLayout.isEmpty()) {
1659784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    mResourceBundle.addLayoutBundle(bindingLayout);
1669784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                }
1679784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            }
1689784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
1699784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            @Override
1709784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            public void processOtherFile(File parentFolder, File file) throws IOException {
17189e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar                final File outParent = convertToOutFile(parentFolder);
1729784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                FileUtils.copyFile(file, new File(outParent, file.getName()));
1739784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            }
1749784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
1759784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            @Override
1769784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            public void processRemovedLayoutFile(File file) {
1779784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                mResourceBundle.addRemovedFile(file);
17889e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar                final File out = convertToOutFile(file);
17989e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar                FileUtils.deleteQuietly(out);
1808e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            }
1819784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
1829784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            @Override
1839784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            public void processRemovedOtherFile(File parentFolder, File file) throws IOException {
18489e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar                final File outParent = convertToOutFile(parentFolder);
18589e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar                FileUtils.deleteQuietly(new File(outParent, file.getName()));
1869784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            }
1879784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
1889784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            @Override
1899784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            public void processOtherFolder(File folder) {
1909784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                //noinspection ResultOfMethodCallIgnored
1919784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                convertToOutFile(folder).mkdirs();
1929784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            }
1939784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
1949784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            @Override
1959784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            public void processLayoutFolder(File folder) {
1969784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                //noinspection ResultOfMethodCallIgnored
1979784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                convertToOutFile(folder).mkdirs();
1989784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            }
1999784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
2009784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            @Override
2019784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            public void processOtherRootFile(File file) throws IOException {
20289e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar                final File outFile = convertToOutFile(file);
2039784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                if (file.isDirectory()) {
2049784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    FileUtils.copyDirectory(file, outFile);
2059784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                } else {
2069784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    FileUtils.copyFile(file, outFile);
2079784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                }
2089784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            }
2099784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
2109784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            @Override
2119784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            public void processRemovedOtherRootFile(File file) throws IOException {
2129784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                final File outFile = convertToOutFile(file);
21389e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar                FileUtils.deleteQuietly(outFile);
2149784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            }
2159784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        };
2169784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        if (input.isIncremental()) {
2179784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            processIncrementalInputFiles(input, callback);
2189784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        } else {
2199784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            processAllInputFiles(input, callback);
2208e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        }
2218e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        mProcessingComplete = true;
2228e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        return true;
2238e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    }
2248e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
2259784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public static String toSystemDependentPath(String path) {
2269784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        if (File.separatorChar != '/') {
2279784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            path = path.replace('/', File.separatorChar);
2289784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
2299784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        return path;
2309784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
2319784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
232be83770af82294c1841eef414513653646b88dc6Yigit Boyar    public void writeLayoutInfoFiles(File xmlOutDir) throws JAXBException {
2338e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        if (mWritten) {
2348e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            return;
2358e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        }
2368e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        for (List<ResourceBundle.LayoutFileBundle> layouts : mResourceBundle.getLayoutBundles()
2378e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount                .values()) {
2388e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            for (ResourceBundle.LayoutFileBundle layout : layouts) {
23928e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                writeXmlFile(xmlOutDir, layout);
2408e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            }
2418e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        }
2429784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        for (File file : mResourceBundle.getRemovedFiles()) {
2439784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            String exportFileName = generateExportFileName(file);
2449784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            FileUtils.deleteQuietly(new File(xmlOutDir, exportFileName));
2459784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
2468e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        mWritten = true;
2478e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    }
2488e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
24928e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar    private void writeXmlFile(File xmlOutDir, ResourceBundle.LayoutFileBundle layout)
25028e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            throws JAXBException {
2519784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        String filename = generateExportFileName(layout);
25228e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        mFileWriter.writeToFile(new File(xmlOutDir, filename), layout.toXML());
253a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
254a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
255a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    public String getInfoClassFullName() {
256a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        return RESOURCE_BUNDLE_PACKAGE + "." + CLASS_NAME;
257a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
258a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
259a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    /**
260a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar     * Generates a string identifier that can uniquely identify the given layout bundle.
261a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar     * This identifier can be used when we need to export data about this layout bundle.
262a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar     */
2639784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    private static String generateExportFileName(ResourceBundle.LayoutFileBundle layout) {
2649784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        return generateExportFileName(layout.getFileName(), layout.getDirectory());
2659784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
2669784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
2679784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    private static String generateExportFileName(File file) {
2689784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        final String fileName = file.getName();
2699784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        return generateExportFileName(fileName.substring(0, fileName.lastIndexOf('.')),
2709784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                file.getParentFile().getName());
2719784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
2729784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
2739784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public static String generateExportFileName(String fileName, String dirName) {
27428e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        return fileName + '-' + dirName + ".xml";
27528e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar    }
27628e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar
27728e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar    public static String exportLayoutNameFromInfoFileName(String infoFileName) {
27828e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        return infoFileName.substring(0, infoFileName.indexOf('-'));
2798e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    }
2808e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
281b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar    public void writeInfoClass(/*Nullable*/ File sdkDir, File xmlOutDir,
282b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar            /*Nullable*/ File exportClassListTo) {
28308119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar        writeInfoClass(sdkDir, xmlOutDir, exportClassListTo, false, false);
284b1356339eaa6c8e967e4fc1dc283b82909a1208dYigit Boyar    }
285b1356339eaa6c8e967e4fc1dc283b82909a1208dYigit Boyar
2869784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public String getPackage() {
2879784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        return mResourceBundle.getAppPackage();
2889784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
2899784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
290b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar    public void writeInfoClass(/*Nullable*/ File sdkDir, File xmlOutDir, File exportClassListTo,
29108119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar            boolean enableDebugLogs, boolean printEncodedErrorLogs) {
2924ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta        Escaper javaEscaper = SourceCodeEscapers.javaCharEscaper();
2934ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta        final String sdkPath = sdkDir == null ? null : javaEscaper.escape(sdkDir.getAbsolutePath());
294a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        final Class annotation = BindingBuildInfo.class;
2954ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta        final String layoutInfoPath = javaEscaper.escape(xmlOutDir.getAbsolutePath());
296b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar        final String exportClassListToPath = exportClassListTo == null ? "" :
2974ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta                javaEscaper.escape(exportClassListTo.getAbsolutePath());
298a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        String classString = "package " + RESOURCE_BUNDLE_PACKAGE + ";\n\n" +
299a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                "import " + annotation.getCanonicalName() + ";\n\n" +
300a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                "@" + annotation.getSimpleName() + "(buildId=\"" + mBuildId + "\", " +
301a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                "modulePackage=\"" + mResourceBundle.getAppPackage() + "\", " +
302ae161289a90f6ac69ffe6411ac8c09653c81d04dYigit Boyar                "sdkRoot=" + "\"" + (sdkPath == null ? "" : sdkPath) + "\"," +
303a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                "layoutInfoDir=\"" + layoutInfoPath + "\"," +
304b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar                "exportClassListTo=\"" + exportClassListToPath + "\"," +
305a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                "isLibrary=" + mIsLibrary + "," +
306b1356339eaa6c8e967e4fc1dc283b82909a1208dYigit Boyar                "minSdk=" + mMinSdk + "," +
30708119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar                "enableDebugLogs=" + enableDebugLogs + "," +
30808119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar                "printEncodedError=" + printEncodedErrorLogs + ")\n" +
309a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                "public class " + CLASS_NAME + " {}\n";
310fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount        mFileWriter.writeToFile(RESOURCE_BUNDLE_PACKAGE + "." + CLASS_NAME, classString);
3118e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    }
3128e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
313a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private static final FilenameFilter layoutFolderFilter = new FilenameFilter() {
3148e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        @Override
3158e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        public boolean accept(File dir, String name) {
3168e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            return name.startsWith("layout");
3178e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        }
3188e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    };
3198e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
320a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private static final FilenameFilter xmlFileFilter = new FilenameFilter() {
3218e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        @Override
3228e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        public boolean accept(File dir, String name) {
3238e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            return name.toLowerCase().endsWith(".xml");
3248e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        }
3258e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    };
326890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar
327890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar    /**
328890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar     * Helper interface that can find the original copy of a resource XML.
329890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar     */
330890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar    public interface OriginalFileLookup {
331890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar
332890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar        /**
333890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar         * @param file The intermediate build file
334890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar         * @return The original file or null if original File cannot be found.
335890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar         */
336890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar        File getOriginalFileFor(File file);
337890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar    }
3389784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
3399784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    /**
3409784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar     * API agnostic class to get resource changes incrementally.
3419784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar     */
3429784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public static class ResourceInput {
3439784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        private final boolean mIncremental;
3449784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        private final File mRootInputFolder;
3459784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        private final File mRootOutputFolder;
3469784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
3479784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        private List<File> mAdded = new ArrayList<File>();
3489784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        private List<File> mRemoved = new ArrayList<File>();
3499784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        private List<File> mChanged = new ArrayList<File>();
3509784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
3519784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        public ResourceInput(boolean incremental, File rootInputFolder, File rootOutputFolder) {
3529784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            mIncremental = incremental;
3539784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            mRootInputFolder = rootInputFolder;
3549784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            mRootOutputFolder = rootOutputFolder;
3559784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
3569784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
3579784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        public void added(File file) {
3589784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            mAdded.add(file);
3599784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
3609784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        public void removed(File file) {
3619784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            mRemoved.add(file);
3629784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
3639784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        public void changed(File file) {
3649784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            mChanged.add(file);
3659784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
3669784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
3679784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        public boolean shouldCopy() {
3689784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            return !mRootInputFolder.equals(mRootOutputFolder);
3699784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
3709784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
3719784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        List<File> getAdded() {
3729784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            return mAdded;
3739784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
3749784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
3759784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        List<File> getRemoved() {
3769784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            return mRemoved;
3779784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
3789784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
3799784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        List<File> getChanged() {
3809784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            return mChanged;
3819784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
3829784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
3839784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        File getRootInputFolder() {
3849784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            return mRootInputFolder;
3859784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
3869784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
3879784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        File getRootOutputFolder() {
3889784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            return mRootOutputFolder;
3899784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
3909784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
3919784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        public boolean isIncremental() {
3929784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            return mIncremental;
3939784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
39489e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar
39589e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar        @Override
39689e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar        public String toString() {
39789e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar            StringBuilder out = new StringBuilder();
39889e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar            out.append("ResourceInput{")
39989e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar                    .append("mIncremental=").append(mIncremental)
40089e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar                    .append(", mRootInputFolder=").append(mRootInputFolder)
40189e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar                    .append(", mRootOutputFolder=").append(mRootOutputFolder);
40289e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar            logFiles(out, "added", mAdded);
40389e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar            logFiles(out, "removed", mRemoved);
40489e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar            logFiles(out, "changed", mChanged);
40589e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar            return out.toString();
40689e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar
40789e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar        }
40889e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar
40989e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar        private static void logFiles(StringBuilder out, String name, List<File> files) {
41089e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar            out.append("\n  ").append(name);
41189e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar            for (File file : files) {
41289e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar                out.append("\n   - ").append(file.getAbsolutePath());
41389e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar            }
41489e50f43810ee32d8fc8ea359cd8e0ca4e9c8de4Yigit Boyar        }
4159784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
4169784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
4179784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    private interface ProcessFileCallback {
4189784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        void processLayoutFile(File file)
4199784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                throws ParserConfigurationException, SAXException, XPathExpressionException,
4209784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                IOException;
4219784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        void processOtherFile(File parentFolder, File file) throws IOException;
4229784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        void processRemovedLayoutFile(File file);
4239784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        void processRemovedOtherFile(File parentFolder, File file) throws IOException;
4249784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
4259784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        void processOtherFolder(File folder);
4269784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
4279784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        void processLayoutFolder(File folder);
4289784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
4299784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        void processOtherRootFile(File file) throws IOException;
4309784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
4319784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        void processRemovedOtherRootFile(File file) throws IOException;
4329784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
4338e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount}
434