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.panel; 018 019import static org.apache.wicket.markup.parser.filter.WicketTagIdentifier.FRAGMENT; 020 021import org.apache.wicket.Component; 022import org.apache.wicket.MarkupContainer; 023import org.apache.wicket.markup.ComponentTag; 024import org.apache.wicket.markup.IMarkupFragment; 025import org.apache.wicket.markup.Markup; 026import org.apache.wicket.markup.MarkupElement; 027import org.apache.wicket.markup.MarkupException; 028import org.apache.wicket.markup.MarkupNotFoundException; 029import org.apache.wicket.markup.MarkupStream; 030import org.apache.wicket.markup.TagUtils; 031import org.apache.wicket.markup.WicketTag; 032import org.apache.wicket.util.lang.Args; 033 034/** 035 * A markup sourcing strategy suitable for Fragment components. 036 * 037 * @author Juergen Donnerstag 038 */ 039public class FragmentMarkupSourcingStrategy extends AbstractMarkupSourcingStrategy 040{ 041 /** The wicket:id of the associated markup fragment */ 042 private String markupId; 043 044 /** The container providing the inline markup */ 045 private final MarkupContainer markupProvider; 046 047 /** 048 * Constructor. 049 * 050 * @param markupId 051 * The associated id of the associated markup fragment 052 * @param markupProvider 053 * The component whose markup contains the fragment's markup 054 */ 055 public FragmentMarkupSourcingStrategy(final String markupId, 056 final MarkupContainer markupProvider) 057 { 058 Args.notNull(markupId, "markupId"); 059 060 this.markupId = markupId; 061 this.markupProvider = markupProvider; 062 } 063 064 /** 065 * Skip the body markup associated with the 'component'. The body markup is expected to be raw 066 * markup only, not containing an wicket component. The body markup may serve documentary 067 * purposes for the developer / designer. 068 * <p> 069 * Than search for the markup of the fragment, effectively replacing the original markup. 070 */ 071 @Override 072 public void onComponentTagBody(final Component component, final MarkupStream markupStream, 073 final ComponentTag openTag) 074 { 075 // Skip the body markup making sure it contains only raw markup 076 super.onComponentTagBody(component, markupStream, openTag); 077 078 // Get the fragments open tag 079 MarkupStream stream = new MarkupStream(getMarkup((MarkupContainer)component, null)); 080 ComponentTag fragmentOpenTag = stream.getTag(); 081 082 // if it is an open close tag, skip this fragment. 083 if (!fragmentOpenTag.isOpenClose()) 084 { 085 // We'll completely ignore the fragments open tag. It'll not be rendered 086 stream.next(); 087 088 // Render the body of the fragment 089 component.onComponentTagBody(stream, fragmentOpenTag); 090 } 091 } 092 093 /** 094 * Returns markup provider associated with this fragment 095 * 096 * @param component 097 * @return markup provider 098 */ 099 protected final MarkupContainer getMarkupProvider(final Component component) 100 { 101 return (markupProvider != null ? markupProvider : component.getParent()); 102 } 103 104 /** 105 * Get the markup stream which shall be used to search for the fragment 106 * 107 * @param component 108 * @return The markup stream to be used to find the fragment markup 109 */ 110 public IMarkupFragment chooseMarkup(final Component component) 111 { 112 return getMarkupProvider(component).getMarkup(null); 113 } 114 115 /** 116 * Search for the child's markup in the fragment markup. 117 */ 118 @Override 119 public IMarkupFragment getMarkup(final MarkupContainer container, final Component child) 120 { 121 // Get the markup to search for the fragment markup 122 IMarkupFragment markup = chooseMarkup(container); 123 if (markup == null) 124 { 125 throw new MarkupException("The fragments markup provider has no associated markup. " + 126 "No markup to search for fragment markup with id: " + markupId); 127 } 128 129 // Search for the fragment markup 130 IMarkupFragment childMarkup = TagUtils.findTagMarkup(markup, markupId, FRAGMENT, 1 ); 131 if (childMarkup == null) 132 { 133 // There is one more option if the markup provider has associated markup 134 MarkupContainer markupProvider = getMarkupProvider(container); 135 Markup associatedMarkup = markupProvider.getAssociatedMarkup(); 136 if (associatedMarkup != null) 137 { 138 markup = associatedMarkup; 139 if (markup != null) 140 { 141 childMarkup = markup.find(markupId); 142 } 143 } 144 } 145 146 if (childMarkup == null) 147 { 148 throw new MarkupNotFoundException("No Markup found for Fragment " + markupId + 149 " in providing markup container " + getMarkupProvider(container)); 150 } 151 else 152 { 153 MarkupElement fragmentTag = childMarkup.get(0); 154 if ((fragmentTag instanceof WicketTag && ((WicketTag)fragmentTag).isFragmentTag()) == false) 155 { 156 throw new MarkupNotFoundException("Markup found for Fragment '" + markupId 157 + "' in providing markup container " + getMarkupProvider(container) 158 + " is not a <wicket:fragment> tag"); 159 } 160 } 161 162 if (child == null) 163 { 164 return childMarkup; 165 } 166 167 // search for the child inside the fragment markup 168 return childMarkup.find(child.getId()); 169 } 170}