    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;
    018
    019import java.util.List;
    020
    021import org.apache.commons.compress.harmony.pack200.Pack200Exception;
    022import org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry;
    023import org.apache.commons.compress.harmony.unpack200.bytecode.ConstantPoolEntry;
    024
    025/**
    026 * SegmentConstantPool manages the constant pool used for re-creating class files.
    027 */
    028public class SegmentConstantPool {
    029
    030    public static final int ALL = 0;
    031    public static final int UTF_8 = 1;
    032
    033    public static final int CP_INT = 2;
    034
    035    // define in archive order
    036
    037    public static final int CP_FLOAT = 3;
    038    public static final int CP_LONG = 4;
    039    public static final int CP_DOUBLE = 5;
    040    public static final int CP_STRING = 6;
    041    public static final int CP_CLASS = 7;
    042    public static final int SIGNATURE = 8; // TODO and more to come --
    043    public static final int CP_DESCR = 9;
    044    public static final int CP_FIELD = 10;
    045    public static final int CP_METHOD = 11;
    046    public static final int CP_IMETHOD = 12;
    047    protected static final String REGEX_MATCH_ALL = ".*";
    048    protected static final String INITSTRING = "<init>";
    049    protected static final String REGEX_MATCH_INIT = "^" + INITSTRING + ".*";
    050
    051    /**
    052     * We don't want a dependency on regex in Pack200. The only place one exists is in matchSpecificPoolEntryIndex(). To eliminate this dependency, we've
    053     * implemented the world's stupidest regexMatch. It knows about the two forms we care about: .* (aka REGEX_MATCH_ALL) {@code ^<init>;.*} (aka
    054     * REGEX_MATCH_INIT) and will answer correctly if those are passed as the regexString.
    055     *
    056     * @param regexString   String against which the compareString will be matched
    057     * @param compareString String to match against the regexString
    058     * @return boolean true if the compareString matches the regexString; otherwise false.
    059     */
    060    protected static boolean regexMatches(final String regexString, final String compareString) {
    061        if (REGEX_MATCH_ALL.equals(regexString)) {
    062            return true;
    063        }
    064        if (REGEX_MATCH_INIT.equals(regexString)) {
    065            if (compareString.length() < INITSTRING.length()) {
    066                return false;
    067            }
    068            return INITSTRING.equals(compareString.substring(0, INITSTRING.length()));
    069        }
    070        throw new Error("regex trying to match a pattern I don't know: " + regexString);
    071    }
    072
    073    private final CpBands bands;
    074    private final SegmentConstantPoolArrayCache arrayCache = new SegmentConstantPoolArrayCache();
    075
    076    /**
    077     * @param bands TODO
    078     */
    079    public SegmentConstantPool(final CpBands bands) {
    080        this.bands = bands;
    081    }
    082
    083    /**
    084     * Given the name of a class, answer the CPClass associated with that class. Answer null if the class doesn't exist.
    085     *
    086     * @param name Class name to look for (form: java/lang/Object)
    087     * @return CPClass for that class name, or null if not found.
    088     */
    089    public ConstantPoolEntry getClassPoolEntry(final String name) {
    090        final String[] classes = bands.getCpClass();
    091        final int index = matchSpecificPoolEntryIndex(classes, name, 0);
    092        if (index == -1) {
    093            return null;
    094        }
    095        try {
    096            return getConstantPoolEntry(CP_CLASS, index);
    097        } catch (final Pack200Exception ex) {
    098            throw new Error("Error getting class pool entry");
    099        }
    100    }
    101
    102    /**
    103     * Subset the constant pool of the specified type to be just that which has the specified class name. Answer the ConstantPoolEntry at the desiredIndex of
    104     * the subsetted pool.
    105     *
    106     * @param cp               type of constant pool array to search
    107     * @param desiredIndex     index of the constant pool
    108     * @param desiredClassName class to use to generate a subset of the pool
    109     * @return ConstantPoolEntry
    110     * @throws Pack200Exception TODO
    111     */
    112    public ConstantPoolEntry getClassSpecificPoolEntry(final int cp, final long desiredIndex, final String desiredClassName) throws Pack200Exception {
    113        final int index = (int) desiredIndex;
    114        int realIndex = -1;
    115        String[] array;
    116        switch (cp) {
    117        case CP_FIELD:
    118            array = bands.getCpFieldClass();
    119            break;
    120        case CP_METHOD:
    121            array = bands.getCpMethodClass();
    122            break;
    123        case CP_IMETHOD:
    124            array = bands.getCpIMethodClass();
    125            break;
    126        default:
    127            throw new Error("Don't know how to handle " + cp);
    128        }
    129        realIndex = matchSpecificPoolEntryIndex(array, desiredClassName, index);
    130        return getConstantPoolEntry(cp, realIndex);
    131    }
    132
    133    public ConstantPoolEntry getConstantPoolEntry(final int cp, final long value) throws Pack200Exception {
    134        final int index = (int) value;
    135        if (index == -1) {
    136            return null;
    137        }
    138        if (index < 0) {
    139            throw new Pack200Exception("Cannot have a negative range");
    140        }
    141        switch (cp) {
    142        case UTF_8:
    143            return bands.cpUTF8Value(index);
    144        case CP_INT:
    145            return bands.cpIntegerValue(index);
    146        case CP_FLOAT:
    147            return bands.cpFloatValue(index);
    148        case CP_LONG:
    149            return bands.cpLongValue(index);
    150        case CP_DOUBLE:
    151            return bands.cpDoubleValue(index);
    152        case CP_STRING:
    153            return bands.cpStringValue(index);
    154        case CP_CLASS:
    155            return bands.cpClassValue(index);
    156        case SIGNATURE:
    157            throw new Error("I don't know what to do with signatures yet");
    158        // return null /* new CPSignature(bands.getCpSignature()[index]) */;
    159        case CP_DESCR:
    160            throw new Error("I don't know what to do with descriptors yet");
    161        // return null /* new CPDescriptor(bands.getCpDescriptor()[index])
    162        // */;
    163        case CP_FIELD:
    164            return bands.cpFieldValue(index);
    165        case CP_METHOD:
    166            return bands.cpMethodValue(index);
    167        case CP_IMETHOD:
    168            return bands.cpIMethodValue(index);
    169        default:
    170            break;
    171        }
    172        // etc
    173        throw new Error("Get value incomplete");
    174    }
    175
    176    /**
    177     * Answer the init method for the specified class.
    178     *
    179     * @param cp               constant pool to search (must be CP_METHOD)
    180     * @param value            index of init method
    181     * @param desiredClassName String class name of the init method
    182     * @return CPMethod init method
    183     * @throws Pack200Exception TODO
    184     */
    185    public ConstantPoolEntry getInitMethodPoolEntry(final int cp, final long value, final String desiredClassName) throws Pack200Exception {
    186        int realIndex = -1;
    187        if (cp != CP_METHOD) {
    188            // TODO really an error?
    189            throw new Error("Nothing but CP_METHOD can be an <init>");
    190        }
    191        realIndex = matchSpecificPoolEntryIndex(bands.getCpMethodClass(), bands.getCpMethodDescriptor(), desiredClassName, REGEX_MATCH_INIT, (int) value);
    192        return getConstantPoolEntry(cp, realIndex);
    193    }
    194
    195    public ClassFileEntry getValue(final int cp, final long value) throws Pack200Exception {
    196        final int index = (int) value;
    197        if (index == -1) {
    198            return null;
    199        }
    200        if (index < 0) {
    201            throw new Pack200Exception("Cannot have a negative range");
    202        }
    203        switch (cp) {
    204        case UTF_8:
    205            return bands.cpUTF8Value(index);
    206        case CP_INT:
    207            return bands.cpIntegerValue(index);
    208        case CP_FLOAT:
    209            return bands.cpFloatValue(index);
    210        case CP_LONG:
    211            return bands.cpLongValue(index);
    212        case CP_DOUBLE:
    213            return bands.cpDoubleValue(index);
    214        case CP_STRING:
    215            return bands.cpStringValue(index);
    216        case CP_CLASS:
    217            return bands.cpClassValue(index);
    218        case SIGNATURE:
    219            return bands.cpSignatureValue(index);
    220        case CP_DESCR:
    221            return bands.cpNameAndTypeValue(index);
    222        default:
    223            break;
    224        }
    225        throw new Error("Tried to get a value I don't know about: " + cp);
    226    }
    227
    228    /**
    229     * A number of things make use of subsets of structures. In one particular example, _super bytecodes will use a subset of method or field classes which have
    230     * just those methods / fields defined in the superclass. Similarly, _this bytecodes use just those methods/fields defined in this class, and _init
    231     * bytecodes use just those methods that start with {@code <init>}.
    232     *
    233     * This method takes an array of names, a String to match for, an index and a boolean as parameters, and answers the array position in the array of the
    234     * indexth element which matches (or equals) the String (depending on the state of the boolean)
    235     *
    236     * In other words, if the class array consists of: Object [position 0, 0th instance of Object] String [position 1, 0th instance of String] String [position
    237     * 2, 1st instance of String] Object [position 3, 1st instance of Object] Object [position 4, 2nd instance of Object] then matchSpecificPoolEntryIndex(...,
    238     * "Object", 2, false) will answer 4. matchSpecificPoolEntryIndex(..., "String", 0, false) will answer 1.
    239     *
    240     * @param nameArray     Array of Strings against which the compareString is tested
    241     * @param compareString String for which to search
    242     * @param desiredIndex  nth element with that match (counting from 0)
    243     * @return int index into nameArray, or -1 if not found.
    244     */
    245    protected int matchSpecificPoolEntryIndex(final String[] nameArray, final String compareString, final int desiredIndex) {
    246        return matchSpecificPoolEntryIndex(nameArray, nameArray, compareString, REGEX_MATCH_ALL, desiredIndex);
    247    }
    248
    249    /**
    250     * This method's function is to look through pairs of arrays. It keeps track of the number of hits it finds using the following basis of comparison for a
    251     * hit: - the primaryArray[index] must be .equals() to the primaryCompareString - the secondaryArray[index] .matches() the secondaryCompareString. When the
    252     * desiredIndex number of hits has been reached, the index into the original two arrays of the element hit is returned.
    253     *
    254     * @param primaryArray          The first array to search
    255     * @param secondaryArray        The second array (must be same .length as primaryArray)
    256     * @param primaryCompareString  The String to compare against primaryArray using .equals()
    257     * @param secondaryCompareRegex The String to compare against secondaryArray using .matches()
    258     * @param desiredIndex          The nth hit whose position we're seeking
    259     * @return int index that represents the position of the nth hit in primaryArray and secondaryArray
    260     */
    261    protected int matchSpecificPoolEntryIndex(final String[] primaryArray, final String[] secondaryArray, final String primaryCompareString,
    262            final String secondaryCompareRegex, final int desiredIndex) {
    263        int instanceCount = -1;
    264        final List<Integer> indexList = arrayCache.indexesForArrayKey(primaryArray, primaryCompareString);
    265        if (indexList.isEmpty()) {
    266            // Primary key not found, no chance of finding secondary
    267            return -1;
    268        }
    269
    270        for (final Integer element : indexList) {
    271            final int arrayIndex = element.intValue();
    272            if (regexMatches(secondaryCompareRegex, secondaryArray[arrayIndex])) {
    273                instanceCount++;
    274                if (instanceCount == desiredIndex) {
    275                    return arrayIndex;
    276                }
    277            }
    278        }
    279        // We didn't return in the for loop, so the desiredMatch
    280        // with desiredIndex must not exist in the arrays.
    281        return -1;
    282    }
    283}