package org.argeo.cms.ui.util;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import org.argeo.cms.ui.CmsTheme;
import org.eclipse.swt.graphics.Image;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
/**
* Simplifies the theming of an app (only RAP is supported at this stage).
*
* Additional fonts listed in /fonts.txt
.
* Additional (standard CSS) header in /header.css
.
* RAP specific CSS files in /rap/*.css
.
* All images added as additional resources based on extensions
* / ** /*.{png,gif,jpeg,...}
.
*/
public class BundleCmsTheme extends AbstractCmsTheme {
public final static String DEFAULT_CMS_THEME_BUNDLE = "org.argeo.theme.argeo2";
public final static String CMS_THEME_PROPERTY = "argeo.cms.theme";
public final static String CMS_THEME_BUNDLE_PROPERTY = "argeo.cms.theme.bundle";
// private final static Log log = LogFactory.getLog(BundleCmsTheme.class);
private String themeId;
private Set rapCssPaths = new TreeSet<>();
private Set imagesPaths = new TreeSet<>();
private String headerCss;
private List fonts = new ArrayList<>();
private String basePath;
private String rapCssPath;
private Bundle themeBundle;
public BundleCmsTheme() {
}
public void init(BundleContext bundleContext, Map properties) {
initResources(bundleContext, null);
}
public void destroy(BundleContext bundleContext, Map properties) {
}
@Deprecated
public BundleCmsTheme(BundleContext bundleContext) {
this(bundleContext, null);
}
@Deprecated
public BundleCmsTheme(BundleContext bundleContext, String symbolicName) {
initResources(bundleContext, symbolicName);
}
private void initResources(BundleContext bundleContext, String symbolicName) {
if (symbolicName == null) {
themeBundle = bundleContext.getBundle();
// basePath = "/theme/";
// cssPath = basePath;
} else {
themeBundle = findThemeBundle(bundleContext, symbolicName);
}
basePath = "/";
rapCssPath = "/rap/";
// this.themeId = RWT.DEFAULT_THEME_ID;
this.themeId = themeBundle.getSymbolicName();
addRapStyleSheets(themeBundle);
addResources("*.png");
addResources("*.gif");
addResources("*.jpg");
addResources("*.jpeg");
addResources("*.svg");
addResources("*.ico");
// fonts
URL fontsUrl = themeBundle.getEntry(basePath + "fonts.txt");
if (fontsUrl != null) {
loadFontsUrl(fontsUrl);
}
// common CSS header (plain CSS)
URL headerCssUrl = themeBundle.getEntry(basePath + "header.css");
if (headerCssUrl != null) {
try (BufferedReader buffer = new BufferedReader(new InputStreamReader(headerCssUrl.openStream(), UTF_8))) {
headerCss = buffer.lines().collect(Collectors.joining("\n"));
} catch (IOException e) {
throw new IllegalArgumentException("Cannot read " + headerCssUrl, e);
}
}
}
public String getHtmlHeaders() {
StringBuilder sb = new StringBuilder();
if (headerCss != null) {
sb.append("\n");
}
for (String link : fonts) {
sb.append("\n");
}
if (sb.length() == 0)
return null;
else
return sb.toString();
}
void addRapStyleSheets(Bundle themeBundle) {
Enumeration themeResources = themeBundle.findEntries(rapCssPath, "*.css", true);
if (themeResources == null)
return;
while (themeResources.hasMoreElements()) {
String resource = themeResources.nextElement().getPath();
// remove first '/' so that RWT registers it
resource = resource.substring(1);
if (!resource.endsWith("/")) {
// if (rapCss.containsKey(resource))
// log.warn("Overriding " + resource + " from " + themeBundle.getSymbolicName());
// rapCss.put(resource, ssRL);
rapCssPaths.add(resource);
}
}
}
void loadFontsUrl(URL url) {
try (BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream(), UTF_8))) {
String line = null;
while ((line = in.readLine()) != null) {
line = line.trim();
if (!line.equals("") && !line.startsWith("#")) {
fonts.add(line);
}
}
} catch (IOException e) {
throw new IllegalArgumentException("Cannot load URL " + url, e);
}
}
void addResources(String pattern) {
Enumeration themeResources = themeBundle.findEntries(basePath, pattern, true);
if (themeResources == null)
return;
while (themeResources.hasMoreElements()) {
String resource = themeResources.nextElement().getPath();
// remove first '/' so that RWT registers it
resource = resource.substring(1);
if (!resource.endsWith("/")) {
// if (resources.containsKey(resource))
// log.warn("Overriding " + resource + " from " + themeBundle.getSymbolicName());
// resources.put(resource, themeBRL);
imagesPaths.add(resource);
}
}
}
@Override
public InputStream getResourceAsStream(String resourceName) throws IOException {
URL res = themeBundle.getEntry(resourceName);
if (res == null) {
res = themeBundle.getResource(resourceName);
if (res == null)
return null;
// throw new IllegalArgumentException(
// "Resource " + resourceName + " not found in bundle " + themeBundle.getSymbolicName());
}
return res.openStream();
}
public String getThemeId() {
return themeId;
}
// public void setThemeId(String themeId) {
// this.themeId = themeId;
// }
//
// public String getBasePath() {
// return basePath;
// }
//
// public void setBasePath(String basePath) {
// this.basePath = basePath;
// }
//
// public String getRapCssPath() {
// return rapCssPath;
// }
//
// public void setRapCssPath(String cssPath) {
// this.rapCssPath = cssPath;
// }
@Override
public Set getRapCssPaths() {
return rapCssPaths;
}
@Override
public Set getImagesPaths() {
return imagesPaths;
}
private static Bundle findThemeBundle(BundleContext bundleContext, String themeId) {
if (themeId == null)
return null;
// TODO optimize
// TODO deal with multiple versions
Bundle themeBundle = null;
if (themeId != null) {
for (Bundle bundle : bundleContext.getBundles())
if (themeId.equals(bundle.getSymbolicName())) {
themeBundle = bundle;
break;
}
}
return themeBundle;
}
}