diff --git a/core/src/main/java/org/jclouds/date/internal/SimpleDateFormatDateService.java b/core/src/main/java/org/jclouds/date/internal/SimpleDateFormatDateService.java index aba0e8533d..7a41397e50 100755 --- a/core/src/main/java/org/jclouds/date/internal/SimpleDateFormatDateService.java +++ b/core/src/main/java/org/jclouds/date/internal/SimpleDateFormatDateService.java @@ -142,7 +142,7 @@ public class SimpleDateFormatDateService implements DateService { public static final Pattern NANOS_TO_MILLIS_PATTERN = Pattern .compile(".*[0-9][0-9][0-9][0-9][0-9][0-9]"); - public static final Pattern TZ_PATTERN = Pattern.compile(".*[+-][0-9][0-9]:[0-9][0-9]"); + public static final Pattern TZ_PATTERN = Pattern.compile(".*[+-][0-9][0-9]:?[0-9][0-9]"); private String trimNanosToMillis(String toParse) { if (NANOS_TO_MILLIS_PATTERN.matcher(toParse).matches()) diff --git a/core/src/main/java/org/jclouds/http/functions/UnwrapOnlyNestedJsonValueInSet.java b/core/src/main/java/org/jclouds/http/functions/UnwrapOnlyNestedJsonValueInSet.java new file mode 100644 index 0000000000..5119797240 --- /dev/null +++ b/core/src/main/java/org/jclouds/http/functions/UnwrapOnlyNestedJsonValueInSet.java @@ -0,0 +1,52 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ + +package org.jclouds.http.functions; + +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.http.HttpResponse; + +import com.google.common.base.Function; +import com.google.common.collect.Iterables; + +/** + * @author Adrian Cole + */ +@Singleton +public class UnwrapOnlyNestedJsonValueInSet implements Function { + + private final UnwrapOnlyNestedJsonValue> json; + + @Inject + UnwrapOnlyNestedJsonValueInSet(UnwrapOnlyNestedJsonValue> json) { + this.json = json; + } + + @Override + public T apply(HttpResponse arg0) { + Set set = json.apply(arg0); + if (set == null || set.size() == 0) + return null; + return Iterables.getOnlyElement(set); + } +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/rest/annotations/Unwrap.java b/core/src/main/java/org/jclouds/rest/annotations/Unwrap.java index 8015ce7995..04bbd1db7e 100644 --- a/core/src/main/java/org/jclouds/rest/annotations/Unwrap.java +++ b/core/src/main/java/org/jclouds/rest/annotations/Unwrap.java @@ -24,9 +24,10 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Retention; import java.lang.annotation.Target; +import java.util.Map; /** - * Unwraps the only value in the json reponse + * Unwraps the only value in a nested json reponse * * ex. { "foo" :"bar" } becomes "bar" * @@ -44,10 +45,27 @@ public @interface Unwrap { * * ex. if (2) * - * { "foo" : {"bar" : "baz} } becomes "baz" + * { "foo" : {"bar" : ["baz"]} } becomes ["baz"] * * @return nestingLevel */ int depth() default 1; + /** + * final collection type + * + * ex. if depth(2), edgeCollection(Map.class) + * + * { "foo" : {"bar" : ["baz"]} } becomes ["baz"] + * + * ex. if depth(3), edgeCollection(Set.class) + * + * { "foo" : {"bar" : ["baz"]} } becomes "baz" + * + *

Note

only Map and Set are valid + * + * @return + */ + Class edgeCollection() default Map.class; + } diff --git a/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java b/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java index ae3a8854c6..6e3c078879 100755 --- a/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java +++ b/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java @@ -88,6 +88,7 @@ import org.jclouds.http.functions.ReturnStringIf2xx; import org.jclouds.http.functions.ReturnTrueIf2xx; import org.jclouds.http.functions.UnwrapOnlyJsonValue; import org.jclouds.http.functions.UnwrapOnlyNestedJsonValue; +import org.jclouds.http.functions.UnwrapOnlyNestedJsonValueInSet; import org.jclouds.http.functions.ParseSax.HandlerWithResult; import org.jclouds.http.options.HttpRequestOptions; import org.jclouds.http.utils.ModifyRequest; @@ -785,13 +786,17 @@ public class RestAnnotationProcessor { } ParameterizedType parserType; if (method.isAnnotationPresent(Unwrap.class)) { - int nestingLevel = method.getAnnotation(Unwrap.class).depth(); - if (nestingLevel == 1) + int depth = method.getAnnotation(Unwrap.class).depth(); + Class edgeCollection = method.getAnnotation(Unwrap.class).edgeCollection(); + if (depth == 1 && edgeCollection == Map.class) parserType = Types.newParameterizedType(UnwrapOnlyJsonValue.class, returnVal); - else if (nestingLevel == 2) + else if (depth == 2 && edgeCollection == Map.class) parserType = Types.newParameterizedType(UnwrapOnlyNestedJsonValue.class, returnVal); + else if (depth == 3 && edgeCollection == Set.class) + parserType = Types.newParameterizedType(UnwrapOnlyNestedJsonValueInSet.class, returnVal); else - throw new IllegalStateException("nesting level " + nestingLevel + " not yet supported for @Unwrap"); + throw new IllegalStateException(String.format( + "depth(%d) edgeCollection(%s) not yet supported for @Unwrap", depth, edgeCollection)); } else { parserType = Types.newParameterizedType(ParseJson.class, returnVal); } diff --git a/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java b/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java index caca04cdc1..436529155f 100755 --- a/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java +++ b/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java @@ -98,6 +98,7 @@ import org.jclouds.http.functions.ReturnStringIf2xx; import org.jclouds.http.functions.ReturnTrueIf2xx; import org.jclouds.http.functions.UnwrapOnlyJsonValue; import org.jclouds.http.functions.UnwrapOnlyNestedJsonValue; +import org.jclouds.http.functions.UnwrapOnlyNestedJsonValueInSet; import org.jclouds.http.internal.PayloadEnclosingImpl; import org.jclouds.http.options.BaseHttpRequestOptions; import org.jclouds.http.options.GetOptions; @@ -771,6 +772,12 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { @Consumes(MediaType.APPLICATION_JSON) ListenableFuture> testUnwrapDepth2(); + @GET + @Path("/") + @Unwrap(depth = 3, edgeCollection = Set.class) + @Consumes(MediaType.APPLICATION_JSON) + ListenableFuture testUnwrapDepth3(); + @Target( { ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @HttpMethod("ROWDY") @@ -955,6 +962,50 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { newStringPayload("{\"runit\":{\"runit\":[\"0.7.0\",\"0.7.1\"]}}"))), ImmutableSet.of("0.7.0", "0.7.1")); } + @SuppressWarnings("unchecked") + public void testUnwrapDepth3() throws SecurityException, NoSuchMethodException, IOException { + Method method = TestPut.class.getMethod("testUnwrapDepth3"); + HttpRequest request = factory(TestPut.class).createRequest(method); + + assertResponseParserClassEquals(method, request, UnwrapOnlyNestedJsonValueInSet.class); + // now test that it works! + + Function> parser = (Function>) RestAnnotationProcessor + .createResponseParser(parserFactory, injector, method, request); + + assertEquals(parser.apply(new HttpResponse(200, "ok", newStringPayload("{\"runit\":{\"runit\":[\"0.7.0\"]}}"))), + "0.7.0"); + } + + @SuppressWarnings("unchecked") + public void testUnwrapDepth3None() throws SecurityException, NoSuchMethodException, IOException { + Method method = TestPut.class.getMethod("testUnwrapDepth3"); + HttpRequest request = factory(TestPut.class).createRequest(method); + + assertResponseParserClassEquals(method, request, UnwrapOnlyNestedJsonValueInSet.class); + // now test that it works! + + Function> parser = (Function>) RestAnnotationProcessor + .createResponseParser(parserFactory, injector, method, request); + + assertEquals(parser.apply(new HttpResponse(200, "ok", newStringPayload("{\"runit\":{\"runit\":[]}}"))), null); + } + + @SuppressWarnings("unchecked") + @Test(expectedExceptions = IllegalArgumentException.class) + public void testUnwrapDepth3TooMany() throws SecurityException, NoSuchMethodException, IOException { + Method method = TestPut.class.getMethod("testUnwrapDepth3"); + HttpRequest request = factory(TestPut.class).createRequest(method); + + assertResponseParserClassEquals(method, request, UnwrapOnlyNestedJsonValueInSet.class); + // now test that it works! + + Function> parser = (Function>) RestAnnotationProcessor + .createResponseParser(parserFactory, injector, method, request); + + parser.apply(new HttpResponse(200, "ok", newStringPayload("{\"runit\":{\"runit\":[\"0.7.0\",\"0.7.1\"]}}"))); + } + static class TestRequestFilter1 implements HttpRequestFilter { public HttpRequest filter(HttpRequest request) throws HttpException { return request;