blob: 625df0299ac4d0a517a040d7b22034559a8680f0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.compare.internal;
import java.text.MessageFormat;
import java.util.StringTokenizer;
import org.eclipse.core.resources.*;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.runtime.IStatus;
public class CompareFilter {
private static final char[][] NO_CHAR_CHAR= new char[0][];
private char[][] fExtraResourceFileFilters;
private String[] fExtraResourceFolderFilters;
public CompareFilter() {
// nothing to do
}
/*
* Returns true if path matches filter, that is if path should be filtered.
*/
public boolean filter(String path0, boolean folder, boolean isArchive) {
if (!folder && fExtraResourceFileFilters != null) {
char[] name= path0.toCharArray();
for (int i= 0, l= fExtraResourceFileFilters.length; i < l; i++)
if (match(fExtraResourceFileFilters[i], name, true))
return true;
}
if (folder && fExtraResourceFolderFilters != null) {
for (int i= 0, l= fExtraResourceFolderFilters.length; i < l; i++)
if (fExtraResourceFolderFilters[i].equals(path0))
return true;
}
return false;
}
public static String validateResourceFilters(String text) {
IWorkspace workspace= ResourcesPlugin.getWorkspace();
String[] filters= getTokens(text, ","); //$NON-NLS-1$
for (int i= 0; i < filters.length; i++) {
String fileName= filters[i].replace('*', 'x');
int resourceType= IResource.FILE;
int lastCharacter= fileName.length() - 1;
if (lastCharacter >= 0 && fileName.charAt(lastCharacter) == '/') {
fileName= fileName.substring(0, lastCharacter);
resourceType= IResource.FOLDER;
}
IStatus status= workspace.validateName(fileName, resourceType);
if (status.matches(IStatus.ERROR)) {
String format= Utilities.getString("ComparePreferencePage.filter.invalidsegment.error"); //$NON-NLS-1$
return MessageFormat.format(format, new String[] { status.getMessage() } );
}
}
return null;
}
public void setFilters(String filterSequence) {
char[][] filters= filterSequence != null && filterSequence.length() > 0
? splitAndTrimOn(',', filterSequence.toCharArray())
: null;
if (filters == null) {
fExtraResourceFileFilters= null;
fExtraResourceFolderFilters= null;
} else {
int fileCount= 0, folderCount= 0;
for (int i= 0, l= filters.length; i < l; i++) {
char[] f= filters[i];
if (f.length == 0)
continue;
if (f[f.length - 1] == '/')
folderCount++;
else
fileCount++;
}
fExtraResourceFileFilters= new char[fileCount][];
fExtraResourceFolderFilters= new String[folderCount];
for (int i= 0, l= filters.length; i < l; i++) {
char[] f= filters[i];
if (f.length == 0)
continue;
if (f[f.length - 1] == '/')
fExtraResourceFolderFilters[--folderCount]= new String(subarray(f, 0, f.length - 1));
else
fExtraResourceFileFilters[--fileCount]= f;
}
}
}
/////////
private static String[] getTokens(String text, String separator) {
StringTokenizer tok= new StringTokenizer(text, separator);
int nTokens= tok.countTokens();
String[] res= new String[nTokens];
for (int i= 0; i < res.length; i++)
res[i]= tok.nextToken().trim();
return res;
}
/**
* Answers true if the pattern matches the given name, false otherwise.
* This char[] pattern matching accepts wild-cards '*' and '?'.
*
* When not case sensitive, the pattern is assumed to already be
* lowercased, the name will be lowercased character per character as
* comparing. If name is null, the answer is false. If pattern is null, the
* answer is true if name is not null. <br><br>For example:
* <ol>
* <li>
*
* <pre>
* pattern = { '?', 'b', '*' } name = { 'a', 'b', 'c' , 'd' } isCaseSensitive = true result => true
* </pre>
*
*
* </li>
* <li>
*
* <pre>
* pattern = { '?', 'b', '?' } name = { 'a', 'b', 'c' , 'd' } isCaseSensitive = true result => false
* </pre>
*
*
* </li>
* <li>
*
* <pre>
* pattern = { 'b', '*' } name = { 'a', 'b', 'c' , 'd' } isCaseSensitive = true result => false
* </pre>
*
*
* </li>
* </ol>
*
* @param pattern
* the given pattern
* @param name
* the given name
* @param isCaseSensitive
* flag to know whether or not the matching should be case
* sensitive
* @return true if the pattern matches the given name, false otherwise
*/
private boolean match(char[] pattern, char[] name, boolean isCaseSensitive) {
if (name == null)
return false; // null name cannot match
if (pattern == null)
return true; // null pattern is equivalent to '*'
return match(pattern, 0, pattern.length, name, 0, name.length, isCaseSensitive);
}
/**
* Answers true if the a sub-pattern matches the subpart of the given name,
* false otherwise. char[] pattern matching, accepting wild-cards '*' and
* '?'. Can match only subset of name/pattern. end positions are
* non-inclusive. The subpattern is defined by the patternStart and
* pattternEnd positions. When not case sensitive, the pattern is assumed
* to already be lowercased, the name will be lowercased character per
* character as comparing. <br><br>For example:
* <ol>
* <li>
*
* <pre>
* pattern = { '?', 'b', '*' } patternStart = 1 patternEnd = 3 name = { 'a', 'b', 'c' , 'd' } nameStart = 1 nameEnd = 4 isCaseSensitive = true result => true
* </pre>
*
*
* </li>
* <li>
*
* <pre>
* pattern = { '?', 'b', '*' } patternStart = 1 patternEnd = 2 name = { 'a', 'b', 'c' , 'd' } nameStart = 1 nameEnd = 2 isCaseSensitive = true result => false
* </pre>
*
*
* </li>
* </ol>
*
* @param pattern
* the given pattern
* @param patternStart
* the given pattern start
* @param patternEnd
* the given pattern end
* @param name
* the given name
* @param nameStart
* the given name start
* @param nameEnd
* the given name end
* @param isCaseSensitive
* flag to know if the matching should be case sensitive
* @return true if the a sub-pattern matches the subpart of the given name,
* false otherwise
*/
private boolean match(char[] pattern, int patternStart, int patternEnd, char[] name, int nameStart, int nameEnd,
boolean isCaseSensitive) {
if (name == null)
return false; // null name cannot match
if (pattern == null)
return true; // null pattern is equivalent to '*'
int iPattern= patternStart;
int iName= nameStart;
if (patternEnd < 0)
patternEnd= pattern.length;
if (nameEnd < 0)
nameEnd= name.length;
/* check first segment */
char patternChar= 0;
while ((iPattern < patternEnd) && (patternChar= pattern[iPattern]) != '*') {
if (iName == nameEnd)
return false;
if (patternChar != (isCaseSensitive ? name[iName] : Character.toLowerCase(name[iName])) && patternChar != '?') {
return false;
}
iName++;
iPattern++;
}
/* check sequence of star+segment */
int segmentStart;
if (patternChar == '*') {
segmentStart= ++iPattern; // skip star
} else {
segmentStart= 0; // force iName check
}
int prefixStart= iName;
checkSegment : while (iName < nameEnd) {
if (iPattern == patternEnd) {
iPattern= segmentStart; // mismatch - restart current segment
iName= ++prefixStart;
continue checkSegment;
}
/* segment is ending */
if ((patternChar= pattern[iPattern]) == '*') {
segmentStart= ++iPattern; // skip start
if (segmentStart == patternEnd) {
return true;
}
prefixStart= iName;
continue checkSegment;
}
/* check current name character */
if ((isCaseSensitive ? name[iName] : Character.toLowerCase(name[iName])) != patternChar && patternChar != '?') {
iPattern= segmentStart; // mismatch - restart current segment
iName= ++prefixStart;
continue checkSegment;
}
iName++;
iPattern++;
}
return (segmentStart == patternEnd) || (iName == nameEnd && iPattern == patternEnd)
|| (iPattern == patternEnd - 1 && pattern[iPattern] == '*');
}
/**
* Return a new array which is the split of the given array using the given
* divider and triming each subarray to remove whitespaces equals to ' '.
* <br><br>For example:
* <ol>
* <li>
*
* <pre>
* divider = 'b' array = { 'a' , 'b', 'b', 'a', 'b', 'a' } result => { { 'a' }, { }, { 'a' }, { 'a' } }
* </pre>
*
*
* </li>
* <li>
*
* <pre>
* divider = 'c' array = { 'a' , 'b', 'b', 'a', 'b', 'a' } result => { { 'a', 'b', 'b', 'a', 'b', 'a' } }
* </pre>
*
*
* </li>
* <li>
*
* <pre>
* divider = 'b' array = { 'a' , ' ', 'b', 'b', 'a', 'b', 'a' } result => { { 'a' }, { }, { 'a' }, { 'a' } }
* </pre>
*
*
* </li>
* <li>
*
* <pre>
* divider = 'c' array = { ' ', ' ', 'a' , 'b', 'b', 'a', 'b', 'a', ' ' } result => { { 'a', 'b', 'b', 'a', 'b', 'a' } }
* </pre>
*
*
* </li>
* </ol>
*
* @param divider
* the given divider
* @param array
* the given array
* @return a new array which is the split of the given array using the
* given divider and triming each subarray to remove whitespaces
* equals to ' '
*/
private char[][] splitAndTrimOn(char divider, char[] array) {
int length= array == null ? 0 : array.length;
if (length == 0)
return NO_CHAR_CHAR;
int wordCount= 1;
for (int i= 0; i < length; i++)
if (array[i] == divider)
wordCount++;
char[][] split= new char[wordCount][];
int last= 0, currentWord= 0;
for (int i= 0; i < length; i++) {
if (array[i] == divider) {
int start= last, end= i - 1;
while (start < i && array[start] == ' ')
start++;
while (end > start && array[end] == ' ')
end--;
split[currentWord]= new char[end - start + 1];
System.arraycopy(array, start, split[currentWord++], 0, end - start + 1);
last= i + 1;
}
}
int start= last, end= length - 1;
while (start < length && array[start] == ' ')
start++;
while (end > start && array[end] == ' ')
end--;
split[currentWord]= new char[end - start + 1];
System.arraycopy(array, start, split[currentWord++], 0, end - start + 1);
return split;
}
/**
* Answers a new array which is a copy of the given array starting at the
* given start and ending at the given end. The given start is inclusive
* and the given end is exclusive. Answers null if start is greater than
* end, if start is lower than 0 or if end is greater than the length of
* the given array. If end equals -1, it is converted to the array length.
* <br><br>For example:
* <ol>
* <li>
*
* <pre>
* array = { 'a' , 'b' } start = 0 end = 1 result => { 'a' }
* </pre>
*
*
* </li>
* <li>
*
* <pre>
* array = { 'a', 'b' } start = 0 end = -1 result => { 'a' , 'b' }
* </pre>
*
*
* </li>
* </ol>
*
* @param array
* the given array
* @param start
* the given starting index
* @param end
* the given ending index
* @return a new array which is a copy of the given array starting at the
* given start and ending at the given end
* @exception NullPointerException
* if the given array is null
*/
private char[] subarray(char[] array, int start, int end) {
if (end == -1)
end= array.length;
if (start > end)
return null;
if (start < 0)
return null;
if (end > array.length)
return null;
char[] result= new char[end - start];
System.arraycopy(array, start, result, 0, end - start);
return result;
}
}