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 }