| /******************************************************************************* |
| * Copyright (c) 2000, 2013 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.util.StringTokenizer; |
| |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IWorkspace; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.IStatus; |
| |
| import com.ibm.icu.text.MessageFormat; |
| |
| |
| public class CompareResourceFilter { |
| private static final char[][] NO_CHAR_CHAR= new char[0][]; |
| |
| private char[][] fExtraResourceFileFilters; |
| private String[] fExtraResourceFolderFilters; |
| |
| |
| public CompareResourceFilter() { |
| // 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, 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; |
| } |
| } |