1/*
2 * Copyright (C) 2008 Google Inc.
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
17package com.android.hit;
18
19import java.io.ByteArrayInputStream;
20import java.io.DataInputStream;
21import java.util.Set;
22
23public class ArrayInstance extends Instance {
24    private int mType;
25    private int mNumEntries;
26    private byte[] mData;
27
28    public ArrayInstance(long id, StackTrace stack, int type, int numEntries,
29            byte[] data) {
30        mId = id;
31        mStack = stack;
32        mType = type;
33        mNumEntries = numEntries;
34        mData = data;
35    }
36
37    public final void resolveReferences(State state) {
38        if (mType != Types.OBJECT) {
39            return;
40        }
41
42        /*
43         * mData holds a stream of object instance ids
44         * Spin through them all and list ourselves as a reference holder.
45         */
46        int idSize = Types.getTypeSize(mType);
47        final int N = mNumEntries;
48
49        ByteArrayInputStream bais = new ByteArrayInputStream(mData);
50        DataInputStream dis = new DataInputStream(bais);
51
52        for (int i = 0; i < N; i++) {
53            long id;
54
55            try {
56                if (idSize == 4) {
57                    id = dis.readInt();
58                } else {
59                    id = dis.readLong();
60                }
61
62                Instance instance = state.findReference(id);
63
64                if (instance != null) {
65                    instance.addParent(this);
66                }
67            } catch (java.io.IOException e) {
68                e.printStackTrace();
69            }
70        }
71    }
72
73    @Override
74    public final int getSize() {
75        return mData.length;
76    }
77
78    @Override
79    public final void visit(Set<Instance> resultSet, Filter filter) {
80        //  If we're in the set then we and our children have been visited
81        if (resultSet.contains(this)) {
82            return;
83        }
84
85        if (null != filter) {
86            if (filter.accept(this)) {
87                resultSet.add(this);
88            }
89        } else {
90            resultSet.add(this);
91        }
92
93        if (mType != Types.OBJECT) {
94            return;
95        }
96
97        /*
98         * mData holds a stream of object instance ids
99         * Spin through them all and visit them
100         */
101        int idSize = Types.getTypeSize(mType);
102        final int N = mNumEntries;
103
104        ByteArrayInputStream bais = new ByteArrayInputStream(mData);
105        DataInputStream dis = new DataInputStream(bais);
106        State state = mHeap.mState;
107
108        for (int i = 0; i < N; i++) {
109            long id;
110
111            try {
112                if (idSize == 4) {
113                    id = dis.readInt();
114                } else {
115                    id = dis.readLong();
116                }
117
118                Instance instance = state.findReference(id);
119
120                if (instance != null) {
121                    instance.visit(resultSet, filter);
122                }
123            } catch (java.io.IOException e) {
124                e.printStackTrace();
125            }
126        }
127    }
128
129    @Override
130    public final String getTypeName() {
131        return Types.getTypeName(mType) + "[" + mNumEntries + "]";
132    }
133
134    public final String toString() {
135        return String.format("%s@0x08x", getTypeName(), mId);
136    }
137
138    @Override
139    public String describeReferenceTo(long referent) {
140        //  If this isn't an object array then we can't refer to an object
141        if (mType != Types.OBJECT) {
142            return super.describeReferenceTo(referent);
143        }
144
145        int idSize = Types.getTypeSize(mType);
146        final int N = mNumEntries;
147        int numRefs = 0;
148        StringBuilder result = new StringBuilder("Elements [");
149        ByteArrayInputStream bais = new ByteArrayInputStream(mData);
150        DataInputStream dis = new DataInputStream(bais);
151
152        /*
153         * Spin through all the objects and build up a string describing
154         * all of the array elements that refer to the target object.
155         */
156        for (int i = 0; i < N; i++) {
157            long id;
158
159            try {
160                if (idSize == 4) {
161                    id = dis.readInt();
162                } else {
163                    id = dis.readLong();
164                }
165
166                if (id == referent) {
167                    numRefs++;
168
169                    if (numRefs > 1) {
170                        result.append(", ");
171                    }
172
173                    result.append(i);
174                }
175            } catch (java.io.IOException e) {
176                e.printStackTrace();
177            }
178        }
179
180        if (numRefs == 0) {
181            return super.describeReferenceTo(referent);
182        }
183
184        result.append("]");
185
186        return result.toString();
187    }
188}
189