1 /*
2  * Copyright 2013 FasterXML.com
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may
5  * not use this file except in compliance with the License. You may obtain
6  * a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the license for the specific language governing permissions and
14  * limitations under the license.
15  */

16
17 package com.fasterxml.jackson.datatype.jsr310.deser;
18
19 import java.io.IOException;
20 import java.time.DateTimeException;
21 import java.time.OffsetTime;
22 import java.time.ZoneOffset;
23 import java.time.format.DateTimeFormatter;
24
25 import com.fasterxml.jackson.annotation.JsonFormat;
26 import com.fasterxml.jackson.core.*;
27 import com.fasterxml.jackson.databind.*;
28
29 /**
30  * Deserializer for Java 8 temporal {@link OffsetTime}s.
31  *
32  * @author Nick Williams
33  */

34 public class OffsetTimeDeserializer extends JSR310DateTimeDeserializerBase<OffsetTime>
35 {
36     private static final long serialVersionUID = 1L;
37
38     public static final OffsetTimeDeserializer INSTANCE = new OffsetTimeDeserializer();
39
40     private OffsetTimeDeserializer() {
41         this(DateTimeFormatter.ISO_OFFSET_TIME);
42     }
43
44     protected OffsetTimeDeserializer(DateTimeFormatter dtf) {
45         super(OffsetTime.class, dtf);
46     }
47
48     /**
49      * Since 2.11
50      */

51     protected OffsetTimeDeserializer(OffsetTimeDeserializer base, Boolean leniency) {
52         super(base, leniency);
53     }
54
55     @Override
56     protected OffsetTimeDeserializer withDateFormat(DateTimeFormatter dtf) {
57         return new OffsetTimeDeserializer(dtf);
58     }
59
60     @Override
61     protected OffsetTimeDeserializer withLeniency(Boolean leniency) {
62         return new OffsetTimeDeserializer(this, leniency);
63     }
64
65     @Override
66     protected OffsetTimeDeserializer withShape(JsonFormat.Shape shape) { return this; }
67
68     @Override
69     public OffsetTime deserialize(JsonParser parser, DeserializationContext context) throws IOException
70     {
71         if (parser.hasToken(JsonToken.VALUE_STRING)) {
72             String string = parser.getText().trim();
73             if (string.length() == 0) {
74                 if (!isLenient()) {
75                     return _failForNotLenient(parser, context, JsonToken.VALUE_STRING);
76                 }
77                 return null;
78             }
79             try {
80                 return OffsetTime.parse(string, _formatter);
81             } catch (DateTimeException e) {
82                 return _handleDateTimeException(context, e, string);
83             }
84         }
85         if (!parser.isExpectedStartArrayToken()) {
86             if (parser.hasToken(JsonToken.VALUE_EMBEDDED_OBJECT)) {
87                 return (OffsetTime) parser.getEmbeddedObject();
88             }
89             if (parser.hasToken(JsonToken.VALUE_NUMBER_INT)) {
90                 _throwNoNumericTimestampNeedTimeZone(parser, context);
91             }
92             throw context.wrongTokenException(parser, handledType(), JsonToken.START_ARRAY,
93                     "Expected array or string.");
94         }
95         JsonToken t = parser.nextToken();
96         if (t != JsonToken.VALUE_NUMBER_INT) {
97             if (t == JsonToken.END_ARRAY) {
98                 return null;
99             }
100             if ((t == JsonToken.VALUE_STRING || t == JsonToken.VALUE_EMBEDDED_OBJECT)
101                     && context.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
102                 final OffsetTime parsed = deserialize(parser, context);
103                 if (parser.nextToken() != JsonToken.END_ARRAY) {
104                     handleMissingEndArrayForSingle(parser, context);
105                 }
106                 return parsed;            
107             }
108             context.reportInputMismatch(handledType(),
109                     "Unexpected token (%s) within Array, expected VALUE_NUMBER_INT",
110                     t);
111         }
112         int hour = parser.getIntValue();
113         int minute = parser.nextIntValue(-1);
114         if (minute == -1) {
115             t = parser.getCurrentToken();
116             if (t == JsonToken.END_ARRAY) {
117                 return null;
118             }
119             if (t != JsonToken.VALUE_NUMBER_INT) {
120                 _reportWrongToken(context, JsonToken.VALUE_NUMBER_INT, "minutes");
121             }
122             minute = parser.getIntValue();
123         }
124         int partialSecond = 0;
125         int second = 0;
126         if (parser.nextToken() == JsonToken.VALUE_NUMBER_INT) {
127             second = parser.getIntValue();
128             if (parser.nextToken() == JsonToken.VALUE_NUMBER_INT) {
129                 partialSecond = parser.getIntValue();
130                 if (partialSecond < 1_000 &&
131                         !context.isEnabled(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS)) {
132                     partialSecond *= 1_000_000; // value is milliseconds, convert it to nanoseconds
133                 }
134                 parser.nextToken();
135             }
136         }
137         if (parser.getCurrentToken() == JsonToken.VALUE_STRING) {
138             OffsetTime result = OffsetTime.of(hour, minute, second, partialSecond, ZoneOffset.of(parser.getText()));
139             if (parser.nextToken() != JsonToken.END_ARRAY) {
140                 _reportWrongToken(context, JsonToken.END_ARRAY, "timezone");
141             }
142             return result;
143         }
144         throw context.wrongTokenException(parser, handledType(), JsonToken.VALUE_STRING,
145                 "Expected string for TimeZone after numeric values");
146     }
147 }
148