001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.markup.html.link;
018
019import org.apache.wicket.IRequestListener;
020import org.apache.wicket.Page;
021import org.apache.wicket.core.request.handler.IPageProvider;
022import org.apache.wicket.core.request.handler.PageProvider;
023import org.apache.wicket.markup.ComponentTag;
024import org.apache.wicket.markup.html.WebMarkupContainer;
025import org.apache.wicket.request.mapper.parameter.PageParameters;
026
027/**
028 * Implementation of an <a href="http://www.w3.org/TR/REC-html40/present/frames.html#h-16.5">inline
029 * frame</a> component. Must be used with an iframe (&lt;iframe src...) element. The src attribute
030 * will be generated.
031 * 
032 * @author Sven Meier
033 * @author Ralf Ebert
034 * 
035 */
036
037public class InlineFrame extends WebMarkupContainer implements IRequestListener
038{
039        private static final long serialVersionUID = 1L;
040
041        /** The provider of the page. */
042        private final IPageProvider pageProvider;
043
044        /**
045         * Constructs an inline frame that instantiates the given Page class when the content of the
046         * inline frame is requested. The instantiated Page is used to render a response to the user.
047         * 
048         * @param <C>
049         * 
050         * @param id
051         *            See Component
052         * @param c
053         *            Page class
054         */
055        public <C extends Page> InlineFrame(final String id, final Class<C> c)
056        {
057                this(id, c, null);
058        }
059
060        /**
061         * Constructs an inline frame that instantiates the given Page class when the content of the
062         * inline frame is requested. The instantiated Page is used to render a response to the user.
063         * 
064         * @param <C>
065         * 
066         * @param id
067         *            See Component
068         * @param c
069         *            Page class
070         * @param params
071         *            Page parameters
072         */
073        public <C extends Page> InlineFrame(final String id, final Class<C> c,
074                final PageParameters params)
075        {
076                this(id, new PageProvider(c, params));
077
078                // Ensure that c is a subclass of Page
079                if (!Page.class.isAssignableFrom(c))
080                {
081                        throw new IllegalArgumentException("Class " + c + " is not a subclass of Page");
082                }
083        }
084
085        /**
086         * This constructor is ideal if a Page object was passed in from a previous Page. Construct an
087         * inline frame containing the given Page.
088         * 
089         * @param id
090         *            See component
091         * @param page
092         *            The page
093         */
094        public InlineFrame(final String id, final Page page)
095        {
096                this(id, new PageProvider(page.getPageId(), page.getClass(), page.getRenderCount()));
097        }
098
099        /**
100         * This constructor is ideal for constructing pages lazily.
101         * 
102         * Constructs an inline frame which invokes the getPage() method of the IPageLink interface when
103         * the content of the inline frame is requested. Whatever Page objects is returned by this
104         * method will be rendered back to the user.
105         * 
106         * @param id
107         *            See Component
108         * @param pageProvider
109         *            the provider of the page to be contained in the inline frame if and when the
110         *            content is requested
111         */
112        public InlineFrame(final String id, IPageProvider pageProvider)
113        {
114                super(id);
115
116                this.pageProvider = pageProvider;
117        }
118
119        /**
120         * Gets the url to use for this link.
121         * 
122         * @return The URL that this link links to
123         */
124        protected CharSequence getURL()
125        {
126                return urlForListener(new PageParameters());
127        }
128
129        /**
130         * Handles this frame's tag.
131         * 
132         * @param tag
133         *            the component tag
134         * @see org.apache.wicket.Component#onComponentTag(ComponentTag)
135         */
136        @Override
137        protected void onComponentTag(final ComponentTag tag)
138        {
139                checkComponentTag(tag, "iframe");
140
141                // Set href to link to this frame's frameRequested method
142                CharSequence url = getURL();
143
144                // generate the src attribute
145                tag.put("src", url);
146
147                super.onComponentTag(tag);
148        }
149
150        @Override
151        public boolean rendersPage()
152        {
153                return false;
154        }
155
156        @Override
157        public final void onRequest()
158        {
159                setResponsePage(pageProvider.getPageInstance());
160        }
161
162
163        @Override
164        protected boolean getStatelessHint()
165        {
166                /*
167                 * TODO optimization: the inlineframe component does not always have to be stateless.
168                 * 
169                 * unfortunately due to current implementation of using a ILinkListener
170                 * callback it has to always be stateful because it can be put inside a ListView item which
171                 * will not be built upon a stateless callback causing a "component at path
172                 * listview:0:iframe not found" error.
173                 * 
174                 * eventually variant such as (string, ipagemap, class<? extends Page>) can be made
175                 * stateless because they can generate a bookmarkable url. another advantage of a
176                 * bookmarkable url is that multiple iframes will not block.
177                 */
178                return false;
179        }
180}