View Javadoc

1   /**
2    * Licensed to jclouds, Inc. (jclouds) under one or more
3    * contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  jclouds licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.jclouds.http.functions;
20  
21  import static com.google.common.base.Preconditions.checkNotNull;
22  
23  import java.io.IOException;
24  import java.io.InputStreamReader;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Set;
28  import java.util.concurrent.atomic.AtomicReference;
29  
30  import org.jclouds.http.HttpResponse;
31  import org.jclouds.json.internal.GsonWrapper;
32  
33  import com.google.common.base.Function;
34  import com.google.common.collect.ImmutableList;
35  import com.google.common.collect.ImmutableMap;
36  import com.google.common.collect.ImmutableSet;
37  import com.google.common.io.Closeables;
38  import com.google.gson.stream.JsonReader;
39  import com.google.gson.stream.JsonToken;
40  import com.google.inject.TypeLiteral;
41  
42  /**
43   * @author Adrian Cole
44   */
45  public class ParseFirstJsonValueNamed<T> implements Function<HttpResponse, T> {
46  
47     private final GsonWrapper json;
48     private final TypeLiteral<T> type;
49     private final String name;
50  
51     public ParseFirstJsonValueNamed(GsonWrapper json, TypeLiteral<T> type, String name) {
52        this.json = checkNotNull(json, "json");
53        this.type = checkNotNull(type, "type");
54        this.name = checkNotNull(name, "name");
55     }
56  
57     @Override
58     public T apply(HttpResponse arg0) {
59        if (arg0.getPayload() == null)
60           return nothing();
61        JsonReader reader = null;
62        try {
63           reader = new JsonReader(new InputStreamReader(arg0.getPayload().getInput()));
64           // in case keys are not in quotes
65           reader.setLenient(true);
66           AtomicReference<String> name = new AtomicReference<String>();
67           JsonToken token = reader.peek();
68           for (; token != JsonToken.END_DOCUMENT && nnn(this.name, reader, token, name); token = skipAndPeek(token,
69                 reader))
70              ;
71           if (name.get().equals(this.name)) {
72              return json.delegate().<T>fromJson(reader, type.getType());
73           } else {
74              return nothing();
75           }
76        } catch (IOException e) {
77           throw new RuntimeException(String.format(
78                 "error reading from stream, parsing object named %s from http response %s", this.name, arg0), e);
79        } finally {
80           Closeables.closeQuietly(reader);
81           arg0.getPayload().release();
82        }
83     }
84  
85     @SuppressWarnings("unchecked")
86     protected T nothing() {
87        if (type.getRawType().isAssignableFrom(Set.class))
88           return (T) ImmutableSet.of();
89        else if (type.getRawType().isAssignableFrom(List.class))
90           return (T) ImmutableList.of();
91        else if (type.getRawType().isAssignableFrom(Map.class))
92           return (T) ImmutableMap.of();
93        return null;
94     }
95  
96     protected boolean nnn(String toFind, JsonReader reader, JsonToken token, AtomicReference<String> name)
97           throws IOException {
98        if (token == JsonToken.NAME) {
99           String name2 = reader.nextName();
100          if (toFind.equals(name2)) {
101             name.set(name2);
102             return false;
103          }
104       }
105       return true;
106 
107    }
108 
109    public JsonToken skipAndPeek(JsonToken token, JsonReader reader) throws IOException {
110       switch (token) {
111       case BEGIN_ARRAY:
112          reader.beginArray();
113          break;
114       case END_ARRAY:
115          reader.endArray();
116          break;
117       case BEGIN_OBJECT:
118          reader.beginObject();
119          break;
120       case END_OBJECT:
121          reader.endObject();
122          break;
123       case NAME:
124          // NOTE that we have already advanced on NAME in the eval block;
125          break;
126       case STRING:
127          reader.nextString();
128          break;
129       case NUMBER:
130          reader.nextString();
131          break;
132       case BOOLEAN:
133          reader.nextBoolean();
134          break;
135       case NULL:
136          reader.nextNull();
137          break;
138       case END_DOCUMENT:
139          break;
140       }
141       return reader.peek();
142    }
143 }