Pages

Showing posts with label filter. Show all posts
Showing posts with label filter. Show all posts

Friday, March 1, 2013

How to use Google's guava library to filter and transform a collection

Guava library provides a very large and powerful set of functions over collections, caching, concurrency, annotations among other things.

In this post I will show you how to filter and transform ('map' in functional terms) a collection using guava. It's easy to understand and natural if you have experience in functional programming.

I'll start with a simple way to add items to a collection:

...
import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
...
Iterables.addAll(dest, source);


This method simply adds all elements in source to dest. The signature of the addAll method is:

public static <T> boolean addAll(Collection<T> dest, Iterable<? extends T> source)

In this step I'll filter source:

Iterables.addAll(dest, Iterables.filter(source, Predicates.not(Predicates.containsPattern("\\.")));

I'm asuming that both dest and source are collections of Strings. In the previous line, I'm adding all elements in source that don't contain a ".".

Finally suppose you want to transform the source collection to add a suffix to each element before adding it to dest.

Iterables.addAll(dest, Iterables.transform(
    Iterables.filter(source, Predicates.not(Predicates.containsPattern("\\."))),
    new Function < String, String >() {
 public String apply(String input) {
     return input + ".xml";
 }
    }));

transform takes two arguments: the source Iterable and a Function that perform the transformation over each element in the source. Here I'm using String as both the type of the elements in the input collection and the elements in the returned collection, but transform can take a collection of some type and the function would return the collection of some other type, for example:

Iterables.addAll(dest, Iterables.transform(otherSource, new Function < Integer, String >() {
    public String apply(Integer input) {
 return input.toString();
    }
}));

That's it. Later I hope to post other uses's examples of this library.

Friday, December 7, 2012

How to render xhtml with images using flying saucer in a servlet container

If you need to render an xhtml file to produce a PDF, you can use flying saucer xhtmlrenderer library. You can do that in a servlet container by implementing a Filter:

public class RendererFilter implements Filter {

    private FilterConfig config;

    private DocumentBuilder documentBuilder;

    /**
     * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
     */
    public void init(FilterConfig configToSet) throws ServletException {
        try {
            this.config = configToSet;
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            documentBuilder = factory.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            throw new ServletException(e);
        }
    }

    /**
     * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse,
     *      javax.servlet.FilterChain)
     */
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain) throws IOException,
        ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        // Check to see if this filter should apply.
        String renderType = request.getParameter("RenderOutputType");
        if (renderType != null) {
            // Capture the content for this request
            ContentCaptureServletResponse capContent = new ContentCaptureServletResponse(response);
            filterChain.doFilter(request, capContent);

            try {
                // Parse the XHTML content to a document that is readable by the XHTML renderer.
                StringReader contentReader = new StringReader(capContent.getContent());
                InputSource source = new InputSource(contentReader);
                Document xhtmlContent = documentBuilder.parse(source);

                if (renderType.equals("pdf")) {
                    ITextRenderer renderer = new ITextRenderer();
                    renderer.setDocument(xhtmlContent, "");
                    renderer.layout();

                    response.setContentType("application/pdf");
                    response.setHeader("Content-Disposition", "inline; filename=print.pdf");
                    OutputStream browserStream = response.getOutputStream();
                    renderer.createPDF(browserStream);
                    return;
                }

            } catch (SAXException e) {
                throw new ServletException(e);
            } catch (DocumentException e) {
                throw new ServletException(e);
            }

        } else {
            // Normal processing
            filterChain.doFilter(request, response);
        }
    }
}

The content of the xhtml file is obtained from the response using a wrapper that captures the response writer and produces a ByteArrayOutputStream that is converted to a string in the getContent method:

public class ContentCaptureServletResponse 
                            extends HttpServletResponseWrapper {
   private ByteArrayOutputStream contentBuffer;
   private PrintWriter writer;
   public ContentCaptureServletResponse(HttpServletResponse resp) {
      super(resp); 
   }
   @Override
   public PrintWriter getWriter() throws IOException {
      if(writer == null){
         contentBuffer = new ByteArrayOutputStream();
         writer = new PrintWriter(contentBuffer);
      }
      return writer;
   }
   public String getContent(){
      writer.flush();
      String xhtmlContent = new String(contentBuffer.toByteArray());
      return xhtmlContent; 
   }
}

Source code obtained from here.


This works fine except in the case that the xhtml has images in it. In that case, the renderer library must kwow the url that the xhtml came from, in order to calculate relative image paths. To achieve that you can change the line 45 of the RendererFilter: 

   renderer.setDocument(xhtmlContent, "");

by:  

   renderer.setDocument(xhtmlContent, request.getRequestURL().toString());

Before you can use the renderer, you must configure the filter and mapping in the web.xml of the application.

Finally, to automatically generate the PDF on the fly you must call or link to the url in the following way:

../path/to/file.xhtml?RenderOutputType=pdf