package coreservlets.filters;

import java.io.*;
import java.util.zip.*;
import javax.servlet.*;
import javax.servlet.http.*;

/** Filtr kompresuje dane wynikowe przy użyciu gzip
 *  (przy załozeniu, że przeglądarka obsługuje kompresję gzip).
 *  <P>
 *  Taken from Core Servlets and JavaServer Pages Volume II
 *  from Prentice Hall and Sun Microsystems Press,
 *  http://volume2.coreservlets.com/.
 *  (C) 2007 Marty Hall, Larry Brown, and Yaakov Chaikin;
 *  may be freely used or adapted.
 */
public class CompressionFilter implements Filter {
  private FilterConfig config;

  /** Jeśli przeglądarka nie obsługuje kompresji gzip,
   *  zasób wywoływany jest w standardowy sposób. Jeśli
   *  przeglądarka <I>obsługuje</I> kompresję gzip,
   *  definiowany nagłówek odpowiedzi Content-Encoding
   *  i następuje wywołanie zasobu z odpowiedzią w obiekcie
   *  przechowującym, który gromadzi wszystkie dane wynikowe
   *  i zapisuje w tablicy skompresowanych bajtów.
   *  Na końcu tablica ta zostaje zapisana
   *  w strumieniu danych wynikowych dla klienta.
   */
  public void doFilter(ServletRequest request,
                       ServletResponse response,
                       FilterChain chain)
      throws ServletException, IOException {
    HttpServletRequest req = (HttpServletRequest)request;
    HttpServletResponse res = (HttpServletResponse)response;
    if (!isGzipSupported(req)) {
      // Standardowe wywołanie zasobu.
      chain.doFilter(req,res);
    } else {
      // Informacja dla przeglądarki,
      // że wysyłane są dane skompresowane gzip.
      res.setHeader("Content-Encoding", "gzip");
      
      // Wywołanie zasobu, zebranie danych wynikowych
      // w obiekcie przechowującym.
      StringWrapper responseWrapper =
        new StringWrapper(res);
      chain.doFilter(req,responseWrapper);

      // Utworzenie obiektu kompresującego dane
      // i umieszczenie ich w tablicy bajtów.
      ByteArrayOutputStream byteStream =
        new ByteArrayOutputStream();
      GZIPOutputStream zipOut =
        new GZIPOutputStream(byteStream);
      OutputStreamWriter tempOut =
        new OutputStreamWriter(zipOut);
      
      // Kompresja oryginalnych danych wynikowych
      // i umieszczenie ich w tablicy bajtów.
      tempOut.write(responseWrapper.toString());
      
      // Strumienie gzip trzeba jawnie zamknąć.
      tempOut.close();

      // Wysłanie skompresowanych danych do klienta.
      OutputStream realOut = res.getOutputStream();
      byteStream.writeTo(realOut);
    }
  }

  /** Zapisanie obiektu FilterConfig na wypadek,
   *  gdyby potrzebowały ich klasy potomne.
   */
  public void init(FilterConfig config)
      throws ServletException {
    this.config = config;
  }

  protected FilterConfig getFilterConfig() {
    return(config);
  }
  
  public void destroy() {}

  private boolean isGzipSupported(HttpServletRequest req) {
    String browserEncodings =
      req.getHeader("Accept-Encoding");
    return((browserEncodings != null) &&
           (browserEncodings.indexOf("gzip") != -1));
  }
}
