    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.io.IOException;
    020import java.io.InputStream;
    021import java.util.ArrayList;
    022import java.util.Collections;
    023import java.util.HashMap;
    024import java.util.HashSet;
    025import java.util.List;
    026import java.util.Map;
    027import java.util.Set;
    028
    029import org.apache.commons.compress.harmony.pack200.Codec;
    030import org.apache.commons.compress.harmony.pack200.Pack200Exception;
    031import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass;
    032import org.apache.commons.compress.harmony.unpack200.bytecode.ClassConstantPool;
    033import org.apache.commons.compress.harmony.unpack200.bytecode.ConstantPoolEntry;
    034
    035/**
    036 * Inner Class Bands
    037 */
    038public class IcBands extends BandSet {
    039
    040    private IcTuple[] icAll;
    041
    042    private final String[] cpUTF8;
    043
    044    private final String[] cpClass;
    045
    046    private Map thisClassToTuple;
    047    private Map outerClassToTuples;
    048
    049    /**
    050     * @param segment TODO
    051     */
    052    public IcBands(final Segment segment) {
    053        super(segment);
    054        this.cpClass = segment.getCpBands().getCpClass();
    055        this.cpUTF8 = segment.getCpBands().getCpUTF8();
    056    }
    057
    058    /*
    059     * (non-Javadoc)
    060     *
    061     * @see org.apache.commons.compress.harmony.unpack200.BandSet#unpack(java.io.InputStream)
    062     */
    063    @Override
    064    public void read(final InputStream in) throws IOException, Pack200Exception {
    065        // Read IC bands
    066        final int innerClassCount = header.getInnerClassCount();
    067        final int[] icThisClassInts = decodeBandInt("ic_this_class", in, Codec.UDELTA5, innerClassCount);
    068        final String[] icThisClass = getReferences(icThisClassInts, cpClass);
    069        final int[] icFlags = decodeBandInt("ic_flags", in, Codec.UNSIGNED5, innerClassCount);
    070        final int outerClasses = SegmentUtils.countBit16(icFlags);
    071        final int[] icOuterClassInts = decodeBandInt("ic_outer_class", in, Codec.DELTA5, outerClasses);
    072        final String[] icOuterClass = new String[outerClasses];
    073        for (int i = 0; i < icOuterClass.length; i++) {
    074            if (icOuterClassInts[i] == 0) {
    075                icOuterClass[i] = null;
    076            } else {
    077                icOuterClass[i] = cpClass[icOuterClassInts[i] - 1];
    078            }
    079        }
    080        final int[] icNameInts = decodeBandInt("ic_name", in, Codec.DELTA5, outerClasses);
    081        final String[] icName = new String[outerClasses];
    082        for (int i = 0; i < icName.length; i++) {
    083            if (icNameInts[i] == 0) {
    084                icName[i] = null;
    085            } else {
    086                icName[i] = cpUTF8[icNameInts[i] - 1];
    087            }
    088        }
    089
    090        // Construct IC tuples
    091        icAll = new IcTuple[icThisClass.length];
    092        int index = 0;
    093        for (int i = 0; i < icThisClass.length; i++) {
    094            final String icTupleC = icThisClass[i];
    095            final int icTupleF = icFlags[i];
    096            String icTupleC2 = null;
    097            String icTupleN = null;
    098            final int cIndex = icThisClassInts[i];
    099            int c2Index = -1;
    100            int nIndex = -1;
    101            if ((icFlags[i] & 1 << 16) != 0) {
    102                icTupleC2 = icOuterClass[index];
    103                icTupleN = icName[index];
    104                c2Index = icOuterClassInts[index] - 1;
    105                nIndex = icNameInts[index] - 1;
    106                index++;
    107            }
    108            icAll[i] = new IcTuple(icTupleC, icTupleF, icTupleC2, icTupleN, cIndex, c2Index, nIndex, i);
    109        }
    110    }
    111
    112    @Override
    113    public void unpack() throws IOException, Pack200Exception {
    114        final IcTuple[] allTuples = getIcTuples();
    115        thisClassToTuple = new HashMap(allTuples.length);
    116        outerClassToTuples = new HashMap(allTuples.length);
    117        for (int index = 0; index < allTuples.length; index++) {
    118
    119            final IcTuple tuple = allTuples[index];
    120
    121            // generate mapping thisClassString -> IcTuple
    122            // presumably this relation is 1:1
    123            //
    124            final Object result = thisClassToTuple.put(tuple.thisClassString(), tuple);
    125            if (result != null) {
    126                throw new Error("Collision detected in <thisClassString, IcTuple> mapping. "
    127                    + "There are at least two inner clases with the same name.");
    128            }
    129
    130            // generate mapping outerClassString -> IcTuple
    131            // this relation is 1:M
    132
    133            // If it's not anon and the outer is not anon, it could be relevant
    134            if ((!tuple.isAnonymous() && !tuple.outerIsAnonymous()) || (tuple.nestedExplicitFlagSet())) {
    135
    136                // add tuple to corresponding bucket
    137                final String key = tuple.outerClassString();
    138                List bucket = (List) outerClassToTuples.get(key);
    139                if (bucket == null) {
    140                    bucket = new ArrayList();
    141                    outerClassToTuples.put(key, bucket);
    142                }
    143                bucket.add(tuple);
    144            }
    145        }
    146    }
    147
    148    public IcTuple[] getIcTuples() {
    149        return icAll;
    150    }
    151
    152    /**
    153     * Answer the relevant IcTuples for the specified className and class constant pool.
    154     *
    155     * @param className String name of the class X for ic_relevant(X)
    156     * @param cp ClassConstantPool used to generate ic_relevant(X)
    157     * @return array of IcTuple
    158     */
    159    public IcTuple[] getRelevantIcTuples(final String className, final ClassConstantPool cp) {
    160        final Set relevantTuplesContains = new HashSet();
    161        final List relevantTuples = new ArrayList();
    162
    163        final List relevantCandidates = (List) outerClassToTuples.get(className);
    164        if (relevantCandidates != null) {
    165            for (int index = 0; index < relevantCandidates.size(); index++) {
    166                final IcTuple tuple = (IcTuple) relevantCandidates.get(index);
    167                relevantTuplesContains.add(tuple);
    168                relevantTuples.add(tuple);
    169            }
    170        }
    171
    172        final List entries = cp.entries();
    173
    174        // For every class constant in both ic_this_class and cp,
    175        // add it to ic_relevant. Repeat until no more
    176        // changes to ic_relevant.
    177
    178        for (int eIndex = 0; eIndex < entries.size(); eIndex++) {
    179            final ConstantPoolEntry entry = (ConstantPoolEntry) entries.get(eIndex);
    180            if (entry instanceof CPClass) {
    181                final CPClass clazz = (CPClass) entry;
    182                final IcTuple relevant = (IcTuple) thisClassToTuple.get(clazz.name);
    183                if (relevant != null && relevantTuplesContains.add(relevant)) {
    184                    relevantTuples.add(relevant);
    185                }
    186            }
    187        }
    188
    189        // Not part of spec: fix up by adding to relevantTuples the parents
    190        // of inner classes which are themselves inner classes.
    191        // i.e., I think that if Foo$Bar$Baz gets added, Foo$Bar needs to be
    192        // added
    193        // as well.
    194
    195        final ArrayList tuplesToScan = new ArrayList(relevantTuples);
    196        final ArrayList tuplesToAdd = new ArrayList();
    197
    198        while (tuplesToScan.size() > 0) {
    199
    200            tuplesToAdd.clear();
    201            for (int index = 0; index < tuplesToScan.size(); index++) {
    202                final IcTuple aRelevantTuple = (IcTuple) tuplesToScan.get(index);
    203                final IcTuple relevant = (IcTuple) thisClassToTuple.get(aRelevantTuple.outerClassString());
    204                if (relevant != null && !aRelevantTuple.outerIsAnonymous()) {
    205                    tuplesToAdd.add(relevant);
    206                }
    207            }
    208
    209            tuplesToScan.clear();
    210            for (int index = 0; index < tuplesToAdd.size(); index++) {
    211                final IcTuple tuple = (IcTuple) tuplesToAdd.get(index);
    212                if (relevantTuplesContains.add(tuple)) {
    213                    relevantTuples.add(tuple);
    214                    tuplesToScan.add(tuple);
    215                }
    216            }
    217
    218        }
    219
    220        // End not part of the spec. Ugh.
    221
    222        // Now order the result as a subsequence of ic_all
    223        Collections.sort(relevantTuples, (arg0, arg1) -> {
    224            final Integer index1 = Integer.valueOf(((IcTuple) arg0).getTupleIndex());
    225            final Integer index2 = Integer.valueOf(((IcTuple) arg1).getTupleIndex());
    226            return index1.compareTo(index2);
    227        });
    228
    229        final IcTuple[] relevantTuplesArray = new IcTuple[relevantTuples.size()];
    230        for (int i = 0; i < relevantTuplesArray.length; i++) {
    231            relevantTuplesArray[i] = (IcTuple) relevantTuples.get(i);
    232        }
    233
    234        return relevantTuplesArray;
    235    }
    236
    237}