| /* |
| * Copyright 2006 Sascha Weinreuter |
| * |
| * 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.intellij.plugins.intelliLang.util; |
| |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.patterns.StringPattern; |
| import com.intellij.util.Function; |
| import com.intellij.util.containers.WeakHashMap; |
| |
| import java.util.LinkedHashSet; |
| import java.util.Set; |
| import java.util.List; |
| |
| /** |
| * Simple abstraction of a String matcher that can be based on simple and vastly more |
| * efficient String comparisons than doing a full pattern match. |
| */ |
| public abstract class StringMatcher<T> { |
| protected final T myTarget; |
| |
| public static final StringMatcher NONE = new Any("<none>", false); |
| public static final StringMatcher ANY = new Any("", true); |
| public static final StringMatcher ANY_PATTERN = new Any(".*", true); |
| |
| protected StringMatcher(T target) { |
| myTarget = target; |
| } |
| |
| private abstract static class Simple extends StringMatcher<String> { |
| Simple(String target) { |
| super(target); |
| } |
| |
| public String getPattern() { |
| return myTarget; |
| } |
| } |
| |
| private static final class Pattern extends StringMatcher<java.util.regex.Pattern> { |
| Pattern(String target) { |
| super(java.util.regex.Pattern.compile(target)); |
| } |
| |
| public boolean matches(String what) { |
| return myTarget.matcher(StringPattern.newBombedCharSequence(what)).matches(); |
| } |
| |
| public String getPattern() { |
| return myTarget.pattern(); |
| } |
| } |
| |
| private final static class Equals extends Simple { |
| Equals(String target) { |
| super(target); |
| } |
| |
| public boolean matches(String what) { |
| return myTarget.equals(what); |
| } |
| } |
| |
| private final static class StartsWith extends Simple { |
| StartsWith(String target) { |
| super(target); |
| } |
| |
| public boolean matches(String what) { |
| return what.startsWith(myTarget); |
| } |
| |
| public String getPattern() { |
| return super.getPattern() + ".*"; |
| } |
| } |
| |
| private final static class EndsWith extends Simple { |
| EndsWith(String target) { |
| super(target); |
| } |
| |
| public boolean matches(String what) { |
| return what.endsWith(myTarget); |
| } |
| |
| public String getPattern() { |
| return ".*" + super.getPattern(); |
| } |
| } |
| |
| private final static class Contains extends Simple { |
| Contains(String target) { |
| super(target); |
| } |
| |
| public boolean matches(String what) { |
| return what.contains(myTarget); |
| } |
| |
| public String getPattern() { |
| return ".*" + super.getPattern() + ".*"; |
| } |
| } |
| |
| private static final class Any extends Simple { |
| private final boolean myMatches; |
| |
| Any(String target, boolean matches) { |
| super(target); |
| myMatches = matches; |
| } |
| |
| public boolean matches(String what) { |
| return myMatches; |
| } |
| } |
| |
| private static final class IgnoreCase extends StringMatcher<StringMatcher> { |
| IgnoreCase(StringMatcher target) { |
| super(target); |
| } |
| |
| public boolean matches(String what) { |
| return myTarget.matches(what.toLowerCase()); |
| } |
| |
| public String getPattern() { |
| return "(?i)" + myTarget.getPattern(); |
| } |
| } |
| |
| private static final class Cache extends StringMatcher<StringMatcher> { |
| private final WeakHashMap<String, Boolean> myCache = new WeakHashMap<String, Boolean>(); |
| |
| Cache(StringMatcher target) { |
| super(target); |
| } |
| |
| public String getPattern() { |
| return myTarget.getPattern(); |
| } |
| |
| public synchronized boolean matches(String what) { |
| final Boolean o = myCache.get(what); |
| if (o != null) { |
| return o; |
| } |
| final boolean b = myTarget.matches(what); |
| myCache.put(what, b); |
| return b; |
| } |
| } |
| |
| public static final class MatcherSet extends StringMatcher<Set<StringMatcher>> { |
| private final String myPattern; |
| |
| protected MatcherSet(Set<StringMatcher> target) { |
| super(target); |
| myPattern = StringUtil.join(target, new Function<StringMatcher, String>() { |
| public String fun(StringMatcher s) { |
| return s.getPattern(); |
| } |
| }, "|"); |
| } |
| |
| public static StringMatcher create(Set<StringMatcher> matchers) { |
| final MatcherSet m = new MatcherSet(matchers); |
| return matchers.size() > 3 ? new Cache(m) : m; |
| } |
| |
| public boolean matches(String what) { |
| for (StringMatcher matcher : myTarget) { |
| if (matcher.matches(what)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public String getPattern() { |
| return myPattern; |
| } |
| } |
| |
| public abstract boolean matches(String what); |
| |
| public abstract String getPattern(); |
| |
| public static StringMatcher create(String target) { |
| if (target.length() == 0) return ANY; |
| if (target.equals(".*")) return ANY_PATTERN; |
| if (target.equals(NONE.getPattern())) return NONE; |
| |
| final List<String> branches = StringUtil.split(target,"|"); |
| final Set<StringMatcher> matchers = new LinkedHashSet<StringMatcher>(); |
| |
| for (String branch : branches) { |
| boolean startsWith = false; |
| boolean endsWith = false; |
| boolean ignoreCase = false; |
| |
| // this assumes the regex is syntactically correct |
| if (branch.startsWith("(?i)")) { |
| ignoreCase = true; |
| branch = branch.substring(2).toLowerCase(); |
| } |
| if (branch.endsWith(".*")) { |
| startsWith = true; |
| branch = branch.substring(0, branch.length() - 2); |
| } |
| if (branch.startsWith(".*")) { |
| endsWith = true; |
| branch = branch.substring(2); |
| } |
| |
| final boolean m = analyseBranch(branch); |
| if (!m) { |
| try { |
| return new Cache(new Pattern(target)); |
| } |
| catch (Exception e) { |
| return new Any(target, false); |
| } |
| } |
| |
| final StringMatcher matcher; |
| if (startsWith && endsWith) { |
| matcher = new Contains(branch); |
| } |
| else if (startsWith) { |
| matcher = new StartsWith(branch); |
| } |
| else if (endsWith) { |
| matcher = new EndsWith(branch); |
| } |
| else { |
| matcher = new Equals(branch); |
| } |
| |
| matchers.add(ignoreCase ? new IgnoreCase(matcher) : matcher); |
| } |
| |
| return matchers.size() == 1 ? matchers.iterator().next() : MatcherSet.create(matchers); |
| } |
| |
| private static boolean analyseBranch(String target) { |
| for (int i = 0; i < target.length(); i++) { |
| final char c = target.charAt(i); |
| if (c != '_' && c != '-' && !Character.isLetterOrDigit(c)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| public int hashCode() { |
| return myTarget.hashCode(); |
| } |
| |
| @SuppressWarnings({"SimplifiableIfStatement"}) |
| public boolean equals(Object obj) { |
| if (obj == null) return false; |
| if (obj.getClass() != getClass()) return false; |
| return ((StringMatcher)obj).myTarget.equals(myTarget); |
| } |
| } |