]> git.basschouten.com Git - openhab-addons.git/blob
a12d3c5803c49da0771dce8de9f9ac89e7b66f6b
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.persistence.dynamodb.internal;
14
15 import java.math.BigDecimal;
16 import java.text.DateFormat;
17 import java.time.ZoneId;
18 import java.time.ZonedDateTime;
19 import java.time.format.DateTimeFormatter;
20 import java.time.format.DateTimeParseException;
21 import java.util.HashMap;
22 import java.util.Map;
23
24 import org.openhab.core.items.Item;
25 import org.openhab.core.library.items.CallItem;
26 import org.openhab.core.library.items.ColorItem;
27 import org.openhab.core.library.items.ContactItem;
28 import org.openhab.core.library.items.DateTimeItem;
29 import org.openhab.core.library.items.DimmerItem;
30 import org.openhab.core.library.items.LocationItem;
31 import org.openhab.core.library.items.NumberItem;
32 import org.openhab.core.library.items.PlayerItem;
33 import org.openhab.core.library.items.RollershutterItem;
34 import org.openhab.core.library.items.StringItem;
35 import org.openhab.core.library.items.SwitchItem;
36 import org.openhab.core.library.types.DateTimeType;
37 import org.openhab.core.library.types.DecimalType;
38 import org.openhab.core.library.types.HSBType;
39 import org.openhab.core.library.types.OnOffType;
40 import org.openhab.core.library.types.OpenClosedType;
41 import org.openhab.core.library.types.PercentType;
42 import org.openhab.core.library.types.PlayPauseType;
43 import org.openhab.core.library.types.PointType;
44 import org.openhab.core.library.types.RewindFastforwardType;
45 import org.openhab.core.library.types.StringListType;
46 import org.openhab.core.library.types.StringType;
47 import org.openhab.core.library.types.UpDownType;
48 import org.openhab.core.persistence.HistoricItem;
49 import org.openhab.core.types.State;
50 import org.openhab.core.types.UnDefType;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54 /**
55  * Base class for all DynamoDBItem. Represents openHAB Item serialized in a suitable format for the database
56  *
57  * @param <T> Type of the state as accepted by the AWS SDK.
58  *
59  * @author Sami Salonen - Initial contribution
60  */
61 public abstract class AbstractDynamoDBItem<T> implements DynamoDBItem<T> {
62
63     public static final DateTimeFormatter DATEFORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT)
64             .withZone(ZoneId.of("UTC"));
65
66     private static final String UNDEFINED_PLACEHOLDER = "<org.openhab.core.types.UnDefType.UNDEF>";
67
68     private static final Map<Class<? extends Item>, Class<? extends DynamoDBItem<?>>> ITEM_CLASS_MAP = new HashMap<>();
69
70     static {
71         ITEM_CLASS_MAP.put(CallItem.class, DynamoDBStringItem.class);
72         ITEM_CLASS_MAP.put(ContactItem.class, DynamoDBBigDecimalItem.class);
73         ITEM_CLASS_MAP.put(DateTimeItem.class, DynamoDBStringItem.class);
74         ITEM_CLASS_MAP.put(LocationItem.class, DynamoDBStringItem.class);
75         ITEM_CLASS_MAP.put(NumberItem.class, DynamoDBBigDecimalItem.class);
76         ITEM_CLASS_MAP.put(RollershutterItem.class, DynamoDBBigDecimalItem.class);
77         ITEM_CLASS_MAP.put(StringItem.class, DynamoDBStringItem.class);
78         ITEM_CLASS_MAP.put(SwitchItem.class, DynamoDBBigDecimalItem.class);
79         ITEM_CLASS_MAP.put(DimmerItem.class, DynamoDBBigDecimalItem.class); // inherited from SwitchItem (!)
80         ITEM_CLASS_MAP.put(ColorItem.class, DynamoDBStringItem.class); // inherited from DimmerItem
81         ITEM_CLASS_MAP.put(PlayerItem.class, DynamoDBStringItem.class);
82     }
83
84     public static final Class<DynamoDBItem<?>> getDynamoItemClass(Class<? extends Item> itemClass)
85             throws NullPointerException {
86         @SuppressWarnings("unchecked")
87         Class<DynamoDBItem<?>> dtoclass = (Class<DynamoDBItem<?>>) ITEM_CLASS_MAP.get(itemClass);
88         if (dtoclass == null) {
89             throw new IllegalArgumentException(String.format("Unknown item class %s", itemClass));
90         }
91         return dtoclass;
92     }
93
94     private final Logger logger = LoggerFactory.getLogger(AbstractDynamoDBItem.class);
95
96     protected String name;
97     protected T state;
98     protected ZonedDateTime time;
99
100     public AbstractDynamoDBItem(String name, T state, ZonedDateTime time) {
101         this.name = name;
102         this.state = state;
103         this.time = time;
104     }
105
106     public static DynamoDBItem<?> fromState(String name, State state, ZonedDateTime time) {
107         if (state instanceof DecimalType && !(state instanceof HSBType)) {
108             // also covers PercentType which is inherited from DecimalType
109             return new DynamoDBBigDecimalItem(name, ((DecimalType) state).toBigDecimal(), time);
110         } else if (state instanceof OnOffType) {
111             return new DynamoDBBigDecimalItem(name,
112                     ((OnOffType) state) == OnOffType.ON ? BigDecimal.ONE : BigDecimal.ZERO, time);
113         } else if (state instanceof OpenClosedType) {
114             return new DynamoDBBigDecimalItem(name,
115                     ((OpenClosedType) state) == OpenClosedType.OPEN ? BigDecimal.ONE : BigDecimal.ZERO, time);
116         } else if (state instanceof UpDownType) {
117             return new DynamoDBBigDecimalItem(name,
118                     ((UpDownType) state) == UpDownType.UP ? BigDecimal.ONE : BigDecimal.ZERO, time);
119         } else if (state instanceof DateTimeType) {
120             return new DynamoDBStringItem(name, ((DateTimeType) state).getZonedDateTime().format(DATEFORMATTER), time);
121         } else if (state instanceof UnDefType) {
122             return new DynamoDBStringItem(name, UNDEFINED_PLACEHOLDER, time);
123         } else if (state instanceof StringListType) {
124             return new DynamoDBStringItem(name, state.toFullString(), time);
125         } else {
126             // HSBType, PointType, PlayPauseType and StringType
127             return new DynamoDBStringItem(name, state.toFullString(), time);
128         }
129     }
130
131     @Override
132     public HistoricItem asHistoricItem(final Item item) {
133         final State[] state = new State[1];
134         accept(new DynamoDBItemVisitor() {
135
136             @Override
137             public void visit(DynamoDBStringItem dynamoStringItem) {
138                 if (item instanceof ColorItem) {
139                     state[0] = new HSBType(dynamoStringItem.getState());
140                 } else if (item instanceof LocationItem) {
141                     state[0] = new PointType(dynamoStringItem.getState());
142                 } else if (item instanceof PlayerItem) {
143                     String value = dynamoStringItem.getState();
144                     try {
145                         state[0] = PlayPauseType.valueOf(value);
146                     } catch (IllegalArgumentException e) {
147                         state[0] = RewindFastforwardType.valueOf(value);
148                     }
149                 } else if (item instanceof DateTimeItem) {
150                     try {
151                         // Parse ZoneDateTime from string. DATEFORMATTER assumes UTC in case it is not clear
152                         // from the string (should be).
153                         // We convert to default/local timezone for user convenience (e.g. display)
154                         state[0] = new DateTimeType(ZonedDateTime.parse(dynamoStringItem.getState(), DATEFORMATTER)
155                                 .withZoneSameInstant(ZoneId.systemDefault()));
156                     } catch (DateTimeParseException e) {
157                         logger.warn("Failed to parse {} as date. Outputting UNDEF instead",
158                                 dynamoStringItem.getState());
159                         state[0] = UnDefType.UNDEF;
160                     }
161                 } else if (dynamoStringItem.getState().equals(UNDEFINED_PLACEHOLDER)) {
162                     state[0] = UnDefType.UNDEF;
163                 } else if (item instanceof CallItem) {
164                     String parts = dynamoStringItem.getState();
165                     String[] strings = parts.split(",");
166                     String orig = strings[0];
167                     String dest = strings[1];
168                     state[0] = new StringListType(orig, dest);
169                 } else {
170                     state[0] = new StringType(dynamoStringItem.getState());
171                 }
172             }
173
174             @Override
175             public void visit(DynamoDBBigDecimalItem dynamoBigDecimalItem) {
176                 if (item instanceof NumberItem) {
177                     state[0] = new DecimalType(dynamoBigDecimalItem.getState());
178                 } else if (item instanceof DimmerItem) {
179                     state[0] = new PercentType(dynamoBigDecimalItem.getState());
180                 } else if (item instanceof SwitchItem) {
181                     state[0] = dynamoBigDecimalItem.getState().compareTo(BigDecimal.ONE) == 0 ? OnOffType.ON
182                             : OnOffType.OFF;
183                 } else if (item instanceof ContactItem) {
184                     state[0] = dynamoBigDecimalItem.getState().compareTo(BigDecimal.ONE) == 0 ? OpenClosedType.OPEN
185                             : OpenClosedType.CLOSED;
186                 } else if (item instanceof RollershutterItem) {
187                     state[0] = new PercentType(dynamoBigDecimalItem.getState());
188                 } else {
189                     logger.warn("Not sure how to convert big decimal item {} to type {}. Using StringType as fallback",
190                             dynamoBigDecimalItem.getName(), item.getClass());
191                     state[0] = new StringType(dynamoBigDecimalItem.getState().toString());
192                 }
193             }
194         });
195         return new DynamoDBHistoricItem(getName(), state[0], getTime());
196     }
197
198     /**
199      * We define all getter and setters in the child class implement those. Having the getter
200      * and setter implementations here in the parent class does not work with introspection done by AWS SDK (1.11.56).
201      */
202
203     /*
204      * (non-Javadoc)
205      *
206      * @see org.openhab.persistence.dynamodb.internal.DynamoItem#accept(org.openhab.persistence.dynamodb.internal.
207      * DynamoItemVisitor)
208      */
209     @Override
210     public abstract void accept(DynamoDBItemVisitor visitor);
211
212     @Override
213     public String toString() {
214         return DateFormat.getDateTimeInstance().format(time) + ": " + name + " -> " + state.toString();
215     }
216 }