/*
* (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public License
* (LGPL) version 2.1 which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl.html
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* Contributors:
* bstefanescu
*
* $Id$
*/
package org.nuxeo.common.utils;
import java.util.ArrayList;
import java.util.List;
/**
* @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
*/
public class FileNamePattern {
private boolean wstart = false;
private boolean wend = false;
private char[][] parts;
public FileNamePattern(String pattern) {
initialize(pattern);
}
private void initialize(String pattern) {
if (pattern == null || pattern.length() == 0) {
return;
}
List<char[]> result = new ArrayList<char[]>();
StringBuffer buf = new StringBuffer();
char[] chars = pattern.toCharArray();
char c = chars[0];
int i = 0;
if (c == '*') {
wstart = true;
i = 1;
}
int len;
for (; i < chars.length; i++) {
c = chars[i];
switch (c) {
case '*':
len = buf.length();
if (len > 0) {
result.add(toCharArray(buf, len));
buf.setLength(0);
}
break;
default:
buf.append(c);
break;
}
}
if (c == '*') {
wend = true;
} else {
len = buf.length();
if (len > 0) {
result.add(toCharArray(buf, len));
}
}
parts = result.toArray(new char[result.size()][]);
}
private static char[] toCharArray(StringBuffer buf, int len) {
char[] part = new char[len];
buf.getChars(0, len, part, 0);
return part;
}
public boolean match(String text) {
if (parts == null || parts.length == 0) {
if (text.length() == 0) { // handle "" empty patterns
return true;
}
return wstart || wend;
}
return match(text.toCharArray(), 0, 0, !wstart);
}
private boolean match(char[] text, int offset, int part, boolean exactMatch) {
if (part >= parts.length) {
int len = text.length;
if (offset > len) {
return false;
} else if (offset == len) {
return true;
} else {
return wend;
}
}
char[] pattern = parts[part];
// no match founds - try next segment matches
if (exactMatch) {
int k = offset + pattern.length;
if (k > text.length) {
return false;
}
if (containsAt(text, offset, pattern)) {
return match(text, k, part + 1, false);
}
return false;
}
int k = offset;
while (true) {
k = indexOf(text, pattern, k);
if (k == -1) {
return false;
}
if (match(text, k + pattern.length, part + 1, false)) {
return true; // matched
}
// not matched -> continue using next matching segments
k++;
}
}
/**
* Variant of indexOf with ? wildcard.
*/
public static int indexOf(char[] chars, char[] pattern, int offset) {
// do not iterate if not needed
if (pattern.length > chars.length - offset) {
// not enough chars in the chars array starting at offset to match the given pattern
return -1;
}
if (pattern.length == 0) {
return 0;
}
// get the number of possible matching offsets (past over this offset
// matching is no more possible)
int len = chars.length - pattern.length + 1;
START: for (int i = offset; i < len; i++) {
char c = pattern[0];
if (chars[i] == c || c == '?') {
// find first char -> now look to the rest of the pattern
int k = 1;
for (; k < pattern.length; k++) {
c = pattern[k];
if (chars[k + i] != c && c != '?') {
continue START;
}
}
return i;
}
}
return -1;
}
/**
* Tests whether the given array match the pattern at the given position.
* Matching allows ? wildcards.
*/
public static boolean containsAt(char[] array, int offset, char[] pattern) {
if (offset + pattern.length > array.length) {
return false;
}
if (pattern.length == 0) {
return true;
}
if (array[offset] != pattern[0] && pattern[0] != '?') {
return false;
}
for (int i = 1; i < pattern.length; i++) {
int k = offset + i;
if (array[k] != pattern[i] && pattern[i] != '?') {
return false;
}
}
return true;
}
}