001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with this 004 * work for additional information regarding copyright ownership. The ASF 005 * licenses this file to You under the Apache License, Version 2.0 (the 006 * "License"); you may not use this file except in compliance with the License. 007 * 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, WITHOUT 013 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 014 * License for the specific language governing permissions and limitations under 015 * the License. 016 */ 017package org.apache.commons.compress.harmony.pack200; 018 019import java.io.IOException; 020import java.io.OutputStream; 021import java.util.ArrayList; 022import java.util.Iterator; 023import java.util.List; 024 025/** 026 * A group of metadata (annotation) bands, such as class_RVA_bands, method_AD_bands etc. 027 */ 028public class MetadataBandGroup extends BandSet { 029 030 public static final int CONTEXT_CLASS = 0; 031 public static final int CONTEXT_FIELD = 1; 032 public static final int CONTEXT_METHOD = 2; 033 034 private final String type; 035 private int numBackwardsCalls; 036 037 public IntList param_NB = new IntList(); // TODO: Lazy instantiation? 038 public IntList anno_N = new IntList(); 039 public List<CPSignature> type_RS = new ArrayList<>(); 040 public IntList pair_N = new IntList(); 041 public List<CPUTF8> name_RU = new ArrayList<>(); 042 public List<String> T = new ArrayList<>(); 043 public List<CPConstant<?>> caseI_KI = new ArrayList<>(); 044 public List<CPConstant<?>> caseD_KD = new ArrayList<>(); 045 public List<CPConstant<?>> caseF_KF = new ArrayList<>(); 046 public List<CPConstant<?>> caseJ_KJ = new ArrayList<>(); 047 public List<CPSignature> casec_RS = new ArrayList<>(); 048 public List<CPSignature> caseet_RS = new ArrayList<>(); 049 public List<CPUTF8> caseec_RU = new ArrayList<>(); 050 public List<CPUTF8> cases_RU = new ArrayList<>(); 051 public IntList casearray_N = new IntList(); 052 public List<CPSignature> nesttype_RS = new ArrayList<>(); 053 public IntList nestpair_N = new IntList(); 054 public List<CPUTF8> nestname_RU = new ArrayList<>(); 055 056 private final CpBands cpBands; 057 private final int context; 058 059 /** 060 * Constructs a new MetadataBandGroup 061 * 062 * @param type must be either AD, RVA, RIA, RVPA or RIPA. 063 * @param context {@code CONTEXT_CLASS}, {@code CONTEXT_METHOD} or {@code CONTEXT_FIELD} 064 * @param cpBands constant pool bands 065 * @param segmentHeader segment header 066 * @param effort packing effort 067 */ 068 public MetadataBandGroup(final String type, final int context, final CpBands cpBands, final SegmentHeader segmentHeader, final int effort) { 069 super(effort, segmentHeader); 070 this.type = type; 071 this.cpBands = cpBands; 072 this.context = context; 073 } 074 075 /** 076 * Add an annotation to this set of bands 077 * 078 * @param desc TODO 079 * @param nameRU TODO 080 * @param tags TODO 081 * @param values TODO 082 * @param caseArrayN TODO 083 * @param nestTypeRS TODO 084 * @param nestNameRU TODO 085 * @param nestPairN TODO 086 */ 087 public void addAnnotation(final String desc, final List<String> nameRU, final List<String> tags, final List<Object> values, final List<Integer> caseArrayN, 088 final List<String> nestTypeRS, final List<String> nestNameRU, final List<Integer> nestPairN) { 089 type_RS.add(cpBands.getCPSignature(desc)); 090 pair_N.add(nameRU.size()); 091 nameRU.forEach(name -> name_RU.add(cpBands.getCPUtf8(name))); 092 093 final Iterator<Object> valuesIterator = values.iterator(); 094 for (final String tag : tags) { 095 T.add(tag); 096 switch (tag) { 097 case "B": 098 case "C": 099 case "I": 100 case "S": 101 case "Z": { 102 caseI_KI.add(cpBands.getConstant(valuesIterator.next())); 103 break; 104 } 105 case "D": { 106 caseD_KD.add(cpBands.getConstant(valuesIterator.next())); 107 break; 108 } 109 case "F": { 110 caseF_KF.add(cpBands.getConstant(valuesIterator.next())); 111 break; 112 } 113 case "J": { 114 caseJ_KJ.add(cpBands.getConstant(valuesIterator.next())); 115 break; 116 } 117 case "c": { 118 casec_RS.add(cpBands.getCPSignature(nextString(valuesIterator))); 119 break; 120 } 121 case "e": { 122 caseet_RS.add(cpBands.getCPSignature(nextString(valuesIterator))); 123 caseec_RU.add(cpBands.getCPUtf8(nextString(valuesIterator))); 124 break; 125 } 126 case "s": { 127 cases_RU.add(cpBands.getCPUtf8(nextString(valuesIterator))); 128 break; 129 } 130 } 131 // do nothing here for [ or @ (handled below) 132 } 133 for (final Integer element : caseArrayN) { 134 final int arraySize = element.intValue(); 135 casearray_N.add(arraySize); 136 numBackwardsCalls += arraySize; 137 } 138 nestTypeRS.forEach(element -> nesttype_RS.add(cpBands.getCPSignature(element))); 139 nestNameRU.forEach(element -> nestname_RU.add(cpBands.getCPUtf8(element))); 140 for (final Integer numPairs : nestPairN) { 141 nestpair_N.add(numPairs.intValue()); 142 numBackwardsCalls += numPairs.intValue(); 143 } 144 } 145 146 /** 147 * Add an annotation to this set of bands. 148 * 149 * @param numParams TODO 150 * @param annoN TODO 151 * @param pairN TODO 152 * @param typeRS TODO 153 * @param nameRU TODO 154 * @param tags TODO 155 * @param values TODO 156 * @param caseArrayN TODO 157 * @param nestTypeRS TODO 158 * @param nestNameRU TODO 159 * @param nestPairN TODO 160 */ 161 public void addParameterAnnotation(final int numParams, final int[] annoN, final IntList pairN, final List<String> typeRS, final List<String> nameRU, 162 final List<String> tags, final List<Object> values, final List<Integer> caseArrayN, final List<String> nestTypeRS, final List<String> nestNameRU, 163 final List<Integer> nestPairN) { 164 param_NB.add(numParams); 165 for (final int element : annoN) { 166 anno_N.add(element); 167 } 168 pair_N.addAll(pairN); 169 typeRS.forEach(desc -> type_RS.add(cpBands.getCPSignature(desc))); 170 nameRU.forEach(name -> name_RU.add(cpBands.getCPUtf8(name))); 171 final Iterator<Object> valuesIterator = values.iterator(); 172 for (final String tag : tags) { 173 T.add(tag); 174 switch (tag) { 175 case "B": 176 case "C": 177 case "I": 178 case "S": 179 case "Z": { 180 caseI_KI.add(cpBands.getConstant(valuesIterator.next())); 181 break; 182 } 183 case "D": { 184 caseD_KD.add(cpBands.getConstant(valuesIterator.next())); 185 break; 186 } 187 case "F": { 188 caseF_KF.add(cpBands.getConstant(valuesIterator.next())); 189 break; 190 } 191 case "J": { 192 caseJ_KJ.add(cpBands.getConstant(valuesIterator.next())); 193 break; 194 } 195 case "c": { 196 casec_RS.add(cpBands.getCPSignature(nextString(valuesIterator))); 197 break; 198 } 199 case "e": { 200 caseet_RS.add(cpBands.getCPSignature(nextString(valuesIterator))); 201 caseec_RU.add(cpBands.getCPUtf8(nextString(valuesIterator))); 202 break; 203 } 204 case "s": { 205 cases_RU.add(cpBands.getCPUtf8(nextString(valuesIterator))); 206 break; 207 } 208 } 209 // do nothing here for [ or @ (handled below) 210 } 211 for (final Integer element : caseArrayN) { 212 final int arraySize = element.intValue(); 213 casearray_N.add(arraySize); 214 numBackwardsCalls += arraySize; 215 } 216 nestTypeRS.forEach(type -> nesttype_RS.add(cpBands.getCPSignature(type))); 217 nestNameRU.forEach(name -> nestname_RU.add(cpBands.getCPUtf8(name))); 218 for (final Integer numPairs : nestPairN) { 219 nestpair_N.add(numPairs.intValue()); 220 numBackwardsCalls += numPairs.intValue(); 221 } 222 } 223 224 /** 225 * Returns true if any annotations have been added to this set of bands. 226 * 227 * @return true if any annotations have been added to this set of bands. 228 */ 229 public boolean hasContent() { 230 return type_RS.size() > 0; 231 } 232 233 public void incrementAnnoN() { 234 anno_N.increment(anno_N.size() - 1); 235 } 236 237 public void newEntryInAnnoN() { 238 anno_N.add(1); 239 } 240 241 private String nextString(final Iterator<Object> valuesIterator) { 242 return (String) valuesIterator.next(); 243 } 244 245 public int numBackwardsCalls() { 246 return numBackwardsCalls; 247 } 248 249 /* 250 * (non-Javadoc) 251 * 252 * @see org.apache.commons.compress.harmony.pack200.BandSet#pack(java.io.OutputStream) 253 */ 254 @Override 255 public void pack(final OutputStream out) throws IOException, Pack200Exception { 256 PackingUtils.log("Writing metadata band group..."); 257 if (hasContent()) { 258 String contextStr; 259 if (context == CONTEXT_CLASS) { 260 contextStr = "Class"; 261 } else if (context == CONTEXT_FIELD) { 262 contextStr = "Field"; 263 } else { 264 contextStr = "Method"; 265 } 266 byte[] encodedBand; 267 if (!type.equals("AD")) { 268 if (type.indexOf('P') != -1) { 269 // Parameter annotation so we need to transmit param_NB 270 encodedBand = encodeBandInt(contextStr + "_" + type + " param_NB", param_NB.toArray(), Codec.BYTE1); 271 out.write(encodedBand); 272 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " anno_N[" + param_NB.size() + "]"); 273 } 274 encodedBand = encodeBandInt(contextStr + "_" + type + " anno_N", anno_N.toArray(), Codec.UNSIGNED5); 275 out.write(encodedBand); 276 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " anno_N[" + anno_N.size() + "]"); 277 278 encodedBand = encodeBandInt(contextStr + "_" + type + " type_RS", cpEntryListToArray(type_RS), Codec.UNSIGNED5); 279 out.write(encodedBand); 280 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " type_RS[" + type_RS.size() + "]"); 281 282 encodedBand = encodeBandInt(contextStr + "_" + type + " pair_N", pair_N.toArray(), Codec.UNSIGNED5); 283 out.write(encodedBand); 284 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " pair_N[" + pair_N.size() + "]"); 285 286 encodedBand = encodeBandInt(contextStr + "_" + type + " name_RU", cpEntryListToArray(name_RU), Codec.UNSIGNED5); 287 out.write(encodedBand); 288 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " name_RU[" + name_RU.size() + "]"); 289 } 290 encodedBand = encodeBandInt(contextStr + "_" + type + " T", tagListToArray(T), Codec.BYTE1); 291 out.write(encodedBand); 292 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " T[" + T.size() + "]"); 293 294 encodedBand = encodeBandInt(contextStr + "_" + type + " caseI_KI", cpEntryListToArray(caseI_KI), Codec.UNSIGNED5); 295 out.write(encodedBand); 296 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseI_KI[" + caseI_KI.size() + "]"); 297 298 encodedBand = encodeBandInt(contextStr + "_" + type + " caseD_KD", cpEntryListToArray(caseD_KD), Codec.UNSIGNED5); 299 out.write(encodedBand); 300 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseD_KD[" + caseD_KD.size() + "]"); 301 302 encodedBand = encodeBandInt(contextStr + "_" + type + " caseF_KF", cpEntryListToArray(caseF_KF), Codec.UNSIGNED5); 303 out.write(encodedBand); 304 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseF_KF[" + caseF_KF.size() + "]"); 305 306 encodedBand = encodeBandInt(contextStr + "_" + type + " caseJ_KJ", cpEntryListToArray(caseJ_KJ), Codec.UNSIGNED5); 307 out.write(encodedBand); 308 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseJ_KJ[" + caseJ_KJ.size() + "]"); 309 310 encodedBand = encodeBandInt(contextStr + "_" + type + " casec_RS", cpEntryListToArray(casec_RS), Codec.UNSIGNED5); 311 out.write(encodedBand); 312 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " casec_RS[" + casec_RS.size() + "]"); 313 314 encodedBand = encodeBandInt(contextStr + "_" + type + " caseet_RS", cpEntryListToArray(caseet_RS), Codec.UNSIGNED5); 315 out.write(encodedBand); 316 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseet_RS[" + caseet_RS.size() + "]"); 317 318 encodedBand = encodeBandInt(contextStr + "_" + type + " caseec_RU", cpEntryListToArray(caseec_RU), Codec.UNSIGNED5); 319 out.write(encodedBand); 320 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseec_RU[" + caseec_RU.size() + "]"); 321 322 encodedBand = encodeBandInt(contextStr + "_" + type + " cases_RU", cpEntryListToArray(cases_RU), Codec.UNSIGNED5); 323 out.write(encodedBand); 324 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " cases_RU[" + cases_RU.size() + "]"); 325 326 encodedBand = encodeBandInt(contextStr + "_" + type + " casearray_N", casearray_N.toArray(), Codec.UNSIGNED5); 327 out.write(encodedBand); 328 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " casearray_N[" + casearray_N.size() + "]"); 329 330 encodedBand = encodeBandInt(contextStr + "_" + type + " nesttype_RS", cpEntryListToArray(nesttype_RS), Codec.UNSIGNED5); 331 out.write(encodedBand); 332 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " nesttype_RS[" + nesttype_RS.size() + "]"); 333 334 encodedBand = encodeBandInt(contextStr + "_" + type + " nestpair_N", nestpair_N.toArray(), Codec.UNSIGNED5); 335 out.write(encodedBand); 336 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " nestpair_N[" + nestpair_N.size() + "]"); 337 338 encodedBand = encodeBandInt(contextStr + "_" + type + " nestname_RU", cpEntryListToArray(nestname_RU), Codec.UNSIGNED5); 339 out.write(encodedBand); 340 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " nestname_RU[" + nestname_RU.size() + "]"); 341 } 342 } 343 344 /** 345 * Remove the latest annotation that was added to this group 346 */ 347 public void removeLatest() { 348 final int latest = anno_N.remove(anno_N.size() - 1); 349 for (int i = 0; i < latest; i++) { 350 type_RS.remove(type_RS.size() - 1); 351 final int pairs = pair_N.remove(pair_N.size() - 1); 352 for (int j = 0; j < pairs; j++) { 353 removeOnePair(); 354 } 355 } 356 } 357 358 /* 359 * Convenience method for removeLatest 360 */ 361 private void removeOnePair() { 362 final String tag = T.remove(T.size() - 1); 363 switch (tag) { 364 case "B": 365 case "C": 366 case "I": 367 case "S": 368 case "Z": 369 caseI_KI.remove(caseI_KI.size() - 1); 370 break; 371 case "D": 372 caseD_KD.remove(caseD_KD.size() - 1); 373 break; 374 case "F": 375 caseF_KF.remove(caseF_KF.size() - 1); 376 break; 377 case "J": 378 caseJ_KJ.remove(caseJ_KJ.size() - 1); 379 break; 380 case "e": 381 caseet_RS.remove(caseet_RS.size() - 1); 382 caseec_RU.remove(caseet_RS.size() - 1); 383 break; 384 case "s": 385 cases_RU.remove(cases_RU.size() - 1); 386 break; 387 case "[": 388 final int arraySize = casearray_N.remove(casearray_N.size() - 1); 389 numBackwardsCalls -= arraySize; 390 for (int k = 0; k < arraySize; k++) { 391 removeOnePair(); 392 } 393 break; 394 case "@": 395 nesttype_RS.remove(nesttype_RS.size() - 1); 396 final int numPairs = nestpair_N.remove(nestpair_N.size() - 1); 397 numBackwardsCalls -= numPairs; 398 for (int i = 0; i < numPairs; i++) { 399 removeOnePair(); 400 } 401 break; 402 } 403 } 404 405 private int[] tagListToArray(final List<String> list) { 406 return list.stream().mapToInt(s -> s.charAt(0)).toArray(); 407 } 408 409}