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.impl;
17
18import android.hardware.camera2.marshal.Marshaler;
19import android.hardware.camera2.marshal.MarshalQueryable;
20import android.hardware.camera2.marshal.MarshalRegistry;
21import android.hardware.camera2.utils.TypeReference;
22import android.util.Log;
23
24import java.lang.reflect.Array;
25import java.nio.ByteBuffer;
26import java.util.ArrayList;
27
28/**
29 * Marshal any array {@code T}.
30 *
31 * <p>To marshal any {@code T} to/from a native type, the marshaler for T to/from that native type
32 * also has to exist.</p>
33 *
34 * <p>{@code T} can be either a T2[] where T2 is an object type, or a P[] where P is a
35 * built-in primitive (e.g. int[], float[], etc).</p>
36
37 * @param <T> the type of the array (e.g. T = int[], or T = Rational[])
38 */
39public class MarshalQueryableArray<T> implements MarshalQueryable<T> {
40
41    private static final String TAG = MarshalQueryableArray.class.getSimpleName();
42    private static final boolean DEBUG = false;
43
44    private class MarshalerArray extends Marshaler<T> {
45        private final Class<T> mClass;
46        private final Marshaler<?> mComponentMarshaler;
47        private final Class<?> mComponentClass;
48
49        @SuppressWarnings("unchecked")
50        protected MarshalerArray(TypeReference<T> typeReference, int nativeType) {
51            super(MarshalQueryableArray.this, typeReference, nativeType);
52
53            mClass = (Class<T>)typeReference.getRawType();
54
55            TypeReference<?> componentToken = typeReference.getComponentType();
56            mComponentMarshaler = MarshalRegistry.getMarshaler(componentToken, mNativeType);
57            mComponentClass = componentToken.getRawType();
58        }
59
60        @Override
61        public void marshal(T value, ByteBuffer buffer) {
62            int length = Array.getLength(value);
63            for (int i = 0; i < length; ++i) {
64                marshalArrayElement(mComponentMarshaler, buffer, value, i);
65            }
66        }
67
68        @Override
69        public T unmarshal(ByteBuffer buffer) {
70            Object array;
71
72            int elementSize = mComponentMarshaler.getNativeSize();
73
74            if (elementSize != Marshaler.NATIVE_SIZE_DYNAMIC) {
75                int remaining = buffer.remaining();
76                int arraySize = remaining / elementSize;
77
78                if (remaining % elementSize != 0) {
79                    throw new UnsupportedOperationException("Arrays for " + mTypeReference
80                            + " must be packed tighly into a multiple of " + elementSize
81                            + "; but there are " + (remaining % elementSize) + " left over bytes");
82                }
83
84                if (DEBUG) {
85                    Log.v(TAG, String.format(
86                            "Attempting to unpack array (count = %d, element size = %d, bytes "
87                            + "remaining = %d) for type %s",
88                            arraySize, elementSize, remaining, mClass));
89                }
90
91                array = Array.newInstance(mComponentClass, arraySize);
92                for (int i = 0; i < arraySize; ++i) {
93                    Object elem = mComponentMarshaler.unmarshal(buffer);
94                    Array.set(array, i, elem);
95                }
96            } else {
97                // Dynamic size, use an array list.
98                ArrayList<Object> arrayList = new ArrayList<Object>();
99
100                // Assumes array is packed tightly; no unused bytes allowed
101                while (buffer.hasRemaining()) {
102                    Object elem = mComponentMarshaler.unmarshal(buffer);
103                    arrayList.add(elem);
104                }
105
106                int arraySize = arrayList.size();
107                array = copyListToArray(arrayList, Array.newInstance(mComponentClass, arraySize));
108            }
109
110            if (buffer.remaining() != 0) {
111                Log.e(TAG, "Trailing bytes (" + buffer.remaining() + ") left over after unpacking "
112                        + mClass);
113            }
114
115            return mClass.cast(array);
116        }
117
118        @Override
119        public int getNativeSize() {
120            return NATIVE_SIZE_DYNAMIC;
121        }
122
123        @Override
124        public int calculateMarshalSize(T value) {
125            int elementSize = mComponentMarshaler.getNativeSize();
126            int arrayLength = Array.getLength(value);
127
128            if (elementSize != Marshaler.NATIVE_SIZE_DYNAMIC) {
129                // The fast way. Every element size is uniform.
130                return elementSize * arrayLength;
131            } else {
132                // The slow way. Accumulate size for each element.
133                int size = 0;
134                for (int i = 0; i < arrayLength; ++i) {
135                    size += calculateElementMarshalSize(mComponentMarshaler, value, i);
136                }
137
138                return size;
139            }
140        }
141
142        /*
143         * Helpers to avoid compiler errors regarding types with wildcards (?)
144         */
145
146        @SuppressWarnings("unchecked")
147        private <TElem> void marshalArrayElement(Marshaler<TElem> marshaler,
148                ByteBuffer buffer, Object array, int index) {
149            marshaler.marshal((TElem)Array.get(array, index), buffer);
150        }
151
152        @SuppressWarnings("unchecked")
153        private Object copyListToArray(ArrayList<?> arrayList, Object arrayDest) {
154            return arrayList.toArray((T[]) arrayDest);
155        }
156
157        @SuppressWarnings("unchecked")
158        private <TElem> int calculateElementMarshalSize(Marshaler<TElem> marshaler,
159                Object array, int index) {
160            Object elem = Array.get(array, index);
161
162            return marshaler.calculateMarshalSize((TElem) elem);
163        }
164    }
165
166    @Override
167    public Marshaler<T> createMarshaler(TypeReference<T> managedType, int nativeType) {
168        return new MarshalerArray(managedType, nativeType);
169    }
170
171    @Override
172    public boolean isTypeMappingSupported(TypeReference<T> managedType, int nativeType) {
173        // support both ConcreteType[] and GenericType<ConcreteType>[]
174        return managedType.getRawType().isArray();
175
176        // TODO: Should this recurse deeper and check that there is
177        // a valid marshaler for the ConcreteType as well?
178    }
179}
180