/** * Copyright (c) 2010-2016, 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.persistence.dynamodb.internal; import java.math.BigDecimal; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.TimeZone; import org.openhab.core.items.Item; import org.openhab.core.library.items.ColorItem; import org.openhab.core.library.items.ContactItem; import org.openhab.core.library.items.DateTimeItem; import org.openhab.core.library.items.DimmerItem; import org.openhab.core.library.items.LocationItem; import org.openhab.core.library.items.NumberItem; import org.openhab.core.library.items.RollershutterItem; import org.openhab.core.library.items.StringItem; import org.openhab.core.library.items.SwitchItem; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.HSBType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.OpenClosedType; import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.PointType; import org.openhab.core.library.types.StringType; import org.openhab.core.library.types.UpDownType; import org.openhab.core.persistence.HistoricItem; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; import org.openhab.library.tel.items.CallItem; import org.openhab.library.tel.types.CallType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Base class for all DynamoDBItem. Represents openHAB Item serialized in a suitable format for the database * * @param <T> Type of the state as accepted by the AWS SDK. * * @author Sami Salonen */ public abstract class AbstractDynamoDBItem<T> implements DynamoDBItem<T> { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractDynamoDBItem.class); public static final SimpleDateFormat DATEFORMATTER = new SimpleDateFormat(DATE_FORMAT); static { DATEFORMATTER.setTimeZone(TimeZone.getTimeZone("UTC")); } private static final String UNDEFINED_PLACEHOLDER = "<org.openhab.core.types.UnDefType.UNDEF>"; static final Map<Class<? extends Item>, Class<? extends DynamoDBItem<?>>> itemClassToDynamoItemClass = new HashMap<Class<? extends Item>, Class<? extends DynamoDBItem<?>>>(); static { itemClassToDynamoItemClass.put(CallItem.class, DynamoDBStringItem.class); itemClassToDynamoItemClass.put(ContactItem.class, DynamoDBBigDecimalItem.class); itemClassToDynamoItemClass.put(DateTimeItem.class, DynamoDBStringItem.class); itemClassToDynamoItemClass.put(LocationItem.class, DynamoDBStringItem.class); itemClassToDynamoItemClass.put(NumberItem.class, DynamoDBBigDecimalItem.class); itemClassToDynamoItemClass.put(RollershutterItem.class, DynamoDBBigDecimalItem.class); itemClassToDynamoItemClass.put(StringItem.class, DynamoDBStringItem.class); itemClassToDynamoItemClass.put(SwitchItem.class, DynamoDBBigDecimalItem.class); itemClassToDynamoItemClass.put(DimmerItem.class, DynamoDBBigDecimalItem.class); // inherited from SwitchItem (!) itemClassToDynamoItemClass.put(ColorItem.class, DynamoDBStringItem.class); // inherited from DimmerItem } public static final Class<DynamoDBItem<?>> getDynamoItemClass(Class<? extends Item> itemClass) throws NullPointerException { @SuppressWarnings("unchecked") Class<DynamoDBItem<?>> dtoclass = (Class<DynamoDBItem<?>>) itemClassToDynamoItemClass.get(itemClass); if (dtoclass == null) { throw new NullPointerException(String.format("Unknown item class %s", itemClass)); } return dtoclass; } protected String name; protected T state; protected Date time; public AbstractDynamoDBItem(String name, T state, Date time) { this.name = name; this.state = state; this.time = time; } public static DynamoDBItem<?> fromState(String name, State state, Date time) { if (state instanceof DecimalType && !(state instanceof HSBType)) { // also covers PercentType which is inherited from DecimalType return new DynamoDBBigDecimalItem(name, ((DecimalType) state).toBigDecimal(), time); } else if (state instanceof OnOffType) { return new DynamoDBBigDecimalItem(name, ((OnOffType) state) == OnOffType.ON ? BigDecimal.ONE : BigDecimal.ZERO, time); } else if (state instanceof OpenClosedType) { return new DynamoDBBigDecimalItem(name, ((OpenClosedType) state) == OpenClosedType.OPEN ? BigDecimal.ONE : BigDecimal.ZERO, time); } else if (state instanceof UpDownType) { return new DynamoDBBigDecimalItem(name, ((UpDownType) state) == UpDownType.UP ? BigDecimal.ONE : BigDecimal.ZERO, time); } else if (state instanceof DateTimeType) { return new DynamoDBStringItem(name, DATEFORMATTER.format(((DateTimeType) state).getCalendar().getTime()), time); } else if (state instanceof UnDefType) { return new DynamoDBStringItem(name, UNDEFINED_PLACEHOLDER, time); } else if (state instanceof CallType) { // CallType.format method instead of toString since that matches the format expected by the String // constructor return new DynamoDBStringItem(name, state.format("%s##%s"), time); } else { // HSBType, PointType and StringType return new DynamoDBStringItem(name, state.toString(), time); } } /* * (non-Javadoc) * * @see org.openhab.persistence.dynamodb.internal.DynamoItem#asHistoricItem(org.openhab.core.items.Item) */ @Override public HistoricItem asHistoricItem(final Item item) { final State[] state = new State[1]; accept(new DynamoDBItemVisitor() { @Override public void visit(DynamoDBStringItem dynamoStringItem) { if (item instanceof ColorItem) { state[0] = new HSBType(dynamoStringItem.getState()); } else if (item instanceof LocationItem) { state[0] = new PointType(dynamoStringItem.getState()); } else if (item instanceof DateTimeItem) { Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); try { cal.setTime(DATEFORMATTER.parse(dynamoStringItem.getState())); } catch (ParseException e) { LOGGER.error("Failed to parse {} as date. Outputting UNDEF instead", dynamoStringItem.getState()); state[0] = UnDefType.UNDEF; } state[0] = new DateTimeType(cal); } else if (dynamoStringItem.getState().equals(UNDEFINED_PLACEHOLDER)) { state[0] = UnDefType.UNDEF; } else if (item instanceof CallItem) { String parts = dynamoStringItem.getState(); String[] strings = parts.split("##"); String dest = strings[0]; String orig = strings[1]; state[0] = new CallType(orig, dest); } else { state[0] = new StringType(dynamoStringItem.getState()); } } @Override public void visit(DynamoDBBigDecimalItem dynamoBigDecimalItem) { if (item instanceof NumberItem) { state[0] = new DecimalType(dynamoBigDecimalItem.getState()); } else if (item instanceof DimmerItem) { state[0] = new PercentType(dynamoBigDecimalItem.getState()); } else if (item instanceof SwitchItem) { state[0] = dynamoBigDecimalItem.getState().compareTo(BigDecimal.ONE) == 0 ? OnOffType.ON : OnOffType.OFF; } else if (item instanceof ContactItem) { state[0] = dynamoBigDecimalItem.getState().compareTo(BigDecimal.ONE) == 0 ? OpenClosedType.OPEN : OpenClosedType.CLOSED; } else if (item instanceof RollershutterItem) { state[0] = new PercentType(dynamoBigDecimalItem.getState()); } else { LOGGER.warn("Not sure how to convert big decimal item {} to type {}. Using StringType as fallback", dynamoBigDecimalItem.getName(), item.getClass()); state[0] = new StringType(dynamoBigDecimalItem.getState().toString()); } } }); return new DynamoDBHistoricItem(getName(), state[0], getTime()); } /** * We define all getter and setters in the child class implement those. Having the getter * and setter implementations here in the parent class does not work with introspection done by AWS SDK (1.11.56). */ /* * (non-Javadoc) * * @see org.openhab.persistence.dynamodb.internal.DynamoItem#accept(org.openhab.persistence.dynamodb.internal. * DynamoItemVisitor) */ @Override public abstract void accept(DynamoDBItemVisitor visitor); @Override public String toString() { return DateFormat.getDateTimeInstance().format(time) + ": " + name + " -> " + state.toString(); } }