001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.servlet.tags.form;
018
019import java.beans.PropertyEditor;
020
021import javax.servlet.jsp.JspException;
022
023import org.springframework.lang.Nullable;
024import org.springframework.util.ObjectUtils;
025import org.springframework.web.servlet.tags.HtmlEscapingAwareTag;
026
027/**
028 * Base class for all JSP form tags. Provides utility methods for
029 * null-safe EL evaluation and for accessing and working with a {@link TagWriter}.
030 *
031 * <p>Subclasses should implement the {@link #writeTagContent(TagWriter)} to perform
032 * actual tag rendering.
033 *
034 * <p>Subclasses (or test classes) can override the {@link #createTagWriter()} method to
035 * redirect output to a {@link java.io.Writer} other than the {@link javax.servlet.jsp.JspWriter}
036 * associated with the current {@link javax.servlet.jsp.PageContext}.
037 *
038 * @author Rob Harrop
039 * @author Juergen Hoeller
040 * @since 2.0
041 */
042@SuppressWarnings("serial")
043public abstract class AbstractFormTag extends HtmlEscapingAwareTag {
044
045        /**
046         * Evaluate the supplied value for the supplied attribute name.
047         * <p>The default implementation simply returns the given value as-is.
048         */
049        @Nullable
050        protected Object evaluate(String attributeName, @Nullable Object value) throws JspException {
051                return value;
052        }
053
054        /**
055         * Optionally writes the supplied value under the supplied attribute name into the supplied
056         * {@link TagWriter}. In this case, the supplied value is {@link #evaluate evaluated} first
057         * and then the {@link ObjectUtils#getDisplayString String representation} is written as the
058         * attribute value. If the resultant {@code String} representation is {@code null}
059         * or empty, no attribute is written.
060         * @see TagWriter#writeOptionalAttributeValue(String, String)
061         */
062        protected final void writeOptionalAttribute(TagWriter tagWriter, String attributeName, @Nullable String value)
063                        throws JspException {
064
065                if (value != null) {
066                        tagWriter.writeOptionalAttributeValue(attributeName, getDisplayString(evaluate(attributeName, value)));
067                }
068        }
069
070        /**
071         * Create the {@link TagWriter} which all output will be written to. By default,
072         * the {@link TagWriter} writes its output to the {@link javax.servlet.jsp.JspWriter}
073         * for the current {@link javax.servlet.jsp.PageContext}. Subclasses may choose to
074         * change the {@link java.io.Writer} to which output is actually written.
075         */
076        protected TagWriter createTagWriter() {
077                return new TagWriter(this.pageContext);
078        }
079
080        /**
081         * Provide a simple template method that calls {@link #createTagWriter()} and passes
082         * the created {@link TagWriter} to the {@link #writeTagContent(TagWriter)} method.
083         * @return the value returned by {@link #writeTagContent(TagWriter)}
084         */
085        @Override
086        protected final int doStartTagInternal() throws Exception {
087                return writeTagContent(createTagWriter());
088        }
089
090        /**
091         * Get the display value of the supplied {@code Object}, HTML escaped
092         * as required. This version is <strong>not</strong> {@link PropertyEditor}-aware.
093         */
094        protected String getDisplayString(@Nullable Object value) {
095                return ValueFormatter.getDisplayString(value, isHtmlEscape());
096        }
097
098        /**
099         * Get the display value of the supplied {@code Object}, HTML escaped
100         * as required. If the supplied value is not a {@link String} and the supplied
101         * {@link PropertyEditor} is not null then the {@link PropertyEditor} is used
102         * to obtain the display value.
103         */
104        protected String getDisplayString(@Nullable Object value, @Nullable PropertyEditor propertyEditor) {
105                return ValueFormatter.getDisplayString(value, propertyEditor, isHtmlEscape());
106        }
107
108        /**
109         * Overridden to default to {@code true} in case of no explicit default given.
110         */
111        @Override
112        protected boolean isDefaultHtmlEscape() {
113                Boolean defaultHtmlEscape = getRequestContext().getDefaultHtmlEscape();
114                return (defaultHtmlEscape == null || defaultHtmlEscape.booleanValue());
115        }
116
117
118        /**
119         * Subclasses should implement this method to perform tag content rendering.
120         * @return valid tag render instruction as per {@link javax.servlet.jsp.tagext.Tag#doStartTag()}.
121         */
122        protected abstract int writeTagContent(TagWriter tagWriter) throws JspException;
123
124}