blob: 810513a1ccb2ea837c9da5179c165da6f072d955 [file] [log] [blame]
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001package aQute.bnd.header;
Stuart McCullochbb014372012-06-07 21:57:32 +00002
3import java.util.*;
4
5import aQute.libg.generics.*;
6import aQute.libg.qtokens.*;
Stuart McCulloch81d48de2012-06-29 19:23:09 +00007import aQute.service.reporter.*;
Stuart McCullochbb014372012-06-07 21:57:32 +00008
9public class OSGiHeader {
10
11 static public Parameters parseHeader(String value) {
12 return parseHeader(value, null);
13 }
14
15 /**
16 * Standard OSGi header parser. This parser can handle the format clauses
17 * ::= clause ( ',' clause ) + clause ::= name ( ';' name ) (';' key '='
Stuart McCulloch2286f232012-06-15 13:27:53 +000018 * value ) This is mapped to a Map { name => Map { attr|directive => value }
19 * }
Stuart McCullochbb014372012-06-07 21:57:32 +000020 *
21 * @param value
22 * A string
23 * @return a Map<String,Map<String,String>>
24 */
25 static public Parameters parseHeader(String value, Reporter logger) {
26 return parseHeader(value, logger, new Parameters());
27 }
28
29 static public Parameters parseHeader(String value, Reporter logger, Parameters result) {
30 if (value == null || value.trim().length() == 0)
31 return result;
32
33 QuotedTokenizer qt = new QuotedTokenizer(value, ";=,");
34 char del = 0;
35 do {
36 boolean hadAttribute = false;
37 Attrs clause = new Attrs();
38 List<String> aliases = Create.list();
39 String name = qt.nextToken(",;");
40
41 del = qt.getSeparator();
42 if (name == null || name.length() == 0) {
43 if (logger != null && logger.isPedantic()) {
44 logger.warning("Empty clause, usually caused by repeating a comma without any name field or by having spaces after the backslash of a property file: "
45 + value);
46 }
47 if (name == null)
48 break;
49 } else {
50 name = name.trim();
51
52 aliases.add(name);
53 while (del == ';') {
54 String adname = qt.nextToken();
55 if ((del = qt.getSeparator()) != '=') {
56 if (hadAttribute)
57 if (logger != null) {
Stuart McCulloch2286f232012-06-15 13:27:53 +000058 logger.error("Header contains name field after attribute or directive: " + adname
59 + " from " + value
Stuart McCullochbb014372012-06-07 21:57:32 +000060 + ". Name fields must be consecutive, separated by a ';' like a;b;c;x=3;y=4");
61 }
62 if (adname != null && adname.length() > 0)
63 aliases.add(adname.trim());
64 } else {
65 String advalue = qt.nextToken();
66 if (clause.containsKey(adname)) {
67 if (logger != null && logger.isPedantic())
Stuart McCulloch2286f232012-06-15 13:27:53 +000068 logger.warning("Duplicate attribute/directive name " + adname + " in " + value
Stuart McCullochbb014372012-06-07 21:57:32 +000069 + ". This attribute/directive will be ignored");
70 }
71 if (advalue == null) {
72 if (logger != null)
73 logger.error("No value after '=' sign for attribute " + adname);
74 advalue = "";
75 }
76 clause.put(adname.trim(), advalue.trim());
77 del = qt.getSeparator();
78 hadAttribute = true;
79 }
80 }
81
82 // Check for duplicate names. The aliases list contains
83 // the list of nams, for each check if it exists. If so,
84 // add a number of "~" to make it unique.
85 for (String clauseName : aliases) {
86 if (result.containsKey(clauseName)) {
87 if (logger != null && logger.isPedantic())
88 logger.warning("Duplicate name "
89 + clauseName
90 + " used in header: '"
91 + clauseName
92 + "'. Duplicate names are specially marked in Bnd with a ~ at the end (which is stripped at printing time).");
93 while (result.containsKey(clauseName))
94 clauseName += "~";
95 }
96 result.put(clauseName, clause);
97 }
98 }
99 } while (del == ',');
100 return result;
101 }
102
103 public static Attrs parseProperties(String input) {
104 return parseProperties(input, null);
105 }
106
107 public static Attrs parseProperties(String input, Reporter logger) {
108 if (input == null || input.trim().length() == 0)
109 return new Attrs();
110
111 Attrs result = new Attrs();
112 QuotedTokenizer qt = new QuotedTokenizer(input, "=,");
113 char del = ',';
114
115 while (del == ',') {
116 String key = qt.nextToken(",=");
117 String value = "";
118 del = qt.getSeparator();
119 if (del == '=') {
120 value = qt.nextToken(",=");
121 del = qt.getSeparator();
122 }
123 result.put(key, value);
124 }
125 if (del != 0) {
126 if (logger == null)
127 throw new IllegalArgumentException("Invalid syntax for properties: " + input);
128 logger.error("Invalid syntax for properties: " + input);
129 }
130
131 return result;
132 }
133
134}