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 */
017package org.apache.wicket.markup.transformer;
018
019import org.apache.wicket.Component;
020import org.apache.wicket.MarkupContainer;
021import org.apache.wicket.WicketRuntimeException;
022import org.apache.wicket.markup.ComponentTag;
023import org.apache.wicket.markup.MarkupStream;
024import org.apache.wicket.model.IModel;
025import org.apache.wicket.request.Response;
026import org.apache.wicket.response.StringResponse;
027
028/**
029 * This abstract container provides the means to post-process the markup generated by its child
030 * components (excluding the containers tag)
031 * <p>
032 * Please see {@link org.apache.wicket.markup.transformer.AbstractTransformerBehavior} for an alternative
033 * based on {@link org.apache.wicket.behavior.Behavior}
034 * 
035 * @see org.apache.wicket.markup.transformer.AbstractTransformerBehavior
036 * @see org.apache.wicket.markup.transformer.ITransformer
037 * 
038 * @author Juergen Donnerstag
039 * */
040public abstract class AbstractOutputTransformerContainer extends MarkupContainer
041        implements
042                ITransformer
043{
044        private static final long serialVersionUID = 1L;
045
046        /** Whether the containers tag shall be transformed as well */
047        private boolean transformBodyOnly = true;
048
049        /**
050         * Construct
051         * 
052         * @see org.apache.wicket.Component#Component(String)
053         */
054        public AbstractOutputTransformerContainer(final String id)
055        {
056                super(id);
057        }
058
059        /**
060         * Construct
061         * 
062         * @see org.apache.wicket.Component#Component(String, IModel)
063         */
064        public AbstractOutputTransformerContainer(final String id, final IModel<?> model)
065        {
066                super(id, model);
067        }
068
069        /**
070         * You can choose whether the body of the tag excluding the tag shall be transformed or
071         * including the tag.
072         * 
073         * @param value
074         *            If true, only the body is applied to transformation.
075         * @return this
076         */
077        public MarkupContainer setTransformBodyOnly(final boolean value)
078        {
079                transformBodyOnly = value;
080                return this;
081        }
082
083        /**
084         * Create a new response object which is used to store the markup generated by the child
085         * objects.
086         * 
087         * @return Response object. Must not be null
088         */
089        protected Response newResponse()
090        {
091                return new StringResponse();
092        }
093
094        @Override
095        public abstract CharSequence transform(final Component component, final CharSequence output)
096                throws Exception;
097
098        @Override
099        public final void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag)
100        {
101                if (transformBodyOnly == true)
102                {
103                        execute(new Runnable()
104                        {
105                                @Override
106                                public void run()
107                                {
108                                        // Invoke default execution
109                                        AbstractOutputTransformerContainer.super.onComponentTagBody(markupStream,
110                                                openTag);
111                                }
112                        });
113                }
114                else
115                {
116                        super.onComponentTagBody(markupStream, openTag);
117                }
118        }
119
120        @Override
121        protected final void onRender()
122        {
123                if (transformBodyOnly == false)
124                {
125                        execute(new Runnable()
126                        {
127                                @Override
128                                public void run()
129                                {
130                                        // Invoke default execution
131                                        AbstractOutputTransformerContainer.super.onRender();
132                                }
133                        });
134                }
135                else
136                {
137                        super.onRender();
138                }
139        }
140
141        /**
142         * 
143         * @param code
144         */
145        private void execute(final Runnable code)
146        {
147                // Temporarily replace the web response with a String response
148                final Response webResponse = getResponse();
149
150                try
151                {
152                        // Create a new response object
153                        final Response response = newResponse();
154                        if (response == null)
155                        {
156                                throw new IllegalStateException("newResponse() must not return null");
157                        }
158
159                        // and make it the current one
160                        getRequestCycle().setResponse(response);
161
162                        // Invoke default execution
163                        code.run();
164
165                        try
166                        {
167                                // Tranform the data
168                                CharSequence output = transform(this, response.toString());
169                                webResponse.write(output);
170                        }
171                        catch (Exception ex)
172                        {
173                                throw new WicketRuntimeException("Error while transforming the output: " + this, ex);
174                        }
175                }
176                finally
177                {
178                        // Restore the original response
179                        getRequestCycle().setResponse(webResponse);
180                }
181        }
182}