blob: 115d90f738840d6c4c27b612914824a564030bf0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012 Rushan R. Gilmullin and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Rushan R. Gilmullin - initial API and implementation
*******************************************************************************/
package org.eclipse.osbp.vaaclipse.theme;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.osbp.vaaclipse.publicapi.theme.Theme;
import org.eclipse.osbp.vaaclipse.publicapi.theme.ThemeContribution;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.wiring.BundleWiring;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ThemeImpl extends ThemeEntryImpl implements Theme {
private static final String PLATFORM_PLUGIN = "platform:/plugin/";
private static final String PLATFORM_RESOURCE = "platform:/resource/";
private static final Logger LOGGER = LoggerFactory.getLogger(ThemeImpl.class);
private static List<String> imageExtensions = new ArrayList<String>();
static {
imageExtensions.add("png");
imageExtensions.add("gif");
imageExtensions.add("jpg");
imageExtensions.add("jpeg");
}
private String label;
private String description;
private List<ThemeContribution> contributions = new ArrayList<ThemeContribution>();
private List<Theme> inheritedThemes = new ArrayList<Theme>();
private List<String> cssCashList;
private List<String> resourcesCashList;
private byte[] cssBytes;
public ThemeImpl(String id, String label) {
super(id);
this.label = label;
}
public void setId(String id) {
this.id = id;
}
public String getLabel() {
return label;
}
@Override
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public List<Theme> getInheritedThemes() {
return Collections.unmodifiableList(this.inheritedThemes);
}
@Override
public List<ThemeContribution> getContributions() {
return Collections.unmodifiableList(this.contributions);
}
public void addContribution(ThemeContribution contibution) {
this.resourcesCashList = null;
this.cssCashList = null;
this.contributions.add(contibution);
}
public void addInheritedTheme(Theme theme) {
this.resourcesCashList = null;
this.cssCashList = null;
this.inheritedThemes.add(theme);
}
@Override
public String toString() {
return "Theme [id=" + id + ", label='" + label;
}
@Override
public List<String> getAllCssURIs() {
if (cssCashList == null) {
cssCashList = new ArrayList<String>();
cssCashList.add(getCssUri());
for (Theme theme : this.getInheritedThemes()) {
cssCashList.addAll(theme.getAllCssURIs());
}
for (ThemeContribution c : this.getContributions()) {
if (c.getCssUri() != null)
cssCashList.add(c.getCssUri());
}
}
return cssCashList;
}
@Override
public List<String> getAllResourceLocationURIs() {
if (resourcesCashList == null) {
resourcesCashList = new ArrayList<String>();
resourcesCashList.addAll(getResourceLocationURIs());
for (Theme theme : this.getInheritedThemes()) {
resourcesCashList.addAll(theme.getAllResourceLocationURIs());
}
for (ThemeContribution c : this.getContributions()) {
resourcesCashList.addAll(c.getResourceLocationURIs());
}
}
return Collections.unmodifiableList(resourcesCashList);
}
@Override
public InputStream getCssAsStream() {
return new ByteArrayInputStream(cssBytes);
}
@Override
public InputStream getThemeResourceAsStream(String themeUri) {
InputStream in = getResourceAsStreamInternal(themeUri, true);
return in;
}
private InputStream getResourceAsStreamInternal(String themeUri, boolean useDefault) {
InputStream in = null;
StringBuffer buffer = new StringBuffer(themeUri);
for (String resLocUri : getAllResourceLocationURIs()) {
String bundlePath = resLocUri + themeUri;
try {
URL u = new URL(bundlePath);
in = u.openStream();
break;
} catch (Exception ex) {
buffer.append(":\n").append(ex.getClass().getName()).append(" in ").append(resLocUri).append("\n");
}
}
if (in == null) {
if (useDefault) {
return getResourceAsStreamInternal("icon/nopic_small.png", false);
} else {
LOGGER.debug("{}", buffer);
}
}
return in;
}
void prepareTheme() throws Exception {
StringBuffer inheritedBuffer = new StringBuffer();
for (Theme inherited : inheritedThemes) {
inheritedBuffer.append(String.format("@import \"../%s/styles.css\";", inherited.getWebId()));
}
List<ThemeContributionImpl> afterMainCss = new ArrayList<ThemeContributionImpl>();
List<ThemeContributionImpl> afterInheritedImports = new ArrayList<ThemeContributionImpl>();
List<ThemeContributionImpl> beforeInheritedImports = new ArrayList<ThemeContributionImpl>();
Map<String, List<ThemeContributionImpl>> bindedContributionsBefore = new HashMap<String, List<ThemeContributionImpl>>();
Map<String, List<ThemeContributionImpl>> bindedContributionsAfter = new HashMap<String, List<ThemeContributionImpl>>();
for (ThemeContribution c : this.contributions) {
if (c.getCssUri() == null)
continue;
ThemeContributionImpl contribution = (ThemeContributionImpl) c;
String pos = contribution.getInsertPosition();
String[] split = pos.split("=");
if (split.length == 2) {
String mod = split[0].trim();
if ("after".equals(mod) || "before".equals(mod)) {
String value = split[1].trim();
if (mod.equals("after") && ThemeContribution.MAIN_CSS.equals(value)) {
afterMainCss.add(contribution);
} else if (mod.equals("after") && ThemeContribution.INHERITED_IMPORTS.equals(value)
|| mod.equals("before") && ThemeContribution.MAIN_CSS.equals(value)) {
afterInheritedImports.add(contribution);
} else if (mod.equals("before") && ThemeContribution.INHERITED_IMPORTS.equals(value)) {
beforeInheritedImports.add(contribution);
} else {// value - is contribution id?
if (mod.equals("before")) {
if (!bindedContributionsBefore.containsKey(value))
bindedContributionsBefore.put(value, new ArrayList<ThemeContributionImpl>());
bindedContributionsBefore.get(value).add(contribution);
} else if (mod.equals("after")) {
if (!bindedContributionsAfter.containsKey(value))
bindedContributionsAfter.put(value, new ArrayList<ThemeContributionImpl>());
bindedContributionsAfter.get(value).add(contribution);
}
}
}
}
}
StringBuffer afterMainCssContributionsBuffer = buildStringBuffer(afterMainCss, bindedContributionsBefore,
bindedContributionsAfter);
StringBuffer afterInheritedImportsContributionsBuffer = buildStringBuffer(afterInheritedImports,
bindedContributionsBefore, bindedContributionsAfter);
StringBuffer beforeInheritedImportsContributionsBuffer = buildStringBuffer(beforeInheritedImports,
bindedContributionsBefore, bindedContributionsAfter);
String ls = System.lineSeparator();
String total = beforeInheritedImportsContributionsBuffer.toString() + ls + inheritedBuffer.toString() + ls
+ afterInheritedImportsContributionsBuffer.toString() + ls + "@import \"original_styles.css\";" + ls
+ afterMainCssContributionsBuffer.toString() + ls;
cssBytes = total.getBytes("UTF-8");
for (Theme inherited : inheritedThemes) {
if (inherited instanceof ThemeImpl) {
((ThemeImpl) inherited).prepareTheme();
}
}
}
@Override
public List<String> getImageURIs() {
List<String> result = new ArrayList<String>();
for (String uri : getAllResourceLocationURIs()) {
PathPair pair = parsePathPair(uri);
if (pair == null) {
continue;
}
Bundle target = findBundle(pair.bundle);
BundleWiring wiring = target.adapt(BundleWiring.class);
Collection<String> resources = wiring.listResources(pair.folder, "*", BundleWiring.LISTRESOURCES_RECURSE);
for (String path : resources) {
if (path.contains(".")) {
String extension = path.substring(path.lastIndexOf(".") + 1);
if (imageExtensions.contains(extension)) {
result.add("platform:/plugin/" + pair.bundle + "/" + path);
}
}
}
}
return result;
}
private Bundle findBundle(String bundleName) {
for (Bundle b : FrameworkUtil.getBundle(getClass()).getBundleContext().getBundles()) {
if (b.getSymbolicName().equals(bundleName)) {
return b;
}
}
return null;
}
private PathPair parsePathPair(String uri) {
PathPair pair = null;
if (uri.startsWith(PLATFORM_RESOURCE)) {
pair = new PathPair();
// eg platform:/resource/org.my.foo.bundle/images/obj16
String result = uri.replace(PLATFORM_RESOURCE, "");
pair.bundle = result.substring(0, result.indexOf("/"));
String folder = result = result.substring(result.indexOf("/"), result.length());
pair.folder = folder;
} else if (uri.startsWith(PLATFORM_PLUGIN)) {
// eg platform:/plugin/org.my.foo.bundle/images/obj16
pair = new PathPair();
String result = uri.replace(PLATFORM_PLUGIN, "");
pair.bundle = result.substring(0, result.indexOf("/"));
String folder = result = result.substring(result.indexOf("/"), result.length());
pair.folder = folder;
}
LOGGER.error("Not a valid URI: " + uri);
return pair;
}
private StringBuffer buildStringBuffer(List<ThemeContributionImpl> list,
Map<String, List<ThemeContributionImpl>> bindedContributionsBefore,
Map<String, List<ThemeContributionImpl>> bindedContributionsAfter) {
StringBuffer buffer = new StringBuffer();
List<ThemeContributionImpl> result = buildList(list, bindedContributionsBefore, bindedContributionsAfter);
for (ThemeContributionImpl c : result) {
buffer.append(String.format("@import \"%s.css\";", c.getWebId()));
}
return buffer;
}
private List<ThemeContributionImpl> buildList(List<ThemeContributionImpl> list,
Map<String, List<ThemeContributionImpl>> bindedContributionsBefore,
Map<String, List<ThemeContributionImpl>> bindedContributionsAfter) {
bindedContributionsBefore = new HashMap<String, List<ThemeContributionImpl>>(bindedContributionsBefore);
bindedContributionsAfter = new HashMap<String, List<ThemeContributionImpl>>(bindedContributionsAfter);
List<ThemeContributionImpl> accumulator = new ArrayList<ThemeContributionImpl>();
int restBefore, restAfter;
do {
restBefore = bindedContributionsBefore.size() + bindedContributionsAfter.size();
for (ThemeContributionImpl c : list) {
List<ThemeContributionImpl> bindedList = bindedContributionsBefore.remove(c.getId());
if (bindedList != null)
accumulator.addAll(bindedList);
accumulator.add(c);
bindedList = bindedContributionsAfter.remove(c.getId());
if (bindedList != null)
accumulator.addAll(bindedList);
}
restAfter = bindedContributionsBefore.size() + bindedContributionsAfter.size();
list = accumulator;
accumulator = new ArrayList<ThemeContributionImpl>();
} while (restBefore != restAfter);
return list;
}
private static class PathPair {
private String bundle;
private String folder;
}
}