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 static android.hardware.camera2.impl.CameraMetadataNative.*;
19import static com.android.internal.util.Preconditions.*;
20
21import android.hardware.camera2.impl.CameraMetadataNative;
22import android.util.Rational;
23
24/**
25 * Static functions in order to help implementing various marshaler functionality.
26 *
27 * <p>The intention is to statically import everything from this file into another file when
28 * implementing a new marshaler (or marshal queryable).</p>
29 *
30 * <p>The helpers are centered around providing primitive knowledge of the native types,
31 * such as the native size, the managed class wrappers, and various precondition checks.</p>
32 */
33public final class MarshalHelpers {
34
35    public static final int SIZEOF_BYTE = 1;
36    public static final int SIZEOF_INT32 = Integer.SIZE / Byte.SIZE;
37    public static final int SIZEOF_INT64 = Long.SIZE / Byte.SIZE;
38    public static final int SIZEOF_FLOAT = Float.SIZE / Byte.SIZE;
39    public static final int SIZEOF_DOUBLE = Double.SIZE / Byte.SIZE;
40    public static final int SIZEOF_RATIONAL = SIZEOF_INT32 * 2;
41
42    /**
43     * Get the size in bytes for the native camera metadata type.
44     *
45     * <p>This used to determine how many bytes it would take to encode/decode a single value
46     * of that {@link nativeType}.</p>
47     *
48     * @param nativeType the native type, e.g.
49     *        {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}.
50     * @return size in bytes >= 1
51     *
52     * @throws UnsupportedOperationException if nativeType was not one of the built-in types
53     */
54    public static int getPrimitiveTypeSize(int nativeType) {
55        switch (nativeType) {
56            case TYPE_BYTE:
57                return SIZEOF_BYTE;
58            case TYPE_INT32:
59                return SIZEOF_INT32;
60            case TYPE_FLOAT:
61                return SIZEOF_FLOAT;
62            case TYPE_INT64:
63                return SIZEOF_INT64;
64            case TYPE_DOUBLE:
65                return SIZEOF_DOUBLE;
66            case TYPE_RATIONAL:
67                return SIZEOF_RATIONAL;
68        }
69
70        throw new UnsupportedOperationException("Unknown type, can't get size for "
71                + nativeType);
72    }
73
74
75    /**
76     * Ensure that the {@code klass} is one of the metadata-primitive classes.
77     *
78     * @param klass a non-{@code null} reference
79     * @return {@code klass} instance
80     *
81     * @throws UnsupportedOperationException if klass was not one of the built-in classes
82     * @throws NullPointerException if klass was null
83     *
84     * @see #isPrimitiveClass
85     */
86    public static <T> Class<T> checkPrimitiveClass(Class<T> klass) {
87        checkNotNull(klass, "klass must not be null");
88
89        if (isPrimitiveClass(klass)) {
90            return klass;
91        }
92
93        throw new UnsupportedOperationException("Unsupported class '" + klass +
94                "'; expected a metadata primitive class");
95    }
96
97    /**
98     * Checks whether or not {@code klass} is one of the metadata-primitive classes.
99     *
100     * <p>The following types (whether boxed or unboxed) are considered primitive:
101     * <ul>
102     * <li>byte
103     * <li>int
104     * <li>float
105     * <li>double
106     * <li>Rational
107     * </ul>
108     * </p>
109     *
110     * <p>This doesn't strictly follow the java understanding of primitive since
111     * boxed objects are included, Rational is included, and other types such as char and
112     * short are not included.</p>
113     *
114     * @param klass a {@link Class} instance; using {@code null} will return {@code false}
115     * @return {@code true} if primitive, {@code false} otherwise
116     */
117    public static <T> boolean isPrimitiveClass(Class<T> klass) {
118        if (klass == null) {
119            return false;
120        }
121
122        if (klass == byte.class || klass == Byte.class) {
123            return true;
124        } else if (klass == int.class || klass == Integer.class) {
125            return true;
126        } else if (klass == float.class || klass == Float.class) {
127            return true;
128        } else if (klass == long.class || klass == Long.class) {
129            return true;
130        } else if (klass == double.class || klass == Double.class) {
131            return true;
132        } else if (klass == Rational.class) {
133            return true;
134        }
135
136        return false;
137    }
138
139    /**
140     * Wrap {@code klass} with its wrapper variant if it was a {@code Class} corresponding
141     * to a Java primitive.
142     *
143     * <p>Non-primitive classes are passed through as-is.</p>
144     *
145     * <p>For example, for a primitive {@code int.class => Integer.class},
146     * but for a non-primitive {@code Rational.class => Rational.class}.</p>
147     *
148     * @param klass a {@code Class} reference
149     *
150     * @return wrapped class object, or same class object if non-primitive
151     */
152    @SuppressWarnings("unchecked")
153    public static <T> Class<T> wrapClassIfPrimitive(Class<T> klass) {
154        if (klass == byte.class) {
155            return (Class<T>)Byte.class;
156        } else if (klass == int.class) {
157            return (Class<T>)Integer.class;
158        } else if (klass == float.class) {
159            return (Class<T>)Float.class;
160        } else if (klass == long.class) {
161            return (Class<T>)Long.class;
162        } else if (klass == double.class) {
163            return (Class<T>)Double.class;
164        }
165
166        return klass;
167    }
168
169    /**
170     * Return a human-readable representation of the {@code nativeType}, e.g. "TYPE_INT32"
171     *
172     * <p>Out-of-range values return a string with "UNKNOWN" as the prefix.</p>
173     *
174     * @param nativeType the native type
175     *
176     * @return human readable type name
177     */
178    public static String toStringNativeType(int nativeType) {
179        switch (nativeType) {
180            case TYPE_BYTE:
181                return "TYPE_BYTE";
182            case TYPE_INT32:
183                return "TYPE_INT32";
184            case TYPE_FLOAT:
185                return "TYPE_FLOAT";
186            case TYPE_INT64:
187                return "TYPE_INT64";
188            case TYPE_DOUBLE:
189                return "TYPE_DOUBLE";
190            case TYPE_RATIONAL:
191                return "TYPE_RATIONAL";
192        }
193
194        return "UNKNOWN(" + nativeType + ")";
195    }
196
197    /**
198     * Ensure that the {@code nativeType} is one of the native types supported
199     * by {@link CameraMetadataNative}.
200     *
201     * @param nativeType the native type
202     *
203     * @return the native type
204     *
205     * @throws UnsupportedOperationException if the native type was invalid
206     */
207    public static int checkNativeType(int nativeType) {
208        switch (nativeType) {
209            case TYPE_BYTE:
210            case TYPE_INT32:
211            case TYPE_FLOAT:
212            case TYPE_INT64:
213            case TYPE_DOUBLE:
214            case TYPE_RATIONAL:
215                return nativeType;
216        }
217
218        throw new UnsupportedOperationException("Unknown nativeType " + nativeType);
219    }
220
221    /**
222     * Ensure that the expected and actual native types are equal.
223     *
224     * @param expectedNativeType the expected native type
225     * @param actualNativeType the actual native type
226     * @return the actual native type
227     *
228     * @throws UnsupportedOperationException if the types are not equal
229     */
230    public static int checkNativeTypeEquals(int expectedNativeType, int actualNativeType) {
231        if (expectedNativeType != actualNativeType) {
232            throw new UnsupportedOperationException(
233                    String.format("Expected native type %d, but got %d",
234                            expectedNativeType, actualNativeType));
235        }
236
237        return actualNativeType;
238    }
239
240    private MarshalHelpers() {
241        throw new AssertionError();
242    }
243}
244