org.openhab.binding.ebus.internal.parser.EBusConfigurationProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.openhab.binding.ebus.internal.parser.EBusConfigurationProvider.java

Source

/**
 * Copyright (c) 2010-2015, openHAB.org and others.
 *
 * 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.openhab.binding.ebus.internal.parser;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The configuration provider reads the vendors specific ebus protocol
 * infomation from the json configuration files. All placeholders (regex)
 * and javascript snippets will be compiled after loading to improve
 * runtime performance.
 * 
 * @author Christian Sowada
 * @since 1.7.0
 */
public class EBusConfigurationProvider {

    private static final Logger logger = LoggerFactory.getLogger(EBusConfigurationProvider.class);

    // The registry with all loaded configuration entries
    private ArrayList<Map<String, Object>> telegramRegistry = new ArrayList<Map<String, Object>>();

    // The script engine if available
    private Compilable compEngine;

    /**
     * Return if the provider is empty.
     * @return
     */
    public boolean isEmpty() {
        return telegramRegistry.isEmpty();
    }

    /**
     * Constructor
     */
    public EBusConfigurationProvider() {
        final ScriptEngineManager mgr = new ScriptEngineManager();

        // load script engine if available
        if (mgr != null) {
            final ScriptEngine engine = mgr.getEngineByName("JavaScript");

            if (engine == null) {
                logger.warn(
                        "Unable to load \"JavaScript\" engine! Skip every eBus value calculated by JavaScript.");

            } else if (engine instanceof Compilable) {
                compEngine = (Compilable) engine;

            }
        }
    }

    /**
     * Clears all loaded configurations
     */
    public void clear() {
        if (telegramRegistry != null) {
            telegramRegistry.clear();
        }
    }

    /**
     * Loads a JSON configuration file by url
     * @param url The url to a configuration file
     * @throws IOException Unable to read configuration file
     * @throws ParseException A invalid json file
     */
    @SuppressWarnings("unchecked")
    public void loadConfigurationFile(URL url) throws IOException {

        final ObjectMapper mapper = new ObjectMapper(); // can reuse, share globally
        final InputStream inputStream = url.openConnection().getInputStream();

        final ArrayList<Map<String, Object>> loadedTelegramRegistry = (ArrayList<Map<String, Object>>) mapper
                .readValue(inputStream, List.class);

        for (Iterator<Map<String, Object>> iterator = loadedTelegramRegistry.iterator(); iterator.hasNext();) {
            Map<String, Object> object = iterator.next();
            transformDataTypes(object);
        }

        if (loadedTelegramRegistry != null && !loadedTelegramRegistry.isEmpty()) {
            telegramRegistry.addAll(loadedTelegramRegistry);
        }
    }

    /**
     * @param configurationEntry
     */
    @SuppressWarnings("unchecked")
    protected void transformDataTypes(Map<String, Object> configurationEntry) {

        // Use filter property if set
        if (configurationEntry.get("filter") instanceof String) {
            String filter = (String) configurationEntry.get("filter");
            filter = filter.replaceAll("\\?\\?", "[0-9A-Z]{2}");
            logger.trace("Compile RegEx filter: {}", filter);
            configurationEntry.put("cfilter", Pattern.compile(filter));

        } else {
            // Build filter string

            // Always ignore first two hex bytes
            String filter = "[0-9A-Z]{2} [0-9A-Z]{2}";

            // Add command to filter string
            if (configurationEntry.containsKey("command")) {
                filter += " " + configurationEntry.get("command");
                filter += " [0-9A-Z]{2}";
            }

            // Add commdata to filter string
            if (configurationEntry.containsKey("data")) {
                filter += " " + configurationEntry.get("data");
            }

            // Finally add .* to end with everthing
            filter += " .*";

            logger.trace("Compile RegEx filter: {}", filter);
            configurationEntry.put("cfilter", Pattern.compile(filter));
        }

        // compile scipt's if available also once
        if (configurationEntry.containsKey("values")) {
            Map<String, Map<String, Object>> values = (Map<String, Map<String, Object>>) configurationEntry
                    .get("values");
            for (Entry<String, Map<String, Object>> entry : values.entrySet()) {
                if (entry.getValue().containsKey("script")) {
                    String script = (String) entry.getValue().get("script");

                    // check if engine is available
                    if (StringUtils.isNotEmpty(script) && compEngine != null) {
                        try {
                            CompiledScript compile = compEngine.compile(script);
                            entry.getValue().put("cscript", compile);
                        } catch (ScriptException e) {
                            logger.error("Error while compiling JavaScript!", e);
                        }
                    }
                }
            }
        }

        // compile scipt's if available
        if (configurationEntry.containsKey("computed_values")) {
            Map<String, Map<String, Object>> cvalues = (Map<String, Map<String, Object>>) configurationEntry
                    .get("computed_values");
            for (Entry<String, Map<String, Object>> entry : cvalues.entrySet()) {
                if (entry.getValue().containsKey("script")) {
                    String script = (String) entry.getValue().get("script");

                    // check if engine is available
                    if (StringUtils.isNotEmpty(script) && compEngine != null) {
                        try {
                            CompiledScript compile = compEngine.compile(script);
                            entry.getValue().put("cscript", compile);
                        } catch (ScriptException e) {
                            logger.error("Error while compiling JavaScript!", e);
                        }
                    }
                }
            }
        }

    }

    /**
     * Return all configuration which filter match the bufferString paramter
     * @param bufferString The byte string to check against all loaded filters
     * @return All configurations with matching filter
     */
    public List<Map<String, Object>> getCommandsByFilter(String bufferString) {

        final List<Map<String, Object>> matchedTelegramRegistry = new ArrayList<Map<String, Object>>();

        /** select matching telegram registry entries */
        for (Map<String, Object> registryEntry : telegramRegistry) {
            Pattern pattern = (Pattern) registryEntry.get("cfilter");
            Matcher matcher = pattern.matcher(bufferString);
            if (matcher.matches()) {
                matchedTelegramRegistry.add(registryEntry);
            }
        }

        return matchedTelegramRegistry;
    }

    /**
     * Return all configurations by command id and class
     * @param commandId The command id
     * @param commandClass The command class
     * @return All matching configurations
     */
    public Map<String, Object> getCommandById(String commandId, String commandClass) {
        for (Map<String, Object> entry : telegramRegistry) {
            if (entry.containsKey("id") && entry.get("id").equals(commandId) && entry.containsKey("class")
                    && entry.get("class").equals(commandClass)) {
                return entry;
            }
        }

        //FIXME: Return empty map
        return null;
    }

}