    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}