/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.felix.utils.manifest; import java.util.ArrayList; import java.util.List; public final class Parser { private Parser() { } public static Clause[] parseHeader(String header) throws IllegalArgumentException { Clause[] clauses = null; if (header != null) { if (header.length() == 0) { throw new IllegalArgumentException("The header cannot be an empty string."); } String[] ss = parseDelimitedString(header, ","); clauses = parseClauses(ss); } return (clauses == null) ? new Clause[0] : clauses; } public static Clause[] parseClauses(String[] ss) throws IllegalArgumentException { if (ss == null) { return null; } List completeList = new ArrayList(); for (int ssIdx = 0; ssIdx < ss.length; ssIdx++) { // Break string into semi-colon delimited pieces. String[] pieces = parseDelimitedString(ss[ssIdx], ";"); // Count the number of different clauses; clauses // will not have an '=' in their string. This assumes // that clauses come first, before directives and // attributes. int pathCount = 0; for (int pieceIdx = 0; pieceIdx < pieces.length; pieceIdx++) { if (pieces[pieceIdx].indexOf('=') >= 0) { break; } pathCount++; } // Error if no packages were specified. if (pathCount == 0) { throw new IllegalArgumentException("No path specified on clause: " + ss[ssIdx]); } // Parse the directives/attributes. Directive[] dirs = new Directive[pieces.length - pathCount]; Attribute[] attrs = new Attribute[pieces.length - pathCount]; int dirCount = 0, attrCount = 0; int idx = -1; String sep = null; for (int pieceIdx = pathCount; pieceIdx < pieces.length; pieceIdx++) { if ((idx = pieces[pieceIdx].indexOf("=")) <= 0) { // It is an error. throw new IllegalArgumentException("Not a directive/attribute: " + ss[ssIdx]); } // This a directive. if (pieces[pieceIdx].charAt(idx - 1) == ':') { idx--; sep = ":="; } // This an attribute. else { sep = "="; } String key = pieces[pieceIdx].substring(0, idx).trim(); String value = pieces[pieceIdx].substring(idx + sep.length()).trim(); // Remove quotes, if value is quoted. if (value.startsWith("\"") && value.endsWith("\"")) { value = value.substring(1, value.length() - 1); } // Save the directive/attribute in the appropriate array. if (sep.equals(":=")) { dirs[dirCount++] = new Directive(key, value); } else { attrs[attrCount++] = new Attribute(key, value); } } // Shrink directive array. Directive[] dirsFinal = new Directive[dirCount]; System.arraycopy(dirs, 0, dirsFinal, 0, dirCount); // Shrink attribute array. Attribute[] attrsFinal = new Attribute[attrCount]; System.arraycopy(attrs, 0, attrsFinal, 0, attrCount); // Create package attributes for each package and // set directives/attributes. Add each package to // completel list of packages. Clause[] pkgs = new Clause[pathCount]; for (int pkgIdx = 0; pkgIdx < pathCount; pkgIdx++) { pkgs[pkgIdx] = new Clause(pieces[pkgIdx], dirsFinal, attrsFinal); completeList.add(pkgs[pkgIdx]); } } Clause[] pkgs = (Clause[]) completeList.toArray(new Clause[completeList.size()]); return pkgs; } /** * Parses delimited string and returns an array containing the tokens. This * parser obeys quotes, so the delimiter character will be ignored if it is * inside of a quote. This method assumes that the quote character is not * included in the set of delimiter characters. * @param value the delimited string to parse. * @param delim the characters delimiting the tokens. * @return an array of string tokens or null if there were no tokens. **/ public static String[] parseDelimitedString(String value, String delim) { if (value == null) { value = ""; } List list = new ArrayList(); int CHAR = 1; int DELIMITER = 2; int STARTQUOTE = 4; int ENDQUOTE = 8; StringBuffer sb = new StringBuffer(); int expecting = (CHAR | DELIMITER | STARTQUOTE); for (int i = 0; i < value.length(); i++) { char c = value.charAt(i); boolean isDelimiter = (delim.indexOf(c) >= 0); boolean isQuote = (c == '"'); if (isDelimiter && ((expecting & DELIMITER) > 0)) { list.add(sb.toString().trim()); sb.delete(0, sb.length()); expecting = (CHAR | DELIMITER | STARTQUOTE); } else if (isQuote && ((expecting & STARTQUOTE) > 0)) { sb.append(c); expecting = CHAR | ENDQUOTE; } else if (isQuote && ((expecting & ENDQUOTE) > 0)) { sb.append(c); expecting = (CHAR | STARTQUOTE | DELIMITER); } else if ((expecting & CHAR) > 0) { sb.append(c); } else { throw new IllegalArgumentException("Invalid delimited string: " + value); } } String s = sb.toString().trim(); if (s.length() > 0) { list.add(s); } return (String[]) list.toArray(new String[list.size()]); } }