| package aQute.bnd.header; |
| |
| import java.util.*; |
| |
| import aQute.libg.generics.*; |
| import aQute.libg.qtokens.*; |
| import aQute.service.reporter.*; |
| |
| public class OSGiHeader { |
| |
| static public Parameters parseHeader(String value) { |
| return parseHeader(value, null); |
| } |
| |
| /** |
| * Standard OSGi header parser. This parser can handle the format clauses |
| * ::= clause ( ',' clause ) + clause ::= name ( ';' name ) (';' key '=' |
| * value ) This is mapped to a Map { name => Map { attr|directive => value } |
| * } |
| * |
| * @param value |
| * A string |
| * @return a Map<String,Map<String,String>> |
| */ |
| static public Parameters parseHeader(String value, Reporter logger) { |
| return parseHeader(value, logger, new Parameters()); |
| } |
| |
| static public Parameters parseHeader(String value, Reporter logger, Parameters result) { |
| if (value == null || value.trim().length() == 0) |
| return result; |
| |
| QuotedTokenizer qt = new QuotedTokenizer(value, ";=,"); |
| char del = 0; |
| do { |
| boolean hadAttribute = false; |
| Attrs clause = new Attrs(); |
| List<String> aliases = Create.list(); |
| String name = qt.nextToken(",;"); |
| |
| del = qt.getSeparator(); |
| if (name == null || name.length() == 0) { |
| if (logger != null && logger.isPedantic()) { |
| 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: " |
| + value); |
| } |
| if (name == null) |
| break; |
| } else { |
| name = name.trim(); |
| |
| aliases.add(name); |
| while (del == ';') { |
| String adname = qt.nextToken(); |
| if ((del = qt.getSeparator()) != '=') { |
| if (hadAttribute) |
| if (logger != null) { |
| logger.error("Header contains name field after attribute or directive: " + adname |
| + " from " + value |
| + ". Name fields must be consecutive, separated by a ';' like a;b;c;x=3;y=4"); |
| } |
| if (adname != null && adname.length() > 0) |
| aliases.add(adname.trim()); |
| } else { |
| String advalue = qt.nextToken(); |
| if (clause.containsKey(adname)) { |
| if (logger != null && logger.isPedantic()) |
| logger.warning("Duplicate attribute/directive name " + adname + " in " + value |
| + ". This attribute/directive will be ignored"); |
| } |
| if (advalue == null) { |
| if (logger != null) |
| logger.error("No value after '=' sign for attribute " + adname); |
| advalue = ""; |
| } |
| clause.put(adname.trim(), advalue.trim()); |
| del = qt.getSeparator(); |
| hadAttribute = true; |
| } |
| } |
| |
| // Check for duplicate names. The aliases list contains |
| // the list of nams, for each check if it exists. If so, |
| // add a number of "~" to make it unique. |
| for (String clauseName : aliases) { |
| if (result.containsKey(clauseName)) { |
| if (logger != null && logger.isPedantic()) |
| logger.warning("Duplicate name " |
| + clauseName |
| + " used in header: '" |
| + clauseName |
| + "'. Duplicate names are specially marked in Bnd with a ~ at the end (which is stripped at printing time)."); |
| while (result.containsKey(clauseName)) |
| clauseName += "~"; |
| } |
| result.put(clauseName, clause); |
| } |
| } |
| } while (del == ','); |
| return result; |
| } |
| |
| public static Attrs parseProperties(String input) { |
| return parseProperties(input, null); |
| } |
| |
| public static Attrs parseProperties(String input, Reporter logger) { |
| if (input == null || input.trim().length() == 0) |
| return new Attrs(); |
| |
| Attrs result = new Attrs(); |
| QuotedTokenizer qt = new QuotedTokenizer(input, "=,"); |
| char del = ','; |
| |
| while (del == ',') { |
| String key = qt.nextToken(",="); |
| String value = ""; |
| del = qt.getSeparator(); |
| if (del == '=') { |
| value = qt.nextToken(",="); |
| del = qt.getSeparator(); |
| } |
| result.put(key, value); |
| } |
| if (del != 0) { |
| if (logger == null) |
| throw new IllegalArgumentException("Invalid syntax for properties: " + input); |
| logger.error("Invalid syntax for properties: " + input); |
| } |
| |
| return result; |
| } |
| |
| } |