/*
* 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.
*/
public class MarshalRegistry {
/**
* Register a marshal queryable for the managed type {@code T}.
*
* 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}.
*
* @param queryable a non-{@code null} marshal queryable that supports marshaling {@code T}
*/
public static void registerMarshalQueryable(MarshalQueryable queryable) {
synchronized(sMarshalLock) {
sRegisteredMarshalQueryables.add(queryable);
}
}
/**
* Lookup a marshaler between {@code T} and {@code nativeType}.
*
* Marshalers are looked up in the order they were registered; earlier registered
* marshal queriers get priority.
*
* @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 Marshaler getMarshaler(TypeReference typeToken, int nativeType) {
synchronized(sMarshalLock) {
// 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 marshalToken = new MarshalToken(typeToken, nativeType);
/*
* Marshalers are instantiated lazily once they are looked up; successive lookups
* will not instantiate new marshalers.
*/
Marshaler marshaler =
(Marshaler) sMarshalerMap.get(marshalToken);
if (marshaler == null) {
if (sRegisteredMarshalQueryables.size() == 0) {
throw new AssertionError("No available query marshalers registered");
}
// Query each marshaler to see if they support the native/managed type combination
for (MarshalQueryable> potentialMarshaler : sRegisteredMarshalQueryables) {
MarshalQueryable castedPotential =
(MarshalQueryable)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 {
public MarshalToken(TypeReference typeReference, int nativeType) {
this.typeReference = typeReference;
this.nativeType = nativeType;
this.hash = typeReference.hashCode() ^ nativeType;
}
final TypeReference 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;
}
}
// Control access to the static data structures below
private static final Object sMarshalLock = new Object();
private static final List> sRegisteredMarshalQueryables =
new ArrayList>();
private static final HashMap, Marshaler>> sMarshalerMap =
new HashMap, Marshaler>>();
private MarshalRegistry() {
throw new AssertionError();
}
}