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