9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.util.jar;
27
28 import java.io.DataOutputStream;
29 import java.io.IOException;
30 import java.util.Collection;
31 import java.util.HashMap;
32 import java.util.LinkedHashMap;
33 import java.util.Map;
34 import java.util.Objects;
35 import java.util.Set;
36
37 import sun.util.logging.PlatformLogger;
38
39 /**
40 * The Attributes class maps Manifest attribute names to associated string
41 * values. Valid attribute names are case-insensitive, are restricted to
42 * the ASCII characters in the set [0-9a-zA-Z_-], and cannot exceed 70
43 * characters in length. There must be a colon and a SPACE after the name;
44 * the combined length will not exceed 72 characters.
45 * Attribute values can contain any characters and
46 * will be UTF8-encoded when written to the output stream. See the
47 * <a href="{@docRoot}/../specs/jar/jar.html">JAR File Specification</a>
48 * for more information about valid attribute names and values.
49 *
50 * <p>This map and its views have a predictable iteration order, namely the
51 * order that keys were inserted into the map, as with {@link LinkedHashMap}.
52 *
53 * @author David Connelly
54 * @see Manifest
55 * @since 1.2
56 */
57 public class Attributes implements Map<Object,Object>, Cloneable {
58 /**
59 * The attribute name-value mappings.
60 */
61 protected Map<Object,Object> map;
62
63 /**
64 * Constructs a new, empty Attributes object with default size.
65 */
66 public Attributes() {
67 this(11);
68 }
69
70 /**
71 * Constructs a new, empty Attributes object with the specified
72 * initial size.
73 *
74 * @param size the initial number of attributes
75 */
76 public Attributes(int size) {
77 map = new LinkedHashMap<>(size);
78 }
79
80 /**
81 * Constructs a new Attributes object with the same attribute name-value
82 * mappings as in the specified Attributes.
83 *
352
353 String value = (String) e.getValue();
354 if (value != null) {
355 byte[] vb = value.getBytes("UTF8");
356 value = new String(vb, 0, 0, vb.length);
357 }
358 buffer.append(value);
359
360 Manifest.make72Safe(buffer);
361 buffer.append("\r\n");
362 out.writeBytes(buffer.toString());
363 }
364 }
365 out.writeBytes("\r\n");
366 }
367
368 /*
369 * Reads attributes from the specified input stream.
370 * XXX Need to handle UTF8 values.
371 */
372 @SuppressWarnings("deprecation")
373 void read(Manifest.FastInputStream is, byte[] lbuf) throws IOException {
374 String name = null, value;
375 byte[] lastline = null;
376
377 int len;
378 while ((len = is.readLine(lbuf)) != -1) {
379 boolean lineContinued = false;
380 byte c = lbuf[--len];
381 if (c != '\n' && c != '\r') {
382 throw new IOException("line too long");
383 }
384 if (len > 0 && lbuf[len-1] == '\r') {
385 --len;
386 }
387 if (len == 0) {
388 break;
389 }
390 int i = 0;
391 if (lbuf[0] == ' ') {
392 // continuation of previous line
393 if (name == null) {
394 throw new IOException("misplaced continuation line");
395 }
396 lineContinued = true;
397 byte[] buf = new byte[lastline.length + len - 1];
398 System.arraycopy(lastline, 0, buf, 0, lastline.length);
399 System.arraycopy(lbuf, 1, buf, lastline.length, len - 1);
400 if (is.peek() == ' ') {
401 lastline = buf;
402 continue;
403 }
404 value = new String(buf, 0, buf.length, "UTF8");
405 lastline = null;
406 } else {
407 while (lbuf[i++] != ':') {
408 if (i >= len) {
409 throw new IOException("invalid header field");
410 }
411 }
412 if (lbuf[i++] != ' ') {
413 throw new IOException("invalid header field");
414 }
415 name = new String(lbuf, 0, 0, i - 2);
416 if (is.peek() == ' ') {
417 lastline = new byte[len - i];
418 System.arraycopy(lbuf, i, lastline, 0, len - i);
419 continue;
420 }
421 value = new String(lbuf, i, len - i, "UTF8");
422 }
423 try {
424 if ((putValue(name, value) != null) && (!lineContinued)) {
425 PlatformLogger.getLogger("java.util.jar").warning(
426 "Duplicate name in Manifest: " + name
427 + ".\n"
428 + "Ensure that the manifest does not "
429 + "have duplicate entries, and\n"
430 + "that blank lines separate "
431 + "individual sections in both your\n"
432 + "manifest and in the META-INF/MANIFEST.MF "
433 + "entry in the jar file.");
434 }
435 } catch (IllegalArgumentException e) {
436 throw new IOException("invalid header field name: " + name);
437 }
438 }
439 }
440
441 /**
442 * The Attributes.Name class represents an attribute name stored in
443 * this Map. Valid attribute names are case-insensitive, are restricted
444 * to the ASCII characters in the set [0-9a-zA-Z_-], and cannot exceed
445 * 70 characters in length. Attribute values can contain any characters
446 * and will be UTF8-encoded when written to the output stream. See the
447 * <a href="{@docRoot}/../specs/jar/jar.html">JAR File Specification</a>
448 * for more information about valid attribute names and values.
449 */
450 public static class Name {
451 private final String name;
452 private final int hashCode;
453
454 /**
455 * Avoid allocation for common Names
456 */
457 private static final Map<String, Name> KNOWN_NAMES;
458
|
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.util.jar;
27
28 import java.io.DataOutputStream;
29 import java.io.File;
30 import java.io.IOException;
31 import java.security.AccessController;
32 import java.security.PrivilegedAction;
33 import java.security.Security;
34 import java.util.Collection;
35 import java.util.HashMap;
36 import java.util.LinkedHashMap;
37 import java.util.Map;
38 import java.util.Objects;
39 import java.util.Set;
40
41 import sun.util.logging.PlatformLogger;
42
43 /**
44 * The Attributes class maps Manifest attribute names to associated string
45 * values. Valid attribute names are case-insensitive, are restricted to
46 * the ASCII characters in the set [0-9a-zA-Z_-], and cannot exceed 70
47 * characters in length. There must be a colon and a SPACE after the name;
48 * the combined length will not exceed 72 characters.
49 * Attribute values can contain any characters and
50 * will be UTF8-encoded when written to the output stream. See the
51 * <a href="{@docRoot}/../specs/jar/jar.html">JAR File Specification</a>
52 * for more information about valid attribute names and values.
53 *
54 * <p>This map and its views have a predictable iteration order, namely the
55 * order that keys were inserted into the map, as with {@link LinkedHashMap}.
56 *
57 * @author David Connelly
58 * @see Manifest
59 * @since 1.2
60 */
61 public class Attributes implements Map<Object,Object>, Cloneable {
62 /**
63 * The attribute name-value mappings.
64 */
65 protected Map<Object,Object> map;
66
67 /**
68 * Security or system property which specifies categories of
69 * (potentially sensitive) information that may be included
70 * in exception text. This class only defines one category:
71 * "jarpath" which represents the path of a jar file
72 * relating to an IO exception.
73 * The property value is a comma separated list of
74 * case insignificant category names.
75 */
76 private static final String enhancedTextPropname = "jdk.includeInExceptions";
77
78 private static final boolean jarPathInExceptionText = initTextProp();
79
80 private static boolean initTextProp() {
81 return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
82 public Boolean run() {
83 String val = System.getProperty(enhancedTextPropname);
84 if (val == null) {
85 val = Security.getProperty(enhancedTextPropname);
86 if (val == null)
87 return false;
88 }
89 String[] tokens = val.split(",");
90 for (String token : tokens) {
91 if (token.equalsIgnoreCase("jarpath"))
92 return true;
93 }
94 return false;
95 }
96 });
97 }
98
99
100 /**
101 * Constructs a new, empty Attributes object with default size.
102 */
103 public Attributes() {
104 this(11);
105 }
106
107 /**
108 * Constructs a new, empty Attributes object with the specified
109 * initial size.
110 *
111 * @param size the initial number of attributes
112 */
113 public Attributes(int size) {
114 map = new LinkedHashMap<>(size);
115 }
116
117 /**
118 * Constructs a new Attributes object with the same attribute name-value
119 * mappings as in the specified Attributes.
120 *
389
390 String value = (String) e.getValue();
391 if (value != null) {
392 byte[] vb = value.getBytes("UTF8");
393 value = new String(vb, 0, 0, vb.length);
394 }
395 buffer.append(value);
396
397 Manifest.make72Safe(buffer);
398 buffer.append("\r\n");
399 out.writeBytes(buffer.toString());
400 }
401 }
402 out.writeBytes("\r\n");
403 }
404
405 /*
406 * Reads attributes from the specified input stream.
407 * XXX Need to handle UTF8 values.
408 */
409 void read(Manifest.FastInputStream is, byte[] lbuf) throws IOException {
410 read(is, lbuf, null, 0);
411 }
412
413 @SuppressWarnings("deprecation")
414 int read(Manifest.FastInputStream is, byte[] lbuf, String filename, int offset) throws IOException {
415 String name = null, value;
416 byte[] lastline = null;
417 int lineNumber = offset;
418
419 int len;
420 while ((len = is.readLine(lbuf)) != -1) {
421 boolean lineContinued = false;
422 byte c = lbuf[--len];
423 lineNumber++;
424
425 if (c != '\n' && c != '\r') {
426 throw new IOException("line too long (" + getErrorPosition(filename, lineNumber) + ")");
427 }
428 if (len > 0 && lbuf[len-1] == '\r') {
429 --len;
430 }
431 if (len == 0) {
432 break;
433 }
434 int i = 0;
435 if (lbuf[0] == ' ') {
436 // continuation of previous line
437 if (name == null) {
438 throw new IOException("misplaced continuation line (" + getErrorPosition(filename, lineNumber) + ")");
439 }
440 lineContinued = true;
441 byte[] buf = new byte[lastline.length + len - 1];
442 System.arraycopy(lastline, 0, buf, 0, lastline.length);
443 System.arraycopy(lbuf, 1, buf, lastline.length, len - 1);
444 if (is.peek() == ' ') {
445 lastline = buf;
446 continue;
447 }
448 value = new String(buf, 0, buf.length, "UTF8");
449 lastline = null;
450 } else {
451 while (lbuf[i++] != ':') {
452 if (i >= len) {
453 throw new IOException("invalid header field (" + getErrorPosition(filename, lineNumber) + ")");
454 }
455 }
456 if (lbuf[i++] != ' ') {
457 throw new IOException("invalid header field (" + getErrorPosition(filename, lineNumber) + ")");
458 }
459 name = new String(lbuf, 0, 0, i - 2);
460 if (is.peek() == ' ') {
461 lastline = new byte[len - i];
462 System.arraycopy(lbuf, i, lastline, 0, len - i);
463 continue;
464 }
465 value = new String(lbuf, i, len - i, "UTF8");
466 }
467 try {
468 if ((putValue(name, value) != null) && (!lineContinued)) {
469 PlatformLogger.getLogger("java.util.jar").warning(
470 "Duplicate name in Manifest: " + name
471 + ".\n"
472 + "Ensure that the manifest does not "
473 + "have duplicate entries, and\n"
474 + "that blank lines separate "
475 + "individual sections in both your\n"
476 + "manifest and in the META-INF/MANIFEST.MF "
477 + "entry in the jar file.");
478 }
479 } catch (IllegalArgumentException e) {
480 throw new IOException("invalid header field name: " + name + " (" + getErrorPosition(filename, lineNumber) + ")");
481 }
482 }
483 return lineNumber;
484 }
485
486 static String getErrorPosition(String filename, final int lineNumber) {
487 if (filename == null || !jarPathInExceptionText) {
488 return "line " + lineNumber;
489 }
490
491 final File file = new File(filename);
492 return AccessController.doPrivileged(new PrivilegedAction<String>() {
493 public String run() {
494 return file.getAbsolutePath() + ":" + lineNumber;
495 }
496 });
497 }
498
499 /**
500 * The Attributes.Name class represents an attribute name stored in
501 * this Map. Valid attribute names are case-insensitive, are restricted
502 * to the ASCII characters in the set [0-9a-zA-Z_-], and cannot exceed
503 * 70 characters in length. Attribute values can contain any characters
504 * and will be UTF8-encoded when written to the output stream. See the
505 * <a href="{@docRoot}/../specs/jar/jar.html">JAR File Specification</a>
506 * for more information about valid attribute names and values.
507 */
508 public static class Name {
509 private final String name;
510 private final int hashCode;
511
512 /**
513 * Avoid allocation for common Names
514 */
515 private static final Map<String, Name> KNOWN_NAMES;
516
|