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