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 */
16package android.hardware.camera2.marshal;
17
18import android.hardware.camera2.impl.CameraMetadataNative;
19import android.hardware.camera2.utils.TypeReference;
20
21import java.util.ArrayList;
22import java.util.HashMap;
23import java.util.List;
24
25/**
26 * Registry of supported marshalers; add new query-able marshalers or lookup existing ones.</p>
27 */
28public 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     */
39    public static <T> void registerMarshalQueryable(MarshalQueryable<T> queryable) {
40        synchronized(sMarshalLock) {
41            sRegisteredMarshalQueryables.add(queryable);
42        }
43    }
44
45    /**
46     * Lookup a marshaler between {@code T} and {@code nativeType}.
47     *
48     * <p>Marshalers are looked up in the order they were registered; earlier registered
49     * marshal queriers get priority.</p>
50     *
51     * @param typeToken The compile-time type reference for {@code T}
52     * @param nativeType The native type, e.g. {@link CameraMetadataNative#TYPE_BYTE TYPE_BYTE}
53     * @return marshaler a non-{@code null} marshaler that supports marshaling the type combo
54     *
55     * @throws UnsupportedOperationException If no marshaler matching the args could be found
56     */
57    @SuppressWarnings("unchecked")
58    public static <T> Marshaler<T> getMarshaler(TypeReference<T> typeToken, int nativeType) {
59        synchronized(sMarshalLock) {
60            // TODO: can avoid making a new token each time by code-genning
61            // the list of type tokens and native types from the keys (at the call sites)
62            MarshalToken<T> marshalToken = new MarshalToken<T>(typeToken, nativeType);
63
64            /*
65             * Marshalers are instantiated lazily once they are looked up; successive lookups
66             * will not instantiate new marshalers.
67             */
68            Marshaler<T> marshaler =
69                    (Marshaler<T>) sMarshalerMap.get(marshalToken);
70
71            if (marshaler == null) {
72
73                if (sRegisteredMarshalQueryables.size() == 0) {
74                    throw new AssertionError("No available query marshalers registered");
75                }
76
77                // Query each marshaler to see if they support the native/managed type combination
78                for (MarshalQueryable<?> potentialMarshaler : sRegisteredMarshalQueryables) {
79
80                    MarshalQueryable<T> castedPotential =
81                            (MarshalQueryable<T>)potentialMarshaler;
82
83                    if (castedPotential.isTypeMappingSupported(typeToken, nativeType)) {
84                        marshaler = castedPotential.createMarshaler(typeToken, nativeType);
85                        break;
86                    }
87                }
88
89                if (marshaler == null) {
90                    throw new UnsupportedOperationException(
91                        "Could not find marshaler that matches the requested " +
92                        "combination of type reference " +
93                        typeToken + " and native type " +
94                        MarshalHelpers.toStringNativeType(nativeType));
95                }
96
97                // Only put when no cached version exists to avoid +0.5ms lookup per call.
98                sMarshalerMap.put(marshalToken, marshaler);
99            }
100
101            return marshaler;
102        }
103    }
104
105    private static class MarshalToken<T> {
106        public MarshalToken(TypeReference<T> typeReference, int nativeType) {
107            this.typeReference = typeReference;
108            this.nativeType = nativeType;
109            this.hash = typeReference.hashCode() ^ nativeType;
110        }
111
112        final TypeReference<T> typeReference;
113        final int nativeType;
114        private final int hash;
115
116        @Override
117        public boolean equals(Object other) {
118            if (other instanceof MarshalToken<?>) {
119                MarshalToken<?> otherToken = (MarshalToken<?>)other;
120                return typeReference.equals(otherToken.typeReference) &&
121                        nativeType == otherToken.nativeType;
122            }
123
124            return false;
125        }
126
127        @Override
128        public int hashCode() {
129            return hash;
130        }
131    }
132
133    // Control access to the static data structures below
134    private static final Object sMarshalLock = new Object();
135
136    private static final List<MarshalQueryable<?>> sRegisteredMarshalQueryables =
137            new ArrayList<MarshalQueryable<?>>();
138    private static final HashMap<MarshalToken<?>, Marshaler<?>> sMarshalerMap =
139            new HashMap<MarshalToken<?>, Marshaler<?>>();
140
141    private MarshalRegistry() {
142        throw new AssertionError();
143    }
144}
145