/**
* Copyright (c) 2014-2017 by the respective copyright holders.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.smarthome.ui.classic.internal.render;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.smarthome.model.sitemap.Frame;
import org.eclipse.smarthome.model.sitemap.Sitemap;
import org.eclipse.smarthome.model.sitemap.Widget;
import org.eclipse.smarthome.ui.classic.internal.WebAppConfig;
import org.eclipse.smarthome.ui.classic.internal.servlet.WebAppServlet;
import org.eclipse.smarthome.ui.classic.render.RenderException;
import org.eclipse.smarthome.ui.classic.render.WidgetRenderer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is an implementation of the {@link WidgetRenderer} interface, which
* is the main entry point for HTML code construction.
*
* It provides the HTML header and skeleton and delegates the rendering of
* widgets on the page to the dedicated widget renderers.
*
* @author Kai Kreuzer - Initial contribution and API
*
*/
public class PageRenderer extends AbstractWidgetRenderer {
private final Logger logger = LoggerFactory.getLogger(PageRenderer.class);
List<WidgetRenderer> widgetRenderers = new ArrayList<WidgetRenderer>();
public void addWidgetRenderer(WidgetRenderer widgetRenderer) {
widgetRenderer.setConfig(config);
widgetRenderers.add(widgetRenderer);
}
public void removeWidgetRenderer(WidgetRenderer widgetRenderer) {
widgetRenderers.remove(widgetRenderer);
}
/**
* This is the main method, which is called to produce the HTML code for a servlet request.
*
* @param id the id of the parent widget whose children are about to appear on this page
* @param sitemap the sitemap to use
* @param label the title of this page
* @param children a list of widgets that should appear on this page
* @param async true, if this is an asynchronous request. This will use a different HTML skeleton
* @return a string builder with the produced HTML code
* @throws RenderException if an error occurs during the processing
*/
public StringBuilder processPage(String id, String sitemap, String label, EList<Widget> children, boolean async)
throws RenderException {
String snippet = getSnippet(async ? "layer" : "main");
snippet = snippet.replaceAll("%id%", id);
// if the label contains a value span, we remove this span as
// the title of a page/layer cannot deal with this
// Note: we can have a span here, if the parent widget had a label
// with some value defined (e.g. "Windows [%d]"), which getLabel()
// will convert into a "Windows <span>5</span>".
if (label.contains("[") && label.endsWith("]")) {
label = label.replace("[", "").replace("]", "");
}
snippet = StringUtils.replace(snippet, "%label%", label);
snippet = StringUtils.replace(snippet, "%servletname%", WebAppServlet.SERVLET_NAME);
snippet = StringUtils.replace(snippet, "%sitemap%", sitemap);
String[] parts = snippet.split("%children%");
StringBuilder pre_children = new StringBuilder(parts[0]);
StringBuilder post_children = new StringBuilder(parts[1]);
if (parts.length == 2) {
processChildren(pre_children, post_children, children);
} else if (parts.length > 2) {
logger.error("Snippet '{}' contains multiple %children% sections, but only one is allowed!",
async ? "layer" : "main");
}
return pre_children.append(post_children);
}
private void processChildren(StringBuilder sb_pre, StringBuilder sb_post, EList<Widget> children)
throws RenderException {
// put a single frame around all children widgets, if there are no explicit frames
if (!children.isEmpty()) {
EObject firstChild = children.get(0);
EObject parent = firstChild.eContainer();
if (!(firstChild instanceof Frame || parent instanceof Frame || parent instanceof Sitemap
|| parent instanceof List)) {
String frameSnippet = getSnippet("frame");
frameSnippet = StringUtils.replace(frameSnippet, "%label%", "");
String[] parts = frameSnippet.split("%children%");
if (parts.length > 1) {
sb_pre.append(parts[0]);
}
if (parts.length > 2) {
sb_post.insert(0, parts[1]);
}
if (parts.length > 2) {
logger.error("Snippet 'frame' contains multiple %children% sections, but only one is allowed!");
}
}
}
for (Widget w : children) {
StringBuilder new_pre = new StringBuilder();
StringBuilder new_post = new StringBuilder();
StringBuilder widgetSB = new StringBuilder();
EList<Widget> nextChildren = renderWidget(w, widgetSB);
if (nextChildren != null) {
String[] parts = widgetSB.toString().split("%children%");
// no %children% placeholder found or at the end
if (parts.length == 1) {
new_pre.append(widgetSB);
}
// %children% section found
if (parts.length > 1) {
new_pre.append(parts[0]);
new_post.insert(0, parts[1]);
}
// multiple %children% sections found -> log an error and ignore all code starting from the second
// occurance
if (parts.length > 2) {
String widgetType = w.eClass().getInstanceTypeName()
.substring(w.eClass().getInstanceTypeName().lastIndexOf(".") + 1);
logger.error(
"Snippet for widget '{}' contains multiple %children% sections, but only one is allowed!",
widgetType);
}
processChildren(new_pre, new_post, nextChildren);
sb_pre.append(new_pre);
sb_pre.append(new_post);
} else {
sb_pre.append(widgetSB);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public EList<Widget> renderWidget(Widget w, StringBuilder sb) throws RenderException {
// Check if this widget is visible
if (itemUIRegistry.getVisiblity(w) == false) {
return null;
}
for (WidgetRenderer renderer : widgetRenderers) {
if (renderer.canRender(w)) {
return renderer.renderWidget(w, sb);
}
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public boolean canRender(Widget w) {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public void setConfig(WebAppConfig config) {
this.config = config;
for (WidgetRenderer renderer : widgetRenderers) {
renderer.setConfig(config);
}
}
}