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; 018 019import java.io.Serializable; 020import java.util.Arrays; 021import java.util.Iterator; 022import java.util.List; 023import java.util.Locale; 024import java.util.Optional; 025 026import org.apache.wicket.ajax.IAjaxRegionMarkupIdProvider; 027import org.apache.wicket.application.IComponentInstantiationListener; 028import org.apache.wicket.authorization.Action; 029import org.apache.wicket.authorization.AuthorizationException; 030import org.apache.wicket.authorization.IAuthorizationStrategy; 031import org.apache.wicket.authorization.UnauthorizedActionException; 032import org.apache.wicket.authorization.strategies.page.SimplePageAuthorizationStrategy; 033import org.apache.wicket.behavior.Behavior; 034import org.apache.wicket.core.request.handler.BookmarkableListenerRequestHandler; 035import org.apache.wicket.core.request.handler.ListenerRequestHandler; 036import org.apache.wicket.core.request.handler.PageAndComponentProvider; 037import org.apache.wicket.core.util.lang.WicketObjects; 038import org.apache.wicket.core.util.string.ComponentStrings; 039import org.apache.wicket.event.Broadcast; 040import org.apache.wicket.event.IEvent; 041import org.apache.wicket.event.IEventSink; 042import org.apache.wicket.event.IEventSource; 043import org.apache.wicket.feedback.FeedbackDelay; 044import org.apache.wicket.feedback.FeedbackMessage; 045import org.apache.wicket.feedback.FeedbackMessages; 046import org.apache.wicket.feedback.IFeedback; 047import org.apache.wicket.feedback.IFeedbackContributor; 048import org.apache.wicket.markup.ComponentTag; 049import org.apache.wicket.markup.IMarkupFragment; 050import org.apache.wicket.markup.Markup; 051import org.apache.wicket.markup.MarkupCache; 052import org.apache.wicket.markup.MarkupElement; 053import org.apache.wicket.markup.MarkupException; 054import org.apache.wicket.markup.MarkupNotFoundException; 055import org.apache.wicket.markup.MarkupStream; 056import org.apache.wicket.markup.WicketTag; 057import org.apache.wicket.markup.head.IHeaderResponse; 058import org.apache.wicket.markup.head.StringHeaderItem; 059import org.apache.wicket.markup.html.IHeaderContributor; 060import org.apache.wicket.markup.html.form.Form; 061import org.apache.wicket.markup.html.form.FormComponent; 062import org.apache.wicket.markup.html.internal.HtmlHeaderContainer; 063import org.apache.wicket.markup.html.panel.DefaultMarkupSourcingStrategy; 064import org.apache.wicket.markup.html.panel.IMarkupSourcingStrategy; 065import org.apache.wicket.model.CompoundPropertyModel; 066import org.apache.wicket.model.IComponentAssignedModel; 067import org.apache.wicket.model.IComponentInheritedModel; 068import org.apache.wicket.model.IModel; 069import org.apache.wicket.model.IModelComparator; 070import org.apache.wicket.model.IWrapModel; 071import org.apache.wicket.protocol.http.WicketFilter; 072import org.apache.wicket.request.IRequestHandler; 073import org.apache.wicket.request.Request; 074import org.apache.wicket.request.Response; 075import org.apache.wicket.request.component.IRequestableComponent; 076import org.apache.wicket.request.component.IRequestablePage; 077import org.apache.wicket.request.cycle.RequestCycle; 078import org.apache.wicket.request.mapper.parameter.PageParameters; 079import org.apache.wicket.request.resource.ResourceReference; 080import org.apache.wicket.response.LazyStringResponse; 081import org.apache.wicket.settings.DebugSettings; 082import org.apache.wicket.settings.ExceptionSettings; 083import org.apache.wicket.util.IHierarchical; 084import org.apache.wicket.util.convert.IConverter; 085import org.apache.wicket.util.io.IClusterable; 086import org.apache.wicket.util.lang.Args; 087import org.apache.wicket.util.lang.Classes; 088import org.apache.wicket.util.string.PrependingStringBuffer; 089import org.apache.wicket.util.string.Strings; 090import org.apache.wicket.util.value.ValueMap; 091import org.apache.wicket.util.visit.IVisitFilter; 092import org.apache.wicket.util.visit.IVisitor; 093import org.apache.wicket.util.visit.Visit; 094import org.slf4j.Logger; 095import org.slf4j.LoggerFactory; 096 097 098/** 099 * Component serves as the highest level abstract base class for all components. 100 * 101 * <ul> 102 * <li><b>Identity </b>- All Components must have a non-null id which is retrieved by calling 103 * getId(). The id must be unique within the {@link MarkupContainer} that holds the Component, but 104 * does not have to be globally unique or unique within a Page's component hierarchy.</li> 105 * <li><b>Hierarchy </b>- A component has a parent which can be retrieved with {@link #getParent()}. 106 * If a component is an instance of MarkupContainer, it may have children. In this way it has a 107 * place in the hierarchy of components contained on a given page. 108 * <p> 109 * The path from the Page at the root of the component hierarchy to a given Component is simply the 110 * concatenation with colon separators of each id along the way. For example, the path "a:b:c" would 111 * refer to the component named "c" inside the MarkupContainer named "b" inside the container named 112 * "a". The path to a component can be retrieved by calling {@link #getPath()}. To get a Component 113 * path relative to the page that contains it, you can call {@link #getPageRelativePath()}.</li> 114 * <li><b>LifeCycle </b>- Components participate in the following lifecycle phases: 115 * <ul> 116 * <li><b>Construction </b>- A Component is constructed with the Java language new operator. 117 * Children may be added during construction if the Component is a MarkupContainer. 118 * {@link IComponentInstantiationListener}s are notified of component instantiation. 119 * <p> 120 * {@link #onInitialize()} is called on the component as soon as the component is part of a page's 121 * component tree. At this state the component is able to access its markup.</li> 122 * <li><b>Request Handling </b>- An incoming request is processed by a protocol request handler such 123 * as {@link WicketFilter}. An associated Application object creates {@link Session}, 124 * {@link Request} and {@link Response} objects for use by a given Component in updating its model 125 * and rendering a response. These objects are stored inside a container called {@link RequestCycle} 126 * which is accessible via {@link Component#getRequestCycle()}. The convenience methods 127 * {@link Component#getRequest()}, {@link Component#getResponse()} and 128 * {@link Component#getSession()} provide easy access to the contents of this container.</li> 129 * <li><b>Listener Invocation </b>- If the request references an {@link IRequestListener} on an 130 * existing Component (or one of its {@link Behavior}s, see below), that listener is notified, 131 * allowing arbitrary user code to handle events such as link clicks or form submits. Although 132 * arbitrary listeners are supported in Wicket, the need to implement a new class of listener is 133 * unlikely for a web application and even the need to implement a listener interface directly is 134 * highly discouraged. Instead, calls to listeners are routed through logic specific to the event, 135 * resulting in calls to user code through other overridable methods. See {@link Form} for an 136 * example of a component which listens for events via {@link IRequestListener}.</li> 137 * <li><b>Rendering </b>- Before a page or part of a page (in case of Ajax updates) is rendered, all 138 * containing components are able to prepare for rendering via two hook methods: 139 * {@link #onConfigure()} (regardless whether they are visible or not) and {@link #onBeforeRender()} 140 * (if visible only) . <br> 141 * A markup response is generated by the Component via {@link Component#render()}, which calls 142 * subclass implementation code contained in {@link Component#onRender()}. Once this phase begins, a 143 * Component becomes immutable. Attempts to alter the Component will result in a 144 * WicketRuntimeException.</li> 145 * <li><b>Detachment </b>- Each request cycle finishes by detaching all touched components. 146 * Subclasses should clean up their state by overriding {@link #onDetach()} or more specifically 147 * {@link #detachModels()} if they keep references to models beside the default model.</li> 148 * </ul> 149 * </li> 150 * <li><b>Visibility </b>- If a component is not visible (see {@link #setVisible(boolean)}) it will 151 * not render a response (nor will their children).</li> 152 * <li><b>Enabling </b>- Component subclasses take into account their enabled state (see 153 * {@link #setEnabled(boolean)} when rendering, and in case of a {@link FormComponent} will not not 154 * update its model while the request is handled.</li> 155 * <li><b>Models </b>- The primary responsibility of a component is to use its model (an object that 156 * implements {@link IModel}) to render a response in an appropriate markup language, such as HTML. 157 * In addition, {@link FormComponent}s know how to update their models based on request information, 158 * see {@link FormComponent#updateModel()}. Since the IModel interface is a wrapper around another 159 * object, a convenience method {@link Component#getDefaultModelObject()} is provided to retrieve 160 * the object from its IModel wrapper. A further convenience method, 161 * {@link Component#getDefaultModelObjectAsString()}, is provided for the very common operation of 162 * converting the wrapped object to a String. <br> 163 * The component's model can be passed in the constructor or set via 164 * {@link Component#setDefaultModel(IModel)}. In neither case a model can be created on demand with 165 * {@link #initModel()}.<br> 166 * Note that a component can have more models besides its default model.</li> 167 * <li><b>Behaviors </b>- You can add multiple {@link Behavior}s to any component if you need to 168 * dynamically alter the behavior of components, e.g. manipulate attributes of the markup tag to 169 * which a Component is attached. Behaviors take part in the component's lifecycle through various 170 * callback methods.</li> 171 * <li><b>Locale </b>- The Locale for a Component is available through {@link #getLocale()}, which 172 * delegates to its parent's locale, finally consulting the {@link Session}'s locale.</li> 173 * <li><b>Style </b>- The Session's style ("skin") is available through 174 * {@link org.apache.wicket.Component#getStyle()}. Styles are intended to give a particular look to 175 * all components or resources in a session that is independent of its Locale. For example, a style 176 * might be a set of resources, including images and markup files, which gives the design look of 177 * "ocean" to the user. If the Session's style is set to "ocean" and these resources are given names 178 * suffixed with "_ocean", Wicket's resource management logic will prefer these resources to other 179 * resources, such as default resources, which are not as good of a match.</li> 180 * <li><b>Variation </b>- Whereas styles are Session (user) specific, variations are component 181 * specific. E.g. if the Style is "ocean" and {@link #getVariation()} returnss "NorthSea", than the 182 * resources are given the names suffixed with "_ocean_NorthSea".</li> 183 * <li><b>String Resources </b>- Components can have associated String resources via the 184 * Application's Localizer, which is available through the method {@link Component#getLocalizer()}. 185 * The convenience methods {@link Component#getString(String key)} and 186 * {@link Component#getString(String key, IModel model)} wrap the identical methods on the 187 * Application Localizer for easy access in Components.</li> 188 * <li><b>Feedback Messages </b>- The {@link Component#debug(Serializable)}, 189 * {@link Component#info(Serializable)}, {@link Component#warn(Serializable)}, 190 * {@link Component#error(java.io.Serializable)} and {@link Component#fatal(Serializable)} methods 191 * associate feedback messages with a Component. It is generally not necessary to use these methods 192 * directly since Wicket validators automatically register feedback messages on Components. Feedback 193 * message for a given Component can be retrieved with {@link Component#getFeedbackMessages}.</li> 194 * <li><b>Versioning </b>- Pages are the unit of versioning in Wicket, but fine-grained control of 195 * which Components should participate in versioning is possible via the 196 * {@link Component#setVersioned(boolean)} method. The versioning participation of a given Component 197 * can be retrieved with {@link Component#isVersioned()}.</li> 198 * <li><b>Page </b>- The Page containing any given Component can be retrieved by calling 199 * {@link Component#getPage()}. If the Component is not attached to a Page, an IllegalStateException 200 * will be thrown. An equivalent method, {@link Component#findPage()} is available for special 201 * circumstances where it might be desirable to get a null reference back instead.</li> 202 * <li><b>Application </b>- The {@link #getApplication()} method provides convenient access to the 203 * {@link Application} for a Component.</li> 204 * <li><b>AJAX support</b>- Components can be re-rendered after the whole Page has been rendered at 205 * least once by calling doRender().</li> 206 * <li><b>Security </b>- All components are subject to an {@link IAuthorizationStrategy} which 207 * controls instantiation, visibility and enabling. See {@link SimplePageAuthorizationStrategy} for 208 * a simple implementation.</li> 209 * </ul> 210 * 211 * @author Jonathan Locke 212 * @author Chris Turner 213 * @author Eelco Hillenius 214 * @author Johan Compagner 215 * @author Juergen Donnerstag 216 * @author Igor Vaynberg (ivaynberg) 217 */ 218public abstract class Component 219 implements 220 IClusterable, 221 IConverterLocator, 222 IRequestableComponent, 223 IHeaderContributor, 224 IHierarchical<Component>, 225 IEventSink, 226 IEventSource, 227 IMetadataContext<Serializable, Component>, 228 IFeedbackContributor 229{ 230 /** Log. */ 231 private static final Logger log = LoggerFactory.getLogger(Component.class); 232 233 private static final long serialVersionUID = 1L; 234 235 /** 236 * Action used with IAuthorizationStrategy to determine whether a component is allowed to be 237 * enabled. 238 * <p> 239 * If enabling is authorized, a component may decide by itself (typically using it's enabled 240 * property) whether it is enabled or not. If enabling is not authorized, the given component is 241 * marked disabled, regardless its enabled property. 242 * <p> 243 * When a component is not allowed to be enabled (in effect disabled through the implementation 244 * of this interface), Wicket will try to prevent model updates too. This is not completely fail 245 * safe, as constructs like: 246 * 247 * <pre> 248 * 249 * User u = (User)getModelObject(); 250 * u.setName("got you there!"); 251 * 252 * </pre> 253 * 254 * can't be prevented. Indeed it can be argued that any model protection is best dealt with in 255 * your model objects to be completely secured. Wicket will catch all normal framework-directed 256 * use though. 257 */ 258 public static final Action ENABLE = new Action(Action.ENABLE); 259 260 /** Separator for component paths */ 261 public static final char PATH_SEPARATOR = ':'; 262 /** Path segment that represents this component's parent */ 263 public static final String PARENT_PATH = ".."; 264 265 /** 266 * Action used with IAuthorizationStrategy to determine whether a component and its children are 267 * allowed to be rendered. 268 * <p> 269 * There are two uses for this method: 270 * <ul> 271 * <li>The 'normal' use is for controlling whether a component is rendered without having any 272 * effect on the rest of the processing. If a strategy lets this method return 'false', then the 273 * target component and its children will not be rendered, in the same fashion as if that 274 * component had visibility property 'false'.</li> 275 * <li>The other use is when a component should block the rendering of the whole page. So 276 * instead of 'hiding' a component, what we generally want to achieve here is that we force the 277 * user to logon/give-credentials for a higher level of authorization. For this functionality, 278 * the strategy implementation should throw a {@link AuthorizationException}, which will then be 279 * handled further by the framework.</li> 280 * </ul> 281 * </p> 282 */ 283 public static final Action RENDER = new Action(Action.RENDER); 284 285 /** meta data for user specified markup id */ 286 private static final MetaDataKey<String> MARKUP_ID_KEY = new MetaDataKey<>() 287 { 288 private static final long serialVersionUID = 1L; 289 }; 290 291 /** meta data for user specified markup id */ 292 private static final MetaDataKey<FeedbackMessages> FEEDBACK_KEY = new MetaDataKey<>() 293 { 294 private static final long serialVersionUID = 1L; 295 }; 296 297 298 /** Basic model IModelComparator implementation for normal object models */ 299 private static final IModelComparator defaultModelComparator = new IModelComparator() 300 { 301 private static final long serialVersionUID = 1L; 302 303 @Override 304 public boolean compare(Component component, Object b) 305 { 306 final Object a = component.getDefaultModelObject(); 307 if (a == null && b == null) 308 { 309 return true; 310 } 311 if (a == null || b == null) 312 { 313 return false; 314 } 315 return a.equals(b); 316 } 317 }; 318 319 /** True when a component is being auto-added */ 320 private static final int FLAG_AUTO = 0x0001; 321 322 /** Flag for escaping HTML in model strings */ 323 private static final int FLAG_ESCAPE_MODEL_STRINGS = 0x0002; 324 325 /** Boolean whether this component's model is inheritable. */ 326 static final int FLAG_INHERITABLE_MODEL = 0x0004; 327 328 /** Versioning boolean */ 329 private static final int FLAG_VERSIONED = 0x0008; 330 331 /** Visibility boolean */ 332 private static final int FLAG_VISIBLE = 0x0010; 333 334 /** Render tag boolean */ 335 private static final int FLAG_RENDER_BODY_ONLY = 0x0020; 336 337 /** Ignore attribute modifiers */ 338 private static final int FLAG_IGNORE_ATTRIBUTE_MODIFIER = 0x0040; 339 340 /** True when a component is enabled for model updates and is reachable. */ 341 private static final int FLAG_ENABLED = 0x0080; 342 343 /** Reserved subclass-definable flag bit */ 344 protected static final int FLAG_RESERVED1 = 0x0100; 345 /** Reserved subclass-definable flag bit */ 346 protected static final int FLAG_RESERVED2 = 0x0200; 347 /** Reserved subclass-definable flag bit */ 348 protected static final int FLAG_RESERVED3 = 0x0400; 349 /** Reserved subclass-definable flag bit */ 350 protected static final int FLAG_RESERVED4 = 0x0800; 351 352 /** Boolean whether this component was rendered at least once for tracking changes. */ 353 private static final int FLAG_HAS_BEEN_RENDERED = 0x1000; 354 355 /** 356 * Internal indicator of whether this component may be rendered given the current context's 357 * authorization. It overrides the visible flag in case this is false. Authorization is done 358 * before trying to render any component (otherwise we would end up with a half rendered page in 359 * the buffer) 360 */ 361 private static final int FLAG_IS_RENDER_ALLOWED = 0x2000; 362 363 /** Whether or not the component should print out its markup id into the id attribute */ 364 private static final int FLAG_OUTPUT_MARKUP_ID = 0x4000; 365 366 /** 367 * Output a placeholder tag if the component is not visible. This is useful in ajax mode to go 368 * to visible(false) to visible(true) without the overhead of repainting a visible parent 369 * container 370 */ 371 private static final int FLAG_PLACEHOLDER = 0x8000; 372 373 /** Reserved subclass-definable flag bit */ 374 protected static final int FLAG_RESERVED5 = 0x10000; 375 /** onInitialize called */ 376 protected static final int FLAG_INITIALIZED = 0x20000; 377 /** Set when a component is removed from the hierarchy */ 378 private static final int FLAG_REMOVED = 0x40000; 379 /** Reserved subclass-definable flag bit */ 380 protected static final int FLAG_RESERVED8 = 0x80000; 381 382 /** 383 * Flag that determines whether the model is set. This is necessary because of the way we 384 * represent component state ({@link #data}). We can't distinguish between model and behavior 385 * using instanceof, because one object can implement both interfaces. Thus we need this flag - 386 * when the flag is set, first object in {@link #data} is always model. 387 */ 388 private static final int FLAG_MODEL_SET = 0x100000; 389 390 /** 391 * Flag that restricts visibility of a component when set to true. This is usually used when a 392 * component wants to restrict visibility of another component. Calling 393 * {@link #setVisible(boolean)} on a component does not always have the desired effect because 394 * isVisible() can be overwritten thus this flag offers an alternative that should always work. 395 */ 396 private static final int FLAG_VISIBILITY_ALLOWED = 0x40000000; 397 398 /** 399 * The name of attribute that will hold markup id 400 */ 401 private static final String MARKUP_ID_ATTR_NAME = "id"; 402 403 /** 404 * Meta data key for line precise error logging for the moment of addition. Made package private 405 * for access in {@link MarkupContainer} and {@link Page} 406 */ 407 static final MetaDataKey<String> ADDED_AT_KEY = new MetaDataKey<>() 408 { 409 private static final long serialVersionUID = 1L; 410 }; 411 412 /** 413 * meta data key for line precise error logging for the moment of construction. Made package 414 * private for access in {@link Page} 415 */ 416 static final MetaDataKey<String> CONSTRUCTED_AT_KEY = new MetaDataKey<>() 417 { 418 private static final long serialVersionUID = 1L; 419 }; 420 421 /** Component flags. See FLAG_* for possible non-exclusive flag values. */ 422 private int flags = FLAG_VISIBLE | FLAG_ESCAPE_MODEL_STRINGS | FLAG_VERSIONED | FLAG_ENABLED | 423 FLAG_IS_RENDER_ALLOWED | FLAG_VISIBILITY_ALLOWED | FLAG_RESERVED5 /* page's stateless hint */; 424 425 // @formatter:off 426 private static final short RFLAG_ENABLED_IN_HIERARCHY_VALUE = 0x1; 427 private static final short RFLAG_ENABLED_IN_HIERARCHY_SET = 0x2; 428 private static final short RFLAG_VISIBLE_IN_HIERARCHY_VALUE = 0x4; 429 private static final short RFLAG_VISIBLE_IN_HIERARCHY_SET = 0x8; 430 /** onconfigure has been called */ 431 private static final short RFLAG_CONFIGURED = 0x10; 432 private static final short RFLAG_BEFORE_RENDER_SUPER_CALL_VERIFIED = 0x20; 433 private static final short RFLAG_INITIALIZE_SUPER_CALL_VERIFIED = 0x40; 434 protected static final short RFLAG_CONTAINER_DEQUEING = 0x80; 435 private static final short RFLAG_ON_RE_ADD_SUPER_CALL_VERIFIED = 0x100; 436 /** 437 * Flag that makes we are in before-render callback phase Set after component.onBeforeRender is 438 * invoked (right before invoking beforeRender on children) 439 */ 440 private static final short RFLAG_RENDERING = 0x200; 441 private static final short RFLAG_PREPARED_FOR_RENDER = 0x400; 442 private static final short RFLAG_AFTER_RENDER_SUPER_CALL_VERIFIED = 0x800; 443 private static final short RFLAG_DETACHING = 0x1000; 444 /** True when a component is being removed from the hierarchy */ 445 private static final short RFLAG_REMOVING_FROM_HIERARCHY = 0x2000; 446 /** 447 * This flag tracks if removals have been set on this component. Clearing this key is an 448 * expensive operation. With this flag this expensive call can be avoided. 449 */ 450 protected static final short RFLAG_CONTAINER_HAS_REMOVALS = 0x4000; 451 private static final short RFLAG_ON_CONFIGURE_SUPER_CALL_VERIFIED = (short) 0x8000; 452 // @formatter:on 453 454 /** 455 * Flags that only keep their value during the request. Useful for cache markers, etc. At the 456 * end of the request the value of this variable is reset to 0 457 */ 458 private transient short requestFlags = 0; 459 460 /** Component id. */ 461 private final String id; 462 463 /** Any parent container. */ 464 private MarkupContainer parent; 465 466 /** 467 * Instead of remembering the whole markupId, we just remember the number for this component so 468 * we can "reconstruct" the markupId on demand. While this could be part of {@link #data}, 469 * profiling showed that having it as separate property consumes less memory. 470 */ 471 int generatedMarkupId = -1; 472 473 /** Must only be used by auto components */ 474 private transient IMarkupFragment markup; 475 476 /** 477 * Will be re-created instead of persisted when session is replicated. Markup sourcing strategy 478 * are typically stateless (but don't have to). 479 */ 480 private transient IMarkupSourcingStrategy markupSourcingStrategy; 481 482 /** 483 * The object that holds the component state. 484 * <p> 485 * What's stored here depends on what attributes are set on component. Data can contains 486 * combination of following attributes: 487 * <ul> 488 * <li>Model (indicated by {@link #FLAG_MODEL_SET}) 489 * <li>MetaDataEntry (optionally {@link MetaDataEntry}[] if more metadata entries are present) * 490 * <li>{@link Behavior}(s) added to component. The behaviors are not stored in separate array, 491 * they are part of the {@link #data} array (this is in order to save the space of the pointer 492 * to an empty array as most components have no behaviours). - FIXME - explain why - is this 493 * correct? 494 * </ul> 495 * If there is only one attribute set (i.e. model or MetaDataEntry([]) or one behavior), the 496 * #data object points directly to value of that attribute. Otherwise the data is of type 497 * Object[] where the attributes are ordered as specified above. 498 * <p> 499 */ 500 Object data = null; 501 502 final int data_start() 503 { 504 return getFlag(FLAG_MODEL_SET) ? 1 : 0; 505 } 506 507 final int data_length() 508 { 509 if (data == null) 510 { 511 return 0; 512 } 513 else if (data instanceof Object[] && !(data instanceof MetaDataEntry<?>[])) 514 { 515 return ((Object[])data).length; 516 } 517 else 518 { 519 return 1; 520 } 521 } 522 523 final Object data_get(int index) 524 { 525 if (data == null) 526 { 527 return null; 528 } 529 else if (data instanceof Object[] && !(data instanceof MetaDataEntry<?>[])) 530 { 531 Object[] array = (Object[])data; 532 return index < array.length ? array[index] : null; 533 } 534 else if (index == 0) 535 { 536 return data; 537 } 538 else 539 { 540 return null; 541 } 542 } 543 544 final void data_set(int index, Object object) 545 { 546 if (index > data_length() - 1) 547 { 548 throw new IndexOutOfBoundsException("can not set data at " + index + 549 " when data_length() is " + data_length()); 550 } 551 else if (index == 0 && !(data instanceof Object[] && !(data instanceof MetaDataEntry<?>[]))) 552 { 553 data = object; 554 } 555 else 556 { 557 Object[] array = (Object[])data; 558 array[index] = object; 559 } 560 } 561 562 final void data_add(Object object) 563 { 564 data_insert(-1, object); 565 } 566 567 final void data_insert(int position, Object object) 568 { 569 int currentLength = data_length(); 570 if (position == -1) 571 { 572 position = currentLength; 573 } 574 if (position > currentLength) 575 { 576 throw new IndexOutOfBoundsException("can not insert data at " + position + 577 " when data_length() is " + currentLength); 578 } 579 if (currentLength == 0) 580 { 581 data = object; 582 } 583 else if (currentLength == 1) 584 { 585 Object[] array = new Object[2]; 586 if (position == 0) 587 { 588 array[0] = object; 589 array[1] = data; 590 } 591 else 592 { 593 array[0] = data; 594 array[1] = object; 595 } 596 data = array; 597 } 598 else 599 { 600 Object[] array = new Object[currentLength + 1]; 601 Object[] current = (Object[])data; 602 int after = currentLength - position; 603 if (position > 0) 604 { 605 System.arraycopy(current, 0, array, 0, position); 606 } 607 array[position] = object; 608 if (after > 0) 609 { 610 System.arraycopy(current, position, array, position + 1, after); 611 } 612 data = array; 613 } 614 } 615 616 final void data_remove(int position) 617 { 618 int currentLength = data_length(); 619 620 if (position > currentLength - 1) 621 { 622 throw new IndexOutOfBoundsException(); 623 } 624 else if (currentLength == 1) 625 { 626 data = null; 627 } 628 else if (currentLength == 2) 629 { 630 Object[] current = (Object[])data; 631 if (position == 0) 632 { 633 data = current[1]; 634 } 635 else 636 { 637 data = current[0]; 638 } 639 } 640 else 641 { 642 Object[] current = (Object[])data; 643 data = new Object[currentLength - 1]; 644 645 if (position > 0) 646 { 647 System.arraycopy(current, 0, data, 0, position); 648 } 649 if (position != currentLength - 1) 650 { 651 final int left = currentLength - position - 1; 652 System.arraycopy(current, position + 1, data, position, left); 653 } 654 } 655 } 656 657 /** 658 * Constructor. All components have names. A component's id cannot be null. This is the minimal 659 * constructor of component. It does not register a model. 660 * 661 * @param id 662 * The non-null id of this component 663 * @throws WicketRuntimeException 664 * Thrown if the component has been given a null id. 665 */ 666 public Component(final String id) 667 { 668 this(id, null); 669 } 670 671 /** 672 * Constructor. All components have names. A component's id cannot be null. This constructor 673 * includes a model. 674 * 675 * @param id 676 * The non-null id of this component 677 * @param model 678 * The component's model 679 * 680 * @throws WicketRuntimeException 681 * Thrown if the component has been given a null id. 682 */ 683 public Component(final String id, final IModel<?> model) 684 { 685 checkId(id); 686 this.id = id; 687 688 init(); 689 690 Application application = getApplication(); 691 application.getComponentInstantiationListeners().onInstantiation(this); 692 693 final DebugSettings debugSettings = application.getDebugSettings(); 694 if (debugSettings.isLinePreciseReportingOnNewComponentEnabled() && debugSettings.getComponentUseCheck()) 695 { 696 setMetaData(CONSTRUCTED_AT_KEY, 697 ComponentStrings.toString(this, new MarkupException("constructed"))); 698 } 699 700 if (model != null) 701 { 702 setModelImpl(wrap(model)); 703 } 704 } 705 706 /** 707 * Let subclasses initialize this instance, before constructors are executed. <br> 708 * This method is intentionally <b>not</b> declared protected, to limit overriding to classes in 709 * this package. 710 */ 711 void init() 712 { 713 } 714 715 /** 716 * Get the Markup associated with the Component. If not subclassed, the parent container is 717 * asked to return the markup of this child component. 718 * <p/> 719 * Components like Panel and Border should return the "calling" markup fragment, e.g. 720 * <code><span wicket:id="myPanel">body</span></code>. You may use 721 * Panel/Border/Enclosure.getMarkup(null) to return the associated markup file. And 722 * Panel/Border/Enclosure.getMarkup(child) will search the child in the appropriate markup 723 * fragment. 724 * 725 * @see MarkupContainer#getMarkup(Component) 726 * 727 * @return The markup fragment 728 */ 729 public IMarkupFragment getMarkup() 730 { 731 // Markup already determined or preset? 732 if (markup != null) 733 { 734 return markup; 735 } 736 737 // No parent, than check associated markup files 738 if (parent == null) 739 { 740 // Must be a MarkupContainer to have associated markup file 741 if (this instanceof MarkupContainer) 742 { 743 MarkupContainer container = (MarkupContainer)this; 744 Markup associatedMarkup = container.getAssociatedMarkup(); 745 if (associatedMarkup != null) 746 { 747 markup = associatedMarkup; 748 return markup; 749 } 750 } 751 752 // Don't know how to find the markup 753 throw new MarkupNotFoundException( 754 "Can not determine Markup. Component is not yet connected to a parent. " + 755 toString()); 756 } 757 758 // Ask the parent for find the markup for me 759 markup = parent.getMarkup(this); 760 return markup; 761 } 762 763 /** 764 * @return The 'id' attribute from the associated markup tag 765 */ 766 public final String getMarkupIdFromMarkup() 767 { 768 ComponentTag tag = getMarkupTag(); 769 if (tag != null) 770 { 771 String id = tag.getAttribute("id"); 772 if (Strings.isEmpty(id) == false) 773 { 774 return id.trim(); 775 } 776 } 777 778 return null; 779 } 780 781 /** 782 * Set the markup for the component. Note that the component's markup variable is transient and 783 * thus must only be used for one render cycle. E.g. auto-component are using it. You may also 784 * it if you subclassed getMarkup(). 785 * 786 * @param markup 787 */ 788 public final Component setMarkup(final IMarkupFragment markup) 789 { 790 this.markup = markup; 791 return this; 792 } 793 794 /** 795 * Called on all components before any component is rendered. This method 796 * should be used to configure such things as visibility and enabled flags. 797 * <p> 798 * Overrides must call {@code super.onConfigure()}, usually before any other code 799 * <p> 800 * NOTE: Component hierarchy should not be modified inside this method, instead it should be 801 * done in {@link #onBeforeRender()} 802 * <p> 803 * NOTE: Why this method is preferrable to directly overriding {@link #isVisible()} and 804 * {@link #isEnabled()}? Because those methods are called multiple times even for processing of 805 * a single request. If they contain expensive logic they can slow down the response time of the 806 * entire page. Further, overriding those methods directly on form components may lead to 807 * inconsistent or unexpected state depending on when those methods are called in the form 808 * processing workflow. It is a better practice to push changes to state rather than pull. 809 * <p> 810 * NOTE: If component's visibility or another property depends on another component you may call 811 * {@code other.configure()} followed by {@code other.isVisible()} as mentioned in 812 * {@link #configure()} javadoc. 813 * <p> 814 * NOTE: Why should {@link #onBeforeRender()} not be used for this? Because if a component's 815 * visibility is controlled inside {@link #onBeforeRender()}, once invisible the component will 816 * never become visible again. 817 */ 818 protected void onConfigure() 819 { 820 setRequestFlag(RFLAG_ON_CONFIGURE_SUPER_CALL_VERIFIED, true); 821 } 822 823 /** 824 * This method is meant to be used as an alternative to initialize components. Usually the 825 * component's constructor is used for this task, but sometimes a component cannot be 826 * initialized in isolation, it may need to access its parent component or its markup in order 827 * to fully initialize. This method is invoked once per component's lifecycle when a path exists 828 * from this component to the {@link Page} thus providing the component with an atomic callback 829 * when the component's environment is built out. 830 * <p> 831 * Overrides must call super#{@link #onInitialize()}. Usually this should be the first thing an 832 * override does, much like a constructor. 833 * </p> 834 * <p> 835 * Parent containers are guaranteed to be initialized before their children 836 * </p> 837 * 838 * <p> 839 * It is safe to use {@link #getPage()} in this method 840 * </p> 841 * 842 * <p> 843 * NOTE:The timing of this call is not precise, the contract is that it is called sometime 844 * before {@link Component#onBeforeRender()}. 845 * </p> 846 * 847 */ 848 protected void onInitialize() 849 { 850 setRequestFlag(RFLAG_INITIALIZE_SUPER_CALL_VERIFIED, true); 851 } 852 853 /** 854 * Checks if the component has been initialized - {@link #onInitialize()} has been called 855 * 856 * @return {@code true} if component has been initialized 857 */ 858 public final boolean isInitialized() 859 { 860 return getFlag(FLAG_INITIALIZED); 861 } 862 863 /** 864 * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! 865 * 866 * Used to call {@link #onInitialize()} 867 */ 868 public void internalInitialize() 869 { 870 fireInitialize(); 871 } 872 873 /** 874 * Used to call {@link #onInitialize()} 875 */ 876 final void fireInitialize() 877 { 878 if (!getFlag(FLAG_INITIALIZED)) 879 { 880 setFlag(FLAG_INITIALIZED, true); 881 882 setRequestFlag(RFLAG_INITIALIZE_SUPER_CALL_VERIFIED, false); 883 onInitialize(); 884 verifySuperCall("onInitialize", RFLAG_INITIALIZE_SUPER_CALL_VERIFIED); 885 886 getApplication().getComponentInitializationListeners().onInitialize(this); 887 } 888 else if (getFlag(FLAG_REMOVED)) 889 { 890 setFlag(FLAG_REMOVED, false); 891 892 setRequestFlag(RFLAG_ON_RE_ADD_SUPER_CALL_VERIFIED, false); 893 onReAdd(); 894 verifySuperCall("onReAdd", RFLAG_ON_RE_ADD_SUPER_CALL_VERIFIED); 895 } 896 } 897 898 /** 899 * Called on every component after the page is rendered. Calls hook {@link #onAfterRender()}. 900 */ 901 final void afterRender() 902 { 903 setRequestFlag(RFLAG_PREPARED_FOR_RENDER, false); 904 905 try 906 { 907 setRequestFlag(RFLAG_AFTER_RENDER_SUPER_CALL_VERIFIED, false); 908 909 onAfterRender(); 910 getApplication().getComponentOnAfterRenderListeners().onAfterRender(this); 911 verifySuperCall("onAfterRender", RFLAG_AFTER_RENDER_SUPER_CALL_VERIFIED); 912 } 913 finally 914 { 915 // this flag must always be set to false. 916 setRequestFlag(RFLAG_RENDERING, false); 917 } 918 } 919 920 /** 921 * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! 922 * 923 * Called on all components before any component is rendered. Calls hooks 924 * {@link #configure()} and (if visible) {@link #onBeforeRender()} 925 * and delegates to {@link #beforeRender()} of all child components. 926 */ 927 public final void beforeRender() 928 { 929 if (this instanceof IFeedback) 930 { 931 Optional<FeedbackDelay> delay = FeedbackDelay.get(getRequestCycle()); 932 if (delay.isPresent()) { 933 delay.get().postpone((IFeedback)this); 934 return; 935 } 936 } 937 938 configure(); 939 940 if ((determineVisibility()) && !getRequestFlag(RFLAG_RENDERING) && 941 !getRequestFlag(RFLAG_PREPARED_FOR_RENDER)) 942 { 943 try { 944 setRequestFlag(RFLAG_BEFORE_RENDER_SUPER_CALL_VERIFIED, false); 945 946 Application application = getApplication(); 947 application.getComponentPreOnBeforeRenderListeners().onBeforeRender(this); 948 949 onBeforeRender(); 950 application.getComponentPostOnBeforeRenderListeners().onBeforeRender(this); 951 952 verifySuperCall("onBeforeRender", RFLAG_BEFORE_RENDER_SUPER_CALL_VERIFIED); 953 } catch (RuntimeException ex) { 954 setRequestFlag(RFLAG_PREPARED_FOR_RENDER, false); 955 throw ex; 956 } 957 } 958 } 959 960 /** 961 * Triggers {@link #onConfigure()} to be invoked on this component if it has not already during 962 * this request. 963 * <p> 964 * This method should be invoked before any calls to {@link #isVisible()} or 965 * {@link #isEnabled()}. Usually this method will be called by the framework before the 966 * component is rendered and so users should not need to call it; however, in cases where 967 * visibility or enabled or other state of one component depends on the state of another this 968 * method should be manually invoked on the other component by the user. EG to link visiliby of 969 * two markup containers the following should be done: 970 * 971 * <pre> 972 * final WebMarkupContainer source=new WebMarkupContainer("a") { 973 * protected void onConfigure() { 974 * setVisible(Math.rand()>0.5f); 975 * } 976 * }; 977 * 978 * WebMarkupContainer linked=new WebMarkupContainer("b") { 979 * protected void onConfigure() { 980 * source.configure(); // make sure source is configured 981 * setVisible(source.isVisible()); 982 * } 983 * } 984 * </pre> 985 * 986 * </p> 987 */ 988 public final void configure() 989 { 990 if (!getRequestFlag(RFLAG_CONFIGURED)) 991 { 992 clearEnabledInHierarchyCache(); 993 clearVisibleInHierarchyCache(); 994 995 setRequestFlag(RFLAG_ON_CONFIGURE_SUPER_CALL_VERIFIED, false); 996 onConfigure(); 997 verifySuperCall("onConfigure", RFLAG_ON_CONFIGURE_SUPER_CALL_VERIFIED); 998 999 for (Behavior behavior : getBehaviors()) 1000 { 1001 if (isBehaviorAccepted(behavior)) 1002 { 1003 behavior.onConfigure(this); 1004 } 1005 } 1006 1007 // check authorization 1008 setRenderAllowed(); 1009 1010 internalOnAfterConfigure(); 1011 1012 getApplication().getComponentOnConfigureListeners().onConfigure(this); 1013 1014 setRequestFlag(RFLAG_CONFIGURED, true); 1015 } 1016 } 1017 1018 /** 1019 * Verify super calls of an overridden hook method. 1020 */ 1021 private final void verifySuperCall(String method, short flag) 1022 { 1023 if (!getRequestFlag(flag)) 1024 { 1025 throw new IllegalStateException(String.format("%s() in the hierarchy of %s has not called super.%s()", method, getClass().getName(), method)); 1026 } 1027 1028 setRequestFlag(flag, false); 1029 } 1030 1031 /** 1032 * Called after the {@link #onConfigure()}, but before {@link #onBeforeRender()} 1033 */ 1034 void internalOnAfterConfigure() 1035 { 1036 } 1037 1038 /** 1039 * Redirects to any intercept page previously specified by a call to {@link #redirectToInterceptPage(Page)}. 1040 * The redirect is done by throwing an exception. If there is no intercept page no exception 1041 * will be thrown and the program flow will continue uninterrupted. 1042 * 1043 * Example: 1044 * 1045 * <pre> 1046 * add(new Link("login") 1047 * { 1048 * protected void onClick() 1049 * { 1050 * if (authenticate()) 1051 * { 1052 * continueToOriginalDestination(); 1053 * // if we reach this line there was no intercept page, so go to home page 1054 * setResponsePage(WelcomePage.class); 1055 * } 1056 * } 1057 * }); 1058 * 1059 * @see Component#redirectToInterceptPage(Page) 1060 */ 1061 public final void continueToOriginalDestination() 1062 { 1063 RestartResponseAtInterceptPageException.continueToOriginalDestination(); 1064 } 1065 1066 /** 1067 * Clears any data about previously intercepted page. 1068 */ 1069 public final void clearOriginalDestination() 1070 { 1071 RestartResponseAtInterceptPageException.clearOriginalDestination(); 1072 } 1073 1074 /** 1075 * Registers a debug feedback message for this component 1076 * 1077 * @param message 1078 * The feedback message 1079 */ 1080 @Override 1081 public final void debug(final Serializable message) 1082 { 1083 getFeedbackMessages().debug(this, message); 1084 addStateChange(); 1085 } 1086 1087 /** 1088 * Signals this Component that it is removed from the Component hierarchy. 1089 */ 1090 final void internalOnRemove() 1091 { 1092 setRequestFlag(RFLAG_REMOVING_FROM_HIERARCHY, true); 1093 onRemove(); 1094 setFlag(FLAG_REMOVED, true); 1095 if (getRequestFlag(RFLAG_REMOVING_FROM_HIERARCHY)) 1096 { 1097 throw new IllegalStateException(Component.class.getName() + 1098 " has not been properly removed from hierarchy. Something in the hierarchy of " + 1099 getClass().getName() + 1100 " has not called super.onRemove() in the override of onRemove() method"); 1101 } 1102 Behaviors.onRemove(this); 1103 removeChildren(); 1104 } 1105 1106 /** 1107 * Detaches the component. This is called at the end of the request for all the pages that are 1108 * touched in that request. 1109 */ 1110 @Override 1111 public final void detach() 1112 { 1113 try 1114 { 1115 setRequestFlag(RFLAG_DETACHING, true); 1116 onDetach(); 1117 if (getRequestFlag(RFLAG_DETACHING)) 1118 { 1119 throw new IllegalStateException(Component.class.getName() + 1120 " has not been properly detached. Something in the hierarchy of " + 1121 getClass().getName() + 1122 " has not called super.onDetach() in the override of onDetach() method"); 1123 } 1124 1125 // always detach models because they can be attached without the 1126 // component. eg component has a compoundpropertymodel and one of its 1127 // children component's getmodelobject is called 1128 detachModels(); 1129 1130 // detach any behaviors 1131 Behaviors.detach(this); 1132 } 1133 catch (Exception x) 1134 { 1135 throw new WicketRuntimeException("An error occurred while detaching component: " + toString(true), x); 1136 } 1137 1138 // always detach children because components can be attached 1139 // independently of their parents 1140 detachChildren(); 1141 1142 // reset the model to null when the current model is a IWrapModel and 1143 // the model that created it/wrapped in it is a IComponentInheritedModel 1144 // The model will be created next time. 1145 if (getFlag(FLAG_INHERITABLE_MODEL)) 1146 { 1147 setModelImpl(null); 1148 setFlag(FLAG_INHERITABLE_MODEL, false); 1149 } 1150 1151 clearEnabledInHierarchyCache(); 1152 clearVisibleInHierarchyCache(); 1153 1154 // clear request flags but keep super call verifications WICKET-5417 1155 requestFlags &= (RFLAG_INITIALIZE_SUPER_CALL_VERIFIED | RFLAG_ON_CONFIGURE_SUPER_CALL_VERIFIED | RFLAG_BEFORE_RENDER_SUPER_CALL_VERIFIED); 1156 1157 detachFeedback(); 1158 1159 internalDetach(); 1160 1161 // notify any detach listener 1162 IDetachListener detachListener = getApplication().getFrameworkSettings() 1163 .getDetachListener(); 1164 if (detachListener != null) 1165 { 1166 detachListener.onDetach(this); 1167 } 1168 } 1169 1170 private void detachFeedback() 1171 { 1172 FeedbackMessages feedback = getMetaData(FEEDBACK_KEY); 1173 if (feedback != null) 1174 { 1175 feedback.clear(getApplication().getApplicationSettings() 1176 .getFeedbackMessageCleanupFilter()); 1177 1178 if (feedback.isEmpty()) 1179 { 1180 setMetaData(FEEDBACK_KEY, null); 1181 } 1182 else 1183 { 1184 feedback.detach(); 1185 } 1186 } 1187 } 1188 1189 /** 1190 * Removes the cached markup at the end of the request. For the next request it will be get 1191 * either from the parent's markup or from {@link MarkupCache}. 1192 */ 1193 private void internalDetach() 1194 { 1195 markup = null; 1196 } 1197 1198 /** 1199 * Detaches all models 1200 */ 1201 public void detachModels() 1202 { 1203 // Detach any detachable model from this component 1204 detachModel(); 1205 } 1206 1207 /** 1208 * Registers an error feedback message for this component 1209 * 1210 * @param message 1211 * The feedback message 1212 */ 1213 @Override 1214 public final void error(final Serializable message) 1215 { 1216 getFeedbackMessages().error(this, message); 1217 addStateChange(); 1218 } 1219 1220 /** 1221 * Registers a fatal feedback message for this component 1222 * 1223 * @param message 1224 * The feedback message 1225 */ 1226 @Override 1227 public final void fatal(final Serializable message) 1228 { 1229 getFeedbackMessages().fatal(this, message); 1230 addStateChange(); 1231 } 1232 1233 /** 1234 * Finds the first container parent of this component of the given class. 1235 * 1236 * @param <Z> 1237 * type of parent 1238 * 1239 * 1240 * @param c 1241 * MarkupContainer class to search for 1242 * @return First container parent that is an instance of the given class, or null if none can be 1243 * found 1244 */ 1245 public final <Z> Z findParent(final Class<Z> c) 1246 { 1247 // Start with immediate parent 1248 MarkupContainer current = parent; 1249 1250 // Walk up containment hierarchy 1251 while (current != null) 1252 { 1253 // Is current an instance of this class? 1254 if (c.isInstance(current)) 1255 { 1256 return c.cast(current); 1257 } 1258 1259 // Check parent 1260 current = current.getParent(); 1261 } 1262 1263 // Failed to find component 1264 return null; 1265 } 1266 1267 /** 1268 * @return The nearest markup container with associated markup 1269 */ 1270 public final MarkupContainer findParentWithAssociatedMarkup() 1271 { 1272 MarkupContainer container = parent; 1273 while (container != null) 1274 { 1275 if (container.getAssociatedMarkup() != null) 1276 { 1277 return container; 1278 } 1279 container = container.getParent(); 1280 } 1281 1282 // This should never happen since Page always has associated markup 1283 throw new WicketRuntimeException("Unable to find parent with associated markup"); 1284 } 1285 1286 /** 1287 * Gets interface to application that this component is a part of. 1288 * 1289 * @return The application associated with the session that this component is in. 1290 * @see Application 1291 */ 1292 public final Application getApplication() 1293 { 1294 return Application.get(); 1295 } 1296 1297 /** 1298 * @return A path of the form [page-class-name]:[page-relative-path] 1299 * @see Component#getPageRelativePath() 1300 */ 1301 public final String getClassRelativePath() 1302 { 1303 return getClass().getName() + PATH_SEPARATOR + getPageRelativePath(); 1304 } 1305 1306 /** 1307 * Get the converter that should be used by this component, delegates to 1308 * {@link #createConverter(Class)} and then to the application's 1309 * {@link IConverterLocator}. 1310 * 1311 * @param type 1312 * The type to convert to 1313 * 1314 * @return The converter that should be used by this component 1315 */ 1316 @SuppressWarnings("unchecked") 1317 @Override 1318 public <C> IConverter<C> getConverter(Class<C> type) { 1319 IConverter<?> converter = createConverter(type); 1320 if (converter != null) { 1321 return (IConverter<C>) converter; 1322 } 1323 return getApplication().getConverterLocator().getConverter(type); 1324 } 1325 1326 /** 1327 * Factory method for converters to be used by this component, 1328 * returns {@code null} by default. 1329 * 1330 * @param type 1331 * The type to convert to 1332 * 1333 * @return a converter to be used by this component 1334 */ 1335 protected IConverter<?> createConverter(Class<?> type) { 1336 return null; 1337 } 1338 1339 /** 1340 * Gets whether model strings should be escaped. 1341 * 1342 * @return Returns whether model strings should be escaped 1343 */ 1344 public final boolean getEscapeModelStrings() 1345 { 1346 return getFlag(FLAG_ESCAPE_MODEL_STRINGS); 1347 } 1348 1349 /** 1350 * Gets the id of this component. 1351 * 1352 * @return The id of this component 1353 */ 1354 @Override 1355 public String getId() 1356 { 1357 return id; 1358 } 1359 1360 /** 1361 * @return Innermost model for this component 1362 */ 1363 public final IModel<?> getInnermostModel() 1364 { 1365 return getInnermostModel(getDefaultModel()); 1366 } 1367 1368 /** 1369 * Gets the locale for this component. By default, it searches its parents for a locale. If no 1370 * parents (it's a recursive search) returns a locale, it gets one from the session. 1371 * 1372 * @return The locale to be used for this component 1373 * @see Session#getLocale() 1374 */ 1375 public Locale getLocale() 1376 { 1377 if (parent != null) 1378 { 1379 return parent.getLocale(); 1380 } 1381 return getSession().getLocale(); 1382 } 1383 1384 /** 1385 * Convenience method to provide easy access to the localizer object within any component. 1386 * 1387 * @return The localizer object 1388 */ 1389 public final Localizer getLocalizer() 1390 { 1391 return getApplication().getResourceSettings().getLocalizer(); 1392 } 1393 1394 /** 1395 * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! 1396 * 1397 * Get the first component tag in the associated markup 1398 * 1399 * @return first component tag 1400 */ 1401 protected final ComponentTag getMarkupTag() 1402 { 1403 IMarkupFragment markup = getMarkup(); 1404 if (markup != null) 1405 { 1406 for (int i = 0; i < markup.size(); i++) 1407 { 1408 MarkupElement elem = markup.get(i); 1409 if (elem instanceof ComponentTag) 1410 { 1411 return (ComponentTag)elem; 1412 } 1413 } 1414 } 1415 return null; 1416 } 1417 1418 /** 1419 * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! 1420 * 1421 * Get a copy of the markup's attributes which are associated with the component. 1422 * <p> 1423 * Modifications to the map returned don't change the tags attributes. It is just a copy. 1424 * <p> 1425 * Note: The component must have been added (directly or indirectly) to a container with an 1426 * associated markup file (Page, Panel or Border). 1427 * 1428 * @return markup attributes 1429 */ 1430 public final ValueMap getMarkupAttributes() 1431 { 1432 ComponentTag tag = getMarkupTag(); 1433 if (tag != null) 1434 { 1435 ValueMap attrs = new ValueMap(tag.getAttributes()); 1436 attrs.makeImmutable(); 1437 return attrs; 1438 } 1439 return ValueMap.EMPTY_MAP; 1440 } 1441 1442 /** 1443 * Get the markupId 1444 * 1445 * @return MarkupId 1446 */ 1447 public final Object getMarkupIdImpl() 1448 { 1449 if (generatedMarkupId != -1) 1450 { 1451 return generatedMarkupId; 1452 } 1453 1454 String id = getMetaData(MARKUP_ID_KEY); 1455 1456 // if still no markup id is found, and the component has been attached to a page, try to 1457 // retrieve the id from the markup file. 1458 if (id == null && findPage() != null) 1459 { 1460 id = getMarkupIdFromMarkup(); 1461 } 1462 return id; 1463 } 1464 1465 /** 1466 * Retrieves id by which this component is represented within the markup. This is either the id 1467 * attribute set explicitly via a call to {@link #setMarkupId(String)}, id attribute defined in 1468 * the markup, or an automatically generated id - in that order. 1469 * <p> 1470 * If no id is set and <code>createIfDoesNotExist</code> is false, this method will return null. 1471 * Otherwise it will generate an id value which by default will be unique in the page. This is 1472 * the preferred way as there is no chance of id collision. This will also enable 1473 * {@link #setOutputMarkupId(boolean)}. 1474 * <p> 1475 * 1476 * <p> 1477 * Note: This method should only be called after the component or its parent have been added to 1478 * the page. 1479 * 1480 * @param createIfDoesNotExist 1481 * When there is no existing markup id, determines whether it should be generated or 1482 * whether <code>null</code> should be returned. 1483 * 1484 * @return markup id of the component 1485 */ 1486 public String getMarkupId(boolean createIfDoesNotExist) 1487 { 1488 IMarkupIdGenerator markupIdGenerator = getApplication().getMarkupSettings().getMarkupIdGenerator(); 1489 String markupId = markupIdGenerator.generateMarkupId(this, createIfDoesNotExist); 1490 return markupId; 1491 } 1492 1493 /** 1494 * Retrieves id by which this component is represented within the markup. This is either the id 1495 * attribute set explicitly via a call to {@link #setMarkupId(String)}, id attribute defined in 1496 * the markup, or an automatically generated id - in that order. 1497 * <p> 1498 * If no explicit id is set this function will generate an id value that will be unique in the 1499 * page. This is the preferred way as there is no chance of id collision. This will also enable 1500 * {@link #setOutputMarkupId(boolean)}. 1501 * <p> 1502 * Note: This method should only be called after the component or its parent have been added to 1503 * the page. 1504 * 1505 * @return markup id of the component 1506 */ 1507 public String getMarkupId() 1508 { 1509 return getMarkupId(true); 1510 } 1511 1512 /** 1513 * Gets metadata for this component using the given key. 1514 * 1515 * @param <M> 1516 * The type of the metadata. 1517 * 1518 * @param key 1519 * The key for the data 1520 * @return The metadata or null of no metadata was found for the given key 1521 * @see MetaDataKey 1522 */ 1523 @Override 1524 public final <M extends Serializable> M getMetaData(final MetaDataKey<M> key) 1525 { 1526 return key.get(getMetaData()); 1527 } 1528 1529 /** 1530 * Gets the meta data entries for this component as an array of {@link MetaDataEntry} objects. 1531 * 1532 * @return the meta data entries for this component 1533 */ 1534 private MetaDataEntry<?>[] getMetaData() 1535 { 1536 MetaDataEntry<?>[] metaData = null; 1537 1538 // index where we should expect the entry 1539 int index = getFlag(FLAG_MODEL_SET) ? 1 : 0; 1540 1541 int length = data_length(); 1542 1543 if (index < length) 1544 { 1545 Object object = data_get(index); 1546 if (object instanceof MetaDataEntry<?>[]) 1547 { 1548 metaData = (MetaDataEntry<?>[])object; 1549 } 1550 else if (object instanceof MetaDataEntry) 1551 { 1552 metaData = new MetaDataEntry[] { (MetaDataEntry<?>)object }; 1553 } 1554 } 1555 1556 return metaData; 1557 } 1558 1559 /** 1560 * Gets the model. It returns the object that wraps the backing model. 1561 * 1562 * @return The model 1563 */ 1564 public final IModel<?> getDefaultModel() 1565 { 1566 IModel<?> model = getModelImpl(); 1567 // If model is null 1568 if (model == null) 1569 { 1570 // give subclass a chance to lazy-init model 1571 model = initModel(); 1572 setModelImpl(model); 1573 } 1574 1575 return model; 1576 } 1577 1578 /** 1579 * Gets the backing model object. Unlike getDefaultModel().getObject(), this method returns null 1580 * for a null model. 1581 * 1582 * @return The backing model object 1583 */ 1584 public final Object getDefaultModelObject() 1585 { 1586 final IModel<?> model = getDefaultModel(); 1587 if (model != null) 1588 { 1589 try 1590 { 1591 // Get model value for this component. 1592 return model.getObject(); 1593 } 1594 catch (Exception ex) 1595 { 1596 // wrap the exception so that it brings info about the component 1597 WicketRuntimeException rex = new WicketRuntimeException( 1598 "An error occurred while getting the model object for Component: " + 1599 this.toString(true), ex); 1600 throw rex; 1601 } 1602 } 1603 return null; 1604 } 1605 1606 /** 1607 * Gets a model object as a string. Depending on the "escape model strings" flag of the 1608 * component, the string is either HTML escaped or not. "HTML escaped" meaning that only HTML 1609 * sensitive chars are escaped but not all none-ascii chars. Proper HTML encoding should be used 1610 * instead. In case you really need a fully escaped model string you may call 1611 * {@link Strings#escapeMarkup(CharSequence, boolean, boolean)} on the model string returned. 1612 * 1613 * @see Strings#escapeMarkup(CharSequence, boolean, boolean) 1614 * @see #getEscapeModelStrings() 1615 * 1616 * @return Model object for this component as a string 1617 */ 1618 public final String getDefaultModelObjectAsString() 1619 { 1620 return getDefaultModelObjectAsString(getDefaultModelObject()); 1621 } 1622 1623 /** 1624 * Gets a model object as a string. Depending on the "escape model strings" flag of the 1625 * component, the string is either HTML escaped or not. "HTML escaped" meaning that only HTML 1626 * sensitive chars are escaped but not all none-ascii chars. Proper HTML encoding should be used 1627 * instead. In case you really need a fully escaped model string you may call 1628 * {@link Strings#escapeMarkup(CharSequence, boolean, boolean)} on the model string returned. 1629 * 1630 * @see Strings#escapeMarkup(CharSequence, boolean, boolean) 1631 * @see #getEscapeModelStrings() 1632 * 1633 * @param modelObject 1634 * Model object to convert to string 1635 * @return The string 1636 */ 1637 @SuppressWarnings({ "rawtypes", "unchecked" }) 1638 public final String getDefaultModelObjectAsString(final Object modelObject) 1639 { 1640 if (modelObject != null) 1641 { 1642 // Get converter 1643 final Class<?> objectClass = modelObject.getClass(); 1644 1645 final IConverter converter = getConverter(objectClass); 1646 1647 // Model string from property 1648 final String modelString = converter.convertToString(modelObject, getLocale()); 1649 1650 if (modelString != null) 1651 { 1652 // If we should escape the markup 1653 if (getFlag(FLAG_ESCAPE_MODEL_STRINGS)) 1654 { 1655 // Escape HTML sensitive characters only. Not all none-ascii chars 1656 return Strings.escapeMarkup(modelString, false, false).toString(); 1657 } 1658 return modelString; 1659 } 1660 } 1661 return ""; 1662 } 1663 1664 /** 1665 * Gets whether or not component will output id attribute into the markup. id attribute will be 1666 * set to the value returned from {@link Component#getMarkupId()}. 1667 * 1668 * @return whether or not component will output id attribute into the markup 1669 */ 1670 public final boolean getOutputMarkupId() 1671 { 1672 return getFlag(FLAG_OUTPUT_MARKUP_ID); 1673 } 1674 1675 /** 1676 * Gets whether or not an invisible component will render a placeholder tag. 1677 * 1678 * @return true if a placeholder tag should be rendered 1679 */ 1680 public final boolean getOutputMarkupPlaceholderTag() 1681 { 1682 return getFlag(FLAG_PLACEHOLDER); 1683 } 1684 1685 /** 1686 * Gets the page holding this component. 1687 * 1688 * @return The page holding this component 1689 * @throws WicketRuntimeException 1690 * Thrown if component is not yet attached to a Page. 1691 * @see #findPage() 1692 */ 1693 @Override 1694 public final Page getPage() 1695 { 1696 // Search for nearest Page 1697 final Page page = findPage(); 1698 1699 // If no Page was found 1700 if (page == null) 1701 { 1702 // Give up with a nice exception 1703 throw new WicketRuntimeException("No Page found for component: " + this.toString(true) 1704 + ". You probably forgot to add it to its parent component."); 1705 } 1706 1707 return page; 1708 } 1709 1710 /** 1711 * Gets the path to this component relative to its containing page, i.e. without leading page 1712 * id. 1713 * 1714 * @return The path to this component relative to the page it is in 1715 */ 1716 @Override 1717 public final String getPageRelativePath() 1718 { 1719 return Strings.afterFirstPathComponent(getPath(), PATH_SEPARATOR); 1720 } 1721 1722 /** 1723 * Gets any parent container, or null if there is none. 1724 * 1725 * @return Any parent container, or null if there is none 1726 */ 1727 @Override 1728 public final MarkupContainer getParent() 1729 { 1730 return parent; 1731 } 1732 1733 /** 1734 * Gets this component's path. 1735 * 1736 * @return Colon separated path to this component in the component hierarchy 1737 */ 1738 public final String getPath() 1739 { 1740 final PrependingStringBuffer buffer = new PrependingStringBuffer(32); 1741 for (Component c = this; c != null; c = c.getParent()) 1742 { 1743 if (buffer.length() > 0) 1744 { 1745 buffer.prepend(PATH_SEPARATOR); 1746 } 1747 buffer.prepend(c.getId()); 1748 } 1749 return buffer.toString(); 1750 } 1751 1752 /** 1753 * If false the component's tag will be printed as well as its body (which is default). If true 1754 * only the body will be printed, but not the component's tag. 1755 * 1756 * @return If true, the component tag will not be printed 1757 */ 1758 public final boolean getRenderBodyOnly() 1759 { 1760 return getFlag(FLAG_RENDER_BODY_ONLY); 1761 } 1762 1763 /** 1764 * @return The request for this component's active request cycle 1765 */ 1766 public final Request getRequest() 1767 { 1768 RequestCycle requestCycle = getRequestCycle(); 1769 if (requestCycle == null) 1770 { 1771 // Happens often with WicketTester when one forgets to call 1772 // createRequestCycle() 1773 throw new WicketRuntimeException("No RequestCycle is currently set!"); 1774 } 1775 return requestCycle.getRequest(); 1776 } 1777 1778 /** 1779 * Gets the active request cycle for this component 1780 * 1781 * @return The request cycle 1782 */ 1783 public final RequestCycle getRequestCycle() 1784 { 1785 return RequestCycle.get(); 1786 } 1787 1788 /** 1789 * @return The response for this component's active request cycle 1790 */ 1791 public final Response getResponse() 1792 { 1793 return getRequestCycle().getResponse(); 1794 } 1795 1796 /** 1797 * Gets the current Session object. 1798 * 1799 * @return The Session that this component is in 1800 */ 1801 public Session getSession() 1802 { 1803 return Session.get(); 1804 } 1805 1806 /** 1807 * @return Size of this Component in bytes. Returns {@code 0} - if the size cannot be calculated for some reason 1808 */ 1809 public long getSizeInBytes() 1810 { 1811 final MarkupContainer originalParent = parent; 1812 parent = null; 1813 long size = 0; 1814 try 1815 { 1816 size = WicketObjects.sizeof(this); 1817 } 1818 catch (Exception e) 1819 { 1820 log.error("Exception getting size for component " + this, e); 1821 } 1822 parent = originalParent; 1823 return size; 1824 } 1825 1826 /** 1827 * @param key 1828 * Key of string resource in property file 1829 * @return The String 1830 * @see Localizer 1831 */ 1832 public final String getString(final String key) 1833 { 1834 return getString(key, null); 1835 } 1836 1837 /** 1838 * @param key 1839 * The resource key 1840 * @param model 1841 * The model 1842 * @return The formatted string 1843 * @see Localizer 1844 */ 1845 public final String getString(final String key, final IModel<?> model) 1846 { 1847 return getLocalizer().getString(key, this, model); 1848 } 1849 1850 /** 1851 * @param key 1852 * The resource key 1853 * @param model 1854 * The model 1855 * @param defaultValue 1856 * A default value if the string cannot be found 1857 * @return The formatted string 1858 * @see Localizer 1859 */ 1860 public final String getString(final String key, final IModel<?> model, final String defaultValue) 1861 { 1862 return getLocalizer().getString(key, this, model, defaultValue); 1863 } 1864 1865 /** 1866 * A convenience method to access the Sessions's style. 1867 * 1868 * @return The style of this component respectively the style of the Session. 1869 * 1870 * @see org.apache.wicket.Session#getStyle() 1871 */ 1872 public final String getStyle() 1873 { 1874 Session session = getSession(); 1875 if (session == null) 1876 { 1877 throw new WicketRuntimeException("Wicket Session object not available"); 1878 } 1879 return session.getStyle(); 1880 } 1881 1882 /** 1883 * Gets the variation string of this component that will be used to look up markup for this 1884 * component. Subclasses can override this method to define by an instance what markup variation 1885 * should be picked up. By default it will return null or the value of a parent. 1886 * 1887 * @return The variation of this component. 1888 */ 1889 public String getVariation() 1890 { 1891 if (parent != null) 1892 { 1893 return parent.getVariation(); 1894 } 1895 return null; 1896 } 1897 1898 /** 1899 * Gets whether this component was rendered at least once. 1900 * 1901 * @return true if the component has been rendered before, false if it is merely constructed 1902 */ 1903 public final boolean hasBeenRendered() 1904 { 1905 return getFlag(FLAG_HAS_BEEN_RENDERED); 1906 } 1907 1908 /** 1909 * Gets feedback messages for this component. This method will instantiate a 1910 * {@link FeedbackMessages} instance and add it to the component metadata, even when called on a 1911 * component that has no feedback messages, to avoid the overhead use 1912 * {@link #hasFeedbackMessage()} 1913 * 1914 * @return feedback messages instance 1915 */ 1916 public FeedbackMessages getFeedbackMessages() 1917 { 1918 FeedbackMessages messages = getMetaData(FEEDBACK_KEY); 1919 if (messages == null) 1920 { 1921 messages = new FeedbackMessages(); 1922 setMetaData(FEEDBACK_KEY, messages); 1923 } 1924 return messages; 1925 } 1926 1927 /** 1928 * @return True if this component has an error message 1929 */ 1930 public final boolean hasErrorMessage() 1931 { 1932 FeedbackMessages messages = getMetaData(FEEDBACK_KEY); 1933 if (messages == null) 1934 { 1935 return false; 1936 } 1937 return messages.hasMessage(FeedbackMessage.ERROR); 1938 } 1939 1940 /** 1941 * @return True if this component has some kind of feedback message 1942 * 1943 */ 1944 public final boolean hasFeedbackMessage() 1945 { 1946 FeedbackMessages messages = getMetaData(FEEDBACK_KEY); 1947 if (messages == null) 1948 { 1949 return false; 1950 } 1951 return messages.size() > 0; 1952 } 1953 1954 /** 1955 * Registers an informational feedback message for this component 1956 * 1957 * @param message 1958 * The feedback message 1959 */ 1960 @Override 1961 public final void info(final Serializable message) 1962 { 1963 getFeedbackMessages().info(this, message); 1964 addStateChange(); 1965 } 1966 1967 /** 1968 * Registers an success feedback message for this component 1969 * 1970 * @param message 1971 * The feedback message 1972 */ 1973 @Override 1974 public final void success(final Serializable message) 1975 { 1976 getFeedbackMessages().success(this, message); 1977 addStateChange(); 1978 } 1979 1980 /** 1981 * Authorizes an action for a component. 1982 * 1983 * @param action 1984 * The action to authorize 1985 * @return True if the action is allowed 1986 * @throws AuthorizationException 1987 * Can be thrown by implementation if action is unauthorized 1988 */ 1989 public final boolean isActionAuthorized(Action action) 1990 { 1991 IAuthorizationStrategy authorizationStrategy = getSession().getAuthorizationStrategy(); 1992 if (authorizationStrategy != null) 1993 { 1994 return authorizationStrategy.isActionAuthorized(this, action); 1995 } 1996 return true; 1997 } 1998 1999 /** 2000 * @return true if this component is authorized to be enabled, false otherwise 2001 */ 2002 public final boolean isEnableAllowed() 2003 { 2004 return isActionAuthorized(ENABLE); 2005 } 2006 2007 /** 2008 * Gets whether this component is enabled. Specific components may decide to implement special 2009 * behavior that uses this property, like web form components that add a disabled='disabled' 2010 * attribute when enabled is false. 2011 * 2012 * @return Whether this component is enabled. 2013 */ 2014 public boolean isEnabled() 2015 { 2016 return getFlag(FLAG_ENABLED); 2017 } 2018 2019 /** 2020 * Checks the security strategy if the {@link Component#RENDER} action is allowed on this 2021 * component 2022 * 2023 * @return true if {@link Component#RENDER} action is allowed, false otherwise 2024 */ 2025 public final boolean isRenderAllowed() 2026 { 2027 return getFlag(FLAG_IS_RENDER_ALLOWED); 2028 } 2029 2030 /** 2031 * Returns if the component is stateless or not. It checks the stateless hint if that is false 2032 * it returns directly false. If that is still true it checks all its behaviors if they can be 2033 * stateless. 2034 * 2035 * @return whether the component is stateless. 2036 */ 2037 public final boolean isStateless() 2038 { 2039 if ((isVisibleInHierarchy() && isEnabledInHierarchy()) == false && canCallListener() == false) 2040 { 2041 // the component is either invisible or disabled and it can't call listeners 2042 // then pretend the component is stateless 2043 return true; 2044 } 2045 2046 if (!getStatelessHint()) 2047 { 2048 return false; 2049 } 2050 2051 for (Behavior behavior : getBehaviors()) 2052 { 2053 if (!behavior.getStatelessHint(this)) 2054 { 2055 return false; 2056 } 2057 } 2058 return true; 2059 } 2060 2061 /** 2062 * @return {@code true} if this component should notify its holding page about changes in its 2063 * state. If a {@link Page} is not versioned then it wont track changes in its 2064 * components and will use the same {@link Page#getPageId()} during its lifetime 2065 */ 2066 public boolean isVersioned() 2067 { 2068 // Is the component itself versioned? 2069 if (!getFlag(FLAG_VERSIONED)) 2070 { 2071 return false; 2072 } 2073 else 2074 { 2075 // If there's a parent and this component is versioned 2076 if (parent != null) 2077 { 2078 // Check if the parent is unversioned. If any parent 2079 // (recursively) is unversioned, then this component is too 2080 if (!parent.isVersioned()) 2081 { 2082 return false; 2083 } 2084 } 2085 return true; 2086 } 2087 } 2088 2089 /** 2090 * Gets whether this component and any children are visible. 2091 * <p> 2092 * WARNING: this method can be called multiple times during a request. If you override this 2093 * method, it is a good idea to keep it cheap in terms of processing. Alternatively, you can 2094 * call {@link #setVisible(boolean)}. 2095 * <p> 2096 * 2097 * @return True if component and any children are visible 2098 */ 2099 public boolean isVisible() 2100 { 2101 return getFlag(FLAG_VISIBLE); 2102 } 2103 2104 /** 2105 * Checks if the component itself and all its parents are visible. 2106 * 2107 * @return true if the component and all its parents are visible. 2108 */ 2109 public final boolean isVisibleInHierarchy() 2110 { 2111 if (getRequestFlag(RFLAG_VISIBLE_IN_HIERARCHY_SET)) 2112 { 2113 return getRequestFlag(RFLAG_VISIBLE_IN_HIERARCHY_VALUE); 2114 } 2115 2116 final boolean state; 2117 Component parent = getParent(); 2118 if (parent != null && !parent.isVisibleInHierarchy()) 2119 { 2120 state = false; 2121 } 2122 else 2123 { 2124 state = determineVisibility(); 2125 } 2126 2127 setRequestFlag(RFLAG_VISIBLE_IN_HIERARCHY_SET, true); 2128 setRequestFlag(RFLAG_VISIBLE_IN_HIERARCHY_VALUE, state); 2129 return state; 2130 } 2131 2132 /** 2133 * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! 2134 * 2135 * Sets the RENDERING flag and removes the PREPARED_FOR_RENDER flag on component and it's 2136 * children. 2137 * 2138 * @param setRenderingFlag 2139 * if this is false only the PREPARED_FOR_RENDER flag is removed from component, the 2140 * RENDERING flag is not set. 2141 */ 2142 public final void markRendering(boolean setRenderingFlag) 2143 { 2144 internalMarkRendering(setRenderingFlag); 2145 } 2146 2147 /** 2148 * Called to indicate that the model content for this component has been changed 2149 */ 2150 public final void modelChanged() 2151 { 2152 // Call user code 2153 internalOnModelChanged(); 2154 onModelChanged(); 2155 } 2156 2157 /** 2158 * Called to indicate that the model content for this component is about to change 2159 */ 2160 public final void modelChanging() 2161 { 2162 checkHierarchyChange(this); 2163 2164 // Call user code 2165 onModelChanging(); 2166 2167 // Tell the page that our model changed 2168 final Page page = findPage(); 2169 if (page != null) 2170 { 2171 page.componentModelChanging(this); 2172 } 2173 } 2174 2175 /** 2176 * Redirects browser to an intermediate page such as a sign-in page. The current request's URL 2177 * is saved for future use by method {@link #continueToOriginalDestination()}; only use this method when 2178 * you plan to continue to the current URL at some later time; otherwise just set a new response page. 2179 * 2180 * @param page 2181 * The sign in page 2182 * 2183 * @see #setResponsePage(Class) 2184 * @see #setResponsePage(IRequestablePage) 2185 * @see #setResponsePage(Class, PageParameters) 2186 * @see Component#continueToOriginalDestination() 2187 */ 2188 public final void redirectToInterceptPage(final Page page) 2189 { 2190 throw new RestartResponseAtInterceptPageException(page); 2191 } 2192 2193 /** 2194 * Removes this component from its parent. It's important to remember that a component that is 2195 * removed cannot be referenced from the markup still. 2196 * <p> 2197 * You must not use this method in your callback to any of the 2198 * {@link MarkupContainer#visitChildren(IVisitor)} methods. See <a 2199 * href="https://issues.apache.org/jira/browse/WICKET-3229">WICKET-3329</a>. 2200 */ 2201 public final void remove() 2202 { 2203 if (parent == null) 2204 { 2205 throw new IllegalStateException("Cannot remove " + this + " from null parent!"); 2206 } 2207 parent.remove(this); 2208 } 2209 2210 /** 2211 * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! 2212 * <p> 2213 * Renders this component as a part of a response - the caller has to 2214 * make sure that this component is prepared for render. 2215 * 2216 * @see #beforeRender() 2217 */ 2218 public final void renderPart() { 2219 Page page = getPage(); 2220 2221 page.startComponentRender(this); 2222 2223 markRendering(true); 2224 2225 render(); 2226 2227 page.endComponentRender(this); 2228 } 2229 2230 /** 2231 * Render this component and all its children. Always calls hook {@link #onAfterRender()} 2232 * regardless of any exception. 2233 */ 2234 public final void render() 2235 { 2236 if (isAuto()) 2237 { 2238 // auto components are prepared when rendered 2239 beforeRender(); 2240 } 2241 2242 // Do the render 2243 RuntimeException exception = null; 2244 try 2245 { 2246 setRequestFlag(RFLAG_RENDERING, true); 2247 2248 internalRender(); 2249 } 2250 catch (final RuntimeException ex) 2251 { 2252 // Remember it as the originating exception 2253 exception = ex; 2254 } 2255 finally 2256 { 2257 try 2258 { 2259 // Cleanup 2260 afterRender(); 2261 } 2262 catch (RuntimeException ex2) 2263 { 2264 // Only remember it if not already another exception happened 2265 if (exception == null) 2266 { 2267 exception = ex2; 2268 } 2269 } 2270 } 2271 2272 // Re-throw if needed 2273 if (exception != null) 2274 { 2275 throw exception; 2276 } 2277 } 2278 2279 /** 2280 * Performs a render of this component as part of a Page level render process. 2281 */ 2282 private void internalRender() 2283 { 2284 // Make sure there is a markup available for the Component 2285 IMarkupFragment markup = getMarkup(); 2286 if (markup == null) 2287 { 2288 throw new MarkupNotFoundException("Markup not found for Component: " + toString()); 2289 } 2290 2291 // MarkupStream is an Iterator for the markup 2292 MarkupStream markupStream = new MarkupStream(markup); 2293 2294 MarkupElement elem = markup.get(0); 2295 if (elem instanceof ComponentTag) 2296 { 2297 // Guarantee that the markupStream is set and determineVisibility not yet tested 2298 // See WICKET-2049 2299 ((ComponentTag)elem).onBeforeRender(this, markupStream); 2300 } 2301 2302 // Determine if component is visible using it's authorization status 2303 // and the isVisible property. 2304 if (determineVisibility()) 2305 { 2306 setFlag(FLAG_HAS_BEEN_RENDERED, true); 2307 2308 // Rendering is beginning 2309 if (log.isDebugEnabled()) 2310 { 2311 log.debug("Begin render {}", this); 2312 } 2313 2314 try 2315 { 2316 notifyBehaviorsComponentBeforeRender(); 2317 onRender(); 2318 notifyBehaviorsComponentRendered(); 2319 2320 // Component has been rendered 2321 rendered(); 2322 } 2323 catch (RuntimeException ex) 2324 { 2325 onException(ex); 2326 } 2327 2328 if (log.isDebugEnabled()) 2329 { 2330 log.debug("End render {}", this); 2331 } 2332 } 2333 // elem is null when rendering a page 2334 else if ((elem != null) && (elem instanceof ComponentTag)) 2335 { 2336 if (getFlag(FLAG_PLACEHOLDER)) 2337 { 2338 renderPlaceholderTag(((ComponentTag)elem).mutable(), getResponse()); 2339 } 2340 } 2341 } 2342 2343 /** 2344 * Called when a runtime exception is caught during the render process 2345 * 2346 * @param ex 2347 * The exception caught. 2348 */ 2349 private void onException(final RuntimeException ex) 2350 { 2351 // Call each behaviors onException() to allow the 2352 // behavior to clean up 2353 for (Behavior behavior : getBehaviors()) 2354 { 2355 if (isBehaviorAccepted(behavior)) 2356 { 2357 try 2358 { 2359 behavior.onException(this, ex); 2360 } 2361 catch (Exception ex2) 2362 { 2363 log.error("Error while cleaning up after exception", ex2); 2364 } 2365 } 2366 } 2367 2368 // Re-throw the exception 2369 throw ex; 2370 } 2371 2372 /** 2373 * Renders a placeholder tag for the component when it is invisible and 2374 * {@link #setOutputMarkupPlaceholderTag(boolean)} has been called with <code>true</code>. 2375 * 2376 * @param tag 2377 * component tag 2378 * @param response 2379 * response 2380 */ 2381 protected void renderPlaceholderTag(final ComponentTag tag, final Response response) 2382 { 2383 String name = Strings.isEmpty(tag.getNamespace()) ? tag.getName() 2384 : tag.getNamespace() + ':' + tag.getName(); 2385 2386 // prefer concatenation over String#format() for performance 2387 response.write( 2388 "<" + name + " id=\"" + getAjaxRegionMarkupId() + 2389 "\" hidden=\"\" data-wicket-placeholder=\"\"></" + name + ">"); 2390 } 2391 2392 2393 /** 2394 * Returns the id of the markup region that will be updated via ajax. This can be different to 2395 * the markup id of the component if a {@link IAjaxRegionMarkupIdProvider} behavior has been 2396 * added. 2397 * 2398 * @return the markup id of the region to be updated via ajax. 2399 */ 2400 public final String getAjaxRegionMarkupId() 2401 { 2402 String markupId = null; 2403 for (Behavior behavior : getBehaviors()) 2404 { 2405 if (behavior instanceof IAjaxRegionMarkupIdProvider && behavior.isEnabled(this)) 2406 { 2407 markupId = ((IAjaxRegionMarkupIdProvider)behavior).getAjaxRegionMarkupId(this); 2408 break; 2409 } 2410 } 2411 if (markupId == null) 2412 { 2413 if (this instanceof IAjaxRegionMarkupIdProvider) 2414 { 2415 markupId = ((IAjaxRegionMarkupIdProvider)this).getAjaxRegionMarkupId(this); 2416 } 2417 } 2418 if (markupId == null) 2419 { 2420 markupId = getMarkupId(); 2421 } 2422 return markupId; 2423 } 2424 2425 2426 /** 2427 * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! 2428 * <p> 2429 * Renders the component at the current position in the given markup stream. The method 2430 * onComponentTag() is called to allow the component to mutate the start tag. The method 2431 * onComponentTagBody() is then called to permit the component to render its body. 2432 */ 2433 protected final void internalRenderComponent() 2434 { 2435 final IMarkupFragment markup = getMarkup(); 2436 if (markup == null) 2437 { 2438 throw new MarkupException("Markup not found. Component: " + toString()); 2439 } 2440 2441 final MarkupStream markupStream = new MarkupStream(markup); 2442 2443 // Get mutable copy of next tag 2444 final ComponentTag openTag = markupStream.getTag(); 2445 final ComponentTag tag = openTag.mutable(); 2446 2447 // call application-wide tag listeners 2448 getApplication().getOnComponentTagListeners().onComponentTag(this, tag); 2449 2450 // Call any tag handler 2451 onComponentTag(tag); 2452 2453 // If we're an openclose tag 2454 if (!tag.isOpenClose() && !tag.isOpen()) 2455 { 2456 // We were something other than <tag> or <tag/> 2457 markupStream.throwMarkupException("Method renderComponent called on bad markup element: " + 2458 tag); 2459 } 2460 2461 if (tag.isOpenClose() && openTag.isOpen()) 2462 { 2463 markupStream.throwMarkupException("You can not modify a open tag to open-close: " + tag); 2464 } 2465 2466 try 2467 { 2468 // Render open tag 2469 boolean renderBodyOnly = getRenderBodyOnly(); 2470 if (renderBodyOnly) 2471 { 2472 ExceptionSettings.NotRenderableErrorStrategy notRenderableErrorStrategy = ExceptionSettings.NotRenderableErrorStrategy.LOG_WARNING; 2473 if (Application.exists()) 2474 { 2475 notRenderableErrorStrategy = getApplication().getExceptionSettings().getNotRenderableErrorStrategy(); 2476 } 2477 2478 if (getFlag(FLAG_OUTPUT_MARKUP_ID)) 2479 { 2480 String message = String.format("Markup id set on a component that renders its body only. " + 2481 "Markup id: %s, component id: %s, type: %s, path: %s", 2482 getMarkupId(), getId(), getClass(), getPage().getPageClass() + ":" + getPageRelativePath()); 2483 if (notRenderableErrorStrategy == ExceptionSettings.NotRenderableErrorStrategy.THROW_EXCEPTION) 2484 { 2485 throw new IllegalStateException(message); 2486 } 2487 log.warn(message); 2488 } 2489 if (getFlag(FLAG_PLACEHOLDER)) 2490 { 2491 String message = String.format("Placeholder tag set on a component that renders its body only. " + 2492 "Component id: %s, type: %s, path: %s\", ", 2493 getId(), getClass(), getPage().getPageClass() + ":" + getPageRelativePath()); 2494 if (notRenderableErrorStrategy == ExceptionSettings.NotRenderableErrorStrategy.THROW_EXCEPTION) 2495 { 2496 throw new IllegalStateException(message); 2497 } 2498 log.warn(message); 2499 } 2500 } 2501 else 2502 { 2503 renderComponentTag(tag); 2504 } 2505 markupStream.next(); 2506 2507 // Render the body only if open-body-close. Do not render if open-close. 2508 if (tag.isOpen()) 2509 { 2510 // Render the body. The default strategy will simply call the component's 2511 // onComponentTagBody() implementation. 2512 getMarkupSourcingStrategy().onComponentTagBody(this, markupStream, tag); 2513 2514 // Render close tag 2515 if (openTag.isOpen()) 2516 { 2517 renderClosingComponentTag(markupStream, tag, renderBodyOnly); 2518 } 2519 else if (renderBodyOnly == false) 2520 { 2521 if (needToRenderTag(openTag)) 2522 { 2523 // Close the manually opened tag. And since the user might have changed the 2524 // tag name ... 2525 tag.writeSyntheticCloseTag(getResponse()); 2526 } 2527 } 2528 } 2529 } 2530 catch (WicketRuntimeException wre) 2531 { 2532 throw wre; 2533 } 2534 catch (RuntimeException re) 2535 { 2536 throw new WicketRuntimeException("Exception in rendering component: " + this, re); 2537 } 2538 } 2539 2540 /** 2541 * 2542 * @param openTag 2543 * @return true, if the tag shall be rendered 2544 */ 2545 private boolean needToRenderTag(final ComponentTag openTag) 2546 { 2547 // If a open-close tag has been modified to be open-body-close then a 2548 // synthetic close tag must be rendered. 2549 boolean renderTag = (openTag != null && !(openTag instanceof WicketTag)); 2550 if (renderTag == false) 2551 { 2552 renderTag = !getApplication().getMarkupSettings().getStripWicketTags(); 2553 } 2554 return renderTag; 2555 } 2556 2557 /** 2558 * Called to indicate that a component has been rendered. This method should only very rarely be 2559 * called at all. Some components may render its children without calling render() on them. 2560 * These components need to call rendered() to indicate that its child components were actually 2561 * rendered, the framework would think they had never been rendered, and in development mode 2562 * this would result in a runtime exception. 2563 */ 2564 public final void rendered() 2565 { 2566 Page page = findPage(); 2567 if (page != null) 2568 { 2569 // Tell the page that the component rendered 2570 page.componentRendered(this); 2571 } 2572 else 2573 { 2574 log.error("Component is not connected to a Page. Cannot register the component as being rendered. Component: " + 2575 toString()); 2576 } 2577 } 2578 2579 /** 2580 * Get the markup sourcing strategy for the component. If null, 2581 * {@link #newMarkupSourcingStrategy()} will be called. 2582 * 2583 * @return Markup sourcing strategy 2584 */ 2585 protected final IMarkupSourcingStrategy getMarkupSourcingStrategy() 2586 { 2587 if (markupSourcingStrategy == null) 2588 { 2589 markupSourcingStrategy = newMarkupSourcingStrategy(); 2590 2591 // If not strategy by provided, than we use a default one. 2592 if (markupSourcingStrategy == null) 2593 { 2594 markupSourcingStrategy = DefaultMarkupSourcingStrategy.get(); 2595 } 2596 } 2597 return markupSourcingStrategy; 2598 } 2599 2600 /** 2601 * If {@link #getMarkupSourcingStrategy()} returns null, this method will be called. By default 2602 * it returns null, which means that a default markup strategy will be attached to the 2603 * component. 2604 * <p> 2605 * Please note that markup source strategies are not persisted. Instead they get re-created by 2606 * calling this method again. That's ok since markup sourcing strategies usually do not maintain 2607 * a state. 2608 * 2609 * @return Markup sourcing strategy 2610 */ 2611 protected IMarkupSourcingStrategy newMarkupSourcingStrategy() 2612 { 2613 return null; 2614 } 2615 2616 /** 2617 * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! 2618 * 2619 * Print to the web response what ever the component wants to contribute to the head section. 2620 * Make sure that all attached behaviors are asked as well. 2621 * <p> 2622 * NOT intended for overriding by framework clients. Rather, use 2623 * {@link Component#renderHead(org.apache.wicket.markup.head.IHeaderResponse)} 2624 * </p> 2625 * 2626 * @param container 2627 * The HtmlHeaderContainer 2628 */ 2629 public void internalRenderHead(final HtmlHeaderContainer container) 2630 { 2631 if (isVisibleInHierarchy() && isRenderAllowed()) 2632 { 2633 if (log.isDebugEnabled()) 2634 { 2635 log.debug("internalRenderHead: {}", toString(false)); 2636 } 2637 2638 IHeaderResponse response = container.getHeaderResponse(); 2639 2640 // Allow component to contribute 2641 boolean wasRendered = response.wasRendered(this); 2642 if (wasRendered == false) 2643 { 2644 LazyStringResponse markupHeaderResponse = new LazyStringResponse(); 2645 Response oldResponse = getResponse(); 2646 RequestCycle.get().setResponse(markupHeaderResponse); 2647 try 2648 { 2649 // Make sure the markup source strategy contributes to the header first 2650 // to be backward compatible. WICKET-3761 2651 getMarkupSourcingStrategy().renderHead(this, container); 2652 CharSequence headerContribution = markupHeaderResponse.getBuffer(); 2653 if (Strings.isEmpty(headerContribution) == false) 2654 { 2655 response.render(StringHeaderItem.forString(headerContribution)); 2656 } 2657 } 2658 finally 2659 { 2660 RequestCycle.get().setResponse(oldResponse); 2661 } 2662 // Then let the component itself to contribute to the header 2663 renderHead(response); 2664 } 2665 2666 // Then ask all behaviors 2667 for (Behavior behavior : getBehaviors()) 2668 { 2669 if (isBehaviorAccepted(behavior)) 2670 { 2671 List<IClusterable> pair = List.of(this, behavior); 2672 if (!response.wasRendered(pair)) 2673 { 2674 behavior.renderHead(this, response); 2675 response.markRendered(pair); 2676 } 2677 } 2678 } 2679 2680 if (wasRendered == false) 2681 { 2682 response.markRendered(this); 2683 } 2684 } 2685 } 2686 2687 /** 2688 * Replaces this component with another. The replacing component must have the same component id 2689 * as this component. This method serves as a shortcut to 2690 * 2691 * <code>this.getParent().replace(replacement)</code> 2692 * 2693 * and provides a better context for errors. 2694 * <p> 2695 * Usage: <code>component = component.replaceWith(replacement);</code> 2696 * </p> 2697 * 2698 * @since 1.2.1 2699 * 2700 * @param replacement 2701 * component to replace this one 2702 * @return the component which replaced this one 2703 */ 2704 public Component replaceWith(Component replacement) 2705 { 2706 Args.notNull(replacement, "replacement"); 2707 2708 if (!getId().equals(replacement.getId())) 2709 { 2710 throw new IllegalArgumentException( 2711 "Replacement component must have the same id as the component it will replace. Replacement id [[" + 2712 replacement.getId() + "]], replaced id [[" + getId() + "]]."); 2713 } 2714 if (parent == null) 2715 { 2716 throw new IllegalStateException( 2717 "This method can only be called on a component that has already been added to its parent."); 2718 } 2719 parent.replace(replacement); 2720 return replacement; 2721 } 2722 2723 /** 2724 * @param component 2725 * The component to compare with 2726 * @return True if the given component's model is the same as this component's model. 2727 */ 2728 public final boolean sameInnermostModel(final Component component) 2729 { 2730 return sameInnermostModel(component.getDefaultModel()); 2731 } 2732 2733 /** 2734 * @param model 2735 * The model to compare with 2736 * @return True if the given component's model is the same as this component's model. 2737 */ 2738 public final boolean sameInnermostModel(final IModel<?> model) 2739 { 2740 // Get the two models 2741 IModel<?> thisModel = getDefaultModel(); 2742 2743 // If both models are non-null they could be the same 2744 if (thisModel != null && model != null) 2745 { 2746 return getInnermostModel(thisModel) == getInnermostModel(model); 2747 } 2748 2749 return false; 2750 } 2751 2752 /** 2753 * Sets whether this component is enabled. Specific components may decide to implement special 2754 * behavior that uses this property, like web form components that add a disabled='disabled' 2755 * attribute when enabled is false. If it is not enabled, it will not be allowed to call any 2756 * listener method on it (e.g. Link.onClick) and the model object will be protected (for the 2757 * common use cases, not for programmer's misuse) 2758 * 2759 * @param enabled 2760 * whether this component is enabled 2761 * @return This 2762 */ 2763 public final Component setEnabled(final boolean enabled) 2764 { 2765 // Is new enabled state a change? 2766 if (enabled != getFlag(FLAG_ENABLED)) 2767 { 2768 // Tell the page that this component's enabled was changed 2769 if (isVersioned()) 2770 { 2771 final Page page = findPage(); 2772 if (page != null) 2773 { 2774 addStateChange(); 2775 } 2776 } 2777 2778 // Change visibility 2779 setFlag(FLAG_ENABLED, enabled); 2780 onEnabledStateChanged(); 2781 } 2782 return this; 2783 } 2784 2785 void clearEnabledInHierarchyCache() 2786 { 2787 setRequestFlag(RFLAG_ENABLED_IN_HIERARCHY_SET, false); 2788 } 2789 2790 void onEnabledStateChanged() 2791 { 2792 clearEnabledInHierarchyCache(); 2793 } 2794 2795 /** 2796 * Sets whether model strings should be escaped. 2797 * 2798 * @param escapeMarkup 2799 * True is model strings should be escaped 2800 * @return This 2801 */ 2802 public final Component setEscapeModelStrings(final boolean escapeMarkup) 2803 { 2804 setFlag(FLAG_ESCAPE_MODEL_STRINGS, escapeMarkup); 2805 return this; 2806 } 2807 2808 /** 2809 * Set markup ID, which must be String or Integer 2810 * 2811 * @param markupId 2812 */ 2813 public final void setMarkupIdImpl(Object markupId) 2814 { 2815 if (markupId != null && !(markupId instanceof String) && !(markupId instanceof Integer)) 2816 { 2817 throw new IllegalArgumentException("markupId must be String or Integer"); 2818 } 2819 2820 setOutputMarkupId(true); 2821 if (markupId instanceof Integer) 2822 { 2823 generatedMarkupId = (Integer)markupId; 2824 setMetaData(MARKUP_ID_KEY, null); 2825 return; 2826 } 2827 2828 generatedMarkupId = -1; 2829 setMetaData(MARKUP_ID_KEY, (String)markupId); 2830 2831 } 2832 2833 /** 2834 * Copy markupId 2835 * 2836 * @param comp 2837 */ 2838 final void setMarkupId(Component comp) 2839 { 2840 Args.notNull(comp, "comp"); 2841 2842 generatedMarkupId = comp.generatedMarkupId; 2843 setMetaData(MARKUP_ID_KEY, comp.getMetaData(MARKUP_ID_KEY)); 2844 if (comp.getOutputMarkupId()) 2845 { 2846 setOutputMarkupId(true); 2847 } 2848 } 2849 2850 /** 2851 * Sets this component's markup id to a user defined value. It is up to the user to ensure this 2852 * value is unique. 2853 * <p> 2854 * The recommended way is to let wicket generate the value automatically, this method is here to 2855 * serve as an override for that value in cases where a specific id must be used. 2856 * <p> 2857 * If null is passed in the user defined value is cleared and markup id value will fall back on 2858 * automatically generated value 2859 * 2860 * @see #getMarkupId() 2861 * 2862 * @param markupId 2863 * markup id value or null to clear any previous user defined value 2864 * @return this for chaining 2865 */ 2866 public Component setMarkupId(String markupId) 2867 { 2868 Args.notEmpty(markupId, "markupId"); 2869 2870 // TODO check if an automatic id has already been generated or getmarkupid() called 2871 // previously and throw an illegalstateexception because something else might be depending 2872 // on previous id 2873 2874 setMarkupIdImpl(markupId); 2875 return this; 2876 } 2877 2878 /** 2879 * Sets the metadata for this component using the given key. If the metadata object is not of 2880 * the correct type for the metadata key, an IllegalArgumentException will be thrown. For 2881 * information on creating MetaDataKeys, see {@link MetaDataKey}. 2882 * 2883 * @param <M> 2884 * The type of the metadata 2885 * 2886 * @param key 2887 * The singleton key for the metadata 2888 * @param object 2889 * The metadata object 2890 * @throws IllegalArgumentException 2891 * @see MetaDataKey 2892 */ 2893 @Override 2894 public final <M extends Serializable> Component setMetaData(final MetaDataKey<M> key, final M object) 2895 { 2896 MetaDataEntry<?>[] old = getMetaData(); 2897 2898 Object metaData = null; 2899 MetaDataEntry<?>[] metaDataArray = key.set(old, object); 2900 if (metaDataArray != null && metaDataArray.length > 0) 2901 { 2902 metaData = (metaDataArray.length > 1) ? metaDataArray : metaDataArray[0]; 2903 } 2904 2905 int index = getFlag(FLAG_MODEL_SET) ? 1 : 0; 2906 2907 if (old == null && metaData != null) 2908 { 2909 data_insert(index, metaData); 2910 } 2911 else if (old != null && metaData != null) 2912 { 2913 data_set(index, metaData); 2914 } 2915 else if (old != null && metaData == null) 2916 { 2917 data_remove(index); 2918 } 2919 return this; 2920 } 2921 2922 /** 2923 * Sets the given model. 2924 * <p> 2925 * WARNING: DO NOT OVERRIDE THIS METHOD UNLESS YOU HAVE A VERY GOOD REASON FOR IT. OVERRIDING 2926 * THIS MIGHT OPEN UP SECURITY LEAKS AND BREAK BACK-BUTTON SUPPORT. 2927 * </p> 2928 * 2929 * @param model 2930 * The model 2931 * @return This 2932 */ 2933 public Component setDefaultModel(final IModel<?> model) 2934 { 2935 IModel<?> prevModel = getModelImpl(); 2936 2937 IModel<?> wrappedModel = prevModel; 2938 if (prevModel instanceof IWrapModel) 2939 { 2940 wrappedModel = ((IWrapModel<?>)prevModel).getWrappedModel(); 2941 } 2942 2943 // Change model 2944 if (wrappedModel != model) 2945 { 2946 // Detach the old/current model 2947 if (prevModel != null) 2948 { 2949 prevModel.detach(); 2950 } 2951 2952 modelChanging(); 2953 setModelImpl(wrap(model)); 2954 modelChanged(); 2955 2956 // WICKET-3413 reset 'inherited model' when model is explicitely set 2957 setFlag(FLAG_INHERITABLE_MODEL, false); 2958 } 2959 2960 return this; 2961 } 2962 2963 /** 2964 * @return model 2965 */ 2966 IModel<?> getModelImpl() 2967 { 2968 if (getFlag(FLAG_MODEL_SET)) 2969 { 2970 return (IModel<?>)data_get(0); 2971 } 2972 return null; 2973 } 2974 2975 /** 2976 * 2977 * @param model 2978 */ 2979 void setModelImpl(IModel<?> model) 2980 { 2981 if (getFlag(FLAG_MODEL_SET)) 2982 { 2983 if (model != null) 2984 { 2985 data_set(0, model); 2986 } 2987 else 2988 { 2989 data_remove(0); 2990 setFlag(FLAG_MODEL_SET, false); 2991 } 2992 } 2993 else 2994 { 2995 if (model != null) 2996 { 2997 data_insert(0, model); 2998 setFlag(FLAG_MODEL_SET, true); 2999 } 3000 } 3001 } 3002 3003 /** 3004 * Sets the backing model object. Unlike <code>getDefaultModel().setObject(object)</code>, this 3005 * method checks authorisation and model comparator, and invokes <code>modelChanging</code> and 3006 * <code>modelChanged</code> if the value really changes. 3007 * 3008 * @param object 3009 * The object to set 3010 * @return This 3011 * @throws IllegalStateException If the component has neither its own model nor any of its 3012 * parents uses {@link IComponentInheritedModel} 3013 */ 3014 @SuppressWarnings("unchecked") 3015 public final Component setDefaultModelObject(final Object object) 3016 { 3017 final IModel<Object> model = (IModel<Object>)getDefaultModel(); 3018 3019 // Check whether anything can be set at all 3020 if (model == null) 3021 { 3022 throw new IllegalStateException( 3023 "Attempt to set a model object on a component without a model! " + 3024 "Either pass an IModel to the constructor or use #setDefaultModel(new SomeModel(object)). " + 3025 "Component: " + getPageRelativePath()); 3026 } 3027 3028 // Check authorization 3029 if (!isActionAuthorized(ENABLE)) 3030 { 3031 throw new UnauthorizedActionException(this, ENABLE); 3032 } 3033 3034 // Check whether this will result in an actual change 3035 if (!getModelComparator().compare(this, object)) 3036 { 3037 modelChanging(); 3038 try 3039 { 3040 model.setObject(object); 3041 } 3042 catch (UnsupportedOperationException uox) 3043 { 3044 throw new WicketRuntimeException("You need to use writeable IModel for component " + getPageRelativePath(), uox); 3045 } 3046 modelChanged(); 3047 } 3048 3049 return this; 3050 } 3051 3052 /** 3053 * Sets whether or not component will output id attribute into the markup. id attribute will be 3054 * set to the value returned from {@link Component#getMarkupId()}. 3055 * 3056 * @param output 3057 * True if the component will output the id attribute into markup. Please note that 3058 * the default behavior is to use the same id as the component. This means that your 3059 * component must begin with [a-zA-Z] in order to generate a valid markup id 3060 * according to: http://www.w3.org/TR/html401/types.html#type-name 3061 * 3062 * @return this for chaining 3063 */ 3064 public final Component setOutputMarkupId(final boolean output) 3065 { 3066 setFlag(FLAG_OUTPUT_MARKUP_ID, output); 3067 return this; 3068 } 3069 3070 /** 3071 * Render a placeholder tag when the component is not visible. The tag is of form: 3072 * <componenttag hidden="" id="markupid"/>. This method will also call 3073 * <code>setOutputMarkupId(true)</code>. 3074 * 3075 * This is useful, for example, in ajax situations where the component starts out invisible and 3076 * then becomes visible through an ajax update. With a placeholder tag already in the markup you 3077 * do not need to repaint this component's parent, instead you can repaint the component 3078 * directly. 3079 * 3080 * When this method is called with parameter <code>false</code> the outputmarkupid flag is not 3081 * reverted to false. 3082 * 3083 * @param outputTag 3084 * @return this for chaining 3085 */ 3086 public final Component setOutputMarkupPlaceholderTag(final boolean outputTag) 3087 { 3088 if (outputTag != getFlag(FLAG_PLACEHOLDER)) 3089 { 3090 if (outputTag) 3091 { 3092 setOutputMarkupId(true); 3093 setFlag(FLAG_PLACEHOLDER, true); 3094 } 3095 else 3096 { 3097 setFlag(FLAG_PLACEHOLDER, false); 3098 // I think it's better to not setOutputMarkupId to false... 3099 // user can do it if she want 3100 } 3101 } 3102 return this; 3103 } 3104 3105 /** 3106 * If false the component's tag will be printed as well as its body (which is default). If true 3107 * only the body will be printed, but not the component's tag. 3108 * 3109 * @param renderTag 3110 * If true, the component tag will not be printed 3111 * @return This 3112 */ 3113 public final Component setRenderBodyOnly(final boolean renderTag) 3114 { 3115 setFlag(FLAG_RENDER_BODY_ONLY, renderTag); 3116 return this; 3117 } 3118 3119 /** 3120 * Sets the page that will respond to this request 3121 * 3122 * @param <C> 3123 * 3124 * @param cls 3125 * The response page class 3126 * @see RequestCycle#setResponsePage(Class) 3127 */ 3128 public final <C extends IRequestablePage> void setResponsePage(final Class<C> cls) 3129 { 3130 getRequestCycle().setResponsePage(cls, (PageParameters)null); 3131 } 3132 3133 /** 3134 * Sets the page class and its parameters that will respond to this request 3135 * 3136 * @param <C> 3137 * 3138 * @param cls 3139 * The response page class 3140 * @param parameters 3141 * The parameters for this bookmarkable page. 3142 * @see RequestCycle#setResponsePage(Class, PageParameters) 3143 */ 3144 public final <C extends IRequestablePage> void setResponsePage(final Class<C> cls, 3145 PageParameters parameters) 3146 { 3147 getRequestCycle().setResponsePage(cls, parameters); 3148 } 3149 3150 /** 3151 * Sets the page that will respond to this request 3152 * 3153 * @param page 3154 * The response page 3155 * 3156 * @see RequestCycle#setResponsePage(org.apache.wicket.request.component.IRequestablePage) 3157 */ 3158 public final void setResponsePage(final IRequestablePage page) 3159 { 3160 getRequestCycle().setResponsePage(page); 3161 } 3162 3163 /** 3164 * @param versioned 3165 * True to turn on versioning for this component, false to turn it off for this 3166 * component and any children. 3167 * @return This 3168 */ 3169 public Component setVersioned(boolean versioned) 3170 { 3171 setFlag(FLAG_VERSIONED, versioned); 3172 return this; 3173 } 3174 3175 /** 3176 * Sets whether this component and any children are visible. 3177 * 3178 * @param visible 3179 * True if this component and any children should be visible 3180 * @return This 3181 */ 3182 public final Component setVisible(final boolean visible) 3183 { 3184 // Is new visibility state a change? 3185 if (visible != getFlag(FLAG_VISIBLE)) 3186 { 3187 // record component's visibility change 3188 addStateChange(); 3189 3190 // Change visibility 3191 setFlag(FLAG_VISIBLE, visible); 3192 onVisibleStateChanged(); 3193 } 3194 return this; 3195 } 3196 3197 void clearVisibleInHierarchyCache() 3198 { 3199 setRequestFlag(RFLAG_VISIBLE_IN_HIERARCHY_SET, false); 3200 } 3201 3202 void onVisibleStateChanged() 3203 { 3204 clearVisibleInHierarchyCache(); 3205 } 3206 3207 3208 /** 3209 * Gets the string representation of this component. 3210 * 3211 * @return The path to this component 3212 */ 3213 @Override 3214 public String toString() 3215 { 3216 return toString(false); 3217 } 3218 3219 /** 3220 * @param detailed 3221 * True if a detailed string is desired 3222 * @return The string 3223 */ 3224 public String toString(final boolean detailed) 3225 { 3226 try 3227 { 3228 final StringBuilder buffer = new StringBuilder(); 3229 buffer.append("[Component id = ").append(getId()); 3230 3231 if (detailed) 3232 { 3233 final Page page = findPage(); 3234 if (page == null) 3235 { 3236 buffer.append(", page = <No Page>, path = ") 3237 .append(getPath()) 3238 .append('.') 3239 .append(Classes.simpleName(getClass())); 3240 } 3241 else 3242 { 3243 buffer.append(", page = ") 3244 .append(Classes.name(getPage().getPageClass())) 3245 .append(", path = ") 3246 .append(getPageRelativePath()) 3247 .append(", type = ") 3248 .append(Classes.name(getClass())) 3249 .append(", isVisible = ") 3250 .append((determineVisibility())) 3251 .append(", isVersioned = ") 3252 .append(isVersioned()); 3253 } 3254 3255 if (markup != null) 3256 { 3257 buffer.append(", markup = ").append(new MarkupStream(getMarkup()).toString()); 3258 } 3259 } 3260 3261 buffer.append(']'); 3262 3263 return buffer.toString(); 3264 } 3265 catch (Exception e) 3266 { 3267 log.warn("Error while building toString()", e); 3268 return String.format( 3269 "[Component id = %s <attributes are not available because exception %s was thrown during toString()>]", 3270 getId(), e.getClass().getName()); 3271 } 3272 } 3273 3274 /** 3275 * Returns a bookmarkable URL that references a given page class using a given set of page 3276 * parameters. Since the URL which is returned contains all information necessary to instantiate 3277 * and render the page, it can be stored in a user's browser as a stable bookmark. 3278 * 3279 * @param <C> 3280 * 3281 * @see RequestCycle#urlFor(Class, org.apache.wicket.request.mapper.parameter.PageParameters) 3282 * 3283 * @param pageClass 3284 * Class of page 3285 * @param parameters 3286 * Parameters to page 3287 * @return Bookmarkable URL to page 3288 */ 3289 public final <C extends Page> CharSequence urlFor(final Class<C> pageClass, 3290 final PageParameters parameters) 3291 { 3292 return getRequestCycle().urlFor(pageClass, parameters); 3293 } 3294 3295 /** 3296 * Gets a URL for the listener interface on a behavior (e.g. {@link IRequestListener} on 3297 * {@link org.apache.wicket.ajax.markup.html.navigation.paging.AjaxPagingNavigationBehavior}). 3298 * 3299 * @param behaviour 3300 * The behavior that the URL should point to 3301 * @param parameters 3302 * The parameters that should be rendered into the urls 3303 * @return The URL 3304 */ 3305 public final CharSequence urlForListener(final Behavior behaviour, final PageParameters parameters) 3306 { 3307 int id = getBehaviorId(behaviour); 3308 IRequestHandler handler = createRequestHandler(parameters, id); 3309 return getRequestCycle().urlFor(handler); 3310 } 3311 3312 /** 3313 * Create a suitable request handler depending whether the page is stateless or bookmarkable. 3314 */ 3315 private IRequestHandler createRequestHandler(PageParameters parameters, Integer id) 3316 { 3317 Page page = getPage(); 3318 3319 PageAndComponentProvider provider = new PageAndComponentProvider(page, this, parameters); 3320 3321 if (page.isPageStateless() 3322 || (page.isBookmarkable() && page.wasCreatedBookmarkable())) 3323 { 3324 return new BookmarkableListenerRequestHandler(provider, id); 3325 } 3326 else 3327 { 3328 return new ListenerRequestHandler(provider, id); 3329 } 3330 } 3331 3332 /** 3333 * Returns a URL that references the given request target. 3334 * 3335 * @see RequestCycle#urlFor(IRequestHandler) 3336 * 3337 * @param requestHandler 3338 * the request target to reference 3339 * 3340 * @return a URL that references the given request target 3341 */ 3342 public final CharSequence urlFor(final IRequestHandler requestHandler) 3343 { 3344 return getRequestCycle().urlFor(requestHandler); 3345 } 3346 3347 /** 3348 * Gets a URL for this {@link IRequestListener}. 3349 * 3350 * @see RequestCycle#urlFor(IRequestHandler) 3351 * 3352 * @param parameters 3353 * The parameters that should be rendered into the URL 3354 * @return The URL 3355 */ 3356 public final CharSequence urlForListener(final PageParameters parameters) 3357 { 3358 IRequestHandler handler = createRequestHandler(parameters, null); 3359 return getRequestCycle().urlFor(handler); 3360 } 3361 3362 /** 3363 * Returns a URL that references a shared resource through the provided resource reference. 3364 * 3365 * @see RequestCycle#urlFor(IRequestHandler) 3366 * 3367 * @param resourceReference 3368 * The resource reference 3369 * @param parameters 3370 * parameters or {@code null} if none 3371 * @return The url for the shared resource 3372 */ 3373 public final CharSequence urlFor(final ResourceReference resourceReference, 3374 PageParameters parameters) 3375 { 3376 return getRequestCycle().urlFor(resourceReference, parameters); 3377 } 3378 3379 /** 3380 * Traverses all parent components of the given class in this parentClass, calling the visitor's 3381 * visit method at each one. 3382 * 3383 * @param <R> 3384 * the type of the result object 3385 * @param parentClass 3386 * Class 3387 * @param visitor 3388 * The visitor to call at each parent of the given type 3389 * @return First non-null value returned by visitor callback 3390 */ 3391 public final <R, C extends MarkupContainer> R visitParents(final Class<C> parentClass, 3392 final IVisitor<C, R> visitor) 3393 { 3394 return visitParents(parentClass, visitor, IVisitFilter.ANY); 3395 } 3396 3397 /** 3398 * Traverses all parent components of the given class in this parentClass, calling the visitor's 3399 * visit method at each one. 3400 * 3401 * @param <R> 3402 * the type of the result object 3403 * @param parentClass 3404 * the class of the parent component 3405 * @param visitor 3406 * The visitor to call at each parent of the given type 3407 * @param filter 3408 * a filter that adds an additional logic to the condition whether a parent container 3409 * matches 3410 * @return First non-null value returned by visitor callback 3411 */ 3412 @SuppressWarnings("unchecked") 3413 public final <R, C extends MarkupContainer> R visitParents(final Class<C> parentClass, 3414 final IVisitor<C, R> visitor, IVisitFilter filter) 3415 { 3416 Args.notNull(filter, "filter"); 3417 3418 // Start here 3419 MarkupContainer current = getParent(); 3420 3421 Visit<R> visit = new Visit<R>(); 3422 3423 // Walk up containment hierarchy 3424 while (current != null) 3425 { 3426 // Is current an instance of this class? 3427 if (parentClass.isInstance(current) && filter.visitObject(current)) 3428 { 3429 visitor.component((C)current, visit); 3430 if (visit.isStopped()) 3431 { 3432 return visit.getResult(); 3433 } 3434 } 3435 3436 // Check parent 3437 current = current.getParent(); 3438 } 3439 return null; 3440 } 3441 3442 /** 3443 * Registers a warning feedback message for this component. 3444 * 3445 * @param message 3446 * The feedback message 3447 */ 3448 @Override 3449 public final void warn(final Serializable message) 3450 { 3451 getFeedbackMessages().warn(this, message); 3452 addStateChange(); 3453 } 3454 3455 /** 3456 * {@link Behavior#beforeRender(Component)} Notify all behaviors that are assigned to this 3457 * component that the component is about to be rendered. 3458 */ 3459 private void notifyBehaviorsComponentBeforeRender() 3460 { 3461 for (Behavior behavior : getBehaviors()) 3462 { 3463 if (isBehaviorAccepted(behavior)) 3464 { 3465 behavior.beforeRender(this); 3466 } 3467 } 3468 } 3469 3470 /** 3471 * {@link Behavior#afterRender(Component)} Notify all behaviors that are assigned to this 3472 * component that the component has rendered. 3473 */ 3474 private void notifyBehaviorsComponentRendered() 3475 { 3476 // notify the behaviors that component has been rendered 3477 for (Behavior behavior : getBehaviors()) 3478 { 3479 if (isBehaviorAccepted(behavior)) 3480 { 3481 behavior.afterRender(this); 3482 } 3483 } 3484 } 3485 3486 /** 3487 * TODO WICKET-NG rename to something more useful - like componentChanged(), this method used to 3488 * be called with a Change object 3489 * 3490 * Adds state change to page. 3491 */ 3492 protected final void addStateChange() 3493 { 3494 checkHierarchyChange(this); 3495 final Page page = findPage(); 3496 if (page != null) 3497 { 3498 page.componentStateChanging(this); 3499 } 3500 } 3501 3502 /** 3503 * Checks whether the given type has the expected name. 3504 * 3505 * @param tag 3506 * The tag to check 3507 * @param name 3508 * The expected tag name 3509 * @throws MarkupException 3510 * Thrown if the tag is not of the right name 3511 */ 3512 protected final void checkComponentTag(final ComponentTag tag, final String name) 3513 { 3514 if (!tag.getName().equalsIgnoreCase(name)) 3515 { 3516 String msg = String.format("Component [%s] (path = [%s]) must be " 3517 + "applied to a tag of type [%s], not: %s", getId(), getPath(), name, 3518 tag.toUserDebugString()); 3519 3520 findMarkupStream().throwMarkupException(msg); 3521 } 3522 } 3523 3524 /** 3525 * Checks that a given tag has a required attribute value. 3526 * 3527 * @param tag 3528 * The tag 3529 * @param key 3530 * The attribute key 3531 * @param values 3532 * The required value for the attribute key 3533 * @throws MarkupException 3534 * Thrown if the tag does not have the required attribute value 3535 */ 3536 protected final void checkComponentTagAttribute(final ComponentTag tag, final String key, 3537 final String... values) 3538 { 3539 if (key != null) 3540 { 3541 final String tagAttributeValue = tag.getAttributes().getString(key); 3542 3543 boolean found = false; 3544 if (tagAttributeValue != null) 3545 { 3546 for (String value : values) 3547 { 3548 if (value.equalsIgnoreCase(tagAttributeValue)) 3549 { 3550 found = true; 3551 break; 3552 } 3553 } 3554 } 3555 3556 if (found == false) 3557 { 3558 String msg = String.format("Component [%s] (path = [%s]) must be applied to a tag " 3559 + "with [%s] attribute matching any of %s, not [%s]", getId(), getPath(), key, 3560 Arrays.toString(values), tagAttributeValue); 3561 3562 findMarkupStream().throwMarkupException(msg); 3563 } 3564 } 3565 } 3566 3567 /** 3568 * Checks whether the hierarchy may be changed at all, and throws an exception if this is not 3569 * the case. 3570 * 3571 * @param component 3572 * the component which is about to be added or removed 3573 */ 3574 protected void checkHierarchyChange(final Component component) 3575 { 3576 // Throw exception if modification is attempted during rendering 3577 if (getRequestFlag(RFLAG_RENDERING) && !component.isAuto()) 3578 { 3579 throw new WicketRuntimeException( 3580 "Cannot modify component hierarchy after render phase has started (page version cant change then anymore)"); 3581 } 3582 } 3583 3584 /** 3585 * Detaches the model for this component if it is detachable. 3586 */ 3587 protected void detachModel() 3588 { 3589 IModel<?> model = getModelImpl(); 3590 if (model != null) 3591 { 3592 model.detach(); 3593 } 3594 // also detach the wrapped model of a component assigned wrap (not 3595 // inherited) 3596 if (model instanceof IWrapModel && !getFlag(FLAG_INHERITABLE_MODEL)) 3597 { 3598 ((IWrapModel<?>)model).getWrappedModel().detach(); 3599 } 3600 } 3601 3602 /** 3603 * Suffixes an exception message with useful information about this. component. 3604 * 3605 * @param message 3606 * The message 3607 * @return The modified message 3608 */ 3609 protected final String exceptionMessage(final String message) 3610 { 3611 return message + ":\n" + toString(); 3612 } 3613 3614 /** 3615 * Finds the markup stream for this component. 3616 * 3617 * @return The markup stream for this component. Since a Component cannot have a markup stream, 3618 * we ask this component's parent to search for it. 3619 */ 3620 protected final MarkupStream findMarkupStream() 3621 { 3622 return new MarkupStream(getMarkup()); 3623 } 3624 3625 /** 3626 * If this Component is a Page, returns self. Otherwise, searches for the nearest Page parent in 3627 * the component hierarchy. If no Page parent can be found, {@code null} is returned. 3628 * 3629 * @return The Page or {@code null} if none can be found 3630 */ 3631 protected final Page findPage() 3632 { 3633 // Search for page 3634 return (Page)(this instanceof Page ? this : findParent(Page.class)); 3635 } 3636 3637 /** 3638 * Gets the subset of the currently coupled {@link Behavior}s that are of the provided type as 3639 * an unmodifiable list. Returns an empty list if there are no behaviors coupled to this 3640 * component. 3641 * 3642 * @param type 3643 * The type or null for all 3644 * @return The subset of the currently coupled behaviors that are of the provided type as an 3645 * unmodifiable list 3646 * @param <M> 3647 * A class derived from Behavior 3648 */ 3649 public <M extends Behavior> List<M> getBehaviors(Class<M> type) 3650 { 3651 return Behaviors.getBehaviors(this, type); 3652 } 3653 3654 /** 3655 * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! 3656 * 3657 * @param flag 3658 * The flag to test 3659 * @return True if the flag is set 3660 */ 3661 protected final boolean getFlag(final int flag) 3662 { 3663 return (flags & flag) != 0; 3664 } 3665 3666 /** 3667 * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! 3668 * 3669 * @param flag 3670 * The flag to test 3671 * @return True if the flag is set 3672 */ 3673 protected final boolean getRequestFlag(final short flag) 3674 { 3675 return (requestFlags & flag) != 0; 3676 } 3677 3678 /** 3679 * Finds the innermost IModel object for an IModel that might contain nested IModel(s). 3680 * 3681 * @param model 3682 * The model 3683 * @return The innermost (most nested) model 3684 */ 3685 protected final IModel<?> getInnermostModel(final IModel<?> model) 3686 { 3687 IModel<?> nested = model; 3688 while (nested != null && nested instanceof IWrapModel) 3689 { 3690 final IModel<?> next = ((IWrapModel<?>)nested).getWrappedModel(); 3691 if (nested == next) 3692 { 3693 throw new WicketRuntimeException("Model for " + nested + " is self-referential"); 3694 } 3695 nested = next; 3696 } 3697 return nested; 3698 } 3699 3700 /** 3701 * Gets the component's current model comparator. Implementations can be used for testing the 3702 * current value of the components model data with the new value that is given. 3703 * 3704 * @return the value defaultModelComparator 3705 */ 3706 public IModelComparator getModelComparator() 3707 { 3708 return defaultModelComparator; 3709 } 3710 3711 /** 3712 * Returns whether the component can be stateless. Also the component behaviors must be 3713 * stateless, otherwise the component will be treat as stateful. In order for page to be 3714 * stateless (and not to be stored in session), all components (and component behaviors) must be 3715 * stateless. 3716 * 3717 * @return whether the component can be stateless 3718 */ 3719 protected boolean getStatelessHint() 3720 { 3721 return true; 3722 } 3723 3724 /** 3725 * Called when a null model is about to be retrieved in order to allow a subclass to provide an 3726 * initial model. 3727 * <p> 3728 * By default this implementation looks components in the parent chain owning a 3729 * {@link IComponentInheritedModel} to provide a model for this component via 3730 * {@link IComponentInheritedModel#wrapOnInheritance(Component)}. 3731 * <p> 3732 * For example a {@link FormComponent} has the opportunity to instantiate a model on the fly 3733 * using its {@code id} and the containing {@link Form}'s model, if the form holds a 3734 * {@link CompoundPropertyModel}. 3735 * 3736 * @return The model 3737 */ 3738 protected IModel<?> initModel() 3739 { 3740 IModel<?> foundModel = null; 3741 // Search parents for IComponentInheritedModel (i.e. CompoundPropertyModel) 3742 for (Component current = getParent(); current != null; current = current.getParent()) 3743 { 3744 // Get model 3745 // Don't call the getModel() that could initialize many in between 3746 // completely useless models. 3747 // IModel model = current.getDefaultModel(); 3748 IModel<?> model = current.getModelImpl(); 3749 3750 if (model instanceof IWrapModel && !(model instanceof IComponentInheritedModel)) 3751 { 3752 model = ((IWrapModel<?>)model).getWrappedModel(); 3753 } 3754 3755 if (model instanceof IComponentInheritedModel) 3756 { 3757 // return the shared inherited 3758 foundModel = ((IComponentInheritedModel<?>)model).wrapOnInheritance(this); 3759 setFlag(FLAG_INHERITABLE_MODEL, true); 3760 break; 3761 } 3762 } 3763 3764 // No model for this component! 3765 return foundModel; 3766 } 3767 3768 /** 3769 * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL OR OVERRIDE. 3770 * 3771 * <p> 3772 * Called anytime a model is changed via setModel or setModelObject. 3773 * </p> 3774 */ 3775 protected void internalOnModelChanged() 3776 { 3777 } 3778 3779 /** 3780 * Components are allowed to reject behavior modifiers. 3781 * 3782 * @param behavior 3783 * @return False, if the component should not apply this behavior 3784 */ 3785 protected boolean isBehaviorAccepted(final Behavior behavior) 3786 { 3787 // Ignore AttributeModifiers when FLAG_IGNORE_ATTRIBUTE_MODIFIER is set 3788 if ((behavior instanceof AttributeModifier) && 3789 (getFlag(FLAG_IGNORE_ATTRIBUTE_MODIFIER) != false)) 3790 { 3791 return false; 3792 } 3793 3794 return behavior.isEnabled(this); 3795 } 3796 3797 /** 3798 * If true, all attribute modifiers will be ignored 3799 * 3800 * @return True, if attribute modifiers are to be ignored 3801 */ 3802 protected final boolean isIgnoreAttributeModifier() 3803 { 3804 return getFlag(FLAG_IGNORE_ATTRIBUTE_MODIFIER); 3805 } 3806 3807 /** 3808 * Called immediately after a component and all its children have been rendered, 3809 * regardless of any exception. 3810 */ 3811 protected void onAfterRender() 3812 { 3813 setRequestFlag(RFLAG_AFTER_RENDER_SUPER_CALL_VERIFIED, true); 3814 } 3815 3816 /** 3817 * Called on all visible components before any component is rendered. 3818 * <p> 3819 * <strong>NOTE</strong>: If you override this, you *must* call super.onBeforeRender() within 3820 * your implementation. 3821 * 3822 * Because this method is responsible for cascading {@link #onBeforeRender()} call to its 3823 * children it is strongly recommended that super call is made at the end of the override. 3824 * </p> 3825 * 3826 * Changes to the component tree can be made only <strong>before</strong> calling 3827 * super.onBeforeRender(). 3828 * 3829 * @see org.apache.wicket.MarkupContainer#addOrReplace(Component...) 3830 */ 3831 protected void onBeforeRender() 3832 { 3833 setRequestFlag(RFLAG_PREPARED_FOR_RENDER, true); 3834 onBeforeRenderChildren(); 3835 setRequestFlag(RFLAG_BEFORE_RENDER_SUPER_CALL_VERIFIED, true); 3836 } 3837 3838 /** 3839 * Processes the component tag. 3840 * 3841 * Overrides of this method most likely should call the super implementation. 3842 * 3843 * @param tag 3844 * Tag to modify 3845 */ 3846 protected void onComponentTag(final ComponentTag tag) 3847 { 3848 // We can't try to get the ID from markup. This could be different than 3849 // id returned from getMarkupId() prior first rendering the component 3850 // (due to transparent resolvers and borders which break the 1:1 3851 // component <-> markup relation) 3852 if (getFlag(FLAG_OUTPUT_MARKUP_ID)) 3853 { 3854 tag.putInternal(MARKUP_ID_ATTR_NAME, getMarkupId()); 3855 } 3856 3857 DebugSettings debugSettings = getApplication().getDebugSettings(); 3858 String componentPathAttributeName = debugSettings.getComponentPathAttributeName(); 3859 if (Strings.isEmpty(componentPathAttributeName) == false) 3860 { 3861 String path = getPageRelativePath(); 3862 path = path.replace("_", "__"); 3863 path = path.replace(':', '_'); 3864 tag.put(componentPathAttributeName, path); 3865 } 3866 3867 // The markup sourcing strategy may also want to work on the tag 3868 getMarkupSourcingStrategy().onComponentTag(this, tag); 3869 } 3870 3871 /** 3872 * Processes the body. 3873 * 3874 * @param markupStream 3875 * The markup stream 3876 * @param openTag 3877 * The open tag for the body 3878 */ 3879 public void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag) 3880 { 3881 } 3882 3883 /** 3884 * Called to allow a component to detach resources after use. 3885 * 3886 * Overrides of this method MUST call the super implementation, the most logical place to do 3887 * this is the last line of the override method. 3888 */ 3889 protected void onDetach() 3890 { 3891 setRequestFlag(RFLAG_DETACHING, false); 3892 } 3893 3894 /** 3895 * Called to notify the component it is being removed from the component hierarchy 3896 * 3897 * Overrides of this method MUST call the super implementation, the most logical place to do 3898 * this is the last line of the override method. 3899 */ 3900 protected void onRemove() 3901 { 3902 setRequestFlag(RFLAG_REMOVING_FROM_HIERARCHY, false); 3903 } 3904 3905 /** 3906 * Called anytime a model is changed after the change has occurred 3907 */ 3908 protected void onModelChanged() 3909 { 3910 } 3911 3912 /** 3913 * Called anytime a model is changed, but before the change actually occurs 3914 */ 3915 protected void onModelChanging() 3916 { 3917 } 3918 3919 /** 3920 * Implementation that renders this component. 3921 */ 3922 protected abstract void onRender(); 3923 3924 /** 3925 * Writes a simple tag out to the response stream. Any components that might be referenced by 3926 * the tag are ignored. Also undertakes any tag attribute modifications if they have been added 3927 * to the component. 3928 * 3929 * @param tag 3930 * The tag to write 3931 */ 3932 protected final void renderComponentTag(ComponentTag tag) 3933 { 3934 if (needToRenderTag(tag)) 3935 { 3936 // apply behaviors that are attached to the component tag. 3937 if (tag.hasBehaviors()) 3938 { 3939 Iterator<? extends Behavior> tagBehaviors = tag.getBehaviors(); 3940 while (tagBehaviors.hasNext()) 3941 { 3942 final Behavior behavior = tagBehaviors.next(); 3943 if (behavior.isEnabled(this)) 3944 { 3945 behavior.onComponentTag(this, tag); 3946 } 3947 behavior.detach(this); 3948 } 3949 } 3950 3951 // Apply behavior modifiers 3952 List<? extends Behavior> behaviors = getBehaviors(); 3953 if ((behaviors != null) && !behaviors.isEmpty() && !tag.isClose() && 3954 (isIgnoreAttributeModifier() == false)) 3955 { 3956 tag = tag.mutable(); 3957 for (Behavior behavior : behaviors) 3958 { 3959 // Components may reject some behavior components 3960 if (isBehaviorAccepted(behavior)) 3961 { 3962 behavior.onComponentTag(this, tag); 3963 } 3964 } 3965 } 3966 3967 if ((tag instanceof WicketTag) && !tag.isClose() && 3968 !getFlag(FLAG_IGNORE_ATTRIBUTE_MODIFIER)) 3969 { 3970 ExceptionSettings.NotRenderableErrorStrategy notRenderableErrorStrategy = ExceptionSettings.NotRenderableErrorStrategy.LOG_WARNING; 3971 if (Application.exists()) 3972 { 3973 notRenderableErrorStrategy = getApplication().getExceptionSettings().getNotRenderableErrorStrategy(); 3974 } 3975 3976 String tagName = tag.getNamespace() + ":" + tag.getName(); 3977 String componentId = getId(); 3978 if (getFlag(FLAG_OUTPUT_MARKUP_ID)) 3979 { 3980 String message = String.format("Markup id set on a component that is usually not rendered into markup. " + 3981 "Markup id: %s, component id: %s, component tag: %s.", 3982 getMarkupId(), componentId, tagName); 3983 if (notRenderableErrorStrategy == ExceptionSettings.NotRenderableErrorStrategy.THROW_EXCEPTION) 3984 { 3985 throw new IllegalStateException(message); 3986 } 3987 log.warn(message); 3988 } 3989 if (getFlag(FLAG_PLACEHOLDER)) 3990 { 3991 String message = String.format( 3992 "Placeholder tag set on a component that is usually not rendered into markup. " + 3993 "Component id: %s, component tag: %s.", componentId, tagName); 3994 if (notRenderableErrorStrategy == ExceptionSettings.NotRenderableErrorStrategy.THROW_EXCEPTION) 3995 { 3996 throw new IllegalStateException(message); 3997 } 3998 log.warn(message); 3999 } 4000 } 4001 4002 // Write the tag 4003 tag.writeOutput(getResponse(), !needToRenderTag(null), 4004 getMarkup().getMarkupResourceStream().getWicketNamespace()); 4005 } 4006 } 4007 4008 /** 4009 * Replaces the body with the given one. 4010 * 4011 * @param markupStream 4012 * The markup stream to replace the tag body in 4013 * @param tag 4014 * The tag 4015 * @param body 4016 * The new markup 4017 */ 4018 protected final void replaceComponentTagBody(final MarkupStream markupStream, 4019 final ComponentTag tag, final CharSequence body) 4020 { 4021 // The tag might have been changed from open-close to open. Hence 4022 // we'll need what was in the markup itself 4023 ComponentTag markupOpenTag = null; 4024 4025 // If tag has a body 4026 if (tag.isOpen()) 4027 { 4028 // Get what tag was in the markup; not what the user it might 4029 // have changed it to. 4030 markupOpenTag = markupStream.getPreviousTag(); 4031 4032 // If it was an open tag in the markup as well, than ... 4033 if (markupOpenTag.isOpen()) 4034 { 4035 // skip any raw markup in the body 4036 markupStream.skipRawMarkup(); 4037 } 4038 } 4039 4040 if (body != null) 4041 { 4042 // Write the new body 4043 getResponse().write(body); 4044 } 4045 4046 // If we had an open tag (and not an openclose tag) and we found a 4047 // close tag, we're good 4048 if (tag.isOpen()) 4049 { 4050 // If it was an open tag in the markup, than there must be 4051 // a close tag as well. 4052 if ((markupOpenTag != null) && markupOpenTag.isOpen() && !markupStream.atCloseTag()) 4053 { 4054 // There must be a component in this discarded body 4055 markupStream.throwMarkupException("Expected close tag for '" + markupOpenTag + 4056 "' Possible attempt to embed component(s) '" + markupStream.get() + 4057 "' in the body of this component which discards its body"); 4058 } 4059 } 4060 } 4061 4062 /** 4063 * @param auto 4064 * True to put component into auto-add mode 4065 */ 4066 protected final Component setAuto(final boolean auto) 4067 { 4068 setFlag(FLAG_AUTO, auto); 4069 return this; 4070 } 4071 4072 /** 4073 * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! 4074 * 4075 * @param flag 4076 * The flag to set 4077 * @param set 4078 * True to turn the flag on, false to turn it off 4079 */ 4080 protected final Component setFlag(final int flag, final boolean set) 4081 { 4082 if (set) 4083 { 4084 flags |= flag; 4085 } 4086 else 4087 { 4088 flags &= ~flag; 4089 } 4090 return this; 4091 } 4092 4093 /** 4094 * @param flag 4095 * The flag to set 4096 * @param set 4097 * True to turn the flag on, false to turn it off 4098 */ 4099 final Component setRequestFlag(final short flag, final boolean set) 4100 { 4101 if (set) 4102 { 4103 requestFlags |= flag; 4104 } 4105 else 4106 { 4107 requestFlags &= ~flag; 4108 } 4109 return this; 4110 } 4111 4112 /** 4113 * If true, all attribute modifiers will be ignored 4114 * 4115 * @param ignore 4116 * If true, all attribute modifiers will be ignored 4117 * @return This 4118 */ 4119 protected final Component setIgnoreAttributeModifier(final boolean ignore) 4120 { 4121 setFlag(FLAG_IGNORE_ATTRIBUTE_MODIFIER, ignore); 4122 return this; 4123 } 4124 4125 /** 4126 * @param <V> 4127 * The model type 4128 * @param model 4129 * The model to wrap if need be 4130 * @return The wrapped model 4131 */ 4132 protected final <V> IModel<V> wrap(final IModel<V> model) 4133 { 4134 if (model instanceof IComponentAssignedModel) 4135 { 4136 return ((IComponentAssignedModel<V>)model).wrapOnAssignment(this); 4137 } 4138 return model; 4139 } 4140 4141 /** 4142 * Detaches any child components 4143 */ 4144 void detachChildren() 4145 { 4146 } 4147 4148 /** 4149 * Signals this components removal from hierarchy to all its children. 4150 */ 4151 void removeChildren() 4152 { 4153 } 4154 4155 /** 4156 * Gets the component at the given path. 4157 * 4158 * @param path 4159 * Path to component 4160 * @return The component at the path 4161 */ 4162 @Override 4163 public Component get(final String path) 4164 { 4165 // Path to this component is an empty path 4166 if (path.length() == 0) 4167 { 4168 return this; 4169 } 4170 throw new IllegalArgumentException( 4171 exceptionMessage("Component is not a container and so does not contain the path " + 4172 path)); 4173 } 4174 4175 /** 4176 * @param setRenderingFlag 4177 * rendering flag 4178 */ 4179 void internalMarkRendering(boolean setRenderingFlag) 4180 { 4181 // WICKET-5460 no longer prepared for render 4182 setRequestFlag(RFLAG_PREPARED_FOR_RENDER, false); 4183 4184 setRequestFlag(RFLAG_RENDERING, setRenderingFlag); 4185 } 4186 4187 /** 4188 * @return True if this component or any of its parents is in auto-add mode 4189 */ 4190 public final boolean isAuto() 4191 { 4192 // Search up hierarchy for FLAG_AUTO 4193 for (Component current = this; current != null; current = current.getParent()) 4194 { 4195 if (current.getFlag(FLAG_AUTO)) 4196 { 4197 return true; 4198 } 4199 } 4200 return false; 4201 } 4202 4203 /** 4204 * 4205 * @return <code>true</code> if component has been prepared for render 4206 */ 4207 boolean isPreparedForRender() 4208 { 4209 return getRequestFlag(RFLAG_PREPARED_FOR_RENDER); 4210 } 4211 4212 /** 4213 * This method is here for {@link MarkupContainer}. It is broken out of 4214 * {@link #onBeforeRender()} so we can guarantee that it executes as the last in 4215 * onBeforeRender() chain no matter where user places the <code>super.onBeforeRender()</code> 4216 * call. 4217 */ 4218 void onBeforeRenderChildren() 4219 { 4220 } 4221 4222 /** 4223 * Renders the close tag at the current position in the markup stream. 4224 * 4225 * @param markupStream 4226 * the markup stream 4227 * @param openTag 4228 * the tag to render 4229 * @param renderBodyOnly 4230 * if true, the tag will not be written to the output 4231 */ 4232 final void renderClosingComponentTag(final MarkupStream markupStream, 4233 final ComponentTag openTag, final boolean renderBodyOnly) 4234 { 4235 // Tag should be open tag and not openclose tag 4236 if (openTag.isOpen()) 4237 { 4238 // If we found a close tag and it closes the open tag, we're good 4239 if (markupStream.atCloseTag() && markupStream.getTag().closes(openTag)) 4240 { 4241 // Render the close tag 4242 if ((renderBodyOnly == false) && needToRenderTag(openTag)) 4243 { 4244 openTag.writeSyntheticCloseTag(getResponse()); 4245 } 4246 } 4247 else if (openTag.requiresCloseTag()) 4248 { 4249 // Missing close tag. Some tags, e.g. <p> are handled like <p/> by most browsers and 4250 // thus will not throw an exception. 4251 markupStream.throwMarkupException("Expected close tag for " + openTag); 4252 } 4253 } 4254 } 4255 4256 /** 4257 * Sets the id of this component. 4258 * 4259 * @param id 4260 * The non-null id of this component 4261 */ 4262 private void checkId(final String id) 4263 { 4264 if (!(this instanceof Page)) 4265 { 4266 if (Strings.isEmpty(id)) 4267 { 4268 throw new WicketRuntimeException("Null or empty component ID's are not allowed."); 4269 } 4270 } 4271 4272 if ((id != null) && (id.indexOf(':') != -1 || id.indexOf('~') != -1)) 4273 { 4274 throw new WicketRuntimeException("The component ID must not contain ':' or '~' chars."); 4275 } 4276 } 4277 4278 /** 4279 * THIS IS A WICKET INTERNAL API. DO NOT USE IT. 4280 * 4281 * Sets the parent of a component. Typically what you really want is parent.add(child). 4282 * <p/> 4283 * Note that calling setParent() and not parent.add() will connect the child to the parent, but 4284 * the parent will not know the child. This might not be a problem in some cases, but e.g. 4285 * child.onDetach() will not be invoked (since the parent doesn't know it is his child). 4286 * 4287 * @param parent 4288 * The parent container 4289 */ 4290 public final void setParent(final MarkupContainer parent) 4291 { 4292 if (this.parent != null && log.isDebugEnabled()) 4293 { 4294 log.debug("Replacing parent " + this.parent + " with " + parent); 4295 } 4296 this.parent = parent; 4297 } 4298 4299 /** 4300 * Sets the render allowed flag. 4301 * 4302 * @param renderAllowed 4303 */ 4304 final void setRenderAllowed(boolean renderAllowed) 4305 { 4306 setFlag(FLAG_IS_RENDER_ALLOWED, renderAllowed); 4307 } 4308 4309 /** 4310 * Sets the render allowed flag. 4311 * 4312 * Visit all this page's children (overridden in MarkupContainer) to check rendering 4313 * authorization, as appropriate. We set any result; positive or negative as a temporary boolean 4314 * in the components, and when a authorization exception is thrown it will block the rendering 4315 * of this page 4316 */ 4317 void setRenderAllowed() 4318 { 4319 setRenderAllowed(isActionAuthorized(RENDER)); 4320 } 4321 4322 /** 4323 * Sets whether or not this component is allowed to be visible. This method is meant to be used 4324 * by components to control visibility of other components. A call to 4325 * {@link #setVisible(boolean)} will not always have a desired effect because that component may 4326 * have {@link #isVisible()} overridden. Both {@link #setVisibilityAllowed(boolean)} and 4327 * {@link #isVisibilityAllowed()} are <code>final</code> so their contract is enforced always. 4328 * 4329 * @param allowed 4330 * @return <code>this</code> for chaining 4331 */ 4332 public final Component setVisibilityAllowed(boolean allowed) 4333 { 4334 if (allowed != getFlag(FLAG_VISIBILITY_ALLOWED)) 4335 { 4336 setFlag(FLAG_VISIBILITY_ALLOWED, allowed); 4337 onVisibleStateChanged(); 4338 } 4339 return this; 4340 } 4341 4342 /** 4343 * Gets whether or not visibility is allowed on this component. See 4344 * {@link #setVisibilityAllowed(boolean)} for details. 4345 * 4346 * @return true if this component is allowed to be visible, false otherwise. 4347 */ 4348 public final boolean isVisibilityAllowed() 4349 { 4350 return getFlag(FLAG_VISIBILITY_ALLOWED); 4351 } 4352 4353 /** 4354 * Determines whether or not a component should be visible, taking into account all the factors: 4355 * {@link #isVisible()}, {@link #isVisibilityAllowed()}, {@link #isRenderAllowed()} 4356 * 4357 * @return <code>true</code> if the component should be visible, <code>false</code> otherwise 4358 */ 4359 public final boolean determineVisibility() 4360 { 4361 return isVisible() && isRenderAllowed() && isVisibilityAllowed(); 4362 } 4363 4364 4365 /** 4366 * Calculates enabled state of the component taking its hierarchy into account. A component is 4367 * enabled iff it is itself enabled ({@link #isEnabled()} and {@link #isEnableAllowed()} both 4368 * return <code>true</code>), and all of its parents are enabled. 4369 * 4370 * @return <code>true</code> if this component is enabled</code> 4371 */ 4372 public boolean isEnabledInHierarchy() 4373 { 4374 if (getRequestFlag(RFLAG_ENABLED_IN_HIERARCHY_SET)) 4375 { 4376 return getRequestFlag(RFLAG_ENABLED_IN_HIERARCHY_VALUE); 4377 } 4378 4379 final boolean state; 4380 Component parent = getParent(); 4381 if (parent != null && !parent.isEnabledInHierarchy()) 4382 { 4383 state = false; 4384 } 4385 else 4386 { 4387 state = isEnabled() && isEnableAllowed(); 4388 } 4389 4390 setRequestFlag(RFLAG_ENABLED_IN_HIERARCHY_SET, true); 4391 setRequestFlag(RFLAG_ENABLED_IN_HIERARCHY_VALUE, state); 4392 return state; 4393 } 4394 4395 /** 4396 * Says if the component is rendering currently. 4397 * 4398 * @return true if this component is rendering, false otherwise. 4399 */ 4400 public final boolean isRendering() 4401 { 4402 return getRequestFlag(RFLAG_PREPARED_FOR_RENDER) || getRequestFlag(RFLAG_RENDERING); 4403 } 4404 4405 /** 4406 * Checks whether or not an {@link IRequestListener} can be invoked on this component. Usually components 4407 * deny these invocations if they are either invisible or disabled in hierarchy. 4408 * <p> 4409 * WARNING: be careful when overriding this method because it may open security holes - such as 4410 * allowing a user to click on a link that should be disabled. 4411 * </p> 4412 * <p> 4413 * Example usecase for overriding: Suppose you are building an component that displays images. 4414 * The component generates a callback to itself using {@link IRequestListener} interface and 4415 * uses this callback to stream image data. If such a component is placed inside a disabled 4416 * {@code WebMarkupContainer} we still want to allow the invocation of the request listener callback 4417 * method so that image data can be streamed. Such a component would override this method and 4418 * return {@literal true}. 4419 * </p> 4420 * 4421 * @return {@literal true} iff the listener method can be invoked on this component 4422 */ 4423 public boolean canCallListener() 4424 { 4425 return isEnabledInHierarchy() && isVisibleInHierarchy(); 4426 } 4427 4428 /** 4429 * Render to the web response whatever the component wants to contribute to the head section. 4430 * 4431 * @param response 4432 * Response object 4433 */ 4434 @Override 4435 public void renderHead(IHeaderResponse response) 4436 { 4437 // noop 4438 } 4439 4440 /** {@inheritDoc} */ 4441 @Override 4442 public void onEvent(IEvent<?> event) 4443 { 4444 } 4445 4446 /** {@inheritDoc} */ 4447 @Override 4448 public final <T> void send(IEventSink sink, Broadcast type, T payload) 4449 { 4450 // if there are no event dispatchers then don't even try to send event 4451 if (getApplication().getFrameworkSettings().hasAnyEventDispatchers()) 4452 { 4453 new ComponentEventSender(this, getApplication().getFrameworkSettings()).send(sink, type, 4454 payload); 4455 } 4456 } 4457 4458 /** 4459 * Removes behavior from component 4460 * 4461 * @param behaviors 4462 * behaviors to remove 4463 * 4464 * @return this (to allow method call chaining) 4465 */ 4466 public Component remove(final Behavior... behaviors) 4467 { 4468 for (Behavior behavior : behaviors) 4469 { 4470 Behaviors.remove(this, behavior); 4471 } 4472 return this; 4473 } 4474 4475 /** {@inheritDoc} */ 4476 @Override 4477 public final Behavior getBehaviorById(int id) 4478 { 4479 return Behaviors.getBehaviorById(this, id); 4480 } 4481 4482 /** {@inheritDoc} */ 4483 @Override 4484 public final int getBehaviorId(Behavior behavior) 4485 { 4486 if (behavior.isTemporary(this)) 4487 { 4488 throw new IllegalArgumentException( 4489 "Cannot get a stable id for temporary behavior " + behavior); 4490 } 4491 return Behaviors.getBehaviorId(this, behavior); 4492 } 4493 4494 /** 4495 * Adds a behavior modifier to the component. 4496 * 4497 * @param behaviors 4498 * The behavior modifier(s) to be added 4499 * @return this (to allow method call chaining) 4500 */ 4501 public Component add(final Behavior... behaviors) 4502 { 4503 Behaviors.add(this, behaviors); 4504 return this; 4505 } 4506 4507 /** 4508 * Gets the currently coupled {@link Behavior}s as an unmodifiable list. Returns an empty list 4509 * rather than null if there are no behaviors coupled to this component. 4510 * 4511 * @return The currently coupled behaviors as an unmodifiable list 4512 */ 4513 public final List<? extends Behavior> getBehaviors() 4514 { 4515 return getBehaviors(null); 4516 } 4517 4518 @Override 4519 public boolean canCallListenerAfterExpiry() 4520 { 4521 return getApplication().getPageSettings() 4522 .getCallListenerAfterExpiry() || isStateless(); 4523 } 4524 /** 4525 * This method is called whenever a component is re-added to the page's component tree, if it 4526 * had been removed at some earlier time, i.e., if it is already initialized 4527 * (see {@link org.apache.wicket.Component#isInitialized()}). 4528 * 4529 * This is similar to onInitialize, but only comes after the component has been removed and 4530 * then added again: 4531 * 4532 * <ul> 4533 * <li>onInitialize is only called the very first time a component is added</li> 4534 * <li>onReAdd is not called the first time, but every time it is re-added after having been 4535 * removed</li> 4536 * </ul> 4537 * 4538 * You can think of it as the opposite of onRemove. A component that was once removed will 4539 * not be re-initialized but only re-added. 4540 * 4541 * Subclasses that override this must call super.onReAdd(). 4542 */ 4543 protected void onReAdd() 4544 { 4545 setRequestFlag(RFLAG_ON_RE_ADD_SUPER_CALL_VERIFIED, true); 4546 } 4547}