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
4    *  this work for additional information regarding copyright ownership.
5    *  The ASF licenses this file to You under the Apache License, Version 2.0
6    *  (the "License"); you may not use this file except in compliance with
7    *  the License.  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,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under 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.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Objects;
26  import java.util.Set;
27  import java.util.TreeSet;
28  
29  /**
30   * Inner class bands (corresponds to the {@code ic_bands} set of bands in the pack200 specification)
31   */
32  public class IcBands extends BandSet {
33  
34      static class IcTuple implements Comparable<IcTuple> {
35  
36          protected CPClass C; // this class
37          protected int F; // flags
38          protected CPClass C2; // outer class
39          protected CPUTF8 N; // name
40  
41          IcTuple(final CPClass C, final int F, final CPClass C2, final CPUTF8 N) {
42              this.C = C;
43              this.F = F;
44              this.C2 = C2;
45              this.N = N;
46          }
47  
48          @Override
49          public int compareTo(final IcTuple arg0) {
50              return C.compareTo(arg0.C);
51          }
52  
53          @Override
54          public boolean equals(final Object o) {
55              if (o instanceof IcTuple) {
56                  final IcTuple icT = (IcTuple) o;
57                  return C.equals(icT.C) && F == icT.F && Objects.equals(C2, icT.C2) && Objects.equals(N, icT.N);
58              }
59              return false;
60          }
61  
62          public boolean isAnonymous() {
63              final String className = C.toString();
64              final String innerName = className.substring(className.lastIndexOf('$') + 1);
65              return Character.isDigit(innerName.charAt(0));
66          }
67  
68          @Override
69          public String toString() {
70              return C.toString();
71          }
72  
73      }
74  
75      private final Set<IcTuple> innerClasses = new TreeSet<>();
76      private final CpBands cpBands;
77      private int bit16Count;
78  
79      private final Map<String, List<IcTuple>> outerToInner = new HashMap<>();
80  
81      public IcBands(final SegmentHeader segmentHeader, final CpBands cpBands, final int effort) {
82          super(effort, segmentHeader);
83          this.cpBands = cpBands;
84      }
85  
86      public void addInnerClass(final String name, final String outerName, final String innerName, int flags) {
87          if (outerName != null || innerName != null) {
88              if (namesArePredictable(name, outerName, innerName)) {
89                  final IcTuple innerClass = new IcTuple(cpBands.getCPClass(name), flags, null, null);
90                  addToMap(outerName, innerClass);
91                  innerClasses.add(innerClass);
92              } else {
93                  flags |= 1 << 16;
94                  final IcTuple icTuple = new IcTuple(cpBands.getCPClass(name), flags, cpBands.getCPClass(outerName), cpBands.getCPUtf8(innerName));
95                  final boolean added = innerClasses.add(icTuple);
96                  if (added) {
97                      bit16Count++;
98                      addToMap(outerName, icTuple);
99                  }
100             }
101         } else {
102             final IcTuple innerClass = new IcTuple(cpBands.getCPClass(name), flags, null, null);
103             addToMap(getOuter(name), innerClass);
104             innerClasses.add(innerClass);
105         }
106     }
107 
108     private void addToMap(final String outerName, final IcTuple icTuple) {
109         List<IcTuple> tuples = outerToInner.get(outerName);
110         if (tuples == null) {
111             tuples = new ArrayList<>();
112             outerToInner.put(outerName, tuples);
113         } else {
114             for (final IcTuple tuple : tuples) {
115                 if (icTuple.equals(tuple)) {
116                     return;
117                 }
118             }
119         }
120         tuples.add(icTuple);
121     }
122 
123     /**
124      * All input classes for the segment have now been read in, so this method is called so that this class can calculate/complete anything it could not do
125      * while classes were being read.
126      */
127     public void finaliseBands() {
128         segmentHeader.setIc_count(innerClasses.size());
129     }
130 
131     public IcTuple getIcTuple(final CPClass inner) {
132         for (final IcTuple icTuple : innerClasses) {
133             if (icTuple.C.equals(inner)) {
134                 return icTuple;
135             }
136         }
137         return null;
138     }
139 
140     public List<IcTuple> getInnerClassesForOuter(final String outerClassName) {
141         return outerToInner.get(outerClassName);
142     }
143 
144     private String getOuter(final String name) {
145         return name.substring(0, name.lastIndexOf('$'));
146     }
147 
148     private boolean namesArePredictable(final String name, final String outerName, final String innerName) {
149         // TODO: Could be multiple characters, not just $
150         return name.equals(outerName + '$' + innerName) && innerName.indexOf('$') == -1;
151     }
152 
153     @Override
154     public void pack(final OutputStream outputStream) throws IOException, Pack200Exception {
155         PackingUtils.log("Writing internal class bands...");
156         final int[] ic_this_class = new int[innerClasses.size()];
157         final int[] ic_flags = new int[innerClasses.size()];
158         final int[] ic_outer_class = new int[bit16Count];
159         final int[] ic_name = new int[bit16Count];
160 
161         int index2 = 0;
162         final List<IcTuple> innerClassesList = new ArrayList<>(innerClasses);
163         for (int i = 0; i < ic_this_class.length; i++) {
164             final IcTuple icTuple = innerClassesList.get(i);
165             ic_this_class[i] = icTuple.C.getIndex();
166             ic_flags[i] = icTuple.F;
167             if ((icTuple.F & 1 << 16) != 0) {
168                 ic_outer_class[index2] = icTuple.C2 == null ? 0 : icTuple.C2.getIndex() + 1;
169                 ic_name[index2] = icTuple.N == null ? 0 : icTuple.N.getIndex() + 1;
170                 index2++;
171             }
172         }
173         byte[] encodedBand = encodeBandInt("ic_this_class", ic_this_class, Codec.UDELTA5);
174         outputStream.write(encodedBand);
175         PackingUtils.log("Wrote " + encodedBand.length + " bytes from ic_this_class[" + ic_this_class.length + "]");
176 
177         encodedBand = encodeBandInt("ic_flags", ic_flags, Codec.UNSIGNED5);
178         outputStream.write(encodedBand);
179         PackingUtils.log("Wrote " + encodedBand.length + " bytes from ic_flags[" + ic_flags.length + "]");
180 
181         encodedBand = encodeBandInt("ic_outer_class", ic_outer_class, Codec.DELTA5);
182         outputStream.write(encodedBand);
183         PackingUtils.log("Wrote " + encodedBand.length + " bytes from ic_outer_class[" + ic_outer_class.length + "]");
184 
185         encodedBand = encodeBandInt("ic_name", ic_name, Codec.DELTA5);
186         outputStream.write(encodedBand);
187         PackingUtils.log("Wrote " + encodedBand.length + " bytes from ic_name[" + ic_name.length + "]");
188     }
189 
190 }