package net.sourceforge.wurfl.spring.wng; import java.io.IOException; import java.util.Map; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.sourceforge.wurfl.core.Device; import net.sourceforge.wurfl.wng.Constants; import net.sourceforge.wurfl.wng.WNGDevice; import net.sourceforge.wurfl.wng.component.ComponentException; import net.sourceforge.wurfl.wng.component.Document; import net.sourceforge.wurfl.wng.component.StyleContainer; import net.sourceforge.wurfl.wng.component.ValidatorVisitor; import net.sourceforge.wurfl.wng.renderer.DefaultDocumentRenderer; import net.sourceforge.wurfl.wng.renderer.DefaultRendererGroupResolver; import net.sourceforge.wurfl.wng.renderer.DocumentRenderer; import net.sourceforge.wurfl.wng.renderer.RenderedDocument; import net.sourceforge.wurfl.wng.style.StyleOptimizerVisitor; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.functors.InstanceofPredicate; import org.springframework.mobile.device.DeviceUtils; import org.springframework.web.servlet.View; /** * A Spring MVC {@link View} that renders a WNG {@link Document}, if one has been set in the current request by a 'target' view this class delegates to. * WNG aims to allow the developer to control the rendering of markup by device type in a declarative manner without resorting to manual if/else logic in his or her JSP templates. * When a WNG-based JSP view renders itself, the view builds a component tree that contains a {@link Document} object as its root element--no response writing is performed at that time. * After view rendering completes, this decorator finishes WNG processing by rendering the assembled Document. * That action triggers the device markup to be generated and written to the response. * @author Keith Donald */ public class WngView implements View { private final View target; private final DocumentRenderer documentRenderer; public WngView(View target) { this(target, new DefaultDocumentRenderer(new DefaultRendererGroupResolver())); } public WngView(View target, DocumentRenderer documentRenderer) { this.target = target; this.documentRenderer = documentRenderer;; } // implementing View public String getContentType() { return target.getContentType(); } public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { BufferedHttpServletResponse buffered = new BufferedHttpServletResponse(response); target.render(model, request, buffered); // logic adapted from WNGContextFilter which has the same responsibility if (isWngDocumentCreated(request)) { WNGDevice device = new WNGDevice((Device) DeviceUtils.getRequiredCurrentDevice(request)); Document document = resolveDocument(request); StyleContainer styleContainer = (StyleContainer)CollectionUtils.find(document.getHead().getChildren(), new InstanceofPredicate(StyleContainer.class)); if (styleContainer == null) { styleContainer = new StyleContainer(); document.addToHead(styleContainer); } StyleOptimizerVisitor visitor = new StyleOptimizerVisitor(device, styleContainer); document.accept(visitor); RenderedDocument renderedDocument = documentRenderer.renderDocument(document, device); writeDocument(renderedDocument, response); } else { buffered.writeTo(response.getOutputStream()); } } private boolean isWngDocumentCreated(ServletRequest request) { return request.getAttribute(Constants.ATT_DOCUMENT) != null; } private Document resolveDocument(ServletRequest request) throws ComponentException { Document document = (Document) request.getAttribute(Constants.ATT_DOCUMENT); ValidatorVisitor validatorVisitor = new ValidatorVisitor(); document.accept(validatorVisitor); return document; } private void writeDocument(RenderedDocument renderedDocument, HttpServletResponse response) throws IOException { response.setContentType(renderedDocument.getContentType()); response.getWriter().print(renderedDocument.getMarkup()); response.getWriter().flush(); } }