/*
* Copyright 2010 NCHOVY
*
* Licensed 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.krakenapps.util;
import java.util.AbstractMap.SimpleEntry;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class QuotedKeyValueParser {
public static Map<String, String> parse(String line) {
List<SimpleEntry<String, String>> parseArgs = parseArgs(line);
HashMap<String, String> result = new HashMap<String, String>();
for (SimpleEntry<String, String> entry : parseArgs) {
result.put(entry.getKey(), entry.getValue());
}
return result;
}
private static List<SimpleEntry<String, String>> parseArgs(String argument) {
StringBuilder currentValue = null;
String currentKey = null;
LinkedList<SimpleEntry<String, String>> result = new LinkedList<SimpleEntry<String, String>>();
// ex: k1=123 k2="asdf asdf asdf" k3= "asdf" k4="" k5="asdf" k6='asdf'
// k7='asdf asdf' k8="hello \" stania \"" k9=asdf"asdf asdf asdf"
// k10=asdf" asdf"
int cur = 0;
int end = argument.length();
while (cur < end) {
if (currentKey == null) {
// find key
int tokenEnd = argument.indexOf('=', cur);
if (tokenEnd == -1)
break;
currentKey = argument.substring(cur, tokenEnd);
int lastIndexOfSpace = currentKey.lastIndexOf(' ');
if (lastIndexOfSpace != -1) {
currentKey = currentKey.substring(lastIndexOfSpace + 1);
}
cur = tokenEnd + 1;
} else {
// find value
int tokenEnd = argument.indexOf(' ', cur);
if (tokenEnd == -1) { // end of the string
tokenEnd = end;
}
int quote = findNextQuote(argument, cur);
// quote found before space
if (quote != -1 && quote < tokenEnd) {
currentValue = new StringBuilder();
char currentQuote = argument.charAt(quote);
if (quote != cur) {
currentValue.append(argument.substring(cur, quote));
}
int closingQuote = findNextQuote(argument, quote + 1, currentQuote);
if (closingQuote == -1)
closingQuote = end;
currentValue.append(argument.substring(quote + 1, closingQuote).replace("\\", ""));
// proceed to find next space
cur = closingQuote + 1;
} else {
if (currentValue == null) {
currentValue = new StringBuilder();
}
currentValue.append(argument.substring(cur, tokenEnd));
result.add(new SimpleEntry<String, String>(currentKey, currentValue.toString()));
currentKey = null;
currentValue = null;
cur = tokenEnd + 1;
}
}
}
if (currentKey != null && currentValue != null) {
result.add(new SimpleEntry<String, String>(currentKey, currentValue.toString()));
}
return result;
}
private static int findNextQuote(String argument, int cur, char quote) {
int quotePos = cur;
do {
quotePos = argument.indexOf(quote, quotePos);
if (quotePos == -1 || quotePos == 0)
break;
if (argument.charAt(quotePos - 1) == '\\') {
// ignore escaped quote
quotePos = quotePos + 1;
continue;
}
break;
} while (true);
return quotePos;
}
private static int findNextQuote(String argument, int cur) {
int quotePos = cur;
do {
int quote1Pos = argument.indexOf('\"', quotePos);
int quote2Pos = argument.indexOf('\'', quotePos);
if (quote1Pos == -1 && quote2Pos == -1)
quotePos = -1;
else {
quotePos = Math.min(quote1Pos == -1 ? Integer.MAX_VALUE : quote1Pos, quote2Pos == -1 ? Integer.MAX_VALUE
: quote2Pos);
}
if (quotePos == -1 || quotePos == 0)
break;
if (argument.charAt(quotePos - 1) == '\\') {
// ignore escaped quote
quotePos = quotePos + 1;
continue;
}
break;
} while (true);
return quotePos;
}
}