1 | /*
|
---|
2 | * Copyright 2001-2004 The Apache Software Foundation
|
---|
3 | *
|
---|
4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
---|
5 | * you may not use this file except in compliance with the License.
|
---|
6 | * You may obtain a copy of the License at
|
---|
7 | *
|
---|
8 | * http://www.apache.org/licenses/LICENSE-2.0
|
---|
9 | *
|
---|
10 | * Unless required by applicable law or agreed to in writing, software
|
---|
11 | * distributed under the License is distributed on an "AS IS" BASIS,
|
---|
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
---|
13 | * See the License for the specific language governing permissions and
|
---|
14 | * limitations under the License.
|
---|
15 | *
|
---|
16 | */
|
---|
17 |
|
---|
18 | package org.apache.tools.ant.taskdefs;
|
---|
19 |
|
---|
20 | import java.io.BufferedReader;
|
---|
21 | import java.io.IOException;
|
---|
22 | import java.io.InputStream;
|
---|
23 | import java.io.InputStreamReader;
|
---|
24 | import java.io.PrintWriter;
|
---|
25 | import java.io.Reader;
|
---|
26 | import java.io.StringWriter;
|
---|
27 | import java.io.UnsupportedEncodingException;
|
---|
28 | import java.util.Enumeration;
|
---|
29 | import java.util.Hashtable;
|
---|
30 | import java.util.Vector;
|
---|
31 | import org.apache.tools.ant.BuildException;
|
---|
32 | import org.apache.tools.ant.util.CollectionUtils;
|
---|
33 |
|
---|
34 | /**
|
---|
35 | * Holds the data of a jar manifest.
|
---|
36 | *
|
---|
37 | * Manifests are processed according to the
|
---|
38 | * {@link <a href="http://java.sun.com/j2se/1.4/docs/guide/jar/jar.html">Jar
|
---|
39 | * file specification.</a>}.
|
---|
40 | * Specifically, a manifest element consists of
|
---|
41 | * a set of attributes and sections. These sections in turn may contain
|
---|
42 | * attributes. Note in particular that this may result in manifest lines
|
---|
43 | * greater than 72 bytes being wrapped and continued on the next
|
---|
44 | * line. If an application can not handle the continuation mechanism, it
|
---|
45 | * is a defect in the application, not this task.
|
---|
46 | *
|
---|
47 | *
|
---|
48 | * @since Ant 1.4
|
---|
49 | */
|
---|
50 | public class Manifest {
|
---|
51 | /** The standard manifest version header */
|
---|
52 | public static final String ATTRIBUTE_MANIFEST_VERSION
|
---|
53 | = "Manifest-Version";
|
---|
54 |
|
---|
55 | /** The standard Signature Version header */
|
---|
56 | public static final String ATTRIBUTE_SIGNATURE_VERSION
|
---|
57 | = "Signature-Version";
|
---|
58 |
|
---|
59 | /** The Name Attribute is the first in a named section */
|
---|
60 | public static final String ATTRIBUTE_NAME = "Name";
|
---|
61 |
|
---|
62 | /** The From Header is disallowed in a Manifest */
|
---|
63 | public static final String ATTRIBUTE_FROM = "From";
|
---|
64 |
|
---|
65 | /** The Class-Path Header is special - it can be duplicated */
|
---|
66 | public static final String ATTRIBUTE_CLASSPATH = "Class-Path";
|
---|
67 |
|
---|
68 | /** Default Manifest version if one is not specified */
|
---|
69 | public static final String DEFAULT_MANIFEST_VERSION = "1.0";
|
---|
70 |
|
---|
71 | /** The max length of a line in a Manifest */
|
---|
72 | public static final int MAX_LINE_LENGTH = 72;
|
---|
73 |
|
---|
74 | /**
|
---|
75 | * Max length of a line section which is continued. Need to allow
|
---|
76 | * for the CRLF.
|
---|
77 | */
|
---|
78 | public static final int MAX_SECTION_LENGTH = MAX_LINE_LENGTH - 2;
|
---|
79 |
|
---|
80 | /** The End-Of-Line marker in manifests */
|
---|
81 | public static final String EOL = "\r\n";
|
---|
82 |
|
---|
83 | /**
|
---|
84 | * An attribute for the manifest.
|
---|
85 | * Those attributes that are not nested into a section will be added to the "Main" section.
|
---|
86 | */
|
---|
87 | public static class Attribute {
|
---|
88 | /** The attribute's name */
|
---|
89 | private String name = null;
|
---|
90 |
|
---|
91 | /** The attribute's value */
|
---|
92 | private Vector values = new Vector();
|
---|
93 |
|
---|
94 | /**
|
---|
95 | * For multivalued attributes, this is the index of the attribute
|
---|
96 | * currently being defined.
|
---|
97 | */
|
---|
98 | private int currentIndex = 0;
|
---|
99 |
|
---|
100 | /**
|
---|
101 | * Construct an empty attribute */
|
---|
102 | public Attribute() {
|
---|
103 | }
|
---|
104 |
|
---|
105 | /**
|
---|
106 | * Construct an attribute by parsing a line from the Manifest
|
---|
107 | *
|
---|
108 | * @param line the line containing the attribute name and value
|
---|
109 | *
|
---|
110 | * @throws ManifestException if the line is not valid
|
---|
111 | */
|
---|
112 | public Attribute(String line) throws ManifestException {
|
---|
113 | parse(line);
|
---|
114 | }
|
---|
115 |
|
---|
116 | /**
|
---|
117 | * Construct a manifest by specifying its name and value
|
---|
118 | *
|
---|
119 | * @param name the attribute's name
|
---|
120 | * @param value the Attribute's value
|
---|
121 | */
|
---|
122 | public Attribute(String name, String value) {
|
---|
123 | this.name = name;
|
---|
124 | setValue(value);
|
---|
125 | }
|
---|
126 |
|
---|
127 | /**
|
---|
128 | * @see java.lang.Object#hashCode
|
---|
129 | */
|
---|
130 | public int hashCode() {
|
---|
131 | int hashCode = 0;
|
---|
132 |
|
---|
133 | if (name != null) {
|
---|
134 | hashCode += name.hashCode();
|
---|
135 | }
|
---|
136 |
|
---|
137 | hashCode += values.hashCode();
|
---|
138 | return hashCode;
|
---|
139 | }
|
---|
140 |
|
---|
141 | /**
|
---|
142 | * @see java.lang.Object#equals
|
---|
143 | */
|
---|
144 | public boolean equals(Object rhs) {
|
---|
145 | if (rhs == null || rhs.getClass() != getClass()) {
|
---|
146 | return false;
|
---|
147 | }
|
---|
148 |
|
---|
149 | if (rhs == this) {
|
---|
150 | return true;
|
---|
151 | }
|
---|
152 |
|
---|
153 | Attribute rhsAttribute = (Attribute) rhs;
|
---|
154 | String lhsKey = getKey();
|
---|
155 | String rhsKey = rhsAttribute.getKey();
|
---|
156 | if ((lhsKey == null && rhsKey != null)
|
---|
157 | || (lhsKey != null && rhsKey == null)
|
---|
158 | || !lhsKey.equals(rhsKey)) {
|
---|
159 | return false;
|
---|
160 | }
|
---|
161 |
|
---|
162 | return CollectionUtils.equals(values, rhsAttribute.values);
|
---|
163 | }
|
---|
164 |
|
---|
165 | /**
|
---|
166 | * Parse a line into name and value pairs
|
---|
167 | *
|
---|
168 | * @param line the line to be parsed
|
---|
169 | *
|
---|
170 | * @throws ManifestException if the line does not contain a colon
|
---|
171 | * separating the name and value
|
---|
172 | */
|
---|
173 | public void parse(String line) throws ManifestException {
|
---|
174 | int index = line.indexOf(": ");
|
---|
175 | if (index == -1) {
|
---|
176 | throw new ManifestException("Manifest line \"" + line
|
---|
177 | + "\" is not valid as it does not "
|
---|
178 | + "contain a name and a value separated by ': ' ");
|
---|
179 | }
|
---|
180 | name = line.substring(0, index);
|
---|
181 | setValue(line.substring(index + 2));
|
---|
182 | }
|
---|
183 |
|
---|
184 | /**
|
---|
185 | * Set the Attribute's name; required
|
---|
186 | *
|
---|
187 | * @param name the attribute's name
|
---|
188 | */
|
---|
189 | public void setName(String name) {
|
---|
190 | this.name = name;
|
---|
191 | }
|
---|
192 |
|
---|
193 | /**
|
---|
194 | * Get the Attribute's name
|
---|
195 | *
|
---|
196 | * @return the attribute's name.
|
---|
197 | */
|
---|
198 | public String getName() {
|
---|
199 | return name;
|
---|
200 | }
|
---|
201 |
|
---|
202 | /**
|
---|
203 | * Get the attribute's Key - its name in lower case.
|
---|
204 | *
|
---|
205 | * @return the attribute's key.
|
---|
206 | */
|
---|
207 | public String getKey() {
|
---|
208 | if (name == null) {
|
---|
209 | return null;
|
---|
210 | }
|
---|
211 | return name.toLowerCase();
|
---|
212 | }
|
---|
213 |
|
---|
214 | /**
|
---|
215 | * Set the Attribute's value; required
|
---|
216 | *
|
---|
217 | * @param value the attribute's value
|
---|
218 | */
|
---|
219 | public void setValue(String value) {
|
---|
220 | if (currentIndex >= values.size()) {
|
---|
221 | values.addElement(value);
|
---|
222 | currentIndex = values.size() - 1;
|
---|
223 | } else {
|
---|
224 | values.setElementAt(value, currentIndex);
|
---|
225 | }
|
---|
226 | }
|
---|
227 |
|
---|
228 | /**
|
---|
229 | * Get the Attribute's value.
|
---|
230 | *
|
---|
231 | * @return the attribute's value.
|
---|
232 | */
|
---|
233 | public String getValue() {
|
---|
234 | if (values.size() == 0) {
|
---|
235 | return null;
|
---|
236 | }
|
---|
237 |
|
---|
238 | String fullValue = "";
|
---|
239 | for (Enumeration e = getValues(); e.hasMoreElements();) {
|
---|
240 | String value = (String) e.nextElement();
|
---|
241 | fullValue += value + " ";
|
---|
242 | }
|
---|
243 | return fullValue.trim();
|
---|
244 | }
|
---|
245 |
|
---|
246 | /**
|
---|
247 | * Add a new value to this attribute - making it multivalued.
|
---|
248 | *
|
---|
249 | * @param value the attribute's additional value
|
---|
250 | */
|
---|
251 | public void addValue(String value) {
|
---|
252 | currentIndex++;
|
---|
253 | setValue(value);
|
---|
254 | }
|
---|
255 |
|
---|
256 | /**
|
---|
257 | * Get all the attribute's values.
|
---|
258 | *
|
---|
259 | * @return an enumeration of the attributes values
|
---|
260 | */
|
---|
261 | public Enumeration getValues() {
|
---|
262 | return values.elements();
|
---|
263 | }
|
---|
264 |
|
---|
265 | /**
|
---|
266 | * Add a continuation line from the Manifest file.
|
---|
267 | *
|
---|
268 | * When lines are too long in a manifest, they are continued on the
|
---|
269 | * next line by starting with a space. This method adds the continuation
|
---|
270 | * data to the attribute value by skipping the first character.
|
---|
271 | *
|
---|
272 | * @param line the continuation line.
|
---|
273 | */
|
---|
274 | public void addContinuation(String line) {
|
---|
275 | String currentValue = (String) values.elementAt(currentIndex);
|
---|
276 | setValue(currentValue + line.substring(1));
|
---|
277 | }
|
---|
278 |
|
---|
279 | /**
|
---|
280 | * Write the attribute out to a print writer.
|
---|
281 | *
|
---|
282 | * @param writer the Writer to which the attribute is written
|
---|
283 | *
|
---|
284 | * @throws IOException if the attribute value cannot be written
|
---|
285 | */
|
---|
286 | public void write(PrintWriter writer) throws IOException {
|
---|
287 | for (Enumeration e = getValues(); e.hasMoreElements();) {
|
---|
288 | writeValue(writer, (String) e.nextElement());
|
---|
289 | }
|
---|
290 | }
|
---|
291 |
|
---|
292 | /**
|
---|
293 | * Write a single attribute value out
|
---|
294 | *
|
---|
295 | * @param writer the Writer to which the attribute is written
|
---|
296 | * @param value the attribute value
|
---|
297 | *
|
---|
298 | * @throws IOException if the attribute value cannot be written
|
---|
299 | */
|
---|
300 | private void writeValue(PrintWriter writer, String value)
|
---|
301 | throws IOException {
|
---|
302 | String line = name + ": " + value;
|
---|
303 | while (line.getBytes().length > MAX_LINE_LENGTH) {
|
---|
304 | // try to find a MAX_LINE_LENGTH byte section
|
---|
305 | int breakIndex = MAX_SECTION_LENGTH;
|
---|
306 | String section = line.substring(0, breakIndex);
|
---|
307 | while (section.getBytes().length > MAX_SECTION_LENGTH
|
---|
308 | && breakIndex > 0) {
|
---|
309 | breakIndex--;
|
---|
310 | section = line.substring(0, breakIndex);
|
---|
311 | }
|
---|
312 | if (breakIndex == 0) {
|
---|
313 | throw new IOException("Unable to write manifest line "
|
---|
314 | + name + ": " + value);
|
---|
315 | }
|
---|
316 | writer.print(section + EOL);
|
---|
317 | line = " " + line.substring(breakIndex);
|
---|
318 | }
|
---|
319 | writer.print(line + EOL);
|
---|
320 | }
|
---|
321 | }
|
---|
322 |
|
---|
323 | /**
|
---|
324 | * A manifest section - you can nest attribute elements into sections.
|
---|
325 | * A section consists of a set of attribute values,
|
---|
326 | * separated from other sections by a blank line.
|
---|
327 | */
|
---|
328 | public static class Section {
|
---|
329 | /** Warnings for this section */
|
---|
330 | private Vector warnings = new Vector();
|
---|
331 |
|
---|
332 | /**
|
---|
333 | * The section's name if any. The main section in a
|
---|
334 | * manifest is unnamed.
|
---|
335 | */
|
---|
336 | private String name = null;
|
---|
337 |
|
---|
338 | /** The section's attributes.*/
|
---|
339 | private Hashtable attributes = new Hashtable();
|
---|
340 |
|
---|
341 | /** Index used to retain the attribute ordering */
|
---|
342 | private Vector attributeIndex = new Vector();
|
---|
343 |
|
---|
344 | /**
|
---|
345 | * The name of the section; optional -default is the main section.
|
---|
346 | * @param name the section's name
|
---|
347 | */
|
---|
348 | public void setName(String name) {
|
---|
349 | this.name = name;
|
---|
350 | }
|
---|
351 |
|
---|
352 | /**
|
---|
353 | * Get the Section's name.
|
---|
354 | *
|
---|
355 | * @return the section's name.
|
---|
356 | */
|
---|
357 | public String getName() {
|
---|
358 | return name;
|
---|
359 | }
|
---|
360 |
|
---|
361 | /**
|
---|
362 | * Read a section through a reader.
|
---|
363 | *
|
---|
364 | * @param reader the reader from which the section is read
|
---|
365 | *
|
---|
366 | * @return the name of the next section if it has been read as
|
---|
367 | * part of this section - This only happens if the
|
---|
368 | * Manifest is malformed.
|
---|
369 | *
|
---|
370 | * @throws ManifestException if the section is not valid according
|
---|
371 | * to the JAR spec
|
---|
372 | * @throws IOException if the section cannot be read from the reader.
|
---|
373 | */
|
---|
374 | public String read(BufferedReader reader)
|
---|
375 | throws ManifestException, IOException {
|
---|
376 | Attribute attribute = null;
|
---|
377 | while (true) {
|
---|
378 | String line = reader.readLine();
|
---|
379 | if (line == null || line.length() == 0) {
|
---|
380 | return null;
|
---|
381 | }
|
---|
382 | if (line.charAt(0) == ' ') {
|
---|
383 | // continuation line
|
---|
384 | if (attribute == null) {
|
---|
385 | if (name != null) {
|
---|
386 | // a continuation on the first line is a
|
---|
387 | // continuation of the name - concatenate this
|
---|
388 | // line and the name
|
---|
389 | name += line.substring(1);
|
---|
390 | } else {
|
---|
391 | throw new ManifestException("Can't start an "
|
---|
392 | + "attribute with a continuation line " + line);
|
---|
393 | }
|
---|
394 | } else {
|
---|
395 | attribute.addContinuation(line);
|
---|
396 | }
|
---|
397 | } else {
|
---|
398 | attribute = new Attribute(line);
|
---|
399 | String nameReadAhead = addAttributeAndCheck(attribute);
|
---|
400 | // refresh attribute in case of multivalued attributes.
|
---|
401 | attribute = getAttribute(attribute.getKey());
|
---|
402 | if (nameReadAhead != null) {
|
---|
403 | return nameReadAhead;
|
---|
404 | }
|
---|
405 | }
|
---|
406 | }
|
---|
407 | }
|
---|
408 |
|
---|
409 | /**
|
---|
410 | * Merge in another section
|
---|
411 | *
|
---|
412 | * @param section the section to be merged with this one.
|
---|
413 | *
|
---|
414 | * @throws ManifestException if the sections cannot be merged.
|
---|
415 | */
|
---|
416 | public void merge(Section section) throws ManifestException {
|
---|
417 | if (name == null && section.getName() != null
|
---|
418 | || name != null
|
---|
419 | && !(name.equalsIgnoreCase(section.getName()))) {
|
---|
420 | throw new ManifestException("Unable to merge sections "
|
---|
421 | + "with different names");
|
---|
422 | }
|
---|
423 |
|
---|
424 | Enumeration e = section.getAttributeKeys();
|
---|
425 | Attribute classpathAttribute = null;
|
---|
426 | while (e.hasMoreElements()) {
|
---|
427 | String attributeName = (String) e.nextElement();
|
---|
428 | Attribute attribute = section.getAttribute(attributeName);
|
---|
429 | if (attributeName.equalsIgnoreCase(ATTRIBUTE_CLASSPATH)) {
|
---|
430 | if (classpathAttribute == null) {
|
---|
431 | classpathAttribute = new Attribute();
|
---|
432 | classpathAttribute.setName(ATTRIBUTE_CLASSPATH);
|
---|
433 | }
|
---|
434 | Enumeration cpe = attribute.getValues();
|
---|
435 | while (cpe.hasMoreElements()) {
|
---|
436 | String value = (String) cpe.nextElement();
|
---|
437 | classpathAttribute.addValue(value);
|
---|
438 | }
|
---|
439 | } else {
|
---|
440 | // the merge file always wins
|
---|
441 | storeAttribute(attribute);
|
---|
442 | }
|
---|
443 | }
|
---|
444 |
|
---|
445 | if (classpathAttribute != null) {
|
---|
446 | // the merge file *always* wins, even for Class-Path
|
---|
447 | storeAttribute(classpathAttribute);
|
---|
448 | }
|
---|
449 |
|
---|
450 | // add in the warnings
|
---|
451 | Enumeration warnEnum = section.warnings.elements();
|
---|
452 | while (warnEnum.hasMoreElements()) {
|
---|
453 | warnings.addElement(warnEnum.nextElement());
|
---|
454 | }
|
---|
455 | }
|
---|
456 |
|
---|
457 | /**
|
---|
458 | * Write the section out to a print writer.
|
---|
459 | *
|
---|
460 | * @param writer the Writer to which the section is written
|
---|
461 | *
|
---|
462 | * @throws IOException if the section cannot be written
|
---|
463 | */
|
---|
464 | public void write(PrintWriter writer) throws IOException {
|
---|
465 | if (name != null) {
|
---|
466 | Attribute nameAttr = new Attribute(ATTRIBUTE_NAME, name);
|
---|
467 | nameAttr.write(writer);
|
---|
468 | }
|
---|
469 | Enumeration e = getAttributeKeys();
|
---|
470 | while (e.hasMoreElements()) {
|
---|
471 | String key = (String) e.nextElement();
|
---|
472 | Attribute attribute = getAttribute(key);
|
---|
473 | attribute.write(writer);
|
---|
474 | }
|
---|
475 | writer.print(EOL);
|
---|
476 | }
|
---|
477 |
|
---|
478 | /**
|
---|
479 | * Get a attribute of the section
|
---|
480 | *
|
---|
481 | * @param attributeName the name of the attribute
|
---|
482 | * @return a Manifest.Attribute instance if the attribute is
|
---|
483 | * single-valued, otherwise a Vector of Manifest.Attribute
|
---|
484 | * instances.
|
---|
485 | */
|
---|
486 | public Attribute getAttribute(String attributeName) {
|
---|
487 | return (Attribute) attributes.get(attributeName.toLowerCase());
|
---|
488 | }
|
---|
489 |
|
---|
490 | /**
|
---|
491 | * Get the attribute keys.
|
---|
492 | *
|
---|
493 | * @return an Enumeration of Strings, each string being the lower case
|
---|
494 | * key of an attribute of the section.
|
---|
495 | */
|
---|
496 | public Enumeration getAttributeKeys() {
|
---|
497 | return attributeIndex.elements();
|
---|
498 | }
|
---|
499 |
|
---|
500 | /**
|
---|
501 | * Get the value of the attribute with the name given.
|
---|
502 | *
|
---|
503 | * @param attributeName the name of the attribute to be returned.
|
---|
504 | *
|
---|
505 | * @return the attribute's value or null if the attribute does not exist
|
---|
506 | * in the section
|
---|
507 | */
|
---|
508 | public String getAttributeValue(String attributeName) {
|
---|
509 | Attribute attribute = getAttribute(attributeName.toLowerCase());
|
---|
510 | if (attribute == null) {
|
---|
511 | return null;
|
---|
512 | }
|
---|
513 | return attribute.getValue();
|
---|
514 | }
|
---|
515 |
|
---|
516 | /**
|
---|
517 | * Remove tge given attribute from the section
|
---|
518 | *
|
---|
519 | * @param attributeName the name of the attribute to be removed.
|
---|
520 | */
|
---|
521 | public void removeAttribute(String attributeName) {
|
---|
522 | String key = attributeName.toLowerCase();
|
---|
523 | attributes.remove(key);
|
---|
524 | attributeIndex.removeElement(key);
|
---|
525 | }
|
---|
526 |
|
---|
527 | /**
|
---|
528 | * Add an attribute to the section.
|
---|
529 | *
|
---|
530 | * @param attribute the attribute to be added to the section
|
---|
531 | *
|
---|
532 | * @exception ManifestException if the attribute is not valid.
|
---|
533 | */
|
---|
534 | public void addConfiguredAttribute(Attribute attribute)
|
---|
535 | throws ManifestException {
|
---|
536 | String check = addAttributeAndCheck(attribute);
|
---|
537 | if (check != null) {
|
---|
538 | throw new BuildException("Specify the section name using "
|
---|
539 | + "the \"name\" attribute of the <section> element rather "
|
---|
540 | + "than using a \"Name\" manifest attribute");
|
---|
541 | }
|
---|
542 | }
|
---|
543 |
|
---|
544 | /**
|
---|
545 | * Add an attribute to the section
|
---|
546 | *
|
---|
547 | * @param attribute the attribute to be added.
|
---|
548 | *
|
---|
549 | * @return the value of the attribute if it is a name
|
---|
550 | * attribute - null other wise
|
---|
551 | *
|
---|
552 | * @exception ManifestException if the attribute already
|
---|
553 | * exists in this section.
|
---|
554 | */
|
---|
555 | public String addAttributeAndCheck(Attribute attribute)
|
---|
556 | throws ManifestException {
|
---|
557 | if (attribute.getName() == null || attribute.getValue() == null) {
|
---|
558 | throw new BuildException("Attributes must have name and value");
|
---|
559 | }
|
---|
560 | if (attribute.getKey().equalsIgnoreCase(ATTRIBUTE_NAME)) {
|
---|
561 | warnings.addElement("\"" + ATTRIBUTE_NAME + "\" attributes "
|
---|
562 | + "should not occur in the main section and must be the "
|
---|
563 | + "first element in all other sections: \""
|
---|
564 | + attribute.getName() + ": " + attribute.getValue() + "\"");
|
---|
565 | return attribute.getValue();
|
---|
566 | }
|
---|
567 |
|
---|
568 | if (attribute.getKey().startsWith(ATTRIBUTE_FROM.toLowerCase())) {
|
---|
569 | warnings.addElement("Manifest attributes should not start "
|
---|
570 | + "with \"" + ATTRIBUTE_FROM + "\" in \""
|
---|
571 | + attribute.getName() + ": " + attribute.getValue() + "\"");
|
---|
572 | } else {
|
---|
573 | // classpath attributes go into a vector
|
---|
574 | String attributeKey = attribute.getKey();
|
---|
575 | if (attributeKey.equalsIgnoreCase(ATTRIBUTE_CLASSPATH)) {
|
---|
576 | Attribute classpathAttribute =
|
---|
577 | (Attribute) attributes.get(attributeKey);
|
---|
578 |
|
---|
579 | if (classpathAttribute == null) {
|
---|
580 | storeAttribute(attribute);
|
---|
581 | } else {
|
---|
582 | warnings.addElement("Multiple Class-Path attributes "
|
---|
583 | + "are supported but violate the Jar "
|
---|
584 | + "specification and may not be correctly "
|
---|
585 | + "processed in all environments");
|
---|
586 | Enumeration e = attribute.getValues();
|
---|
587 | while (e.hasMoreElements()) {
|
---|
588 | String value = (String) e.nextElement();
|
---|
589 | classpathAttribute.addValue(value);
|
---|
590 | }
|
---|
591 | }
|
---|
592 | } else if (attributes.containsKey(attributeKey)) {
|
---|
593 | throw new ManifestException("The attribute \""
|
---|
594 | + attribute.getName() + "\" may not occur more "
|
---|
595 | + "than once in the same section");
|
---|
596 | } else {
|
---|
597 | storeAttribute(attribute);
|
---|
598 | }
|
---|
599 | }
|
---|
600 | return null;
|
---|
601 | }
|
---|
602 |
|
---|
603 | /**
|
---|
604 | * Clone this section
|
---|
605 | *
|
---|
606 | * @return the cloned Section
|
---|
607 | * @since Ant 1.5.2
|
---|
608 | */
|
---|
609 | public Object clone() {
|
---|
610 | Section cloned = new Section();
|
---|
611 | cloned.setName(name);
|
---|
612 | Enumeration e = getAttributeKeys();
|
---|
613 | while (e.hasMoreElements()) {
|
---|
614 | String key = (String) e.nextElement();
|
---|
615 | Attribute attribute = getAttribute(key);
|
---|
616 | cloned.storeAttribute(new Attribute(attribute.getName(),
|
---|
617 | attribute.getValue()));
|
---|
618 | }
|
---|
619 | return cloned;
|
---|
620 | }
|
---|
621 |
|
---|
622 | /**
|
---|
623 | * Store an attribute and update the index.
|
---|
624 | *
|
---|
625 | * @param attribute the attribute to be stored
|
---|
626 | */
|
---|
627 | private void storeAttribute(Attribute attribute) {
|
---|
628 | if (attribute == null) {
|
---|
629 | return;
|
---|
630 | }
|
---|
631 | String attributeKey = attribute.getKey();
|
---|
632 | attributes.put(attributeKey, attribute);
|
---|
633 | if (!attributeIndex.contains(attributeKey)) {
|
---|
634 | attributeIndex.addElement(attributeKey);
|
---|
635 | }
|
---|
636 | }
|
---|
637 |
|
---|
638 | /**
|
---|
639 | * Get the warnings for this section.
|
---|
640 | *
|
---|
641 | * @return an Enumeration of warning strings.
|
---|
642 | */
|
---|
643 | public Enumeration getWarnings() {
|
---|
644 | return warnings.elements();
|
---|
645 | }
|
---|
646 |
|
---|
647 | /**
|
---|
648 | * @see java.lang.Object#hashCode
|
---|
649 | */
|
---|
650 | public int hashCode() {
|
---|
651 | int hashCode = 0;
|
---|
652 |
|
---|
653 | if (name != null) {
|
---|
654 | hashCode += name.hashCode();
|
---|
655 | }
|
---|
656 |
|
---|
657 | hashCode += attributes.hashCode();
|
---|
658 | return hashCode;
|
---|
659 | }
|
---|
660 |
|
---|
661 | /**
|
---|
662 | * @see java.lang.Object#equals
|
---|
663 | */
|
---|
664 | public boolean equals(Object rhs) {
|
---|
665 | if (rhs == null || rhs.getClass() != getClass()) {
|
---|
666 | return false;
|
---|
667 | }
|
---|
668 |
|
---|
669 | if (rhs == this) {
|
---|
670 | return true;
|
---|
671 | }
|
---|
672 |
|
---|
673 | Section rhsSection = (Section) rhs;
|
---|
674 |
|
---|
675 | return CollectionUtils.equals(attributes, rhsSection.attributes);
|
---|
676 | }
|
---|
677 | }
|
---|
678 |
|
---|
679 |
|
---|
680 | /** The version of this manifest */
|
---|
681 | private String manifestVersion = DEFAULT_MANIFEST_VERSION;
|
---|
682 |
|
---|
683 | /** The main section of this manifest */
|
---|
684 | private Section mainSection = new Section();
|
---|
685 |
|
---|
686 | /** The named sections of this manifest */
|
---|
687 | private Hashtable sections = new Hashtable();
|
---|
688 |
|
---|
689 | /** Index of sections - used to retain order of sections in manifest */
|
---|
690 | private Vector sectionIndex = new Vector();
|
---|
691 |
|
---|
692 | /**
|
---|
693 | * Construct a manifest from Ant's default manifest file.
|
---|
694 | *
|
---|
695 | * @return the default manifest.
|
---|
696 | * @exception BuildException if there is a problem loading the
|
---|
697 | * default manifest
|
---|
698 | */
|
---|
699 | public static Manifest getDefaultManifest() throws BuildException {
|
---|
700 | try {
|
---|
701 | String defManifest = "/org/apache/tools/ant/defaultManifest.mf";
|
---|
702 | InputStream in = Manifest.class.getResourceAsStream(defManifest);
|
---|
703 | if (in == null) {
|
---|
704 | throw new BuildException("Could not find default manifest: "
|
---|
705 | + defManifest);
|
---|
706 | }
|
---|
707 | try {
|
---|
708 | Manifest defaultManifest
|
---|
709 | = new Manifest(new InputStreamReader(in, "UTF-8"));
|
---|
710 | Attribute createdBy = new Attribute("Created-By",
|
---|
711 | System.getProperty("java.vm.version") + " ("
|
---|
712 | + System.getProperty("java.vm.vendor") + ")");
|
---|
713 | defaultManifest.getMainSection().storeAttribute(createdBy);
|
---|
714 | return defaultManifest;
|
---|
715 | } catch (UnsupportedEncodingException e) {
|
---|
716 | return new Manifest(new InputStreamReader(in));
|
---|
717 | }
|
---|
718 | } catch (ManifestException e) {
|
---|
719 | throw new BuildException("Default manifest is invalid !!", e);
|
---|
720 | } catch (IOException e) {
|
---|
721 | throw new BuildException("Unable to read default manifest", e);
|
---|
722 | }
|
---|
723 | }
|
---|
724 |
|
---|
725 | /** Construct an empty manifest */
|
---|
726 | public Manifest() {
|
---|
727 | manifestVersion = null;
|
---|
728 | }
|
---|
729 |
|
---|
730 | /**
|
---|
731 | * Read a manifest file from the given reader
|
---|
732 | *
|
---|
733 | * @param r is the reader from which the Manifest is read
|
---|
734 | *
|
---|
735 | * @throws ManifestException if the manifest is not valid according
|
---|
736 | * to the JAR spec
|
---|
737 | * @throws IOException if the manifest cannot be read from the reader.
|
---|
738 | */
|
---|
739 | public Manifest(Reader r) throws ManifestException, IOException {
|
---|
740 | BufferedReader reader = new BufferedReader(r);
|
---|
741 | // This should be the manifest version
|
---|
742 | String nextSectionName = mainSection.read(reader);
|
---|
743 | String readManifestVersion
|
---|
744 | = mainSection.getAttributeValue(ATTRIBUTE_MANIFEST_VERSION);
|
---|
745 | if (readManifestVersion != null) {
|
---|
746 | manifestVersion = readManifestVersion;
|
---|
747 | mainSection.removeAttribute(ATTRIBUTE_MANIFEST_VERSION);
|
---|
748 | }
|
---|
749 |
|
---|
750 | String line = null;
|
---|
751 | while ((line = reader.readLine()) != null) {
|
---|
752 | if (line.length() == 0) {
|
---|
753 | continue;
|
---|
754 | }
|
---|
755 |
|
---|
756 | Section section = new Section();
|
---|
757 | if (nextSectionName == null) {
|
---|
758 | Attribute sectionName = new Attribute(line);
|
---|
759 | if (!sectionName.getName().equalsIgnoreCase(ATTRIBUTE_NAME)) {
|
---|
760 | throw new ManifestException("Manifest sections should "
|
---|
761 | + "start with a \"" + ATTRIBUTE_NAME
|
---|
762 | + "\" attribute and not \""
|
---|
763 | + sectionName.getName() + "\"");
|
---|
764 | }
|
---|
765 | nextSectionName = sectionName.getValue();
|
---|
766 | } else {
|
---|
767 | // we have already started reading this section
|
---|
768 | // this line is the first attribute. set it and then
|
---|
769 | // let the normal read handle the rest
|
---|
770 | Attribute firstAttribute = new Attribute(line);
|
---|
771 | section.addAttributeAndCheck(firstAttribute);
|
---|
772 | }
|
---|
773 |
|
---|
774 | section.setName(nextSectionName);
|
---|
775 | nextSectionName = section.read(reader);
|
---|
776 | addConfiguredSection(section);
|
---|
777 | }
|
---|
778 | }
|
---|
779 |
|
---|
780 | /**
|
---|
781 | * Add a section to the manifest
|
---|
782 | *
|
---|
783 | * @param section the manifest section to be added
|
---|
784 | *
|
---|
785 | * @exception ManifestException if the secti0on is not valid.
|
---|
786 | */
|
---|
787 | public void addConfiguredSection(Section section)
|
---|
788 | throws ManifestException {
|
---|
789 | String sectionName = section.getName();
|
---|
790 | if (sectionName == null) {
|
---|
791 | throw new BuildException("Sections must have a name");
|
---|
792 | }
|
---|
793 | sections.put(sectionName, section);
|
---|
794 | if (!sectionIndex.contains(sectionName)) {
|
---|
795 | sectionIndex.addElement(sectionName);
|
---|
796 | }
|
---|
797 | }
|
---|
798 |
|
---|
799 | /**
|
---|
800 | * Add an attribute to the manifest - it is added to the main section.
|
---|
801 | *
|
---|
802 | * @param attribute the attribute to be added.
|
---|
803 | *
|
---|
804 | * @exception ManifestException if the attribute is not valid.
|
---|
805 | */
|
---|
806 | public void addConfiguredAttribute(Attribute attribute)
|
---|
807 | throws ManifestException {
|
---|
808 | if (attribute.getKey() == null || attribute.getValue() == null) {
|
---|
809 | throw new BuildException("Attributes must have name and value");
|
---|
810 | }
|
---|
811 | if (attribute.getKey().equalsIgnoreCase(ATTRIBUTE_MANIFEST_VERSION)) {
|
---|
812 | manifestVersion = attribute.getValue();
|
---|
813 | } else {
|
---|
814 | mainSection.addConfiguredAttribute(attribute);
|
---|
815 | }
|
---|
816 | }
|
---|
817 |
|
---|
818 | /**
|
---|
819 | * Merge the contents of the given manifest into this manifest
|
---|
820 | *
|
---|
821 | * @param other the Manifest to be merged with this one.
|
---|
822 | *
|
---|
823 | * @throws ManifestException if there is a problem merging the
|
---|
824 | * manifest according to the Manifest spec.
|
---|
825 | */
|
---|
826 | public void merge(Manifest other) throws ManifestException {
|
---|
827 | merge(other, false);
|
---|
828 | }
|
---|
829 |
|
---|
830 | /**
|
---|
831 | * Merge the contents of the given manifest into this manifest
|
---|
832 | *
|
---|
833 | * @param other the Manifest to be merged with this one.
|
---|
834 | * @param overwriteMain whether to overwrite the main section
|
---|
835 | * of the current manifest
|
---|
836 | *
|
---|
837 | * @throws ManifestException if there is a problem merging the
|
---|
838 | * manifest according to the Manifest spec.
|
---|
839 | */
|
---|
840 | public void merge(Manifest other, boolean overwriteMain)
|
---|
841 | throws ManifestException {
|
---|
842 | if (other != null) {
|
---|
843 | if (overwriteMain) {
|
---|
844 | mainSection = (Section) other.mainSection.clone();
|
---|
845 | } else {
|
---|
846 | mainSection.merge(other.mainSection);
|
---|
847 | }
|
---|
848 |
|
---|
849 | if (other.manifestVersion != null) {
|
---|
850 | manifestVersion = other.manifestVersion;
|
---|
851 | }
|
---|
852 |
|
---|
853 | Enumeration e = other.getSectionNames();
|
---|
854 | while (e.hasMoreElements()) {
|
---|
855 | String sectionName = (String) e.nextElement();
|
---|
856 | Section ourSection = (Section) sections.get(sectionName);
|
---|
857 | Section otherSection
|
---|
858 | = (Section) other.sections.get(sectionName);
|
---|
859 | if (ourSection == null) {
|
---|
860 | if (otherSection != null) {
|
---|
861 | addConfiguredSection((Section) otherSection.clone());
|
---|
862 | }
|
---|
863 | } else {
|
---|
864 | ourSection.merge(otherSection);
|
---|
865 | }
|
---|
866 | }
|
---|
867 | }
|
---|
868 | }
|
---|
869 |
|
---|
870 | /**
|
---|
871 | * Write the manifest out to a print writer.
|
---|
872 | *
|
---|
873 | * @param writer the Writer to which the manifest is written
|
---|
874 | *
|
---|
875 | * @throws IOException if the manifest cannot be written
|
---|
876 | */
|
---|
877 | public void write(PrintWriter writer) throws IOException {
|
---|
878 | writer.print(ATTRIBUTE_MANIFEST_VERSION + ": " + manifestVersion + EOL);
|
---|
879 | String signatureVersion
|
---|
880 | = mainSection.getAttributeValue(ATTRIBUTE_SIGNATURE_VERSION);
|
---|
881 | if (signatureVersion != null) {
|
---|
882 | writer.print(ATTRIBUTE_SIGNATURE_VERSION + ": "
|
---|
883 | + signatureVersion + EOL);
|
---|
884 | mainSection.removeAttribute(ATTRIBUTE_SIGNATURE_VERSION);
|
---|
885 | }
|
---|
886 | mainSection.write(writer);
|
---|
887 |
|
---|
888 | // add it back
|
---|
889 | if (signatureVersion != null) {
|
---|
890 | try {
|
---|
891 | Attribute svAttr = new Attribute(ATTRIBUTE_SIGNATURE_VERSION,
|
---|
892 | signatureVersion);
|
---|
893 | mainSection.addConfiguredAttribute(svAttr);
|
---|
894 | } catch (ManifestException e) {
|
---|
895 | // shouldn't happen - ignore
|
---|
896 | }
|
---|
897 | }
|
---|
898 |
|
---|
899 | Enumeration e = sectionIndex.elements();
|
---|
900 | while (e.hasMoreElements()) {
|
---|
901 | String sectionName = (String) e.nextElement();
|
---|
902 | Section section = getSection(sectionName);
|
---|
903 | section.write(writer);
|
---|
904 | }
|
---|
905 | }
|
---|
906 |
|
---|
907 | /**
|
---|
908 | * Convert the manifest to its string representation
|
---|
909 | *
|
---|
910 | * @return a multiline string with the Manifest as it
|
---|
911 | * appears in a Manifest file.
|
---|
912 | */
|
---|
913 | public String toString() {
|
---|
914 | StringWriter sw = new StringWriter();
|
---|
915 | try {
|
---|
916 | write(new PrintWriter(sw));
|
---|
917 | } catch (IOException e) {
|
---|
918 | return null;
|
---|
919 | }
|
---|
920 | return sw.toString();
|
---|
921 | }
|
---|
922 |
|
---|
923 | /**
|
---|
924 | * Get the warnings for this manifest.
|
---|
925 | *
|
---|
926 | * @return an enumeration of warning strings
|
---|
927 | */
|
---|
928 | public Enumeration getWarnings() {
|
---|
929 | Vector warnings = new Vector();
|
---|
930 |
|
---|
931 | Enumeration warnEnum = mainSection.getWarnings();
|
---|
932 | while (warnEnum.hasMoreElements()) {
|
---|
933 | warnings.addElement(warnEnum.nextElement());
|
---|
934 | }
|
---|
935 |
|
---|
936 | // create a vector and add in the warnings for all the sections
|
---|
937 | Enumeration e = sections.elements();
|
---|
938 | while (e.hasMoreElements()) {
|
---|
939 | Section section = (Section) e.nextElement();
|
---|
940 | Enumeration e2 = section.getWarnings();
|
---|
941 | while (e2.hasMoreElements()) {
|
---|
942 | warnings.addElement(e2.nextElement());
|
---|
943 | }
|
---|
944 | }
|
---|
945 |
|
---|
946 | return warnings.elements();
|
---|
947 | }
|
---|
948 |
|
---|
949 | /**
|
---|
950 | * @see java.lang.Object#hashCode
|
---|
951 | */
|
---|
952 | public int hashCode() {
|
---|
953 | int hashCode = 0;
|
---|
954 |
|
---|
955 | if (manifestVersion != null) {
|
---|
956 | hashCode += manifestVersion.hashCode();
|
---|
957 | }
|
---|
958 | hashCode += mainSection.hashCode();
|
---|
959 | hashCode += sections.hashCode();
|
---|
960 |
|
---|
961 | return hashCode;
|
---|
962 | }
|
---|
963 |
|
---|
964 | /**
|
---|
965 | * @see java.lang.Object#equals
|
---|
966 | */
|
---|
967 | public boolean equals(Object rhs) {
|
---|
968 | if (rhs == null || rhs.getClass() != getClass()) {
|
---|
969 | return false;
|
---|
970 | }
|
---|
971 |
|
---|
972 | if (rhs == this) {
|
---|
973 | return true;
|
---|
974 | }
|
---|
975 |
|
---|
976 | Manifest rhsManifest = (Manifest) rhs;
|
---|
977 | if (manifestVersion == null) {
|
---|
978 | if (rhsManifest.manifestVersion != null) {
|
---|
979 | return false;
|
---|
980 | }
|
---|
981 | } else if (!manifestVersion.equals(rhsManifest.manifestVersion)) {
|
---|
982 | return false;
|
---|
983 | }
|
---|
984 |
|
---|
985 | if (!mainSection.equals(rhsManifest.mainSection)) {
|
---|
986 | return false;
|
---|
987 | }
|
---|
988 |
|
---|
989 | return CollectionUtils.equals(sections, rhsManifest.sections);
|
---|
990 | }
|
---|
991 |
|
---|
992 | /**
|
---|
993 | * Get the version of the manifest
|
---|
994 | *
|
---|
995 | * @return the manifest's version string
|
---|
996 | */
|
---|
997 | public String getManifestVersion() {
|
---|
998 | return manifestVersion;
|
---|
999 | }
|
---|
1000 |
|
---|
1001 | /**
|
---|
1002 | * Get the main section of the manifest
|
---|
1003 | *
|
---|
1004 | * @return the main section of the manifest
|
---|
1005 | */
|
---|
1006 | public Section getMainSection() {
|
---|
1007 | return mainSection;
|
---|
1008 | }
|
---|
1009 |
|
---|
1010 | /**
|
---|
1011 | * Get a particular section from the manifest
|
---|
1012 | *
|
---|
1013 | * @param name the name of the section desired.
|
---|
1014 | * @return the specified section or null if that section
|
---|
1015 | * does not exist in the manifest
|
---|
1016 | */
|
---|
1017 | public Section getSection(String name) {
|
---|
1018 | return (Section) sections.get(name);
|
---|
1019 | }
|
---|
1020 |
|
---|
1021 | /**
|
---|
1022 | * Get the section names in this manifest.
|
---|
1023 | *
|
---|
1024 | * @return an Enumeration of section names
|
---|
1025 | */
|
---|
1026 | public Enumeration getSectionNames() {
|
---|
1027 | return sectionIndex.elements();
|
---|
1028 | }
|
---|
1029 | }
|
---|