1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.codehaus.plexus.archiver.jar;
18
19 import java.io.ByteArrayInputStream;
20 import java.io.ByteArrayOutputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.Reader;
24 import java.io.StringWriter;
25 import java.io.Writer;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Enumeration;
29 import java.util.Hashtable;
30 import java.util.Iterator;
31 import java.util.Locale;
32 import java.util.StringTokenizer;
33 import java.util.Vector;
34 import java.util.jar.Attributes;
35
36 import org.codehaus.plexus.archiver.ArchiverException;
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 public class Manifest extends java.util.jar.Manifest implements Iterable<String> {
54
55
56
57
58 private static final String ATTRIBUTE_NAME = ManifestConstants.ATTRIBUTE_NAME;
59
60
61
62
63 private static final String ATTRIBUTE_FROM = ManifestConstants.ATTRIBUTE_FROM;
64
65
66
67
68 private static final String DEFAULT_MANIFEST_VERSION = ManifestConstants.DEFAULT_MANIFEST_VERSION;
69
70
71
72
73 private static final int MAX_LINE_LENGTH = 72;
74
75
76
77
78
79 private static final int MAX_SECTION_LENGTH = MAX_LINE_LENGTH - 2;
80
81
82
83
84 static final String EOL = "\r\n";
85
86 public static class BaseAttribute {
87
88
89
90
91 protected String name = null;
92
93
94
95
96
97
98 public String getName() {
99 return name;
100 }
101
102 @Override
103 public boolean equals(Object o) {
104 if (this == o) {
105 return true;
106 }
107 if (!(o instanceof BaseAttribute)) {
108 return false;
109 }
110
111 BaseAttribute that = (BaseAttribute) o;
112
113 return !(name != null ? !name.equals(that.name) : that.name != null);
114 }
115
116 @Override
117 public int hashCode() {
118 return name != null ? name.hashCode() : 0;
119 }
120 }
121
122
123
124
125
126 public static class Attribute extends BaseAttribute implements Iterable<String> {
127
128
129
130
131 private Vector<String> values = new Vector<String>();
132
133
134
135
136
137 private int currentIndex = 0;
138
139
140
141
142 public Attribute() {}
143
144
145
146
147
148
149
150 public Attribute(String name, String value) {
151 this.name = name;
152 setValue(value);
153 }
154
155 @Override
156 public Iterator<String> iterator() {
157 return values.iterator();
158 }
159
160
161
162
163 @Override
164 public int hashCode() {
165 int hashCode = super.hashCode();
166 hashCode += values.hashCode();
167 return hashCode;
168 }
169
170
171
172
173 @Override
174 public boolean equals(Object rhs) {
175 if (super.equals(rhs)) {
176 return false;
177 }
178 if (rhs == null || rhs.getClass() != getClass()) {
179 return false;
180 }
181
182 if (rhs == this) {
183 return true;
184 }
185
186 Attribute rhsAttribute = (Attribute) rhs;
187 String lhsKey = getKey();
188 String rhsKey = rhsAttribute.getKey();
189
190 if ((lhsKey == null && rhsKey != null) || (lhsKey != null && rhsKey == null) || !lhsKey.equals(rhsKey)) {
191 return false;
192 }
193
194 return rhsAttribute.values != null && values.equals(rhsAttribute.values);
195 }
196
197
198
199
200
201
202 public void setName(String name) {
203 this.name = name;
204 }
205
206
207
208
209
210
211 public String getKey() {
212 return getKey(name);
213 }
214
215
216
217
218
219
220 private static String getKey(String name) {
221 if (name == null) {
222 return null;
223 }
224 return name.toLowerCase(Locale.ENGLISH);
225 }
226
227
228
229
230
231
232 public void setValue(String value) {
233 if (currentIndex >= values.size()) {
234 values.addElement(value);
235 currentIndex = values.size() - 1;
236 } else {
237 values.setElementAt(value, currentIndex);
238 }
239 }
240
241
242
243
244
245
246 public String getValue() {
247 if (values.size() == 0) {
248 return null;
249 }
250
251 String fullValue = "";
252 for (String value : values) {
253 fullValue += value + " ";
254 }
255 return fullValue.trim();
256 }
257
258
259
260
261
262
263 public void addValue(String value) {
264 currentIndex++;
265 setValue(value);
266 }
267
268
269
270
271
272
273
274
275 void write(Writer writer) throws IOException {
276 for (String value : values) {
277 writeValue(writer, value);
278 }
279 }
280
281
282
283
284
285
286
287
288
289 private void writeValue(Writer writer, String value) throws IOException {
290 String nameValue = name + ": " + value;
291
292 StringTokenizer tokenizer = new StringTokenizer(nameValue, "\n\r");
293
294 String prefix = "";
295
296 while (tokenizer.hasMoreTokens()) {
297 writeLine(writer, prefix + tokenizer.nextToken());
298 prefix = " ";
299 }
300 }
301
302
303
304
305
306
307
308
309
310 private void writeLine(Writer writer, String line) throws IOException {
311
312 while (line.getBytes("UTF-8").length > MAX_SECTION_LENGTH) {
313
314
315
316 int breakIndex = Math.min(line.length(), MAX_SECTION_LENGTH);
317 String section = line.substring(0, breakIndex);
318 while (section.getBytes("UTF-8").length > MAX_SECTION_LENGTH && breakIndex > 0) {
319 breakIndex--;
320 section = line.substring(0, breakIndex);
321 }
322 if (breakIndex == 0) {
323 throw new IOException("Unable to write manifest line " + line);
324 }
325 writer.write(section + EOL);
326 line = " " + line.substring(breakIndex);
327 }
328 writer.write(line + EOL);
329 }
330 }
331
332 public class ExistingAttribute extends Attribute implements Iterable<String> {
333
334 private final Attributes attributes;
335
336 public ExistingAttribute(Attributes attributes, String name) {
337 this.attributes = attributes;
338 this.name = name;
339 }
340
341 @Override
342 public Iterator<String> iterator() {
343 return getKeys(attributes).iterator();
344 }
345
346 @Override
347 public void setName(String name) {
348 throw new UnsupportedOperationException("Cant do this");
349 }
350
351 @Override
352 public String getKey() {
353 return name;
354 }
355
356 @Override
357 public void setValue(String value) {
358 attributes.putValue(name, value);
359 }
360
361 @Override
362 public String getValue() {
363 return attributes.getValue(name);
364 }
365
366 @Override
367 public void addValue(String value) {
368 String value1 = getValue();
369 value1 = (value1 != null) ? " " + value : value;
370 setValue(value1);
371 }
372
373 @Override
374 void write(Writer writer) throws IOException {
375 throw new UnsupportedOperationException("Cant do this");
376 }
377 }
378
379 private static Collection<String> getKeys(Attributes attributes) {
380 Collection<String> result = new ArrayList<String>();
381 for (Object objectObjectEntry : attributes.keySet()) {
382 result.add(objectObjectEntry.toString());
383 }
384 return result;
385 }
386
387
388
389
390
391
392 public static class Section implements Iterable<String> {
393
394
395
396
397 private Vector<String> warnings = new Vector<String>();
398
399
400
401
402
403 private String name = null;
404
405
406
407
408 private Hashtable<String, Attribute> attributes = new Hashtable<String, Attribute>();
409
410
411
412
413 private Vector<String> attributeIndex = new Vector<String>();
414
415
416
417
418
419
420 public void setName(String name) {
421 this.name = name;
422 }
423
424
425
426
427
428
429 public String getName() {
430 return name;
431 }
432
433 @Override
434 public Iterator<String> iterator() {
435 return attributes.keySet().iterator();
436 }
437
438
439
440
441
442
443
444
445
446
447 public Attribute getAttribute(String attributeName) {
448 return attributes.get(attributeName.toLowerCase(Locale.ENGLISH));
449 }
450
451
452
453
454
455
456
457
458 public void addConfiguredAttribute(Attribute attribute) throws ManifestException {
459 String check = addAttributeAndCheck(attribute);
460 if (check != null) {
461 throw new ManifestException(
462 "Specify the section name using " + "the \"name\" attribute of the <section> element rather "
463 + "than using a \"Name\" manifest attribute");
464 }
465 }
466
467
468
469
470
471
472
473
474
475
476
477
478 public String addAttributeAndCheck(Attribute attribute) throws ManifestException {
479 if (attribute.getName() == null || attribute.getValue() == null) {
480 throw new ManifestException("Attributes must have name and value");
481 }
482 if (attribute.getKey().equalsIgnoreCase(ATTRIBUTE_NAME)) {
483 warnings.addElement("\"" + ATTRIBUTE_NAME + "\" attributes "
484 + "should not occur in the main section and must be the "
485 + "first element in all other sections: \"" + attribute.getName() + ": " + attribute.getValue()
486 + "\"");
487 return attribute.getValue();
488 }
489
490 if (attribute.getKey().startsWith(Attribute.getKey(ATTRIBUTE_FROM))) {
491 warnings.addElement("Manifest attributes should not start " + "with \"" + ATTRIBUTE_FROM + "\" in \""
492 + attribute.getName() + ": " + attribute.getValue() + "\"");
493 } else {
494
495 String attributeKey = attribute.getKey();
496 if (attributeKey.equalsIgnoreCase(ManifestConstants.ATTRIBUTE_CLASSPATH)) {
497 Attribute classpathAttribute = attributes.get(attributeKey);
498
499 if (classpathAttribute == null) {
500 storeAttribute(attribute);
501 } else {
502 warnings.addElement("Multiple Class-Path attributes " + "are supported but violate the Jar "
503 + "specification and may not be correctly "
504 + "processed in all environments");
505
506 for (String value : attribute) {
507 classpathAttribute.addValue(value);
508 }
509 }
510 } else if (attributes.containsKey(attributeKey)) {
511 throw new ManifestException("The attribute \"" + attribute.getName() + "\" may not occur more "
512 + "than once in the same section");
513 } else {
514 storeAttribute(attribute);
515 }
516 }
517 return null;
518 }
519
520
521
522
523
524
525 protected void storeAttribute(Attribute attribute) {
526 if (attribute == null) {
527 return;
528 }
529
530 String attributeKey = attribute.getKey();
531 attributes.put(attributeKey, attribute);
532 if (!attributeIndex.contains(attributeKey)) {
533 attributeIndex.addElement(attributeKey);
534 }
535 }
536
537
538
539
540
541
542 public Enumeration<String> getWarnings() {
543 return warnings.elements();
544 }
545
546
547
548
549 @Override
550 public int hashCode() {
551 int hashCode = 0;
552
553 if (name != null) {
554 hashCode += name.hashCode();
555 }
556
557 hashCode += attributes.hashCode();
558 return hashCode;
559 }
560
561
562
563
564 @Override
565 public boolean equals(Object rhs) {
566 if (rhs == null || rhs.getClass() != getClass()) {
567 return false;
568 }
569
570 if (rhs == this) {
571 return true;
572 }
573
574 Section rhsSection = (Section) rhs;
575
576 return rhsSection.attributes != null && attributes.equals(rhsSection.attributes);
577 }
578 }
579
580 public class ExistingSection implements Iterable<String> {
581
582 private final Attributes backingAttributes;
583
584 private final String sectionName;
585
586 public ExistingSection(Attributes backingAttributes, String sectionName) {
587 this.backingAttributes = backingAttributes;
588 this.sectionName = sectionName;
589 }
590
591 @Override
592 public Iterator<String> iterator() {
593 return getKeys(backingAttributes).iterator();
594 }
595
596 public ExistingAttribute getAttribute(String attributeName) {
597 Attributes.Name name = new Attributes.Name(attributeName);
598 return backingAttributes.containsKey(name) ? new ExistingAttribute(backingAttributes, attributeName) : null;
599 }
600
601 public String getName() {
602 return sectionName;
603 }
604
605 public String getAttributeValue(String attributeName) {
606 return backingAttributes.getValue(attributeName);
607 }
608
609 public void removeAttribute(String attributeName) {
610 backingAttributes.remove(new Attributes.Name(attributeName));
611 }
612
613 public void addConfiguredAttribute(Attribute attribute) throws ManifestException {
614 backingAttributes.putValue(attribute.getName(), attribute.getValue());
615 }
616
617 public String addAttributeAndCheck(Attribute attribute) throws ManifestException {
618 return remap(backingAttributes, attribute);
619 }
620
621 @Override
622 public int hashCode() {
623 return backingAttributes.hashCode();
624 }
625
626 @Override
627 public boolean equals(Object rhs) {
628 return rhs instanceof ExistingSection
629 && backingAttributes.equals(((ExistingSection) rhs).backingAttributes);
630 }
631 }
632
633 @Override
634 public Iterator<String> iterator() {
635 return getEntries().keySet().iterator();
636 }
637
638
639
640
641 private Section mainSection = new Section();
642
643
644
645
646
647
648
649
650
651
652
653
654
655 public static Manifest getDefaultManifest(boolean minimalDefaultManifest) throws ArchiverException {
656 final Manifest defaultManifest = new Manifest();
657 defaultManifest.getMainAttributes().putValue("Manifest-Version", "1.0");
658
659 if (!minimalDefaultManifest) {
660 String createdBy = "Plexus Archiver";
661
662 final String plexusArchiverVersion = JdkManifestFactory.getArchiverVersion();
663
664 if (plexusArchiverVersion != null) {
665 createdBy += " " + plexusArchiverVersion;
666 }
667
668 defaultManifest.getMainAttributes().putValue("Created-By", createdBy);
669 }
670
671 return defaultManifest;
672 }
673
674
675
676
677 public static Manifest getDefaultManifest() throws ArchiverException {
678 return getDefaultManifest(false);
679 }
680
681
682
683
684 public Manifest() {
685 setManifestVersion();
686 }
687
688 private void setManifestVersion() {
689 getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
690 }
691
692
693
694
695
696
697
698
699
700
701
702
703 @Deprecated
704 public Manifest(Reader r) throws ManifestException, IOException {
705 super(getInputStream(r));
706 setManifestVersion();
707 }
708
709 public Manifest(InputStream is) throws IOException {
710 super(is);
711 setManifestVersion();
712 }
713
714
715
716
717
718
719
720
721 public void addConfiguredSection(Section section) throws ManifestException {
722 String sectionName = section.getName();
723 if (sectionName == null) {
724 throw new ManifestException("Sections must have a name");
725 }
726 Attributes attributes = getOrCreateAttributes(sectionName);
727 for (String s : section.attributes.keySet()) {
728
729 Attribute attribute = section.getAttribute(s);
730 attributes.putValue(attribute.getName(), attribute.getValue());
731 }
732 }
733
734 private Attributes getOrCreateAttributes(String name) {
735 Attributes attributes = getAttributes(name);
736 if (attributes == null) {
737 attributes = new Attributes();
738 getEntries().put(name, attributes);
739 }
740 return attributes;
741 }
742
743
744
745
746
747
748
749
750 public void addConfiguredAttribute(Attribute attribute) throws ManifestException {
751 remap(getMainAttributes(), attribute);
752 }
753
754
755
756
757
758
759
760
761 public void write(Writer writer) throws IOException {
762 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
763 super.write(byteArrayOutputStream);
764
765 writer.write(byteArrayOutputStream.toString("UTF-8"));
766 }
767
768
769
770
771
772
773
774 @Override
775 public String toString() {
776 StringWriter sw = new StringWriter();
777 try {
778 write(sw);
779 } catch (IOException e) {
780 return null;
781 }
782 return sw.toString();
783 }
784
785
786
787
788
789
790 Enumeration<String> getWarnings() {
791 Vector<String> warnings = new Vector<String>();
792
793 Enumeration<String> warnEnum = mainSection.getWarnings();
794 while (warnEnum.hasMoreElements()) {
795 warnings.addElement(warnEnum.nextElement());
796 }
797
798 return warnings.elements();
799 }
800
801
802
803
804
805
806 public String getManifestVersion() {
807
808
809
810 return DEFAULT_MANIFEST_VERSION;
811 }
812
813
814
815
816
817
818 public ExistingSection getMainSection() {
819 return new ExistingSection(getMainAttributes(), null);
820 }
821
822
823
824
825
826
827
828
829
830 public ExistingSection getSection(String name) {
831 Attributes attributes = getAttributes(name);
832 if (attributes != null) {
833 return new ExistingSection(attributes, name);
834 }
835 return null;
836 }
837
838 @Deprecated
839 private static InputStream getInputStream(Reader r) throws IOException {
840 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
841 int read;
842 while ((read = r.read()) != -1) {
843 byteArrayOutputStream.write(read);
844 }
845 return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
846 }
847
848 public static String remap(Attributes backingAttributes, Attribute attribute) throws ManifestException {
849 if (attribute.getKey() == null || attribute.getValue() == null) {
850 throw new ManifestException("Attributes must have name and value");
851 }
852
853 String attributeKey = attribute.getKey();
854 if (attributeKey.equalsIgnoreCase(ManifestConstants.ATTRIBUTE_CLASSPATH)) {
855 String classpathAttribute = backingAttributes.getValue(attributeKey);
856
857 if (classpathAttribute == null) {
858 classpathAttribute = attribute.getValue();
859 } else {
860 classpathAttribute += " " + attribute.getValue();
861 }
862 backingAttributes.putValue(ManifestConstants.ATTRIBUTE_CLASSPATH, classpathAttribute);
863 } else {
864 backingAttributes.putValue(attribute.getName(), attribute.getValue());
865 if (attribute.getKey().equalsIgnoreCase(ATTRIBUTE_NAME)) {
866 return attribute.getValue();
867 }
868 }
869 return null;
870 }
871 }