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.parser.filter; 018 019import java.text.ParseException; 020import java.util.ArrayDeque; 021import java.util.Deque; 022 023import org.apache.wicket.Component; 024import org.apache.wicket.MarkupContainer; 025import org.apache.wicket.markup.ComponentTag; 026import org.apache.wicket.markup.ComponentTag.IAutoComponentFactory; 027import org.apache.wicket.markup.MarkupElement; 028import org.apache.wicket.markup.MarkupResourceStream; 029import org.apache.wicket.markup.MarkupStream; 030import org.apache.wicket.markup.WicketParseException; 031import org.apache.wicket.markup.WicketTag; 032import org.apache.wicket.markup.html.internal.Enclosure; 033import org.apache.wicket.markup.parser.AbstractMarkupFilter; 034import org.apache.wicket.markup.resolver.IComponentResolver; 035import org.apache.wicket.util.string.Strings; 036 037 038/** 039 * This is a markup inline filter. It identifies <wicket:enclosure> tags. If the 'child' 040 * attribute is empty it determines the wicket:id of the child component automatically by analyzing 041 * the wicket component (in this case on one wicket component is allowed) in between the open and 042 * close tags. If the enclosure tag has a 'child' attribute like 043 * <code><wicket:enclosure child="xxx"></code> than more than just one wicket component inside 044 * the enclosure tags are allowed and the child component which determines the visibility of the 045 * enclosure is identified by the 'child' attribute value which must be equal to the relative child 046 * id path. 047 * 048 * @see Enclosure 049 * 050 * @author Juergen Donnerstag 051 */ 052public final class EnclosureHandler extends AbstractMarkupFilter implements IComponentResolver 053{ 054 private static final long serialVersionUID = 1L; 055 056 private static final IAutoComponentFactory FACTORY = new IAutoComponentFactory() 057 { 058 @Override 059 public Component newComponent(MarkupContainer container, ComponentTag tag) 060 { 061 return new Enclosure(tag.getId(), tag 062 .getAttribute(EnclosureHandler.CHILD_ATTRIBUTE)); 063 } 064 }; 065 066 /** */ 067 public static final String ENCLOSURE = "enclosure"; 068 069 /** The child attribute */ 070 public static final String CHILD_ATTRIBUTE = "child"; 071 072 /** Stack of <wicket:enclosure> tags */ 073 private Deque<ComponentTag> stack; 074 075 /** The id of the first wicket tag inside the enclosure */ 076 private String childId; 077 078 /** 079 * Construct. 080 */ 081 public EnclosureHandler() 082 { 083 this(null); 084 } 085 086 public EnclosureHandler(MarkupResourceStream resourceStream) 087 { 088 super(resourceStream); 089 } 090 091 @Override 092 protected final MarkupElement onComponentTag(ComponentTag tag) throws ParseException 093 { 094 final boolean isWicketTag = tag instanceof WicketTag; 095 final boolean isEnclosureTag = isWicketTag && ((WicketTag)tag).isEnclosureTag(); 096 097 // If wicket:enclosure 098 if (isEnclosureTag) 099 { 100 // If open tag, than put the tag onto the stack 101 if (tag.isOpen()) 102 { 103 tag.setId(tag.getId() + getRequestUniqueId()); 104 tag.setModified(true); 105 tag.setAutoComponentFactory(FACTORY); 106 107 if (stack == null) 108 { 109 stack = new ArrayDeque<>(); 110 } 111 stack.push(tag); 112 } 113 // If close tag, then remove the tag from the stack and update 114 // the child attribute of the open tag if required 115 else if (tag.isClose()) 116 { 117 if (stack == null) 118 { 119 throw new WicketParseException("Missing open tag for Enclosure:", tag); 120 } 121 122 // Remove the open tag from the stack 123 ComponentTag lastEnclosure = stack.pop(); 124 125 // If the child attribute has not been given by the user, 126 // then ... 127 if (childId != null) 128 { 129 lastEnclosure.put(CHILD_ATTRIBUTE, childId); 130 lastEnclosure.setModified(true); 131 childId = null; 132 } 133 134 if (stack.size() == 0) 135 { 136 stack = null; 137 } 138 } 139 else 140 { 141 throw new WicketParseException("Open-close tag not allowed for Enclosure:", tag); 142 } 143 } 144 // Are we inside a wicket:enclosure tag? 145 else if (stack != null) 146 { 147 ComponentTag lastEnclosure = stack.getFirst(); 148 149 // If the enclosure tag has NO child attribute, then ... 150 if (Strings.isEmpty(lastEnclosure.getAttribute(CHILD_ATTRIBUTE))) 151 { 152 String id = tag.getAttribute(getWicketNamespace() + ":id"); 153 if (id != null) 154 { 155 // We encountered more than one child component inside 156 // the enclosure and are not able to automatically 157 // determine the child component to delegate the 158 // isVisible() to => Exception 159 if (childId != null) 160 { 161 throw new WicketParseException("Use <" + getWicketNamespace() + 162 ":enclosure child='xxx'> to name the child component:", tag); 163 } 164 // Remember the child id. The open tag will be updated 165 // once the close tag is found. See above. 166 childId = id; 167 } 168 } 169 } 170 171 return tag; 172 } 173 174 @Override 175 public Component resolve(final MarkupContainer container, final MarkupStream markupStream, 176 final ComponentTag tag) 177 { 178 if ((tag instanceof WicketTag) && ((WicketTag)tag).isEnclosureTag()) 179 { 180 // Yes, we handled the tag 181 return new Enclosure(tag.getId(), tag.getAttribute(EnclosureHandler.CHILD_ATTRIBUTE)); 182 } 183 184 // We were not able to handle the tag 185 return null; 186 } 187}