| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 static com.google.common.base.Preconditions.checkNotNull; |
| |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.atomic.AtomicReference; |
| |
| import javax.annotation.Resource; |
| |
| import org.jclouds.http.HttpResponse; |
| import org.jclouds.json.internal.GsonWrapper; |
| import org.jclouds.logging.Logger; |
| import org.jclouds.util.Closeables2; |
| |
| import com.google.common.base.Function; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.util.concurrent.Atomics; |
| import com.google.gson.stream.JsonReader; |
| import com.google.gson.stream.JsonToken; |
| import com.google.inject.TypeLiteral; |
| |
| public class ParseFirstJsonValueNamed<T> implements Function<HttpResponse, T> { |
| |
| @Resource |
| protected Logger logger = Logger.NULL; |
| |
| private final GsonWrapper json; |
| private final TypeLiteral<T> type; |
| private final ImmutableSet<String> nameChoices; |
| |
| /** |
| * @param nameChoices |
| * tried in order, first match wins |
| */ |
| public ParseFirstJsonValueNamed(GsonWrapper json, TypeLiteral<T> type, String... nameChoices) { |
| this.json = checkNotNull(json, "json"); |
| this.type = checkNotNull(type, "type"); |
| this.nameChoices = ImmutableSet.copyOf(checkNotNull(nameChoices, "nameChoices")); |
| } |
| |
| @Override |
| public T apply(HttpResponse arg0) { |
| if (arg0.getPayload() == null) |
| return nothing(); |
| JsonReader reader = null; |
| try { |
| reader = new JsonReader(new InputStreamReader(arg0.getPayload().getInput())); |
| // in case keys are not in quotes |
| reader.setLenient(true); |
| AtomicReference<String> name = Atomics.newReference(); |
| JsonToken token = reader.peek(); |
| for (; token != JsonToken.END_DOCUMENT && nnn(reader, token, name); token = skipAndPeek(token, reader)) { |
| } |
| if (name.get() == null) { |
| logger.trace("did not object named %s in json from response %s", nameChoices, arg0); |
| return nothing(); |
| } else if (nameChoices.contains(name.get())) { |
| return json.delegate().<T> fromJson(reader, type.getType()); |
| } else { |
| return nothing(); |
| } |
| } catch (IOException e) { |
| throw new RuntimeException(String.format( |
| "error reading from stream, parsing object named %s from http response %s", nameChoices, arg0), e); |
| } finally { |
| Closeables2.closeQuietly(reader); |
| arg0.getPayload().release(); |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| private T nothing() { |
| if (type.getRawType().isAssignableFrom(Set.class)) |
| return (T) ImmutableSet.of(); |
| else if (type.getRawType().isAssignableFrom(List.class)) |
| return (T) ImmutableList.of(); |
| else if (type.getRawType().isAssignableFrom(Map.class)) |
| return (T) ImmutableMap.of(); |
| return null; |
| } |
| |
| private boolean nnn(JsonReader reader, JsonToken token, AtomicReference<String> name) throws IOException { |
| if (token == JsonToken.NAME) { |
| String name2 = reader.nextName(); |
| if (nameChoices.contains(name2)) { |
| name.set(name2); |
| return false; |
| } |
| } |
| return true; |
| |
| } |
| |
| private JsonToken skipAndPeek(JsonToken token, JsonReader reader) throws IOException { |
| switch (token) { |
| case BEGIN_ARRAY: |
| reader.beginArray(); |
| break; |
| case END_ARRAY: |
| reader.endArray(); |
| break; |
| case BEGIN_OBJECT: |
| reader.beginObject(); |
| break; |
| case END_OBJECT: |
| reader.endObject(); |
| break; |
| case NAME: |
| // NOTE that we have already advanced on NAME in the eval block; |
| break; |
| case STRING: |
| reader.nextString(); |
| break; |
| case NUMBER: |
| reader.nextString(); |
| break; |
| case BOOLEAN: |
| reader.nextBoolean(); |
| break; |
| case NULL: |
| reader.nextNull(); |
| break; |
| case END_DOCUMENT: |
| break; |
| } |
| return reader.peek(); |
| } |
| } |