1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.hardware.camera2.marshal;
17 
18 import android.hardware.camera2.impl.CameraMetadataNative;
19 import android.hardware.camera2.utils.TypeReference;
20 
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.List;
24 
25 /**
26  * Registry of supported marshalers; add new query-able marshalers or lookup existing ones.</p>
27  */
28 public class MarshalRegistry {
29 
30     /**
31      * Register a marshal queryable for the managed type {@code T}.
32      *
33      * <p>Multiple marshal queryables for the same managed type {@code T} may be registered;
34      * this is desirable if they support different native types (e.g. marshaler 1 supports
35      * {@code Integer <-> TYPE_INT32}, marshaler 2 supports {@code Integer <-> TYPE_BYTE}.</p>
36      *
37      * @param queryable a non-{@code null} marshal queryable that supports marshaling {@code T}
38      */
registerMarshalQueryable(MarshalQueryable<T> queryable)39     public static <T> void registerMarshalQueryable(MarshalQueryable<T> queryable) {
40         sRegisteredMarshalQueryables.add(queryable);
41     }
42 
43     /**
44      * Lookup a marshaler between {@code T} and {@code nativeType}.
45      *
46      * <p>Marshalers are looked up in the order they were registered; earlier registered
47      * marshal queriers get priority.</p>
48      *
49      * @param typeToken The compile-time type reference for {@code T}
50      * @param nativeType The native type, e.g. {@link CameraMetadataNative#TYPE_BYTE TYPE_BYTE}
51      * @return marshaler a non-{@code null} marshaler that supports marshaling the type combo
52      *
53      * @throws UnsupportedOperationException If no marshaler matching the args could be found
54      */
55     @SuppressWarnings("unchecked")
getMarshaler(TypeReference<T> typeToken, int nativeType)56     public static <T> Marshaler<T> getMarshaler(TypeReference<T> typeToken, int nativeType) {
57         // TODO: can avoid making a new token each time by code-genning
58         // the list of type tokens and native types from the keys (at the call sites)
59         MarshalToken<T> marshalToken = new MarshalToken<T>(typeToken, nativeType);
60 
61         /*
62          * Marshalers are instantiated lazily once they are looked up; successive lookups
63          * will not instantiate new marshalers.
64          */
65         Marshaler<T> marshaler =
66                 (Marshaler<T>) sMarshalerMap.get(marshalToken);
67 
68         if (sRegisteredMarshalQueryables.size() == 0) {
69             throw new AssertionError("No available query marshalers registered");
70         }
71 
72         if (marshaler == null) {
73             // Query each marshaler to see if they support the native/managed type combination
74             for (MarshalQueryable<?> potentialMarshaler : sRegisteredMarshalQueryables) {
75 
76                 MarshalQueryable<T> castedPotential =
77                         (MarshalQueryable<T>)potentialMarshaler;
78 
79                 if (castedPotential.isTypeMappingSupported(typeToken, nativeType)) {
80                     marshaler = castedPotential.createMarshaler(typeToken, nativeType);
81                     break;
82                 }
83             }
84 
85             if (marshaler == null) {
86                 throw new UnsupportedOperationException(
87                         "Could not find marshaler that matches the requested " +
88                                 "combination of type reference " +
89                                 typeToken + " and native type " +
90                                 MarshalHelpers.toStringNativeType(nativeType));
91             }
92 
93             // Only put when no cached version exists to avoid +0.5ms lookup per call.
94             sMarshalerMap.put(marshalToken, marshaler);
95         }
96 
97         return marshaler;
98     }
99 
100     private static class MarshalToken<T> {
MarshalToken(TypeReference<T> typeReference, int nativeType)101         public MarshalToken(TypeReference<T> typeReference, int nativeType) {
102             this.typeReference = typeReference;
103             this.nativeType = nativeType;
104             this.hash = typeReference.hashCode() ^ nativeType;
105         }
106 
107         final TypeReference<T> typeReference;
108         final int nativeType;
109         private final int hash;
110 
111         @Override
equals(Object other)112         public boolean equals(Object other) {
113             if (other instanceof MarshalToken<?>) {
114                 MarshalToken<?> otherToken = (MarshalToken<?>)other;
115                 return typeReference.equals(otherToken.typeReference) &&
116                         nativeType == otherToken.nativeType;
117             }
118 
119             return false;
120         }
121 
122         @Override
hashCode()123         public int hashCode() {
124             return hash;
125         }
126     }
127 
128     private static List<MarshalQueryable<?>> sRegisteredMarshalQueryables =
129             new ArrayList<MarshalQueryable<?>>();
130     private static HashMap<MarshalToken<?>, Marshaler<?>> sMarshalerMap =
131             new HashMap<MarshalToken<?>, Marshaler<?>>();
132 
MarshalRegistry()133     private MarshalRegistry() {
134         throw new AssertionError();
135     }
136 }
137