001package org.apache.commons.digester3; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.commons.logging.Log; 023import org.apache.commons.logging.LogFactory; 024 025/** 026 * <p> 027 * Simple regex pattern matching algorithm. 028 * </p> 029 * <p> 030 * This uses just two wildcards: 031 * <ul> 032 * <li><code>*</code> matches any sequence of none, one or more characters 033 * <li><code>?</code> matches any one character 034 * </ul> 035 * Escaping these wildcards is not supported . 036 * </p> 037 * 038 * @since 1.5 039 */ 040public class SimpleRegexMatcher 041 extends RegexMatcher 042{ 043 044 // --------------------------------------------------------- Fields 045 046 /** Default log (class wide) */ 047 private static final Log BASE_LOG = LogFactory.getLog( SimpleRegexMatcher.class ); 048 049 /** Custom log (can be set per object) */ 050 private Log log = BASE_LOG; 051 052 // --------------------------------------------------------- Properties 053 054 /** 055 * Gets the <code>Log</code> implementation. 056 * 057 * @return the <code>Log</code> implementation. 058 */ 059 public Log getLog() 060 { 061 return log; 062 } 063 064 /** 065 * Sets the current <code>Log</code> implementation used by this class. 066 * 067 * @param log the current <code>Log</code> implementation used by this class. 068 */ 069 public void setLog( Log log ) 070 { 071 this.log = log; 072 } 073 074 // --------------------------------------------------------- Public Methods 075 076 /** 077 * {@inheritDoc} 078 */ 079 @Override 080 public boolean match( String basePattern, String regexPattern ) 081 { 082 // check for nulls 083 if ( basePattern == null || regexPattern == null ) 084 { 085 return false; 086 } 087 return match( basePattern, regexPattern, 0, 0 ); 088 } 089 090 // --------------------------------------------------------- Implementations Methods 091 092 /** 093 * Implementation of regex matching algorithm. This calls itself recursively. 094 * 095 * @param basePattern the standard digester path representing the element 096 * @param regexPattern the regex pattern the path will be tested against 097 * @param baseAt FIXME 098 * @param regexAt FIXME 099 */ 100 private boolean match( String basePattern, String regexPattern, int baseAt, int regexAt ) 101 { 102 if ( log.isTraceEnabled() ) 103 { 104 log.trace( "Base: " + basePattern ); 105 log.trace( "Regex: " + regexPattern ); 106 log.trace( "Base@" + baseAt ); 107 log.trace( "Regex@" + regexAt ); 108 } 109 110 // check bounds 111 if ( regexAt >= regexPattern.length() ) 112 { 113 // maybe we've got a match 114 if ( baseAt >= basePattern.length() ) 115 { 116 // ok! 117 return true; 118 } 119 // run out early 120 return false; 121 122 } 123 if ( baseAt >= basePattern.length() ) 124 { 125 // run out early 126 return false; 127 } 128 129 // ok both within bounds 130 char regexCurrent = regexPattern.charAt( regexAt ); 131 switch ( regexCurrent ) 132 { 133 case '*': 134 // this is the tricky case 135 // check for terminal 136 if ( ++regexAt >= regexPattern.length() ) 137 { 138 // this matches anything let - so return true 139 return true; 140 } 141 // go through every subsequent apperance of the next character 142 // and so if the rest of the regex matches 143 char nextRegex = regexPattern.charAt( regexAt ); 144 if ( log.isTraceEnabled() ) 145 { 146 log.trace( "Searching for next '" + nextRegex + "' char" ); 147 } 148 int nextMatch = basePattern.indexOf( nextRegex, baseAt ); 149 while ( nextMatch != -1 ) 150 { 151 if ( log.isTraceEnabled() ) 152 { 153 log.trace( "Trying '*' match@" + nextMatch ); 154 } 155 if ( match( basePattern, regexPattern, nextMatch, regexAt ) ) 156 { 157 return true; 158 } 159 nextMatch = basePattern.indexOf( nextRegex, nextMatch + 1 ); 160 } 161 log.trace( "No matches found." ); 162 return false; 163 164 case '?': 165 // this matches anything 166 return match( basePattern, regexPattern, ++baseAt, ++regexAt ); 167 168 default: 169 if ( log.isTraceEnabled() ) 170 { 171 log.trace( "Camparing " + regexCurrent + " to " + basePattern.charAt( baseAt ) ); 172 } 173 if ( regexCurrent == basePattern.charAt( baseAt ) ) 174 { 175 // still got more to go 176 return match( basePattern, regexPattern, ++baseAt, ++regexAt ); 177 } 178 return false; 179 } 180 } 181 182}