package com.fasterxml.jackson.dataformat.javaprop.util;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.fasterxml.jackson.dataformat.javaprop.JavaPropsSchema;
public abstract class JPropPathSplitter
{
protected final boolean _useSimpleIndex;
protected JPropPathSplitter(boolean useSimpleIndex) {
_useSimpleIndex = useSimpleIndex;
}
public static JPropPathSplitter create(JavaPropsSchema schema)
{
String sep = schema.pathSeparator();
final Markers indexMarker = schema.indexMarker();
if (indexMarker == null) {
return pathOnlySplitter(schema);
}
if (sep.isEmpty()) {
return new IndexOnlySplitter(schema.parseSimpleIndexes(), indexMarker);
}
return new FullSplitter(sep, schema.parseSimpleIndexes(),
indexMarker,
pathOnlySplitter(schema),
schema.prefix());
}
private static JPropPathSplitter pathOnlySplitter(JavaPropsSchema schema)
{
String sep = schema.pathSeparator();
if (sep.isEmpty()) {
return NonSplitting.instance;
}
if (sep.length() == 1) {
return new CharPathOnlySplitter(sep.charAt(0), schema.parseSimpleIndexes());
}
return new StringPathOnlySplitter(sep, schema.parseSimpleIndexes());
}
public abstract JPropNode splitAndAdd(JPropNode parent,
String key, String value);
protected JPropNode _addSegment(JPropNode parent, String segment)
{
if (_useSimpleIndex) {
int ix = _asInt(segment);
if (ix >= 0) {
return parent.addByIndex(ix);
}
}
return parent.addByName(segment);
}
protected JPropNode _lastSegment(JPropNode parent, String path, int start, int end)
{
if (start < end) {
if (start > 0) {
path = path.substring(start);
}
parent = _addSegment(parent, path);
}
return parent;
}
protected int _asInt(String segment) {
final int len = segment.length();
if ((len == 0) || (len > 9)) {
return -1;
}
char c = segment.charAt(0);
if ((c > '9') || (c < '0')) {
return -1;
}
for (int i = 0; i < len; ++i) {
c = segment.charAt(i);
if ((c > '9') || (c < '0')) {
return -1;
}
}
return Integer.parseInt(segment);
}
public static class NonSplitting extends JPropPathSplitter
{
public final static NonSplitting instance = new NonSplitting();
private NonSplitting() { super(false); }
@Override
public JPropNode splitAndAdd(JPropNode parent,
String key, String value)
{
return parent.addByName(key).setValue(value);
}
}
public static class CharPathOnlySplitter extends JPropPathSplitter
{
protected final char _pathSeparatorChar;
public CharPathOnlySplitter(char sepChar, boolean useIndex)
{
super(useIndex);
_pathSeparatorChar = sepChar;
}
@Override
public JPropNode splitAndAdd(JPropNode parent,
String key, String value)
{
JPropNode curr = parent;
int start = 0;
final int keyLen = key.length();
int ix;
while ((ix = key.indexOf(_pathSeparatorChar, start)) >= start) {
if (ix > start) {
String segment = key.substring(start, ix);
curr = _addSegment(curr, segment);
}
start = ix + 1;
if (start == key.length()) {
break;
}
}
return _lastSegment(curr, key, start, keyLen).setValue(value);
}
}
public static class StringPathOnlySplitter extends JPropPathSplitter
{
protected final String _pathSeparator;
protected final int _pathSeparatorLength;
public StringPathOnlySplitter(String pathSeparator, boolean useIndex)
{
super(useIndex);
_pathSeparator = pathSeparator;
_pathSeparatorLength = pathSeparator.length();
}
@Override
public JPropNode splitAndAdd(JPropNode parent,
String key, String value)
{
JPropNode curr = parent;
int start = 0;
final int keyLen = key.length();
int ix;
while ((ix = key.indexOf(_pathSeparator, start)) >= start) {
if (ix > start) {
String segment = key.substring(start, ix);
curr = _addSegment(curr, segment);
}
start = ix + _pathSeparatorLength;
if (start == key.length()) {
break;
}
}
return _lastSegment(curr, key, start, keyLen).setValue(value);
}
}
public static class IndexOnlySplitter extends JPropPathSplitter
{
protected final Pattern _indexMatch;
public IndexOnlySplitter(boolean useSimpleIndex,
Markers indexMarker)
{
super(useSimpleIndex);
_indexMatch = Pattern.compile(String.format("(.*)%s(\\d{1,9})%s$",
Pattern.quote(indexMarker.getStart()),
Pattern.quote(indexMarker.getEnd())));
}
@Override
public JPropNode splitAndAdd(JPropNode parent,
String key, String value)
{
Matcher m = _indexMatch.matcher(key);
if (!m.matches()) {
return _addSegment(parent, key).setValue(value);
}
return _splitMore(parent, m.group(1), m.group(2))
.setValue(value);
}
protected JPropNode _splitMore(JPropNode parent, String prefix, String indexStr)
{
int ix = Integer.parseInt(indexStr);
Matcher m = _indexMatch.matcher(prefix);
if (!m.matches()) {
parent = _addSegment(parent, prefix);
} else {
parent = _splitMore(parent, m.group(1), m.group(2));
}
return parent.addByIndex(ix);
}
}
public static class FullSplitter extends JPropPathSplitter
{
protected final Pattern _indexMatch;
protected final int _indexFirstChar;
protected final JPropPathSplitter _simpleSplitter;
protected final String _prefix;
public FullSplitter(String pathSeparator, boolean useSimpleIndex,
Markers indexMarker, JPropPathSplitter fallbackSplitter,
String prefix)
{
super(useSimpleIndex);
String startMarker = indexMarker.getStart();
_indexFirstChar = startMarker.charAt(0);
_simpleSplitter = fallbackSplitter;
if (prefix == null || prefix.isEmpty()) {
_prefix = null;
} else {
_prefix = prefix + pathSeparator;
}
_indexMatch = Pattern.compile(String.format
("(%s)|(%s(\\d{1,9})%s)",
Pattern.quote(pathSeparator),
Pattern.quote(startMarker),
Pattern.quote(indexMarker.getEnd())));
}
@Override
public JPropNode splitAndAdd(JPropNode parent,
String key, String value)
{
if (_prefix != null) {
if (!key.startsWith(_prefix)) {
return null;
}
key = key.substring(_prefix.length());
}
if (key.indexOf(_indexFirstChar) < 0) {
return _simpleSplitter.splitAndAdd(parent, key, value);
}
Matcher m = _indexMatch.matcher(key);
int start = 0;
while (m.find()) {
int ix = m.start(1);
if (ix >= 0) {
if (ix > start) {
String segment = key.substring(start, ix);
parent = _addSegment(parent, segment);
}
start = m.end(1);
continue;
}
ix = m.start(2);
if (ix > start) {
String segment = key.substring(start, ix);
parent = _addSegment(parent, segment);
}
start = m.end(2);
ix = Integer.parseInt(m.group(3));
parent = parent.addByIndex(ix);
}
return _lastSegment(parent, key, start, key.length()).setValue(value);
}
}
}