/*
* Copyright 2017-present Facebook, Inc.
*
* 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 com.facebook.buck.ide.intellij.projectview;
import com.google.common.base.Joiner;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Utility code for working with {@link java.util.regex.Pattern regex patterns.} Only
* package-visible, for now at least, because the APIs are not very general.
*/
class Patterns {
private final Pattern[] patterns;
private Patterns(Pattern... patterns) {
this.patterns = patterns;
}
private Patterns(String... patterns) {
Pattern[] compiled = new Pattern[patterns.length];
for (int index = 0, length = patterns.length; index < length; ++index) {
compiled[index] = Pattern.compile(patterns[index]);
}
this.patterns = compiled;
}
/**
* Parameter to {@link #onAnyMatch(String, MatchHandler)}. Normally will be implemented as a
* lambda.
*/
interface MatchHandler {
/**
* Called when {@link #onAnyMatch(String, MatchHandler)} finds a match
*
* @param matcher The {@link Matcher} with match location, captures, &c
* @param text The text that was passed to {@link #onMatch(Matcher, String)}
*/
void onMatch(Matcher matcher, String text);
}
/**
* Passes {@code target} to each regex passed to the constructor. Calls {@code handler} on first
* match.
*
* @param target Text to match against each regex
* @param handler Code to call on first match
* @return {@code true} if any regex matched; {@code false} if no regex matched
*/
boolean onAnyMatch(String target, MatchHandler handler) {
for (Pattern pattern : patterns) {
Matcher match = pattern.matcher(target);
if (match.find()) {
handler.onMatch(match, target);
return true;
}
}
return false;
}
/**
* The {@link Patterns} constructor takes either a {@code String...} parameter or a {@code
* Pattern...} parameter. This {@code Builder} is an alternative to {@code new Patterns(compile(a,
* b, c), compile(d, e, f))}
*/
static Builder builder() {
return new Builder();
}
static class Builder {
private final List<String> patterns = new ArrayList<>();
/** Add a regex to the Builder */
Builder add(String pattern) {
patterns.add(pattern);
return this;
}
/** Add a regex to the Builder */
Builder add(String... elements) {
return add(join(elements));
}
/** Create a new {@#link Patterns} */
Patterns build() {
String[] patternArray = new String[patterns.size()];
return new Patterns(patterns.toArray(patternArray));
}
}
/**
* Factory method to create a single-regex {@link Patterns}: equivalent to {@code
* Patterns.builder().add(pattern).build()} but smaller and more efficient
*/
static Patterns build(String pattern) {
return new Patterns(pattern);
}
/**
* Factory method to create a single-regex {@link Patterns}: equivalent to {@code
* Patterns.builder().add(elements).build()} but smaller and more efficient
*/
static Patterns build(String... elements) {
return new Patterns(join(elements));
}
/**
* Convenience function to compile a complex regex from a comma-delimited stream of elements. This
* can be easier to read than a single long string or {@code "..." + "..."}.
*
* @param elements A stream of regex elements
* @return A compiled regex
*/
static Pattern compile(String... elements) {
return compile(0, elements);
}
/**
* Convenience function to compile a complex regex from a comma-delimited stream of elements. This
* can be easier to read than a single long string or {@code "..." + "..."}.
*
* @param flags {@link Pattern#compile(String, int) Pattern.compile() flags}
* @param elements A stream of regex elements
* @return A compiled regex
*/
static Pattern compile(int flags, String... elements) {
return Pattern.compile(join(elements), flags);
}
/**
* Convenience function to generate a capture group. {@code "capture("foo")} is bigger (and more
* expensive) than {@code "(foo)"} but it's very explicit, and contrasts naturally with {@link
* #noncapture(String)}.
*/
static String capture(String element) {
return "(" + element + ")";
}
/**
* Convenience function to generate a capture group. {@code "capture("foo")} is bigger (and more
* expensive) than {@code "(foo)"} but it's very explicit, and contrasts naturally with {@link
* #noncapture(String)}.
*/
static String capture(String... elements) {
return capture(join(elements));
}
/**
* Convenience function to generate a non-capture group. {@code "noncapture("foo"} is easier to
* read than {@code "(?:foo)"}.
*/
static String noncapture(String element) {
return "(?:" + element + ")";
}
/**
* Convenience function to generate a non-capture group. {@code "noncapture("foo"} is easier to
* read than {@code "(?:foo)"}.
*/
static String noncapture(String... elements) {
return noncapture(join(elements));
}
/**
* Convenience function to generate an optional regex element. {@code optional(noncapture("foo"))}
* is a lot easier to read/verify than {@code "(?:foo)?"}.
*/
static String optional(String element) {
return element + "?";
}
/**
* Convenience function to generate an optional regex element. {@code optional(noncapture("foo"))}
* is a lot easier to read/verify than {@code "(?:foo)?"}.
*/
static String optional(String... elements) {
return optional(join(elements));
}
private static String join(String... elements) {
return Joiner.on("").join(elements);
}
}