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.io.IOException;
020
021import javax.servlet.jsp.JspException;
022import javax.servlet.jsp.tagext.BodyContent;
023import javax.servlet.jsp.tagext.BodyTag;
024
025import org.springframework.lang.Nullable;
026import org.springframework.util.Assert;
027import org.springframework.util.StringUtils;
028
029/**
030 * Convenient super class for many html tags that render content using the databinding
031 * features of the {@link AbstractHtmlElementTag AbstractHtmlElementTag}. The only thing
032 * sub-tags need to do is override {@link #renderDefaultContent(TagWriter)}.
033 *
034 * @author Rob Harrop
035 * @author Juergen Hoeller
036 * @since 2.0
037 */
038@SuppressWarnings("serial")
039public abstract class AbstractHtmlElementBodyTag extends AbstractHtmlElementTag implements BodyTag {
040
041        @Nullable
042        private BodyContent bodyContent;
043
044        @Nullable
045        private TagWriter tagWriter;
046
047
048        @Override
049        protected int writeTagContent(TagWriter tagWriter) throws JspException {
050                onWriteTagContent();
051                this.tagWriter = tagWriter;
052                if (shouldRender()) {
053                        exposeAttributes();
054                        return EVAL_BODY_BUFFERED;
055                }
056                else {
057                        return SKIP_BODY;
058                }
059        }
060
061        /**
062         * If {@link #shouldRender rendering}, flush any buffered
063         * {@link BodyContent} or, if no {@link BodyContent} is supplied,
064         * {@link #renderDefaultContent render the default content}.
065         * @return a {@link javax.servlet.jsp.tagext.Tag#EVAL_PAGE} result
066         */
067        @Override
068        public int doEndTag() throws JspException {
069                if (shouldRender()) {
070                        Assert.state(this.tagWriter != null, "No TagWriter set");
071                        if (this.bodyContent != null && StringUtils.hasText(this.bodyContent.getString())) {
072                                renderFromBodyContent(this.bodyContent, this.tagWriter);
073                        }
074                        else {
075                                renderDefaultContent(this.tagWriter);
076                        }
077                }
078                return EVAL_PAGE;
079        }
080
081        /**
082         * Render the tag contents based on the supplied {@link BodyContent}.
083         * <p>The default implementation simply {@link #flushBufferedBodyContent flushes}
084         * the {@link BodyContent} directly to the output. Subclasses may choose to
085         * override this to add additional content to the output.
086         */
087        protected void renderFromBodyContent(BodyContent bodyContent, TagWriter tagWriter) throws JspException {
088                flushBufferedBodyContent(bodyContent);
089        }
090
091        /**
092         * Clean up any attributes and stored resources.
093         */
094        @Override
095        public void doFinally() {
096                super.doFinally();
097                removeAttributes();
098                this.tagWriter = null;
099                this.bodyContent = null;
100        }
101
102
103        //---------------------------------------------------------------------
104        // Template methods
105        //---------------------------------------------------------------------
106
107        /**
108         * Called at the start of {@link #writeTagContent} allowing subclasses to perform
109         * any precondition checks or setup tasks that might be necessary.
110         */
111        protected void onWriteTagContent() {
112        }
113
114        /**
115         * Should rendering of this tag proceed at all. Returns '{@code true}' by default
116         * causing rendering to occur always, Subclasses can override this if they
117         * provide conditional rendering.
118         */
119        protected boolean shouldRender() throws JspException {
120                return true;
121        }
122
123        /**
124         * Called during {@link #writeTagContent} allowing subclasses to add any attributes to the
125         * {@link javax.servlet.jsp.PageContext} as needed.
126         */
127        protected void exposeAttributes() throws JspException {
128        }
129
130        /**
131         * Called by {@link #doFinally} allowing subclasses to remove any attributes from the
132         * {@link javax.servlet.jsp.PageContext} as needed.
133         */
134        protected void removeAttributes() {
135        }
136
137        /**
138         * The user customised the output of the error messages - flush the
139         * buffered content into the main {@link javax.servlet.jsp.JspWriter}.
140         */
141        protected void flushBufferedBodyContent(BodyContent bodyContent) throws JspException {
142                try {
143                        bodyContent.writeOut(bodyContent.getEnclosingWriter());
144                }
145                catch (IOException ex) {
146                        throw new JspException("Unable to write buffered body content.", ex);
147                }
148        }
149
150        protected abstract void renderDefaultContent(TagWriter tagWriter) throws JspException;
151
152
153        //---------------------------------------------------------------------
154        // BodyTag implementation
155        //---------------------------------------------------------------------
156
157        @Override
158        public void doInitBody() throws JspException {
159                // no op
160        }
161
162        @Override
163        public void setBodyContent(BodyContent bodyContent) {
164                this.bodyContent = bodyContent;
165        }
166
167}