/* * 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.resolver.test.util; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; public class ClauseParser { private static final char EOF = (char) -1; private static char charAt(int pos, String headers, int length) { if (pos >= length) { return EOF; } return headers.charAt(pos); } private static final int CLAUSE_START = 0; private static final int PARAMETER_START = 1; private static final int KEY = 2; private static final int DIRECTIVE_OR_TYPEDATTRIBUTE = 4; private static final int ARGUMENT = 8; private static final int VALUE = 16; @SuppressWarnings({ "unchecked", "rawtypes" }) public static List<ParsedHeaderClause> parseStandardHeader(String header) { List<ParsedHeaderClause> clauses = new ArrayList<ParsedHeaderClause>(); if (header == null) { return clauses; } ParsedHeaderClause clause = null; String key = null; Map targetMap = null; int state = CLAUSE_START; int currentPosition = 0; int startPosition = 0; int length = header.length(); boolean quoted = false; boolean escaped = false; char currentChar = EOF; do { currentChar = charAt(currentPosition, header, length); switch (state) { case CLAUSE_START: clause = new ParsedHeaderClause(); clauses.add(clause); state = PARAMETER_START; case PARAMETER_START: startPosition = currentPosition; state = KEY; case KEY: switch (currentChar) { case ':': case '=': key = header.substring(startPosition, currentPosition).trim(); startPosition = currentPosition + 1; targetMap = clause.attrs; state = currentChar == ':' ? DIRECTIVE_OR_TYPEDATTRIBUTE : ARGUMENT; break; case EOF: case ',': case ';': clause.paths.add(header.substring(startPosition, currentPosition).trim()); state = currentChar == ',' ? CLAUSE_START : PARAMETER_START; break; default: break; } currentPosition++; break; case DIRECTIVE_OR_TYPEDATTRIBUTE: switch(currentChar) { case '=': if (startPosition != currentPosition) { clause.types.put(key, header.substring(startPosition, currentPosition).trim()); } else { targetMap = clause.dirs; } state = ARGUMENT; startPosition = currentPosition + 1; break; default: break; } currentPosition++; break; case ARGUMENT: if (currentChar == '\"') { quoted = true; currentPosition++; } else { quoted = false; } if (!Character.isWhitespace(currentChar)) { state = VALUE; } else { currentPosition++; } break; case VALUE: if (escaped) { escaped = false; } else { if (currentChar == '\\' ) { escaped = true; } else if (quoted && currentChar == '\"') { quoted = false; } else if (!quoted) { String value = null; switch(currentChar) { case EOF: case ';': case ',': value = header.substring(startPosition, currentPosition).trim(); if (value.startsWith("\"") && value.endsWith("\"")) { value = value.substring(1, value.length() - 1); } if (targetMap.put(key, value) != null) { throw new IllegalArgumentException( "Duplicate '" + key + "' in: " + header); } state = currentChar == ';' ? PARAMETER_START : CLAUSE_START; break; default: break; } } } currentPosition++; break; default: break; } } while ( currentChar != EOF); if (state > PARAMETER_START) { throw new IllegalArgumentException("Unable to parse header: " + header); } return clauses; } public static List<String> parseDelimitedString(String value, String delim) { return parseDelimitedString(value, delim, true); } /** * 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 a list of string or an empty list if there are none. **/ public static List<String> parseDelimitedString(String value, String delim, boolean trim) { if (value == null) { value = ""; } List<String> list = new ArrayList(); int CHAR = 1; int DELIMITER = 2; int STARTQUOTE = 4; int ENDQUOTE = 8; StringBuffer sb = new StringBuffer(); int expecting = (CHAR | DELIMITER | STARTQUOTE); boolean isEscaped = false; for (int i = 0; i < value.length(); i++) { char c = value.charAt(i); boolean isDelimiter = (delim.indexOf(c) >= 0); if (!isEscaped && (c == '\\')) { isEscaped = true; continue; } if (isEscaped) { sb.append(c); } else if (isDelimiter && ((expecting & DELIMITER) > 0)) { if (trim) { list.add(sb.toString().trim()); } else { list.add(sb.toString()); } sb.delete(0, sb.length()); expecting = (CHAR | DELIMITER | STARTQUOTE); } else if ((c == '"') && ((expecting & STARTQUOTE) > 0)) { sb.append(c); expecting = CHAR | ENDQUOTE; } else if ((c == '"') && ((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); } isEscaped = false; } if (sb.length() > 0) { if (trim) { list.add(sb.toString().trim()); } else { list.add(sb.toString()); } } return list; } public static class ParsedHeaderClause { public final List<String> paths = new ArrayList<String>(); public final Map<String, String> dirs = new LinkedHashMap<String, String>(); public final Map<String, Object> attrs = new LinkedHashMap<String, Object>(); public final Map<String, String> types = new LinkedHashMap<String, String>(); } }