diff --git a/graphsdk/src/androidTest/java/com/microsoft/graph/serializer/DateOnlyTests.java b/graphsdk/src/androidTest/java/com/microsoft/graph/serializer/DateOnlyTests.java new file mode 100644 index 00000000..1d79cbd6 --- /dev/null +++ b/graphsdk/src/androidTest/java/com/microsoft/graph/serializer/DateOnlyTests.java @@ -0,0 +1,32 @@ +package com.microsoft.graph.serializer; + +import android.test.AndroidTestCase; + +import com.microsoft.graph.model.DateOnly; + +public class DateOnlyTests extends AndroidTestCase { + + public void testDateSerializer() throws Exception { + String strDate = DateOnly.parse("2016-04-27").toString(); + assertEquals("2016-04-27", strDate); + } + + public void testDateSerializerIndefinite() throws Exception { + String strDate = DateOnly.parse("0001-01-01").toString(); + assertEquals("0001-01-01", strDate); + } + + public void testDateDeserializer() throws Exception { + DateOnly date = DateOnly.parse("2016-04-27"); + assertEquals(2016, date.getYear()); + assertEquals(4, date.getMonth()); + assertEquals(27, date.getDay()); + } + + public void testDateDeserializerIndefinite() throws Exception{ + DateOnly date = DateOnly.parse("0001-01-01"); + assertEquals(1, date.getYear()); + assertEquals(1, date.getMonth()); + assertEquals(1, date.getDay()); + } +} diff --git a/graphsdk/src/androidTest/java/com/microsoft/graph/serializer/DefaultSerializerTests.java b/graphsdk/src/androidTest/java/com/microsoft/graph/serializer/DefaultSerializerTests.java index a1119b29..4202d356 100644 --- a/graphsdk/src/androidTest/java/com/microsoft/graph/serializer/DefaultSerializerTests.java +++ b/graphsdk/src/androidTest/java/com/microsoft/graph/serializer/DefaultSerializerTests.java @@ -22,18 +22,22 @@ package com.microsoft.graph.serializer; +import android.test.AndroidTestCase; + import com.microsoft.graph.extensions.Drive; +import com.microsoft.graph.extensions.RecurrenceRangeType; +import com.microsoft.graph.generated.BaseRecurrenceRange; import com.microsoft.graph.logger.DefaultLogger; - -import android.test.AndroidTestCase; +import com.microsoft.graph.model.DateOnly; /** * Test cases for the {@see DefaultSerializer} */ -public class DefaultSerializerTests extends AndroidTestCase { +public class DefaultSerializerTests extends AndroidTestCase { /** * Make sure that deserializing a Drive also returns members from BaseDrive + * * @throws Exception If there is an exception during the test */ public void testDriveDeserialization() throws Exception { @@ -46,4 +50,36 @@ public void testDriveDeserialization() throws Exception { assertEquals("8bf6ae90006c4a4c", result.id); } + + public void testRecurrenceRangeDeserialization() throws Exception { + final DefaultSerializer serializer = new DefaultSerializer(new DefaultLogger()); + String source = "{\n" + + " \"type\": \"noEnd\",\n" + + " \"startDate\": \"2016-04-27\",\n" + + " \"endDate\": \"0001-01-01\",\n" + + " \"recurrenceTimeZone\": \"China Standard Time\",\n" + + " \"numberOfOccurrences\": 0\n" + + "}"; + BaseRecurrenceRange baseRecurrenceRange = serializer.deserializeObject(source, BaseRecurrenceRange.class); + assertNotNull(source); + assertEquals(baseRecurrenceRange.type, RecurrenceRangeType.noEnd); + assertEquals("2016-04-27", baseRecurrenceRange.startDate.toString()); + assertEquals("0001-01-01", baseRecurrenceRange.endDate.toString()); + assertEquals("China Standard Time", baseRecurrenceRange.recurrenceTimeZone); + assertEquals(Integer.valueOf(0), baseRecurrenceRange.numberOfOccurrences); + } + + public void testRecurrenceRangeSerialization() throws Exception { + final String expected = "{\"endDate\":\"2016-05-25\",\"numberOfOccurrences\":4,\"@odata.type\":\"microsoft.graph.recurrenceRange\",\"recurrenceTimeZone\":\"PST\",\"startDate\":\"2016-04-25\",\"type\":\"endDate\"}"; + final DefaultSerializer serializer = new DefaultSerializer(new DefaultLogger()); + BaseRecurrenceRange brr = new BaseRecurrenceRange(); + brr.type = RecurrenceRangeType.endDate; + brr.startDate = new DateOnly(2016, 4, 25); + brr.endDate = new DateOnly(2016, 5, 25); + brr.recurrenceTimeZone = "PST"; + brr.numberOfOccurrences = 4; + String jsonOut = serializer.serializeObject(brr); + assertNotNull(jsonOut); + assertEquals(jsonOut, expected); + } } diff --git a/graphsdk/src/main/java/com/microsoft/graph/generated/BaseRecurrenceRange.java b/graphsdk/src/main/java/com/microsoft/graph/generated/BaseRecurrenceRange.java index d05f9301..8af39aaa 100644 --- a/graphsdk/src/main/java/com/microsoft/graph/generated/BaseRecurrenceRange.java +++ b/graphsdk/src/main/java/com/microsoft/graph/generated/BaseRecurrenceRange.java @@ -42,13 +42,13 @@ public BaseRecurrenceRange(){ * The Start Date. */ @SerializedName("startDate") - public java.util.Calendar startDate; + public com.microsoft.graph.model.DateOnly startDate; /** * The End Date. */ @SerializedName("endDate") - public java.util.Calendar endDate; + public com.microsoft.graph.model.DateOnly endDate; /** * The Recurrence Time Zone. diff --git a/graphsdk/src/main/java/com/microsoft/graph/model/DateOnly.java b/graphsdk/src/main/java/com/microsoft/graph/model/DateOnly.java new file mode 100644 index 00000000..b2facbe8 --- /dev/null +++ b/graphsdk/src/main/java/com/microsoft/graph/model/DateOnly.java @@ -0,0 +1,106 @@ +package com.microsoft.graph.model; + + +import java.text.ParseException; +import java.util.Locale; + +/** + * A timezone-nonspecific date + */ +public class DateOnly { + + /** + * The year + */ + private final int mYear; + + /** + * The month + */ + private final int mMonth; + + /** + * The day + */ + private final int mDay; + + /** + * Constructs a timezone-nonspecific DateOnly + * + * @param dateStr date string of the form yyyy-mm-dd + * @return the parsed DateOnly instance + * @exception ParseException If there was a failure parsing the dateStr + */ + public static DateOnly parse(final String dateStr) throws ParseException { + // break the date up into its constituent parts + String[] dateInfo = dateStr.split("-"); + + // validate the split date string + final int expectedLength = 3; + if (dateInfo.length != expectedLength) { + throw new ParseException( + "Expected datestring format 'yyyy-mm-dd' but found: " + dateStr, 0 + ); + } + + // array indices for date parsing + final int indYear = 0; + final int indMonth = 1; + final int indDay = 2; + + // unpack this array + int year = Integer.parseInt(dateInfo[indYear]); + int month = Integer.parseInt(dateInfo[indMonth]); + int day = Integer.parseInt(dateInfo[indDay]); + + return new DateOnly(year, month, day); + } + + /** + * Constructs a timezone-nonspecific DateOnly + * + * @param year the year + * @param month 1-indexed month value (Jan == 1) + * @param day day of the month + */ + public DateOnly(final int year, final int month, final int day) { + mYear = year; + mMonth = month; + mDay = day; + } + + /** + * Gets the year + * + * @return the year + */ + public int getYear() { + return mYear; + } + + /** + * Gets the month + * + * @return the month + */ + public int getMonth() { + return mMonth; + } + + /** + * Gets the day + * + * @return the day + */ + public int getDay() { + return mDay; + } + + @Override + public String toString() { + return String.format( + Locale.ROOT, + "%04d-%02d-%02d", mYear, mMonth, mDay + ); + } +} diff --git a/graphsdk/src/main/java/com/microsoft/graph/serializer/GsonFactory.java b/graphsdk/src/main/java/com/microsoft/graph/serializer/GsonFactory.java index fbc5446c..eeb80b93 100644 --- a/graphsdk/src/main/java/com/microsoft/graph/serializer/GsonFactory.java +++ b/graphsdk/src/main/java/com/microsoft/graph/serializer/GsonFactory.java @@ -1,16 +1,16 @@ // ------------------------------------------------------------------------------ // Copyright (c) 2015 Microsoft Corporation -// +// // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -31,8 +31,8 @@ import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; - import com.microsoft.graph.logger.ILogger; +import com.microsoft.graph.model.DateOnly; import java.lang.reflect.Type; import java.text.ParseException; @@ -52,12 +52,13 @@ private GsonFactory() { /** * Creates an instance of Gson. + * * @param logger The logger. * @return The new instance. */ public static Gson getGsonInstance(final ILogger logger) { - final JsonSerializer dateJsonSerializer = new JsonSerializer() { + final JsonSerializer calendarJsonSerializer = new JsonSerializer() { @Override public JsonElement serialize(final Calendar src, final Type typeOfSrc, @@ -74,7 +75,7 @@ public JsonElement serialize(final Calendar src, } }; - final JsonDeserializer dateJsonDeserializer = new JsonDeserializer() { + final JsonDeserializer calendarJsonDeserializer = new JsonDeserializer() { @Override public Calendar deserialize(final JsonElement json, final Type typeOfT, @@ -111,13 +112,43 @@ public JsonElement serialize(final byte[] src, final JsonDeserializer byteArrayJsonDeserializer = new JsonDeserializer() { @Override public byte[] deserialize(final JsonElement json, + final Type typeOfT, + final JsonDeserializationContext context) throws JsonParseException { + if (json == null) { + return null; + } + try { + return ByteArraySerializer.deserialize(json.getAsString()); + } catch (final ParseException e) { + logger.logError("Parsing issue on " + json.getAsString(), e); + return null; + } + } + }; + + final JsonSerializer dateJsonSerializer = new JsonSerializer() { + @Override + public JsonElement serialize(final DateOnly src, + final Type typeOfSrc, + final JsonSerializationContext context) { + if (src == null) { + return null; + } + return new JsonPrimitive(src.toString()); + } + }; + + final JsonDeserializer dateJsonDeserializer = new JsonDeserializer() { + @Override + public DateOnly deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException { if (json == null) { return null; } + try { - return ByteArraySerializer.deserialize(json.getAsString()); + return DateOnly.parse(json.getAsString()); } catch (final ParseException e) { logger.logError("Parsing issue on " + json.getAsString(), e); return null; @@ -126,12 +157,14 @@ public byte[] deserialize(final JsonElement json, }; return new GsonBuilder() - .registerTypeAdapter(Calendar.class, dateJsonSerializer) - .registerTypeAdapter(Calendar.class, dateJsonDeserializer) - .registerTypeAdapter(GregorianCalendar.class, dateJsonSerializer) - .registerTypeAdapter(GregorianCalendar.class, dateJsonDeserializer) + .registerTypeAdapter(Calendar.class, calendarJsonSerializer) + .registerTypeAdapter(Calendar.class, calendarJsonDeserializer) + .registerTypeAdapter(GregorianCalendar.class, calendarJsonSerializer) + .registerTypeAdapter(GregorianCalendar.class, calendarJsonDeserializer) .registerTypeAdapter(byte[].class, byteArrayJsonDeserializer) .registerTypeAdapter(byte[].class, byteArrayJsonSerializer) + .registerTypeAdapter(DateOnly.class, dateJsonSerializer) + .registerTypeAdapter(DateOnly.class, dateJsonDeserializer) .registerTypeAdapterFactory(new FallBackEnumTypeAdapter()) .create(); }