com.isotrol.impe3.core.engine.AbstractHTMLRenderingEngine.java Source code

Java tutorial

Introduction

Here is the source code for com.isotrol.impe3.core.engine.AbstractHTMLRenderingEngine.java

Source

/**
 * This file is part of Port@l
 * Port@l 3.0 - Portal Engine and Management System
 * Copyright (C) 2010  Isotrol, SA.  http://www.isotrol.com
 *
 * Port@l is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Port@l is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Port@l.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.isotrol.impe3.core.engine;

import static com.isotrol.impe3.api.component.html.HTMLConstants.CLASS;
import static com.isotrol.impe3.api.component.html.HTMLConstants.CONTENT;
import static com.isotrol.impe3.api.component.html.HTMLConstants.HTTP_EQUIV;
import static com.isotrol.impe3.api.component.html.HTMLConstants.STYLE;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import java.util.UUID;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.StreamingOutput;

import org.slf4j.Logger;

import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.isotrol.impe3.api.Device;
import com.isotrol.impe3.api.component.RenderContext;
import com.isotrol.impe3.api.component.html.CSS;
import com.isotrol.impe3.api.component.html.HTML;
import com.isotrol.impe3.api.component.html.HTMLFragment;
import com.isotrol.impe3.api.component.html.HTMLRenderer;
import com.isotrol.impe3.api.component.html.Script;
import com.isotrol.impe3.api.component.html.Tag;
import com.isotrol.impe3.core.CIP;
import com.isotrol.impe3.core.CIPResult;
import com.isotrol.impe3.core.Column;
import com.isotrol.impe3.core.Frame;
import com.isotrol.impe3.core.LayoutResponse;
import com.isotrol.impe3.core.Loggers;
import com.isotrol.impe3.core.PageResult.Ok;
import com.isotrol.impe3.core.RenderingEngine;

/**
 * Abstract HTML Rendering engine. This class is NOT THREAD-SAFE.
 * @author Andres Rodriguez
 */
public abstract class AbstractHTMLRenderingEngine implements RenderingEngine<HTMLRenderer> {
    private static final String MAIN_COLUMN = "impe3_mainColumn";
    private static final String COLUMN = "impe3_column";
    private static final String FRAME = "impe3_frame";

    private static String classAtt(String base, String name) {
        if (name == null) {
            return base;
        }
        return base + ' ' + name;
    }

    final Logger logger = Loggers.core();

    AbstractHTMLRenderingEngine() {
    }

    final HTMLFragment timed(final Ok result, final String name, final UUID cipId, final HTMLFragment f) {
        if (f == null) {
            return null;
        }
        return new HTMLFragment() {
            public void writeTo(OutputStream output, Charset charset) throws IOException {
                final Stopwatch w = Stopwatch.createStarted();
                try {
                    f.writeTo(output, charset);
                } finally {
                    final long t = w.elapsed(TimeUnit.MILLISECONDS);
                    if (t > 150) {
                        logger.warn(String.format("CIP [%s]-[%s] took [%d] ms to render [%s]", cipId,
                                result.getCips().get(cipId).getCip().getDefinition().getType(), t, name));
                    }
                }
            }
        };

    }

    abstract String getDocType();

    abstract MediaType getMediaType();

    abstract void decorateHTML(Tag html);

    private Function<CIP, RenderContext> rc(final Ok result, final List<Frame> layout) {
        Map<CIP, RenderContext> map = Maps.newHashMap();
        for (CIPResult cr : result.getCips().values()) {
            map.put(cr.getCip(), RequestContexts.render(cr.getContext(), null, result.getQuery()));
        }
        return Functions.forMap(map);
    }

    public LayoutResponse getLayout(PageInstance pi, Ok result) {
        final LayoutResponse.Builder builder = LayoutResponse.builder();
        final Map<UUID, HTMLRenderer> renderers = result.getRenderers(HTMLRenderer.class,
                rc(result, pi.getLayout()));
        for (HTMLRenderer renderer : renderers.values()) {
            Iterable<CSS> csss = renderer.getCSS();
            if (csss != null) {
                for (CSS css : csss) {
                    builder.addStyle(css.getUri(), css.getMedia());
                }
            }
            csss = renderer.getIE6CSS();
            if (csss != null) {
                for (CSS css : csss) {
                    builder.addIE6Style(css.getUri(), css.getMedia());
                }
            }
            csss = renderer.getIE7CSS();
            if (csss != null) {
                for (CSS css : csss) {
                    builder.addIE7Style(css.getUri(), css.getMedia());
                }
            }
            csss = renderer.getIE8CSS();
            if (csss != null) {
                for (CSS css : csss) {
                    builder.addIE8Style(css.getUri(), css.getMedia());
                }
            }
        }
        for (UUID cipId : renderers.keySet()) {
            final HTMLRenderer renderer = renderers.get(cipId);
            final HTMLFragment f = renderer.getBody();
            final Stopwatch w = Stopwatch.createStarted();
            try {
                if (f != null) {
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    HTML.create((Device) null).safeAdd(f).writeTo(bos, Charset.forName("UTF-8"));
                    String value = new String(bos.toByteArray(), "UTF-8");
                    if (value != null) {
                        builder.putMarkup(cipId, value);
                    }
                }
            } catch (Exception e) {
                builder.putMarkup(cipId, "<p>Error: " + e.getMessage() + "</p>");
            } finally {
                final long t = w.elapsed(TimeUnit.MILLISECONDS);
                if (t > 150) {
                    logger.warn(String.format("CIP [%s]-[%s] took [%d] ms to render in layout mode", cipId,
                            result.getCips().get(cipId).getCip().getDefinition().getType(), t));
                }
            }
        }
        return builder.get();
    }

    /**
     * @see com.isotrol.impe3.core.RenderingEngine#render(com.isotrol.impe3.core.PageResult.Ok, java.util.List,
     * javax.ws.rs.core.Response.ResponseBuilder)
     */
    public Response render(final PageInstance pi, final Ok result, final ResponseBuilder builder) {
        final Charset charset = Charset.forName("UTF-8");
        final Map<UUID, HTMLRenderer> renderers = result.getRenderers(HTMLRenderer.class,
                rc(result, pi.getLayout()));
        final StreamingOutput output = new StreamingOutput() {
            public void write(OutputStream output) throws IOException, WebApplicationException {
                final HTML document = HTML.create(result.getContext().getDevice());
                document.add(getDocType());
                // HTML header
                final Tag html = document.html();
                html.set("lang", result.getContext().getLocale().getLanguage());
                decorateHTML(html);
                // Head
                final Tag head = html.head();
                // In line styles
                final Integer width_int = result.getContext().getDevice().getWidth();
                final String width_str = (width_int != null && width_int > 0) ? width_int.toString() + "px"
                        : "100%";
                StringBuilder sb = new StringBuilder("<style type=\"text/css\">\n");
                sb.append(String.format(
                        "\t.impe3_mainColumn { margin: auto; float: none; text-align:left; width: %s; }\n",
                        width_str));
                sb.append("\t.impe3_column { float: left; width: auto; }\n");
                sb.append("\t.impe3_frame { width: 100%; }\n");
                sb.append("</style>\n");
                head.add(sb.toString());
                // Stylesheets
                final List<CSS> ie6css = Lists.newLinkedList();
                final List<CSS> ie7css = Lists.newLinkedList();
                final List<CSS> ie8css = Lists.newLinkedList();
                for (HTMLRenderer renderer : renderers.values()) {
                    Iterable<CSS> csss = renderer.getCSS();
                    if (csss != null) {
                        for (CSS css : csss) {
                            head.add(css);
                        }
                    }
                    csss = renderer.getIE6CSS();
                    if (csss != null) {
                        for (CSS css : csss) {
                            ie6css.add(css);
                        }
                    }
                    csss = renderer.getIE7CSS();
                    if (csss != null) {
                        for (CSS css : csss) {
                            ie7css.add(css);
                        }
                    }
                    csss = renderer.getIE8CSS();
                    if (csss != null) {
                        for (CSS css : csss) {
                            ie8css.add(css);
                        }
                    }
                }
                // Additional CSS for IE6, 7, 8
                cssIE(head, ie6css, 6);
                cssIE(head, ie7css, 7);
                cssIE(head, ie8css, 8);
                // Scripts
                for (HTMLRenderer renderer : renderers.values()) {
                    final Iterable<Script> scripts = renderer.getHeaderScripts();
                    if (scripts != null) {
                        for (Script script : scripts) {
                            head.add(script);
                        }
                    }
                }
                // Meta
                head.meta().set(HTTP_EQUIV, "content-type").set(CONTENT, getMediaType().toString());
                // Headers
                for (Entry<UUID, HTMLRenderer> entry : renderers.entrySet()) {
                    head.add(timed(result, "HEAD", entry.getKey(), entry.getValue().getHeader()));
                }
                // Body
                final Tag body = html.body();
                final Tag main = body.div().set(CLASS, MAIN_COLUMN);
                frames(main, pi.getLayout());
                // Footer
                for (Entry<UUID, HTMLRenderer> entry : renderers.entrySet()) {
                    body.add(timed(result, "FOOTER", entry.getKey(), entry.getValue().getFooter()));
                }
                // End
                document.writeTo(output, charset);
            }

            private void cssIE(Tag tag, List<CSS> csss, int version) {
                if (csss.isEmpty()) {
                    return;
                }
                tag.add(String.format("<!--[if IE %d]>", version));
                for (CSS css : csss) {
                    tag.add(css);
                }
                tag.add("<![endif]-->");
            }

            private void frames(Tag tag, Iterable<Frame> frames) throws IOException {
                for (Frame frame : frames) {
                    frame(tag, frame);
                }
            }

            private void frame(Tag tag, Frame frame) throws IOException {
                final Tag div = tag.div();
                final String name = frame.getName();
                div.set(CLASS, classAtt(FRAME, name));
                if (frame instanceof Frame.Component) {
                    Frame.Component fc = (Frame.Component) frame;
                    final HTMLRenderer r = renderers.get(fc.getId());
                    if (r != null) {
                        div.add(timed(result, "BODY", fc.getId(), r.getBody()));
                    } else {
                        div.p();
                    }
                } else {
                    for (Column col : ((Frame.Columns) frame).getColumns()) {
                        final Tag divcol = div.div();
                        divcol.set(STYLE, String.format("width:%s%%", col.getWidth()));
                        divcol.set(CLASS, classAtt(COLUMN, col.getName()));
                        frames(divcol, col.getFrames());
                    }
                    // Closing div
                    div.div().set(STYLE, "clear: both;font-size: 0.1em;").add("<!-- Layout -->");
                }
            }
        };
        return builder.entity(output).type(getMediaType()).build();
    }
}