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}