View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with this
4    * work for additional information regarding copyright ownership. The ASF
5    * licenses this file to You under the Apache License, Version 2.0 (the
6    * "License"); you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    * http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
16   */
17  package org.apache.commons.compress.harmony.pack200;
18  
19  import java.io.IOException;
20  import java.io.OutputStream;
21  import java.util.ArrayList;
22  import java.util.Iterator;
23  import java.util.List;
24  
25  /**
26   * A group of metadata (annotation) bands, such as class_RVA_bands, method_AD_bands etc.
27   */
28  public class MetadataBandGroup extends BandSet {
29  
30      public static final int CONTEXT_CLASS = 0;
31      public static final int CONTEXT_FIELD = 1;
32      public static final int CONTEXT_METHOD = 2;
33  
34      private final String type;
35      private int numBackwardsCalls;
36  
37      public IntList param_NB = new IntList(); // TODO: Lazy instantiation?
38      public IntList anno_N = new IntList();
39      public List<CPSignature> type_RS = new ArrayList<>();
40      public IntList pair_N = new IntList();
41      public List<CPUTF8> name_RU = new ArrayList<>();
42      public List<String> T = new ArrayList<>();
43      public List<CPConstant<?>> caseI_KI = new ArrayList<>();
44      public List<CPConstant<?>> caseD_KD = new ArrayList<>();
45      public List<CPConstant<?>> caseF_KF = new ArrayList<>();
46      public List<CPConstant<?>> caseJ_KJ = new ArrayList<>();
47      public List<CPSignature> casec_RS = new ArrayList<>();
48      public List<CPSignature> caseet_RS = new ArrayList<>();
49      public List<CPUTF8> caseec_RU = new ArrayList<>();
50      public List<CPUTF8> cases_RU = new ArrayList<>();
51      public IntList casearray_N = new IntList();
52      public List<CPSignature> nesttype_RS = new ArrayList<>();
53      public IntList nestpair_N = new IntList();
54      public List<CPUTF8> nestname_RU = new ArrayList<>();
55  
56      private final CpBands cpBands;
57      private final int context;
58  
59      /**
60       * Constructs a new MetadataBandGroup
61       *
62       * @param type          must be either AD, RVA, RIA, RVPA or RIPA.
63       * @param context       {@code CONTEXT_CLASS}, {@code CONTEXT_METHOD} or {@code CONTEXT_FIELD}
64       * @param cpBands       constant pool bands
65       * @param segmentHeader segment header
66       * @param effort        packing effort
67       */
68      public MetadataBandGroup(final String type, final int context, final CpBands cpBands, final SegmentHeader segmentHeader, final int effort) {
69          super(effort, segmentHeader);
70          this.type = type;
71          this.cpBands = cpBands;
72          this.context = context;
73      }
74  
75      /**
76       * Add an annotation to this set of bands
77       *
78       * @param desc       TODO
79       * @param nameRU     TODO
80       * @param tags       TODO
81       * @param values     TODO
82       * @param caseArrayN TODO
83       * @param nestTypeRS TODO
84       * @param nestNameRU TODO
85       * @param nestPairN  TODO
86       */
87      public void addAnnotation(final String desc, final List<String> nameRU, final List<String> tags, final List<Object> values, final List<Integer> caseArrayN,
88              final List<String> nestTypeRS, final List<String> nestNameRU, final List<Integer> nestPairN) {
89          type_RS.add(cpBands.getCPSignature(desc));
90          pair_N.add(nameRU.size());
91          nameRU.forEach(name -> name_RU.add(cpBands.getCPUtf8(name)));
92  
93          final Iterator<Object> valuesIterator = values.iterator();
94          for (final String tag : tags) {
95              T.add(tag);
96              switch (tag) {
97              case "B":
98              case "C":
99              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 }