View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.renderkit.html.util;
20  
21  import org.apache.commons.lang.builder.EqualsBuilder;
22  import org.apache.commons.lang.builder.HashCodeBuilder;
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  import org.apache.myfaces.shared_tomahawk.config.MyfacesConfig;
26  import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
27  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils;
28  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlResponseWriterImpl;
29  import org.apache.myfaces.shared_tomahawk.util.ClassUtils;
30  
31  import javax.faces.context.FacesContext;
32  import javax.faces.context.ResponseWriter;
33  import javax.servlet.http.HttpServletRequest;
34  import javax.servlet.http.HttpServletResponse;
35  import java.io.IOException;
36  import java.util.Iterator;
37  import java.util.LinkedHashSet;
38  import java.util.Set;
39  
40  /**
41   * This is a utility class to render link to resources used by custom components.
42   * Mostly used to avoid having to include [script src="..."][/script]
43   * in the head of the pages before using a component.
44   * <p/>
45   * When used together with the ExtensionsFilter, this class can allow components
46   * in the body of a page to emit script and stylesheet references into the page
47   * head section. The relevant methods on this object simply queue the changes,
48   * and when the page is complete the ExtensionsFilter calls back into this
49   * class to allow it to insert the commands into the buffered response.
50   * <p/>
51   * This class also works with the ExtensionsFilter to allow components to
52   * emit references to javascript/css/etc which are bundled in the component's
53   * jar file. Special URLs are generated which the ExtensionsFilter will later
54   * handle by retrieving the specified resource from the classpath.
55   * <p/>
56   * The special URL format is:
57   * <pre>
58   * {contextPath}/faces/myFacesExtensionResource/
59   *    {resourceLoaderName}/{cacheKey}/{resourceURI}
60   * </pre>
61   * Where:
62   * <ul>
63   * <li> {contextPath} is the context path of the current webapp
64   * <li> {resourceLoaderName} is the fully-qualified name of a class which
65   * implements the ResourceLoader interface. When a browser app sends a request
66   * for the specified resource, an instance of the specified ResourceLoader class
67   * will be created and passed the resourceURI part of the URL for resolving to the
68   * actual resource to be served back. The standard MyFaces ResourceLoader
69   * implementation only serves resources for files stored beneath path
70   * org/apache/myfaces/custom in the classpath but non-myfaces code can provide their
71   * own ResourceLoader implementations.
72   * </ul>
73   *
74   * @author Sylvain Vieujot (latest modification by $Author: mmarinschek $)
75   * @version $Revision: 371739 373827 $ $Date: 2006-01-31 14:50:35 +0000 (Tue, 31 Jan 2006) $
76   */
77  public class DefaultAddResource extends NonBufferingAddResource
78  {
79      protected Log log = LogFactory.getLog(DefaultAddResource.class);
80  
81  
82      protected StringBuffer originalResponse;
83  
84      private Set headerBeginInfo;
85      private Set bodyEndInfo;
86      private Set bodyOnloadInfo;
87  
88  
89      protected boolean parserCalled = false;
90      protected int headerInsertPosition = -1;
91      protected int bodyInsertPosition = -1;
92      protected int beforeBodyPosition = -1;
93      protected int afterBodyContentInsertPosition = -1;
94      protected int beforeBodyEndPosition = -1;
95  
96  
97      protected DefaultAddResource()
98      {
99      }
100 
101     // Methods to add resources
102 
103     /**
104      * Adds the given Javascript resource to the document header at the specified
105      * document positioy by supplying a resourcehandler instance.
106      * <p/>
107      * Use this method to have full control about building the reference url
108      * to identify the resource and to customize how the resource is
109      * written to the response. In most cases, however, one of the convenience
110      * methods on this class can be used without requiring a custom ResourceHandler
111      * to be provided.
112      * <p/>
113      * If the script has already been referenced, it's added only once.
114      * <p/>
115      * Note that this method <i>queues</i> the javascript for insertion, and that
116      * the script is inserted into the buffered response by the ExtensionsFilter
117      * after the page is complete.
118      */
119     public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position,
120                                         ResourceHandler resourceHandler)
121     {
122         addJavaScriptAtPosition(context, position, resourceHandler, false);
123     }
124 
125     /**
126      * Insert a [script src="url"] entry into the document header at the
127      * specified document position. If the script has already been
128      * referenced, it's added only once.
129      * <p/>
130      * The resource is expected to be in the classpath, at the same location as the
131      * specified component + "/resource".
132      * <p/>
133      * Example: when customComponent is class example.Widget, and
134      * resourceName is script.js, the resource will be retrieved from
135      * "example/Widget/resource/script.js" in the classpath.
136      */
137     public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position,
138                                         Class myfacesCustomComponent, String resourceName)
139     {
140         addJavaScriptAtPosition(context, position, new MyFacesResourceHandler(
141                 myfacesCustomComponent, resourceName));
142     }
143 
144     public void addJavaScriptAtPositionPlain(FacesContext context, ResourcePosition position, Class myfacesCustomComponent, String resourceName)
145     {
146         addJavaScriptAtPosition(context, position,
147                 new MyFacesResourceHandler(myfacesCustomComponent, resourceName),
148                 false, false);
149     }
150 
151 
152     /**
153      * Insert a [script src="url"] entry into the document header at the
154      * specified document position. If the script has already been
155      * referenced, it's added only once.
156      *
157      * @param defer specifies whether the html attribute "defer" is set on the
158      *              generated script tag. If this is true then the browser will continue
159      *              processing the html page without waiting for the specified script to
160      *              load and be run.
161      */
162     public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position,
163                                         Class myfacesCustomComponent, String resourceName, boolean defer)
164     {
165         addJavaScriptAtPosition(context, position, new MyFacesResourceHandler(
166                 myfacesCustomComponent, resourceName), defer);
167     }
168 
169     /**
170      * Insert a [script src="url"] entry into the document header at the
171      * specified document position. If the script has already been
172      * referenced, it's added only once.
173      *
174      * @param uri is the location of the desired resource, relative to the base
175      *            directory of the webapp (ie its contextPath).
176      */
177     public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position, String uri)
178     {
179         addJavaScriptAtPosition(context, position, uri, false);
180     }
181 
182     /**
183      * Adds the given Javascript resource at the specified document position.
184      * If the script has already been referenced, it's added only once.
185      */
186     public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position, String uri,
187                                         boolean defer)
188     {
189         addPositionedInfo(position, getScriptInstance(context, uri, defer));
190     }
191 
192     public void addJavaScriptToBodyTag(FacesContext context, String javascriptEventName,
193                                        String addedJavaScript)
194     {
195         AttributeInfo info = new AttributeInfo();
196         info.setAttributeName(javascriptEventName);
197         info.setAttributeValue(addedJavaScript);
198 
199         addPositionedInfo(BODY_ONLOAD, info);
200     }
201 
202     /**
203      * Adds the given Javascript resource at the specified document position.
204      * If the script has already been referenced, it's added only once.
205      */
206     public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position,
207                                         ResourceHandler resourceHandler, boolean defer)
208     {
209         validateResourceHandler(resourceHandler);
210         addPositionedInfo(position, getScriptInstance(context, resourceHandler, defer));
211     }
212 
213     private void addJavaScriptAtPosition(FacesContext context, ResourcePosition position,
214                                          ResourceHandler resourceHandler, boolean defer, boolean encodeUrl)
215     {
216         validateResourceHandler(resourceHandler);
217         addPositionedInfo(position, getScriptInstance(context, resourceHandler, defer, encodeUrl));
218     }
219 
220     /**
221      * Adds the given Style Sheet at the specified document position.
222      * If the style sheet has already been referenced, it's added only once.
223      */
224     public void addStyleSheet(FacesContext context, ResourcePosition position,
225                               Class myfacesCustomComponent, String resourceName)
226     {
227         addStyleSheet(context, position, new MyFacesResourceHandler(myfacesCustomComponent,
228                 resourceName));
229     }
230 
231     /**
232      * Adds the given Style Sheet at the specified document position.
233      * If the style sheet has already been referenced, it's added only once.
234      */
235     public void addStyleSheet(FacesContext context, ResourcePosition position, String uri)
236     {
237         addPositionedInfo(position, getStyleInstance(context, uri));
238     }
239 
240     /**
241      * Adds the given Style Sheet at the specified document position.
242      * If the style sheet has already been referenced, it's added only once.
243      */
244     public void addStyleSheet(FacesContext context, ResourcePosition position,
245                               ResourceHandler resourceHandler)
246     {
247         validateResourceHandler(resourceHandler);
248         addPositionedInfo(position, getStyleInstance(context, resourceHandler));
249     }
250 
251     /**
252      * Adds the given Inline Style at the specified document position.
253      */
254     public void addInlineStyleAtPosition(FacesContext context, ResourcePosition position, String inlineStyle)
255     {
256         addPositionedInfo(position, getInlineStyleInstance(inlineStyle));
257     }
258 
259     /**
260      * Adds the given Inline Script at the specified document position.
261      */
262     public void addInlineScriptAtPosition(FacesContext context, ResourcePosition position,
263                                           String inlineScript)
264     {
265         addPositionedInfo(position, getInlineScriptInstance(inlineScript));
266     }
267 
268     // Positioned stuffs
269 
270     protected Set getHeaderBeginInfos()
271     {
272         if (headerBeginInfo == null)
273         {
274             headerBeginInfo = new LinkedHashSet();
275         }
276         return headerBeginInfo;
277     }
278 
279     protected Set getBodyEndInfos()
280     {
281         if (bodyEndInfo == null)
282         {
283             bodyEndInfo = new LinkedHashSet();
284         }
285         return bodyEndInfo;
286     }
287 
288     protected Set getBodyOnloadInfos()
289     {
290         if (bodyOnloadInfo == null)
291         {
292             bodyOnloadInfo = new LinkedHashSet();
293         }
294         return bodyOnloadInfo;
295     }
296 
297     private void addPositionedInfo(ResourcePosition position, PositionedInfo info)
298     {
299         if (HEADER_BEGIN.equals(position))
300         {
301             Set set = getHeaderBeginInfos();
302             set.add(info);
303         }
304         else if (BODY_END.equals(position))
305         {
306             Set set = getBodyEndInfos();
307             set.add(info);
308 
309         }
310         else if (BODY_ONLOAD.equals(position))
311         {
312             Set set = getBodyOnloadInfos();
313             set.add(info);
314         }
315     }
316 
317     public boolean hasHeaderBeginInfos()
318     {
319         return headerBeginInfo != null;
320     }
321 
322     /**
323      * Parses the response to mark the positions where code will be inserted
324      */
325     public void parseResponse(HttpServletRequest request, String bufferedResponse,
326                               HttpServletResponse response)
327     {
328 
329         originalResponse = new StringBuffer(bufferedResponse);
330 
331         ParseCallbackListener l = new ParseCallbackListener();
332         ReducedHTMLParser.parse(originalResponse, l);
333 
334         headerInsertPosition = l.getHeaderInsertPosition();
335         bodyInsertPosition = l.getBodyInsertPosition();
336         beforeBodyPosition = l.getBeforeBodyPosition();
337         afterBodyContentInsertPosition = l.getAfterBodyContentInsertPosition();
338         beforeBodyEndPosition = l.getAfterBodyEndPosition() - 7;  // 7, which is the length of </body>
339 
340         parserCalled = true;
341     }
342 
343     /**
344      * Writes the javascript code necessary for myfaces in every page, just befode the closing &lt;/body&gt; tag
345      */
346     public void writeMyFacesJavascriptBeforeBodyEnd(HttpServletRequest request,
347                                                     HttpServletResponse response) throws IOException
348     {
349         if (!parserCalled)
350         {
351             throw new IOException("Method parseResponse has to be called first");
352         }
353 
354         if (beforeBodyEndPosition >= 0)
355         {
356             String myFacesJavascript = (String) request.getAttribute("org.apache.myfaces.myFacesJavascript");
357             if (myFacesJavascript != null)
358             {
359                 originalResponse.insert(beforeBodyEndPosition, myFacesJavascript);
360             }
361             else
362             {
363                 log.warn("MyFaces special javascript could not be retrieved from request-map.");
364             }
365         }
366     }
367 
368     /**
369      * Add the resources to the &lt;head&gt; of the page.
370      * If the head tag is missing, but the &lt;body&gt; tag is present, the head tag is added.
371      * If both are missing, no resource is added.
372      * <p/>
373      * The ordering is such that the user header CSS & JS override the MyFaces' ones.
374      */
375     public void writeWithFullHeader(HttpServletRequest request,
376                                     HttpServletResponse response) throws IOException
377     {
378         if (!parserCalled)
379         {
380             throw new IOException("Method parseResponse has to be called first");
381         }
382 
383         boolean addHeaderTags = false;
384 
385         if (headerInsertPosition == -1)
386         {
387             if (beforeBodyPosition != -1)
388             {
389                 // The input html has a body start tag, but no head tags. We therefore
390                 // need to insert head start/end tags for our content to live in.
391                 addHeaderTags = true;
392                 headerInsertPosition = beforeBodyPosition;
393             }
394             else
395             {
396                 // neither head nor body tags in the input
397                 log.warn("Response has no <head> or <body> tag:\n" + originalResponse);
398             }
399         }
400 
401         ResponseWriter writer = new HtmlResponseWriterImpl(response.getWriter(),
402                 HtmlRendererUtils.selectContentType(request.getHeader("accept")),
403                 response.getCharacterEncoding());
404 
405         if (afterBodyContentInsertPosition >= 0)
406         {
407             // insert all the items that want to go immediately after the <body> tag.
408             HtmlBufferResponseWriterWrapper writerWrapper = HtmlBufferResponseWriterWrapper
409                     .getInstance(writer);
410 
411             for (Iterator i = getBodyEndInfos().iterator(); i.hasNext();)
412             {
413                 writerWrapper.write("\n");
414 
415                 PositionedInfo positionedInfo = (PositionedInfo) i.next();
416 
417                 if (!(positionedInfo instanceof WritablePositionedInfo))
418                     throw new IllegalStateException("positionedInfo of type : "
419                             + positionedInfo.getClass().getName());
420                 ((WritablePositionedInfo) positionedInfo).writePositionedInfo(response,
421                         writerWrapper);
422             }
423 
424             originalResponse.insert(headerInsertPosition, writerWrapper.toString());
425         }
426 
427         if (bodyInsertPosition > 0)
428         {
429             StringBuffer buf = new StringBuffer();
430             Set bodyInfos = getBodyOnloadInfos();
431             if (bodyInfos.size() > 0)
432             {
433                 int i = 0;
434                 for (Iterator it = getBodyOnloadInfos().iterator(); it.hasNext();)
435                 {
436                     AttributeInfo positionedInfo = (AttributeInfo) it.next();
437                     if (i == 0)
438                     {
439                         buf.append(positionedInfo.getAttributeName());
440                         buf.append("=\"");
441                     }
442                     buf.append(positionedInfo.getAttributeValue());
443 
444                     i++;
445                 }
446 
447                 buf.append("\"");
448                 originalResponse.insert(bodyInsertPosition - 1, " " + buf.toString());
449             }
450         }
451 
452         if (headerInsertPosition >= 0)
453         {
454             HtmlBufferResponseWriterWrapper writerWrapper = HtmlBufferResponseWriterWrapper
455                     .getInstance(writer);
456 
457             if (addHeaderTags)
458                 writerWrapper.write("<head>");
459 
460             for (Iterator i = getHeaderBeginInfos().iterator(); i.hasNext();)
461             {
462                 writerWrapper.write("\n");
463 
464                 PositionedInfo positionedInfo = (PositionedInfo) i.next();
465 
466                 if (!(positionedInfo instanceof WritablePositionedInfo))
467                     throw new IllegalStateException("positionedInfo of type : "
468                             + positionedInfo.getClass().getName());
469                 ((WritablePositionedInfo) positionedInfo).writePositionedInfo(response,
470                         writerWrapper);
471             }
472 
473             if (addHeaderTags)
474                 writerWrapper.write("</head>");
475 
476             originalResponse.insert(headerInsertPosition, writerWrapper.toString());
477 
478         }
479 
480     }
481 
482     /**
483      * Writes the response
484      */
485     public void writeResponse(HttpServletRequest request,
486                               HttpServletResponse response) throws IOException
487     {
488         ResponseWriter writer = new HtmlResponseWriterImpl(response.getWriter(), HtmlRendererUtils
489                 .selectContentType(request.getHeader("accept")),
490                 response.getCharacterEncoding());
491         writer.write(originalResponse.toString());
492     }
493 
494     private PositionedInfo getStyleInstance(FacesContext context, ResourceHandler resourceHandler)
495     {
496         return new StylePositionedInfo(getResourceUri(context, resourceHandler));
497     }
498 
499     private PositionedInfo getScriptInstance(FacesContext context, ResourceHandler resourceHandler,
500                                              boolean defer)
501     {
502         return new ScriptPositionedInfo(getResourceUri(context, resourceHandler), defer);
503     }
504 
505     private PositionedInfo getScriptInstance(FacesContext context, ResourceHandler resourceHandler,
506                                              boolean defer, boolean encodeURL)
507     {
508            return new ScriptPositionedInfo(getResourceUri(context, resourceHandler), defer, encodeURL);
509     }
510 
511     private PositionedInfo getStyleInstance(FacesContext context, String uri)
512     {
513         return new StylePositionedInfo(getResourceUri(context, uri));
514     }
515 
516     protected PositionedInfo getScriptInstance(FacesContext context, String uri, boolean defer)
517     {
518         return new ScriptPositionedInfo(getResourceUri(context, uri), defer);
519     }
520 
521     private PositionedInfo getInlineScriptInstance(String inlineScript)
522     {
523         return new InlineScriptPositionedInfo(inlineScript);
524     }
525 
526     private PositionedInfo getInlineStyleInstance(String inlineStyle)
527     {
528         return new InlineStylePositionedInfo(inlineStyle);
529     }
530 
531     protected interface PositionedInfo
532     {
533     }
534 
535     protected static class AttributeInfo implements PositionedInfo
536     {
537         private String _attributeName;
538         private String _attributeValue;
539 
540         public String getAttributeName()
541         {
542             return _attributeName;
543         }
544 
545         public void setAttributeName(String attributeName)
546         {
547             _attributeName = attributeName;
548         }
549 
550         public String getAttributeValue()
551         {
552             return _attributeValue;
553         }
554 
555         public void setAttributeValue(String attributeValue)
556         {
557             _attributeValue = attributeValue;
558         }
559     }
560 
561     protected interface WritablePositionedInfo extends PositionedInfo
562     {
563         public abstract void writePositionedInfo(HttpServletResponse response, ResponseWriter writer)
564                 throws IOException;
565     }
566 
567     private abstract class AbstractResourceUri
568     {
569         protected final String _resourceUri;
570 
571         protected AbstractResourceUri(String resourceUri)
572         {
573             _resourceUri = resourceUri;
574         }
575 
576         public int hashCode()
577         {
578             return _resourceUri.hashCode();
579         }
580 
581         public boolean equals(Object obj)
582         {
583             if (obj == null)
584             {
585                 return false;
586             }
587             if (obj == this)
588             {
589                 return true;
590             }
591             if (obj instanceof AbstractResourceUri)
592             {
593                 AbstractResourceUri other = (AbstractResourceUri) obj;
594                 return _resourceUri.equals(other._resourceUri);
595             }
596             return false;
597         }
598 
599         protected String getResourceUri()
600         {
601             return _resourceUri;
602         }
603     }
604 
605     private class StylePositionedInfo extends AbstractResourceUri implements WritablePositionedInfo
606     {
607         protected StylePositionedInfo(String resourceUri)
608         {
609             super(resourceUri);
610         }
611 
612         public void writePositionedInfo(HttpServletResponse response, ResponseWriter writer)
613                 throws IOException
614         {
615             writeStyleReference(response, writer, this.getResourceUri());
616         }
617     }
618 
619     private class ScriptPositionedInfo extends AbstractResourceUri implements
620             WritablePositionedInfo
621     {
622         protected final boolean _defer;
623         protected final boolean _encode;
624 
625         public ScriptPositionedInfo(String resourceUri, boolean defer)
626         {
627             this(resourceUri, defer, true);
628         }
629 
630         public ScriptPositionedInfo(String resourceUri, boolean defer, boolean encodeUrl)
631         {
632             super(resourceUri);
633             _defer = defer;
634             _encode = encodeUrl;
635         }
636 
637         public int hashCode()
638         {
639             return new HashCodeBuilder()
640                 .append(this.getResourceUri())
641                 .append(_defer)
642                 .append(_encode)
643                 .toHashCode();
644         }
645 
646         public boolean equals(Object obj)
647         {
648             if (super.equals(obj))
649             {
650                 if (obj instanceof ScriptPositionedInfo)
651                 {
652                     ScriptPositionedInfo other = (ScriptPositionedInfo) obj;
653                     return new EqualsBuilder()
654                         .append(_defer, other._defer)
655                         .append(_encode, other._encode)
656                         .isEquals();
657                 }
658             }
659             return false;
660         }
661 
662         public void writePositionedInfo(HttpServletResponse response, ResponseWriter writer)
663                 throws IOException
664         {
665             writeJavaScriptReference(response, writer, this.getResourceUri(),_encode,_defer);
666         }
667     }
668 
669     private abstract class InlinePositionedInfo implements WritablePositionedInfo
670     {
671         private final String _inlineValue;
672 
673         protected InlinePositionedInfo(String inlineValue)
674         {
675             _inlineValue = inlineValue;
676         }
677 
678         public String getInlineValue()
679         {
680             return _inlineValue;
681         }
682 
683         public int hashCode()
684         {
685             return new HashCodeBuilder().append(_inlineValue).toHashCode();
686         }
687 
688         public boolean equals(Object obj)
689         {
690             if (obj == null)
691             {
692                 return false;
693             }
694             if (obj == this)
695             {
696                 return true;
697             }
698             if (obj instanceof InlinePositionedInfo)
699             {
700                 InlinePositionedInfo other = (InlinePositionedInfo) obj;
701                 return new EqualsBuilder().append(_inlineValue, other._inlineValue).isEquals();
702             }
703             return false;
704         }
705     }
706 
707     private class InlineScriptPositionedInfo extends InlinePositionedInfo
708     {
709         protected InlineScriptPositionedInfo(String inlineScript)
710         {
711             super(inlineScript);
712         }
713 
714         public void writePositionedInfo(HttpServletResponse response, ResponseWriter writer)
715                 throws IOException
716         {
717             writeInlineScript(writer, getInlineValue());
718         }        
719     }
720 
721     private class InlineStylePositionedInfo extends InlinePositionedInfo
722     {
723         protected InlineStylePositionedInfo(String inlineStyle)
724         {
725             super(inlineStyle);
726         }
727 
728         public void writePositionedInfo(HttpServletResponse response, ResponseWriter writer)
729                 throws IOException
730         {
731             writeInlineStylesheet(writer, getInlineValue());
732         }
733     }
734 
735     protected static class ParseCallbackListener implements CallbackListener
736     {
737         private int headerInsertPosition = -1;
738         private int bodyInsertPosition = -1;
739         private int beforeBodyPosition = -1;
740         private int afterBodyContentInsertPosition = -1;
741         private int afterBodyEndPosition = -1;
742 
743         public ParseCallbackListener()
744         {
745         }
746 
747         public void openedStartTag(int charIndex, int tagIdentifier)
748         {
749             if (tagIdentifier == ReducedHTMLParser.BODY_TAG)
750             {
751                 beforeBodyPosition = charIndex;
752             }
753         }
754 
755         public void closedStartTag(int charIndex, int tagIdentifier)
756         {
757             if (tagIdentifier == ReducedHTMLParser.HEAD_TAG)
758             {
759                 headerInsertPosition = charIndex;
760             }
761             else if (tagIdentifier == ReducedHTMLParser.BODY_TAG)
762             {
763                 bodyInsertPosition = charIndex;
764             }
765         }
766 
767         public void openedEndTag(int charIndex, int tagIdentifier)
768         {
769             if (tagIdentifier == ReducedHTMLParser.BODY_TAG)
770             {
771                 afterBodyContentInsertPosition = charIndex;
772             }
773         }
774 
775         public void closedEndTag(int charIndex, int tagIdentifier)
776         {
777             if (tagIdentifier == ReducedHTMLParser.BODY_TAG)
778             {
779                 afterBodyEndPosition = charIndex;
780             }
781         }
782 
783         public void attribute(int charIndex, int tagIdentifier, String key, String value)
784         {
785         }
786 
787         public int getHeaderInsertPosition()
788         {
789             return headerInsertPosition;
790         }
791 
792         public int getBodyInsertPosition()
793         {
794             return bodyInsertPosition;
795         }
796 
797         public int getBeforeBodyPosition()
798         {
799             return beforeBodyPosition;
800         }
801 
802         public int getAfterBodyContentInsertPosition()
803         {
804             return afterBodyContentInsertPosition;
805         }
806 
807         public int getAfterBodyEndPosition()
808         {
809             return afterBodyEndPosition;
810         }
811     }
812 
813     public boolean requiresBuffer()
814     {
815         return true;
816     }
817 
818     public void responseStarted()
819     {
820     }
821 
822     public void responseFinished()
823     {
824     }
825 }