/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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 android.hardware.camera2.marshal;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.utils.TypeReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* Registry of supported marshalers; add new query-able marshalers or lookup existing ones.</p>
*/
public class MarshalRegistry {
/**
* Register a marshal queryable for the managed type {@code T}.
*
* <p>Multiple marshal queryables for the same managed type {@code T} may be registered;
* this is desirable if they support different native types (e.g. marshaler 1 supports
* {@code Integer <-> TYPE_INT32}, marshaler 2 supports {@code Integer <-> TYPE_BYTE}.</p>
*
* @param queryable a non-{@code null} marshal queryable that supports marshaling {@code T}
*/
public static <T> void registerMarshalQueryable(MarshalQueryable<T> queryable) {
sRegisteredMarshalQueryables.add(queryable);
}
/**
* Lookup a marshaler between {@code T} and {@code nativeType}.
*
* <p>Marshalers are looked up in the order they were registered; earlier registered
* marshal queriers get priority.</p>
*
* @param typeToken The compile-time type reference for {@code T}
* @param nativeType The native type, e.g. {@link CameraMetadataNative#TYPE_BYTE TYPE_BYTE}
* @return marshaler a non-{@code null} marshaler that supports marshaling the type combo
*
* @throws UnsupportedOperationException If no marshaler matching the args could be found
*/
@SuppressWarnings("unchecked")
public static <T> Marshaler<T> getMarshaler(TypeReference<T> typeToken, int nativeType) {
// TODO: can avoid making a new token each time by code-genning
// the list of type tokens and native types from the keys (at the call sites)
MarshalToken<T> marshalToken = new MarshalToken<T>(typeToken, nativeType);
/*
* Marshalers are instantiated lazily once they are looked up; successive lookups
* will not instantiate new marshalers.
*/
Marshaler<T> marshaler =
(Marshaler<T>) sMarshalerMap.get(marshalToken);
if (sRegisteredMarshalQueryables.size() == 0) {
throw new AssertionError("No available query marshalers registered");
}
if (marshaler == null) {
// Query each marshaler to see if they support the native/managed type combination
for (MarshalQueryable<?> potentialMarshaler : sRegisteredMarshalQueryables) {
MarshalQueryable<T> castedPotential =
(MarshalQueryable<T>)potentialMarshaler;
if (castedPotential.isTypeMappingSupported(typeToken, nativeType)) {
marshaler = castedPotential.createMarshaler(typeToken, nativeType);
break;
}
}
if (marshaler == null) {
throw new UnsupportedOperationException(
"Could not find marshaler that matches the requested " +
"combination of type reference " +
typeToken + " and native type " +
MarshalHelpers.toStringNativeType(nativeType));
}
// Only put when no cached version exists to avoid +0.5ms lookup per call.
sMarshalerMap.put(marshalToken, marshaler);
}
return marshaler;
}
private static class MarshalToken<T> {
public MarshalToken(TypeReference<T> typeReference, int nativeType) {
this.typeReference = typeReference;
this.nativeType = nativeType;
this.hash = typeReference.hashCode() ^ nativeType;
}
final TypeReference<T> typeReference;
final int nativeType;
private final int hash;
@Override
public boolean equals(Object other) {
if (other instanceof MarshalToken<?>) {
MarshalToken<?> otherToken = (MarshalToken<?>)other;
return typeReference.equals(otherToken.typeReference) &&
nativeType == otherToken.nativeType;
}
return false;
}
@Override
public int hashCode() {
return hash;
}
}
private static List<MarshalQueryable<?>> sRegisteredMarshalQueryables =
new ArrayList<MarshalQueryable<?>>();
private static HashMap<MarshalToken<?>, Marshaler<?>> sMarshalerMap =
new HashMap<MarshalToken<?>, Marshaler<?>>();
private MarshalRegistry() {
throw new AssertionError();
}
}