/**
* Copyright (c) 2010-2016 by the respective copyright holders.
*
* 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.io.gcal.internal.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.items.Item;
import org.openhab.core.items.ItemNotFoundException;
import org.openhab.core.items.ItemRegistry;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.openhab.core.types.TypeParser;
import org.openhab.io.console.ConsoleInterpreter;
import org.openhab.io.gcal.internal.GCalActivator;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implementation of Quartz {@link Job}-Interface. It parses the Calendar-Event
* content into single commands and let {@link ConsoleInterpreter} handle those
* commands.
*
* @author Thomas.Eichstaedt-Engelen
* @since 0.7.0
*/
public class ExecuteCommandJob implements Job {
private static final Logger logger = LoggerFactory.getLogger(ExecuteCommandJob.class);
public static final String JOB_DATA_CONTENT_KEY = "content";
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
String content = (String) context.getJobDetail().getJobDataMap().get(JOB_DATA_CONTENT_KEY);
ItemRegistry registry = GCalActivator.itemRegistryTracker.getService();
EventPublisher publisher = GCalActivator.eventPublisherTracker.getService();
if (registry == null) {
logger.warn("Sorry, no item registry service available!");
return;
}
if (publisher == null) {
logger.warn("Sorry, no event publisher service available!");
return;
}
if (content.startsWith("[PresenceSimulation]")) {
try {
Item item = registry.getItem("PresenceSimulation");
if (item.getState() != OnOffType.ON) {
logger.debug(
"Presence Simulation job detected, but PresenceSimulation is not in ON state. Job is not executed");
return;
}
} catch (ItemNotFoundException e) {
logger.warn(
"Presence Simulation job detected, but PresenceSimulation item does not exists. Check configuration");
return;
}
}
if (StringUtils.isNotBlank(content)) {
String[] commands = parseCommands(content);
for (String command : commands) {
String[] args = parseCommand(command);
try {
if (args[0].equals("send")) {
if (args.length > 2) {
Item item = registry.getItem(args[1]);
Command cmd = TypeParser.parseCommand(item.getAcceptedCommandTypes(), args[2]);
if (cmd != null) {
publisher.sendCommand(item.getName(), cmd);
logger.debug("Command {} has been sent", Arrays.asList(args));
} else {
logger.warn("Command '{}' is not valid. Command not sent.", Arrays.asList(args));
}
}
} else if (args[0].equals("update")) {
if (args.length > 2) {
Item item = registry.getItem(args[1]);
State state = TypeParser.parseState(item.getAcceptedDataTypes(), args[2]);
publisher.postUpdate(item.getName(), state);
logger.debug("Published update {}", Arrays.asList(args));
} else {
logger.warn("Command '{}' is not valid. Update not sent.", Arrays.asList(args));
}
} else {
logger.warn("Command {} not supported", args[0]);
}
} catch (ItemNotFoundException e) {
logger.warn("Executing command failed. Item {} not found", args[1]);
}
}
}
}
/**
* Reads the Calendar-Event content String line by line. It is assumed, that
* each line contains a single command. Blank lines are omitted.
*
* @param content the Calendar-Event content
* @return an array of single commands which can be executed afterwards
*/
protected String[] parseCommands(String content) {
Collection<String> parsedCommands = new ArrayList<String>();
BufferedReader in = new BufferedReader(new StringReader(content));
if (content.startsWith("[PresenceSimulation]")) {
// Presence Simulation event. Needs to be fired only if PresenceSimulation event is set to ON
try {
in.readLine();
} catch (IOException e) {
logger.error("reading event content throws exception", e);
}
}
try {
String command;
while ((command = in.readLine()) != null) {
if (StringUtils.isNotBlank(command)) {
parsedCommands.add(command.trim());
}
}
} catch (IOException ioe) {
logger.error("reading event content throws exception", ioe);
} finally {
try {
in.close();
} catch (IOException ioe) {
}
}
return parsedCommands.toArray(new String[0]);
}
/**
* Parses a <code>command</code>. Utilizes the {@link StreamTokenizer} which
* takes care of quoted Strings as well.
*
* @param command the command to parse
* @return the tokenized command which can be processed by the
* <code>ConsoleInterpreter</code>
*
* @see org.openhab.io.console.ConsoleInterpreter
*/
protected String[] parseCommand(String command) {
logger.trace("going to parse command '{}'", command);
// if the command starts with '>' it contains a script which needs no
// further handling here ...
if (command.startsWith(">")) {
return new String[] { ">", command.substring(1).trim() };
}
StreamTokenizer tokenizer = new StreamTokenizer(new StringReader(command));
// treat all characters as ordinary, including digits, so we never
// have to deal with doubles
tokenizer.resetSyntax();
tokenizer.wordChars(0x23, 0xFF);
tokenizer.whitespaceChars(0x00, 0x20);
tokenizer.quoteChar('"');
List<String> tokens = new ArrayList<String>();
try {
int tokenType = 0;
while (tokenType != StreamTokenizer.TT_EOF && tokenType != StreamTokenizer.TT_EOL) {
tokenType = tokenizer.nextToken();
String token = "";
switch (tokenType) {
case StreamTokenizer.TT_WORD:
case 34 /* quoted String */:
token = tokenizer.sval;
break;
}
tokens.add(token);
logger.trace("read value {} from the given command", token);
}
} catch (IOException ioe) {
}
return tokens.toArray(new String[0]);
}
}