FieldAccessExpr.java revision 716ba89e7f459f49ea85070d4710c1d79d715298
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.expr; 18 19import com.google.common.base.Preconditions; 20 21import android.databinding.tool.reflection.Callable; 22import android.databinding.tool.reflection.Callable.Type; 23import android.databinding.tool.reflection.ModelAnalyzer; 24import android.databinding.tool.reflection.ModelClass; 25import android.databinding.tool.reflection.ModelMethod; 26import android.databinding.tool.util.L; 27 28import java.util.ArrayList; 29import java.util.List; 30 31public class FieldAccessExpr extends Expr { 32 String mName; 33 Callable mGetter; 34 final boolean mIsObservableField; 35 private List<ModelMethod> mListenerMethods; 36 private List<ModelMethod> mCalledMethods; 37 private List<ModelClass> mListenerTypes; 38 private List<ModelMethod> mPotentialListeners; 39 40 FieldAccessExpr(Expr parent, String name) { 41 super(parent); 42 mName = name; 43 mIsObservableField = false; 44 } 45 46 FieldAccessExpr(Expr parent, String name, boolean isObservableField) { 47 super(parent); 48 mName = name; 49 mIsObservableField = isObservableField; 50 } 51 52 public Expr getChild() { 53 return getChildren().get(0); 54 } 55 56 public Callable getGetter() { 57 if (mGetter == null) { 58 getResolvedType(); 59 } 60 return mGetter; 61 } 62 63 public List<ModelMethod> getListenerMethods() { 64 return mListenerMethods; 65 } 66 67 public List<ModelMethod> getCalledMethods() { 68 return mCalledMethods; 69 } 70 71 public List<ModelClass> getListenerTypes() { return mListenerTypes; } 72 73 public boolean isListener() { 74 return mListenerMethods != null && !mListenerMethods.isEmpty(); 75 } 76 77 public int getMinApi() { 78 if (isListener()) { 79 int minApi = 1; 80 for (ModelClass listener : mListenerTypes) { 81 int listenerApi = listener.getMinApi(); 82 minApi = Math.max(minApi, listenerApi); 83 } 84 return minApi; 85 } 86 return mGetter.getMinApi(); 87 } 88 89 @Override 90 public boolean isDynamic() { 91 if (mGetter == null) { 92 getResolvedType(); 93 } 94 if (mGetter == null || mGetter.type == Type.METHOD) { 95 return !isListener(); 96 } 97 // if it is static final, gone 98 if (getChild().isDynamic()) { 99 // if owner is dynamic, then we can be dynamic unless we are static final 100 return !mGetter.isStatic() || mGetter.isDynamic(); 101 } 102 103 // if owner is NOT dynamic, we can be dynamic if an only if getter is dynamic 104 return mGetter.isDynamic(); 105 } 106 107 public boolean hasBindableAnnotations() { 108 return mGetter.canBeInvalidated(); 109 } 110 111 @Override 112 public boolean resolveListeners(ModelClass listener) { 113 if (mPotentialListeners == null) { 114 return false; 115 } 116 117 List<ModelMethod> abstractMethods = listener.getAbstractMethods(); 118 int numberOfAbstractMethods = abstractMethods == null ? 0 : abstractMethods.size(); 119 if (numberOfAbstractMethods != 1) { 120 if (mGetter == null) { 121 L.e("Could not find accessor %s.%s and %s has %d abstract methods, so is" + 122 " not resolved as a listener", 123 getChild().getResolvedType().getCanonicalName(), mName, 124 listener.getCanonicalName(), numberOfAbstractMethods); 125 } 126 return false; 127 } 128 129 // See if we've already resolved this listener type 130 if (mListenerMethods == null) { 131 mListenerMethods = new ArrayList<ModelMethod>(); 132 mCalledMethods = new ArrayList<ModelMethod>(); 133 mListenerTypes = new ArrayList<ModelClass>(); 134 } else { 135 for (ModelClass previousListeners : mListenerTypes) { 136 if (previousListeners.equals(listener)) { 137 return false; 138 } 139 } 140 } 141 142 // Look for a signature matching the abstract method 143 final ModelMethod listenerMethod = abstractMethods.get(0); 144 final ModelClass[] listenerParameters = listenerMethod.getParameterTypes(); 145 for (ModelMethod method : mPotentialListeners) { 146 if (acceptsParameters(method, listenerParameters)) { 147 mListenerTypes.add(listener); 148 mListenerMethods.add(listenerMethod); 149 mCalledMethods.add(method); 150 resetResolvedType(); 151 return true; 152 } 153 } 154 155 if (mGetter == null) { 156 L.e("Listener class %s with method %s did not match signature of any method %s.%s", 157 listener.getCanonicalName(), listenerMethod.getName(), 158 getChild().getResolvedType().getCanonicalName(), mName); 159 } 160 return false; 161 } 162 163 private boolean acceptsParameters(ModelMethod method, ModelClass[] listenerParameters) { 164 ModelClass[] parameters = method.getParameterTypes(); 165 if (parameters.length != listenerParameters.length) { 166 return false; 167 } 168 for (int i = 0; i < parameters.length; i++) { 169 if (!parameters[i].isAssignableFrom(listenerParameters[i])) { 170 return false; 171 } 172 } 173 return true; 174 } 175 176 @Override 177 protected List<Dependency> constructDependencies() { 178 final List<Dependency> dependencies = constructDynamicChildrenDependencies(); 179 for (Dependency dependency : dependencies) { 180 if (dependency.getOther() == getChild()) { 181 dependency.setMandatory(true); 182 } 183 } 184 return dependencies; 185 } 186 187 @Override 188 protected String computeUniqueKey() { 189 if (mIsObservableField) { 190 return sUniqueKeyJoiner.join(mName, "..", super.computeUniqueKey()); 191 } 192 return sUniqueKeyJoiner.join(mName, ".", super.computeUniqueKey()); 193 } 194 195 public String getName() { 196 return mName; 197 } 198 199 @Override 200 public void updateExpr(ModelAnalyzer modelAnalyzer) { 201 resolveType(modelAnalyzer); 202 super.updateExpr(modelAnalyzer); 203 } 204 205 @Override 206 protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { 207 if (mGetter == null) { 208 if (mPotentialListeners != null) { 209 return modelAnalyzer.findClass(Object.class); 210 } 211 Expr child = getChild(); 212 child.resolveType(modelAnalyzer); 213 boolean isStatic = child instanceof StaticIdentifierExpr; 214 ModelClass resolvedType = child.getResolvedType(); 215 L.d("resolving %s. Resolved class type: %s", this, resolvedType); 216 217 mGetter = resolvedType.findGetterOrField(mName, isStatic); 218 mPotentialListeners = resolvedType.findMethods(mName, isStatic); 219 220 if (mGetter == null) { 221 if (mPotentialListeners == null) { 222 L.e("Could not find accessor %s.%s", resolvedType.getCanonicalName(), mName); 223 } 224 return modelAnalyzer.findClass(Object.class); 225 } 226 227 if (mGetter.isStatic() && !isStatic) { 228 // found a static method on an instance. register a new one 229 child.getParents().remove(this); 230 getChildren().remove(child); 231 StaticIdentifierExpr staticId = getModel().staticIdentifierFor(resolvedType); 232 getChildren().add(staticId); 233 staticId.getParents().add(this); 234 child = getChild(); // replace the child for the next if stmt 235 } 236 237 if (mGetter.resolvedType.isObservableField()) { 238 // Make this the ".get()" and add an extra field access for the observable field 239 child.getParents().remove(this); 240 getChildren().remove(child); 241 242 FieldAccessExpr observableField = getModel().observableField(child, mName); 243 observableField.mGetter = mGetter; 244 245 getChildren().add(observableField); 246 observableField.getParents().add(this); 247 mGetter = mGetter.resolvedType.findGetterOrField("get", false); 248 mName = ""; 249 } 250 } 251 if (isListener()) { 252 return modelAnalyzer.findClass(Object.class); 253 } 254 return mGetter.resolvedType; 255 } 256 257 @Override 258 protected String asPackage() { 259 String parentPackage = getChild().asPackage(); 260 return parentPackage == null ? null : parentPackage + "." + mName; 261 } 262} 263