1/*
2 * Copyright (C) 2007 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
17package com.android.dx.cf.direct;
18
19import com.android.dx.cf.iface.Attribute;
20import com.android.dx.cf.iface.ParseException;
21import com.android.dx.cf.iface.ParseObserver;
22import com.android.dx.cf.iface.StdAttributeList;
23import com.android.dx.util.ByteArray;
24import com.android.dx.util.Hex;
25
26/**
27 * Parser for lists of attributes.
28 */
29final /*package*/ class AttributeListParser {
30    /** {@code non-null;} the class file to parse from */
31    private final DirectClassFile cf;
32
33    /** attribute parsing context */
34    private final int context;
35
36    /** offset in the byte array of the classfile to the start of the list */
37    private final int offset;
38
39    /** {@code non-null;} attribute factory to use */
40    private final AttributeFactory attributeFactory;
41
42    /** {@code non-null;} list of parsed attributes */
43    private final StdAttributeList list;
44
45    /** {@code >= -1;} the end offset of this list in the byte array of the
46     * classfile, or {@code -1} if not yet parsed */
47    private int endOffset;
48
49    /** {@code null-ok;} parse observer, if any */
50    private ParseObserver observer;
51
52    /**
53     * Constructs an instance.
54     *
55     * @param cf {@code non-null;} class file to parse from
56     * @param context attribute parsing context (see {@link AttributeFactory})
57     * @param offset offset in {@code bytes} to the start of the list
58     * @param attributeFactory {@code non-null;} attribute factory to use
59     */
60    public AttributeListParser(DirectClassFile cf, int context, int offset,
61                               AttributeFactory attributeFactory) {
62        if (cf == null) {
63            throw new NullPointerException("cf == null");
64        }
65
66        if (attributeFactory == null) {
67            throw new NullPointerException("attributeFactory == null");
68        }
69
70        int size = cf.getBytes().getUnsignedShort(offset);
71
72        this.cf = cf;
73        this.context = context;
74        this.offset = offset;
75        this.attributeFactory = attributeFactory;
76        this.list = new StdAttributeList(size);
77        this.endOffset = -1;
78    }
79
80    /**
81     * Sets the parse observer for this instance.
82     *
83     * @param observer {@code null-ok;} the observer
84     */
85    public void setObserver(ParseObserver observer) {
86        this.observer = observer;
87    }
88
89    /**
90     * Gets the end offset of this constant pool in the {@code byte[]}
91     * which it came from.
92     *
93     * @return {@code >= 0;} the end offset
94     */
95    public int getEndOffset() {
96        parseIfNecessary();
97        return endOffset;
98    }
99
100    /**
101     * Gets the parsed list.
102     *
103     * @return {@code non-null;} the list
104     */
105    public StdAttributeList getList() {
106        parseIfNecessary();
107        return list;
108    }
109
110    /**
111     * Runs {@link #parse} if it has not yet been run successfully.
112     */
113    private void parseIfNecessary() {
114        if (endOffset < 0) {
115            parse();
116        }
117    }
118
119    /**
120     * Does the actual parsing.
121     */
122    private void parse() {
123        int sz = list.size();
124        int at = offset + 2; // Skip the count.
125
126        ByteArray bytes = cf.getBytes();
127
128        if (observer != null) {
129            observer.parsed(bytes, offset, 2,
130                            "attributes_count: " + Hex.u2(sz));
131        }
132
133        for (int i = 0; i < sz; i++) {
134            try {
135                if (observer != null) {
136                    observer.parsed(bytes, at, 0,
137                                    "\nattributes[" + i + "]:\n");
138                    observer.changeIndent(1);
139                }
140
141                Attribute attrib =
142                    attributeFactory.parse(cf, context, at, observer);
143
144                at += attrib.byteLength();
145                list.set(i, attrib);
146
147                if (observer != null) {
148                    observer.changeIndent(-1);
149                    observer.parsed(bytes, at, 0,
150                                    "end attributes[" + i + "]\n");
151                }
152            } catch (ParseException ex) {
153                ex.addContext("...while parsing attributes[" + i + "]");
154                throw ex;
155            } catch (RuntimeException ex) {
156                ParseException pe = new ParseException(ex);
157                pe.addContext("...while parsing attributes[" + i + "]");
158                throw pe;
159            }
160        }
161
162        endOffset = at;
163    }
164}
165