/* * Copyright 2015 MovingBlocks * * 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.terasology.module.filesystem; import com.google.common.base.Joiner; import com.google.common.collect.Lists; import java.util.List; /** * Provides support for glob expressions when working with ModuleFileSystems. * @author Immortius */ public final class GlobSupport { private GlobSupport() { } /** * Converts a glob expression into the equivalent regular expression for use with module file systems. * @param glob A glob expression compatible with ModuleFileSystem. * @return The equivalent regular expression to glob. */ public static String globToRegex(String glob) { StringBuilder regex = new StringBuilder(); int index = 0; while (index < glob.length()) { char c = glob.charAt(index++); switch (c) { case '*': if (index < glob.length() && glob.charAt(index) == '*') { regex.append(".*"); index++; } else { regex.append("[^/]*"); } break; case '?': regex.append("[^/]"); break; case '\\': if (index < glob.length()) { switch (glob.charAt(index)) { case '[': case '{': case '*': case '?': case '\\': regex.append("\\"); regex.append(glob.charAt(index++)); break; default: regex.append(glob.charAt(index++)); } } else { throw new IllegalArgumentException("Expected character to escape after '\\'"); } break; case '{': index = extractSubpatterns(index, glob, regex); break; case '[': index = startExtractRange(index, glob, regex); break; case '.': case '^': case '$': case '+': case '(': case '|': regex.append("\\"); regex.append(c); break; default: regex.append(c); break; } } return regex.toString(); } private static int startExtractRange(int startingIndex, String glob, StringBuilder regex) { char c = glob.charAt(startingIndex); switch (c) { case ']': throw new IllegalArgumentException("Empty ranges not supported"); case '!': regex.append("[[^/]&&[^"); return extractRange(startingIndex + 1, glob, regex); default: regex.append("[[^/]&&["); return extractRange(startingIndex, glob, regex); } } private static int extractRange(int startingIndex, String glob, StringBuilder regex) { int index = startingIndex; while (index < glob.length()) { char c = glob.charAt(index++); switch (c) { case '\\': case '[': regex.append('\\'); regex.append(c); break; case ']': regex.append("]]"); return index; default: regex.append(c); } } throw new IllegalArgumentException("Incomplete range group, expected ']'"); } private static int extractSubpatterns(int startingIndex, String glob, StringBuilder regex) { List<String> subexpressions = Lists.newArrayList(); StringBuilder subpattern = new StringBuilder(); int index = startingIndex; while (index < glob.length()) { char c = glob.charAt(index++); switch (c) { case '\\': if (index < glob.length()) { subpattern.append('\\'); subpattern.append(glob.charAt(index++)); } else { throw new IllegalArgumentException("Expected character to escape after '\\'"); } break; case '{': throw new IllegalArgumentException("Nested subpattern groups are not allowed"); case ',': if (subpattern.length() > 0) { subexpressions.add(globToRegex(subpattern.toString())); subpattern.setLength(0); } break; case '}': if (subpattern.length() > 0) { subexpressions.add(globToRegex(subpattern.toString())); } if (!subexpressions.isEmpty()) { if (subexpressions.size() == 1) { regex.append(subexpressions.get(0)); } else { regex.append('('); regex.append(Joiner.on("|").join(subexpressions)); regex.append(')'); } } return index; default: subpattern.append(c); } } throw new IllegalArgumentException("Incomplete subpattern group, expected '}'"); } }