package be.dnsbelgium.rdap.sample.parser;
import be.dnsbelgium.rdap.sample.parser.fieldparser.FieldParser;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public abstract class AbstractWhoisParser<T> {
private static final Logger logger = LoggerFactory.getLogger(AbstractWhoisParser.class);
private static final String NEWLINE_PATTERN = "\\r?\\n";
private static final String KEY_SEPARATOR = ":";
private WhoisKeyBlock previousKeyBlock;
private Object currentBlockItem;
protected abstract T createNewInstance();
protected abstract ParseLayout getParseLayout();
public T parse(String data) {
T instance = createNewInstance();
previousKeyBlock = null;
currentBlockItem = null;
doBeforeParsing(instance, data);
List<String> lines = removeCommentLines(data.split(NEWLINE_PATTERN));
for (String line : lines) {
try {
Pair<String, String> keyValue = splitLine(line);
if (keyValue != null) {
WhoisEntry entry = getParseLayout().getEntry(keyValue.getKey(), previousKeyBlock);
if (entry == null) {
logger.info("Skipping line! Cannot find a parser to parse line: [{}]", line);
} else {
Pair<Object, String> ofPair = getObjectFieldPair(instance, entry.getPath(), entry);
Object value = getFieldValue(keyValue.getValue(), entry.getFieldParser());
// If the item is repeatable -> check if we can access the public field
// -> if we can't use the addXxx() method
if (entry.isItemRepeatable()) {
addItemToCollection(ofPair.getKey(), ofPair.getValue(), value);
} else {
FieldUtils.writeField(ofPair.getKey(), ofPair.getValue(), value);
}
previousKeyBlock = entry.getBlock();
}
}
} catch (Exception e) {
logger.error("Skipping line! There was a problem while trying to parse the line: [{}]", line, e);
}
}
doAfterParsing(instance, data);
return instance;
}
private Object getFieldValue(String value, FieldParser fieldParser) {
if (StringUtils.isBlank(value)) {
value = null;
}
return fieldParser != null ? fieldParser.parse(value) : value;
}
public List<String> removeCommentLines(String[] input) {
List<String> result = new ArrayList<>();
for (String line : input) {
if (!line.startsWith("%") && !line.startsWith(">")) {
result.add(line);
}
}
return result;
}
private void addItemToCollection(Object object, String fieldName, Object value)
throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Object collection = null;
try {
collection = FieldUtils.readField(object, fieldName);
if (!(collection instanceof Collection)) {
collection = null;
}
} catch (Exception e) {
// Do nothing -> just using this to check if we have to use the field or the addXxxx() method
}
if (collection != null) {
MethodUtils.invokeExactMethod(collection, "add", new Object[]{value}, new Class[]{Object.class});
} else {
MethodUtils.invokeExactMethod(object, "add" + StringUtils.capitalize(fieldName), value);
}
}
protected void doBeforeParsing(T instance, String data) {
// Nothing to do in abstract class
}
protected void doAfterParsing(T instance, String data) {
// Nothing to do in abstract class
}
private Pair<Object, String> getObjectFieldPair(Object object, String path, WhoisEntry entry)
throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
int lastDotIdx = path.indexOf('.');
if (lastDotIdx > 0) {
String parentPath = path.substring(0, lastDotIdx);
String fieldPath = path.substring(lastDotIdx + 1);
if (parentPath.endsWith("[]")) {
if (entry.isFirstBlockItem()) {
Object newInstance = entry.getBlock().getRepeatClass().newInstance();
currentBlockItem = newInstance;
parentPath = parentPath.substring(0, parentPath.length() - 2);
addItemToCollection(object, parentPath, newInstance);
}
return getObjectFieldPair(currentBlockItem, fieldPath, entry);
} else {
return getObjectFieldPair(FieldUtils.readField(object, parentPath, true), fieldPath, entry);
}
} else {
return new ImmutablePair<>(object, path);
}
}
/**
* Split the line into a key-value pair. The key being the whois label and the value being the label value.
* The key and value will also be trimmed.
*
* @param line The whois line
* @return The key-value pair
*/
private Pair<String, String> splitLine(String line) {
String[] result = StringUtils.splitByWholeSeparator(line, KEY_SEPARATOR, 2);
if (result.length != 2) {
return null;
}
return new ImmutablePair<>(StringUtils.trim(result[0]), StringUtils.trim(result[1]));
}
}