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.request.component;
018
019import org.apache.wicket.Component;
020import org.apache.wicket.WicketRuntimeException;
021import org.apache.wicket.behavior.Behavior;
022import org.apache.wicket.behavior.InvalidBehaviorIdException;
023import org.apache.wicket.model.IDetachable;
024
025/**
026 * Base interface for components. The purpose of this interface is to make certain parts of Wicket
027 * easier to mock and unit test.
028 * 
029 * @author Matej Knopp
030 */
031public interface IRequestableComponent
032{
033        /**
034         * Gets this component's path.
035         * 
036         * @return Colon separated path to this component in the component hierarchy
037         */
038        String getPageRelativePath();
039
040        /**
041         * Gets the id of this component.
042         * 
043         * @return The id of this component
044         */
045        String getId();
046
047        /**
048         * Returns page this component belongs to.
049         * 
050         * @return The page holding this component
051         * @throws WicketRuntimeException
052         *             Thrown if component is not yet attached to a Page.
053         */
054        IRequestablePage getPage() throws WicketRuntimeException;
055
056        /**
057         * Gets the component at the given path.
058         * 
059         * @param path
060         *            Path to component
061         * @return The component at the path
062         */
063        IRequestableComponent get(String path);
064
065        /**
066         * Gets a stable id for the specified non-temporary behavior. The id remains stable from the
067         * point this method is first called for the behavior until the behavior has been removed from
068         * the component. This includes from one request to the next, when the component itself is
069         * retained for the next request (i.e. is stateful). Note that the bookkeeping required for
070         * these stable ids increases the memory footprint of the component.
071         * 
072         * @param behavior
073         * @return a stable id for the specified behavior
074         * @throws IllegalArgumentException when the behavior is temporary
075         */
076        int getBehaviorId(Behavior behavior);
077
078        /**
079         * Gets the behavior for the specified id
080         * 
081         * @param id
082         * @return behavior or {@code null} if none
083         * @throws InvalidBehaviorIdException
084         *             when behavior with this id cannot be found
085         */
086        Behavior getBehaviorById(int id);
087
088        /**
089         * Detaches the component.
090         * <p>
091         * NOTE: this method is not inherited from {@link IDetachable} on purpose. in Wicket the
092         * assumption for a long time has been that {@link Component}s do not implement
093         * {@link IDetachable}; doing so may lead to some very nasty side-effects. Consider
094         * {@code AbstractPropertyModel#detach()} which looks like this:
095         * 
096         * <pre>
097         * public void detach()
098         * {
099         *      // Detach nested object if it's a detachable
100         *      if (target instanceof IDetachable)
101         *      {
102         *              ((IDetachable) target).detach();
103         *      }
104         * }
105         * </pre>
106         * 
107         * If the model was constructed thusly, which is quite common: {@code new PropertyModel(this,
108         * "person")} and {@link Component} implemented {@link IDetachable} then calling @{code
109         * model.detach()} will cause an infinite loop with the model trying to detach the component and
110         * the component trying to detach the model.
111         * 
112         * </p>
113         */
114        void detach();
115
116        /**
117         * @return {@code true} if it is save to call an {@link org.apache.wicket.IRequestListener} on
118         *         this component when the owner page is freshly created after expiration
119         */
120        boolean canCallListenerAfterExpiry();
121}