1a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar/*
2a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * Copyright (C) 2015 The Android Open Source Project
3a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar *
4a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
5a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * you may not use this file except in compliance with the License.
6a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * You may obtain a copy of the License at
7a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar *
8a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
9a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar *
10a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * Unless required by applicable law or agreed to in writing, software
11a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
12a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * See the License for the specific language governing permissions and
14a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * limitations under the License.
15a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar */
16a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
17fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountpackage android.databinding.annotationprocessor;
18b617e5da04aa910be70204afb886b1ebebb80618George Mount
19fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.Bindable;
20fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.BindingBuildInfo;
2161630faa88ee4817834d47294a0e17f19d8e1c51George Mountimport android.databinding.tool.CompilerChef.BindableHolder;
22fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.util.GenerationalClassUtil;
23fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.util.L;
242611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyarimport android.databinding.tool.util.Preconditions;
254df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyarimport android.databinding.tool.writer.BRWriter;
264df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyarimport android.databinding.tool.writer.JavaFileWriter;
27b617e5da04aa910be70204afb886b1ebebb80618George Mount
28846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mountimport java.io.Serializable;
29846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mountimport java.util.HashMap;
30b617e5da04aa910be70204afb886b1ebebb80618George Mountimport java.util.HashSet;
31a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.util.List;
32b617e5da04aa910be70204afb886b1ebebb80618George Mountimport java.util.Set;
33b617e5da04aa910be70204afb886b1ebebb80618George Mount
341b9940e612fc73202837fbe9db2f9035f307b5d1George Mountimport javax.annotation.processing.ProcessingEnvironment;
35b617e5da04aa910be70204afb886b1ebebb80618George Mountimport javax.annotation.processing.RoundEnvironment;
36b617e5da04aa910be70204afb886b1ebebb80618George Mountimport javax.lang.model.element.Element;
378e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mountimport javax.lang.model.element.ElementKind;
38b617e5da04aa910be70204afb886b1ebebb80618George Mountimport javax.lang.model.element.ExecutableElement;
39b617e5da04aa910be70204afb886b1ebebb80618George Mountimport javax.lang.model.element.Name;
40b617e5da04aa910be70204afb886b1ebebb80618George Mountimport javax.lang.model.element.TypeElement;
41b617e5da04aa910be70204afb886b1ebebb80618George Mountimport javax.lang.model.element.VariableElement;
42b617e5da04aa910be70204afb886b1ebebb80618George Mountimport javax.lang.model.type.TypeKind;
4386558cbd9a58387d6b88a8d032eb2d1e33353030George Mountimport javax.lang.model.util.Types;
44b617e5da04aa910be70204afb886b1ebebb80618George Mount
45a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar// binding app info and library info are necessary to trigger this.
4661630faa88ee4817834d47294a0e17f19d8e1c51George Mountpublic class ProcessBindable extends ProcessDataBinding.ProcessingStep implements BindableHolder {
47a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    Intermediate mProperties;
48b1356339eaa6c8e967e4fc1dc283b82909a1208dYigit Boyar    HashMap<String, HashSet<String>> mLayoutVariables = new HashMap<String, HashSet<String>>();
491b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
501b9940e612fc73202837fbe9db2f9035f307b5d1George Mount    @Override
51a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    public boolean onHandleStep(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv,
52a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            BindingBuildInfo buildInfo) {
53a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        if (mProperties == null) {
54a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            mProperties = new IntermediateV1(buildInfo.modulePackage());
5561630faa88ee4817834d47294a0e17f19d8e1c51George Mount            mergeLayoutVariables();
5661630faa88ee4817834d47294a0e17f19d8e1c51George Mount            mLayoutVariables.clear();
5786558cbd9a58387d6b88a8d032eb2d1e33353030George Mount            TypeElement observableType = processingEnv.getElementUtils().
5886558cbd9a58387d6b88a8d032eb2d1e33353030George Mount                    getTypeElement("android.databinding.Observable");
5986558cbd9a58387d6b88a8d032eb2d1e33353030George Mount            Types typeUtils = processingEnv.getTypeUtils();
6061630faa88ee4817834d47294a0e17f19d8e1c51George Mount            for (Element element : AnnotationUtil
6161630faa88ee4817834d47294a0e17f19d8e1c51George Mount                    .getElementsAnnotatedWith(roundEnv, Bindable.class)) {
6261630faa88ee4817834d47294a0e17f19d8e1c51George Mount                Element enclosingElement = element.getEnclosingElement();
6361630faa88ee4817834d47294a0e17f19d8e1c51George Mount                ElementKind kind = enclosingElement.getKind();
6461630faa88ee4817834d47294a0e17f19d8e1c51George Mount                if (kind != ElementKind.CLASS && kind != ElementKind.INTERFACE) {
6561630faa88ee4817834d47294a0e17f19d8e1c51George Mount                    L.e("Bindable must be on a member field or method. The enclosing type is %s",
6661630faa88ee4817834d47294a0e17f19d8e1c51George Mount                            enclosingElement.getKind());
6761630faa88ee4817834d47294a0e17f19d8e1c51George Mount                }
6861630faa88ee4817834d47294a0e17f19d8e1c51George Mount                TypeElement enclosing = (TypeElement) enclosingElement;
6986558cbd9a58387d6b88a8d032eb2d1e33353030George Mount                if (!typeUtils.isAssignable(enclosing.asType(), observableType.asType())) {
7086558cbd9a58387d6b88a8d032eb2d1e33353030George Mount                    L.e("Bindable must be on a member in an Observable class. %s is not Observable",
7186558cbd9a58387d6b88a8d032eb2d1e33353030George Mount                            enclosingElement.getSimpleName());
7286558cbd9a58387d6b88a8d032eb2d1e33353030George Mount                }
7361630faa88ee4817834d47294a0e17f19d8e1c51George Mount                String name = getPropertyName(element);
7461630faa88ee4817834d47294a0e17f19d8e1c51George Mount                if (name != null) {
7561630faa88ee4817834d47294a0e17f19d8e1c51George Mount                    Preconditions
7661630faa88ee4817834d47294a0e17f19d8e1c51George Mount                            .checkNotNull(mProperties, "Must receive app / library info before "
7761630faa88ee4817834d47294a0e17f19d8e1c51George Mount                                    + "Bindable fields.");
7861630faa88ee4817834d47294a0e17f19d8e1c51George Mount                    mProperties.addProperty(enclosing.getQualifiedName().toString(), name);
7961630faa88ee4817834d47294a0e17f19d8e1c51George Mount                }
808e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            }
814a0f06e92a846a9bfd8986443e8dae7459157f8fGeorge Mount            GenerationalClassUtil.writeIntermediateFile(processingEnv,
824a0f06e92a846a9bfd8986443e8dae7459157f8fGeorge Mount                    mProperties.getPackage(),
834a0f06e92a846a9bfd8986443e8dae7459157f8fGeorge Mount                    createIntermediateFileName(mProperties.getPackage()), mProperties);
844a0f06e92a846a9bfd8986443e8dae7459157f8fGeorge Mount            generateBRClasses(!buildInfo.isLibrary(), mProperties.getPackage());
85b617e5da04aa910be70204afb886b1ebebb80618George Mount        }
86a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        return false;
87a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
88a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
89a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    @Override
9061630faa88ee4817834d47294a0e17f19d8e1c51George Mount    public void addVariable(String variableName, String containingClassName) {
9161630faa88ee4817834d47294a0e17f19d8e1c51George Mount        HashSet<String> variableNames = mLayoutVariables.get(containingClassName);
9261630faa88ee4817834d47294a0e17f19d8e1c51George Mount        if (variableNames == null) {
93b1356339eaa6c8e967e4fc1dc283b82909a1208dYigit Boyar            variableNames = new HashSet<String>();
9461630faa88ee4817834d47294a0e17f19d8e1c51George Mount            mLayoutVariables.put(containingClassName, variableNames);
9561630faa88ee4817834d47294a0e17f19d8e1c51George Mount        }
9661630faa88ee4817834d47294a0e17f19d8e1c51George Mount        variableNames.add(variableName);
9761630faa88ee4817834d47294a0e17f19d8e1c51George Mount    }
9861630faa88ee4817834d47294a0e17f19d8e1c51George Mount
9961630faa88ee4817834d47294a0e17f19d8e1c51George Mount    @Override
100a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    public void onProcessingOver(RoundEnvironment roundEnvironment,
101a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) {
102b617e5da04aa910be70204afb886b1ebebb80618George Mount    }
103b617e5da04aa910be70204afb886b1ebebb80618George Mount
104a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private String createIntermediateFileName(String appPkg) {
105e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar        return appPkg + GenerationalClassUtil.ExtensionFilter.BR.getExtension();
106a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
107a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
108a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private void generateBRClasses(boolean useFinalFields, String pkg) {
109a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        L.d("************* Generating BR file %s. use final: %s", pkg, useFinalFields);
110b1356339eaa6c8e967e4fc1dc283b82909a1208dYigit Boyar        HashSet<String> properties = new HashSet<String>();
111a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        mProperties.captureProperties(properties);
112a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        List<Intermediate> previousIntermediates = loadPreviousBRFiles();
113a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        for (Intermediate intermediate : previousIntermediates) {
114a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            intermediate.captureProperties(properties);
115a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
1164df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar        final JavaFileWriter writer = getWriter();
1174df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar        BRWriter brWriter = new BRWriter(properties, useFinalFields);
1184df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar        writer.writeToFile(pkg + ".BR", brWriter.write(pkg));
1194df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar        //writeBRClass(useFinalFields, pkg, properties);
120a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        if (useFinalFields) {
121a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            // generate BR for all previous packages
122a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            for (Intermediate intermediate : previousIntermediates) {
1234df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar                writer.writeToFile(intermediate.getPackage() + ".BR",
1244df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar                        brWriter.write(intermediate.getPackage()));
12543596c2b2997e40b709627419732100d78a62ff0Yigit Boyar            }
126b617e5da04aa910be70204afb886b1ebebb80618George Mount        }
1274df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar        mCallback.onBrWriterReady(brWriter);
128a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
129a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
130b617e5da04aa910be70204afb886b1ebebb80618George Mount    private String getPropertyName(Element element) {
131b617e5da04aa910be70204afb886b1ebebb80618George Mount        switch (element.getKind()) {
132b617e5da04aa910be70204afb886b1ebebb80618George Mount            case FIELD:
133b617e5da04aa910be70204afb886b1ebebb80618George Mount                return stripPrefixFromField((VariableElement) element);
134b617e5da04aa910be70204afb886b1ebebb80618George Mount            case METHOD:
135b617e5da04aa910be70204afb886b1ebebb80618George Mount                return stripPrefixFromMethod((ExecutableElement) element);
136b617e5da04aa910be70204afb886b1ebebb80618George Mount            default:
137a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                L.e("@Bindable is not allowed on %s", element.getKind());
138b617e5da04aa910be70204afb886b1ebebb80618George Mount                return null;
139b617e5da04aa910be70204afb886b1ebebb80618George Mount        }
140b617e5da04aa910be70204afb886b1ebebb80618George Mount    }
141b617e5da04aa910be70204afb886b1ebebb80618George Mount
142b617e5da04aa910be70204afb886b1ebebb80618George Mount    private static String stripPrefixFromField(VariableElement element) {
143b617e5da04aa910be70204afb886b1ebebb80618George Mount        Name name = element.getSimpleName();
144b617e5da04aa910be70204afb886b1ebebb80618George Mount        if (name.length() >= 2) {
145b617e5da04aa910be70204afb886b1ebebb80618George Mount            char firstChar = name.charAt(0);
146b617e5da04aa910be70204afb886b1ebebb80618George Mount            char secondChar = name.charAt(1);
1477201118b5554426cea3fcd07e4fd96e999d3eea9George Mount            if (name.length() > 2 && firstChar == 'm' && secondChar == '_') {
1487201118b5554426cea3fcd07e4fd96e999d3eea9George Mount                char thirdChar = name.charAt(2);
1497201118b5554426cea3fcd07e4fd96e999d3eea9George Mount                if (Character.isJavaIdentifierStart(thirdChar)) {
1507201118b5554426cea3fcd07e4fd96e999d3eea9George Mount                    return "" + Character.toLowerCase(thirdChar) +
1517201118b5554426cea3fcd07e4fd96e999d3eea9George Mount                            name.subSequence(3, name.length());
1527201118b5554426cea3fcd07e4fd96e999d3eea9George Mount                }
1537201118b5554426cea3fcd07e4fd96e999d3eea9George Mount            } else if ((firstChar == 'm' && Character.isUpperCase(secondChar)) ||
154b617e5da04aa910be70204afb886b1ebebb80618George Mount                    (firstChar == '_' && Character.isJavaIdentifierStart(secondChar))) {
155b617e5da04aa910be70204afb886b1ebebb80618George Mount                return "" + Character.toLowerCase(secondChar) + name.subSequence(2, name.length());
156b617e5da04aa910be70204afb886b1ebebb80618George Mount            }
157b617e5da04aa910be70204afb886b1ebebb80618George Mount        }
158b617e5da04aa910be70204afb886b1ebebb80618George Mount        return name.toString();
159b617e5da04aa910be70204afb886b1ebebb80618George Mount    }
160b617e5da04aa910be70204afb886b1ebebb80618George Mount
161b617e5da04aa910be70204afb886b1ebebb80618George Mount    private String stripPrefixFromMethod(ExecutableElement element) {
162b617e5da04aa910be70204afb886b1ebebb80618George Mount        Name name = element.getSimpleName();
163b617e5da04aa910be70204afb886b1ebebb80618George Mount        CharSequence propertyName;
164b617e5da04aa910be70204afb886b1ebebb80618George Mount        if (isGetter(element) || isSetter(element)) {
165b617e5da04aa910be70204afb886b1ebebb80618George Mount            propertyName = name.subSequence(3, name.length());
166b617e5da04aa910be70204afb886b1ebebb80618George Mount        } else if (isBooleanGetter(element)) {
167b617e5da04aa910be70204afb886b1ebebb80618George Mount            propertyName = name.subSequence(2, name.length());
168b617e5da04aa910be70204afb886b1ebebb80618George Mount        } else {
169a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            L.e("@Bindable associated with method must follow JavaBeans convention %s", element);
170b617e5da04aa910be70204afb886b1ebebb80618George Mount            return null;
171b617e5da04aa910be70204afb886b1ebebb80618George Mount        }
172b617e5da04aa910be70204afb886b1ebebb80618George Mount        char firstChar = propertyName.charAt(0);
173b617e5da04aa910be70204afb886b1ebebb80618George Mount        return "" + Character.toLowerCase(firstChar) +
174b617e5da04aa910be70204afb886b1ebebb80618George Mount                propertyName.subSequence(1, propertyName.length());
175b617e5da04aa910be70204afb886b1ebebb80618George Mount    }
176b617e5da04aa910be70204afb886b1ebebb80618George Mount
17761630faa88ee4817834d47294a0e17f19d8e1c51George Mount    private void mergeLayoutVariables() {
17861630faa88ee4817834d47294a0e17f19d8e1c51George Mount        for (String containingClass : mLayoutVariables.keySet()) {
17961630faa88ee4817834d47294a0e17f19d8e1c51George Mount            for (String variable : mLayoutVariables.get(containingClass)) {
18061630faa88ee4817834d47294a0e17f19d8e1c51George Mount                mProperties.addProperty(containingClass, variable);
18161630faa88ee4817834d47294a0e17f19d8e1c51George Mount            }
18261630faa88ee4817834d47294a0e17f19d8e1c51George Mount        }
18361630faa88ee4817834d47294a0e17f19d8e1c51George Mount    }
18461630faa88ee4817834d47294a0e17f19d8e1c51George Mount
185b617e5da04aa910be70204afb886b1ebebb80618George Mount    private static boolean prefixes(CharSequence sequence, String prefix) {
186b617e5da04aa910be70204afb886b1ebebb80618George Mount        boolean prefixes = false;
187b617e5da04aa910be70204afb886b1ebebb80618George Mount        if (sequence.length() > prefix.length()) {
188b617e5da04aa910be70204afb886b1ebebb80618George Mount            int count = prefix.length();
189b617e5da04aa910be70204afb886b1ebebb80618George Mount            prefixes = true;
190b617e5da04aa910be70204afb886b1ebebb80618George Mount            for (int i = 0; i < count; i++) {
191b617e5da04aa910be70204afb886b1ebebb80618George Mount                if (sequence.charAt(i) != prefix.charAt(i)) {
192b617e5da04aa910be70204afb886b1ebebb80618George Mount                    prefixes = false;
193b617e5da04aa910be70204afb886b1ebebb80618George Mount                    break;
194b617e5da04aa910be70204afb886b1ebebb80618George Mount                }
195b617e5da04aa910be70204afb886b1ebebb80618George Mount            }
196b617e5da04aa910be70204afb886b1ebebb80618George Mount        }
197b617e5da04aa910be70204afb886b1ebebb80618George Mount        return prefixes;
198b617e5da04aa910be70204afb886b1ebebb80618George Mount    }
199b617e5da04aa910be70204afb886b1ebebb80618George Mount
200b617e5da04aa910be70204afb886b1ebebb80618George Mount    private static boolean isGetter(ExecutableElement element) {
201b617e5da04aa910be70204afb886b1ebebb80618George Mount        Name name = element.getSimpleName();
202b617e5da04aa910be70204afb886b1ebebb80618George Mount        return prefixes(name, "get") &&
203b617e5da04aa910be70204afb886b1ebebb80618George Mount                Character.isJavaIdentifierStart(name.charAt(3)) &&
204b617e5da04aa910be70204afb886b1ebebb80618George Mount                element.getParameters().isEmpty() &&
205b617e5da04aa910be70204afb886b1ebebb80618George Mount                element.getReturnType().getKind() != TypeKind.VOID;
206b617e5da04aa910be70204afb886b1ebebb80618George Mount    }
207b617e5da04aa910be70204afb886b1ebebb80618George Mount
208b617e5da04aa910be70204afb886b1ebebb80618George Mount    private static boolean isSetter(ExecutableElement element) {
209b617e5da04aa910be70204afb886b1ebebb80618George Mount        Name name = element.getSimpleName();
210b617e5da04aa910be70204afb886b1ebebb80618George Mount        return prefixes(name, "set") &&
211b617e5da04aa910be70204afb886b1ebebb80618George Mount                Character.isJavaIdentifierStart(name.charAt(3)) &&
212b617e5da04aa910be70204afb886b1ebebb80618George Mount                element.getParameters().size() == 1 &&
213b617e5da04aa910be70204afb886b1ebebb80618George Mount                element.getReturnType().getKind() == TypeKind.VOID;
214b617e5da04aa910be70204afb886b1ebebb80618George Mount    }
215b617e5da04aa910be70204afb886b1ebebb80618George Mount
216b617e5da04aa910be70204afb886b1ebebb80618George Mount    private static boolean isBooleanGetter(ExecutableElement element) {
217b617e5da04aa910be70204afb886b1ebebb80618George Mount        Name name = element.getSimpleName();
218b617e5da04aa910be70204afb886b1ebebb80618George Mount        return prefixes(name, "is") &&
219b617e5da04aa910be70204afb886b1ebebb80618George Mount                Character.isJavaIdentifierStart(name.charAt(2)) &&
220b617e5da04aa910be70204afb886b1ebebb80618George Mount                element.getParameters().isEmpty() &&
221b617e5da04aa910be70204afb886b1ebebb80618George Mount                element.getReturnType().getKind() == TypeKind.BOOLEAN;
222b617e5da04aa910be70204afb886b1ebebb80618George Mount    }
223612997fe2e41366573855f56898b27d4c8787244George Mount
224a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private List<Intermediate> loadPreviousBRFiles() {
225a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        return GenerationalClassUtil
226e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                .loadObjects(GenerationalClassUtil.ExtensionFilter.BR);
227612997fe2e41366573855f56898b27d4c8787244George Mount    }
228612997fe2e41366573855f56898b27d4c8787244George Mount
229a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private interface Intermediate extends Serializable {
230846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount
231846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount        void captureProperties(Set<String> properties);
232846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount
233846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount        void addProperty(String className, String propertyName);
234a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
23561630faa88ee4817834d47294a0e17f19d8e1c51George Mount        boolean hasValues();
23661630faa88ee4817834d47294a0e17f19d8e1c51George Mount
237a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        String getPackage();
238846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount    }
239846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount
240846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount    private static class IntermediateV1 implements Serializable, Intermediate {
241846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount
242a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        private static final long serialVersionUID = 2L;
243a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
244a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        private String mPackage;
245b1356339eaa6c8e967e4fc1dc283b82909a1208dYigit Boyar        private final HashMap<String, HashSet<String>> mProperties = new HashMap<String, HashSet<String>>();
246846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount
247a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        public IntermediateV1(String aPackage) {
248a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            mPackage = aPackage;
249a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
250a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
251846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount        @Override
252846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount        public void captureProperties(Set<String> properties) {
253846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount            for (HashSet<String> propertySet : mProperties.values()) {
254846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount                properties.addAll(propertySet);
255846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount            }
256846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount        }
257846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount
258846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount        @Override
259846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount        public void addProperty(String className, String propertyName) {
260846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount            HashSet<String> properties = mProperties.get(className);
261846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount            if (properties == null) {
262b1356339eaa6c8e967e4fc1dc283b82909a1208dYigit Boyar                properties = new HashSet<String>();
263846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount                mProperties.put(className, properties);
264846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount            }
265846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount            properties.add(propertyName);
266846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount        }
267a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
268a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        @Override
26961630faa88ee4817834d47294a0e17f19d8e1c51George Mount        public boolean hasValues() {
27061630faa88ee4817834d47294a0e17f19d8e1c51George Mount            return !mProperties.isEmpty();
27161630faa88ee4817834d47294a0e17f19d8e1c51George Mount        }
27261630faa88ee4817834d47294a0e17f19d8e1c51George Mount
27361630faa88ee4817834d47294a0e17f19d8e1c51George Mount        @Override
274a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        public String getPackage() {
275a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            return mPackage;
276a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
277846795ea3e69eb482968cfea3312df04787c9bcfGeorge Mount    }
278b617e5da04aa910be70204afb886b1ebebb80618George Mount}
279