001 // Copyright 2009, 2010 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.tapestry5.corelib.internal; 016 017 import org.apache.tapestry5.MarkupWriter; 018 import org.apache.tapestry5.MarkupWriterListener; 019 import org.apache.tapestry5.dom.Element; 020 import org.apache.tapestry5.ioc.internal.util.OneShotLock; 021 import org.apache.tapestry5.services.HiddenFieldLocationRules; 022 023 /** 024 * Used to position a hidden field (as part of a form-related component). Hidden fields are not allowed to go just 025 * anywhere, there are rules, dictated by the (X)HTML schema, about where they are allowed. We use the 026 * {@link org.apache.tapestry5.MarkupWriterListener} interface to monitor elements as they are started and ended to find 027 * a 028 * place to put content. 029 */ 030 public class HiddenFieldPositioner 031 { 032 /** 033 * The type of element to create. 034 */ 035 private static final String ELEMENT = "input"; 036 037 private final MarkupWriter writer; 038 039 private final HiddenFieldLocationRules rules; 040 041 private final OneShotLock lock = new OneShotLock(); 042 043 private Element hiddenFieldElement; 044 045 private final MarkupWriterListener listener = new MarkupWriterListener() 046 { 047 public void elementDidStart(Element element) 048 { 049 if (rules.placeHiddenFieldInside(element)) 050 { 051 hiddenFieldElement = element.element(ELEMENT); 052 writer.removeListener(this); 053 } 054 } 055 056 public void elementDidEnd(Element element) 057 { 058 if (rules.placeHiddenFieldAfter(element)) 059 { 060 hiddenFieldElement = element.getContainer().element(ELEMENT); 061 writer.removeListener(this); 062 } 063 } 064 }; 065 066 public HiddenFieldPositioner(MarkupWriter writer, HiddenFieldLocationRules rules) 067 { 068 this.writer = writer; 069 this.rules = rules; 070 071 this.writer.addListener(listener); 072 } 073 074 /** 075 * Returns the hidden field element, which can have its attributes filled in. 076 * 077 * @return the element 078 * @throws IllegalStateException 079 * if the element was not positioned 080 */ 081 public Element getElement() 082 { 083 lock.lock(); 084 085 // Remove the listener if it hasn't been removed already. 086 087 writer.removeListener(listener); 088 089 if (hiddenFieldElement == null) 090 throw new IllegalStateException( 091 "The rendered content did not include any elements that allow for the positioning of the hidden form field's element."); 092 093 return hiddenFieldElement; 094 } 095 096 /** 097 * Discard this positioner (an alternative to invoking {@link #getElement()}). 098 * If an {@link Element} has been created for the hidden field, that 099 * element is removed. 100 * 101 * @since 5.2.0 102 */ 103 public void discard() 104 { 105 lock.lock(); 106 107 if (hiddenFieldElement != null) 108 hiddenFieldElement.remove(); 109 110 writer.removeListener(listener); 111 } 112 113 }