ResourceBundle.java revision 8510dab6a17ccd59a2a9cbc07f900dfc2f11ff2b
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * http://www.apache.org/licenses/LICENSE-2.0 7 * Unless required by applicable law or agreed to in writing, software 8 * distributed under the License is distributed on an "AS IS" BASIS, 9 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 * See the License for the specific language governing permissions and 11 * limitations under the License. 12 */ 13 14package android.databinding.tool.store; 15 16import org.apache.commons.lang3.ArrayUtils; 17 18import android.databinding.tool.processing.ErrorMessages; 19import android.databinding.tool.processing.Scope; 20import android.databinding.tool.processing.ScopedException; 21import android.databinding.tool.processing.scopes.FileScopeProvider; 22import android.databinding.tool.processing.scopes.LocationScopeProvider; 23import android.databinding.tool.util.L; 24import android.databinding.tool.util.ParserHelper; 25import android.databinding.tool.util.Preconditions; 26 27import java.io.File; 28import java.io.Serializable; 29import java.util.ArrayList; 30import java.util.Arrays; 31import java.util.HashMap; 32import java.util.HashSet; 33import java.util.List; 34import java.util.Map; 35import java.util.Set; 36 37import javax.xml.bind.annotation.XmlAccessType; 38import javax.xml.bind.annotation.XmlAccessorType; 39import javax.xml.bind.annotation.XmlAttribute; 40import javax.xml.bind.annotation.XmlElement; 41import javax.xml.bind.annotation.XmlElementWrapper; 42import javax.xml.bind.annotation.XmlRootElement; 43 44/** 45 * This is a serializable class that can keep the result of parsing layout files. 46 */ 47public class ResourceBundle implements Serializable { 48 private static final String[] ANDROID_VIEW_PACKAGE_VIEWS = new String[] 49 {"View", "ViewGroup", "ViewStub", "TextureView", "SurfaceView"}; 50 private String mAppPackage; 51 52 private HashMap<String, List<LayoutFileBundle>> mLayoutBundles 53 = new HashMap<String, List<LayoutFileBundle>>(); 54 55 public ResourceBundle(String appPackage) { 56 mAppPackage = appPackage; 57 } 58 59 public void addLayoutBundle(LayoutFileBundle bundle) { 60 if (bundle.mFileName == null) { 61 L.e("File bundle must have a name. %s does not have one.", bundle); 62 return; 63 } 64 if (!mLayoutBundles.containsKey(bundle.mFileName)) { 65 mLayoutBundles.put(bundle.mFileName, new ArrayList<LayoutFileBundle>()); 66 } 67 final List<LayoutFileBundle> bundles = mLayoutBundles.get(bundle.mFileName); 68 for (LayoutFileBundle existing : bundles) { 69 if (existing.equals(bundle)) { 70 L.d("skipping layout bundle %s because it already exists.", bundle); 71 return; 72 } 73 } 74 L.d("adding bundle %s", bundle); 75 bundles.add(bundle); 76 } 77 78 public HashMap<String, List<LayoutFileBundle>> getLayoutBundles() { 79 return mLayoutBundles; 80 } 81 82 public String getAppPackage() { 83 return mAppPackage; 84 } 85 86 public void validateMultiResLayouts() { 87 for (List<LayoutFileBundle> layoutFileBundles : mLayoutBundles.values()) { 88 for (LayoutFileBundle layoutFileBundle : layoutFileBundles) { 89 List<BindingTargetBundle> unboundIncludes = new ArrayList<>(); 90 for (BindingTargetBundle target : layoutFileBundle.getBindingTargetBundles()) { 91 if (target.isBinder()) { 92 List<LayoutFileBundle> boundTo = 93 mLayoutBundles.get(target.getIncludedLayout()); 94 if (boundTo == null || boundTo.isEmpty()) { 95 L.d("There is no binding for %s, reverting to plain layout", 96 target.getIncludedLayout()); 97 if (target.getId() == null) { 98 unboundIncludes.add(target); 99 } else { 100 target.setIncludedLayout(null); 101 target.setInterfaceType("android.view.View"); 102 target.mViewName = "android.view.View"; 103 } 104 } else { 105 String binding = boundTo.get(0).getFullBindingClass(); 106 target.setInterfaceType(binding); 107 } 108 } 109 } 110 layoutFileBundle.getBindingTargetBundles().removeAll(unboundIncludes); 111 } 112 } 113 114 for (Map.Entry<String, List<LayoutFileBundle>> bundles : mLayoutBundles.entrySet()) { 115 if (bundles.getValue().size() < 2) { 116 continue; 117 } 118 119 // validate all ids are in correct view types 120 // and all variables have the same name 121 for (LayoutFileBundle bundle : bundles.getValue()) { 122 bundle.mHasVariations = true; 123 } 124 String bindingClass = validateAndGetSharedClassName(bundles.getValue()); 125 Map<String, NameTypeLocation> variableTypes = validateAndMergeNameTypeLocations( 126 bundles.getValue(), ErrorMessages.MULTI_CONFIG_VARIABLE_TYPE_MISMATCH, 127 new ValidateAndFilterCallback() { 128 @Override 129 public List<? extends NameTypeLocation> get(LayoutFileBundle bundle) { 130 return bundle.mVariables; 131 } 132 }); 133 134 Map<String, NameTypeLocation> importTypes = validateAndMergeNameTypeLocations( 135 bundles.getValue(), ErrorMessages.MULTI_CONFIG_IMPORT_TYPE_MISMATCH, 136 new ValidateAndFilterCallback() { 137 @Override 138 public List<NameTypeLocation> get(LayoutFileBundle bundle) { 139 return bundle.mImports; 140 } 141 }); 142 143 for (LayoutFileBundle bundle : bundles.getValue()) { 144 // now add missing ones to each to ensure they can be referenced 145 L.d("checking for missing variables in %s / %s", bundle.mFileName, 146 bundle.mConfigName); 147 for (Map.Entry<String, NameTypeLocation> variable : variableTypes.entrySet()) { 148 if (!NameTypeLocation.contains(bundle.mVariables, variable.getKey())) { 149 NameTypeLocation orig = variable.getValue(); 150 bundle.addVariable(orig.name, orig.type, orig.location, false); 151 L.d("adding missing variable %s to %s / %s", variable.getKey(), 152 bundle.mFileName, bundle.mConfigName); 153 } 154 } 155 for (Map.Entry<String, NameTypeLocation> userImport : importTypes.entrySet()) { 156 if (!NameTypeLocation.contains(bundle.mImports, userImport.getKey())) { 157 bundle.mImports.add(userImport.getValue()); 158 L.d("adding missing import %s to %s / %s", userImport.getKey(), 159 bundle.mFileName, bundle.mConfigName); 160 } 161 } 162 } 163 164 Set<String> includeBindingIds = new HashSet<String>(); 165 Set<String> viewBindingIds = new HashSet<String>(); 166 Map<String, String> viewTypes = new HashMap<String, String>(); 167 Map<String, String> includes = new HashMap<String, String>(); 168 L.d("validating ids for %s", bundles.getKey()); 169 Set<String> conflictingIds = new HashSet<>(); 170 for (LayoutFileBundle bundle : bundles.getValue()) { 171 try { 172 Scope.enter(bundle); 173 for (BindingTargetBundle target : bundle.mBindingTargetBundles) { 174 try { 175 Scope.enter(target); 176 L.d("checking %s %s %s", target.getId(), target.getFullClassName(), 177 target.isBinder()); 178 if (target.mId != null) { 179 if (target.isBinder()) { 180 if (viewBindingIds.contains(target.mId)) { 181 L.d("%s is conflicting", target.mId); 182 conflictingIds.add(target.mId); 183 continue; 184 } 185 includeBindingIds.add(target.mId); 186 } else { 187 if (includeBindingIds.contains(target.mId)) { 188 L.d("%s is conflicting", target.mId); 189 conflictingIds.add(target.mId); 190 continue; 191 } 192 viewBindingIds.add(target.mId); 193 } 194 String existingType = viewTypes.get(target.mId); 195 if (existingType == null) { 196 L.d("assigning %s as %s", target.getId(), 197 target.getFullClassName()); 198 viewTypes.put(target.mId, target.getFullClassName()); 199 if (target.isBinder()) { 200 includes.put(target.mId, target.getIncludedLayout()); 201 } 202 } else if (!existingType.equals(target.getFullClassName())) { 203 if (target.isBinder()) { 204 L.d("overriding %s as base binder", target.getId()); 205 viewTypes.put(target.mId, 206 "android.databinding.ViewDataBinding"); 207 includes.put(target.mId, target.getIncludedLayout()); 208 } else { 209 L.d("overriding %s as base view", target.getId()); 210 viewTypes.put(target.mId, "android.view.View"); 211 } 212 } 213 } 214 } catch (ScopedException ex) { 215 Scope.defer(ex); 216 } finally { 217 Scope.exit(); 218 } 219 } 220 } finally { 221 Scope.exit(); 222 } 223 } 224 225 if (!conflictingIds.isEmpty()) { 226 for (LayoutFileBundle bundle : bundles.getValue()) { 227 for (BindingTargetBundle target : bundle.mBindingTargetBundles) { 228 if (conflictingIds.contains(target.mId)) { 229 Scope.registerError(String.format( 230 ErrorMessages.MULTI_CONFIG_ID_USED_AS_IMPORT, 231 target.mId), bundle, target); 232 } 233 } 234 } 235 } 236 237 for (LayoutFileBundle bundle : bundles.getValue()) { 238 try { 239 Scope.enter(bundle); 240 for (Map.Entry<String, String> viewType : viewTypes.entrySet()) { 241 BindingTargetBundle target = bundle.getBindingTargetById(viewType.getKey()); 242 if (target == null) { 243 String include = includes.get(viewType.getKey()); 244 if (include == null) { 245 bundle.createBindingTarget(viewType.getKey(), viewType.getValue(), 246 false, null, null, null); 247 } else { 248 BindingTargetBundle bindingTargetBundle = bundle 249 .createBindingTarget( 250 viewType.getKey(), null, false, null, null, null); 251 bindingTargetBundle 252 .setIncludedLayout(includes.get(viewType.getKey())); 253 bindingTargetBundle.setInterfaceType(viewType.getValue()); 254 } 255 } else { 256 L.d("setting interface type on %s (%s) as %s", target.mId, 257 target.getFullClassName(), viewType.getValue()); 258 target.setInterfaceType(viewType.getValue()); 259 } 260 } 261 } catch (ScopedException ex) { 262 Scope.defer(ex); 263 } finally { 264 Scope.exit(); 265 } 266 } 267 } 268 // assign class names to each 269 for (Map.Entry<String, List<LayoutFileBundle>> entry : mLayoutBundles.entrySet()) { 270 for (LayoutFileBundle bundle : entry.getValue()) { 271 final String configName; 272 if (bundle.hasVariations()) { 273 // append configuration specifiers. 274 final String parentFileName = bundle.mDirectory; 275 L.d("parent file for %s is %s", bundle.getFileName(), parentFileName); 276 if ("layout".equals(parentFileName)) { 277 configName = ""; 278 } else { 279 configName = ParserHelper.toClassName(parentFileName.substring("layout-".length())); 280 } 281 } else { 282 configName = ""; 283 } 284 bundle.mConfigName = configName; 285 } 286 } 287 } 288 289 /** 290 * Receives a list of bundles which are representations of the same layout file in different 291 * configurations. 292 * @param bundles 293 * @return The map for variables and their types 294 */ 295 private Map<String, NameTypeLocation> validateAndMergeNameTypeLocations( 296 List<LayoutFileBundle> bundles, String errorMessage, 297 ValidateAndFilterCallback callback) { 298 Map<String, NameTypeLocation> result = new HashMap<>(); 299 Set<String> mismatched = new HashSet<>(); 300 for (LayoutFileBundle bundle : bundles) { 301 for (NameTypeLocation item : callback.get(bundle)) { 302 NameTypeLocation existing = result.get(item.name); 303 if (existing != null && !existing.type.equals(item.type)) { 304 mismatched.add(item.name); 305 continue; 306 } 307 result.put(item.name, item); 308 } 309 } 310 if (mismatched.isEmpty()) { 311 return result; 312 } 313 // create exceptions. We could get more clever and find the outlier but for now, listing 314 // each file w/ locations seems enough 315 for (String mismatch : mismatched) { 316 for (LayoutFileBundle bundle : bundles) { 317 NameTypeLocation found = null; 318 for (NameTypeLocation item : callback.get(bundle)) { 319 if (mismatch.equals(item.name)) { 320 found = item; 321 break; 322 } 323 } 324 if (found == null) { 325 // variable is not defined in this layout, continue 326 continue; 327 } 328 Scope.registerError(String.format( 329 errorMessage, found.name, found.type, 330 bundle.mDirectory + "/" + bundle.getFileName()), bundle, 331 found.location.createScope()); 332 } 333 } 334 return result; 335 } 336 337 /** 338 * Receives a list of bundles which are representations of the same layout file in different 339 * configurations. 340 * @param bundles 341 * @return The shared class name for these bundles 342 */ 343 private String validateAndGetSharedClassName(List<LayoutFileBundle> bundles) { 344 String sharedClassName = null; 345 boolean hasMismatch = false; 346 for (LayoutFileBundle bundle : bundles) { 347 bundle.mHasVariations = true; 348 String fullBindingClass = bundle.getFullBindingClass(); 349 if (sharedClassName == null) { 350 sharedClassName = fullBindingClass; 351 } else if (!sharedClassName.equals(fullBindingClass)) { 352 hasMismatch = true; 353 break; 354 } 355 } 356 if (!hasMismatch) { 357 return sharedClassName; 358 } 359 // generate proper exceptions for each 360 for (LayoutFileBundle bundle : bundles) { 361 Scope.registerError(String.format(ErrorMessages.MULTI_CONFIG_LAYOUT_CLASS_NAME_MISMATCH, 362 bundle.getFullBindingClass(), bundle.mDirectory + "/" + bundle.getFileName()), 363 bundle, bundle.getClassNameLocationProvider()); 364 } 365 return sharedClassName; 366 } 367 368 @XmlAccessorType(XmlAccessType.NONE) 369 @XmlRootElement(name="Layout") 370 public static class LayoutFileBundle implements Serializable, FileScopeProvider { 371 @XmlAttribute(name="layout", required = true) 372 public String mFileName; 373 @XmlAttribute(name="modulePackage", required = true) 374 public String mModulePackage; 375 @XmlAttribute(name="absoluteFilePath", required = true) 376 public String mAbsoluteFilePath; 377 private String mConfigName; 378 379 // The binding class as given by the user 380 @XmlAttribute(name="bindingClass", required = false) 381 public String mBindingClass; 382 383 // The location of the name of the generated class, optional 384 @XmlElement(name = "ClassNameLocation", required = false) 385 private Location mClassNameLocation; 386 // The full package and class name as determined from mBindingClass and mModulePackage 387 private String mFullBindingClass; 388 389 // The simple binding class name as determined from mBindingClass and mModulePackage 390 private String mBindingClassName; 391 392 // The package of the binding class as determined from mBindingClass and mModulePackage 393 private String mBindingPackage; 394 395 @XmlAttribute(name="directory", required = true) 396 public String mDirectory; 397 public boolean mHasVariations; 398 399 @XmlElement(name="Variables") 400 public List<VariableDeclaration> mVariables = new ArrayList<>(); 401 402 @XmlElement(name="Imports") 403 public List<NameTypeLocation> mImports = new ArrayList<>(); 404 405 @XmlElementWrapper(name="Targets") 406 @XmlElement(name="Target") 407 public List<BindingTargetBundle> mBindingTargetBundles = new ArrayList<BindingTargetBundle>(); 408 409 @XmlAttribute(name="isMerge", required = true) 410 private boolean mIsMerge; 411 412 private LocationScopeProvider mClassNameLocationProvider; 413 414 // for XML binding 415 public LayoutFileBundle() { 416 } 417 418 public LayoutFileBundle(File file, String fileName, String directory, 419 String modulePackage, boolean isMerge) { 420 mFileName = fileName; 421 mDirectory = directory; 422 mModulePackage = modulePackage; 423 mIsMerge = isMerge; 424 mAbsoluteFilePath = file.getAbsolutePath(); 425 } 426 427 public LocationScopeProvider getClassNameLocationProvider() { 428 if (mClassNameLocationProvider == null && mClassNameLocation != null 429 && mClassNameLocation.isValid()) { 430 mClassNameLocationProvider = mClassNameLocation.createScope(); 431 } 432 return mClassNameLocationProvider; 433 } 434 435 public void addVariable(String name, String type, Location location, boolean declared) { 436 Preconditions.check(!NameTypeLocation.contains(mVariables, name), 437 "Cannot use same variable name twice. %s in %s", name, location); 438 mVariables.add(new VariableDeclaration(name, type, location, declared)); 439 } 440 441 public void addImport(String alias, String type, Location location) { 442 Preconditions.check(!NameTypeLocation.contains(mImports, alias), 443 "Cannot import same alias twice. %s in %s", alias, location); 444 mImports.add(new NameTypeLocation(alias, type, location)); 445 } 446 447 public BindingTargetBundle createBindingTarget(String id, String viewName, 448 boolean used, String tag, String originalTag, Location location) { 449 BindingTargetBundle target = new BindingTargetBundle(id, viewName, used, tag, 450 originalTag, location); 451 mBindingTargetBundles.add(target); 452 return target; 453 } 454 455 public boolean isEmpty() { 456 return mVariables.isEmpty() && mImports.isEmpty() && mBindingTargetBundles.isEmpty(); 457 } 458 459 public BindingTargetBundle getBindingTargetById(String key) { 460 for (BindingTargetBundle target : mBindingTargetBundles) { 461 if (key.equals(target.mId)) { 462 return target; 463 } 464 } 465 return null; 466 } 467 468 public String getFileName() { 469 return mFileName; 470 } 471 472 public String getConfigName() { 473 return mConfigName; 474 } 475 476 public String getDirectory() { 477 return mDirectory; 478 } 479 480 public boolean hasVariations() { 481 return mHasVariations; 482 } 483 484 public List<VariableDeclaration> getVariables() { 485 return mVariables; 486 } 487 488 public List<NameTypeLocation> getImports() { 489 return mImports; 490 } 491 492 public boolean isMerge() { 493 return mIsMerge; 494 } 495 496 public String getBindingClassName() { 497 if (mBindingClassName == null) { 498 String fullClass = getFullBindingClass(); 499 int dotIndex = fullClass.lastIndexOf('.'); 500 mBindingClassName = fullClass.substring(dotIndex + 1); 501 } 502 return mBindingClassName; 503 } 504 505 public void setBindingClass(String bindingClass, Location location) { 506 mBindingClass = bindingClass; 507 mClassNameLocation = location; 508 } 509 510 public String getBindingClassPackage() { 511 if (mBindingPackage == null) { 512 String fullClass = getFullBindingClass(); 513 int dotIndex = fullClass.lastIndexOf('.'); 514 mBindingPackage = fullClass.substring(0, dotIndex); 515 } 516 return mBindingPackage; 517 } 518 519 private String getFullBindingClass() { 520 if (mFullBindingClass == null) { 521 if (mBindingClass == null) { 522 mFullBindingClass = getModulePackage() + ".databinding." + 523 ParserHelper.toClassName(getFileName()) + "Binding"; 524 } else if (mBindingClass.startsWith(".")) { 525 mFullBindingClass = getModulePackage() + mBindingClass; 526 } else if (mBindingClass.indexOf('.') < 0) { 527 mFullBindingClass = getModulePackage() + ".databinding." + mBindingClass; 528 } else { 529 mFullBindingClass = mBindingClass; 530 } 531 } 532 return mFullBindingClass; 533 } 534 535 public List<BindingTargetBundle> getBindingTargetBundles() { 536 return mBindingTargetBundles; 537 } 538 539 @Override 540 public boolean equals(Object o) { 541 if (this == o) { 542 return true; 543 } 544 if (o == null || getClass() != o.getClass()) { 545 return false; 546 } 547 548 LayoutFileBundle bundle = (LayoutFileBundle) o; 549 550 if (mConfigName != null ? !mConfigName.equals(bundle.mConfigName) 551 : bundle.mConfigName != null) { 552 return false; 553 } 554 if (mDirectory != null ? !mDirectory.equals(bundle.mDirectory) 555 : bundle.mDirectory != null) { 556 return false; 557 } 558 if (mFileName != null ? !mFileName.equals(bundle.mFileName) 559 : bundle.mFileName != null) { 560 return false; 561 } 562 563 return true; 564 } 565 566 @Override 567 public int hashCode() { 568 int result = mFileName != null ? mFileName.hashCode() : 0; 569 result = 31 * result + (mConfigName != null ? mConfigName.hashCode() : 0); 570 result = 31 * result + (mDirectory != null ? mDirectory.hashCode() : 0); 571 return result; 572 } 573 574 @Override 575 public String toString() { 576 return "LayoutFileBundle{" + 577 "mHasVariations=" + mHasVariations + 578 ", mDirectory='" + mDirectory + '\'' + 579 ", mConfigName='" + mConfigName + '\'' + 580 ", mModulePackage='" + mModulePackage + '\'' + 581 ", mFileName='" + mFileName + '\'' + 582 '}'; 583 } 584 585 public String getModulePackage() { 586 return mModulePackage; 587 } 588 589 public String getAbsoluteFilePath() { 590 return mAbsoluteFilePath; 591 } 592 593 @Override 594 public String provideScopeFilePath() { 595 return mAbsoluteFilePath; 596 } 597 } 598 599 @XmlAccessorType(XmlAccessType.NONE) 600 public static class NameTypeLocation { 601 @XmlAttribute(name="type", required = true) 602 public String type; 603 604 @XmlAttribute(name="name", required = true) 605 public String name; 606 607 @XmlElement(name="location", required = false) 608 public Location location; 609 610 public NameTypeLocation() { 611 } 612 613 public NameTypeLocation(String name, String type, Location location) { 614 this.type = type; 615 this.name = name; 616 this.location = location; 617 } 618 619 @Override 620 public String toString() { 621 return "{" + 622 "type='" + type + '\'' + 623 ", name='" + name + '\'' + 624 ", location=" + location + 625 '}'; 626 } 627 628 @Override 629 public boolean equals(Object o) { 630 if (this == o) { 631 return true; 632 } 633 if (o == null || getClass() != o.getClass()) { 634 return false; 635 } 636 637 NameTypeLocation that = (NameTypeLocation) o; 638 639 if (location != null ? !location.equals(that.location) : that.location != null) { 640 return false; 641 } 642 if (!name.equals(that.name)) { 643 return false; 644 } 645 if (!type.equals(that.type)) { 646 return false; 647 } 648 649 return true; 650 } 651 652 @Override 653 public int hashCode() { 654 int result = type.hashCode(); 655 result = 31 * result + name.hashCode(); 656 result = 31 * result + (location != null ? location.hashCode() : 0); 657 return result; 658 } 659 660 public static boolean contains(List<? extends NameTypeLocation> list, String name) { 661 for (NameTypeLocation ntl : list) { 662 if (name.equals(ntl.name)) { 663 return true; 664 } 665 } 666 return false; 667 } 668 } 669 670 @XmlAccessorType(XmlAccessType.NONE) 671 public static class VariableDeclaration extends NameTypeLocation { 672 @XmlAttribute(name="declared", required = false) 673 public boolean declared; 674 675 public VariableDeclaration() { 676 677 } 678 679 public VariableDeclaration(String name, String type, Location location, boolean declared) { 680 super(name, type, location); 681 this.declared = declared; 682 } 683 } 684 685 public static class MarshalledMapType { 686 public List<NameTypeLocation> entries; 687 } 688 689 @XmlAccessorType(XmlAccessType.NONE) 690 public static class BindingTargetBundle implements Serializable, LocationScopeProvider { 691 // public for XML serialization 692 693 @XmlAttribute(name="id") 694 public String mId; 695 @XmlAttribute(name="tag", required = true) 696 public String mTag; 697 @XmlAttribute(name="originalTag") 698 public String mOriginalTag; 699 @XmlAttribute(name="view", required = false) 700 public String mViewName; 701 private String mFullClassName; 702 public boolean mUsed = true; 703 @XmlElementWrapper(name="Expressions") 704 @XmlElement(name="Expression") 705 public List<BindingBundle> mBindingBundleList = new ArrayList<BindingBundle>(); 706 @XmlAttribute(name="include") 707 public String mIncludedLayout; 708 @XmlElement(name="location") 709 public Location mLocation; 710 private String mInterfaceType; 711 712 // For XML serialization 713 public BindingTargetBundle() {} 714 715 public BindingTargetBundle(String id, String viewName, boolean used, 716 String tag, String originalTag, Location location) { 717 mId = id; 718 mViewName = viewName; 719 mUsed = used; 720 mTag = tag; 721 mOriginalTag = originalTag; 722 mLocation = location; 723 } 724 725 public void addBinding(String name, String expr, Location location, Location valueLocation) { 726 mBindingBundleList.add(new BindingBundle(name, expr, location, valueLocation)); 727 } 728 729 public void setIncludedLayout(String includedLayout) { 730 mIncludedLayout = includedLayout; 731 } 732 733 public String getIncludedLayout() { 734 return mIncludedLayout; 735 } 736 737 public boolean isBinder() { 738 return mIncludedLayout != null; 739 } 740 741 public void setInterfaceType(String interfaceType) { 742 mInterfaceType = interfaceType; 743 } 744 745 public void setLocation(Location location) { 746 mLocation = location; 747 } 748 749 public Location getLocation() { 750 return mLocation; 751 } 752 753 public String getId() { 754 return mId; 755 } 756 757 public String getTag() { 758 return mTag; 759 } 760 761 public String getOriginalTag() { 762 return mOriginalTag; 763 } 764 765 public String getFullClassName() { 766 if (mFullClassName == null) { 767 if (isBinder()) { 768 mFullClassName = mInterfaceType; 769 } else if (mViewName.indexOf('.') == -1) { 770 if (ArrayUtils.contains(ANDROID_VIEW_PACKAGE_VIEWS, mViewName)) { 771 mFullClassName = "android.view." + mViewName; 772 } else if("WebView".equals(mViewName)) { 773 mFullClassName = "android.webkit." + mViewName; 774 } else { 775 mFullClassName = "android.widget." + mViewName; 776 } 777 } else { 778 mFullClassName = mViewName; 779 } 780 } 781 if (mFullClassName == null) { 782 L.e("Unexpected full class name = null. view = %s, interface = %s, layout = %s", 783 mViewName, mInterfaceType, mIncludedLayout); 784 } 785 return mFullClassName; 786 } 787 788 public boolean isUsed() { 789 return mUsed; 790 } 791 792 public List<BindingBundle> getBindingBundleList() { 793 return mBindingBundleList; 794 } 795 796 public String getInterfaceType() { 797 return mInterfaceType; 798 } 799 800 @Override 801 public List<Location> provideScopeLocation() { 802 return mLocation == null ? null : Arrays.asList(mLocation); 803 } 804 805 @XmlAccessorType(XmlAccessType.NONE) 806 public static class BindingBundle implements Serializable { 807 808 private String mName; 809 private String mExpr; 810 private Location mLocation; 811 private Location mValueLocation; 812 813 public BindingBundle() {} 814 815 public BindingBundle(String name, String expr, Location location, 816 Location valueLocation) { 817 mName = name; 818 mExpr = expr; 819 mLocation = location; 820 mValueLocation = valueLocation; 821 } 822 823 @XmlAttribute(name="attribute", required=true) 824 public String getName() { 825 return mName; 826 } 827 828 @XmlAttribute(name="text", required=true) 829 public String getExpr() { 830 return mExpr; 831 } 832 833 public void setName(String name) { 834 mName = name; 835 } 836 837 public void setExpr(String expr) { 838 mExpr = expr; 839 } 840 841 @XmlElement(name="Location") 842 public Location getLocation() { 843 return mLocation; 844 } 845 846 public void setLocation(Location location) { 847 mLocation = location; 848 } 849 850 @XmlElement(name="ValueLocation") 851 public Location getValueLocation() { 852 return mValueLocation; 853 } 854 855 public void setValueLocation(Location valueLocation) { 856 mValueLocation = valueLocation; 857 } 858 } 859 } 860 861 /** 862 * Just an inner callback class to process imports and variables w/ the same code. 863 */ 864 private interface ValidateAndFilterCallback { 865 List<? extends NameTypeLocation> get(LayoutFileBundle bundle); 866 } 867} 868