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    */
017    package javax.servlet.jsp.tagext;
018    
019    import java.io.Serializable;
020    import java.util.Enumeration;
021    import java.util.Hashtable;
022    
023    import javax.servlet.jsp.JspException;
024    import javax.servlet.jsp.PageContext;
025    
026    /**
027     * A base class for defining new tag handlers implementing Tag.
028     *
029     * <p> The TagSupport class is a utility class intended to be used as
030     * the base class for new tag handlers.  The TagSupport class
031     * implements the Tag and IterationTag interfaces and adds additional
032     * convenience methods including getter methods for the properties in
033     * Tag.  TagSupport has one static method that is included to
034     * facilitate coordination among cooperating tags.
035     *
036     * <p> Many tag handlers will extend TagSupport and only redefine a
037     * few methods. 
038     */
039    
040    public class TagSupport implements IterationTag, Serializable {
041    
042        /**
043         * Find the instance of a given class type that is closest to a given
044         * instance.
045         * This method uses the getParent method from the Tag
046         * interface.
047         * This method is used for coordination among cooperating tags.
048         *
049         * <p>
050         * The current version of the specification only provides one formal
051         * way of indicating the observable type of a tag handler: its
052         * tag handler implementation class, described in the tag-class
053         * subelement of the tag element.  This is extended in an
054         * informal manner by allowing the tag library author to
055         * indicate in the description subelement an observable type.
056         * The type should be a subtype of the tag handler implementation
057         * class or void.
058         * This addititional constraint can be exploited by a
059         * specialized container that knows about that specific tag library,
060         * as in the case of the JSP standard tag library.
061         *
062         * <p>
063         * When a tag library author provides information on the
064         * observable type of a tag handler, client programmatic code
065         * should adhere to that constraint.  Specifically, the Class
066         * passed to findAncestorWithClass should be a subtype of the
067         * observable type.
068         * 
069         *
070         * @param from The instance from where to start looking.
071         * @param klass The subclass of Tag or interface to be matched
072         * @return the nearest ancestor that implements the interface
073         * or is an instance of the class specified
074         */
075    
076        public static final Tag findAncestorWithClass(Tag from, Class klass) {
077            boolean isInterface = false;
078    
079            if (from == null ||
080                klass == null ||
081                (!Tag.class.isAssignableFrom(klass) &&
082                 !(isInterface = klass.isInterface()))) {
083                return null;
084            }
085    
086            for (;;) {
087                Tag tag = from.getParent();
088    
089                if (tag == null) {
090                    return null;
091                }
092    
093                if ((isInterface && klass.isInstance(tag)) ||
094                    klass.isAssignableFrom(tag.getClass()))
095                    return tag;
096                else
097                    from = tag;
098            }
099        }
100    
101        /**
102         * Default constructor, all subclasses are required to define only
103         * a public constructor with the same signature, and to call the
104         * superclass constructor.
105         *
106         * This constructor is called by the code generated by the JSP
107         * translator.
108         */
109    
110        public TagSupport() { }
111    
112        /**
113         * Default processing of the start tag, returning SKIP_BODY.
114         *
115         * @return SKIP_BODY
116         * @throws JspException if an error occurs while processing this tag
117         *
118         * @see Tag#doStartTag()
119         */
120     
121        public int doStartTag() throws JspException {
122            return SKIP_BODY;
123        }
124    
125        /**
126         * Default processing of the end tag returning EVAL_PAGE.
127         *
128         * @return EVAL_PAGE
129         * @throws JspException if an error occurs while processing this tag
130         *
131         * @see Tag#doEndTag()
132         */
133    
134        public int doEndTag() throws JspException {
135            return EVAL_PAGE;
136        }
137    
138    
139        /**
140         * Default processing for a body.
141         *
142         * @return SKIP_BODY
143         * @throws JspException if an error occurs while processing this tag
144         *
145         * @see IterationTag#doAfterBody()
146         */
147        
148        public int doAfterBody() throws JspException {
149            return SKIP_BODY;
150        }
151    
152        // Actions related to body evaluation
153    
154    
155        /**
156         * Release state.
157         *
158         * @see Tag#release()
159         */
160    
161        public void release() {
162            parent = null;
163            id = null;
164            if( values != null ) {
165                values.clear();
166            }
167            values = null;
168        }
169    
170        /**
171         * Set the nesting tag of this tag.
172         *
173         * @param t The parent Tag.
174         * @see Tag#setParent(Tag)
175         */
176    
177        public void setParent(Tag t) {
178            parent = t;
179        }
180    
181        /**
182         * The Tag instance most closely enclosing this tag instance.
183         * @see Tag#getParent()
184         *
185         * @return the parent tag instance or null
186         */
187    
188        public Tag getParent() {
189            return parent;
190        }
191    
192        /**
193         * Set the id attribute for this tag.
194         *
195         * @param id The String for the id.
196         */
197    
198        public void setId(String id) {
199            this.id = id;
200        }
201    
202        /**
203         * The value of the id attribute of this tag; or null.
204         *
205         * @return the value of the id attribute, or null
206         */
207        
208        public String getId() {
209            return id;
210        }
211    
212        /**
213         * Set the page context.
214         *
215         * @param pageContext The PageContext.
216         * @see Tag#setPageContext
217         */
218    
219        public void setPageContext(PageContext pageContext) {
220            this.pageContext = pageContext;
221        }
222    
223        /**
224         * Associate a value with a String key.
225         *
226         * @param k The key String.
227         * @param o The value to associate.
228         */
229    
230        public void setValue(String k, Object o) {
231            if (values == null) {
232                values = new Hashtable<String, Object>();
233            }
234            values.put(k, o);
235        }
236    
237        /**
238         * Get a the value associated with a key.
239         *
240         * @param k The string key.
241         * @return The value associated with the key, or null.
242         */
243    
244        public Object getValue(String k) {
245            if (values == null) {
246                return null;
247            } else {
248                return values.get(k);
249            }
250        }
251    
252        /**
253         * Remove a value associated with a key.
254         *
255         * @param k The string key.
256         */
257    
258        public void removeValue(String k) {
259            if (values != null) {
260                values.remove(k);
261            }
262        }
263    
264        /**
265         * Enumerate the keys for the values kept by this tag handler.
266         *
267         * @return An enumeration of all the keys for the values set,
268         *     or null or an empty Enumeration if no values have been set.
269         */
270    
271        public Enumeration<String> getValues() {
272            if (values == null) {
273                return null;
274            }
275            return values.keys();
276        }
277    
278        // private fields
279    
280        private   Tag         parent;
281        private   Hashtable<String, Object>   values;
282        /**
283         * The value of the id attribute of this tag; or null.
284         */
285        protected String      id;
286    
287        // protected fields
288    
289        /**
290         * The PageContext.
291         */
292        protected PageContext pageContext;
293    }
294