/** * 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.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.HashMap; import java.util.Map; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.eclipse.smarthome.core.items.Item; import org.eclipse.smarthome.core.items.ItemNotFoundException; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.library.types.PercentType; import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.model.sitemap.Widget; import org.eclipse.smarthome.ui.classic.internal.WebAppActivator; import org.eclipse.smarthome.ui.classic.internal.WebAppConfig; import org.eclipse.smarthome.ui.classic.render.RenderException; import org.eclipse.smarthome.ui.classic.render.WidgetRenderer; import org.eclipse.smarthome.ui.items.ItemUIRegistry; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This is an abstract implementation of a widget renderer. It provides * methods that are very useful for any widget renderer implementation, * so it should be subclassed by most concrete implementations. * * @author Kai Kreuzer - Initial contribution and API * */ abstract public class AbstractWidgetRenderer implements WidgetRenderer { private final Logger logger = LoggerFactory.getLogger(AbstractWidgetRenderer.class); protected WebAppConfig config; protected ItemUIRegistry itemUIRegistry; /* the file extension of the snippets */ protected static final String SNIPPET_EXT = ".html"; /* the snippet location inside this bundle */ protected static final String SNIPPET_LOCATION = "snippets/"; /* a local cache so we do not have to read the snippets over and over again from the bundle */ protected static final Map<String, String> snippetCache = new HashMap<String, String>(); public void setItemUIRegistry(ItemUIRegistry itemUIRegistry) { this.itemUIRegistry = itemUIRegistry; } public void unsetItemUIRegistry(ItemUIRegistry itemUIRegistry) { this.itemUIRegistry = null; } public ItemUIRegistry getItemUIRegistry() { return itemUIRegistry; } protected void activate(ComponentContext context) { } protected void deactivate(ComponentContext context) { } /** * This method provides the html snippet for a given elementType of the sitemap model. * * @param elementType the name of the model type (e.g. "Group" or "Switch") * @return the html snippet to be used in the UI (including placeholders for variables) * @throws RenderException if snippet could not be read */ protected synchronized String getSnippet(String elementType) throws RenderException { elementType = elementType.toLowerCase(); String snippet = snippetCache.get(elementType); if (snippet == null) { String snippetLocation = SNIPPET_LOCATION + elementType + SNIPPET_EXT; URL entry = WebAppActivator.getContext().getBundle().getEntry(snippetLocation); if (entry != null) { try { snippet = IOUtils.toString(entry.openStream()); if (!config.isHtmlCacheDisabled()) { snippetCache.put(elementType, snippet); } } catch (IOException e) { logger.warn("Cannot load snippet for element type '{}'", elementType, e); } } else { throw new RenderException("Cannot find a snippet for element type '" + elementType + "'"); } } return snippet; } /** * Retrieves the label for a widget and formats it for the WebApp.Net framework * * @param w the widget to retrieve the label for * @return the label to use for the widget */ public String getLabel(Widget w) { return getLabel(w, null); } /** * Retrieves the label for a widget and formats it for the WebApp.Net framework * * @param w the widget to retrieve the label for * @param preferredValue the value to consider in place of the value between [ and ] if not null * @return the label to use for the widget */ public String getLabel(Widget w, String preferredValue) { String label = itemUIRegistry.getLabel(w); int index = label.indexOf('['); int index2 = label.lastIndexOf(']'); if (index != -1 && index2 != -1) { label = formatLabel(label.substring(0, index).trim(), (preferredValue == null) ? label.substring(index + 1, index2) : preferredValue); } else { label = formatLabel(label, null); } return label; } /** * Formats the widget label for the WebApp.Net framework * * @param left the left part of the label * @param right the right part of the label; null if no right part to consider * @return the label to use for the widget */ private String formatLabel(String left, String right) { String label = "<span style=\"%labelstyle%\" class=\"iLabel\">" + left + "</span>"; if (right != null) { label += "<span class=\"iValue\" style=\"%valuestyle%\">" + right + "</span>"; } return label; } /** * Escapes the path part of a URL as defined in RFC2396. This means, that for example the * path "/hello world" gets escaped to "/hello%20world". * * @param path The path of the URL that has to be escaped * @return The escaped path */ protected String escapeURLPath(String path) { try { return new URI(null, null, path, null).toString(); } catch (URISyntaxException use) { logger.warn("Cannot escape path '{}' in URL. Returning unmodified path.", path); return path; } } /** * Process the color tags - labelcolor and valuecolor * * @param w * The widget to process * @param snippet * The snippet to translate * @return The updated snippet */ protected String processColor(Widget w, String snippet) { String style = ""; String color = itemUIRegistry.getLabelColor(w); if (color != null) { style = "color:" + color; } snippet = StringUtils.replace(snippet, "%labelstyle%", style); style = ""; color = itemUIRegistry.getValueColor(w); if (color != null) { style = "color:" + color; } snippet = StringUtils.replace(snippet, "%valuestyle%", style); return snippet; } protected String getFormat() { return config.getIconType(); } protected String getState(Widget w) { State state = itemUIRegistry.getState(w); if (state != null) { return escapeURLPath(state.toString()); } else { return "NULL"; } } protected String getStateAsNumber(Widget w) { String itemName = w.getItem(); if (itemName != null) { try { Item item = itemUIRegistry.getItem(itemName); State state = item.getState(); if (item.getAcceptedDataTypes().contains(PercentType.class)) { state = item.getStateAs(PercentType.class); } else { state = item.getStateAs(DecimalType.class); } if (state != null) { return escapeURLPath(state.toString()); } else { logger.debug("State '{}' of item '{}' is not a number!", item.getState(), itemName); } } catch (ItemNotFoundException e) { logger.error("Cannot retrieve item '{}' for widget {}", new Object[] { itemName, w.eClass().getInstanceTypeName() }); } } return "NULL"; } protected String getCategory(Widget w) { String icon = escapeURLPath(itemUIRegistry.getCategory(w)); return icon; } @Override public void setConfig(WebAppConfig config) { this.config = config; } }