001    // Copyright 2004, 2005 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    package org.apache.tapestry.services;
015    
016    import java.io.IOException;
017    
018    import org.apache.tapestry.IComponent;
019    import org.apache.tapestry.IMarkupWriter;
020    import org.apache.tapestry.IRender;
021    import org.apache.tapestry.IRequestCycle;
022    
023    
024    /**
025     * Represents the service responsible for managing all content output that is sent
026     * to the client. In the case of normal http responses this management would inlude 
027     * handing out {@link IMarkupWriter} instances to render components with, as well as 
028     * managing any javascript written to the output using Script templates.
029     *
030     * <p>
031     *  This is a major internal change in terms of the way tapestry renders pages/components.
032     *  Traditionally a response has been rendered via:
033     *  <em>
034     *  IPage.render(writer, cycle);
035     *  </em>
036     *  The logic has now changed somewhat, while the IPage.render(writer, cycle) does still happen, this
037     *  service is the primary invoker of all renders, even nested component bodies. That means that in the majority
038     *  of cases the ResponseBuilder service is used to invoke IComponent.render() throught the entire render
039     *  cycle, creating a great deal of flexibility in terms of what can be done to control the output of a given
040     *  response.
041     * </p>
042     *
043     * <p>
044     * This service was primarily created to help bolster support for more dynamic content responses, such 
045     * as XHR/JSON/etc - where controlling individual component output (and javascript) becomes very important
046     * when managaing client side browser state. 
047     * </p>
048     *
049     * @author jkuhnert
050     * @since 4.1
051     */
052    public interface ResponseBuilder {
053        
054        /**
055         * Inside a {@link org.apache.tapestry.util.ContentType}, the output encoding is called
056         * "charset".
057         */
058        String ENCODING_KEY = "charset";
059    
060        /**
061         * The content type of the response that will be returned.
062         */
063        String CONTENT_TYPE = "text/xml";
064    
065        /**
066         * The response element type.
067         */
068        String ELEMENT_TYPE = "element";
069    
070        /**
071         * The response exception type.
072         */
073        String EXCEPTION_TYPE = "exception";
074    
075        String SCRIPT_TYPE = "script";
076        
077        String BODY_SCRIPT = "bodyscript";
078        
079        String INCLUDE_SCRIPT = "includescript";
080        
081        String INITIALIZATION_SCRIPT = "initializationscript";
082        
083        /**
084         * Implementors that manage content writes dynamically (ie {@link DojoAjaxResponseBuilder}) should
085         * return true to denote that dynamic behaviour is on for a particular response.
086         * @return
087         */
088        boolean isDynamic();
089        
090        /**
091         * Renders the response to a client. Handles transitioning logic
092         * for setting up page and associated components for response.
093         * 
094         * @param cycle
095         *          The main request cycle object for this request.
096         */
097        
098        void renderResponse(IRequestCycle cycle)
099        throws IOException;
100        
101        /**
102         * Invoked to render a renderable object. Performs any necessary
103         * under the hood type logic involving ajax/json/normal responses, where
104         * needed.
105         * 
106         * @param writer 
107         *          The markup writer to use, this may be ignored or swapped
108         *          out for a different writer depending on the implementation being used.
109         * @param render The renderable object to render
110         * @param cycle Render request cycle
111         */
112        
113        void render(IMarkupWriter writer, IRender render, IRequestCycle cycle);
114        
115        /**
116         * If the component identified by the specified id isn't already set to
117         * be updated, will add it to the response for updating. (Only applicable
118         * in dynamic responses such as XHR/JSON ).
119         * 
120         * @param id
121         *          The {@link IComponent} id to update.
122         */
123        void updateComponent(String id);
124        
125        /**
126         * Checks if the rendered response contains a particular component. Contains
127         * can mean many things. In the instance of a dynamic response it could potentially
128         * mean a component explicitly set to be updated - or a component that has a containing
129         * component explicitly set to be updated.
130         * 
131         * @param target The component to check containment of.
132         * @return True if response contains the specified component, false otherwise.
133         */
134        boolean contains(IComponent target);
135        
136        /**
137         * Invoked by {@link PageRenderSupport} to write external js package
138         * includes. This method will be invoked for each external script requesting
139         * inclusion in the response.
140         * 
141         * These will typically be written out as 
142         * <code>
143         * <script type="text/javascript" src="url"></script>
144         * </code>.
145         *
146         * @param writer
147         *          The markup writer to use, this may be ignored or swapped
148         *          out for a different writer depending on the implementation being used.
149         * @param url
150         *          The absolute url to the .js package to be included.
151         * @param cycle
152         *          The associated request.
153         */    
154        void writeExternalScript(IMarkupWriter writer, String url, IRequestCycle cycle);
155        
156        /**
157         * Marks the beginning of the core body script.
158         *
159         * @param writer
160         *          The markup writer to use, this may be ignored or swapped
161         *          out for a different writer depending on the implementation being used.
162         * @param cycle
163         *          The associated request.
164         */
165        void beginBodyScript(IMarkupWriter writer, IRequestCycle cycle);
166        
167        /**
168         * Intended to be written within the confines of the body script, should
169         * be invoked once just after {@link #beginBodyScript(IMarkupWriter, IRequestCycle)} is called
170         * to include any image initializations. This method should only be called if
171         * there are actually images that need pre-initialization. Ie in many instances 
172         * it will not be called at all.
173         *
174         * @param writer
175         *          The markup writer to use, this may be ignored or swapped
176         *          out for a different writer depending on the implementation being used.
177         * @param script
178         *          The non null value of the script images to include. 
179         * @param preloadName 
180         *          The global variable name to give to the preloaded images array.
181         * @param cycle
182         *          The associated request.
183         */
184        void writeImageInitializations(IMarkupWriter writer, String script, String preloadName, IRequestCycle cycle);
185        
186        /**
187         * Called after {@link #beginBodyScript(IMarkupWriter, IRequestCycle)} to write the containing
188         * body script. This method may not be called at all if there is no js body 
189         * to write into the response.
190         *
191         * @param writer
192         *          The markup writer to use, this may be ignored or swapped
193         *          out for a different writer depending on the implementation being used.
194         * @param script
195         *          The script to write into the body response.
196         * @param cycle
197         *          The associated request.
198         */
199        void writeBodyScript(IMarkupWriter writer, String script, IRequestCycle cycle);
200        
201        /**
202         * Marks the end of the body block being called. This method will 
203         * always be called if {@link #beginBodyScript(IMarkupWriter, IRequestCycle)} was previously
204         * called. 
205         *
206         * @param writer
207         *          The markup writer to use, this may be ignored or swapped
208         *          out for a different writer depending on the implementation being used.
209         * @param cycle
210         *          The associated request.
211         */
212        void endBodyScript(IMarkupWriter writer, IRequestCycle cycle);
213        
214        /**
215         * Writes any javascript that should only execute after all other items
216         * on a page have completed rendering. This is typically implemented via
217         * wrapping the executing of the code to some sort of <code>window.onload</code> 
218         * event, but will vary depending on the implementation of the builder being used.
219         * 
220         * This method will ~only~ be called if there is any queued intialization script 
221         * to write.
222         *
223         * @param writer
224         *          The markup writer to use, this may be ignored or swapped
225         *          out for a different writer depending on the implementation being used.
226         * @param script
227         *          The initialzation script to write.
228         */
229        void writeInitializationScript(IMarkupWriter writer, String script);
230        
231        /**
232         * Returns the IMarkupWriter associated with this response, it may or may
233         * not be a NullWriter instance depending on the response type or stage 
234         * of the render cycle. (specifically during rewind)
235         * 
236         * @return A validly writable markup writer, even if the content is sometimes
237         * ignored.
238         */
239        
240        IMarkupWriter getWriter();
241        
242        /**
243         * Gets a write that will output its content in a <code>response</code>
244         * element with the given id and type. 
245         * 
246         * @param id 
247         *          The response element id to give writer.
248         * @param type
249         *          Optional - If specified will give the response element a type
250         *          attribute.
251         * @return A valid {@link IMarkupWriter} instance to write content to.
252         */
253        IMarkupWriter getWriter(String id, String type);
254        
255        /**
256         * Determines if the specified component should have any asset image URL
257         * references embedded in the response.
258         * 
259         * @param target
260         *          The component to allow/disallow image initialization script content from.
261         * @return True if the component script should be allowed.
262         */
263        boolean isImageInitializationAllowed(IComponent target);
264        
265        /**
266         * Determines if the specified component should have its javascript 
267         * body added to the response.
268         * 
269         * @param target
270         *          The component to allow/disallow body script content from.
271         * @return True if the component script should be allowed.
272         */
273        boolean isBodyScriptAllowed(IComponent target);
274        
275        /**
276         * Determines if the specified component should have its javascript 
277         * initialization added to the response.
278         * 
279         * @param target
280         *          The component to allow/disallow initialization script content from.
281         * @return True if the component script should be allowed.
282         */
283        boolean isInitializationScriptAllowed(IComponent target);
284        
285        /**
286         * Determines if the specified component should have its javascript 
287         * external resource scripts added to the response.
288         * 
289         * @param target
290         *          The component to check for inclusion/exclusion.
291         * @return True if external scripts from this component should be added to
292         *          the response.
293         */
294        boolean isExternalScriptAllowed(IComponent target);
295    }