001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.markup.resolver; 018 019import java.util.HashMap; 020import java.util.HashSet; 021import java.util.Map; 022import java.util.Set; 023 024import org.apache.wicket.Component; 025import org.apache.wicket.MarkupContainer; 026import org.apache.wicket.Page; 027import org.apache.wicket.application.IClassResolver; 028import org.apache.wicket.core.util.string.JavaScriptUtils; 029import org.apache.wicket.markup.ComponentTag; 030import org.apache.wicket.markup.IMarkupFragment; 031import org.apache.wicket.markup.MarkupStream; 032import org.apache.wicket.markup.html.TransparentWebMarkupContainer; 033import org.apache.wicket.markup.html.WebMarkupContainer; 034import org.apache.wicket.markup.html.link.BookmarkablePageLink; 035import org.apache.wicket.markup.html.link.DisabledAttributeLinkBehavior; 036import org.apache.wicket.protocol.http.RequestUtils; 037import org.apache.wicket.request.handler.resource.ResourceReferenceRequestHandler; 038import org.apache.wicket.request.mapper.parameter.PageParameters; 039import org.apache.wicket.request.resource.PackageResource; 040import org.apache.wicket.request.resource.PackageResourceReference; 041import org.apache.wicket.request.resource.ResourceReference; 042import org.apache.wicket.util.lang.Packages; 043import org.apache.wicket.util.string.Strings; 044import org.slf4j.Logger; 045import org.slf4j.LoggerFactory; 046 047/** 048 * The AutoLinkResolver is responsible to handle automatic link resolution. Tags are marked 049 * "autolink" by the MarkupParser for all tags with href attribute, such as anchor and link tags 050 * with no explicit wicket id. E.g. <a href="Home.html"> 051 * <p> 052 * If href points to a *.html file, a BookmarkablePageLink will automatically be created, except 053 * for absolute paths, where an ExternalLink is created. 054 * <p> 055 * If href points to a *.html file, it resolves the given URL by searching for a page class, either 056 * relative or absolute, specified by the href attribute of the tag. If relative the href URL must 057 * be relative to the package containing the associated page. An exception is thrown if no Page 058 * class was found. 059 * <p> 060 * If href is no *.html file a static reference to the resource is created. 061 * 062 * @see org.apache.wicket.markup.parser.filter.WicketLinkTagHandler 063 * 064 * @author Juergen Donnerstag 065 * @author Eelco Hillenius 066 */ 067public final class AutoLinkResolver implements IComponentResolver 068{ 069 /** 070 * Abstract implementation that has a helper method for creating a resource reference. 071 */ 072 public static abstract class AbstractAutolinkResolverDelegate 073 implements 074 IAutolinkResolverDelegate 075 { 076 /** 077 * Creates a new auto component that references a package resource. 078 * 079 * @param autoId 080 * the automatically generated id for the auto component 081 * @param pathInfo 082 * the path info object that contains information about the link reference 083 * @param attribute 084 * the attribute to replace the value of 085 * @return a new auto component or null if the path was absolute 086 */ 087 protected final Component newPackageResourceReferenceAutoComponent( 088 final String autoId, final PathInfo pathInfo, 089 final String attribute) 090 { 091 final MarkupContainer container = pathInfo.getContainer(); 092 093 if (!pathInfo.absolute && (pathInfo.path != null) && (pathInfo.path.length() > 0)) 094 { 095 // Href is relative. Create a resource reference pointing at this file 096 097 // <wicket:head> components are handled differently. We can 098 // not use the container, because it is the container the 099 // header has been added to (e.g. the Page). What we need 100 // however, is the component (e.g. a Panel) which 101 // contributed it. 102 MarkupStream markupStream = pathInfo.getMarkupStream(); 103 Class<? extends Component> clazz = markupStream.getContainerClass(); 104 105 // However if the markup stream is a merged markup stream (inheritance), than we 106 // need the class of the markup file which contained the tag. 107 if ((markupStream.get() instanceof ComponentTag) && 108 (markupStream.getTag().getMarkupClass() != null)) 109 { 110 clazz = markupStream.getTag().getMarkupClass(); 111 } 112 113 // Create the component implementing the link 114 ResourceReferenceAutolink autoLink = new ResourceReferenceAutolink(autoId, clazz, 115 pathInfo.reference, attribute, container); 116 if (autoLink.resourceReference != null) 117 { 118 // if the resource reference is null, it means that it the 119 // reference was not found as a package resource 120 return autoLink; 121 } 122 } 123 // else we can't have absolute resource references, at least not at 124 // this time 125 126 // fall back on default processing 127 return null; 128 } 129 } 130 131 /** 132 * Autolink components delegate component resolution to their parent components. Reason: 133 * autolink tags don't have wicket:id and users wouldn't know where to add the component to. 134 * 135 * @author Juergen Donnerstag 136 * @param <T> 137 * type of model object 138 */ 139 public final static class AutolinkBookmarkablePageLink<T> extends BookmarkablePageLink<T> 140 implements 141 IComponentResolver 142 { 143 private static final long serialVersionUID = 1L; 144 145 private final String anchor; 146 147 /** 148 * When using <wicket:link> to let Wicket lookup for pages and create the related links, 149 * it's not possible to change the "setAutoEnable" property, which defaults to true. This 150 * affects the prototype because, sometimes designers _want_ links to be enabled. 151 */ 152 public static boolean autoEnable = true; 153 154 /** 155 * Construct 156 * 157 * @param <C> 158 * 159 * @see BookmarkablePageLink#BookmarkablePageLink(String, Class, PageParameters) 160 * 161 * @param id 162 * @param pageClass 163 * @param parameters 164 * @param anchor 165 */ 166 public <C extends Page> AutolinkBookmarkablePageLink(final String id, 167 final Class<C> pageClass, final PageParameters parameters, final String anchor) 168 { 169 super(id, pageClass, parameters); 170 this.anchor = anchor; 171 setAutoEnable(autoEnable); 172 add(new DisabledAttributeLinkBehavior()); 173 } 174 175 /** 176 * 177 * @see org.apache.wicket.markup.html.link.BookmarkablePageLink#getURL() 178 */ 179 @Override 180 protected CharSequence getURL() 181 { 182 CharSequence url = super.getURL(); 183 if (anchor != null) 184 { 185 url = url + anchor; 186 } 187 188 return url; 189 } 190 191 /** 192 * @see org.apache.wicket.markup.resolver.IComponentResolver#resolve(org.apache.wicket.MarkupContainer, 193 * org.apache.wicket.markup.MarkupStream, org.apache.wicket.markup.ComponentTag) 194 */ 195 @Override 196 public Component resolve(final MarkupContainer container, final MarkupStream markupStream, 197 ComponentTag tag) 198 { 199 return getParent().get(tag.getId()); 200 } 201 } 202 203 /** 204 * Interface to delegate the actual resolving of auto components to. 205 */ 206 public interface IAutolinkResolverDelegate 207 { 208 /** 209 * Returns a new auto component based on the pathInfo object. The auto component must have 210 * the autoId assigned as it's id. Should return null in case the component could not be 211 * created as expected and the default resolving should take place. 212 * 213 * @param autoId 214 * the automatically generated id for the auto component 215 * @param pathInfo 216 * the path info object that contains information about the link reference 217 * @return a new auto component or null in case this method couldn't resolve to a proper 218 * auto component 219 */ 220 Component newAutoComponent(final String autoId, final PathInfo pathInfo); 221 } 222 223 /** 224 * Encapsulates different aspects of a path. For instance, the path 225 * <code>org.apache.wicket.markup.html.tree.Tree/tree.css</code> has extension <code>css</code>, 226 * is relative (absolute == true) and has no page parameters. 227 */ 228 public static final class PathInfo 229 { 230 /** whether the reference is absolute. */ 231 private final boolean absolute; 232 233 /** An optional anchor like #top */ 234 private final String anchor; 235 236 /** The extension if any. */ 237 private final String extension; 238 239 /** The optional page parameters. */ 240 private final PageParameters pageParameters; 241 242 /** The path excluding any parameters. */ 243 private final String path; 244 245 /** The original reference (e.g the full value of a href attribute). */ 246 private final String reference; 247 248 /** The container for this path */ 249 private final MarkupContainer container; 250 251 /** Parent markup stream */ 252 private final MarkupStream markupStream; 253 254 /** 255 * Construct. 256 * 257 * @param reference 258 * the original reference (e.g the full value of a href attribute) 259 */ 260 public PathInfo(final String reference, MarkupContainer container, MarkupStream markupStream) 261 { 262 this.reference = reference; 263 this.container = container; 264 this.markupStream = markupStream; 265 // If href contains URL query parameters .. 266 String infoPath; 267 // get the query string 268 int queryStringPos = reference.indexOf("?"); 269 if (queryStringPos != -1) 270 { 271 final String queryString = reference.substring(queryStringPos + 1); 272 pageParameters = new PageParameters(); 273 RequestUtils.decodeParameters(queryString, pageParameters); 274 infoPath = reference.substring(0, queryStringPos); 275 } 276 else 277 { 278 pageParameters = null; 279 infoPath = reference; 280 } 281 282 absolute = (infoPath.startsWith("/") || infoPath.startsWith("\\")); 283 284 // remove file extension, but remember it 285 String extension = null; 286 int pos = infoPath.lastIndexOf("."); 287 if (pos != -1) 288 { 289 extension = infoPath.substring(pos + 1); 290 infoPath = infoPath.substring(0, pos); 291 } 292 293 String anchor = null; 294 if (extension != null) 295 { 296 pos = extension.indexOf('#'); 297 if (pos != -1) 298 { 299 anchor = extension.substring(pos); 300 extension = extension.substring(0, pos); 301 } 302 } 303 304 // Anchors without path, e.g. "#link" 305 if (anchor == null) 306 { 307 pos = infoPath.indexOf("#"); 308 if (pos != -1) 309 { 310 anchor = infoPath.substring(pos); 311 infoPath = infoPath.substring(0, pos); 312 } 313 } 314 315 path = infoPath; 316 this.extension = extension; 317 this.anchor = anchor; 318 } 319 320 /** 321 * Gets the anchor (e.g. #top) 322 * 323 * @return anchor 324 */ 325 public final String getAnchor() 326 { 327 return anchor; 328 } 329 330 /** 331 * Gets extension. 332 * 333 * @return extension 334 */ 335 public final String getExtension() 336 { 337 return extension; 338 } 339 340 /** 341 * Gets pageParameters. 342 * 343 * @return pageParameters 344 */ 345 public final PageParameters getPageParameters() 346 { 347 return pageParameters; 348 } 349 350 /** 351 * Gets path. 352 * 353 * @return path 354 */ 355 public final String getPath() 356 { 357 return path; 358 } 359 360 /** 361 * Gets reference. 362 * 363 * @return reference 364 */ 365 public final String getReference() 366 { 367 return reference; 368 } 369 370 /** 371 * Gets absolute. 372 * 373 * @return absolute 374 */ 375 public final boolean isAbsolute() 376 { 377 return absolute; 378 } 379 380 /** 381 * Gets container. 382 * 383 * @return container 384 */ 385 public MarkupContainer getContainer() 386 { 387 return container; 388 } 389 390 /** 391 * Gets markup stream 392 * 393 * @return markup stream 394 */ 395 public MarkupStream getMarkupStream() 396 { 397 return markupStream; 398 } 399 } 400 401 /** 402 * Resolves to anchor/ link components. 403 */ 404 private static final class AnchorResolverDelegate extends AbstractAutolinkResolverDelegate 405 { 406 /** the attribute to fetch. */ 407 private static final String attribute = "href"; 408 409 /** 410 * Set of supported extensions for creating bookmarkable page links. Anything that is not in 411 * this list will be handled as a resource reference. 412 */ 413 private final Set<String> supportedPageExtensions = new HashSet<>(4); 414 415 /** 416 * Construct. 417 */ 418 public AnchorResolverDelegate() 419 { 420 // Initialize supported list of file name extension which'll create 421 // bookmarkable pages 422 supportedPageExtensions.add("html"); 423 supportedPageExtensions.add("xml"); 424 supportedPageExtensions.add("wml"); 425 supportedPageExtensions.add("svg"); 426 } 427 428 /** 429 * @see org.apache.wicket.markup.resolver.AutoLinkResolver.IAutolinkResolverDelegate#newAutoComponent(java.lang.String, 430 * org.apache.wicket.markup.resolver.AutoLinkResolver.PathInfo) 431 */ 432 @Override 433 @SuppressWarnings("unchecked") 434 public Component newAutoComponent(final String autoId, PathInfo pathInfo) 435 { 436 final MarkupContainer container = pathInfo.getContainer(); 437 438 if ((pathInfo.extension != null) && 439 supportedPageExtensions.contains(pathInfo.extension)) 440 { 441 // Obviously a href like href="myPkg.MyLabel.html" will do as 442 // well. Wicket will not throw an exception. It accepts it. 443 444 Page page = container.getPage(); 445 final IClassResolver defaultClassResolver = page.getApplication() 446 .getApplicationSettings() 447 .getClassResolver(); 448 String className = Packages.absolutePath(page.getClass(), pathInfo.path); 449 className = Strings.replaceAll(className, "/", ".").toString(); 450 if (className.startsWith(".")) 451 { 452 className = className.substring(1); 453 } 454 455 try 456 { 457 final Class<? extends Page> clazz = (Class<? extends Page>)defaultClassResolver.resolveClass(className); 458 return new AutolinkBookmarkablePageLink<Void>(autoId, clazz, 459 pathInfo.pageParameters, pathInfo.anchor); 460 } 461 catch (ClassNotFoundException ex) 462 { 463 log.warn("Did not find corresponding java class: " + className); 464 // fall through 465 } 466 467 // Make sure base markup pages (inheritance) are handled correct 468 MarkupContainer parentWithContainer = container; 469 if (container.getParent() != null) 470 { 471 parentWithContainer = container.findParentWithAssociatedMarkup(); 472 } 473 if ((parentWithContainer instanceof Page) && !pathInfo.path.startsWith("/") && 474 new MarkupStream(page.getMarkup()).isMergedMarkup()) 475 { 476 IMarkupFragment containerMarkup = container.getMarkup(); 477 MarkupStream containerMarkupStream = new MarkupStream(containerMarkup); 478 if (containerMarkupStream.atTag()) 479 { 480 ComponentTag tag = containerMarkupStream.getTag(); 481 Class<? extends Page> clazz = (Class<? extends Page>)tag.getMarkupClass(); 482 if (clazz != null) 483 { 484 // Href is relative. Resolve the url given relative to 485 // the current page 486 className = Packages.absolutePath(clazz, pathInfo.path); 487 className = Strings.replaceAll(className, "/", ".").toString(); 488 if (className.startsWith(".")) 489 { 490 className = className.substring(1); 491 } 492 493 try 494 { 495 clazz = (Class<? extends Page>)defaultClassResolver.resolveClass(className); 496 return new AutolinkBookmarkablePageLink<Void>(autoId, clazz, 497 pathInfo.getPageParameters(), pathInfo.anchor); 498 } 499 catch (ClassNotFoundException ex) 500 { 501 log.warn("Did not find corresponding java class: " + className); 502 // fall through 503 } 504 } 505 } 506 } 507 } 508 else 509 { 510 // not a registered type for bookmarkable pages; create a link 511 // to a resource instead 512 return newPackageResourceReferenceAutoComponent(autoId, pathInfo, attribute); 513 } 514 515 // fallthrough 516 return null; 517 } 518 } 519 520 /** 521 * Resolver that returns the proper attribute value from a component tag reflecting a URL 522 * reference such as src or href. 523 */ 524 private interface ITagReferenceResolver 525 { 526 /** 527 * Gets the reference attribute value of the tag depending on the type of the tag. For 528 * instance, anchors use the <code>href</code> attribute but script and image references use 529 * the <code>src</code> attribute. 530 * 531 * @param tag 532 * The component tag. Not for modification. 533 * @return the tag value that constitutes the reference 534 */ 535 String getReference(final ComponentTag tag); 536 } 537 538 /** 539 * Autolink component that points to a {@link ResourceReference}. Autolink component delegate 540 * component resolution to their parent components. Reason: autolink tags don't have wicket:id 541 * and users wouldn't know where to add the component to. 542 */ 543 private final static class ResourceReferenceAutolink extends WebMarkupContainer 544 implements 545 IComponentResolver 546 { 547 private static final long serialVersionUID = 1L; 548 549 private final String attribute; 550 551 /** Resource reference */ 552 private final ResourceReference resourceReference; 553 554 private final MarkupContainer parent; 555 556 /** 557 * @param id 558 * @param clazz 559 * @param href 560 * @param attribute 561 * @param parent 562 */ 563 public ResourceReferenceAutolink(final String id, final Class<?> clazz, final String href, 564 final String attribute, MarkupContainer parent) 565 { 566 super(id); 567 568 this.parent = parent; 569 this.attribute = attribute; 570 // Check whether it is a valid resource reference 571 if (PackageResource.exists(clazz, href, getLocale(), getStyle(), getVariation())) 572 { 573 // Create the component implementing the link 574 resourceReference = new PackageResourceReference(clazz, href, null, null, null); 575 } 576 else 577 { 578 // The resource does not exist. Set to null and ignore when 579 // rendering. 580 resourceReference = null; 581 } 582 } 583 584 /** 585 * @see org.apache.wicket.Component#getVariation() 586 */ 587 @Override 588 public String getVariation() 589 { 590 if (parent != null) 591 { 592 return parent.getVariation(); 593 } 594 595 return super.getVariation(); 596 } 597 598 599 /** 600 * Handles this link's tag. 601 * 602 * @param tag 603 * the component tag 604 * @see org.apache.wicket.Component#onComponentTag(ComponentTag) 605 */ 606 @Override 607 protected final void onComponentTag(final ComponentTag tag) 608 { 609 // Default handling for tag 610 super.onComponentTag(tag); 611 612 // only set the href attribute when the resource exists 613 if (resourceReference != null) 614 { 615 // Set href to link to this link's linkClicked method 616 617 ResourceReferenceRequestHandler handler = new ResourceReferenceRequestHandler( 618 resourceReference); 619 CharSequence url = getRequestCycle().urlFor(handler); 620 621 // generate the href attribute 622 tag.put(attribute, url); 623 624 // add nonce if required 625 final var csp = getWebApplication().getCspSettings(); 626 if(csp.isNonceEnabled()) 627 { 628 tag.put(JavaScriptUtils.ATTR_CSP_NONCE, csp.getNonce(getRequestCycle())); 629 } 630 } 631 } 632 633 /** 634 * @see org.apache.wicket.markup.resolver.IComponentResolver#resolve(org.apache.wicket.MarkupContainer, 635 * org.apache.wicket.markup.MarkupStream, org.apache.wicket.markup.ComponentTag) 636 */ 637 @Override 638 public Component resolve(MarkupContainer container, MarkupStream markupStream, 639 ComponentTag tag) 640 { 641 return getParent().get(tag.getId()); 642 } 643 } 644 645 /** 646 * Resolves to {@link ResourceReference} link components. Typically used for header 647 * contributions like javascript and css files. 648 */ 649 private static final class ResourceReferenceResolverDelegate extends 650 AbstractAutolinkResolverDelegate 651 { 652 private final String attribute; 653 654 /** 655 * Construct. 656 * 657 * @param attribute 658 */ 659 public ResourceReferenceResolverDelegate(final String attribute) 660 { 661 this.attribute = attribute; 662 } 663 664 /** 665 * @see org.apache.wicket.markup.resolver.AutoLinkResolver.IAutolinkResolverDelegate#newAutoComponent(java.lang.String, 666 * org.apache.wicket.markup.resolver.AutoLinkResolver.PathInfo) 667 */ 668 @Override 669 public Component newAutoComponent(final String autoId, final PathInfo pathInfo) 670 { 671 return newPackageResourceReferenceAutoComponent(autoId, pathInfo, attribute); 672 } 673 } 674 675 /** 676 * Resolver object that returns the proper attribute value from component tags. 677 */ 678 private static final class TagReferenceResolver implements ITagReferenceResolver 679 { 680 /** the attribute to fetch. */ 681 private final String attribute; 682 683 /** 684 * Construct. 685 * 686 * @param attribute 687 * the attribute to fetch 688 */ 689 public TagReferenceResolver(final String attribute) 690 { 691 this.attribute = attribute; 692 } 693 694 /** 695 * Gets the reference attribute value of the tag depending on the type of the tag. For 696 * instance, anchors use the <code>href</code> attribute but script and image references use 697 * the <code>src</code> attribute. 698 * 699 * @param tag 700 * The component tag. Not for modification. 701 * @return the tag value that constitutes the reference 702 */ 703 @Override 704 public String getReference(final ComponentTag tag) 705 { 706 return tag.getAttributes().getString(attribute); 707 } 708 } 709 710 /** 711 * If no specific resolver is found, always use the href attribute for references. 712 */ 713 private static final TagReferenceResolver DEFAULT_ATTRIBUTE_RESOLVER = new TagReferenceResolver( 714 "href"); 715 716 /** Logging */ 717 private static final Logger log = LoggerFactory.getLogger(AutoLinkResolver.class); 718 719 private static final long serialVersionUID = 1L; 720 721 /** 722 * Autolink resolver delegates for constructing new autolinks reference keyed on tag name (such 723 * as <script> or <a>. 724 */ 725 private final Map<String, IAutolinkResolverDelegate> tagNameToAutolinkResolverDelegates = new HashMap<>(); 726 727 /** 728 * Resolver objects that know what attribute to read for getting the reference keyed on tag name 729 * (such as <script> or <a>. 730 */ 731 private final Map<String, ITagReferenceResolver> tagNameToTagReferenceResolvers = new HashMap<>(); 732 733 /** 734 * Construct. 735 */ 736 public AutoLinkResolver() 737 { 738 // register tag reference resolvers 739 TagReferenceResolver hrefTagReferenceResolver = new TagReferenceResolver("href"); 740 TagReferenceResolver srcTagReferenceResolver = new TagReferenceResolver("src"); 741 tagNameToTagReferenceResolvers.put("a", hrefTagReferenceResolver); 742 tagNameToTagReferenceResolvers.put("link", hrefTagReferenceResolver); 743 tagNameToTagReferenceResolvers.put("script", srcTagReferenceResolver); 744 tagNameToTagReferenceResolvers.put("img", srcTagReferenceResolver); 745 tagNameToTagReferenceResolvers.put("input", srcTagReferenceResolver); 746 tagNameToTagReferenceResolvers.put("embed", srcTagReferenceResolver); 747 748 // register autolink resolver delegates 749 tagNameToAutolinkResolverDelegates.put("a", new AnchorResolverDelegate()); 750 tagNameToAutolinkResolverDelegates.put("link", 751 new ResourceReferenceResolverDelegate("href")); 752 ResourceReferenceResolverDelegate srcResRefResolver = new ResourceReferenceResolverDelegate( 753 "src"); 754 tagNameToAutolinkResolverDelegates.put("script", srcResRefResolver); 755 tagNameToAutolinkResolverDelegates.put("img", srcResRefResolver); 756 tagNameToAutolinkResolverDelegates.put("input", srcResRefResolver); 757 tagNameToAutolinkResolverDelegates.put("embed", srcResRefResolver); 758 } 759 760 /** 761 * Register (add or replace) a new resolver with the tagName and attributeName. The resolver 762 * will be invoked each time an appropriate tag and attribute is found. 763 * 764 * @param tagName 765 * The tag name 766 * @param attributeName 767 * The attribute name 768 * @param resolver 769 * Implements what to do based on the tag and the attribute 770 */ 771 public final void addTagReferenceResolver(final String tagName, final String attributeName, 772 final IAutolinkResolverDelegate resolver) 773 { 774 TagReferenceResolver tagReferenceResolver = new TagReferenceResolver(attributeName); 775 tagNameToTagReferenceResolvers.put(tagName, tagReferenceResolver); 776 777 tagNameToAutolinkResolverDelegates.put(tagName, resolver); 778 } 779 780 /** 781 * Get the resolver registered for 'tagName' 782 * 783 * @param tagName 784 * The tag's name 785 * @return The resolver found. Null, if none registered 786 */ 787 public final IAutolinkResolverDelegate getAutolinkResolverDelegate(final String tagName) 788 { 789 return tagNameToAutolinkResolverDelegates.get(tagName); 790 } 791 792 /** 793 * @see org.apache.wicket.markup.resolver.IComponentResolver#resolve(org.apache.wicket.MarkupContainer, 794 * org.apache.wicket.markup.MarkupStream, org.apache.wicket.markup.ComponentTag) 795 */ 796 @Override 797 public final Component resolve(final MarkupContainer container, 798 final MarkupStream markupStream, final ComponentTag tag) 799 { 800 // Must be marked as autolink tag 801 if (tag.isAutolinkEnabled()) 802 { 803 // get the reference resolver 804 ITagReferenceResolver referenceResolver = tagNameToTagReferenceResolvers.get(tag.getName()); 805 if (referenceResolver == null) 806 { 807 // fallback on default 808 referenceResolver = DEFAULT_ATTRIBUTE_RESOLVER; 809 } 810 811 // get the reference, which is typically the value of e.g. a href or src 812 // attribute 813 String reference = referenceResolver.getReference(tag); 814 815 // create the path info object 816 PathInfo pathInfo = new PathInfo(reference, container, markupStream); 817 818 // Try to find the Page matching the href 819 // Note: to not use tag.getId() because it will be modified while 820 // resolving the link and hence the 2nd render will fail. 821 Component link = resolveAutomaticLink(pathInfo, tag); 822 823 if (log.isDebugEnabled()) 824 { 825 log.debug("Added autolink " + link); 826 } 827 828 // Tell the container, we resolved the id 829 return link; 830 } 831 832 // We were not able to resolve the id 833 return null; 834 } 835 836 /** 837 * Resolves the given tag's page class and page parameters by parsing the tag component name and 838 * then searching for a page class at the absolute or relative URL specified by the href 839 * attribute of the tag. 840 * <p> 841 * None html references are treated similar. 842 * 843 * @param pathInfo 844 * The container where the link is 845 * @param tag 846 * the component tag 847 * @return A BookmarkablePageLink<?> to handle the href 848 */ 849 private Component resolveAutomaticLink(final PathInfo pathInfo, final ComponentTag tag) 850 { 851 final String componentId = tag.getId(); 852 853 // get the tag name, which is something like 'a' or 'script' 854 final String tagName = tag.getName(); 855 856 // By setting the component name, the tag becomes a Wicket component 857 // tag, which must have a associated Component. 858 if (tag.getId() == null) 859 { 860 tag.setAutoComponentTag(true); 861 } 862 863 // now get the resolver delegate 864 IAutolinkResolverDelegate autolinkResolverDelegate = tagNameToAutolinkResolverDelegates.get(tagName); 865 Component autoComponent = null; 866 if (autolinkResolverDelegate != null) 867 { 868 autoComponent = autolinkResolverDelegate.newAutoComponent(componentId, pathInfo); 869 } 870 871 if (autoComponent == null) 872 { 873 // resolving didn't have the desired result or there was no delegate 874 // found; fallback on the default resolving which is a simple 875 // component that leaves the tag unchanged 876 autoComponent = new TransparentWebMarkupContainer(componentId); 877 } 878 879 return autoComponent; 880 } 881}