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