Home | History | Annotate | Download | only in impl
      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.impl;
     17 
     18 import android.hardware.camera2.marshal.Marshaler;
     19 import android.hardware.camera2.marshal.MarshalQueryable;
     20 import android.hardware.camera2.utils.TypeReference;
     21 import android.util.Log;
     22 
     23 import java.nio.ByteBuffer;
     24 import java.util.HashMap;
     25 
     26 import static android.hardware.camera2.impl.CameraMetadataNative.*;
     27 import static android.hardware.camera2.marshal.MarshalHelpers.*;
     28 
     29 /**
     30  * Marshal any simple enum (0-arg constructors only) into/from either
     31  * {@code TYPE_BYTE} or {@code TYPE_INT32}.
     32  *
     33  * <p>Default values of the enum are mapped to its ordinal; this can be overridden
     34  * by providing a manual value with {@link #registerEnumValues}.</p>
     35 
     36  * @param <T> the type of {@code Enum}
     37  */
     38 public class MarshalQueryableEnum<T extends Enum<T>> implements MarshalQueryable<T> {
     39 
     40     private static final String TAG = MarshalQueryableEnum.class.getSimpleName();
     41     private static final boolean DEBUG = false;
     42 
     43     private static final int UINT8_MIN = 0x0;
     44     private static final int UINT8_MAX = (1 << Byte.SIZE) - 1;
     45     private static final int UINT8_MASK = UINT8_MAX;
     46 
     47     private class MarshalerEnum extends Marshaler<T> {
     48 
     49         private final Class<T> mClass;
     50 
     51         @SuppressWarnings("unchecked")
     52         protected MarshalerEnum(TypeReference<T> typeReference, int nativeType) {
     53             super(MarshalQueryableEnum.this, typeReference, nativeType);
     54 
     55             mClass = (Class<T>)typeReference.getRawType();
     56         }
     57 
     58         @Override
     59         public void marshal(T value, ByteBuffer buffer) {
     60             int enumValue = getEnumValue(value);
     61 
     62             if (mNativeType == TYPE_INT32) {
     63                 buffer.putInt(enumValue);
     64             } else if (mNativeType == TYPE_BYTE) {
     65                 if (enumValue < UINT8_MIN || enumValue > UINT8_MAX) {
     66                     throw new UnsupportedOperationException(String.format(
     67                             "Enum value %x too large to fit into unsigned byte", enumValue));
     68                 }
     69                 buffer.put((byte)enumValue);
     70             } else {
     71                 throw new AssertionError();
     72             }
     73         }
     74 
     75         @Override
     76         public T unmarshal(ByteBuffer buffer) {
     77             int enumValue;
     78 
     79             switch (mNativeType) {
     80                 case TYPE_INT32:
     81                     enumValue = buffer.getInt();
     82                     break;
     83                 case TYPE_BYTE:
     84                     // get the unsigned byte value; avoid sign extension
     85                     enumValue = buffer.get() & UINT8_MASK;
     86                     break;
     87                 default:
     88                     throw new AssertionError(
     89                             "Unexpected native type; impossible since its not supported");
     90             }
     91 
     92             return getEnumFromValue(mClass, enumValue);
     93         }
     94 
     95         @Override
     96         public int getNativeSize() {
     97             return getPrimitiveTypeSize(mNativeType);
     98         }
     99     }
    100 
    101     @Override
    102     public Marshaler<T> createMarshaler(TypeReference<T> managedType, int nativeType) {
    103         return new MarshalerEnum(managedType, nativeType);
    104     }
    105 
    106     @Override
    107     public boolean isTypeMappingSupported(TypeReference<T> managedType, int nativeType) {
    108         if (nativeType == TYPE_INT32 || nativeType == TYPE_BYTE) {
    109             if (managedType.getType() instanceof Class<?>) {
    110                 Class<?> typeClass = (Class<?>)managedType.getType();
    111 
    112                 if (typeClass.isEnum()) {
    113                     if (DEBUG) {
    114                         Log.v(TAG, "possible enum detected for " + typeClass);
    115                     }
    116 
    117                     // The enum must not take extra arguments
    118                     try {
    119                         // match a class like: "public enum Fruits { Apple, Orange; }"
    120                         typeClass.getDeclaredConstructor(String.class, int.class);
    121                         return true;
    122                     } catch (NoSuchMethodException e) {
    123                         // Skip: custom enum with a special constructor e.g. Foo(T), but need Foo()
    124                         Log.e(TAG, "Can't marshal class " + typeClass + "; no default constructor");
    125                     } catch (SecurityException e) {
    126                         // Skip: wouldn't be able to touch the enum anyway
    127                         Log.e(TAG, "Can't marshal class " + typeClass + "; not accessible");
    128                     }
    129                 }
    130             }
    131         }
    132 
    133         return false;
    134     }
    135 
    136     @SuppressWarnings("rawtypes")
    137     private static final HashMap<Class<? extends Enum>, int[]> sEnumValues =
    138             new HashMap<Class<? extends Enum>, int[]>();
    139 
    140     /**
    141      * Register a non-sequential set of values to be used with the marshal/unmarshal functions.
    142      *
    143      * <p>This enables get/set to correctly marshal the enum into a value that is C-compatible.</p>
    144      *
    145      * @param enumType The class for an enum
    146      * @param values A list of values mapping to the ordinals of the enum
    147      */
    148     public static <T extends Enum<T>> void registerEnumValues(Class<T> enumType, int[] values) {
    149         if (enumType.getEnumConstants().length != values.length) {
    150             throw new IllegalArgumentException(
    151                     "Expected values array to be the same size as the enumTypes values "
    152                             + values.length + " for type " + enumType);
    153         }
    154         if (DEBUG) {
    155             Log.v(TAG, "Registered enum values for type " + enumType + " values");
    156         }
    157 
    158         sEnumValues.put(enumType, values);
    159     }
    160 
    161     /**
    162      * Get the numeric value from an enum.
    163      *
    164      * <p>This is usually the same as the ordinal value for
    165      * enums that have fully sequential values, although for C-style enums the range of values
    166      * may not map 1:1.</p>
    167      *
    168      * @param enumValue Enum instance
    169      * @return Int guaranteed to be ABI-compatible with the C enum equivalent
    170      */
    171     private static <T extends Enum<T>> int getEnumValue(T enumValue) {
    172         int[] values;
    173         values = sEnumValues.get(enumValue.getClass());
    174 
    175         int ordinal = enumValue.ordinal();
    176         if (values != null) {
    177             return values[ordinal];
    178         }
    179 
    180         return ordinal;
    181     }
    182 
    183     /**
    184      * Finds the enum corresponding to it's numeric value. Opposite of {@link #getEnumValue} method.
    185      *
    186      * @param enumType Class of the enum we want to find
    187      * @param value The numeric value of the enum
    188      * @return An instance of the enum
    189      */
    190     private static <T extends Enum<T>> T getEnumFromValue(Class<T> enumType, int value) {
    191         int ordinal;
    192 
    193         int[] registeredValues = sEnumValues.get(enumType);
    194         if (registeredValues != null) {
    195             ordinal = -1;
    196 
    197             for (int i = 0; i < registeredValues.length; ++i) {
    198                 if (registeredValues[i] == value) {
    199                     ordinal = i;
    200                     break;
    201                 }
    202             }
    203         } else {
    204             ordinal = value;
    205         }
    206 
    207         T[] values = enumType.getEnumConstants();
    208 
    209         if (ordinal < 0 || ordinal >= values.length) {
    210             throw new IllegalArgumentException(
    211                     String.format(
    212                             "Argument 'value' (%d) was not a valid enum value for type %s "
    213                                     + "(registered? %b)",
    214                             value,
    215                             enumType, (registeredValues != null)));
    216         }
    217 
    218         return values[ordinal];
    219     }
    220 }
    221