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.model;
018
019import org.apache.wicket.Component;
020
021/**
022 * Quick detachable model that is implements the IComponentAssignedModel and the IModel interfaces.
023 * Its a quick replacement for the current setObject(Component,Object) and getObject(Component)
024 * methods when the component is needed in a detachable model.
025 * 
026 * @author jcompagner
027 * 
028 * @param <T>
029 *            The model object type
030 */
031public class ComponentDetachableModel<T> implements IComponentAssignedModel<T>
032{
033        private static final long serialVersionUID = 1L;
034
035        /**
036         * Transient flag to prevent multiple detach/attach scenario. We need to maintain this flag as
037         * we allow 'null' model values.
038         */
039        private transient boolean attached = false;
040
041        /**
042         * This getObject throws an exception.
043         * 
044         * @see org.apache.wicket.model.IModel#getObject()
045         */
046        @Override
047        public final T getObject()
048        {
049                throw new RuntimeException("get object call not expected on a IComponentAssignedModel");
050        }
051
052        @Override
053        public final void setObject(T object)
054        {
055                throw new RuntimeException("set object call not expected on a IComponentAssignedModel");
056        }
057
058        /**
059         * Gets whether this model has been attached to the current session.
060         * 
061         * @return whether this model has been attached to the current session
062         */
063        public final boolean isAttached()
064        {
065                return attached;
066        }
067
068        /**
069         * Set this model in an attached state. Called if the constructor sets the data. (attached)
070         */
071        protected final void setAttached()
072        {
073                attached = true;
074        }
075
076        /**
077         * Attaches to the current request. Implement this method with custom behavior, such as loading
078         * the model object.
079         */
080        protected void attach()
081        {
082        }
083
084        /**
085         * Called when getObject is called in order to retrieve the detachable object. Before this
086         * method is called, attach() is always called to ensure that the object is attached.
087         * 
088         * @param component
089         *            The component asking for the object
090         * @return The object
091         */
092        protected T getObject(Component component)
093        {
094                return null;
095        }
096
097        /**
098         * Called when setObject is called in order to change the detachable object. Before this method
099         * is called, attach() is always called to ensure that the object is attached.
100         * 
101         * @param component
102         *            The component asking for replacement of the model object
103         * @param object
104         *            The new model object
105         */
106        protected void setObject(Component component, T object)
107        {
108        }
109
110        @Override
111        public IWrapModel<T> wrapOnAssignment(Component comp)
112        {
113                return new WrapModel<T>(comp);
114        }
115
116        private class WrapModel<P> implements IWrapModel<T>
117        {
118                private static final long serialVersionUID = 1L;
119
120                private final Component component;
121
122                /**
123                 * @param comp
124                 */
125                public WrapModel(Component comp)
126                {
127                        component = comp;
128                }
129
130                @Override
131                public IModel<T> getWrappedModel()
132                {
133                        return ComponentDetachableModel.this;
134                }
135
136                /**
137                 * Attaches the model.
138                 */
139                private void attach()
140                {
141                        if (!attached)
142                        {
143                                attached = true;
144                                ComponentDetachableModel.this.attach();
145                        }
146                }
147
148                @Override
149                public T getObject()
150                {
151                        attach();
152                        return ComponentDetachableModel.this.getObject(component);
153                }
154
155                @Override
156                public void setObject(T object)
157                {
158                        attach();
159                        ComponentDetachableModel.this.setObject(component, object);
160                }
161
162                @Override
163                public void detach()
164                {
165                        if (attached)
166                        {
167                                attached = false;
168                                ComponentDetachableModel.this.detach();
169                        }
170                }
171        }
172}