You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by mc...@apache.org on 2009/06/03 18:05:49 UTC

svn commit: r781452 - in /myfaces/core/branches/2_0_0: api/src/main/java/javax/faces/view/ impl/src/main/java/org/apache/myfaces/view/ impl/src/main/java/org/apache/myfaces/view/jsp/

Author: mconcini
Date: Wed Jun  3 16:05:49 2009
New Revision: 781452

URL: http://svn.apache.org/viewvc?rev=781452&view=rev
Log:
MYFACES-2231 - JSPViewDeclarationLanguage initial impl check-in.  

Modified:
    myfaces/core/branches/2_0_0/api/src/main/java/javax/faces/view/ViewDeclarationLanguage.java
    myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/view/ViewDeclarationLanguageBase.java
    myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/view/jsp/JspViewDeclarationLanguage.java

Modified: myfaces/core/branches/2_0_0/api/src/main/java/javax/faces/view/ViewDeclarationLanguage.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/api/src/main/java/javax/faces/view/ViewDeclarationLanguage.java?rev=781452&r1=781451&r2=781452&view=diff
==============================================================================
--- myfaces/core/branches/2_0_0/api/src/main/java/javax/faces/view/ViewDeclarationLanguage.java (original)
+++ myfaces/core/branches/2_0_0/api/src/main/java/javax/faces/view/ViewDeclarationLanguage.java Wed Jun  3 16:05:49 2009
@@ -53,12 +53,12 @@
     
     public void retargetAttachedObjects(FacesContext context, UIComponent topLevelComponent, List<AttachedObjectHandler> handlers)
     {
-        //TODO: implement impl
+        throw new UnsupportedOperationException(); 
     }
 
     public void retargetMethodExpressions(FacesContext context, UIComponent topLevelComponent)
     {
-        //TODO: implement impl
+        throw new UnsupportedOperationException(); 
     }
               
 }

Modified: myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/view/ViewDeclarationLanguageBase.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/view/ViewDeclarationLanguageBase.java?rev=781452&r1=781451&r2=781452&view=diff
==============================================================================
--- myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/view/ViewDeclarationLanguageBase.java (original)
+++ myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/view/ViewDeclarationLanguageBase.java Wed Jun  3 16:05:49 2009
@@ -62,7 +62,6 @@
 public abstract class ViewDeclarationLanguageBase extends ViewDeclarationLanguage
 {
     
-    private static final Log log = LogFactory.getLog(ViewDeclarationLanguageBase.class);
     /**
      * Process the specification required algorithm that is generic to all PDL.
      * 
@@ -71,15 +70,8 @@
      */
     public UIViewRoot createView(FacesContext context, String viewId)
     {
-        if (context == null)
-        {
-            throw new NullPointerException("context");
-        }
-
-        if (viewId == null)
-        {
-            throw new NullPointerException("viewId");
-        }
+        checkNull(context, "context");
+        checkNull(viewId, "viewId");
 
         try
         {
@@ -125,181 +117,8 @@
     protected abstract String calculateViewId(FacesContext context, String viewId);
     
     protected abstract void sendSourceNotFound(FacesContext context, String message);
-    
-    @Override
-    public void retargetMethodExpressions(FacesContext context, UIComponent topLevelComponent)
-    {
-        checkNull(context, "context");
-        checkNull(topLevelComponent, "topLevelComponent");
         
-        BeanInfo beanInfo = (BeanInfo)topLevelComponent.getAttributes().get(UIComponent.BEANINFO_KEY);
-        if(beanInfo == null)
-        {
-            return;  // should we log an error here?  spec doesn't say one way or the other so leaving it alone for now. 
-        }
-        
-        PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
-        for(PropertyDescriptor curDescriptor : descriptors)
-        {
-            ExpressionFactory expressionFactory = null;
-            ValueExpression valueExpression = null;
-            MethodExpression attributeMethodExpression = null;
-            Class expectedReturn = null;
-            Class expectedParams[] = null;
-            
-            if(curDescriptor.getValue("type") != null || curDescriptor.getValue("method-signature") == null)
-            {
-                continue;   
-            }
-            
-            String targets = null;
-            valueExpression = (ValueExpression)curDescriptor.getValue("targets");
-            if(valueExpression != null)
-            {
-                targets = (String)valueExpression.getValue(context.getELContext());
-            }
-
-            if(targets == null)
-            {
-                targets = curDescriptor.getName();
-            }
-            
-            if(targets == null)
-            {
-                continue;   //not explicitly part of the algorithm, but could lead to an NPE if we don't check this
-            }
-            
-            String[] targetArray = targets.split(" ");
-            for (String curTarget : targetArray)
-            {
-                UIComponent target = topLevelComponent.findComponent(curTarget);
-                if(target == null)
-                {
-                    log.error("target not found");
-                    continue;
-                }
-                
-                String name = curDescriptor.getName();
-                
-                ValueExpression attributeValueExpression = (ValueExpression)topLevelComponent.getAttributes().get(name);
-                
-                if(attributeValueExpression == null)
-                {
-                    log.error("attributeValueExpression not found");
-                    continue;
-                }
-
-                if(expressionFactory == null)
-                {   //initialize expression factory if hasn't been used yet
-                    expressionFactory = context.getApplication().getExpressionFactory();
-                }
-                
-                boolean isAction = name.equals("action"),
-                        isActionListener = name.equals("actionListener"),
-                        isValidator = name.equals("validator"),
-                        isValueChangeListener = name.equals("valueChangeListener");
-                
-                String expressionString = attributeValueExpression.getExpressionString();
-                    
-                if(isAction)
-                    expectedReturn = Object.class;
-                else
-                    expectedReturn = Void.class;
-                
-                if(isAction)
-                {
-                    expectedParams =  new Class[]{};
-                    attributeMethodExpression = expressionFactory.createMethodExpression(context.getELContext(), expressionString, expectedReturn, expectedParams);
-                    ((ActionSource2) target).setActionExpression(attributeMethodExpression);
-                }
-                else if(isActionListener)
-                {
-                    expectedParams = new Class[]{ActionEvent.class};
-                    attributeMethodExpression = expressionFactory.createMethodExpression(context.getELContext(), expressionString, expectedReturn, expectedParams);
-                    ((ActionSource2) target).addActionListener(new MethodExpressionActionListener(attributeMethodExpression));
-                }
-                else if(isValidator)
-                {
-                    expectedParams = new Class[]{FacesContext.class,UIComponent.class,Object.class};
-                    attributeMethodExpression = expressionFactory.createMethodExpression(context.getELContext(), expressionString, expectedReturn, expectedParams);
-                    ((EditableValueHolder) target).addValidator(new MethodExpressionValidator(attributeMethodExpression));
-                }
-                else if(isValueChangeListener)
-                {
-                    expectedParams = new Class[]{ValueChangeEvent.class};
-                    attributeMethodExpression = expressionFactory.createMethodExpression(context.getELContext(), expressionString, expectedReturn, expectedParams);
-                    ((EditableValueHolder) target).addValueChangeListener(new MethodExpressionValueChangeListener(attributeMethodExpression));
-                }
-                else
-                {
-                    //TODO: implement here - derive attribute name from method-signature
-                    //If name is not equal to any of the previously listed strings, call getExpressionString() on the attributeValueExpression and use that string to create a MethodExpression 
-                    //where the signature is created based on the value of the "method-signature" attribute of the <composite:attribute /> tag.
-                    
-                    //Otherwise, assume that the MethodExpression  should be placed in the components attribute set. The runtme must create the MethodExpression instance based on the value of the "method-signature" attribute.
-                }
-                
-                
-            }
-        }
-    }
-    
-    @Override
-    public void retargetAttachedObjects(FacesContext context,
-            UIComponent topLevelComponent, List<AttachedObjectHandler> handlers)
-    {
-        checkNull(context, "context");
-        checkNull(topLevelComponent, "topLevelComponent");
-        checkNull(handlers, "handlers");
-        
-        BeanInfo beanInfo = (BeanInfo)topLevelComponent.getAttributes().get(UIComponent.BEANINFO_KEY);
-        
-        if(beanInfo == null)
-        {
-            log.error("BeanInfo not found");
-            return;
-        }
-        
-        BeanDescriptor beanDescriptor = beanInfo.getBeanDescriptor();
-        List<AttachedObjectTarget> targetList = (List<AttachedObjectTarget>)beanDescriptor.getValue(AttachedObjectTarget.ATTACHED_OBJECT_TARGETS_KEY);
-
-        for (AttachedObjectHandler curHandler : handlers)
-        {
-            String forAttributeValue = curHandler.getFor();
-
-            for(AttachedObjectTarget curTarget : targetList)
-            {
-                List<UIComponent> targetUIComponents = curTarget.getTargets(topLevelComponent);
-                String curTargetName = curTarget.getName();
-
-                if( (curHandler instanceof ActionSource2AttachedObjectHandler) && curHandler instanceof ActionSource2AttachedObjectTarget)
-                {
-                    applyAttachedObjects(context,targetUIComponents, curHandler);
-                    break;
-                }
-                if( (curHandler instanceof EditableValueHolderAttachedObjectHandler) && curHandler instanceof EditableValueHolderAttachedObjectTarget)
-                {
-                    applyAttachedObjects(context,targetUIComponents, curHandler);
-                    break;
-                }
-                if( (curHandler instanceof ValueHolderAttachedObjectHandler) && curHandler instanceof ValueHolderAttachedObjectTarget)
-                {
-                    applyAttachedObjects(context,targetUIComponents, curHandler);
-                    break;
-                }
-            }
-        }
-    }
-    
-    private void applyAttachedObjects(FacesContext context, List<UIComponent> targetUIComponents, AttachedObjectHandler curHandler)
-    {
-        for (UIComponent target : targetUIComponents)
-        {
-            curHandler.applyAttachedObject(context, target);
-        }
-    }
-    
-    private void checkNull(final Object o, final String param)
+    protected void checkNull(final Object o, final String param)
     {
         if (o == null)
         {

Modified: myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/view/jsp/JspViewDeclarationLanguage.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/view/jsp/JspViewDeclarationLanguage.java?rev=781452&r1=781451&r2=781452&view=diff
==============================================================================
--- myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/view/jsp/JspViewDeclarationLanguage.java (original)
+++ myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/view/jsp/JspViewDeclarationLanguage.java Wed Jun  3 16:05:49 2009
@@ -20,16 +20,39 @@
 
 import java.beans.BeanInfo;
 import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Locale;
 
 import javax.faces.FacesException;
+import javax.faces.FactoryFinder;
+import javax.faces.application.Application;
 import javax.faces.application.Resource;
+import javax.faces.application.StateManager;
+import javax.faces.application.ViewHandler;
 import javax.faces.component.UIViewRoot;
+import javax.faces.context.ExternalContext;
 import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+import javax.faces.render.RenderKit;
+import javax.faces.render.RenderKitFactory;
 import javax.faces.view.StateManagementStrategy;
 import javax.faces.view.ViewMetadata;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.servlet.jsp.jstl.core.Config;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.myfaces.application.DefaultViewHandlerSupport;
 import org.apache.myfaces.application.ViewHandlerSupport;
+import org.apache.myfaces.application.jsp.JspViewHandlerImpl;
+import org.apache.myfaces.application.jsp.ViewResponseWrapper;
+import org.apache.myfaces.shared_impl.config.MyfacesConfig;
+import org.apache.myfaces.shared_impl.renderkit.html.util.JavascriptUtils;
 import org.apache.myfaces.view.ViewDeclarationLanguageBase;
 
 /**
@@ -40,6 +63,12 @@
  */
 public class JspViewDeclarationLanguage extends ViewDeclarationLanguageBase
 {
+    private static final Log log = LogFactory.getLog(JspViewDeclarationLanguage.class);
+    public static final String FORM_STATE_MARKER = "<!-...@-->";
+    public static final int FORM_STATE_MARKER_LEN = FORM_STATE_MARKER.length();
+
+    private static final String AFTER_VIEW_TAG_CONTENT_PARAM = JspViewDeclarationLanguage.class + ".AFTER_VIEW_TAG_CONTENT";
+    
     private ViewHandlerSupport _cachedViewHandlerSupport;
     
     /**
@@ -47,8 +76,9 @@
      */
     public JspViewDeclarationLanguage()
     {
-        // TODO: IMPLEMENT HERE
-    }
+        if (log.isTraceEnabled())
+            log.trace("New JspViewDeclarationLanguage instance created");    
+        }
 
     /**
      * {@inheritDoc}
@@ -56,7 +86,30 @@
     @Override
     public void buildView(FacesContext context, UIViewRoot view) throws IOException
     {
-        // TODO: IMPLEMENT HERE
+        ExternalContext externalContext = context.getExternalContext();
+        ServletResponse response = (ServletResponse)externalContext.getResponse(); 
+        String viewId = view.getViewId();
+        ViewResponseWrapper wrappedResponse = new ViewResponseWrapper((HttpServletResponse) response);
+
+        externalContext.setResponse(wrappedResponse);
+        try
+        {
+            externalContext.dispatch(viewId);
+        }
+        finally
+        {
+            externalContext.setResponse(response);
+        }
+
+        boolean errorResponse = wrappedResponse.getStatus() < 200 || wrappedResponse.getStatus() > 299;
+        if (errorResponse)
+        {
+            wrappedResponse.flushToWrappedResponse();
+            return;
+        }
+
+        // store the wrapped response in the request, so it is thread-safe
+        externalContext.getRequestMap().put(AFTER_VIEW_TAG_CONTENT_PARAM, wrappedResponse);
     }
 
     /**
@@ -65,8 +118,7 @@
     @Override
     public BeanInfo getComponentMetadata(FacesContext context, Resource componentResource)
     {
-        // TODO: IMPLEMENT HERE
-        return null;
+        throw new UnsupportedOperationException();
     }
 
     /**
@@ -75,8 +127,7 @@
     @Override
     public Resource getScriptComponentResource(FacesContext context, Resource componentResource)
     {
-        // TODO: IMPLEMENT HERE
-        return null;
+        throw new UnsupportedOperationException();
     }
 
     /**
@@ -88,15 +139,8 @@
         // Not necessary given that this method always returns null, but staying true to
         // the spec.
         
-        if (context == null)
-        {
-            throw new NullPointerException ("context must not be null");
-        }
-        
-        if (viewId == null)
-        {
-            throw new NullPointerException ("viewId must not be null");
-        }
+        checkNull(context, "context");
+        checkNull(viewId, "viewId");
         
         // JSP impl must return null.
         
@@ -109,8 +153,102 @@
     @Override
     public void renderView(FacesContext context, UIViewRoot view) throws IOException
     {
-        // TODO: IMPLEMENT HERE
-    }
+        checkNull(context, "context");
+        checkNull(view, "view");
+
+        // do not render the view if the rendered attribute for the view is false
+        if (!view.isRendered())
+        {
+            if (log.isTraceEnabled())
+                log.trace("View is not rendered");
+            return;
+        }
+
+        ExternalContext externalContext = context.getExternalContext();
+
+        String viewId = context.getViewRoot().getViewId();
+
+        if (log.isTraceEnabled())
+            log.trace("Rendering JSP view: " + viewId);
+
+        ServletResponse response = (ServletResponse) externalContext.getResponse();
+        ServletRequest request = (ServletRequest) externalContext.getRequest();
+
+        Locale locale = view.getLocale();
+        response.setLocale(locale);
+        Config.set(request, Config.FMT_LOCALE, context.getViewRoot().getLocale());
+
+        buildView(context, view);
+
+        // handle character encoding as of section 2.5.2.2 of JSF 1.1
+        if (externalContext.getRequest() instanceof HttpServletRequest)
+        {
+            HttpServletRequest httpServletRequest = (HttpServletRequest) externalContext.getRequest();
+            HttpSession session = httpServletRequest.getSession(false);
+
+            if (session != null)
+            {
+                session.setAttribute(ViewHandler.CHARACTER_ENCODING_KEY, response.getCharacterEncoding());
+            }
+        }
+
+        // render the view in this method (since JSF 1.2)
+        RenderKitFactory renderFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
+        RenderKit renderKit = renderFactory.getRenderKit(context, view.getRenderKitId());
+
+        ResponseWriter responseWriter = context.getResponseWriter();
+        if (responseWriter == null)
+        {
+            responseWriter = renderKit.createResponseWriter(response.getWriter(), null,
+                    ((HttpServletRequest) externalContext.getRequest()).getCharacterEncoding());
+            context.setResponseWriter(responseWriter);
+        }
+
+        ResponseWriter oldResponseWriter = responseWriter;
+        StateMarkerAwareWriter stateAwareWriter = null;
+
+        StateManager stateManager = context.getApplication().getStateManager();
+        if (stateManager.isSavingStateInClient(context))
+        {
+            stateAwareWriter = new StateMarkerAwareWriter();
+
+            // Create a new response-writer using as an underlying writer the stateAwareWriter
+            // Effectively, all output will be buffered in the stateAwareWriter so that later
+            // this writer can replace the state-markers with the actual state.
+            responseWriter = oldResponseWriter.cloneWithWriter(stateAwareWriter);
+            context.setResponseWriter(responseWriter);
+        }
+
+        actuallyRenderView(context, view);
+
+        context.setResponseWriter(oldResponseWriter);
+
+        //We're done with the document - now we can write all content
+        //to the response, properly replacing the state-markers on the way out
+        //by using the stateAwareWriter
+        if (stateManager.isSavingStateInClient(context))
+        {
+            stateAwareWriter.flushToWriter(response.getWriter());
+        }
+        else
+        {
+            stateManager.saveView(context);
+        }
+
+        // Final step - we output any content in the wrappedResponse response from above to the response,
+        // removing the wrappedResponse response from the request, we don't need it anymore
+        ViewResponseWrapper afterViewTagResponse = (ViewResponseWrapper) externalContext.getRequestMap().get(
+                AFTER_VIEW_TAG_CONTENT_PARAM);
+        externalContext.getRequestMap().remove(AFTER_VIEW_TAG_CONTENT_PARAM);
+
+        if (afterViewTagResponse != null)
+        {
+            afterViewTagResponse.flushToWriter(response.getWriter(),
+                    context.getExternalContext().getResponseCharacterEncoding());
+        }
+
+        response.flushBuffer();
+        }
 
     /**
      * {@inheritDoc}
@@ -118,8 +256,12 @@
     @Override
     public UIViewRoot restoreView(FacesContext context, String viewId)
     {
-        // TODO: IMPLEMENT HERE
-        return null;
+        Application application = context.getApplication();
+        ViewHandler applicationViewHandler = application.getViewHandler();
+        String renderKitId = applicationViewHandler.calculateRenderKitId(context);
+        String calculatedViewId = calculateViewId(context, viewId);
+        UIViewRoot viewRoot = application.getStateManager().restoreView(context, calculatedViewId, renderKitId);
+        return viewRoot;
     }
 
     @Override
@@ -127,7 +269,7 @@
     {
         if (_cachedViewHandlerSupport == null)
         {
-            _cachedViewHandlerSupport = null; // TODO: IMPLEMENT HERE
+            _cachedViewHandlerSupport = new DefaultViewHandlerSupport();
         }
         
         return _cachedViewHandlerSupport.calculateViewId(context, viewId);
@@ -153,7 +295,138 @@
     public StateManagementStrategy getStateManagementStrategy(
             FacesContext context, String viewId)
     {
-        // TODO implement here
         return null;
     }
+    
+    /**
+     * Render the view now - properly setting and resetting the response writer
+     */
+    private void actuallyRenderView(FacesContext facesContext,
+                                    UIViewRoot viewToRender) throws IOException {
+        // Set the new ResponseWriter into the FacesContext, saving the old one aside.
+        ResponseWriter responseWriter = facesContext.getResponseWriter();
+
+        //Now we actually render the document
+        // Call startDocument() on the ResponseWriter.
+        responseWriter.startDocument();
+
+        // Call encodeAll() on the UIViewRoot
+        viewToRender.encodeAll(facesContext);
+
+        // Call endDocument() on the ResponseWriter
+        responseWriter.endDocument();
+
+        responseWriter.flush();
+    }
+    
+    /**
+     * Writes the response and replaces the state marker tags with the state information for the current context
+     */
+    private static class StateMarkerAwareWriter extends Writer
+    {
+        private StringBuilder buf;
+
+        public StateMarkerAwareWriter()
+        {
+            this.buf = new StringBuilder();
+        }
+
+        @Override
+        public void close() throws IOException
+        {
+        }
+
+        @Override
+        public void flush() throws IOException
+        {
+        }
+
+        @Override
+        public void write(char[] cbuf, int off, int len) throws IOException
+        {
+            if ((off < 0) || (off > cbuf.length) || (len < 0) ||
+                    ((off + len) > cbuf.length) || ((off + len) < 0)) {
+                throw new IndexOutOfBoundsException();
+            } else if (len == 0) {
+                return;
+            }
+            buf.append(cbuf, off, len);
+        }
+
+        public StringBuilder getStringBuilder()
+        {
+            return buf;
+        }
+
+        public void flushToWriter(Writer writer) throws IOException
+        {
+            FacesContext facesContext = FacesContext.getCurrentInstance();
+            StateManager stateManager = facesContext.getApplication().getStateManager();
+
+            StringWriter stateWriter = new StringWriter();
+            ResponseWriter realWriter = facesContext.getResponseWriter();
+            facesContext.setResponseWriter(realWriter.cloneWithWriter(stateWriter));
+
+            Object serializedView = stateManager.saveView(facesContext);
+
+            stateManager.writeState(facesContext, serializedView);
+            facesContext.setResponseWriter(realWriter);
+
+            StringBuilder contentBuffer = getStringBuilder();
+            String state = stateWriter.getBuffer().toString();
+
+            ExternalContext extContext = facesContext.getExternalContext();
+            if (JavascriptUtils.isJavascriptAllowed(extContext) && MyfacesConfig.getCurrentInstance(extContext).isViewStateJavascript()) {
+                // If javascript viewstate is enabled no state markers were written
+                write(contentBuffer, 0, contentBuffer.length(), writer);
+                writer.write(state);
+            } else {
+                // If javascript viewstate is disabled state markers must be replaced
+                int lastFormMarkerPos = 0;
+                int formMarkerPos = 0;
+                // Find all state markers and write out actual state instead
+                while ((formMarkerPos = contentBuffer.indexOf(FORM_STATE_MARKER, formMarkerPos)) > -1)
+                {
+                    // Write content before state marker
+                    write(contentBuffer, lastFormMarkerPos, formMarkerPos, writer);
+                    // Write state and move position in buffer after marker
+                    writer.write(state);
+                    formMarkerPos += FORM_STATE_MARKER_LEN;
+                    lastFormMarkerPos = formMarkerPos;
+                }
+                // Write content after last state marker
+                if (lastFormMarkerPos < contentBuffer.length()) {
+                    write(contentBuffer, lastFormMarkerPos, contentBuffer.length(), writer);
+                }
+            }
+
+        }
+
+        /**
+         * Writes the content of the specified StringBuffer from index
+         * <code>beginIndex</code> to index <code>endIndex - 1</code>.
+         *
+         * @param contentBuffer  the <code>StringBuffer</code> to copy content from
+         * @param beginIndex  the beginning index, inclusive.
+         * @param endIndex  the ending index, exclusive
+         * @param writer  the <code>Writer</code> to write to
+         * @throws IOException  if an error occurs writing to specified <code>Writer</code>
+         */
+        private void write(StringBuilder contentBuffer, int beginIndex, int endIndex, Writer writer) throws IOException {
+            int index = beginIndex;
+            int bufferSize = 2048;
+            char[] bufToWrite = new char[bufferSize];
+
+            while (index < endIndex)
+            {
+                int maxSize = Math.min(bufferSize, endIndex - index);
+
+                contentBuffer.getChars(index, index + maxSize, bufToWrite, 0);
+                writer.write(bufToWrite, 0, maxSize);
+
+                index += bufferSize;
+            }
+        }
+    }
+
 }