1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.databinding.tool;
18
19import org.apache.commons.io.IOUtils;
20
21import android.databinding.tool.processing.Scope;
22import android.databinding.tool.processing.ScopedException;
23import android.databinding.tool.util.L;
24import android.databinding.tool.util.Preconditions;
25import android.databinding.tool.writer.JavaFileWriter;
26
27import java.io.File;
28import java.io.FileInputStream;
29import java.io.FileNotFoundException;
30import java.io.FileOutputStream;
31import java.io.IOException;
32import java.io.InputStream;
33import java.util.ArrayList;
34import java.util.HashMap;
35import java.util.List;
36import java.util.Map;
37import java.util.Properties;
38
39
40/**
41 * This class is used by Android Gradle plugin.
42 */
43@SuppressWarnings("unused")
44public class DataBindingBuilder {
45    Versions mVersions;
46    private final static String EXCLUDE_PATTERN = "android/databinding/layouts/*.*";
47    public String getCompilerVersion() {
48        return getVersions().compiler;
49    }
50
51    public String getBaseLibraryVersion(String compilerVersion) {
52        return getVersions().baseLibrary;
53    }
54
55    public String getLibraryVersion(String compilerVersion) {
56        return getVersions().extensions;
57    }
58
59    public String getBaseAdaptersVersion(String compilerVersion) {
60        return getVersions().extensions;
61    }
62
63    public void setPrintMachineReadableOutput(boolean machineReadableOutput) {
64        ScopedException.encodeOutput(machineReadableOutput);
65    }
66
67    public boolean getPrintMachineReadableOutput() {
68        return ScopedException.issEncodeOutput();
69    }
70
71    public void setDebugLogEnabled(boolean enableDebugLogs) {
72        L.setDebugLog(enableDebugLogs);
73    }
74
75    public boolean isDebugLogEnabled() {
76        return L.isDebugEnabled();
77    }
78
79    private Versions getVersions() {
80        if (mVersions != null) {
81            return mVersions;
82        }
83        try {
84            Properties props = new Properties();
85            InputStream stream = getClass().getResourceAsStream("/data_binding_version_info.properties");
86            try {
87                props.load(stream);
88                mVersions = new Versions(props);
89            } finally {
90                IOUtils.closeQuietly(stream);
91            }
92        } catch (IOException exception) {
93            L.e(exception, "Cannot read data binding version");
94        }
95        return mVersions;
96    }
97
98    /**
99     * Returns the list of classes that should be excluded from package task
100     *
101     * @param layoutXmlProcessor The LayoutXmlProcessor for this variant
102     * @param generatedClassListFile The location of the File into which data binding compiler wrote
103     *                               list of generated classes
104     *
105     * @return The list of classes to exclude. They are already in JNI format.
106     */
107    public List<String> getJarExcludeList(LayoutXmlProcessor layoutXmlProcessor,
108            File generatedClassListFile) {
109        List<String> excludes = new ArrayList<String>();
110        String appPkgAsClass = layoutXmlProcessor.getPackage().replace('.', '/');
111        String infoClassAsClass = layoutXmlProcessor.getInfoClassFullName().replace('.', '/');
112
113        excludes.add(infoClassAsClass + ".class");
114        excludes.add(EXCLUDE_PATTERN);
115        excludes.add(appPkgAsClass + "/BR.*");
116        excludes.add("android/databinding/DynamicUtil.class");
117        if (generatedClassListFile != null) {
118            List<String> generatedClasses = readGeneratedClasses(generatedClassListFile);
119            for (String klass : generatedClasses) {
120                excludes.add(klass.replace('.', '/') + ".class");
121            }
122        }
123        Scope.assertNoError();
124        return excludes;
125    }
126
127    private List<String> readGeneratedClasses(File generatedClassListFile) {
128        Preconditions.checkNotNull(generatedClassListFile, "Data binding exclude generated task"
129                + " is not configured properly");
130        Preconditions.check(generatedClassListFile.exists(),
131                "Generated class list does not exist %s", generatedClassListFile.getAbsolutePath());
132        FileInputStream fis = null;
133        try {
134            fis = new FileInputStream(generatedClassListFile);
135            return IOUtils.readLines(fis);
136        } catch (FileNotFoundException e) {
137            L.e(e, "Unable to read generated class list from %s",
138                    generatedClassListFile.getAbsoluteFile());
139        } catch (IOException e) {
140            L.e(e, "Unexpected exception while reading %s",
141                    generatedClassListFile.getAbsoluteFile());
142        } finally {
143            IOUtils.closeQuietly(fis);
144        }
145        L.e("Could not read data binding generated class list");
146        return null;
147    }
148
149    public JavaFileWriter createJavaFileWriter(File outFolder) {
150        return new GradleFileWriter(outFolder.getAbsolutePath());
151    }
152
153    static class GradleFileWriter extends JavaFileWriter {
154
155        private final String outputBase;
156
157        public GradleFileWriter(String outputBase) {
158            this.outputBase = outputBase;
159        }
160
161        @Override
162        public void writeToFile(String canonicalName, String contents) {
163            String asPath = canonicalName.replace('.', '/');
164            File f = new File(outputBase + "/" + asPath + ".java");
165            //noinspection ResultOfMethodCallIgnored
166            f.getParentFile().mkdirs();
167            FileOutputStream fos = null;
168            try {
169                fos = new FileOutputStream(f);
170                IOUtils.write(contents, fos);
171            } catch (IOException e) {
172                L.e(e, "cannot write file " + f.getAbsolutePath());
173            } finally {
174                IOUtils.closeQuietly(fos);
175            }
176        }
177    }
178
179    private static class Versions {
180        final String compilerCommon;
181        final String compiler;
182        final String baseLibrary;
183        final String extensions;
184
185        public Versions(Properties properties) {
186            compilerCommon = properties.getProperty("compilerCommon");
187            compiler = properties.getProperty("compiler");
188            baseLibrary = properties.getProperty("baseLibrary");
189            extensions = properties.getProperty("extensions");
190            Preconditions.checkNotNull(compilerCommon, "cannot read compiler common version");
191            Preconditions.checkNotNull(compiler, "cannot read compiler version");
192            Preconditions.checkNotNull(baseLibrary, "cannot read baseLibrary version");
193            Preconditions.checkNotNull(extensions, "cannot read extensions version");
194        }
195    }
196}