001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one or more
003 *  contributor license agreements.  See the NOTICE file distributed with
004 *  this work for additional information regarding copyright ownership.
005 *  The ASF licenses this file to You under the Apache License, Version 2.0
006 *  (the "License"); you may not use this file except in compliance with
007 *  the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 */
017package org.apache.commons.compress.harmony.unpack200.bytecode;
018
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.Collections;
022import java.util.HashMap;
023import java.util.HashSet;
024import java.util.List;
025import java.util.Map;
026import java.util.TreeSet;
027
028import org.apache.commons.compress.harmony.unpack200.Segment;
029
030/**
031 * The Class constant pool
032 */
033public class ClassConstantPool {
034
035    protected HashSet entriesContainsSet = new HashSet();
036    protected HashSet othersContainsSet = new HashSet();
037
038    private final HashSet mustStartClassPool = new HashSet();
039
040    protected Map indexCache;
041
042    private final List others = new ArrayList(500);
043    private final List entries = new ArrayList(500);
044
045    private boolean resolved;
046
047    public ClassFileEntry add(final ClassFileEntry entry) {
048        if (entry instanceof ByteCode) {
049            return null;
050        }
051        if (entry instanceof ConstantPoolEntry) {
052            if (entriesContainsSet.add(entry)) {
053                entries.add(entry);
054            }
055        } else if (othersContainsSet.add(entry)) {
056            others.add(entry);
057        }
058
059        return entry;
060    }
061
062    public void addNestedEntries() {
063        boolean added = true;
064
065        // initial assignment
066        final ArrayList parents = new ArrayList(512);
067        final ArrayList children = new ArrayList(512);
068
069        // adding old entries
070        parents.addAll(entries);
071        parents.addAll(others);
072
073        // while there any parents to traverse and at least one change in target
074        // storage was made
075        while (added || parents.size() > 0) {
076
077            children.clear();
078
079            final int entriesOriginalSize = entries.size();
080            final int othersOriginalSize = others.size();
081
082            // get the parents' children and add them to buffer
083            // concurrently add parents to target storage
084            for (int indexParents = 0; indexParents < parents.size(); indexParents++) {
085                final ClassFileEntry entry = (ClassFileEntry) parents.get(indexParents);
086
087                // traverse children
088                final ClassFileEntry[] entryChildren = entry.getNestedClassFileEntries();
089                children.addAll(Arrays.asList(entryChildren));
090
091                final boolean isAtStart = (entry instanceof ByteCode) && ((ByteCode) entry).nestedMustStartClassPool();
092
093                if (isAtStart) {
094                    mustStartClassPool.addAll(Arrays.asList(entryChildren));
095                }
096
097                // add parent
098                add(entry);
099            }
100
101            added = !(entries.size() == entriesOriginalSize && others.size() == othersOriginalSize);
102
103            // parents are not needed anymore
104            // children now become parents
105            parents.clear();
106            parents.addAll(children);
107        }
108    }
109
110    public int indexOf(final ClassFileEntry entry) {
111        if (!resolved) {
112            throw new IllegalStateException("Constant pool is not yet resolved; this does not make any sense");
113        }
114        if (null == indexCache) {
115            throw new IllegalStateException("Index cache is not initialized!");
116        }
117        final Integer entryIndex = ((Integer) indexCache.get(entry));
118        // If the entry isn't found, answer -1. Otherwise answer the entry.
119        if (entryIndex != null) {
120            return entryIndex.intValue() + 1;
121        }
122        return -1;
123    }
124
125    public int size() {
126        return entries.size();
127    }
128
129    public ClassFileEntry get(int i) {
130        if (!resolved) {
131            throw new IllegalStateException("Constant pool is not yet resolved; this does not make any sense");
132        }
133        return (ClassFileEntry) entries.get(--i);
134    }
135
136    public void resolve(final Segment segment) {
137        initialSort();
138        sortClassPool();
139
140        resolved = true;
141
142        for (int it = 0; it < entries.size(); it++) {
143            final ClassFileEntry entry = (ClassFileEntry) entries.get(it);
144            entry.resolve(this);
145        }
146
147        for (int it = 0; it < others.size(); it++) {
148            final ClassFileEntry entry = (ClassFileEntry) others.get(it);
149            entry.resolve(this);
150        }
151
152    }
153
154    private void initialSort() {
155        final TreeSet inCpAll = new TreeSet(
156            (arg0, arg1) -> ((ConstantPoolEntry) arg0).getGlobalIndex() - ((ConstantPoolEntry) arg1).getGlobalIndex());
157        final TreeSet cpUtf8sNotInCpAll = new TreeSet(
158            (arg0, arg1) -> ((CPUTF8) arg0).underlyingString().compareTo(((CPUTF8) arg1).underlyingString()));
159        final TreeSet cpClassesNotInCpAll = new TreeSet(
160            (arg0, arg1) -> ((CPClass) arg0).getName().compareTo(((CPClass) arg1).getName()));
161
162        for (int index = 0; index < entries.size(); index++) {
163            final ConstantPoolEntry entry = (ConstantPoolEntry) entries.get(index);
164            if (entry.getGlobalIndex() == -1) {
165                if (entry instanceof CPUTF8) {
166                    cpUtf8sNotInCpAll.add(entry);
167                } else if (entry instanceof CPClass) {
168                    cpClassesNotInCpAll.add(entry);
169                } else {
170                    throw new Error("error");
171                }
172            } else {
173                inCpAll.add(entry);
174            }
175        }
176        entries.clear();
177        entries.addAll(inCpAll);
178        entries.addAll(cpUtf8sNotInCpAll);
179        entries.addAll(cpClassesNotInCpAll);
180    }
181
182    public List entries() {
183        return Collections.unmodifiableList(entries);
184    }
185
186    protected void sortClassPool() {
187        // Now that everything has been resolved, do one
188        // final sort of the class pool. This fixes up
189        // references to objects which need to be at the
190        // start of the class pool
191
192        final ArrayList startOfPool = new ArrayList(entries.size());
193        final ArrayList finalSort = new ArrayList(entries.size());
194
195        for (int i = 0; i < entries.size(); i++) {
196            final ClassFileEntry nextEntry = (ClassFileEntry) entries.get(i);
197            if (mustStartClassPool.contains(nextEntry)) {
198                startOfPool.add(nextEntry);
199            } else {
200                finalSort.add(nextEntry);
201            }
202        }
203
204        // copy over and rebuild the cache
205        //
206        indexCache = new HashMap(entries.size());
207        int index = 0;
208
209        entries.clear();
210
211        for (int itIndex = 0; itIndex < startOfPool.size(); itIndex++) {
212            final ClassFileEntry entry = (ClassFileEntry) startOfPool.get(itIndex);
213            indexCache.put(entry, Integer.valueOf(index));
214
215            if (entry instanceof CPLong || entry instanceof CPDouble) {
216                entries.add(entry); // these get 2 slots because of their size
217                entries.add(entry);
218                index += 2;
219            } else {
220                entries.add(entry);
221                index += 1;
222            }
223        }
224
225        for (int itFinal = 0; itFinal < finalSort.size(); itFinal++) {
226            final ClassFileEntry entry = (ClassFileEntry) finalSort.get(itFinal);
227            indexCache.put(entry, Integer.valueOf(index));
228
229            if (entry instanceof CPLong || entry instanceof CPDouble) {
230                entries.add(entry); // these get 2 slots because of their size
231                entries.add(entry);
232                index += 2;
233            } else {
234                entries.add(entry);
235                index += 1;
236            }
237        }
238
239    }
240
241    public ClassFileEntry addWithNestedEntries(final ClassFileEntry entry) {
242        add(entry);
243        final ClassFileEntry[] nestedEntries = entry.getNestedClassFileEntries();
244        for (int i = 0; i < nestedEntries.length; i++) {
245            addWithNestedEntries(nestedEntries[i]);
246        }
247        return entry;
248    }
249}