001// Copyright 2022, 2023 The Apache Software Foundation 002// 003// Licensed under the Apache License, Version 2.0 (the "License"); 004// you may not use this file except in compliance with the License. 005// You may obtain a copy of the License at 006// 007// http://www.apache.org/licenses/LICENSE-2.0 008// 009// Unless required by applicable law or agreed to in writing, software 010// distributed under the License is distributed on an "AS IS" BASIS, 011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012// See the License for the specific language governing permissions and 013// limitations under the License. 014package org.apache.tapestry5.internal.services; 015 016import java.io.BufferedReader; 017import java.io.BufferedWriter; 018import java.io.File; 019import java.io.FileReader; 020import java.io.FileWriter; 021import java.io.IOException; 022import java.lang.reflect.Field; 023import java.util.ArrayList; 024import java.util.Collection; 025import java.util.Collections; 026import java.util.HashMap; 027import java.util.HashSet; 028import java.util.Iterator; 029import java.util.List; 030import java.util.Locale; 031import java.util.Map; 032import java.util.Objects; 033import java.util.Set; 034import java.util.WeakHashMap; 035import java.util.function.Consumer; 036import java.util.stream.Collectors; 037 038import org.apache.tapestry5.ComponentResources; 039import org.apache.tapestry5.annotations.InjectComponent; 040import org.apache.tapestry5.annotations.InjectPage; 041import org.apache.tapestry5.annotations.Mixin; 042import org.apache.tapestry5.annotations.MixinClasses; 043import org.apache.tapestry5.annotations.Mixins; 044import org.apache.tapestry5.commons.Resource; 045import org.apache.tapestry5.commons.internal.util.TapestryException; 046import org.apache.tapestry5.commons.services.InvalidationEventHub; 047import org.apache.tapestry5.internal.TapestryInternalUtils; 048import org.apache.tapestry5.internal.parser.ComponentTemplate; 049import org.apache.tapestry5.internal.parser.StartComponentToken; 050import org.apache.tapestry5.internal.parser.TemplateToken; 051import org.apache.tapestry5.internal.structure.ComponentPageElement; 052import org.apache.tapestry5.ioc.Orderable; 053import org.apache.tapestry5.ioc.internal.util.ClasspathResource; 054import org.apache.tapestry5.ioc.internal.util.InternalUtils; 055import org.apache.tapestry5.ioc.services.PerthreadManager; 056import org.apache.tapestry5.json.JSONArray; 057import org.apache.tapestry5.json.JSONObject; 058import org.apache.tapestry5.model.ComponentModel; 059import org.apache.tapestry5.model.EmbeddedComponentModel; 060import org.apache.tapestry5.model.MutableComponentModel; 061import org.apache.tapestry5.model.ParameterModel; 062import org.apache.tapestry5.plastic.PlasticField; 063import org.apache.tapestry5.plastic.PlasticManager; 064import org.apache.tapestry5.runtime.Component; 065import org.apache.tapestry5.services.ComponentClassResolver; 066import org.apache.tapestry5.services.pageload.PageClassLoaderContextManager; 067import org.apache.tapestry5.services.templates.ComponentTemplateLocator; 068import org.slf4j.Logger; 069 070public class ComponentDependencyRegistryImpl implements ComponentDependencyRegistry 071{ 072 073 private static final List<String> EMPTY_LIST = Collections.emptyList(); 074 075 final private PageClassLoaderContextManager pageClassLoaderContextManager; 076 077 private static final String META_ATTRIBUTE = "injectedComponentDependencies"; 078 079 private static final String META_ATTRIBUTE_SEPARATOR = ","; 080 081 // Key is a component, values are the components that depend on it. 082 final private Map<String, Set<Dependency>> map; 083 084 // Cache to check which classes were already processed or not. 085 final private Set<String> alreadyProcessed; 086 087 final private File storedDependencies; 088 089 final private static ThreadLocal<Integer> INVALIDATIONS_DISABLED = ThreadLocal.withInitial(() -> 0); 090 091 final private PlasticManager plasticManager; 092 093 final private ComponentClassResolver resolver; 094 095 final private TemplateParser templateParser; 096 097 final private Map<String, Boolean> isPageCache = new WeakHashMap<>(); 098 099 @SuppressWarnings("deprecation") 100 final private ComponentTemplateLocator componentTemplateLocator; 101 102 final private boolean storedDependencyInformationPresent; 103 104 public ComponentDependencyRegistryImpl( 105 final PageClassLoaderContextManager pageClassLoaderContextManager, 106 final PlasticManager plasticManager, 107 final ComponentClassResolver componentClassResolver, 108 final TemplateParser templateParser, 109 final ComponentTemplateLocator componentTemplateLocator) 110 { 111 this.pageClassLoaderContextManager = pageClassLoaderContextManager; 112 map = new HashMap<>(); 113 alreadyProcessed = new HashSet<>(); 114 this.plasticManager = plasticManager; 115 this.resolver = componentClassResolver; 116 this.templateParser = templateParser; 117 this.componentTemplateLocator = componentTemplateLocator; 118 119 storedDependencies = new File(FILENAME); 120 if (storedDependencies.exists()) 121 { 122 try (FileReader fileReader = new FileReader(storedDependencies); 123 BufferedReader reader = new BufferedReader(fileReader)) 124 { 125 StringBuilder builder = new StringBuilder(); 126 String line = reader.readLine(); 127 while (line != null) 128 { 129 builder.append(line); 130 line = reader.readLine(); 131 } 132 JSONArray jsonArray = new JSONArray(builder.toString()); 133 for (int i = 0; i < jsonArray.size(); i++) 134 { 135 final JSONObject jsonObject = jsonArray.getJSONObject(i); 136 final String className = jsonObject.getString("class"); 137 final DependencyType dependencyType = DependencyType.valueOf(jsonObject.getString("type")); 138 final String dependency = jsonObject.getString("dependency"); 139 add(className, dependency, dependencyType); 140 alreadyProcessed.add(dependency); 141 alreadyProcessed.add(className); 142 } 143 } catch (IOException e) 144 { 145 throw new TapestryException("Exception trying to read " + FILENAME, e); 146 } 147 148 } 149 150 storedDependencyInformationPresent = !map.isEmpty(); 151 152 } 153 154 public void setupThreadCleanup(final PerthreadManager perthreadManager) 155 { 156 perthreadManager.addThreadCleanupCallback(() -> { 157 INVALIDATIONS_DISABLED.set(0); 158 }); 159 } 160 161 @Override 162 public void register(Class<?> component) 163 { 164 165 final String className = component.getName(); 166 final Set<Class<?>> furtherDependencies = new HashSet<>(); 167 Consumer<Class<?>> processClass = furtherDependencies::add; 168 Consumer<String> processClassName = s -> { 169 try { 170 furtherDependencies.add(component.getClassLoader().loadClass(s)); 171 } catch (ClassNotFoundException e) { 172 throw new RuntimeException(e); 173 } 174 }; 175 176 // Components declared in the template 177 registerTemplate(component, processClassName); 178 179 // Dependencies from injecting or component-declaring annotations: 180 // @InjectPage, @InjectComponent 181 for (Field field : component.getDeclaredFields()) 182 { 183 184 // Component injection annotation 185 if (field.isAnnotationPresent(InjectComponent.class)) 186 { 187 final Class<?> dependency = field.getType(); 188 add(component, dependency, DependencyType.USAGE); 189 processClass.accept(dependency); 190 } 191 192 // Page injection annotation 193 if (field.isAnnotationPresent(InjectPage.class)) 194 { 195 final Class<?> dependency = field.getType(); 196 add(component, dependency, DependencyType.INJECT_PAGE); 197 } 198 199 // @Component 200 registerComponentInstance(field, processClassName); 201 202 // Mixins, class level: @Mixin 203 registerMixin(field, processClassName); 204 205 // Mixins applied to embedded component instances through @MixinClasses or @Mixins 206 registerComponentInstanceMixins(field, processClass, processClassName); 207 } 208 209 // Superclass 210 Class superclass = component.getSuperclass(); 211 if (isTransformed(superclass)) 212 { 213 processClass.accept(superclass); 214 add(component, superclass, DependencyType.SUPERCLASS); 215 } 216 217 alreadyProcessed.add(className); 218 219 for (Class<?> dependency : furtherDependencies) 220 { 221 // Avoid infinite recursion 222 final String dependencyClassName = dependency.getName(); 223 if (!alreadyProcessed.contains(dependencyClassName) 224 && plasticManager.shouldInterceptClassLoading(dependency.getName())) 225 { 226 register(dependency); 227 } 228 } 229 230 } 231 232 /** 233 * Notice only the main template (i.e. not the locale- or axis-specific ones) 234 * are checked here. They hopefully will be covered when the ComponentModel-based 235 * component dependency processing is done. 236 * @param component 237 * @param processClassName 238 */ 239 @SuppressWarnings("deprecation") 240 private void registerTemplate(Class<?> component, Consumer<String> processClassName) 241 { 242 // TODO: implement caching of template dependency information, probably 243 // by listening separaterly to ComponentTemplateSource to invalidate caches 244 // just when template changes. 245 246 final String className = component.getName(); 247 ComponentModel mock = new ComponentModelMock(component, isPage(className)); 248 final Resource templateResource = componentTemplateLocator.locateTemplate(mock, Locale.getDefault()); 249 String dependency; 250 if (templateResource != null) 251 { 252 final ComponentTemplate template = templateParser.parseTemplate(templateResource); 253 for (TemplateToken token: template.getTokens()) 254 { 255 if (token instanceof StartComponentToken) 256 { 257 StartComponentToken componentToken = (StartComponentToken) token; 258 String logicalName = componentToken.getComponentType(); 259 if (logicalName != null) 260 { 261 dependency = resolver.resolveComponentTypeToClassName(logicalName); 262 add(className, dependency, DependencyType.USAGE); 263 processClassName.accept(dependency); 264 } 265 for (String mixin : TapestryInternalUtils.splitAtCommas(componentToken.getMixins())) 266 { 267 dependency = resolver.resolveMixinTypeToClassName(mixin); 268 add(className, dependency, DependencyType.USAGE); 269 processClassName.accept(dependency); 270 } 271 } 272 } 273 } 274 } 275 276 private boolean isNotPage(final String className) 277 { 278 return !isPage(className); 279 } 280 281 private boolean isPage(final String className) 282 { 283 Boolean result = isPageCache.get(className); 284 if (result == null) 285 { 286 result = resolver.isPage(className); 287 isPageCache.put(className, result); 288 } 289 return result; 290 } 291 292 private void registerComponentInstance(Field field, Consumer<String> processClassName) 293 { 294 if (field.isAnnotationPresent(org.apache.tapestry5.annotations.Component.class)) 295 { 296 org.apache.tapestry5.annotations.Component component = 297 field.getAnnotation(org.apache.tapestry5.annotations.Component.class); 298 299 final String typeFromAnnotation = component.type().trim(); 300 String dependency; 301 if (typeFromAnnotation.isEmpty()) 302 { 303 dependency = field.getType().getName(); 304 } 305 else 306 { 307 dependency = resolver.resolveComponentTypeToClassName(typeFromAnnotation); 308 } 309 add(field.getDeclaringClass().getName(), dependency, DependencyType.USAGE); 310 processClassName.accept(dependency); 311 } 312 } 313 314 private void registerMixin(Field field, Consumer<String> processClassName) { 315 if (field.isAnnotationPresent(Mixin.class)) 316 { 317 // Logic adapted from MixinWorker 318 String mixinType = field.getAnnotation(Mixin.class).value(); 319 String mixinClassName = InternalUtils.isBlank(mixinType) ? 320 getFieldTypeClassName(field) : 321 resolver.resolveMixinTypeToClassName(mixinType); 322 323 add(getDeclaringClassName(field), mixinClassName, DependencyType.USAGE); 324 processClassName.accept(mixinClassName); 325 } 326 } 327 328 private String getDeclaringClassName(Field field) { 329 return field.getDeclaringClass().getName(); 330 } 331 332 private String getFieldTypeClassName(Field field) { 333 return field.getType().getName(); 334 } 335 336 private void registerComponentInstanceMixins(Field field, Consumer<Class<?>> processClass, Consumer<String> processClassName) 337 { 338 339 if (field.isAnnotationPresent(org.apache.tapestry5.annotations.Component.class)) 340 { 341 342 MixinClasses mixinClasses = field.getAnnotation(MixinClasses.class); 343 if (mixinClasses != null) 344 { 345 for (Class dependency : mixinClasses.value()) 346 { 347 add(field.getDeclaringClass(), dependency, DependencyType.USAGE); 348 processClass.accept(dependency); 349 } 350 } 351 352 Mixins mixins = field.getAnnotation(Mixins.class); 353 if (mixins != null) 354 { 355 for (String mixin : mixins.value()) 356 { 357 // Logic adapted from MixinsWorker 358 Orderable<String> typeAndOrder = TapestryInternalUtils.mixinTypeAndOrder(mixin); 359 final String dependency = resolver.resolveMixinTypeToClassName(typeAndOrder.getTarget()); 360 add(getDeclaringClassName(field), dependency, DependencyType.USAGE); 361 processClassName.accept(dependency); 362 } 363 } 364 365 } 366 367 } 368 369 @Override 370 public void register(ComponentPageElement componentPageElement) 371 { 372 final String componentClassName = getClassName(componentPageElement); 373 374 if (!alreadyProcessed.contains(componentClassName)) 375 { 376 synchronized (map) 377 { 378 379 // Components in the tree (i.e. declared in the template 380 for (String id : componentPageElement.getEmbeddedElementIds()) 381 { 382 final ComponentPageElement child = componentPageElement.getEmbeddedElement(id); 383 add(componentPageElement, child, DependencyType.USAGE); 384 register(child); 385 } 386 387 // Mixins, class level 388 final ComponentResources componentResources = componentPageElement.getComponentResources(); 389 final ComponentModel componentModel = componentResources.getComponentModel(); 390 for (String mixinClassName : componentModel.getMixinClassNames()) 391 { 392 add(componentClassName, mixinClassName, DependencyType.USAGE); 393 } 394 395 // Mixins applied to embedded component instances 396 final List<String> embeddedComponentIds = componentModel.getEmbeddedComponentIds(); 397 for (String id : embeddedComponentIds) 398 { 399 final EmbeddedComponentModel embeddedComponentModel = componentResources 400 .getComponentModel() 401 .getEmbeddedComponentModel(id); 402 final List<String> mixinClassNames = embeddedComponentModel 403 .getMixinClassNames(); 404 for (String mixinClassName : mixinClassNames) { 405 add(componentClassName, mixinClassName, DependencyType.USAGE); 406 } 407 } 408 409 // Superclass 410 final Component component = componentPageElement.getComponent(); 411 Class<?> parent = component.getClass().getSuperclass(); 412 if (parent != null && !Object.class.equals(parent)) 413 { 414 add(componentClassName, parent.getName(), DependencyType.SUPERCLASS); 415 } 416 417 // Dependencies from injecting annotations: 418 // @InjectPage, @InjectComponent, @InjectComponent 419 final String metaDependencies = component.getComponentResources().getComponentModel().getMeta(META_ATTRIBUTE); 420 if (metaDependencies != null) 421 { 422 for (String dependency : metaDependencies.split(META_ATTRIBUTE_SEPARATOR)) 423 { 424 add(componentClassName, dependency, 425 isPage(dependency) ? DependencyType.INJECT_PAGE : DependencyType.USAGE); 426 } 427 } 428 429 alreadyProcessed.add(componentClassName); 430 431 } 432 433 } 434 435 } 436 437 @Override 438 public void register(PlasticField plasticField, MutableComponentModel componentModel) 439 { 440 if (plasticField.hasAnnotation(InjectPage.class) || 441 plasticField.hasAnnotation(InjectComponent.class) || 442 plasticField.hasAnnotation(org.apache.tapestry5.annotations.Component.class)) 443 { 444 String dependencies = componentModel.getMeta(META_ATTRIBUTE); 445 final String dependency = plasticField.getTypeName(); 446 if (dependencies == null) 447 { 448 dependencies = dependency; 449 } 450 else 451 { 452 if (!dependencies.contains(dependency)) 453 { 454 dependencies = dependencies + META_ATTRIBUTE_SEPARATOR + dependency; 455 } 456 } 457 componentModel.setMeta(META_ATTRIBUTE, dependencies); 458 } 459 } 460 461 private String getClassName(ComponentPageElement component) 462 { 463 return component.getComponentResources().getComponentModel().getComponentClassName(); 464 } 465 466 @Override 467 public void clear(String className) 468 { 469 synchronized (map) 470 { 471 alreadyProcessed.remove(className); 472 map.remove(className); 473 final Collection<Set<Dependency>> allDependentSets = map.values(); 474 for (Set<Dependency> dependents : allDependentSets) 475 { 476 if (dependents != null) 477 { 478 final Iterator<Dependency> iterator = dependents.iterator(); 479 while (iterator.hasNext()) 480 { 481 if (className.equals(iterator.next().className)) 482 { 483 iterator.remove(); 484 } 485 } 486 } 487 } 488 } 489 } 490 491 @Override 492 public void clear(ComponentPageElement component) 493 { 494 clear(getClassName(component)); 495 } 496 497 @Override 498 public void clear() { 499 map.clear(); 500 alreadyProcessed.clear(); 501 } 502 503 @Override 504 public Set<String> getDependents(String className) 505 { 506 final Set<Dependency> dependents = map.get(className); 507 return dependents != null 508 ? dependents.stream().map(d -> d.className).collect(Collectors.toSet()) 509 : Collections.emptySet(); 510 } 511 512 @Override 513 public Set<String> getDependencies(String className, DependencyType type) 514 { 515 Set<String> dependencies = Collections.emptySet(); 516 if (alreadyProcessed.contains(className)) 517 { 518 dependencies = map.entrySet().stream() 519 .filter(e -> contains(e.getValue(), className, type)) 520 .map(e -> e.getKey()) 521 .collect(Collectors.toSet()); 522 } 523 524 return dependencies; 525 } 526 527 528 private boolean contains(Set<Dependency> dependencies, String className, DependencyType type) 529 { 530 boolean contains = false; 531 for (Dependency dependency : dependencies) 532 { 533 if (dependency.type.equals(type) && dependency.className.equals(className)) 534 { 535 contains = true; 536 break; 537 } 538 } 539 return contains; 540 } 541 542 private void add(ComponentPageElement component, ComponentPageElement dependency, DependencyType type) 543 { 544 add(getClassName(component), getClassName(dependency), type); 545 } 546 547 // Just for unit tests 548 void add(String component, String dependency, DependencyType type, boolean markAsAlreadyProcessed) 549 { 550 if (markAsAlreadyProcessed) 551 { 552 alreadyProcessed.add(component); 553 } 554 if (dependency != null) 555 { 556 add(component, dependency, type); 557 } 558 } 559 560 private void add(Class<?> component, Class<?> dependency, DependencyType type) 561 { 562 if (plasticManager.shouldInterceptClassLoading(dependency.getName())) 563 { 564 add(component.getName(), dependency.getName(), type); 565 } 566 } 567 568 private void add(String component, String dependency, DependencyType type) 569 { 570 Objects.requireNonNull(component, "Parameter component cannot be null"); 571 Objects.requireNonNull(dependency, "Parameter dependency cannot be null"); 572 Objects.requireNonNull(dependency, "Parameter type cannot be null"); 573 synchronized (map) 574 { 575 Set<Dependency> dependents = map.get(dependency); 576 if (dependents == null) 577 { 578 dependents = new HashSet<>(); 579 map.put(dependency, dependents); 580 } 581 dependents.add(new Dependency(component, type)); 582 } 583 } 584 585 @Override 586 public void listen(InvalidationEventHub invalidationEventHub) 587 { 588 invalidationEventHub.addInvalidationCallback(this::listen); 589 } 590 591 // Protected just for testing 592 List<String> listen(List<String> resources) 593 { 594 List<String> furtherDependents = EMPTY_LIST; 595 if (resources.isEmpty()) 596 { 597 clear(); 598 furtherDependents = EMPTY_LIST; 599 } 600 else if (INVALIDATIONS_DISABLED.get() > 0) 601 { 602 furtherDependents = Collections.emptyList(); 603 } 604 // Don't invalidate component dependency information when 605 // PageClassloaderContextManager is merging contexts 606 // TODO: is this still needed since the inception of INVALIDATIONS_ENABLED? 607 else if (!pageClassLoaderContextManager.isMerging()) 608 { 609 furtherDependents = new ArrayList<>(); 610 for (String resource : resources) 611 { 612 613 final Set<String> dependents = getDependents(resource); 614 for (String furtherDependent : dependents) 615 { 616 if (!resources.contains(furtherDependent) && !furtherDependents.contains(furtherDependent)) 617 { 618 furtherDependents.add(furtherDependent); 619 } 620 } 621 622 clear(resource); 623 624 } 625 } 626 return furtherDependents; 627 } 628 629 @Override 630 public void writeFile() 631 { 632 synchronized (this) 633 { 634 try (FileWriter fileWriter = new FileWriter(storedDependencies); 635 BufferedWriter bufferedWriter = new BufferedWriter(fileWriter)) 636 { 637 Set<String> classNames = new HashSet<>(alreadyProcessed.size()); 638 classNames.addAll(map.keySet()); 639 classNames.addAll(alreadyProcessed); 640 JSONArray jsonArray = new JSONArray(); 641 for (String className : classNames) 642 { 643 for (DependencyType dependencyType : DependencyType.values()) 644 { 645 final Set<String> dependencies = getDependencies(className, dependencyType); 646 for (String dependency : dependencies) 647 { 648 JSONObject object = new JSONObject(); 649 object.put("class", className); 650 object.put("type", dependencyType.name()); 651 object.put("dependency", dependency); 652 jsonArray.add(object); 653 } 654 } 655 } 656 bufferedWriter.write(jsonArray.toString()); 657 } 658 catch (IOException e) 659 { 660 throw new TapestryException("Exception trying to read " + FILENAME, e); 661 } 662 } 663 } 664 665 @Override 666 public boolean contains(String className) 667 { 668 return alreadyProcessed.contains(className); 669 } 670 671 @Override 672 public Set<String> getClassNames() 673 { 674 return Collections.unmodifiableSet(new HashSet<>(alreadyProcessed)); 675 } 676 677 @Override 678 public Set<String> getRootClasses() { 679 return alreadyProcessed.stream() 680 .filter(c -> getDependencies(c, DependencyType.USAGE).isEmpty() && 681 getDependencies(c, DependencyType.INJECT_PAGE).isEmpty() && 682 getDependencies(c, DependencyType.SUPERCLASS).isEmpty()) 683 .collect(Collectors.toSet()); 684 } 685 686 private boolean isTransformed(Class clasz) 687 { 688 return plasticManager.shouldInterceptClassLoading(clasz.getName()); 689 } 690 691 @Override 692 public boolean isStoredDependencyInformationPresent() 693 { 694 return storedDependencyInformationPresent; 695 } 696 697 @Override 698 public void disableInvalidations() 699 { 700 INVALIDATIONS_DISABLED.set(INVALIDATIONS_DISABLED.get() + 1); 701 } 702 703 @Override 704 public void enableInvalidations() 705 { 706 INVALIDATIONS_DISABLED.set(INVALIDATIONS_DISABLED.get() - 1); 707 if (INVALIDATIONS_DISABLED.get() < 0) 708 { 709 INVALIDATIONS_DISABLED.set(0); 710 } 711 } 712 713 /** 714 * Only really implemented method is {@link ComponentModel#getBaseResource()} 715 */ 716 private class ComponentModelMock implements ComponentModel 717 { 718 719 final private Resource baseResource; 720 final private boolean isPage; 721 final private String componentClassName; 722 723 public ComponentModelMock(Class<?> component, boolean isPage) 724 { 725 componentClassName = component.getName(); 726 String templateLocation = componentClassName.replace('.', '/'); 727 baseResource = new ClasspathResource(templateLocation); 728 729 this.isPage = isPage; 730 } 731 732 @Override 733 public Resource getBaseResource() 734 { 735 return baseResource; 736 } 737 738 @Override 739 public String getLibraryName() 740 { 741 return null; 742 } 743 744 @Override 745 public boolean isPage() 746 { 747 return isPage; 748 } 749 750 @Override 751 public String getComponentClassName() 752 { 753 return componentClassName; 754 } 755 756 @Override 757 public List<String> getEmbeddedComponentIds() 758 { 759 return null; 760 } 761 762 @Override 763 public EmbeddedComponentModel getEmbeddedComponentModel(String componentId) 764 { 765 return null; 766 } 767 768 @Override 769 public String getFieldPersistenceStrategy(String fieldName) 770 { 771 return null; 772 } 773 774 @Override 775 public Logger getLogger() 776 { 777 return null; 778 } 779 780 @Override 781 public List<String> getMixinClassNames() 782 { 783 return null; 784 } 785 786 @Override 787 public ParameterModel getParameterModel(String parameterName) 788 { 789 return null; 790 } 791 792 @Override 793 public boolean isFormalParameter(String parameterName) 794 { 795 return false; 796 } 797 798 @Override 799 public List<String> getParameterNames() 800 { 801 return null; 802 } 803 804 @Override 805 public List<String> getDeclaredParameterNames() 806 { 807 return null; 808 } 809 810 @Override 811 public List<String> getPersistentFieldNames() 812 { 813 return null; 814 } 815 816 @Override 817 public boolean isRootClass() 818 { 819 return false; 820 } 821 822 @Override 823 public boolean getSupportsInformalParameters() 824 { 825 return false; 826 } 827 828 @Override 829 public ComponentModel getParentModel() 830 { 831 return null; 832 } 833 834 @Override 835 public boolean isMixinAfter() 836 { 837 return false; 838 } 839 840 @Override 841 public String getMeta(String key) 842 { 843 return null; 844 } 845 846 @Override 847 public Set<Class> getHandledRenderPhases() 848 { 849 return null; 850 } 851 852 @Override 853 public boolean handlesEvent(String eventType) 854 { 855 return false; 856 } 857 858 @Override 859 public String[] getOrderForMixin(String mixinClassName) 860 { 861 return null; 862 } 863 864 @Override 865 public boolean handleActivationEventContext() 866 { 867 return false; 868 } 869 870 } 871 872 private static final class Dependency 873 { 874 private final String className; 875 private final DependencyType type; 876 877 public Dependency(String className, DependencyType dependencyType) 878 { 879 super(); 880 this.className = className; 881 this.type = dependencyType; 882 } 883 884 @Override 885 public int hashCode() { 886 return Objects.hash(className, type); 887 } 888 889 @Override 890 public boolean equals(Object obj) 891 { 892 if (this == obj) 893 { 894 return true; 895 } 896 if (!(obj instanceof Dependency)) 897 { 898 return false; 899 } 900 Dependency other = (Dependency) obj; 901 return Objects.equals(className, other.className) && type == other.type; 902 } 903 904 @Override 905 public String toString() 906 { 907 return "Dependency [className=" + className + ", dependencyType=" + type + "]"; 908 } 909 910 } 911 912}