1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.commons.compress.harmony.pack200;
20
21 import java.io.IOException;
22 import java.io.OutputStream;
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Objects;
28 import java.util.Set;
29 import java.util.TreeSet;
30
31
32
33
34 public class IcBands extends BandSet {
35
36 static final class IcTuple implements Comparable<IcTuple> {
37
38 protected CPClass C;
39 protected int F;
40 protected CPClass C2;
41 protected CPUTF8 N;
42
43 IcTuple(final CPClass C, final int F, final CPClass C2, final CPUTF8 N) {
44 this.C = C;
45 this.F = F;
46 this.C2 = C2;
47 this.N = N;
48 }
49
50 @Override
51 public int compareTo(final IcTuple arg0) {
52 return C.compareTo(arg0.C);
53 }
54
55 @Override
56 public boolean equals(final Object o) {
57 if (o instanceof IcTuple) {
58 final IcTuple icT = (IcTuple) o;
59 return C.equals(icT.C) && F == icT.F && Objects.equals(C2, icT.C2) && Objects.equals(N, icT.N);
60 }
61 return false;
62 }
63
64 public boolean isAnonymous() {
65 final String className = C.toString();
66 final String innerName = className.substring(className.lastIndexOf('$') + 1);
67 return Character.isDigit(innerName.charAt(0));
68 }
69
70 @Override
71 public String toString() {
72 return C.toString();
73 }
74
75 }
76
77 private final Set<IcTuple> innerClasses = new TreeSet<>();
78 private final CpBands cpBands;
79 private int bit16Count;
80
81 private final Map<String, List<IcTuple>> outerToInner = new HashMap<>();
82
83 public IcBands(final SegmentHeader segmentHeader, final CpBands cpBands, final int effort) {
84 super(effort, segmentHeader);
85 this.cpBands = cpBands;
86 }
87
88 public void addInnerClass(final String name, final String outerName, final String innerName, int flags) {
89 if (outerName != null || innerName != null) {
90 if (namesArePredictable(name, outerName, innerName)) {
91 final IcTuple innerClass = new IcTuple(cpBands.getCPClass(name), flags, null, null);
92 addToMap(outerName, innerClass);
93 innerClasses.add(innerClass);
94 } else {
95 flags |= 1 << 16;
96 final IcTuple icTuple = new IcTuple(cpBands.getCPClass(name), flags, cpBands.getCPClass(outerName), cpBands.getCPUtf8(innerName));
97 final boolean added = innerClasses.add(icTuple);
98 if (added) {
99 bit16Count++;
100 addToMap(outerName, icTuple);
101 }
102 }
103 } else {
104 final IcTuple innerClass = new IcTuple(cpBands.getCPClass(name), flags, null, null);
105 addToMap(getOuter(name), innerClass);
106 innerClasses.add(innerClass);
107 }
108 }
109
110 private void addToMap(final String outerName, final IcTuple icTuple) {
111 List<IcTuple> tuples = outerToInner.get(outerName);
112 if (tuples == null) {
113 tuples = new ArrayList<>();
114 outerToInner.put(outerName, tuples);
115 } else {
116 for (final IcTuple tuple : tuples) {
117 if (icTuple.equals(tuple)) {
118 return;
119 }
120 }
121 }
122 tuples.add(icTuple);
123 }
124
125
126
127
128
129 public void finaliseBands() {
130 segmentHeader.setIc_count(innerClasses.size());
131 }
132
133 public IcTuple getIcTuple(final CPClass inner) {
134 for (final IcTuple icTuple : innerClasses) {
135 if (icTuple.C.equals(inner)) {
136 return icTuple;
137 }
138 }
139 return null;
140 }
141
142 public List<IcTuple> getInnerClassesForOuter(final String outerClassName) {
143 return outerToInner.get(outerClassName);
144 }
145
146 private String getOuter(final String name) {
147 return name.substring(0, name.lastIndexOf('$'));
148 }
149
150 private boolean namesArePredictable(final String name, final String outerName, final String innerName) {
151
152 return name.equals(outerName + '$' + innerName) && innerName.indexOf('$') == -1;
153 }
154
155 @Override
156 public void pack(final OutputStream outputStream) throws IOException, Pack200Exception {
157 PackingUtils.log("Writing internal class bands...");
158 final int[] ic_this_class = new int[innerClasses.size()];
159 final int[] ic_flags = new int[innerClasses.size()];
160 final int[] ic_outer_class = new int[bit16Count];
161 final int[] ic_name = new int[bit16Count];
162
163 int index2 = 0;
164 final List<IcTuple> innerClassesList = new ArrayList<>(innerClasses);
165 for (int i = 0; i < ic_this_class.length; i++) {
166 final IcTuple icTuple = innerClassesList.get(i);
167 ic_this_class[i] = icTuple.C.getIndex();
168 ic_flags[i] = icTuple.F;
169 if ((icTuple.F & 1 << 16) != 0) {
170 ic_outer_class[index2] = icTuple.C2 == null ? 0 : icTuple.C2.getIndex() + 1;
171 ic_name[index2] = icTuple.N == null ? 0 : icTuple.N.getIndex() + 1;
172 index2++;
173 }
174 }
175 byte[] encodedBand = encodeBandInt("ic_this_class", ic_this_class, Codec.UDELTA5);
176 outputStream.write(encodedBand);
177 PackingUtils.log("Wrote " + encodedBand.length + " bytes from ic_this_class[" + ic_this_class.length + "]");
178
179 encodedBand = encodeBandInt("ic_flags", ic_flags, Codec.UNSIGNED5);
180 outputStream.write(encodedBand);
181 PackingUtils.log("Wrote " + encodedBand.length + " bytes from ic_flags[" + ic_flags.length + "]");
182
183 encodedBand = encodeBandInt("ic_outer_class", ic_outer_class, Codec.DELTA5);
184 outputStream.write(encodedBand);
185 PackingUtils.log("Wrote " + encodedBand.length + " bytes from ic_outer_class[" + ic_outer_class.length + "]");
186
187 encodedBand = encodeBandInt("ic_name", ic_name, Codec.DELTA5);
188 outputStream.write(encodedBand);
189 PackingUtils.log("Wrote " + encodedBand.length + " bytes from ic_name[" + ic_name.length + "]");
190 }
191
192 }