blob: 5bf6a52f4af7305851b5d69c22d4dac2f6b11b9e [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2016, 2021 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.jcommons.text.core;
import org.eclipse.statet.jcommons.collections.IntArrayList;
import org.eclipse.statet.jcommons.collections.IntList;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
@NonNullByDefault
public class SearchPattern {
//-- Rules --
public static final int EXACT_MATCH= 1 << 1;
public static final int PREFIX_MATCH= 1 << 2;
public static final int WORD_PREFIX_MATCH= 1 << 3;
public static final int SUBSTRING_MATCH= 1 << 4;
/** Unspecific match */
public static final int OTHER_MATCH= 1 << 24;
private int allowedRules;
private String pattern= "";
private char @Nullable [] patternChars;
private String nameCharsString;
private char @Nullable [] nameChars;
protected final IntList tmpRegions= new IntArrayList();
public SearchPattern(final int rules, final String pattern) {
setRules(rules);
setPattern(pattern);
}
public int getSupportedRules() {
return (PREFIX_MATCH | SUBSTRING_MATCH);
}
public int getRules() {
return this.allowedRules;
}
protected int checkRules(int rules) {
if ((rules & SUBSTRING_MATCH) != 0) {
rules|= PREFIX_MATCH;
}
return (rules & getSupportedRules());
}
public void setRules(int rules) {
rules= checkRules(rules);
if (rules == this.allowedRules) {
return;
}
this.allowedRules= rules;
}
public void setPattern(final String pattern) {
if (pattern.equals(this.pattern)) {
return;
}
this.pattern= pattern;
this.patternChars= null;
onPatternChanged(pattern);
}
protected void onPatternChanged(final String pattern) {
}
public String getPattern() {
return this.pattern;
}
protected char[] getPatternChars() {
char[] patternChars= this.patternChars;
if (patternChars == null) {
patternChars= createCompareChars(getPattern());
this.patternChars= patternChars;
}
return patternChars;
}
protected char[] getNameChars(final String name) {
char[] nameChars= (this.nameCharsString == name) ? this.nameChars : null;
if (nameChars == null) {
nameChars= createCompareChars(name);
this.nameCharsString= name;
this.nameChars= nameChars;
}
return nameChars;
}
protected char[] createCompareChars(final String s) {
final char[] chars= new char[s.length()];
for (int i= 0; i < chars.length; i++) {
chars[i]= Character.toLowerCase(s.charAt(i));
}
return chars;
}
public int matches(final String name) {
final int allowedRules= getRules();
char[] patternChars= null;
char[] nameChars= null;
final int patternLength= getPattern().length();
final int lDiff= name.length() - patternLength;
if (lDiff >= 0) {
if ((allowedRules & PREFIX_MATCH) != 0
&& (patternLength == 0
|| isPrefixMatch(patternChars= getPatternChars(), 0, patternChars.length,
name, 0, name.length() ) )) {
return PREFIX_MATCH;
}
if (lDiff > 0) {
if ((allowedRules & SUBSTRING_MATCH) != 0
&& isSubstringMatch(patternChars= getPatternChars(), 0, patternChars.length,
nameChars= getNameChars(name), 1, nameChars.length )) {
return SUBSTRING_MATCH;
}
}
}
return 0;
}
// protected boolean isPrefixMatch(final String name) {
// final char[] patternChars;
//
// if (this.pattern.isEmpty()) {
// return true;
// }
// return isPrefixMatch(patternChars= getPatternChars(), 0, patternChars.length,
// name, 0, name.length() );
// }
//
// protected boolean isSubstringMatch(final String name) {
// final char[] patternChars;
// final char[] nameChars;
// return isSubstringMatch(patternChars= getPatternChars(), 0, patternChars.length,
// nameChars= getNameChars(name), 1, nameChars.length );
// }
public int @Nullable [] getMatchingRegions(final String name, final int matchRule) {
final char[] patternChars;
final char[] nameChars;
switch (matchRule) {
case EXACT_MATCH:
case PREFIX_MATCH:
if (this.pattern.isEmpty()) {
return null;
}
return new int[] { 0, this.pattern.length() };
case SUBSTRING_MATCH:
this.tmpRegions.clear();
addSubstringMatches(patternChars= getPatternChars(), 0, patternChars.length,
nameChars= getNameChars(name), 0, nameChars.length );
return this.tmpRegions.toArray();
default:
return null;
}
}
protected boolean isPrefixMatch(final char[] pattern, final int patternStart, final int patternEnd,
final String name, final int nameStart, final int nameEnd) {
if (nameEnd - nameStart < patternEnd - patternStart) {
return false;
}
int patternIdx= patternStart;
int nameIdx= nameStart;
while (patternIdx < patternEnd) {
final char patternChar= pattern[patternIdx++];
final char nameChar= name.charAt(nameIdx++);
if (nameChar != patternChar && Character.toLowerCase(nameChar) != patternChar) {
return false;
}
}
return true;
}
protected boolean isPrefixMatch(final char[] pattern, final int patternStart, final int patternEnd,
final char[] name, final int nameStart, final int nameEnd) {
if (nameEnd - nameStart < patternEnd - patternStart) {
return false;
}
int patternIdx= patternStart;
int nameIdx= nameStart;
while (patternIdx < patternEnd) {
final char patternChar= pattern[patternIdx++];
final char nameChar= name[nameIdx++];
if (nameChar != patternChar && Character.toLowerCase(nameChar) != patternChar) {
return false;
}
}
return true;
}
protected void addPrefixMatch(final int patternStart, final int patternEnd, final int nameStart) {
this.tmpRegions.add(nameStart);
this.tmpRegions.add(patternEnd - patternStart);
}
protected boolean isSubstringMatch(final char[] pattern, final int patternStart, final int patternEnd,
final char[] name, final int nameStart, final int nameEnd) {
final int nameLastStart= nameEnd - (patternEnd - patternStart);
ITER_SUB: for (int nameCurrentStart= nameStart; nameCurrentStart <= nameLastStart; ) {
int patternIdx= patternStart;
int nameIdx= nameCurrentStart;
while (patternIdx < patternEnd) {
final char patternChar= pattern[patternIdx++];
final char nameChar= name[nameIdx++];
if (nameChar != patternChar) {
nameCurrentStart++;
continue ITER_SUB;
}
}
return true;
}
return false;
}
protected void addSubstringMatches(final char[] pattern, final int patternStart, final int patternEnd,
final char[] name, final int nameStart, final int nameEnd) {
if (patternEnd - patternStart == 0) {
return;
}
final int nameLastStart= nameEnd - (patternEnd - patternStart);
ITER_SUB: for (int nameCurrentStart= nameStart; nameCurrentStart <= nameLastStart; ) {
int patternIdx= patternStart;
int nameIdx= nameCurrentStart;
while (patternIdx < patternEnd) {
final char patternChar= pattern[patternIdx++];
final char nameChar= name[nameIdx++];
if (nameChar != patternChar) {
nameCurrentStart++;
continue ITER_SUB;
}
}
this.tmpRegions.add(nameCurrentStart);
this.tmpRegions.add(nameIdx);
nameCurrentStart= nameIdx;
continue ITER_SUB;
}
}
}