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.renderStrategy; 018 019import java.lang.reflect.InvocationTargetException; 020 021import org.apache.wicket.Application; 022import org.apache.wicket.Component; 023import org.apache.wicket.ajax.AjaxRequestTarget; 024import org.apache.wicket.application.HeaderContributorListenerCollection; 025import org.apache.wicket.markup.head.IHeaderResponse; 026import org.apache.wicket.markup.html.IHeaderContributor; 027import org.apache.wicket.markup.html.internal.HtmlHeaderContainer; 028import org.apache.wicket.markup.html.internal.HtmlHeaderContainer.HeaderStreamState; 029import org.apache.wicket.markup.html.internal.InlineEnclosure; 030import org.apache.wicket.util.lang.Args; 031import org.apache.wicket.util.visit.IVisit; 032import org.apache.wicket.util.visit.IVisitor; 033 034/** 035 * An abstract implementation of a header render strategy which is only missing the code to traverse 036 * the child hierarchy, since the sequence of that traversal is what will make the difference 037 * between the different header render strategies. 038 * 039 * Besides the child hierarchy the render sequence by default (may be changed via subclassing) is as 040 * follows: 041 * <ul> 042 * <li>1. application level headers</li> 043 * <li>2. the root component's headers</li> 044 * <li>3. the children hierarchy (to be implemented per subclass)</li> 045 * </ul> 046 * 047 * @author Juergen Donnerstag 048 */ 049public abstract class AbstractHeaderRenderStrategy implements IHeaderRenderStrategy 050{ 051 /** 052 * @return Gets the strategy registered with the application 053 */ 054 public static IHeaderRenderStrategy get() 055 { 056 // NOT OFFICIALLY SUPPORTED BY WICKET 057 // By purpose it is "difficult" to change to another render strategy. 058 // We don't want it to be modifiable by users, but we needed a way to easily test other 059 // strategies. 060 String className = System.getProperty("Wicket_HeaderRenderStrategy"); 061 if (className != null) 062 { 063 Class<?> clazz; 064 try 065 { 066 clazz = Application.get() 067 .getApplicationSettings() 068 .getClassResolver() 069 .resolveClass(className); 070 071 if (clazz != null) 072 { 073 return (IHeaderRenderStrategy)clazz.getDeclaredConstructor().newInstance(); 074 } 075 } 076 catch (ClassNotFoundException | InstantiationException | IllegalAccessException 077 | NoSuchMethodException | InvocationTargetException ex) 078 { 079 // ignore 080 } 081 } 082 083 // Our default header render strategy 084 // Pre 1.5 085 // return new ParentFirstHeaderRenderStrategy(); 086 087 // Since 1.5 088 return new ChildFirstHeaderRenderStrategy(); 089 } 090 091 /** 092 * Construct. 093 */ 094 public AbstractHeaderRenderStrategy() 095 { 096 } 097 098 @Override 099 public void renderHeader(final HtmlHeaderContainer headerContainer, 100 HeaderStreamState headerStreamState, final Component rootComponent) 101 { 102 Args.notNull(headerContainer, "headerContainer"); 103 Args.notNull(rootComponent, "rootComponent"); 104 105 // First the application level headers 106 renderApplicationLevelHeaders(headerContainer); 107 108 // Then the root component's headers 109 renderRootComponent(headerContainer, headerStreamState, rootComponent); 110 111 // Then its child hierarchy 112 renderChildHeaders(headerContainer, rootComponent); 113 } 114 115 /** 116 * Render the root component (e.g. Page). 117 * 118 * @param headerContainer 119 * @param headerStreamState 120 * @param rootComponent 121 */ 122 protected void renderRootComponent(final HtmlHeaderContainer headerContainer, 123 final HeaderStreamState headerStreamState, final Component rootComponent) 124 { 125 headerContainer.renderHeaderTagBody(headerStreamState); 126 127 rootComponent.internalRenderHead(headerContainer); 128 129 if (rootComponent instanceof InlineEnclosure) { 130 renderInlineEnclosure(headerContainer, (InlineEnclosure) rootComponent); 131 } 132 } 133 134 135 /** 136 * Searches for the siblings of the given enclosure for the controller of the given enclosure and 137 * renders that controller's header contributions. 138 * 139 * This is done explicitly because when an enclosed component is added to the {@link AjaxRequestTarget} 140 * and is consequently replaced for render by the enclosure, the component's header contributions would not make 141 * it to the response as the enclosure is a sibling of the component in the hierarchy and only children's header contributions 142 * are added to the response. 143 * 144 * Fixes WICKET-6459 145 * 146 * @param container the header container to render the header contributions of the enclosure's controller 147 * @param enclosure the enclosure whose controller's contributions are going to be rendered 148 */ 149 protected void renderInlineEnclosure(HtmlHeaderContainer container, InlineEnclosure enclosure){ 150 151 final String childId = enclosure.getChildId(); 152 153 // Visit the siblings of the enclosure to attempt and find the controller of the enclosure 154 Component enclosureController = enclosure.getParent().visitChildren(new IVisitor<Component, Component>() { 155 @Override 156 public void component(Component object, IVisit<Component> visit) { 157 if (object.getId().equals(childId)){ 158 visit.stop(object); 159 } else { 160 visit.dontGoDeeper(); 161 } 162 } 163 }); 164 165 if (enclosureController != null){ 166 enclosureController.internalRenderHead(container); 167 } 168 } 169 170 /** 171 * Render the child hierarchy headers. 172 * 173 * @param headerContainer 174 * @param rootComponent 175 */ 176 abstract protected void renderChildHeaders(final HtmlHeaderContainer headerContainer, 177 final Component rootComponent); 178 179 /** 180 * Render the application level headers 181 * 182 * @param headerContainer 183 */ 184 protected final void renderApplicationLevelHeaders(final HtmlHeaderContainer headerContainer) 185 { 186 Args.notNull(headerContainer, "headerContainer"); 187 188 if (Application.exists()) 189 { 190 HeaderContributorListenerCollection headerContributorListenerCollection = 191 Application.get().getHeaderContributorListeners(); 192 IHeaderResponse headerResponse = headerContainer.getHeaderResponse(); 193 194 for (IHeaderContributor listener : headerContributorListenerCollection) 195 { 196 listener.renderHead(headerResponse); 197 } 198 } 199 } 200}