blob: c5845a4fea651d458a57152b5e15a469123eb16e [file] [log] [blame]
/*
* 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()]);
}
}