001/* 002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * bstefanescu 018 * 019 * $Id$ 020 */ 021 022package org.nuxeo.common.utils; 023 024import java.util.ArrayList; 025import java.util.List; 026 027/** 028 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 029 */ 030public class FileNamePattern { 031 032 private boolean wstart = false; 033 034 private boolean wend = false; 035 036 private char[][] parts; 037 038 public FileNamePattern(String pattern) { 039 initialize(pattern); 040 } 041 042 private void initialize(String pattern) { 043 if (pattern == null || pattern.length() == 0) { 044 return; 045 } 046 List<char[]> result = new ArrayList<char[]>(); 047 StringBuffer buf = new StringBuffer(); 048 char[] chars = pattern.toCharArray(); 049 char c = chars[0]; 050 int i = 0; 051 if (c == '*') { 052 wstart = true; 053 i = 1; 054 } 055 int len; 056 for (; i < chars.length; i++) { 057 c = chars[i]; 058 switch (c) { 059 case '*': 060 len = buf.length(); 061 if (len > 0) { 062 result.add(toCharArray(buf, len)); 063 buf.setLength(0); 064 } 065 break; 066 default: 067 buf.append(c); 068 break; 069 } 070 } 071 if (c == '*') { 072 wend = true; 073 } else { 074 len = buf.length(); 075 if (len > 0) { 076 result.add(toCharArray(buf, len)); 077 } 078 } 079 parts = result.toArray(new char[result.size()][]); 080 } 081 082 private static char[] toCharArray(StringBuffer buf, int len) { 083 char[] part = new char[len]; 084 buf.getChars(0, len, part, 0); 085 return part; 086 } 087 088 public boolean match(String text) { 089 if (parts == null || parts.length == 0) { 090 if (text.length() == 0) { // handle "" empty patterns 091 return true; 092 } 093 return wstart || wend; 094 } 095 096 return match(text.toCharArray(), 0, 0, !wstart); 097 } 098 099 private boolean match(char[] text, int offset, int part, boolean exactMatch) { 100 if (part >= parts.length) { 101 int len = text.length; 102 if (offset > len) { 103 return false; 104 } else if (offset == len) { 105 return true; 106 } else { 107 return wend; 108 } 109 } 110 char[] pattern = parts[part]; 111 // no match founds - try next segment matches 112 if (exactMatch) { 113 int k = offset + pattern.length; 114 if (k > text.length) { 115 return false; 116 } 117 if (containsAt(text, offset, pattern)) { 118 return match(text, k, part + 1, false); 119 } 120 return false; 121 } 122 int k = offset; 123 while (true) { 124 k = indexOf(text, pattern, k); 125 if (k == -1) { 126 return false; 127 } 128 if (match(text, k + pattern.length, part + 1, false)) { 129 return true; // matched 130 } 131 // not matched -> continue using next matching segments 132 k++; 133 } 134 } 135 136 /** 137 * Variant of indexOf with ? wildcard. 138 */ 139 public static int indexOf(char[] chars, char[] pattern, int offset) { 140 // do not iterate if not needed 141 if (pattern.length > chars.length - offset) { 142 // not enough chars in the chars array starting at offset to match the given pattern 143 return -1; 144 } 145 if (pattern.length == 0) { 146 return 0; 147 } 148 // get the number of possible matching offsets (past over this offset 149 // matching is no more possible) 150 int len = chars.length - pattern.length + 1; 151 START: for (int i = offset; i < len; i++) { 152 char c = pattern[0]; 153 if (chars[i] == c || c == '?') { 154 // find first char -> now look to the rest of the pattern 155 int k = 1; 156 for (; k < pattern.length; k++) { 157 c = pattern[k]; 158 if (chars[k + i] != c && c != '?') { 159 continue START; 160 } 161 } 162 return i; 163 } 164 } 165 return -1; 166 } 167 168 /** 169 * Tests whether the given array match the pattern at the given position. Matching allows ? wildcards. 170 */ 171 public static boolean containsAt(char[] array, int offset, char[] pattern) { 172 if (offset + pattern.length > array.length) { 173 return false; 174 } 175 if (pattern.length == 0) { 176 return true; 177 } 178 if (array[offset] != pattern[0] && pattern[0] != '?') { 179 return false; 180 } 181 for (int i = 1; i < pattern.length; i++) { 182 int k = offset + i; 183 if (array[k] != pattern[i] && pattern[i] != '?') { 184 return false; 185 } 186 } 187 return true; 188 } 189 190}