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