/* * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file 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 com.amazonaws.protocol.json.internal; import com.amazonaws.SdkClientException; import com.amazonaws.annotation.SdkInternalApi; import com.amazonaws.protocol.MarshallLocation; import com.amazonaws.protocol.MarshallingType; import com.amazonaws.protocol.StructuredPojo; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; @SdkInternalApi public class MarshallerRegistry { private final Map<MarshallLocation, Map<MarshallingType, JsonMarshaller<?>>> marshallers; private final Set<MarshallingType<?>> marshallingTypes; private final Map<Class<?>, MarshallingType<?>> marshallingTypeCache; private MarshallerRegistry(Builder builder) { this.marshallers = builder.marshallers; this.marshallingTypes = builder.marshallingTypes; this.marshallingTypeCache = new HashMap<Class<?>, MarshallingType<?>>(marshallingTypes.size()); } public <T> JsonMarshaller<T> getMarshaller(MarshallLocation marshallLocation, T val) { return getMarshaller(marshallLocation, toMarshallingType(val)); } public <T> JsonMarshaller<T> getMarshaller(MarshallLocation marshallLocation, MarshallingType<T> marshallingType, T val) { return getMarshaller(marshallLocation, val == null ? MarshallingType.NULL : marshallingType); } @SuppressWarnings("unchecked") private <T> JsonMarshaller<T> getMarshaller(MarshallLocation marshallLocation, MarshallingType<?> marshallingType) { return (JsonMarshaller<T>) marshallers.get(marshallLocation).get(marshallingType); } @SuppressWarnings("unchecked") public <T> MarshallingType<T> toMarshallingType(T val) { if (val == null) { return (MarshallingType<T>) MarshallingType.NULL; } else if (val instanceof StructuredPojo) { // We don't want to cache every single POJO type so we make a special case of it here. return (MarshallingType<T>) MarshallingType.STRUCTURED; } else if (!marshallingTypeCache.containsKey(val.getClass())) { return (MarshallingType<T>) populateMarshallingTypeCache(val.getClass()); } return (MarshallingType<T>) marshallingTypeCache.get(val.getClass()); } private MarshallingType<?> populateMarshallingTypeCache(Class<?> clzz) { synchronized (marshallingTypeCache) { if (!marshallingTypeCache.containsKey(clzz)) { for (MarshallingType<?> marshallingType : marshallingTypes) { if (marshallingType.isDefaultMarshallerForType(clzz)) { marshallingTypeCache.put(clzz, marshallingType); return marshallingType; } } throw new SdkClientException("MarshallingType not found for class " + clzz); } } return marshallingTypeCache.get(clzz); } /** * @return Builder instance to construct a {@link MarshallerRegistry}. */ public static Builder builder() { return new Builder(); } /** * Builder for a {@link MarshallerRegistry}. */ public static final class Builder { private final Map<MarshallLocation, Map<MarshallingType, JsonMarshaller<?>>> marshallers = new HashMap<MarshallLocation, Map<MarshallingType, JsonMarshaller<?>>>(); private final Set<MarshallingType<?>> marshallingTypes = new HashSet<MarshallingType<?>>(); private Builder() { } public <T> Builder payloadMarshaller(MarshallingType<T> marshallingType, JsonMarshaller<T> marshaller) { return addMarshaller(MarshallLocation.PAYLOAD, marshallingType, marshaller); } public <T> Builder headerMarshaller(MarshallingType<T> marshallingType, JsonMarshaller<T> marshaller) { return addMarshaller(MarshallLocation.HEADER, marshallingType, marshaller); } public <T> Builder queryParamMarshaller(MarshallingType<T> marshallingType, JsonMarshaller<T> marshaller) { return addMarshaller(MarshallLocation.QUERY_PARAM, marshallingType, marshaller); } public <T> Builder pathParamMarshaller(MarshallingType<T> marshallingType, JsonMarshaller<T> marshaller) { return addMarshaller(MarshallLocation.PATH, marshallingType, marshaller); } public <T> Builder greedyPathParamMarshaller(MarshallingType<T> marshallingType, JsonMarshaller<T> marshaller) { return addMarshaller(MarshallLocation.GREEDY_PATH, marshallingType, marshaller); } private <T> Builder addMarshaller(MarshallLocation marshallLocation, MarshallingType<T> marshallingType, JsonMarshaller<T> marshaller) { marshallingTypes.add(marshallingType); if (!marshallers.containsKey(marshallLocation)) { marshallers.put(marshallLocation, new HashMap<MarshallingType, JsonMarshaller<?>>()); } marshallers.get(marshallLocation).put(marshallingType, marshaller); return this; } /** * @return An immutable {@link MarshallerRegistry} object. */ public MarshallerRegistry build() { return new MarshallerRegistry(this); } } }