1/* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved.
2 *
3 * This program and the accompanying materials are made available under
4 * the terms of the Common Public License v1.0 which accompanies this distribution,
5 * and is available at http://www.eclipse.org/legal/cpl-v10.html
6 *
7 * $Id: ConstantCollection.java,v 1.1.1.1 2004/05/09 16:57:45 vlad_r Exp $
8 */
9package com.vladium.jcd.cls;
10
11import java.io.IOException;
12import java.util.ArrayList;
13import java.util.List;
14
15import com.vladium.jcd.cls.constant.*;
16import com.vladium.jcd.lib.UDataOutputStream;
17import com.vladium.util.ObjectIntMap;
18
19// ----------------------------------------------------------------------------
20/**
21 * @author (C) 2001, Vladimir Roubtsov
22 */
23final class ConstantCollection implements IConstantCollection
24{
25    // public: ................................................................
26
27    // IConstantCollection:
28
29    // ACCESSORS:
30
31    public CONSTANT_info get (final int index)
32    {
33        final Object result = m_constants.get (index - 1);
34
35        if (result == null)
36            throw new IllegalStateException ("assertion failure: dereferencing an invalid constant pool slot " + index);
37
38        return (CONSTANT_info) result;
39    }
40
41    public IConstantCollection.IConstantIterator iterator ()
42    {
43        return new ConstantIterator (m_constants);
44    }
45
46    public int find (final int type, final IConstantComparator comparator)
47    {
48        if (comparator == null)
49            throw new IllegalArgumentException ("null input: comparator");
50
51        for (int i = 0; i < m_constants.size (); ++ i)
52        {
53            final CONSTANT_info constant = (CONSTANT_info) m_constants.get (i);
54
55            if ((constant != null) && (constant.tag () == type) && comparator.equals (constant))
56                return i /* !!! */ + 1;
57        }
58
59        return -1;
60    }
61
62    public int findCONSTANT_Utf8 (final String value)
63    {
64        if (value == null)
65            throw new IllegalArgumentException ("null input: value");
66
67        // create index lazily:
68        final ObjectIntMap index = getCONSTANT_Utf8_index ();
69        final int [] result = new int [1];
70
71        if (index.get (value, result))
72            return result [0] /* !!! */ + 1;
73        else
74            return -1;
75    }
76
77    public int size ()
78    {
79        return m_size;
80    }
81
82    // Cloneable:
83
84    /**
85     * Performs a deep copy.
86     */
87    public Object clone ()
88    {
89        try
90        {
91            final ConstantCollection _clone = (ConstantCollection) super.clone ();
92
93            // deep copy:
94            final int constants_count = m_constants.size ();
95            _clone.m_constants = new ArrayList (constants_count);
96            for (int c = 0; c < constants_count; ++ c)
97            {
98                final CONSTANT_info constant = (CONSTANT_info) m_constants.get (c);
99                _clone.m_constants.add (constant == null ? null : constant.clone ());
100            }
101
102            // note: m_CONSTANT_Utf8_index is not cloned intentionally
103
104            return _clone;
105        }
106        catch (CloneNotSupportedException e)
107        {
108            throw new InternalError (e.toString ());
109        }
110    }
111
112    // IClassFormatOutput:
113
114    public void writeInClassFormat (final UDataOutputStream out) throws IOException
115    {
116        final int constant_pool_count = m_constants.size (); // note: this is not the same as size()
117        out.writeU2 (constant_pool_count + /* !!! */1);
118
119        final ConstantIterator i = new ConstantIterator (m_constants);
120        for (CONSTANT_info entry; (entry = i.nextConstant ()) != null; )
121        {
122            entry.writeInClassFormat (out);
123        }
124    }
125
126    // Visitor:
127
128    public void accept (final IClassDefVisitor visitor, final Object ctx)
129    {
130        visitor.visit (this, ctx);
131    }
132
133
134    // MUTATORS:
135
136    public CONSTANT_info set (final int index, final CONSTANT_info constant)
137    {
138        final int zindex = index - 1;
139        final CONSTANT_info result = (CONSTANT_info) m_constants.get (zindex);
140
141        if (result == null)
142            throw new IllegalStateException ("assertion failure: dereferencing an invalid constant pool slot " + index);
143
144        if (result.width () != constant.width ())
145            throw new IllegalArgumentException ("assertion failure: can't set entry of type [" + result.getClass ().getName () + "] to an entry of type [" + result.getClass ().getName () + "] at pool slot " + index);
146
147        m_constants.set (zindex, constant);
148
149        // update the string index if it is in use:
150        if (m_CONSTANT_Utf8_index != null)
151        {
152            // remove the old index value if it exists and is equal to 'index':
153            if (result instanceof CONSTANT_Utf8_info)
154            {
155                final String mapKey = ((CONSTANT_Utf8_info) result).m_value;
156                final int [] out = new int [1];
157
158                if (m_CONSTANT_Utf8_index.get (mapKey, out) && (out [0] == zindex))
159                    m_CONSTANT_Utf8_index.remove (mapKey);
160            }
161
162            // add new index value if necessary:
163            if (constant instanceof CONSTANT_Utf8_info)
164                m_CONSTANT_Utf8_index.put (((CONSTANT_Utf8_info) constant).m_value, zindex);
165        }
166
167        return result;
168    }
169
170    public int add (final CONSTANT_info constant)
171    {
172        m_constants.add (constant);
173        ++ m_size;
174        final int result = m_constants.size ();
175
176        for (int width = 1; width < constant.width (); ++ width)
177        {
178            ++ m_size;
179            m_constants.add (null); // insert padding empty slots
180        }
181
182        // update the string index if it is in use:
183        if ((m_CONSTANT_Utf8_index != null) && (constant instanceof CONSTANT_Utf8_info))
184            m_CONSTANT_Utf8_index.put (((CONSTANT_Utf8_info) constant).m_value, result /* !!! */ - 1);
185
186        return result;
187    }
188
189    // protected: .............................................................
190
191    // package: ...............................................................
192
193
194    ConstantCollection (final int capacity)
195    {
196        m_constants = capacity < 0 ? new ArrayList () : new ArrayList (capacity);
197    }
198
199    // private: ...............................................................
200
201
202    private static final class ConstantIterator implements IConstantCollection.IConstantIterator
203    {
204        ConstantIterator (final List/* CONSTANT_info */ constants)
205        {
206            m_constants = constants;
207            m_next_index = 1;
208            shift ();
209        }
210
211
212        public int nextIndex ()
213        {
214            final int result = m_index;
215            shift ();
216
217            return result;
218        }
219
220        public CONSTANT_info nextConstant ()
221        {
222            final int nextIndex = nextIndex ();
223            if (nextIndex < 0)
224                return null;
225            else
226                return (CONSTANT_info) m_constants.get (nextIndex - 1);
227        }
228
229        public CONSTANT_info set (final CONSTANT_info constant)
230        {
231            final int zindex = m_prev_index - 1;
232            final CONSTANT_info result = (CONSTANT_info) m_constants.get (zindex);
233
234            if (result == null) // this should never happen with iterators
235                throw new IllegalStateException ("assertion failure: dereferencing an invalid constant pool slot " + m_prev_index);
236
237            if (result.width () != constant.width ())
238                throw new IllegalArgumentException ("assertion failure: can't set entry of type [" + result.getClass ().getName () + "] to an entry of type [" + result.getClass ().getName () + "] at pool slot " + m_prev_index);
239
240            m_constants.set (zindex, constant);
241
242            return result;
243        }
244
245
246        private void shift ()
247        {
248            m_prev_index = m_index;
249            m_index = m_next_index;
250
251            if (m_index > 0)
252            {
253                try
254                {
255                    final CONSTANT_info entry = (CONSTANT_info) m_constants.get (m_index - 1);
256
257                    m_next_index += entry.width ();
258                    if (m_next_index > m_constants.size ()) m_next_index = -1;
259                }
260                catch (IndexOutOfBoundsException ioobe) // empty collection edge case
261                {
262                    m_index = m_next_index = -1;
263                }
264            }
265        }
266
267
268        private int m_index, m_prev_index, m_next_index;
269        private List/* CONSTANT_info */ m_constants;
270
271    } // end of nested class
272
273
274    private ObjectIntMap getCONSTANT_Utf8_index ()
275    {
276        if (m_CONSTANT_Utf8_index == null)
277        {
278            final ObjectIntMap index = new ObjectIntMap (m_size);
279
280            for (int i = 0; i < m_constants.size (); ++ i)
281            {
282                final CONSTANT_info constant = (CONSTANT_info) m_constants.get (i);
283
284                if ((constant != null) && (constant.tag () == CONSTANT_Utf8_info.TAG))
285                {
286                    // it's ok to always put: the later indices will simply override the earlier ones
287                    index.put (((CONSTANT_Utf8_info) constant).m_value, i); // note: unadjusted index saved here
288                }
289            }
290
291            m_CONSTANT_Utf8_index = index;
292        }
293
294        return m_CONSTANT_Utf8_index;
295    }
296
297
298    private List/* CONSTANT_info */ m_constants; // never null
299    private int m_size;
300    private transient ObjectIntMap /* String(CONSTANT_Utf value) -> int(index) */ m_CONSTANT_Utf8_index;
301
302} // end of class
303// ----------------------------------------------------------------------------
304