]> git.argeo.org Git - lgpl/argeo-commons.git/blob - util/BundleCmsTheme.java
Prepare next development cycle
[lgpl/argeo-commons.git] / util / BundleCmsTheme.java
1 package org.argeo.cms.ui.util;
2
3 import static java.nio.charset.StandardCharsets.UTF_8;
4
5 import java.io.BufferedReader;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.io.InputStreamReader;
9 import java.net.URL;
10 import java.util.ArrayList;
11 import java.util.Enumeration;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Set;
15 import java.util.TreeSet;
16 import java.util.stream.Collectors;
17
18 import org.argeo.cms.ui.CmsTheme;
19 import org.eclipse.swt.graphics.Image;
20 import org.osgi.framework.Bundle;
21 import org.osgi.framework.BundleContext;
22
23 /**
24 * Simplifies the theming of an app (only RAP is supported at this stage).<br>
25 *
26 * Additional fonts listed in <code>/fonts.txt</code>.<br>
27 * Additional (standard CSS) header in <code>/header.css</code>.<br>
28 * RAP specific CSS files in <code>/rap/*.css</code>.<br>
29 * All images added as additional resources based on extensions
30 * <code>/ ** /*.{png,gif,jpeg,...}</code>.<br>
31 */
32 public class BundleCmsTheme extends AbstractCmsTheme {
33 public final static String DEFAULT_CMS_THEME_BUNDLE = "org.argeo.theme.argeo2";
34
35 public final static String CMS_THEME_PROPERTY = "argeo.cms.theme";
36 public final static String CMS_THEME_BUNDLE_PROPERTY = "argeo.cms.theme.bundle";
37
38 // private final static Log log = LogFactory.getLog(BundleCmsTheme.class);
39
40 private String themeId;
41 private Set<String> rapCssPaths = new TreeSet<>();
42 private Set<String> imagesPaths = new TreeSet<>();
43
44 private String headerCss;
45 private List<String> fonts = new ArrayList<>();
46
47 private String basePath;
48 private String rapCssPath;
49 private Bundle themeBundle;
50
51 public BundleCmsTheme() {
52
53 }
54
55 public void init(BundleContext bundleContext, Map<String, String> properties) {
56 initResources(bundleContext, null);
57 }
58
59 public void destroy(BundleContext bundleContext, Map<String, String> properties) {
60
61 }
62
63 @Deprecated
64 public BundleCmsTheme(BundleContext bundleContext) {
65 this(bundleContext, null);
66 }
67
68 @Deprecated
69 public BundleCmsTheme(BundleContext bundleContext, String symbolicName) {
70 initResources(bundleContext, symbolicName);
71 }
72
73 private void initResources(BundleContext bundleContext, String symbolicName) {
74 if (symbolicName == null) {
75 themeBundle = bundleContext.getBundle();
76 // basePath = "/theme/";
77 // cssPath = basePath;
78 } else {
79 themeBundle = findThemeBundle(bundleContext, symbolicName);
80 }
81 basePath = "/";
82 rapCssPath = "/rap/";
83 // this.themeId = RWT.DEFAULT_THEME_ID;
84 this.themeId = themeBundle.getSymbolicName();
85 addRapStyleSheets(themeBundle);
86 addResources("*.png");
87 addResources("*.gif");
88 addResources("*.jpg");
89 addResources("*.jpeg");
90 addResources("*.svg");
91 addResources("*.ico");
92
93 // fonts
94 URL fontsUrl = themeBundle.getEntry(basePath + "fonts.txt");
95 if (fontsUrl != null) {
96 loadFontsUrl(fontsUrl);
97 }
98
99 // common CSS header (plain CSS)
100 URL headerCssUrl = themeBundle.getEntry(basePath + "header.css");
101 if (headerCssUrl != null) {
102 try (BufferedReader buffer = new BufferedReader(new InputStreamReader(headerCssUrl.openStream(), UTF_8))) {
103 headerCss = buffer.lines().collect(Collectors.joining("\n"));
104 } catch (IOException e) {
105 throw new IllegalArgumentException("Cannot read " + headerCssUrl, e);
106 }
107 }
108 }
109
110 public String getHtmlHeaders() {
111 StringBuilder sb = new StringBuilder();
112 if (headerCss != null) {
113 sb.append("<style type='text/css'>\n");
114 sb.append(headerCss);
115 sb.append("\n</style>\n");
116 }
117 for (String link : fonts) {
118 sb.append("<link rel='stylesheet' href='");
119 sb.append(link);
120 sb.append("'/>\n");
121 }
122 if (sb.length() == 0)
123 return null;
124 else
125 return sb.toString();
126 }
127
128 void addRapStyleSheets(Bundle themeBundle) {
129 Enumeration<URL> themeResources = themeBundle.findEntries(rapCssPath, "*.css", true);
130 if (themeResources == null)
131 return;
132 while (themeResources.hasMoreElements()) {
133 String resource = themeResources.nextElement().getPath();
134 // remove first '/' so that RWT registers it
135 resource = resource.substring(1);
136 if (!resource.endsWith("/")) {
137 // if (rapCss.containsKey(resource))
138 // log.warn("Overriding " + resource + " from " + themeBundle.getSymbolicName());
139 // rapCss.put(resource, ssRL);
140 rapCssPaths.add(resource);
141 }
142
143 }
144
145 }
146
147 void loadFontsUrl(URL url) {
148 try (BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream(), UTF_8))) {
149 String line = null;
150 while ((line = in.readLine()) != null) {
151 line = line.trim();
152 if (!line.equals("") && !line.startsWith("#")) {
153 fonts.add(line);
154 }
155 }
156 } catch (IOException e) {
157 throw new IllegalArgumentException("Cannot load URL " + url, e);
158 }
159 }
160
161 void addResources(String pattern) {
162 Enumeration<URL> themeResources = themeBundle.findEntries(basePath, pattern, true);
163 if (themeResources == null)
164 return;
165 while (themeResources.hasMoreElements()) {
166 String resource = themeResources.nextElement().getPath();
167 // remove first '/' so that RWT registers it
168 resource = resource.substring(1);
169 if (!resource.endsWith("/")) {
170 // if (resources.containsKey(resource))
171 // log.warn("Overriding " + resource + " from " + themeBundle.getSymbolicName());
172 // resources.put(resource, themeBRL);
173 imagesPaths.add(resource);
174 }
175
176 }
177
178 }
179
180 @Override
181 public InputStream getResourceAsStream(String resourceName) throws IOException {
182 URL res = themeBundle.getEntry(resourceName);
183 if (res == null) {
184 res = themeBundle.getResource(resourceName);
185 if (res == null)
186 return null;
187 // throw new IllegalArgumentException(
188 // "Resource " + resourceName + " not found in bundle " + themeBundle.getSymbolicName());
189 }
190 return res.openStream();
191 }
192
193 public String getThemeId() {
194 return themeId;
195 }
196
197
198 // public void setThemeId(String themeId) {
199 // this.themeId = themeId;
200 // }
201 //
202 // public String getBasePath() {
203 // return basePath;
204 // }
205 //
206 // public void setBasePath(String basePath) {
207 // this.basePath = basePath;
208 // }
209 //
210 // public String getRapCssPath() {
211 // return rapCssPath;
212 // }
213 //
214 // public void setRapCssPath(String cssPath) {
215 // this.rapCssPath = cssPath;
216 // }
217
218 @Override
219 public Set<String> getRapCssPaths() {
220 return rapCssPaths;
221 }
222
223 @Override
224 public Set<String> getImagesPaths() {
225 return imagesPaths;
226 }
227
228 private static Bundle findThemeBundle(BundleContext bundleContext, String themeId) {
229 if (themeId == null)
230 return null;
231 // TODO optimize
232 // TODO deal with multiple versions
233 Bundle themeBundle = null;
234 if (themeId != null) {
235 for (Bundle bundle : bundleContext.getBundles())
236 if (themeId.equals(bundle.getSymbolicName())) {
237 themeBundle = bundle;
238 break;
239 }
240 }
241 return themeBundle;
242 }
243
244 }