package org.simpleflatmapper.reflect.meta;
import java.util.ArrayList;
import java.util.List;
import static org.simpleflatmapper.util.Asserts.requireNonNull;
public final class DefaultPropertyNameMatcher implements PropertyNameMatcher {
private final String column;
private final int from;
private final boolean exactMatch;
private final boolean caseSensitive;
public DefaultPropertyNameMatcher(String column, int from, boolean exactMatch, boolean caseSensitive) {
this.column = requireNonNull("property", column);
this.from = from;
this.exactMatch = exactMatch;
this.caseSensitive = caseSensitive;
}
@Override
public boolean matches(final CharSequence property) {
return property != null && _partialMatch(property) == column.length();
}
@Override
public IndexedColumn matchIndex() {
int index = -1;
int listIndexStart = from;
// skip separtor char
while(listIndexStart < column.length() && isSeparatorChar(column.charAt(listIndexStart))) {
listIndexStart++;
}
boolean encounterSeparator = false;
while(listIndexStart < column.length()) {
char ch = column.charAt(listIndexStart);
if (Character.isDigit(ch)) {
break;
} else if (isSeparatorChar(ch)) {
encounterSeparator = true;
} else if (encounterSeparator) {
return null;
}
listIndexStart++;
}
int listIndexEnd = listIndexStart;
while(listIndexEnd < column.length() && Character.isDigit(column.charAt(listIndexEnd))) {
listIndexEnd++;
}
if (listIndexStart != listIndexEnd) {
index = Integer.parseInt(column.substring(listIndexStart, listIndexEnd));
}
if (index == -1) {
return null;
}
PropertyNameMatcher subPropertyNameMatcher = null;
if (listIndexEnd < column.length()) {
subPropertyNameMatcher = new DefaultPropertyNameMatcher(column, listIndexEnd, exactMatch, caseSensitive );
}
return new IndexedColumn(index, subPropertyNameMatcher);
}
private int _partialMatch(final CharSequence property) {
if (property == null) return -1;
int indexColumn = from;
int indexProperty = 0;
boolean nextToUpperCase = false;
do {
if (indexProperty < property.length()) {
char charProperty = property.charAt(indexProperty);
if (indexColumn < column.length()) {
char charColumn = column.charAt(indexColumn);
if (nextToUpperCase) {
charColumn = Character.toUpperCase(charColumn);
nextToUpperCase = false;
}
indexColumn ++;
if (ignoreCharacter(charColumn)) {
if (ignoreCharacter(charProperty)) {
indexProperty++;
}
if (caseSensitive) {
nextToUpperCase = true;
}
} else if (areDifferentCharacters(charProperty, charColumn)) {
return -1;
} else {
indexProperty++;
}
} else {
return -1;
}
} else {
// partial match
return indexColumn;
}
}
while(true);
}
private boolean areDifferentCharacters(char c1, char c2) {
if (caseSensitive) {
return c1 != c2;
} else {
return Character.toLowerCase(c1) != Character.toLowerCase(c2);
}
}
private boolean ignoreCharacter(final char charColumn) {
return !exactMatch && isSeparatorChar(charColumn);
}
private boolean isSeparatorChar(char charColumn) {
return charColumn == '_' || charColumn == ' ' || charColumn == '.';
}
@Override
public PropertyNameMatcher partialMatch(final CharSequence property) {
int index = _partialMatch(property);
if (index != -1) {
return new DefaultPropertyNameMatcher(column, index, exactMatch, caseSensitive);
} else {
return null;
}
}
@Override
public PropertyNameMatch speculativeMatch() {
int index = _speculativeMatch();
if (index != -1) {
return new PropertyNameMatch(column.substring(from, index), new DefaultPropertyNameMatcher(column, index, exactMatch, caseSensitive));
} else {
return null;
}
}
@Override
public List<PropertyNameMatcherKeyValuePair> keyValuePairs() {
List<PropertyNameMatcherKeyValuePair> keyValuePairs = new ArrayList<PropertyNameMatcherKeyValuePair>();
keyValuePairs.add(
new PropertyNameMatcherKeyValuePair(
new DefaultPropertyNameMatcher(column, from, exactMatch, caseSensitive),
new DefaultPropertyNameMatcher("", 0, exactMatch, caseSensitive)
));
for(int i = column.length() - 1; i >= from; i--) {
char c = column.charAt(i);
if (isSeparatorChar(c)) {
PropertyNameMatcher key = new DefaultPropertyNameMatcher(column.substring(from, i), 0, exactMatch, caseSensitive);
PropertyNameMatcher value = new DefaultPropertyNameMatcher(column, from + i + 1, exactMatch, caseSensitive);
keyValuePairs.add(new PropertyNameMatcherKeyValuePair(key, value));
}
}
return keyValuePairs;
}
private int _speculativeMatch() {
for(int i = from; i < column.length(); i++) {
char c = column.charAt(i);
if (c == '_' || c == '.') {
return i;
}
}
return -1;
}
@Override
public String toString() {
return column.substring(from, column.length());
}
public static PropertyNameMatcher of(String value) {
return new DefaultPropertyNameMatcher(value, 0, false, false);
}
public static PropertyNameMatcher exact(String value) {
return new DefaultPropertyNameMatcher(value, 0, true, true);
}
}