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.unpack200;
20
21 import java.io.BufferedInputStream;
22 import java.io.ByteArrayInputStream;
23 import java.io.ByteArrayOutputStream;
24 import java.io.DataOutputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.OutputStream;
28 import java.io.OutputStreamWriter;
29 import java.io.PrintWriter;
30 import java.nio.charset.Charset;
31 import java.util.ArrayList;
32 import java.util.HashSet;
33 import java.util.List;
34 import java.util.Set;
35 import java.util.TimeZone;
36 import java.util.jar.JarEntry;
37 import java.util.jar.JarOutputStream;
38 import java.util.zip.CRC32;
39 import java.util.zip.GZIPInputStream;
40 import java.util.zip.ZipEntry;
41
42 import org.apache.commons.compress.harmony.pack200.Codec;
43 import org.apache.commons.compress.harmony.pack200.Pack200Exception;
44 import org.apache.commons.compress.harmony.unpack200.bytecode.Attribute;
45 import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass;
46 import org.apache.commons.compress.harmony.unpack200.bytecode.CPField;
47 import org.apache.commons.compress.harmony.unpack200.bytecode.CPMethod;
48 import org.apache.commons.compress.harmony.unpack200.bytecode.CPUTF8;
49 import org.apache.commons.compress.harmony.unpack200.bytecode.ClassConstantPool;
50 import org.apache.commons.compress.harmony.unpack200.bytecode.ClassFile;
51 import org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry;
52 import org.apache.commons.compress.harmony.unpack200.bytecode.InnerClassesAttribute;
53 import org.apache.commons.compress.harmony.unpack200.bytecode.SourceFileAttribute;
54 import org.apache.commons.io.IOUtils;
55 import org.apache.commons.io.input.BoundedInputStream;
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 public class Segment {
90
91
92
93
94 public static final int LOG_LEVEL_VERBOSE = 2;
95
96
97
98
99 public static final int LOG_LEVEL_STANDARD = 1;
100
101
102
103
104 public static final int LOG_LEVEL_QUIET = 0;
105
106 private SegmentHeader header;
107
108 private CpBands cpBands;
109
110 private AttrDefinitionBands attrDefinitionBands;
111
112 private IcBands icBands;
113
114 private ClassBands classBands;
115
116 private BcBands bcBands;
117
118 private FileBands fileBands;
119
120 private boolean overrideDeflateHint;
121
122 private boolean deflateHint;
123
124 private boolean doPreRead;
125
126 private int logLevel;
127
128 private PrintWriter logPrintWriter;
129
130 private byte[][] classFilesContents;
131
132 private boolean[] fileDeflate;
133
134 private boolean[] fileIsClass;
135
136 private InputStream internalBuffer;
137
138 private ClassFile buildClassFile(final int classNum) {
139 final ClassFile classFile = new ClassFile();
140 final int[] major = classBands.getClassVersionMajor();
141 final int[] minor = classBands.getClassVersionMinor();
142 if (major != null) {
143 classFile.major = major[classNum];
144 classFile.minor = minor[classNum];
145 } else {
146 classFile.major = header.getDefaultClassMajorVersion();
147 classFile.minor = header.getDefaultClassMinorVersion();
148 }
149
150 final ClassConstantPool cp = classFile.pool;
151 final int fullNameIndexInCpClass = classBands.getClassThisInts()[classNum];
152 final String fullName = cpBands.getCpClass()[fullNameIndexInCpClass];
153
154 int i = fullName.lastIndexOf("/") + 1;
155
156
157
158
159 final List<Attribute> classAttributes = classBands.getClassAttributes()[classNum];
160 SourceFileAttribute sourceFileAttribute = null;
161 for (final Attribute classAttribute : classAttributes) {
162 if (classAttribute.isSourceFileAttribute()) {
163 sourceFileAttribute = (SourceFileAttribute) classAttribute;
164 }
165 }
166
167 if (sourceFileAttribute == null) {
168
169
170 final AttributeLayout SOURCE_FILE = attrDefinitionBands.getAttributeDefinitionMap().getAttributeLayout(AttributeLayout.ATTRIBUTE_SOURCE_FILE,
171 AttributeLayout.CONTEXT_CLASS);
172 if (SOURCE_FILE.matches(classBands.getRawClassFlags()[classNum])) {
173 int firstDollar = -1;
174 for (int index = 0; index < fullName.length(); index++) {
175 if (fullName.charAt(index) <= '$') {
176 firstDollar = index;
177 }
178 }
179 final String fileName;
180 if (firstDollar > -1 && i <= firstDollar) {
181 fileName = fullName.substring(i, firstDollar) + ".java";
182 } else {
183 fileName = fullName.substring(i) + ".java";
184 }
185 sourceFileAttribute = new SourceFileAttribute(cpBands.cpUTF8Value(fileName, false));
186 classFile.attributes = new Attribute[] { (Attribute) cp.add(sourceFileAttribute) };
187 } else {
188 classFile.attributes = new Attribute[] {};
189 }
190 } else {
191 classFile.attributes = new Attribute[] { (Attribute) cp.add(sourceFileAttribute) };
192 }
193
194
195
196
197
198 final List<Attribute> classAttributesWithoutSourceFileAttribute = new ArrayList<>(classAttributes.size());
199 for (int index = 0; index < classAttributes.size(); index++) {
200 final Attribute attrib = classAttributes.get(index);
201 if (!attrib.isSourceFileAttribute()) {
202 classAttributesWithoutSourceFileAttribute.add(attrib);
203 }
204 }
205 final Attribute[] originalAttributes = classFile.attributes;
206 classFile.attributes = new Attribute[originalAttributes.length + classAttributesWithoutSourceFileAttribute.size()];
207 System.arraycopy(originalAttributes, 0, classFile.attributes, 0, originalAttributes.length);
208 for (int index = 0; index < classAttributesWithoutSourceFileAttribute.size(); index++) {
209 final Attribute attrib = classAttributesWithoutSourceFileAttribute.get(index);
210 cp.add(attrib);
211 classFile.attributes[originalAttributes.length + index] = attrib;
212 }
213
214
215 final ClassFileEntry cfThis = cp.add(cpBands.cpClassValue(fullNameIndexInCpClass));
216 final ClassFileEntry cfSuper = cp.add(cpBands.cpClassValue(classBands.getClassSuperInts()[classNum]));
217
218 final ClassFileEntry[] cfInterfaces = new ClassFileEntry[classBands.getClassInterfacesInts()[classNum].length];
219 for (i = 0; i < cfInterfaces.length; i++) {
220 cfInterfaces[i] = cp.add(cpBands.cpClassValue(classBands.getClassInterfacesInts()[classNum][i]));
221 }
222
223 final ClassFileEntry[] cfFields = new ClassFileEntry[classBands.getClassFieldCount()[classNum]];
224
225 for (i = 0; i < cfFields.length; i++) {
226 final int descriptorIndex = classBands.getFieldDescrInts()[classNum][i];
227 final int nameIndex = cpBands.getCpDescriptorNameInts()[descriptorIndex];
228 final int typeIndex = cpBands.getCpDescriptorTypeInts()[descriptorIndex];
229 final CPUTF8 name = cpBands.cpUTF8Value(nameIndex);
230 final CPUTF8 descriptor = cpBands.cpSignatureValue(typeIndex);
231 cfFields[i] = cp.add(new CPField(name, descriptor, classBands.getFieldFlags()[classNum][i], classBands.getFieldAttributes()[classNum][i]));
232 }
233
234 final ClassFileEntry[] cfMethods = new ClassFileEntry[classBands.getClassMethodCount()[classNum]];
235
236 for (i = 0; i < cfMethods.length; i++) {
237 final int descriptorIndex = classBands.getMethodDescrInts()[classNum][i];
238 final int nameIndex = cpBands.getCpDescriptorNameInts()[descriptorIndex];
239 final int typeIndex = cpBands.getCpDescriptorTypeInts()[descriptorIndex];
240 final CPUTF8 name = cpBands.cpUTF8Value(nameIndex);
241 final CPUTF8 descriptor = cpBands.cpSignatureValue(typeIndex);
242 cfMethods[i] = cp.add(new CPMethod(name, descriptor, classBands.getMethodFlags()[classNum][i], classBands.getMethodAttributes()[classNum][i]));
243 }
244 cp.addNestedEntries();
245
246
247 boolean addInnerClassesAttr = false;
248 final IcTuple[] icLocal = getClassBands().getIcLocal()[classNum];
249 final boolean icLocalSent = icLocal != null;
250 final InnerClassesAttribute innerClassesAttribute = new InnerClassesAttribute("InnerClasses");
251 final IcTuple[] icRelevant = getIcBands().getRelevantIcTuples(fullName, cp);
252 final List<IcTuple> ic_stored = computeIcStored(icLocal, icRelevant);
253 for (final IcTuple icStored : ic_stored) {
254 final int innerClassIndex = icStored.thisClassIndex();
255 final int outerClassIndex = icStored.outerClassIndex();
256 final int simpleClassNameIndex = icStored.simpleClassNameIndex();
257
258 final String innerClassString = icStored.thisClassString();
259 final String outerClassString = icStored.outerClassString();
260 final String simpleClassName = icStored.simpleClassName();
261
262 CPUTF8 innerName = null;
263 CPClass outerClass = null;
264
265 final CPClass innerClass = innerClassIndex != -1 ? cpBands.cpClassValue(innerClassIndex) : cpBands.cpClassValue(innerClassString);
266 if (!icStored.isAnonymous()) {
267 innerName = simpleClassNameIndex != -1 ? cpBands.cpUTF8Value(simpleClassNameIndex) : cpBands.cpUTF8Value(simpleClassName);
268 }
269
270 if (icStored.isMember()) {
271 outerClass = outerClassIndex != -1 ? cpBands.cpClassValue(outerClassIndex) : cpBands.cpClassValue(outerClassString);
272 }
273 final int flags = icStored.F;
274 innerClassesAttribute.addInnerClassesEntry(innerClass, outerClass, innerName, flags);
275 addInnerClassesAttr = true;
276 }
277
278
279 if (icLocalSent && icLocal.length == 0) {
280 addInnerClassesAttr = false;
281 }
282
283
284
285 if (!icLocalSent && icRelevant.length == 0) {
286 addInnerClassesAttr = false;
287 }
288
289 if (addInnerClassesAttr) {
290
291
292 final Attribute[] originalAttrs = classFile.attributes;
293 final Attribute[] newAttrs = new Attribute[originalAttrs.length + 1];
294 System.arraycopy(originalAttrs, 0, newAttrs, 0, originalAttrs.length);
295 newAttrs[newAttrs.length - 1] = innerClassesAttribute;
296 classFile.attributes = newAttrs;
297 cp.addWithNestedEntries(innerClassesAttribute);
298 }
299
300 cp.resolve(this);
301
302
303 classFile.accessFlags = (int) classBands.getClassFlags()[classNum];
304 classFile.thisClass = cp.indexOf(cfThis);
305 classFile.superClass = cp.indexOf(cfSuper);
306
307 classFile.interfaces = new int[cfInterfaces.length];
308 for (i = 0; i < cfInterfaces.length; i++) {
309 classFile.interfaces[i] = cp.indexOf(cfInterfaces[i]);
310 }
311 classFile.fields = cfFields;
312 classFile.methods = cfMethods;
313 return classFile;
314 }
315
316
317
318
319
320
321
322
323
324 private List<IcTuple> computeIcStored(final IcTuple[] icLocal, final IcTuple[] icRelevant) {
325 final List<IcTuple> result = new ArrayList<>(icRelevant.length);
326 final List<IcTuple> duplicates = new ArrayList<>(icRelevant.length);
327 final Set<IcTuple> isInResult = new HashSet<>(icRelevant.length);
328
329
330
331
332
333 if (icLocal != null) {
334 for (final IcTuple element : icLocal) {
335 if (isInResult.add(element)) {
336 result.add(element);
337 }
338 }
339 }
340
341
342 for (final IcTuple element : icRelevant) {
343 if (isInResult.add(element)) {
344 result.add(element);
345 } else {
346 duplicates.add(element);
347 }
348 }
349
350
351 duplicates.forEach(result::remove);
352
353 return result;
354 }
355
356 protected AttrDefinitionBands getAttrDefinitionBands() {
357 return attrDefinitionBands;
358 }
359
360 protected ClassBands getClassBands() {
361 return classBands;
362 }
363
364
365
366
367
368
369 public SegmentConstantPool getConstantPool() {
370 return cpBands.getConstantPool();
371 }
372
373
374
375
376
377
378 protected CpBands getCpBands() {
379 return cpBands;
380 }
381
382
383
384
385
386
387 protected IcBands getIcBands() {
388 return icBands;
389 }
390
391
392
393
394
395
396 public SegmentHeader getSegmentHeader() {
397 return header;
398 }
399
400
401
402
403
404
405
406 public void log(final int messageLevel, final String message) {
407 if (logLevel >= messageLevel && logPrintWriter != null) {
408 logPrintWriter.println(message);
409 }
410 }
411
412
413
414
415
416
417 public void overrideDeflateHint(final boolean deflateHint) {
418 this.overrideDeflateHint = true;
419 this.deflateHint = deflateHint;
420 }
421
422
423
424
425
426
427
428 private void parseSegment() throws IOException, Pack200Exception {
429
430 header.unpack();
431 cpBands.unpack();
432 attrDefinitionBands.unpack();
433 icBands.unpack();
434 classBands.unpack();
435 bcBands.unpack();
436 fileBands.unpack();
437
438 int classNum = 0;
439 final int numberOfFiles = header.getNumberOfFiles();
440 final String[] fileName = fileBands.getFileName();
441 final int[] fileOptions = fileBands.getFileOptions();
442 final SegmentOptions options = header.getOptions();
443
444 classFilesContents = new byte[numberOfFiles][];
445 fileDeflate = new boolean[numberOfFiles];
446 fileIsClass = new boolean[numberOfFiles];
447
448 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
449 final DataOutputStream dos = new DataOutputStream(bos);
450
451 for (int i = 0; i < numberOfFiles; i++) {
452 String name = fileName[i];
453
454 final boolean nameIsEmpty = name == null || name.isEmpty();
455 final boolean isClass = (fileOptions[i] & 2) == 2 || nameIsEmpty;
456 if (isClass && nameIsEmpty) {
457 name = cpBands.getCpClass()[classBands.getClassThisInts()[classNum]] + ".class";
458 fileName[i] = name;
459 }
460
461 if (!overrideDeflateHint) {
462 fileDeflate[i] = (fileOptions[i] & 1) == 1 || options.shouldDeflate();
463 } else {
464 fileDeflate[i] = deflateHint;
465 }
466
467 fileIsClass[i] = isClass;
468
469 if (isClass) {
470 final ClassFile classFile = buildClassFile(classNum);
471 classFile.write(dos);
472 dos.flush();
473
474 classFilesContents[classNum] = bos.toByteArray();
475 bos.reset();
476
477 classNum++;
478 }
479 }
480 }
481
482
483
484
485
486
487
488
489 private void readSegment(final InputStream in) throws IOException, Pack200Exception {
490 log(LOG_LEVEL_VERBOSE, "-------");
491 cpBands = new CpBands(this);
492 cpBands.read(in);
493 attrDefinitionBands = new AttrDefinitionBands(this);
494 attrDefinitionBands.read(in);
495 icBands = new IcBands(this);
496 icBands.read(in);
497 classBands = new ClassBands(this);
498 classBands.read(in);
499 bcBands = new BcBands(this);
500 bcBands.read(in);
501 fileBands = new FileBands(this);
502 fileBands.read(in);
503 fileBands.processFileBits();
504 }
505
506
507
508
509
510
511 public void setLogLevel(final int logLevel) {
512 this.logLevel = logLevel;
513 }
514
515
516
517
518
519
520 public void setLogStream(final OutputStream logStream) {
521 this.logPrintWriter = logStream != null ? new PrintWriter(new OutputStreamWriter(logStream, Charset.defaultCharset()), false) : null;
522 }
523
524
525
526
527
528
529 public void setPreRead(final boolean value) {
530 doPreRead = value;
531 }
532
533
534
535
536
537
538
539
540
541 public void unpack(final InputStream inputStream, final JarOutputStream out) throws IOException, Pack200Exception {
542 unpackRead(inputStream);
543 unpackProcess();
544 unpackWrite(out);
545 }
546
547 void unpackProcess() throws IOException, Pack200Exception {
548 if (internalBuffer != null) {
549 readSegment(internalBuffer);
550 }
551 parseSegment();
552 }
553
554 void unpackRead(final InputStream inputStream) throws IOException, Pack200Exception {
555 @SuppressWarnings("resource")
556 final InputStream in = Pack200UnpackerAdapter.newBoundedInputStream(inputStream);
557
558 header = new SegmentHeader(this);
559 header.read(in);
560
561 final int size = (int) header.getArchiveSize() - header.getArchiveSizeOffset();
562
563 if (doPreRead && header.getArchiveSize() != 0) {
564 final byte[] data = new byte[size];
565 in.read(data);
566 internalBuffer = new BufferedInputStream(new ByteArrayInputStream(data));
567 } else {
568 readSegment(in);
569 }
570 }
571
572 void unpackWrite(final JarOutputStream out) throws IOException {
573 writeJar(out);
574 IOUtils.close(logPrintWriter);
575 }
576
577
578
579
580
581
582
583
584
585 public void writeJar(final JarOutputStream out) throws IOException {
586 final String[] fileName = fileBands.getFileName();
587 final int[] fileModtime = fileBands.getFileModtime();
588 final long[] fileSize = fileBands.getFileSize();
589 final byte[][] fileBits = fileBands.getFileBits();
590
591
592 int classNum = 0;
593 final int numberOfFiles = header.getNumberOfFiles();
594 final long archiveModtime = header.getArchiveModtime();
595
596 for (int i = 0; i < numberOfFiles; i++) {
597 final String name = fileName[i];
598
599
600
601
602
603
604 final long modtime = 1000 * (archiveModtime + fileModtime[i]);
605 final boolean deflate = fileDeflate[i];
606
607 final JarEntry entry = new JarEntry(name);
608 if (deflate) {
609 entry.setMethod(ZipEntry.DEFLATED);
610 } else {
611 entry.setMethod(ZipEntry.STORED);
612 final CRC32 crc = new CRC32();
613 if (fileIsClass[i]) {
614 crc.update(classFilesContents[classNum]);
615 entry.setSize(classFilesContents[classNum].length);
616 } else {
617 crc.update(fileBits[i]);
618 entry.setSize(fileSize[i]);
619 }
620 entry.setCrc(crc.getValue());
621 }
622
623 entry.setTime(modtime - TimeZone.getDefault().getRawOffset());
624 out.putNextEntry(entry);
625
626
627 if (fileIsClass[i]) {
628 entry.setSize(classFilesContents[classNum].length);
629 out.write(classFilesContents[classNum]);
630 classNum++;
631 } else {
632 entry.setSize(fileSize[i]);
633 out.write(fileBits[i]);
634 }
635 }
636 }
637
638 }