com.yahoo.jdisc.application.GlobPattern Maven / Gradle / Ivy
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.jdisc.application;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
/**
* @author Simon Thoresen Hult
*/
class GlobPattern implements Comparable {
private static final GlobPattern WILDCARD = new WildcardPattern();
protected final String[] parts;
private GlobPattern(String... parts) {
this.parts = parts;
}
public final Match match(String text) {
return match(text, 0);
}
public Match match(String text, int offset) {
int[] pos = new int[parts.length - 1 << 1];
if (!matches(text, offset, 0, pos)) {
return null;
}
return new Match(text, pos);
}
private boolean matches(String text, int textIdx, int partIdx, int[] out) {
String part = parts[partIdx];
if (partIdx == parts.length - 1 && part.isEmpty()) {
out[partIdx - 1 << 1 | 1] = text.length();
return true; // optimize trailing wildcard
}
int partEnd = textIdx + part.length();
if (partEnd > text.length()|| !text.startsWith(part, textIdx)) {
return false;
}
if (partIdx == parts.length - 1) {
return partEnd == text.length();
}
out[partIdx << 1] = partEnd;
for (int i = partEnd; i <= text.length(); ++i) {
out[partIdx << 1 | 1] = i;
if (matches(text, i, partIdx + 1, out)) {
return true;
}
}
return false;
}
@Override
public int compareTo(GlobPattern rhs) {
// wildcard pattern always orders last
if (parts.length == 0 || rhs.parts.length == 0) {
return rhs.parts.length - parts.length;
}
// next is trailing wildcard
int cmp = compare(parts[parts.length - 1], rhs.parts[rhs.parts.length - 1], false);
if (cmp != 0) {
return cmp;
}
// then comes part comparison
for (int i = 0; i < parts.length && i < rhs.parts.length; ++i) {
cmp = compare(parts[i], rhs.parts[i], true);
if (cmp != 0) {
return cmp;
}
}
// one starts with the other, sort longest first
return rhs.parts.length - parts.length;
}
private static int compare(String lhs, String rhs, boolean compareNonEmpty) {
if ((lhs.isEmpty() || rhs.isEmpty()) && !lhs.equals(rhs)) {
return rhs.length() - lhs.length();
}
if (!compareNonEmpty) {
return 0;
}
return rhs.compareTo(lhs);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof GlobPattern)) {
return false;
}
GlobPattern rhs = (GlobPattern)obj;
if (!Arrays.equals(parts, rhs.parts)) {
return false;
}
return true;
}
@Override
public int hashCode() {
return Arrays.hashCode(parts);
}
@Override
public String toString() {
StringBuilder ret = new StringBuilder();
for (int i = 0; i < parts.length; ++i) {
ret.append(parts[i]);
if (i < parts.length - 1) {
ret.append("*");
}
}
return ret.toString();
}
public static Match match(String glob, String text) {
return compile(glob).match(text);
}
public static GlobPattern compile(String pattern) {
if (pattern.equals("*")) {
return WILDCARD;
}
if (pattern.indexOf('*') < 0) {
return new VerbatimPattern(pattern);
}
List arr = new LinkedList<>();
for (int prev = 0, next = 0; next <= pattern.length(); ++next) {
if (next == pattern.length() || pattern.charAt(next) == '*') {
arr.add(pattern.substring(prev, next));
prev = next + 1;
}
}
return new GlobPattern(arr.toArray(new String[arr.size()]));
}
public static class Match {
private final String str;
private final int[] pos;
private Match(String str, int[] pos) {
this.str = str;
this.pos = pos;
}
public int groupCount() {
return pos.length >> 1;
}
public String group(int idx) {
return str.substring(pos[idx << 1], pos[idx << 1 | 1]);
}
}
private static class VerbatimPattern extends GlobPattern {
VerbatimPattern(String value) {
super(value);
}
@Override
public Match match(String text, int offset) {
int len = text.length() - offset;
if (len != parts[0].length()) {
return null;
}
if (!parts[0].regionMatches(0, text, offset, len)) {
return null;
}
return new Match(parts[0], new int[0]);
}
}
private static class WildcardPattern extends GlobPattern {
@Override
public Match match(String text, int offset) {
int len = text.length();
if (len <= offset) {
return new Match(text, new int[] { 0, 0 });
}
return new Match(text, new int[] { offset, len });
}
@Override
public String toString() {
return "*";
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy