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;
018
019import org.apache.wicket.Component;
020import org.apache.wicket.MarkupContainer;
021import org.apache.wicket.markup.ComponentTag;
022import org.apache.wicket.markup.MarkupElement;
023import org.apache.wicket.markup.MarkupStream;
024import org.apache.wicket.markup.WicketTag;
025import org.apache.wicket.markup.html.internal.HtmlHeaderContainer;
026import org.apache.wicket.markup.resolver.ComponentResolvers;
027import org.apache.wicket.markup.resolver.IComponentResolver;
028import org.apache.wicket.request.Request;
029import org.apache.wicket.request.cycle.RequestCycle;
030import org.apache.wicket.request.http.WebRequest;
031
032/**
033 * A simple "transparent" markup container.
034 * 
035 * @author Juergen Donnerstag
036 */
037public class TransparentWebMarkupContainer extends WebMarkupContainer implements IComponentResolver
038{
039        private static final long serialVersionUID = 1L;
040
041        /**
042         * Construct.
043         * 
044         * @param id
045         *          The component id
046         */
047        public TransparentWebMarkupContainer(String id)
048        {
049                super(id);
050        }
051
052        /**
053         * @see org.apache.wicket.markup.resolver.IComponentResolver#resolve(org.apache.wicket.MarkupContainer,
054         *      org.apache.wicket.markup.MarkupStream, org.apache.wicket.markup.ComponentTag)
055         */
056        @Override
057        public Component resolve(MarkupContainer container, MarkupStream markupStream, ComponentTag tag)
058        {
059                if (tag instanceof WicketTag && ((WicketTag)tag).isFragmentTag())
060                {
061                        // even having a wicket:id it isn't a component's markup
062                        return null;
063                }
064
065                Component resolvedComponent = getParent().get(tag.getId());
066                if (resolvedComponent != null && getPage().wasRendered(resolvedComponent))
067                {
068                        /*
069                         * Means that parent container has an associated homonymous tag to this grandchildren
070                         * tag in markup. The parent container wants render it and it should be not resolved to
071                         * their grandchildren.
072                         */
073                        return null;
074                }
075                return resolvedComponent;
076        }
077
078        @Override
079        public void internalRenderHead(HtmlHeaderContainer container)
080        {
081                /*
082                 * https://issues.apache.org/jira/browse/WICKET-5941
083                 * 
084                 * if this component is updated via AJAX and is the root
085                 * one, then render head also for embedded components.
086                 */
087                if (isAjaxRequest() && !isParentRendering())
088                {
089                        renderHeadForInnerSiblings(container);
090                }
091
092                super.internalRenderHead(container);
093        }
094
095        /**
096         * Says if parent container is rendering or not.
097         * 
098         * @return true if parent is rendering, false otherwise.
099         */
100        private boolean isParentRendering()
101        {
102                MarkupContainer parent = getParent();
103
104                return parent != null && parent.isRendering();
105        }
106
107        /**
108         * Says if the current request is an AJAX one.
109         * 
110         * @return true if the current request is an AJAX one, false otherwise.
111         */
112        private boolean isAjaxRequest()
113        {
114                Request request = RequestCycle.get().getRequest();
115
116                if (request instanceof WebRequest)
117                {
118                        WebRequest webRequest = (WebRequest)request;
119                        return webRequest.isAjax();
120                }
121
122                return false;
123        }
124
125        /**
126         * Renders head for embedded component, i.e. those who are not added 
127         * directly to this container but have the markup inside it.
128         * 
129         * @param container
130         *                              The HtmlHeaderContainer
131         */
132        private void renderHeadForInnerSiblings(HtmlHeaderContainer container)
133        {
134                MarkupStream stream = new MarkupStream(getMarkup());
135
136                while (stream.isCurrentIndexInsideTheStream())
137                {
138                        MarkupElement childOpenTag = stream.nextOpenTag();
139
140                        if ((childOpenTag instanceof ComponentTag) && !stream.atCloseTag())
141                        {
142                                // Get element as tag
143                                final ComponentTag tag = (ComponentTag)childOpenTag;
144
145                                // Get component id
146                                final String id = tag.getId();
147
148                                Component component = null;
149
150                                if (get(id) == null)
151                                {
152                                        component = ComponentResolvers.resolveByComponentHierarchy(this, stream, tag);
153                                }
154
155                                if (component != null)
156                                {
157                                        component.internalRenderHead(container);
158                                }
159
160                                //consider just direct children
161                                stream.skipToMatchingCloseTag(tag);
162                        }
163                }
164        }
165        
166        @Override
167        protected Component findChildComponent(ComponentTag tag)
168        {
169                Component childComponent = super.findChildComponent(tag);
170                
171                return childComponent != null ? childComponent : resolve(this, null, tag);
172        }
173}