001/*
002 * (C) Copyright 2006-2007 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Jean-Marc Orliaguet, Chalmers
018 *
019 * $Id$
020 */
021
022package org.nuxeo.theme.elements;
023
024import java.io.StringWriter;
025import java.net.URL;
026
027import org.apache.commons.logging.Log;
028import org.apache.commons.logging.LogFactory;
029import org.nuxeo.theme.Manager;
030import org.nuxeo.theme.engines.EngineType;
031import org.nuxeo.theme.formats.Format;
032import org.nuxeo.theme.formats.FormatFilter;
033import org.nuxeo.theme.formats.FormatType;
034import org.nuxeo.theme.fragments.Fragment;
035import org.nuxeo.theme.models.InfoPool;
036import org.nuxeo.theme.models.ModelException;
037import org.nuxeo.theme.nodes.Node;
038import org.nuxeo.theme.rendering.Filter;
039import org.nuxeo.theme.rendering.FilterType;
040import org.nuxeo.theme.rendering.FilterTypeFamily;
041import org.nuxeo.theme.rendering.RendererType;
042import org.nuxeo.theme.rendering.RenderingInfo;
043import org.nuxeo.theme.types.TypeFamily;
044import org.nuxeo.theme.types.TypeRegistry;
045
046public final class ElementRenderer {
047
048    private static final Log log = LogFactory.getLog(ElementRenderer.class);
049
050    private ElementRenderer() {
051        // This class is not supposed to be instantiated.
052    }
053
054    public static RenderingInfo render(final RenderingInfo info) {
055        return render(info, true);
056    }
057
058    public static RenderingInfo render(RenderingInfo info, final boolean cache) {
059        InfoPool.register(info);
060
061        final StringWriter rendered = new StringWriter();
062        final URL themeUrl = info.getThemeUrl();
063        if (themeUrl == null) {
064            log.warn("Theme URL not set for the element: " + info.getElement());
065        }
066
067        final EngineType engine = info.getEngine();
068        final Element element = info.getElement();
069
070        String markup = "";
071        if (element.isLeaf()) {
072            if (!(element instanceof Fragment)) {
073                log.error(String.format("Leaf nodes must be fragments, ignoring element: %s",
074                        element.getElementType().getTypeName()));
075                return info;
076            }
077            final Fragment fragment = (Fragment) element;
078            try {
079                info.setModel(fragment.getModel());
080            } catch (ModelException e) {
081                if (info.isDirty()) {
082                    final String fragmentName = fragment.getFragmentType().getTypeName();
083                    log.error("Rendering of fragment '" + fragmentName + "' failed.", e);
084                    return info;
085                }
086            }
087            if (fragment.isDynamic()) {
088                info.setDirty(true);
089            }
090        } else {
091            for (Node child : element.getChildrenInContext(themeUrl)) {
092                final RenderingInfo childInfo = new RenderingInfo((Element) child, themeUrl);
093                final RenderingInfo renderedChild = render(childInfo);
094                if (renderedChild == null) {
095                    continue;
096                }
097                rendered.append(renderedChild.getMarkup());
098            }
099            markup = rendered.toString();
100        }
101
102        info.setMarkup(markup);
103
104        final RendererType renderer = engine.getRenderers().get(element.getElementType().getTypeName());
105
106        if (renderer == null) {
107            return info;
108        }
109
110        final String templateEngineName = info.getTemplateEngine().getName();
111        final String engineName = info.getEngine().getName();
112        final String viewMode = info.getViewMode();
113        for (final String filterName : renderer.getFilters()) {
114
115            // Look for a filter for the current engine
116            FilterType filterType = getFilterFor(engineName, filterName, templateEngineName, viewMode);
117
118            // Fall back to no specific engine
119            if (filterType == null) {
120                filterType = getFilterFor("*", filterName, templateEngineName, viewMode);
121            }
122
123            if (filterType == null) {
124                log.warn("Filter type '" + filterName + "' not found.");
125                continue;
126            }
127
128            final Filter filter = filterType.getFilter();
129            if (filter == null) {
130                log.warn("Filter instantiation failed: " + filterName);
131                continue;
132            }
133
134            final FilterTypeFamily filterTypeFamily = filterType.getFilterTypeFamily();
135
136            if (filterTypeFamily == FilterTypeFamily.FORMAT) {
137                final FormatType formatType = ((FormatFilter) filter).getFormatType();
138                final Format format = ElementFormatter.getFormatByType(element, formatType);
139                if (format == null) {
140                    log.debug("Could not find '" + formatType.getTypeName() + "' format for: "
141                            + element.getElementType().getTypeName());
142                    continue;
143                }
144                info.setFormat(format);
145            } else if (filterTypeFamily == FilterTypeFamily.STANDALONE) {
146                // Do nothing
147            } else {
148                log.warn("Unsupported filter type: " + filterName);
149            }
150
151            info = filter.process(info, cache);
152
153            // Abort the rendering if the filter returns null
154            if (info == null) {
155                break;
156            }
157        }
158        return info;
159    }
160
161    private static FilterType getFilterFor(final String engineName, final String filterName,
162            final String templateEngineName, final String viewMode) {
163
164        TypeRegistry typeRegistry = Manager.getTypeRegistry();
165
166        // get the filter for this specified template engine and view mode
167        FilterType filterType = (FilterType) typeRegistry.lookup(TypeFamily.FILTER,
168                String.format("%s/%s/%s/%s", engineName, templateEngineName, viewMode, filterName));
169
170        // fall back to unspecified view mode
171        if (filterType == null) {
172            filterType = (FilterType) typeRegistry.lookup(TypeFamily.FILTER,
173                    String.format("%s/%s/*/%s", engineName, templateEngineName, filterName));
174        }
175
176        // fall back to unspecified template engine and view mode
177        if (filterType == null) {
178            filterType = (FilterType) typeRegistry.lookup(TypeFamily.FILTER,
179                    String.format("%s/*/*/%s", engineName, filterName));
180        }
181        return filterType;
182    }
183}