]> git.basschouten.com Git - openhab-addons.git/blob
660bd632efd7cf9ea1641c23136a93ded157985b
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.transform.javascript.internal;
14
15 import java.io.File;
16 import java.io.FilenameFilter;
17 import java.net.URI;
18 import java.util.Arrays;
19 import java.util.Collection;
20 import java.util.List;
21 import java.util.Locale;
22 import java.util.stream.Collectors;
23
24 import javax.script.Bindings;
25 import javax.script.CompiledScript;
26 import javax.script.ScriptException;
27
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.eclipse.jdt.annotation.Nullable;
30 import org.openhab.core.config.core.ConfigOptionProvider;
31 import org.openhab.core.config.core.ParameterOption;
32 import org.openhab.core.transform.TransformationException;
33 import org.openhab.core.transform.TransformationService;
34 import org.osgi.service.component.annotations.Activate;
35 import org.osgi.service.component.annotations.Component;
36 import org.osgi.service.component.annotations.Reference;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 /**
41  * The implementation of {@link TransformationService} which transforms the
42  * input by Java Script.
43  *
44  * @author Pauli Anttila - Initial contribution
45  * @author Thomas Kordelle - pre compiled scripts
46  */
47 @NonNullByDefault
48 @Component(service = { TransformationService.class, ConfigOptionProvider.class }, property = { "openhab.transform=JS" })
49 public class JavaScriptTransformationService implements TransformationService, ConfigOptionProvider {
50
51     private final Logger logger = LoggerFactory.getLogger(JavaScriptTransformationService.class);
52
53     private static final char EXTENSION_SEPARATOR = '.';
54
55     private static final String PROFILE_CONFIG_URI = "profile:transform:JS";
56     private static final String CONFIG_PARAM_FUNCTION = "function";
57     private static final String[] FILE_NAME_EXTENSIONS = { "js" };
58
59     private final JavaScriptEngineManager manager;
60
61     @Activate
62     public JavaScriptTransformationService(final @Reference JavaScriptEngineManager manager) {
63         this.manager = manager;
64     }
65
66     /**
67      * Transforms the input <code>source</code> by Java Script. It expects the
68      * transformation rule to be read from a file which is stored under the
69      * 'configurations/transform' folder. To organize the various
70      * transformations one should use subfolders.
71      *
72      * @param filename the name of the file which contains the Java script
73      *            transformation rule. Transformation service inject input
74      *            (source) to 'input' variable.
75      * @param source the input to transform
76      */
77     @Override
78     public @Nullable String transform(String filename, String source) throws TransformationException {
79         if (filename == null || source == null) {
80             throw new TransformationException("the given parameters 'filename' and 'source' must not be null");
81         }
82
83         final long startTime = System.currentTimeMillis();
84         logger.debug("about to transform '{}' by the JavaScript '{}'", source, filename);
85
86         String result = "";
87
88         try {
89             final CompiledScript cScript = manager.getScript(filename);
90             final Bindings bindings = cScript.getEngine().createBindings();
91             bindings.put("input", source);
92             result = String.valueOf(cScript.eval(bindings));
93             return result;
94         } catch (ScriptException e) {
95             throw new TransformationException("An error occurred while executing script. " + e.getMessage(), e);
96         } finally {
97             logger.trace("JavaScript execution elapsed {} ms. Result: {}", System.currentTimeMillis() - startTime,
98                     result);
99         }
100     }
101
102     @Override
103     public @Nullable Collection<ParameterOption> getParameterOptions(URI uri, String param, @Nullable String context,
104             @Nullable Locale locale) {
105         if (PROFILE_CONFIG_URI.equals(uri.toString())) {
106             switch (param) {
107                 case CONFIG_PARAM_FUNCTION:
108                     return getFilenames(FILE_NAME_EXTENSIONS).stream().map(f -> new ParameterOption(f, f))
109                             .collect(Collectors.toList());
110             }
111         }
112         return null;
113     }
114
115     /**
116      * Returns a list of all files with the given extensions in the transformation folder
117      */
118     private List<String> getFilenames(String[] validExtensions) {
119         File path = new File(TransformationScriptWatcher.TRANSFORM_FOLDER + File.separator);
120         return Arrays.asList(path.listFiles(new FileExtensionsFilter(validExtensions))).stream().map(f -> f.getName())
121                 .collect(Collectors.toList());
122     }
123
124     private class FileExtensionsFilter implements FilenameFilter {
125
126         private final String[] validExtensions;
127
128         public FileExtensionsFilter(String[] validExtensions) {
129             this.validExtensions = validExtensions;
130         }
131
132         @Override
133         public boolean accept(@Nullable File dir, @Nullable String name) {
134             if (name != null) {
135                 for (String extension : validExtensions) {
136                     if (name.toLowerCase().endsWith(EXTENSION_SEPARATOR + extension)) {
137                         return true;
138                     }
139                 }
140             }
141             return false;
142         }
143     }
144 }