LayoutXmlProcessor.java revision 9784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3
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
169784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport org.apache.commons.io.FileUtils;
179784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport org.apache.commons.io.filefilter.FileFilterUtils;
1897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport org.apache.commons.lang3.StringEscapeUtils;
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;
249784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport android.databinding.tool.util.Preconditions;
25fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.writer.JavaFileWriter;
26a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
278e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport java.io.File;
288e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport java.io.FilenameFilter;
298e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport java.io.IOException;
308e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport java.io.StringWriter;
319784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport java.net.URI;
32a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.util.ArrayList;
338e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport java.util.List;
348e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport java.util.UUID;
358e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
368e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport javax.xml.bind.JAXBContext;
378e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport javax.xml.bind.JAXBException;
388e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport javax.xml.bind.Marshaller;
398e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport javax.xml.parsers.ParserConfigurationException;
408e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport javax.xml.xpath.XPathExpressionException;
418e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
428e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount/**
438e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * Processes the layout XML, stripping the binding attributes and elements
448e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * and writes the information into an annotated class file for the annotation
458e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount * processor to work with.
468e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount */
478e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountpublic class LayoutXmlProcessor {
48a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    // hardcoded in baseAdapters
49fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount    public static final String RESOURCE_BUNDLE_PACKAGE = "android.databinding.layouts";
50a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    public static final String CLASS_NAME = "DataBindingInfo";
518e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    private final JavaFileWriter mFileWriter;
528e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    private final ResourceBundle mResourceBundle;
5397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    private final int mMinSdk;
5497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
558e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    private boolean mProcessingComplete;
568e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    private boolean mWritten;
57a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private final boolean mIsLibrary;
588e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    private final String mBuildId = UUID.randomUUID().toString();
599784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    private final OriginalFileLookup mOriginalFileLookup;
608e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
619784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public LayoutXmlProcessor(String applicationPackage,
629784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            JavaFileWriter fileWriter, int minSdk, boolean isLibrary,
639784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            OriginalFileLookup originalFileLookup) {
648e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        mFileWriter = fileWriter;
658e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        mResourceBundle = new ResourceBundle(applicationPackage);
6697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        mMinSdk = minSdk;
67a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        mIsLibrary = isLibrary;
689784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        mOriginalFileLookup = originalFileLookup;
69a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
70a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
719784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    private static void processIncrementalInputFiles(ResourceInput input,
729784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            ProcessFileCallback callback)
739784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            throws IOException, ParserConfigurationException, XPathExpressionException,
749784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            SAXException {
759784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        processExistingIncrementalFiles(input.getRootInputFolder(), input.getAdded(), callback);
769784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        processExistingIncrementalFiles(input.getRootInputFolder(), input.getChanged(), callback);
779784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        processRemovedIncrementalFiles(input.getRootInputFolder(), input.getRemoved(), callback);
78890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar    }
79890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar
809784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    private static void processExistingIncrementalFiles(File inputRoot, List<File> files,
819784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            ProcessFileCallback callback)
829784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            throws IOException, XPathExpressionException, SAXException,
839784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            ParserConfigurationException {
849784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        for (File file : files) {
859784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            File parent = file.getParentFile();
869784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            if (inputRoot.equals(parent)) {
879784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                callback.processOtherRootFile(file);
889784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            } else if (layoutFolderFilter.accept(parent, parent.getName())) {
899784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                callback.processLayoutFile(file);
909784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            } else {
919784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                callback.processOtherFile(parent, file);
920c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar            }
939784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
949784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
959784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
969784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    private static void processRemovedIncrementalFiles(File inputRoot, List<File> files,
979784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            ProcessFileCallback callback)
989784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            throws IOException {
999784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        for (File file : files) {
1009784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            File parent = file.getParentFile();
1019784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            if (inputRoot.equals(parent)) {
1029784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                callback.processRemovedOtherRootFile(file);
1039784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            } else if (layoutFolderFilter.accept(parent, parent.getName())) {
1049784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                callback.processRemovedLayoutFile(file);
1059784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            } else {
1069784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                callback.processRemovedOtherFile(parent, file);
1079784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            }
1089784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
1099784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
110a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
1119784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    private static void processAllInputFiles(ResourceInput input, ProcessFileCallback callback)
1129784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            throws IOException, XPathExpressionException, SAXException,
1139784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            ParserConfigurationException {
1149784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        FileUtils.deleteDirectory(input.getRootOutputFolder());
1159784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        Preconditions.check(input.getRootOutputFolder().mkdirs(), "out dir should be re-created");
1169784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        Preconditions.check(input.getRootInputFolder().isDirectory(), "it must be a directory");
1179784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        for (File firstLevel : input.getRootInputFolder().listFiles()) {
1189784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            if (firstLevel.isDirectory()) {
1199784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                if (layoutFolderFilter.accept(firstLevel, firstLevel.getName())) {
1209784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    callback.processLayoutFolder(firstLevel);
1219784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    for (File xmlFile : firstLevel.listFiles(xmlFileFilter)) {
1229784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                        callback.processLayoutFile(xmlFile);
1239784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    }
1249784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                } else {
1259784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    callback.processOtherFolder(firstLevel);
1269784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    for (File file : firstLevel.listFiles()) {
1279784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                        callback.processOtherFile(firstLevel, file);
1289784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    }
129a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                }
1309784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            } else {
1319784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                callback.processOtherRootFile(firstLevel);
132a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            }
1339784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
134a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
135a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
136a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
137a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    /**
138a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar     * used by the studio plugin
139a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar     */
140a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    public ResourceBundle getResourceBundle() {
141a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        return mResourceBundle;
1428e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    }
1438e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
1449784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public boolean processResources(final ResourceInput input)
1458e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            throws ParserConfigurationException, SAXException, XPathExpressionException,
1468e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            IOException {
1478e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        if (mProcessingComplete) {
1488e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            return false;
1498e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        }
1509784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        final LayoutFileParser layoutFileParser = new LayoutFileParser();
1519784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        final URI inputRootUri = input.getRootInputFolder().toURI();
1529784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        ProcessFileCallback callback = new ProcessFileCallback() {
1539784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            private File convertToOutFile(File file) {
1549784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                final String subPath = toSystemDependentPath(inputRootUri
1559784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                        .relativize(file.toURI()).getPath());
1569784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                return new File(input.getRootOutputFolder(), subPath);
1579784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            }
1589784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            @Override
1599784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            public void processLayoutFile(File file)
1609784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    throws ParserConfigurationException, SAXException, XPathExpressionException,
1619784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    IOException {
1629784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                File output = convertToOutFile(file);
1639784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                final ResourceBundle.LayoutFileBundle bindingLayout = layoutFileParser
1649784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                        .parseXml(file, output, mResourceBundle.getAppPackage(), mOriginalFileLookup);
1659784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                if (bindingLayout != null && !bindingLayout.isEmpty()) {
1669784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    mResourceBundle.addLayoutBundle(bindingLayout);
1679784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                }
1689784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            }
1699784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
1709784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            @Override
1719784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            public void processOtherFile(File parentFolder, File file) throws IOException {
1729784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                File outParent = convertToOutFile(parentFolder);
1739784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                FileUtils.copyFile(file, new File(outParent, file.getName()));
1749784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            }
1759784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
1769784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            @Override
1779784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            public void processRemovedLayoutFile(File file) {
1789784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                mResourceBundle.addRemovedFile(file);
1798e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            }
1809784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
1819784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            @Override
1829784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            public void processRemovedOtherFile(File parentFolder, File file) throws IOException {
1839784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                File outParent = convertToOutFile(parentFolder);
1849784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                FileUtils.forceDelete(new File(outParent, file.getName()));
1859784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            }
1869784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
1879784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            @Override
1889784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            public void processOtherFolder(File folder) {
1899784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                //noinspection ResultOfMethodCallIgnored
1909784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                convertToOutFile(folder).mkdirs();
1919784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            }
1929784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
1939784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            @Override
1949784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            public void processLayoutFolder(File folder) {
1959784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                //noinspection ResultOfMethodCallIgnored
1969784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                convertToOutFile(folder).mkdirs();
1979784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            }
1989784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
1999784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            @Override
2009784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            public void processOtherRootFile(File file) throws IOException {
2019784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                File outFile = convertToOutFile(file);
2029784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                if (file.isDirectory()) {
2039784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    FileUtils.copyDirectory(file, outFile);
2049784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                } else {
2059784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    FileUtils.copyFile(file, outFile);
2069784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                }
2079784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            }
2089784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
2099784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            @Override
2109784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            public void processRemovedOtherRootFile(File file) throws IOException {
2119784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                final File outFile = convertToOutFile(file);
2129784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                if (file.isDirectory()) {
2139784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    FileUtils.deleteDirectory(outFile);
2149784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                } else {
2159784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    FileUtils.deleteQuietly(outFile);
2169784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                }
2179784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            }
2189784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        };
2199784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        if (input.isIncremental()) {
2209784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            processIncrementalInputFiles(input, callback);
2219784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        } else {
2229784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            processAllInputFiles(input, callback);
2238e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        }
2248e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        mProcessingComplete = true;
2258e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        return true;
2268e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    }
2278e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
2289784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public static String toSystemDependentPath(String path) {
2299784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        if (File.separatorChar != '/') {
2309784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            path = path.replace('/', File.separatorChar);
2319784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
2329784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        return path;
2339784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
2349784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
235be83770af82294c1841eef414513653646b88dc6Yigit Boyar    public void writeLayoutInfoFiles(File xmlOutDir) throws JAXBException {
2368e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        if (mWritten) {
2378e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            return;
2388e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        }
2398e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        JAXBContext context = JAXBContext.newInstance(ResourceBundle.LayoutFileBundle.class);
2408e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        Marshaller marshaller = context.createMarshaller();
241be83770af82294c1841eef414513653646b88dc6Yigit Boyar
2428e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        for (List<ResourceBundle.LayoutFileBundle> layouts : mResourceBundle.getLayoutBundles()
2438e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount                .values()) {
2448e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            for (ResourceBundle.LayoutFileBundle layout : layouts) {
245a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                writeXmlFile(xmlOutDir, layout, marshaller);
2468e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            }
2478e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        }
2489784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        for (File file : mResourceBundle.getRemovedFiles()) {
2499784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            String exportFileName = generateExportFileName(file);
2509784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            FileUtils.deleteQuietly(new File(xmlOutDir, exportFileName));
2519784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
2528e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        mWritten = true;
2538e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    }
2548e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
255a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private void writeXmlFile(File xmlOutDir, ResourceBundle.LayoutFileBundle layout,
256a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            Marshaller marshaller) throws JAXBException {
2579784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        String filename = generateExportFileName(layout);
258a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        String xml = toXML(layout, marshaller);
259a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        mFileWriter.writeToFile(new File(xmlOutDir, filename), xml);
260a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
261a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
262a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    public String getInfoClassFullName() {
263a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        return RESOURCE_BUNDLE_PACKAGE + "." + CLASS_NAME;
264a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
265a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
266a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private String toXML(ResourceBundle.LayoutFileBundle layout, Marshaller marshaller)
2678e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            throws JAXBException {
268a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        StringWriter writer = new StringWriter();
269a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        marshaller.marshal(layout, writer);
270a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        return writer.getBuffer().toString();
271a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
272a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
273a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    /**
274a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar     * Generates a string identifier that can uniquely identify the given layout bundle.
275a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar     * This identifier can be used when we need to export data about this layout bundle.
276a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar     */
2779784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    private static String generateExportFileName(ResourceBundle.LayoutFileBundle layout) {
2789784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        return generateExportFileName(layout.getFileName(), layout.getDirectory());
2799784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
2809784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
2819784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    private static String generateExportFileName(File file) {
2829784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        final String fileName = file.getName();
2839784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        return generateExportFileName(fileName.substring(0, fileName.lastIndexOf('.')),
2849784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                file.getParentFile().getName());
2859784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
2869784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
2879784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public static String generateExportFileName(String fileName, String dirName) {
2889784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        StringBuilder name = new StringBuilder(fileName);
2899784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        name.append('-').append(dirName);
290a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        for (int i = name.length() - 1; i >= 0; i--) {
291a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            char c = name.charAt(i);
2928e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            if (c == '-') {
293a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                name.deleteCharAt(i);
294a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                c = Character.toUpperCase(name.charAt(i));
295a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                name.setCharAt(i, c);
2968e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            }
2978e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        }
2989784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        name.append(".xml");
299a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        return name.toString();
3008e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    }
3018e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
302b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar    public void writeInfoClass(/*Nullable*/ File sdkDir, File xmlOutDir,
303b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar            /*Nullable*/ File exportClassListTo) {
30408119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar        writeInfoClass(sdkDir, xmlOutDir, exportClassListTo, false, false);
305b1356339eaa6c8e967e4fc1dc283b82909a1208dYigit Boyar    }
306b1356339eaa6c8e967e4fc1dc283b82909a1208dYigit Boyar
3079784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public String getPackage() {
3089784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        return mResourceBundle.getAppPackage();
3099784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
3109784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
311b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar    public void writeInfoClass(/*Nullable*/ File sdkDir, File xmlOutDir, File exportClassListTo,
31208119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar            boolean enableDebugLogs, boolean printEncodedErrorLogs) {
313ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyar        final String sdkPath = sdkDir == null ? null : StringEscapeUtils.escapeJava(sdkDir.getAbsolutePath());
314a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        final Class annotation = BindingBuildInfo.class;
315a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        final String layoutInfoPath = StringEscapeUtils.escapeJava(xmlOutDir.getAbsolutePath());
316b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar        final String exportClassListToPath = exportClassListTo == null ? "" :
317b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar                StringEscapeUtils.escapeJava(exportClassListTo.getAbsolutePath());
318a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        String classString = "package " + RESOURCE_BUNDLE_PACKAGE + ";\n\n" +
319a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                "import " + annotation.getCanonicalName() + ";\n\n" +
320a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                "@" + annotation.getSimpleName() + "(buildId=\"" + mBuildId + "\", " +
321a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                "modulePackage=\"" + mResourceBundle.getAppPackage() + "\", " +
322ae161289a90f6ac69ffe6411ac8c09653c81d04dYigit Boyar                "sdkRoot=" + "\"" + (sdkPath == null ? "" : sdkPath) + "\"," +
323a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                "layoutInfoDir=\"" + layoutInfoPath + "\"," +
324b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar                "exportClassListTo=\"" + exportClassListToPath + "\"," +
325a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                "isLibrary=" + mIsLibrary + "," +
326b1356339eaa6c8e967e4fc1dc283b82909a1208dYigit Boyar                "minSdk=" + mMinSdk + "," +
32708119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar                "enableDebugLogs=" + enableDebugLogs + "," +
32808119ea342cb47910ca80ff646d746f00e4663ceYigit Boyar                "printEncodedError=" + printEncodedErrorLogs + ")\n" +
329a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                "public class " + CLASS_NAME + " {}\n";
330fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount        mFileWriter.writeToFile(RESOURCE_BUNDLE_PACKAGE + "." + CLASS_NAME, classString);
3318e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    }
3328e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
333a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private static final FilenameFilter layoutFolderFilter = new FilenameFilter() {
3348e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        @Override
3358e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        public boolean accept(File dir, String name) {
3368e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            return name.startsWith("layout");
3378e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        }
3388e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    };
3398e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
340a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private static final FilenameFilter xmlFileFilter = new FilenameFilter() {
3418e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        @Override
3428e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        public boolean accept(File dir, String name) {
3438e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            return name.toLowerCase().endsWith(".xml");
3448e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        }
3458e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    };
346890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar
347890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar    /**
348890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar     * Helper interface that can find the original copy of a resource XML.
349890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar     */
350890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar    public interface OriginalFileLookup {
351890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar
352890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar        /**
353890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar         * @param file The intermediate build file
354890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar         * @return The original file or null if original File cannot be found.
355890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar         */
356890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar        File getOriginalFileFor(File file);
357890b4850c628f04eb75397e427ba7074e4f9c386Yigit Boyar    }
3589784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
3599784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    /**
3609784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar     * API agnostic class to get resource changes incrementally.
3619784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar     */
3629784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public static class ResourceInput {
3639784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        private final boolean mIncremental;
3649784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        private final File mRootInputFolder;
3659784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        private final File mRootOutputFolder;
3669784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
3679784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        private List<File> mAdded = new ArrayList<File>();
3689784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        private List<File> mRemoved = new ArrayList<File>();
3699784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        private List<File> mChanged = new ArrayList<File>();
3709784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
3719784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        public ResourceInput(boolean incremental, File rootInputFolder, File rootOutputFolder) {
3729784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            mIncremental = incremental;
3739784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            mRootInputFolder = rootInputFolder;
3749784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            mRootOutputFolder = rootOutputFolder;
3759784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
3769784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
3779784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        public void added(File file) {
3789784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            mAdded.add(file);
3799784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
3809784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        public void removed(File file) {
3819784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            mRemoved.add(file);
3829784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
3839784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        public void changed(File file) {
3849784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            mChanged.add(file);
3859784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
3869784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
3879784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        public boolean shouldCopy() {
3889784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            return !mRootInputFolder.equals(mRootOutputFolder);
3899784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
3909784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
3919784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        List<File> getAdded() {
3929784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            return mAdded;
3939784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
3949784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
3959784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        List<File> getRemoved() {
3969784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            return mRemoved;
3979784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
3989784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
3999784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        List<File> getChanged() {
4009784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            return mChanged;
4019784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
4029784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
4039784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        File getRootInputFolder() {
4049784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            return mRootInputFolder;
4059784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
4069784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
4079784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        File getRootOutputFolder() {
4089784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            return mRootOutputFolder;
4099784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
4109784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
4119784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        public boolean isIncremental() {
4129784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            return mIncremental;
4139784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
4149784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
4159784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
4169784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    private interface ProcessFileCallback {
4179784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        void processLayoutFile(File file)
4189784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                throws ParserConfigurationException, SAXException, XPathExpressionException,
4199784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                IOException;
4209784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        void processOtherFile(File parentFolder, File file) throws IOException;
4219784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        void processRemovedLayoutFile(File file);
4229784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        void processRemovedOtherFile(File parentFolder, File file) throws IOException;
4239784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
4249784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        void processOtherFolder(File folder);
4259784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
4269784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        void processLayoutFolder(File folder);
4279784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
4289784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        void processOtherRootFile(File file) throws IOException;
4299784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
4309784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        void processRemovedOtherRootFile(File file) throws IOException;
4319784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
4328e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount}
433