blob: 563203438ab80184fa1540abb12cbfc281681288 [file] [log] [blame]
Stuart McCulloch26e7a5a2011-10-17 10:31:43 +00001package aQute.libg.header;
2
3import java.util.*;
4
5import aQute.libg.generics.*;
6import aQute.libg.qtokens.*;
7import aQute.libg.reporter.*;
8
9public class OSGiHeader {
10
11 static public Map<String, Map<String, String>> 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 '='
18 * value )
19 *
20 * This is mapped to a Map { name => Map { attr|directive => value } }
21 *
22 * @param value
23 * A string
24 * @return a Map<String,Map<String,String>>
25 */
26 static public Map<String, Map<String, String>> parseHeader(String value,
27 Reporter logger) {
28 if (value == null || value.trim().length() == 0)
29 return Create.map();
30
31 Map<String, Map<String, String>> result = Create.map();
32 QuotedTokenizer qt = new QuotedTokenizer(value, ";=,");
33 char del = 0;
34 do {
35 boolean hadAttribute = false;
36 Map<String, String> clause = Create.map();
37 List<String> aliases = Create.list();
38 String name = qt.nextToken(",;");
39
40 del = qt.getSeparator();
41 if (name == null || name.length() == 0) {
42 if (logger != null && logger.isPedantic()) {
43 logger
44 .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) {
58 logger
59 .error("Header contains name field after attribute or directive: "
60 + adname
61 + " from "
62 + value
63 + ". Name fields must be consecutive, separated by a ';' like a;b;c;x=3;y=4");
64 }
65 if (adname != null && adname.length() > 0)
66 aliases.add(adname.trim());
67 } else {
68 String advalue = qt.nextToken();
69 if (clause.containsKey(adname)) {
70 if (logger != null && logger.isPedantic())
71 logger
72 .warning("Duplicate attribute/directive name "
73 + adname
74 + " in "
75 + value
76 + ". This attribute/directive will be ignored");
77 }
78 if (advalue == null) {
79 if (logger != null)
80 logger
81 .error("No value after '=' sign for attribute "
82 + adname);
83 advalue = "";
84 }
85 clause.put(adname.trim(), advalue.trim());
86 del = qt.getSeparator();
87 hadAttribute = true;
88 }
89 }
90
91 // Check for duplicate names. The aliases list contains
92 // the list of nams, for each check if it exists. If so,
93 // add a number of "~" to make it unique.
94 for (String clauseName : aliases) {
95 if (result.containsKey(clauseName)) {
96 if (logger != null && logger.isPedantic())
97 logger
98 .warning("Duplicate name "
99 + clauseName
100 + " used in header: '"
101 + clauseName
102 + "'. Duplicate names are specially marked in Bnd with a ~ at the end (which is stripped at printing time).");
103 while (result.containsKey(clauseName))
104 clauseName += "~";
105 }
106 result.put(clauseName, clause);
107 }
108 }
109 } while (del == ',');
110 return result;
111 }
112
113 public static Map<String, String> parseProperties(String input) {
114 return parseProperties(input, null);
115 }
116
117 public static Map<String, String> parseProperties(String input, Reporter logger) {
118 if (input == null || input.trim().length() == 0)
119 return Create.map();
120
121 Map<String, String> result = Create.map();
122 QuotedTokenizer qt = new QuotedTokenizer(input, "=,");
123 char del = ',';
124
125 while (del == ',') {
126 String key = qt.nextToken(",=");
127 String value = "";
128 del = qt.getSeparator();
129 if (del == '=') {
130 value = qt.nextToken(",=");
131 del = qt.getSeparator();
132 }
133 result.put(key, value);
134 }
135 if (del != 0)
136 if ( logger == null )
137 throw new IllegalArgumentException(
138 "Invalid syntax for properties: " + input);
139 else
140 logger.error("Invalid syntax for properties: " + input);
141
142 return result;
143 }
144
145}