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