001/*
002 * Copyright 2004-2006 Geert Bevin <gbevin[remove] at uwyn dot com>
003 * Distributed under the terms of either:
004 * - the common development and distribution license (CDDL), v1.0; or
005 * - the GNU Lesser General Public License, v2.1 or later
006 * $Id: HighlightFilter.java 3106 2006-03-13 17:53:50Z gbevin $
007 */
008package com.uwyn.jhighlight.servlet;
009
010import javax.servlet.*;
011
012import com.uwyn.jhighlight.renderer.Renderer;
013import com.uwyn.jhighlight.renderer.XhtmlRendererFactory;
014import com.uwyn.jhighlight.tools.FileUtils;
015import java.io.ByteArrayInputStream;
016import java.io.ByteArrayOutputStream;
017import java.io.IOException;
018import java.io.InputStream;
019import java.io.OutputStream;
020import javax.servlet.http.HttpServletRequest;
021import javax.servlet.http.HttpServletRequestWrapper;
022import javax.servlet.http.HttpServletResponse;
023import javax.servlet.http.HttpServletResponseWrapper;
024
025/**
026 * A servlet filter that offers on-the-fly syntax highlighting for Java, HTML,
027 * XHTML, XML and LZX files.
028 * <p>The filter should be declared in a similar fashion as this:
029 * <pre>&lt;filter&gt;
030 *    &lt;filter-name&gt;jhighlight&lt;/filter-name&gt;
031 *    &lt;filter-class&gt;com.uwyn.jhighlight.servlet.HighlightFilter&lt;/filter-class&gt;
032 *&lt;/filter&gt;
033 *
034 *&lt;filter-mapping&gt;
035 *    &lt;filter-name&gt;jhighlight&lt;/filter-name&gt;
036 *    &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
037 *&lt;/filter-mapping&gt;</pre>
038 * <p>It will respond to files with the following extensions:
039 * <code>.javas</code>, <code>.htmls</code>, <code>.htms</code>,
040 * <code>.xhtmls</code>, <code>.xmls</code> and <code>.lzxs</code>. These will
041 * be automatically mapped to files without the last <code>s</code> in the
042 * filenames. Thus, for example, a request like this:
043 * <pre>http://myhost.com/folder/MySource.javas</pre>
044 * <p>will retrieve this file:
045 * <pre>http://myhost.com/folder/MySource.java</pre>
046 * <p>The contents of this file will be automatically highlighted and the
047 * resulting HTML will be served.
048 *
049 * @author Geert Bevin (gbevin[remove] at uwyn dot com)
050 * @version $Revision: 3106 $
051 * @since 1.0
052 */
053public final class HighlightFilter implements Filter
054{
055        public void init(FilterConfig filterConfig)
056        {
057        }
058        
059        public void destroy()
060        {
061        }
062        
063        public void doFilter(ServletRequest request,
064                                                 ServletResponse response, FilterChain chain)
065        throws IOException, ServletException
066        {
067                if (request instanceof HttpServletRequest &&
068                        response instanceof HttpServletResponse)
069                {
070                        HttpServletRequest  http_request = (HttpServletRequest)request;
071                        HttpServletResponse http_response = (HttpServletResponse)response;
072                        
073                        Renderer renderer = null;
074                        String uri = http_request.getRequestURI();
075                        String extension = FileUtils.getExtension(uri);
076                        if (extension != null &&
077                                extension.endsWith("s"))
078                        {
079                                renderer = XhtmlRendererFactory.getRenderer(extension.substring(0, extension.length()-1));
080                        }
081                        
082                        if (renderer != null)
083                        {
084                                SourceRequestWrapper    request_wrapper = new SourceRequestWrapper(http_request);
085                                CharResponseWrapper     response_wrapper = new CharResponseWrapper(http_response);
086                                
087                                chain.doFilter(request_wrapper, response_wrapper);
088                                
089                                OutputStream out = response.getOutputStream();
090                                try
091                                {
092                                        if (HttpServletResponse.SC_OK == response_wrapper.getStatus())
093                                        {
094                                                InputStream is = new ByteArrayInputStream(response_wrapper.getWrappedOutputStream().toByteArray());
095                                                ByteArrayOutputStream os = new ByteArrayOutputStream();
096                                                
097                                                String encoding = request.getCharacterEncoding();
098                                                if (null == encoding)
099                                                {
100                                                        encoding = "UTF-8";
101                                                }
102                                                
103                                                renderer.highlight(http_request.getServletPath().substring(1), is, os, encoding, false);
104                                                
105                                                String highlighted = os.toString("ISO-8859-1");
106                                                
107                                                response.setContentType("text/html");
108                                                response.setContentLength(highlighted.length());
109                                                out.write(highlighted.getBytes("ISO-8859-1"));
110                                        }
111                                        else
112                                        {
113                                                out.write(response_wrapper.getWrappedOutputStream().toByteArray());
114                                        }
115                                }
116                                finally
117                                {
118                                        out.close();
119                                }
120                        }
121                        else
122                        {
123                                chain.doFilter(request, response);
124                        }
125                }
126                else
127                {
128                        chain.doFilter(request, response);
129                }
130        }
131        
132        private static class SourceRequestWrapper extends HttpServletRequestWrapper
133        {
134                public SourceRequestWrapper(HttpServletRequest request)
135                {
136                        super(request);
137                }
138                
139                public String getServletPath()
140                {
141                        String path = super.getServletPath();
142                        return path.substring(0, path.length() - 1);
143                }
144                
145                public String getPathTranslated()
146                {
147                        String path = super.getPathTranslated();
148                        return path.substring(0, path.length() - 1);
149                }
150                
151                public String getRequestURI()
152                {
153                        String uri =  super.getRequestURI();
154                        return uri.substring(0, uri.length() - 1);
155                }
156                
157                public StringBuffer getRequestURL()
158                {
159                        StringBuffer url =  super.getRequestURL();
160                        url.setLength(url.length() - 1);
161                        return url;
162                }
163        }
164        
165        private static class CharResponseWrapper extends HttpServletResponseWrapper
166        {
167                private ServletOutputStreamWrapper mOutput;
168                private int mStatus = HttpServletResponse.SC_OK;
169                
170                public ServletOutputStreamWrapper getWrappedOutputStream()
171                {
172                        return mOutput;
173                }
174                
175                public CharResponseWrapper(HttpServletResponse response)
176                {
177                        super(response);
178                        
179                        mOutput = new ServletOutputStreamWrapper();
180                }
181                
182                public ServletOutputStream getOutputStream()
183                throws IOException
184                {
185                        return mOutput;
186                }
187                
188                public void setStatus(int status)
189                {
190                        mStatus = status;
191                        
192                        super.setStatus(status);
193                }
194                
195                public void sendError(int status, String msg)
196                throws IOException
197                {
198                        mStatus = status;
199                        
200                        super.sendError(status, msg);
201                }
202                
203                public void sendError(int status)
204                throws IOException
205                {
206                        mStatus = status;
207                        
208                        super.sendError(status);
209                }
210                
211                public int getStatus()
212                {
213                        return mStatus;
214                }
215        }
216        
217        private static class ServletOutputStreamWrapper extends ServletOutputStream
218        {
219                protected ByteArrayOutputStream mOutput;
220                
221                public ServletOutputStreamWrapper()
222                {
223                        mOutput = new ByteArrayOutputStream();
224                }
225                
226                public void write(int b) throws IOException
227                {
228                        mOutput.write(b);
229                }
230                
231                public byte[] toByteArray()
232                {
233                        return mOutput.toByteArray();
234                }
235        }
236}