197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar/*
297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * Copyright (C) 2015 The Android Open Source Project
397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar *
497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * you may not use this file except in compliance with the License.
697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * You may obtain a copy of the License at
797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar *
897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar *
1097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * Unless required by applicable law or agreed to in writing, software
1197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
1297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * See the License for the specific language governing permissions and
1497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * limitations under the License.
1597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar */
1697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
17fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountpackage android.databinding.annotationprocessor;
18e6c6d3bf4fac3fa11c5780cfd3bc14cdb0caaea1George Mount
194ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Guptaimport com.google.common.base.Joiner;
204ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta
21a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport org.apache.commons.io.FileUtils;
221b9940e612fc73202837fbe9db2f9035f307b5d1George Mountimport org.apache.commons.io.IOUtils;
231b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
24fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.BindingBuildInfo;
25fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.CompilerChef;
2628e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyarimport android.databinding.tool.LayoutXmlProcessor;
27fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.SdkUtil;
28fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.store.ResourceBundle;
29fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.util.GenerationalClassUtil;
30fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.util.L;
3128e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyarimport android.databinding.tool.util.Preconditions;
324ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Guptaimport android.databinding.tool.util.StringUtils;
331b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
3497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport java.io.File;
35a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.io.FilenameFilter;
36e6c6d3bf4fac3fa11c5780cfd3bc14cdb0caaea1George Mountimport java.io.IOException;
37a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.io.InputStream;
38a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.io.Serializable;
3928e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyarimport java.util.ArrayList;
40a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.util.HashMap;
4128e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyarimport java.util.HashSet;
42a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.util.List;
43a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.util.Map;
44b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyarimport java.util.Set;
45e6c6d3bf4fac3fa11c5780cfd3bc14cdb0caaea1George Mount
46a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport javax.annotation.processing.ProcessingEnvironment;
47e6c6d3bf4fac3fa11c5780cfd3bc14cdb0caaea1George Mountimport javax.annotation.processing.RoundEnvironment;
488e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport javax.xml.bind.JAXBContext;
498e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport javax.xml.bind.JAXBException;
508e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport javax.xml.bind.Unmarshaller;
51e6c6d3bf4fac3fa11c5780cfd3bc14cdb0caaea1George Mount
52a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarpublic class ProcessExpressions extends ProcessDataBinding.ProcessingStep {
534df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar    public ProcessExpressions() {
5461630faa88ee4817834d47294a0e17f19d8e1c51George Mount    }
5561630faa88ee4817834d47294a0e17f19d8e1c51George Mount
56a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    @Override
57a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    public boolean onHandleStep(RoundEnvironment roundEnvironment,
5828e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo)
5928e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            throws JAXBException {
60a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        ResourceBundle resourceBundle;
61a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        SdkUtil.initialize(buildInfo.minSdk(), new File(buildInfo.sdkRoot()));
62a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        resourceBundle = new ResourceBundle(buildInfo.modulePackage());
6328e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        List<IntermediateV2> intermediateList = loadDependencyIntermediates();
6428e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        for (Intermediate intermediate : intermediateList) {
6528e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            try {
6628e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                intermediate.appendTo(resourceBundle);
6728e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            } catch (Throwable throwable) {
6828e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                L.e(throwable, "unable to prepare resource bundle");
6928e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            }
7028e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        }
7128e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar
7228e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        IntermediateV2 mine = createIntermediateFromLayouts(buildInfo.layoutInfoDir(),
7328e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                intermediateList);
74a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        if (mine != null) {
7528e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            mine.updateOverridden(resourceBundle);
76a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            intermediateList.add(mine);
77a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            saveIntermediate(processingEnvironment, buildInfo, mine);
7828e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            mine.appendTo(resourceBundle);
79a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
80a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        // generate them here so that bindable parser can read
81a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        try {
8228e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            writeResourceBundle(resourceBundle, buildInfo.isLibrary(), buildInfo.minSdk(),
8328e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                    buildInfo.exportClassListTo());
84a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        } catch (Throwable t) {
85a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            L.e(t, "cannot generate view binders");
86a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
87a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        return true;
88a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
89a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
9028e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar    private List<IntermediateV2> loadDependencyIntermediates() {
911907fd71019ef16d4fc2953d56f1ec0702275aa0Yigit Boyar        final List<Intermediate> original = GenerationalClassUtil.loadObjects(
9228e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                GenerationalClassUtil.ExtensionFilter.LAYOUT);
9328e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        final List<IntermediateV2> upgraded = new ArrayList<IntermediateV2>(original.size());
9428e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        for (Intermediate intermediate : original) {
9528e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            final Intermediate updatedIntermediate = intermediate.upgrade();
9628e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            Preconditions.check(updatedIntermediate instanceof IntermediateV2, "Incompatible data"
9728e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                    + " binding dependency. Please update your dependencies or recompile them with"
9828e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                    + " application module's data binding version.");
9928e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            //noinspection ConstantConditions
10028e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            upgraded.add((IntermediateV2) updatedIntermediate);
10128e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        }
10228e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        return upgraded;
10328e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar    }
10428e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar
105a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private void saveIntermediate(ProcessingEnvironment processingEnvironment,
10628e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            BindingBuildInfo buildInfo, IntermediateV2 intermediate) {
107a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        GenerationalClassUtil.writeIntermediateFile(processingEnvironment,
108e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                buildInfo.modulePackage(), buildInfo.modulePackage() +
109e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                        GenerationalClassUtil.ExtensionFilter.LAYOUT.getExtension(),
110a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                intermediate);
111a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
112a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
113a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    @Override
114a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    public void onProcessingOver(RoundEnvironment roundEnvironment,
115a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) {
116a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
117a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
11828e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar    private IntermediateV2 createIntermediateFromLayouts(String layoutInfoFolderPath,
11928e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            List<IntermediateV2> intermediateList) {
12028e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        final Set<String> excludeList = new HashSet<String>();
12128e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        for (IntermediateV2 lib : intermediateList) {
12228e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            excludeList.addAll(lib.mLayoutInfoMap.keySet());
123a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
124a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        final File layoutInfoFolder = new File(layoutInfoFolderPath);
125a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        if (!layoutInfoFolder.isDirectory()) {
126a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            L.d("layout info folder does not exist, skipping for %s", layoutInfoFolderPath);
127a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            return null;
128a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
12928e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        IntermediateV2 result = new IntermediateV2();
130a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        for (File layoutFile : layoutInfoFolder.listFiles(new FilenameFilter() {
131a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            @Override
132a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            public boolean accept(File dir, String name) {
13328e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                return name.endsWith(".xml") && !excludeList.contains(name);
13400da715547ee7d5d38a3b8576090ca427a94daa5George Mount            }
135a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        })) {
136a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            try {
137a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                result.addEntry(layoutFile.getName(), FileUtils.readFileToString(layoutFile));
138a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            } catch (IOException e) {
139a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                L.e(e, "cannot load layout file information. Try a clean build");
1401b9940e612fc73202837fbe9db2f9035f307b5d1George Mount            }
141a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
142a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        return result;
143a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
144a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
145e7c2a5e45d7651899790bd347da635875f9c73fbGeorge Mount    private void writeResourceBundle(ResourceBundle resourceBundle, boolean forLibraryModule,
1464df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar            final int minSdk, String exportClassNamesTo)
147a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            throws JAXBException {
1484df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar        final CompilerChef compilerChef = CompilerChef.createChef(resourceBundle, getWriter());
149a649c6ce3f6eb4882221e77a1d27e0d28c4c13a0George Mount        compilerChef.sealModels();
150a649c6ce3f6eb4882221e77a1d27e0d28c4c13a0George Mount        compilerChef.writeComponent();
151a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        if (compilerChef.hasAnythingToGenerate()) {
152dea555cf42dc3583604699c8c018d22681f56166George Mount            compilerChef.writeViewBinderInterfaces(forLibraryModule);
153a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            if (!forLibraryModule) {
15496e1c821dd446d1ed78f8ae61131550588f60a24George Mount                compilerChef.writeViewBinders(minSdk);
1551b9940e612fc73202837fbe9db2f9035f307b5d1George Mount            }
1561b9940e612fc73202837fbe9db2f9035f307b5d1George Mount        }
157b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar        if (forLibraryModule && exportClassNamesTo == null) {
158b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar            L.e("When compiling a library module, build info must include exportClassListTo path");
159b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar        }
160b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar        if (forLibraryModule) {
161b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar            Set<String> classNames = compilerChef.getWrittenClassNames();
1624ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta            String out = Joiner.on(StringUtils.LINE_SEPARATOR).join(classNames);
163b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar            L.d("Writing list of classes to %s . \nList:%s", exportClassNamesTo, out);
164b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar            try {
1654df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar                //noinspection ConstantConditions
1664df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar                FileUtils.write(new File(exportClassNamesTo), out);
167b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar            } catch (IOException e) {
168b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar                L.e(e, "Cannot create list of written classes");
169b6887f1479c3ecec38a7989748ef33de1fbcd973Yigit Boyar            }
1701bbaf7cdf7f9d93ae09365192abb2288cf0dfb41George Mount        }
1714df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar        mCallback.onChefReady(compilerChef, forLibraryModule, minSdk);
172a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
173e6c6d3bf4fac3fa11c5780cfd3bc14cdb0caaea1George Mount
17428e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar    public interface Intermediate extends Serializable {
175a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
176a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        Intermediate upgrade();
177a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
17828e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        void appendTo(ResourceBundle resourceBundle) throws Throwable;
1798e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount    }
1808e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
181a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    public static class IntermediateV1 implements Intermediate {
182a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
183a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        transient Unmarshaller mUnmarshaller;
184a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
185a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        // name to xml content map
186b1356339eaa6c8e967e4fc1dc283b82909a1208dYigit Boyar        Map<String, String> mLayoutInfoMap = new HashMap<String, String>();
187a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
188a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        @Override
189a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        public Intermediate upgrade() {
19028e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            final IntermediateV2 updated = new IntermediateV2();
19128e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            updated.mLayoutInfoMap = mLayoutInfoMap;
19228e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            updated.mUnmarshaller = mUnmarshaller;
19328e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            return updated;
194a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
195a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
196a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        @Override
197a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        public void appendTo(ResourceBundle resourceBundle) throws JAXBException {
198a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            if (mUnmarshaller == null) {
199a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                JAXBContext context = JAXBContext
200a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                        .newInstance(ResourceBundle.LayoutFileBundle.class);
201a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                mUnmarshaller = context.createUnmarshaller();
2028e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            }
203a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            for (String content : mLayoutInfoMap.values()) {
204a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                final InputStream is = IOUtils.toInputStream(content);
205a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                try {
206a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                    final ResourceBundle.LayoutFileBundle bundle
207a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                            = (ResourceBundle.LayoutFileBundle) mUnmarshaller.unmarshal(is);
2080390898cf7c4fcad255e8cfd6802f722b516cb2cGeorge Mount                    resourceBundle.addLayoutBundle(bundle);
209a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                    L.d("loaded layout info file %s", bundle);
210a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                } finally {
211a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                    IOUtils.closeQuietly(is);
2121b9940e612fc73202837fbe9db2f9035f307b5d1George Mount                }
2131b9940e612fc73202837fbe9db2f9035f307b5d1George Mount            }
214a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
2158e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
216a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        public void addEntry(String name, String contents) {
217a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            mLayoutInfoMap.put(name, contents);
2181b9940e612fc73202837fbe9db2f9035f307b5d1George Mount        }
2198e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount
22028e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        // keeping the method to match deserialized structure
22128e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        @SuppressWarnings("unused")
222a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        public void removeOverridden(List<Intermediate> existing) {
22328e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        }
22428e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar    }
22528e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar
22628e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar    public static class IntermediateV2 extends IntermediateV1 {
22728e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        // specify so that we can define updates ourselves.
22828e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        private static final long serialVersionUID = 2L;
22928e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        @Override
23028e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        public void appendTo(ResourceBundle resourceBundle) throws JAXBException {
23128e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            for (Map.Entry<String, String> entry : mLayoutInfoMap.entrySet()) {
23228e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                final InputStream is = IOUtils.toInputStream(entry.getValue());
23328e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                try {
23428e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                    final ResourceBundle.LayoutFileBundle bundle = ResourceBundle.LayoutFileBundle
23528e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                            .fromXML(is);
23628e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                    resourceBundle.addLayoutBundle(bundle);
23728e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                    L.d("loaded layout info file %s", bundle);
23828e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                } finally {
23928e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                    IOUtils.closeQuietly(is);
24028e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                }
24128e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            }
24228e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        }
24328e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar
24428e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        /**
24528e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar         * if a layout is overridden from a module (which happens when layout is auto-generated),
24628e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar         * we need to update its contents from the class that overrides it.
24728e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar         * This must be done before this bundle is saved, otherwise, it will not be recognized
24828e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar         * when it is used in another project.
24928e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar         */
25028e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar        public void updateOverridden(ResourceBundle bundle) throws JAXBException {
25128e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            // When a layout is copied from inherited module, it is eleminated while reading
25228e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            // info files. (createIntermediateFromLayouts).
25328e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            // Build process may also duplicate some files at compile time. This is where
25428e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            // we detect those copies and force inherit their module and classname information.
25528e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            final HashMap<String, List<ResourceBundle.LayoutFileBundle>> bundles = bundle
25628e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                    .getLayoutBundles();
25728e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            for (Map.Entry<String, String> info : mLayoutInfoMap.entrySet()) {
25828e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                String key = LayoutXmlProcessor.exportLayoutNameFromInfoFileName(info.getKey());
25928e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                final List<ResourceBundle.LayoutFileBundle> existingList = bundles.get(key);
26028e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                if (existingList != null && !existingList.isEmpty()) {
26128e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                    ResourceBundle.LayoutFileBundle myBundle = ResourceBundle.LayoutFileBundle
26228e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                            .fromXML(IOUtils.toInputStream(info.getValue()));
26328e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                    final ResourceBundle.LayoutFileBundle inheritFrom = existingList.get(0);
26428e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                    myBundle.inheritConfigurationFrom(inheritFrom);
26528e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                    L.d("inheriting data for %s (%s) from %s", info.getKey(), key, inheritFrom);
26628e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                    mLayoutInfoMap.put(info.getKey(), myBundle.toXML());
267a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                }
268a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            }
26900da715547ee7d5d38a3b8576090ca427a94daa5George Mount        }
270e6c6d3bf4fac3fa11c5780cfd3bc14cdb0caaea1George Mount    }
271e6c6d3bf4fac3fa11c5780cfd3bc14cdb0caaea1George Mount}
272