1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
31
32 public class IcBands extends BandSet {
33
34 static class IcTuple implements Comparable<IcTuple> {
35
36 protected CPClass C;
37 protected int F;
38 protected CPClass C2;
39 protected CPUTF8 N;
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
125
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
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 }