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; 018 019import java.lang.ref.WeakReference; 020import java.util.ArrayList; 021import java.util.Collections; 022import java.util.HashMap; 023import java.util.Iterator; 024import java.util.List; 025import java.util.Map; 026 027import org.apache.wicket.Component; 028import org.apache.wicket.MarkupContainer; 029import org.apache.wicket.behavior.Behavior; 030import org.apache.wicket.markup.parser.XmlTag; 031import org.apache.wicket.markup.parser.XmlTag.TagType; 032import org.apache.wicket.markup.parser.filter.HtmlHandler; 033import org.apache.wicket.request.Response; 034import org.apache.wicket.util.lang.Args; 035import org.apache.wicket.util.lang.Generics; 036import org.apache.wicket.util.string.AppendingStringBuffer; 037import org.apache.wicket.util.string.StringValue; 038import org.apache.wicket.util.string.Strings; 039import org.apache.wicket.util.value.IValueMap; 040import org.slf4j.Logger; 041import org.slf4j.LoggerFactory; 042 043 044/** 045 * A subclass of MarkupElement which represents a "significant" markup tag, such as a component open 046 * tag. Insignificant markup tags (those which are merely concerned with markup formatting 047 * operations and do not denote components or component nesting) are coalesced into instances of 048 * RawMarkup (also a subclass of MarkupElement). 049 * 050 * @author Jonathan Locke 051 */ 052public class ComponentTag extends MarkupElement 053{ 054 /** 055 * Factory that creates component during markup root container's initialization. These 056 * components get queued, which allows other components to be dequeued under these auto 057 * components. 058 * 059 * @author igor 060 */ 061 public interface IAutoComponentFactory 062 { 063 /** 064 * Creates a new instance of auto component to be queued 065 * 066 * @param container 067 * The component that will become a parent of the newly created auto component 068 * @param tag 069 * The markup element for the newly created auto component 070 */ 071 Component newComponent(MarkupContainer container, ComponentTag tag); 072 } 073 074 075 /** Log. */ 076 private static final Logger log = LoggerFactory.getLogger(ComponentTag.class); 077 078 /** True if a href attribute is available and autolinking is on */ 079 private final static int AUTOLINK = 0x0001; 080 081 /** True, if attributes have been modified or added */ 082 private final static int MODIFIED = 0x0002; 083 084 /** If true, then the MarkupParser will ignore (remove) it. Temporary working variable */ 085 private final static int IGNORE = 0x0004; 086 087 /** If true, then the tag contain an automatically created wicket id */ 088 private final static int AUTO_COMPONENT = 0x0008; 089 090 /** Some HTML tags are allow to have no close tag, e.g. 'br' */ 091 private final static int NO_CLOSE_TAG = 0x0010; 092 093 /** Render the tag as RawMarkup even if no Component can be found */ 094 public final static int RENDER_RAW = 0x0020; 095 096 /** If true, the current tag contains a child or a descendant with the "wicket:id" attribute */ 097 public final static int CONTAINS_WICKET_ID = 0x0040; 098 099 /** If close tag, than reference to the corresponding open tag */ 100 private ComponentTag openTag; 101 102 /** The underlying xml tag */ 103 protected final XmlTag xmlTag; 104 105 /** Boolean flags. See above */ 106 private int flags = 0; 107 108 /** 109 * By default this is equal to the wicket:id="xxx" attribute value, but may be provided e.g. for 110 * auto-tags 111 */ 112 private String id; 113 114 /** 115 * In case of inherited markup, the base and the extended markups are merged and the information 116 * about the tags origin is lost. In some cases like wicket:head and wicket:link this 117 * information however is required. 118 */ 119 private WeakReference<Class<? extends Component>> markupClassRef = null; 120 121 /** added behaviors */ 122 private List<Behavior> behaviors; 123 124 /** Filters and Handlers may add their own attributes to the tag */ 125 private Map<String, Object> userData; 126 127 private IAutoComponentFactory autoComponentFactory; 128 129 /** 130 * Automatically create a XmlTag, assign the name and the type, and construct a ComponentTag 131 * based on this XmlTag. 132 * 133 * @param name 134 * The name of html tag 135 * @param type 136 * The type of tag 137 */ 138 public ComponentTag(final String name, final XmlTag.TagType type) 139 { 140 final XmlTag tag = new XmlTag(); 141 tag.setName(name); 142 tag.setType(type); 143 xmlTag = tag; 144 } 145 146 /** 147 * Construct. 148 * 149 * @param tag 150 * The underlying xml tag 151 */ 152 public ComponentTag(final XmlTag tag) 153 { 154 super(); 155 xmlTag = tag; 156 } 157 158 /** 159 * Constructor 160 * 161 * @param tag 162 * The ComponentTag tag which this wicket tag is based upon. 163 */ 164 public ComponentTag(final ComponentTag tag) 165 { 166 this(tag.getXmlTag()); 167 tag.copyPropertiesTo(this); 168 } 169 170 /** 171 * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! 172 * 173 * @param flag 174 * The flag to set 175 * @param set 176 * True to turn the flag on, false to turn it off 177 */ 178 public final void setFlag(final int flag, final boolean set) 179 { 180 if (set) 181 { 182 flags |= flag; 183 } 184 else 185 { 186 flags &= ~flag; 187 } 188 } 189 190 /** 191 * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! 192 * 193 * @param flag 194 * The flag to test 195 * @return True if the flag is set 196 */ 197 public final boolean getFlag(final int flag) 198 { 199 return (flags & flag) != 0; 200 } 201 202 /** 203 * Adds a behavior to this component tag. 204 * 205 * @param behavior 206 */ 207 public final void addBehavior(final Behavior behavior) 208 { 209 Args.notNull(behavior, "behavior"); 210 211 if (behaviors == null) 212 { 213 behaviors = Generics.newArrayList(); 214 } 215 behaviors.add(behavior); 216 } 217 218 /** 219 * @return true if this tag has any behaviors added, false otherwise 220 */ 221 public final boolean hasBehaviors() 222 { 223 return behaviors != null; 224 } 225 226 /** 227 * @return read only iterator over added behaviors 228 */ 229 // TODO change to return unmodifiable list which will never be null. See Component.getBehavior 230 public final Iterator<? extends Behavior> getBehaviors() 231 { 232 if (behaviors == null) 233 { 234 List<Behavior> lst = Collections.emptyList(); 235 return lst.iterator(); 236 } 237 238 return Collections.unmodifiableCollection(behaviors).iterator(); 239 } 240 241 /** 242 * Gets whether this tag closes the provided open tag. 243 * 244 * @param open 245 * The open tag 246 * @return True if this tag closes the given open tag 247 */ 248 @Override 249 public final boolean closes(final MarkupElement open) 250 { 251 if (open instanceof ComponentTag) 252 { 253 return (openTag == open) || getXmlTag().closes(((ComponentTag)open).getXmlTag()); 254 } 255 256 return false; 257 } 258 259 /** 260 * If autolink is set to true, href attributes will automatically be converted into Wicket 261 * bookmarkable URLs. 262 * 263 * @param autolink 264 * enable/disable automatic href conversion 265 */ 266 public final void enableAutolink(final boolean autolink) 267 { 268 setFlag(AUTOLINK, autolink); 269 } 270 271 /** 272 * @see org.apache.wicket.markup.parser.XmlTag#getAttributes() 273 * @return The tag#s attributes 274 */ 275 public final IValueMap getAttributes() 276 { 277 return xmlTag.getAttributes(); 278 } 279 280 /** 281 * A convenient method. The same as getAttributes().getString(name) 282 * 283 * @param name 284 * @return The attributes value 285 */ 286 public final String getAttribute(String name) 287 { 288 return xmlTag.getAttributes().getString(name); 289 } 290 291 /** 292 * Get the tag's component id 293 * 294 * @return The component id attribute of this tag 295 */ 296 public final String getId() 297 { 298 return id; 299 } 300 301 /** 302 * Gets the length of the tag in characters. 303 * 304 * @return The tag's length 305 */ 306 public final int getLength() 307 { 308 return xmlTag.getLength(); 309 } 310 311 /** 312 * @return The tag's name 313 */ 314 public final String getName() 315 { 316 return xmlTag.getName(); 317 } 318 319 /** 320 * @return The tag's namespace 321 */ 322 public final String getNamespace() 323 { 324 return xmlTag.getNamespace(); 325 } 326 327 /** 328 * If set, return the corresponding open tag (ComponentTag). 329 * 330 * @return The corresponding open tag 331 */ 332 public final ComponentTag getOpenTag() 333 { 334 return openTag; 335 } 336 337 /** 338 * @see org.apache.wicket.markup.parser.XmlTag#getPos() 339 * @return Tag location (index in input string) 340 */ 341 public final int getPos() 342 { 343 return xmlTag.getPos(); 344 } 345 346 /** 347 * @return the tag type (OPEN, CLOSE or OPEN_CLOSE). 348 */ 349 public final TagType getType() 350 { 351 return xmlTag.getType(); 352 } 353 354 /** 355 * True if autolink is enabled and the tag contains a href attribute. 356 * 357 * @return True, if the href contained should automatically be converted 358 */ 359 public final boolean isAutolinkEnabled() 360 { 361 return getFlag(AUTOLINK); 362 } 363 364 /** 365 * @see org.apache.wicket.markup.parser.XmlTag#isClose() 366 * @return True if this tag is a close tag 367 */ 368 public final boolean isClose() 369 { 370 return xmlTag.isClose(); 371 } 372 373 /** 374 * @see org.apache.wicket.markup.parser.XmlTag#isOpen() 375 * @return True if this tag is an open tag 376 */ 377 public final boolean isOpen() 378 { 379 return xmlTag.isOpen(); 380 } 381 382 /** 383 * @param id 384 * Required component id 385 * @return True if this tag is an open tag with the given component name 386 * @see org.apache.wicket.markup.parser.XmlTag#isOpen() 387 */ 388 public final boolean isOpen(String id) 389 { 390 return xmlTag.isOpen() && this.id.equals(id); 391 } 392 393 /** 394 * @see org.apache.wicket.markup.parser.XmlTag#isOpenClose() 395 * @return True if this tag is an open and a close tag 396 */ 397 public final boolean isOpenClose() 398 { 399 return xmlTag.isOpenClose(); 400 } 401 402 /** 403 * @param id 404 * Required component id 405 * @return True if this tag is an openclose tag with the given component id 406 * @see org.apache.wicket.markup.parser.XmlTag#isOpenClose() 407 */ 408 public final boolean isOpenClose(String id) 409 { 410 return xmlTag.isOpenClose() && this.id.equals(id); 411 } 412 413 /** 414 * Makes this tag object immutable by making the attribute map unmodifiable. Immutable tags 415 * cannot be made mutable again. They can only be copied into new mutable tag objects. 416 */ 417 public final void makeImmutable() 418 { 419 xmlTag.makeImmutable(); 420 } 421 422 /** 423 * Gets this tag if it is already mutable, or a mutable copy of this tag if it is immutable. 424 * 425 * @return This tag if it is already mutable, or a mutable copy of this tag if it is immutable. 426 */ 427 public ComponentTag mutable() 428 { 429 if (xmlTag.isMutable()) 430 { 431 return this; 432 } 433 else 434 { 435 ComponentTag tag = new ComponentTag(xmlTag.mutable()); 436 copyPropertiesTo(tag); 437 return tag; 438 } 439 } 440 441 /** 442 * Copies all internal properties from this tag to <code>dest</code>. This is basically cloning 443 * without instance creation. 444 * 445 * @param dest 446 * tag whose properties will be set 447 */ 448 void copyPropertiesTo(final ComponentTag dest) 449 { 450 dest.id = id; 451 dest.flags = flags; 452 dest.autoComponentFactory = autoComponentFactory; 453 454 if (markupClassRef != null) 455 { 456 dest.setMarkupClass(markupClassRef.get()); 457 } 458 if (behaviors != null) 459 { 460 dest.behaviors = new ArrayList<>(behaviors); 461 } 462 if (userData != null) 463 { 464 dest.userData = new HashMap<>(userData); 465 } 466 } 467 468 /** 469 * @see org.apache.wicket.markup.parser.XmlTag#put(String, boolean) 470 * @param key 471 * The key 472 * @param value 473 * The value 474 */ 475 public final void put(final String key, final boolean value) 476 { 477 xmlTag.put(key, value); 478 setModified(true); 479 } 480 481 /** 482 * @see org.apache.wicket.markup.parser.XmlTag#put(String, int) 483 * @param key 484 * The key 485 * @param value 486 * The value 487 */ 488 public final void put(final String key, final int value) 489 { 490 xmlTag.put(key, value); 491 setModified(true); 492 } 493 494 /** 495 * @see org.apache.wicket.markup.parser.XmlTag#put(String, CharSequence) 496 * @param key 497 * The key 498 * @param value 499 * The value 500 */ 501 public final void put(String key, CharSequence value) 502 { 503 checkIdAttribute(key); 504 putInternal(key, value); 505 } 506 507 /** 508 * THIS METHOD IS NOT PART OF THE PUBLIC API, DO NOT CALL IT 509 * 510 * @see org.apache.wicket.markup.parser.XmlTag#put(String, CharSequence) 511 * @param key 512 * The key 513 * @param value 514 * The value 515 */ 516 public final void putInternal(String key, CharSequence value) 517 { 518 xmlTag.put(key, value); 519 setModified(true); 520 } 521 522 /** 523 * @param key 524 */ 525 private void checkIdAttribute(String key) 526 { 527 if ("id".equalsIgnoreCase(key)) 528 { 529 log.warn("Please use component.setMarkupId(String) to change the tag's 'id' attribute."); 530 } 531 } 532 533 /** 534 * Appends specified {@code value} to the attribute 535 * 536 * @param key 537 * The key 538 * @param value 539 * The value 540 * @param separator 541 * The separator used to append the value 542 */ 543 public final void append(String key, CharSequence value, String separator) 544 { 545 String current = getAttribute(key); 546 if (Strings.isEmpty(current)) 547 { 548 xmlTag.put(key, value); 549 } 550 else 551 { 552 xmlTag.put(key, current + separator + value); 553 } 554 555 setModified(true); 556 } 557 558 /** 559 * @see org.apache.wicket.markup.parser.XmlTag#put(String, StringValue) 560 * @param key 561 * The key 562 * @param value 563 * The value 564 */ 565 public final void put(String key, StringValue value) 566 { 567 xmlTag.put(key, value); 568 setModified(true); 569 } 570 571 /** 572 * @see org.apache.wicket.markup.parser.XmlTag#putAll(Map) 573 * @param map 574 * a key/value map 575 */ 576 public final void putAll(final Map<String, Object> map) 577 { 578 xmlTag.putAll(map); 579 setModified(true); 580 } 581 582 /** 583 * @see org.apache.wicket.markup.parser.XmlTag#remove(String) 584 * @param key 585 * The key to remove 586 */ 587 public final void remove(String key) 588 { 589 xmlTag.remove(key); 590 setModified(true); 591 } 592 593 /** 594 * Gets whether this tag does not require a closing tag. 595 * 596 * @return True if this tag does not require a closing tag 597 */ 598 public final boolean requiresCloseTag() 599 { 600 if (getNamespace() == null) 601 { 602 return HtmlHandler.requiresCloseTag(getName()); 603 } 604 else 605 { 606 return HtmlHandler.requiresCloseTag(getNamespace() + ":" + getName()); 607 } 608 } 609 610 /** 611 * Set the component's id. The value is usually taken from the tag's id attribute, e.g. 612 * wicket:id="componentId". 613 * 614 * @param id 615 * The component's id assigned to the tag. 616 */ 617 public final void setId(final String id) 618 { 619 this.id = id; 620 } 621 622 /** 623 * @see org.apache.wicket.markup.parser.XmlTag#setName(String) 624 * @param name 625 * New tag name 626 */ 627 public final void setName(String name) 628 { 629 xmlTag.setName(name); 630 } 631 632 /** 633 * @see org.apache.wicket.markup.parser.XmlTag#setNamespace(String) 634 * @param namespace 635 * New tag name namespace 636 */ 637 public final void setNamespace(String namespace) 638 { 639 xmlTag.setNamespace(namespace); 640 } 641 642 /** 643 * Assuming this is a close tag, assign it's corresponding open tag. 644 * 645 * @param tag 646 * the open-tag 647 * @throws RuntimeException 648 * if 'this' is not a close tag 649 */ 650 public final void setOpenTag(final ComponentTag tag) 651 { 652 openTag = tag; 653 getXmlTag().setOpenTag(tag.getXmlTag()); 654 } 655 656 /** 657 * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL IT. 658 * 659 * @param type 660 * The new type 661 */ 662 public final void setType(final TagType type) 663 { 664 if (type != xmlTag.getType()) 665 { 666 xmlTag.setType(type); 667 setModified(true); 668 } 669 } 670 671 /** 672 * @return A synthetic close tag for this tag 673 * @deprecated use {{@link #writeSyntheticCloseTag(Response)}} instead 674 */ 675 @Deprecated(since = "9.10.0", forRemoval = true) 676 public final CharSequence syntheticCloseTagString() 677 { 678 AppendingStringBuffer buf = new AppendingStringBuffer(); 679 buf.append("</"); 680 if (getNamespace() != null) 681 { 682 buf.append(getNamespace()).append(':'); 683 } 684 buf.append(getName()).append('>'); 685 686 return buf; 687 } 688 689 /** 690 * Writes the synthetic close tag for this tag to the response 691 * 692 * @param response 693 * The response to write to 694 */ 695 public final void writeSyntheticCloseTag(Response response) 696 { 697 response.write("</"); 698 if (getNamespace() != null) 699 { 700 response.write(getNamespace()); 701 response.write(":"); 702 } 703 response.write(getName()); 704 response.write(">"); 705 } 706 707 /** 708 * @see org.apache.wicket.markup.MarkupElement#toCharSequence() 709 */ 710 @Override 711 public CharSequence toCharSequence() 712 { 713 return xmlTag.toCharSequence(); 714 } 715 716 /** 717 * Converts this object to a string representation. 718 * 719 * @return String version of this object 720 */ 721 @Override 722 public final String toString() 723 { 724 return toCharSequence().toString(); 725 } 726 727 /** 728 * Write the tag to the response 729 * 730 * @param response 731 * The response to write to 732 * @param stripWicketAttributes 733 * if true, wicket:id are removed from output 734 * @param namespace 735 * Wicket's namespace to use 736 */ 737 public final void writeOutput(final Response response, final boolean stripWicketAttributes, 738 final String namespace) 739 { 740 response.write("<"); 741 742 if (getType() == TagType.CLOSE) 743 { 744 response.write("/"); 745 } 746 747 if (getNamespace() != null) 748 { 749 response.write(getNamespace()); 750 response.write(":"); 751 } 752 753 response.write(getName()); 754 755 String namespacePrefix = null; 756 if (stripWicketAttributes == true) 757 { 758 namespacePrefix = namespace + ":"; 759 } 760 761 if (getAttributes().size() > 0) 762 { 763 for (String key : getAttributes().keySet()) 764 { 765 if (key == null) 766 { 767 continue; 768 } 769 770 if ((namespacePrefix == null) || (key.startsWith(namespacePrefix) == false)) 771 { 772 response.write(" "); 773 response.write(key); 774 CharSequence value = getAttribute(key); 775 776 // attributes without values are possible, e.g.' disabled' 777 if (value != null) 778 { 779 response.write("=\""); 780 value = Strings.escapeMarkup(value); 781 response.write(value); 782 response.write("\""); 783 } 784 } 785 } 786 } 787 788 if (getType() == TagType.OPEN_CLOSE) 789 { 790 response.write("/"); 791 } 792 793 response.write(">"); 794 } 795 796 /** 797 * Converts this object to a string representation including useful information for debugging 798 * 799 * @return String version of this object 800 */ 801 @Override 802 public final String toUserDebugString() 803 { 804 return xmlTag.toUserDebugString(); 805 } 806 807 /** 808 * @return Returns the underlying xml tag. 809 */ 810 public final XmlTag getXmlTag() 811 { 812 return xmlTag; 813 } 814 815 /** 816 * Manually mark the ComponentTag being modified. Flagging the tag being modified does not 817 * happen automatically. 818 * 819 * @param modified 820 */ 821 public final void setModified(final boolean modified) 822 { 823 setFlag(MODIFIED, modified); 824 } 825 826 /** 827 * 828 * @return True, if the component tag has been marked modified 829 */ 830 final boolean isModified() 831 { 832 return getFlag(MODIFIED); 833 } 834 835 /** 836 * 837 * @return True if the HTML tag (e.g. br) has no close tag 838 */ 839 public boolean hasNoCloseTag() 840 { 841 return getFlag(NO_CLOSE_TAG); 842 } 843 844 /** 845 * True if the HTML tag (e.g. br) has no close tag 846 * 847 * @param hasNoCloseTag 848 */ 849 public void setHasNoCloseTag(boolean hasNoCloseTag) 850 { 851 setFlag(NO_CLOSE_TAG, hasNoCloseTag); 852 } 853 854 /** 855 * Sets the flag to indicate if the current tag contains a child 856 * or a descendant with the "wicket:id" attribute. 857 * 858 * @param containsWicketId 859 */ 860 public void setContainsWicketId(boolean containsWicketId) 861 { 862 setFlag(CONTAINS_WICKET_ID, containsWicketId); 863 } 864 865 /** 866 * Says if the current tag contains a child or a descendant with the "wicket:id" attribute. 867 * @return true if the current tag contains a child or a descendant with the "wicket:id" attribute. 868 */ 869 public boolean containsWicketId() 870 { 871 return getFlag(CONTAINS_WICKET_ID); 872 } 873 874 /** 875 * In case of inherited markup, the base and the extended markups are merged and the information 876 * about the tags origin is lost. In some cases like wicket:head and wicket:link this 877 * information however is required. 878 * 879 * @return wicketHeaderClass 880 */ 881 public Class<? extends Component> getMarkupClass() 882 { 883 return (markupClassRef == null ? null : markupClassRef.get()); 884 } 885 886 /** 887 * Set the class of wicket component which contains the wicket:head tag. 888 * 889 * @param <C> 890 * 891 * @param wicketHeaderClass 892 * wicketHeaderClass 893 */ 894 public <C extends Component> void setMarkupClass(Class<C> wicketHeaderClass) 895 { 896 if (wicketHeaderClass == null) 897 { 898 markupClassRef = null; 899 } 900 else 901 { 902 markupClassRef = new WeakReference<Class<? extends Component>>(wicketHeaderClass); 903 } 904 } 905 906 /** 907 * @see org.apache.wicket.markup.MarkupElement#equalTo(org.apache.wicket.markup.MarkupElement) 908 */ 909 @Override 910 public boolean equalTo(final MarkupElement element) 911 { 912 if (element instanceof ComponentTag) 913 { 914 final ComponentTag that = (ComponentTag)element; 915 return getXmlTag().equalTo(that.getXmlTag()); 916 } 917 return false; 918 } 919 920 /** 921 * Gets ignore. 922 * 923 * @return If true than MarkupParser will remove it from the markup 924 */ 925 public boolean isIgnore() 926 { 927 return getFlag(IGNORE); 928 } 929 930 /** 931 * Sets ignore. 932 * 933 * @param ignore 934 * If true than MarkupParser will remove it from the markup 935 */ 936 public void setIgnore(boolean ignore) 937 { 938 setFlag(IGNORE, ignore); 939 } 940 941 /** 942 * @return True, if wicket:id has been automatically created (internal component) 943 */ 944 public boolean isAutoComponentTag() 945 { 946 return getFlag(AUTO_COMPONENT); 947 } 948 949 /** 950 * @param auto 951 * True, if wicket:id has been automatically created (internal component) 952 */ 953 public void setAutoComponentTag(boolean auto) 954 { 955 setFlag(AUTO_COMPONENT, auto); 956 } 957 958 /** 959 * Gets userData. 960 * 961 * @param key 962 * The key to store and retrieve the value 963 * @return userData 964 */ 965 public Object getUserData(final String key) 966 { 967 if (userData == null) 968 { 969 return null; 970 } 971 972 return userData.get(key); 973 } 974 975 /** 976 * Sets userData. 977 * 978 * @param key 979 * The key to store and retrieve the value 980 * @param value 981 * The user specific value to store 982 */ 983 public void setUserData(final String key, final Object value) 984 { 985 if (userData == null) 986 { 987 userData = new HashMap<>(); 988 } 989 userData.put(key, value); 990 } 991 992 /** 993 * For subclasses to override. Gets called just before a Component gets rendered. It is 994 * guaranteed that the markupStream is set on the Component and determineVisibility is not yet 995 * called. 996 * 997 * @param component 998 * The component that is about to be rendered 999 * @param markupStream 1000 * The current markup stream 1001 */ 1002 public void onBeforeRender(final Component component, final MarkupStream markupStream) 1003 { 1004 } 1005 1006 public IAutoComponentFactory getAutoComponentFactory() 1007 { 1008 return autoComponentFactory; 1009 } 1010 1011 public void setAutoComponentFactory(IAutoComponentFactory autoComponentFactory) 1012 { 1013 this.autoComponentFactory = autoComponentFactory; 1014 } 1015 1016 1017}