001 //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/model/filterencoding/PropertyIsLikeOperation.java $ 002 /*---------------------------------------------------------------------------- 003 This file is part of deegree, http://deegree.org/ 004 Copyright (C) 2001-2009 by: 005 Department of Geography, University of Bonn 006 and 007 lat/lon GmbH 008 009 This library is free software; you can redistribute it and/or modify it under 010 the terms of the GNU Lesser General Public License as published by the Free 011 Software Foundation; either version 2.1 of the License, or (at your option) 012 any later version. 013 This library is distributed in the hope that it will be useful, but WITHOUT 014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 015 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 016 details. 017 You should have received a copy of the GNU Lesser General Public License 018 along with this library; if not, write to the Free Software Foundation, Inc., 019 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 020 021 Contact information: 022 023 lat/lon GmbH 024 Aennchenstr. 19, 53177 Bonn 025 Germany 026 http://lat-lon.de/ 027 028 Department of Geography, University of Bonn 029 Prof. Dr. Klaus Greve 030 Postfach 1147, 53001 Bonn 031 Germany 032 http://www.geographie.uni-bonn.de/deegree/ 033 034 e-mail: info@deegree.org 035 ----------------------------------------------------------------------------*/ 036 package org.deegree.model.filterencoding; 037 038 import org.deegree.framework.xml.ElementList; 039 import org.deegree.framework.xml.XMLTools; 040 import org.deegree.model.feature.Feature; 041 import org.w3c.dom.Element; 042 043 /** 044 * Encapsulates the information of a <PropertyIsLike>-element (as defined in Filter DTD). 045 * 046 * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider</a> 047 * @author last edited by: $Author: mschneider $ 048 * 049 * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $ 050 */ 051 public class PropertyIsLikeOperation extends ComparisonOperation { 052 053 private PropertyName propertyName; 054 055 private Literal literal; 056 057 // attributes of <PropertyIsLike> 058 private char wildCard; 059 060 private char singleChar; 061 062 private char escapeChar; 063 064 private boolean matchCase; 065 066 /** 067 * 068 * @param propertyName 069 * @param literal 070 * @param wildCard 071 * @param singleChar 072 * @param escapeChar 073 */ 074 public PropertyIsLikeOperation( PropertyName propertyName, Literal literal, char wildCard, char singleChar, 075 char escapeChar ) { 076 this( propertyName, literal, wildCard, singleChar, escapeChar, true ); 077 } 078 079 /** 080 * 081 * @param propertyName 082 * @param literal 083 * @param wildCard 084 * @param singleChar 085 * @param escapeChar 086 * @param matchCase 087 */ 088 public PropertyIsLikeOperation( PropertyName propertyName, Literal literal, char wildCard, char singleChar, 089 char escapeChar, boolean matchCase ) { 090 super( OperationDefines.PROPERTYISLIKE ); 091 this.propertyName = propertyName; 092 this.literal = literal; 093 this.wildCard = wildCard; 094 this.singleChar = singleChar; 095 this.escapeChar = escapeChar; 096 this.matchCase = matchCase; 097 } 098 099 /** 100 * @return the wildcard 101 */ 102 public char getWildCard() { 103 return wildCard; 104 } 105 106 /** 107 * @return the singleChar 108 */ 109 public char getSingleChar() { 110 return singleChar; 111 } 112 113 /** 114 * @return the escape character 115 */ 116 public char getEscapeChar() { 117 return escapeChar; 118 } 119 120 /** 121 * @return matchCase flag 122 */ 123 public boolean isMatchCase() { 124 return matchCase; 125 } 126 127 /** 128 * Given a DOM-fragment, a corresponding Operation-object is built. This method recursively 129 * calls other buildFromDOM () - methods to validate the structure of the DOM-fragment. 130 * 131 * @param element 132 * the element to parse 133 * @return a Bean of the DOM 134 * 135 * @throws FilterConstructionException 136 * if the structure of the DOM-fragment is invalid 137 */ 138 public static Operation buildFromDOM( Element element ) 139 throws FilterConstructionException { 140 141 // check if root element's name equals 'PropertyIsLike' 142 if ( !element.getLocalName().equals( "PropertyIsLike" ) ) { 143 throw new FilterConstructionException( "Name of element does not equal 'PropertyIsLike'!" ); 144 } 145 146 ElementList children = XMLTools.getChildElements( element ); 147 if ( children.getLength() != 2 ) { 148 throw new FilterConstructionException( "'PropertyIsLike' requires exactly 2 elements!" ); 149 } 150 151 PropertyName propertyName = (PropertyName) PropertyName.buildFromDOM( children.item( 0 ) ); 152 Literal literal = (Literal) Literal.buildFromDOM( children.item( 1 ) ); 153 154 // determine the needed attributes 155 String wildCard = element.getAttribute( "wildCard" ); 156 if ( wildCard == null || wildCard.length() == 0 ) { 157 throw new FilterConstructionException( "wildCard-Attribute is unspecified!" ); 158 } 159 if ( wildCard.length() != 1 ) { 160 throw new FilterConstructionException( "wildCard-Attribute must be exactly one character!" ); 161 } 162 163 // This shouldn't be necessary and can actually cause problems... 164 // // always use '%' as wildcard because this is compliant to SQL databases 165 // literal = new Literal( StringTools.replace( literal.getValue(), wildCard, "%", true ) ); 166 // wildCard = "%"; 167 String singleChar = element.getAttribute( "singleChar" ); 168 if ( singleChar == null || singleChar.length() == 0 ) { 169 throw new FilterConstructionException( "singleChar-Attribute is unspecified!" ); 170 } 171 if ( singleChar.length() != 1 ) { 172 throw new FilterConstructionException( "singleChar-Attribute must be exactly one character!" ); 173 } 174 String escapeChar = element.getAttribute( "escape" ); 175 if ( escapeChar == null || escapeChar.length() == 0 ) { 176 escapeChar = element.getAttribute( "escapeChar" ); 177 } 178 if ( escapeChar == null || escapeChar.length() == 0 ) { 179 throw new FilterConstructionException( "escape-Attribute is unspecified!" ); 180 } 181 if ( escapeChar.length() != 1 ) { 182 throw new FilterConstructionException( "escape-Attribute must be exactly one character!" ); 183 } 184 boolean matchCase = true; 185 String tmp = element.getAttribute( "matchCase" ); 186 if ( tmp != null && tmp.length() > 0 ) { 187 try { 188 matchCase = Boolean.parseBoolean( tmp ); 189 } catch ( Exception e ) { 190 // undocumented 191 } 192 193 } 194 195 return new PropertyIsLikeOperation( propertyName, literal, wildCard.charAt( 0 ), singleChar.charAt( 0 ), 196 escapeChar.charAt( 0 ), matchCase ); 197 } 198 199 /** 200 * @return the name of the property that shall be compared to the literal 201 * 202 */ 203 public PropertyName getPropertyName() { 204 return propertyName; 205 } 206 207 /** 208 * @return the literal the property shall be compared to 209 */ 210 public Literal getLiteral() { 211 return literal; 212 } 213 214 public StringBuffer toXML() { 215 StringBuffer sb = new StringBuffer( 500 ); 216 sb.append( "<ogc:" ).append( getOperatorName() ).append( " wildCard=\"" ).append( wildCard ); 217 sb.append( "\" singleChar=\"" ).append( singleChar ).append( "\" escape=\"" ); 218 sb.append( escapeChar ).append( "\" matchCase=\"" ).append( matchCase ).append( "\">" ); 219 sb.append( propertyName.toXML() ).append( literal.toXML() ); 220 sb.append( "</ogc:" ).append( getOperatorName() ).append( ">" ); 221 return sb; 222 } 223 224 public StringBuffer to100XML() { 225 return toXML(); 226 } 227 228 public StringBuffer to110XML() { 229 return toXML(); 230 } 231 232 /** 233 * Calculates the <tt>PropertyIsLike</tt>'s logical value based on the certain property 234 * values of the given <tt>Feature</tt>. 235 * <p> 236 * 237 * @param feature 238 * that determines the property values 239 * @return true, if the <tt>Literal</tt> matches the <tt>PropertyName</tt>'s value 240 * @throws FilterEvaluationException 241 * if the evaluation could not be performed (for example a specified Property did 242 * not exist) 243 */ 244 public boolean evaluate( Feature feature ) 245 throws FilterEvaluationException { 246 247 Object value1 = null; 248 Object value2 = null; 249 try { 250 value1 = propertyName.evaluate( feature ); 251 value2 = literal.getValue(); 252 } catch ( Exception e ) { 253 e.printStackTrace(); 254 } 255 if ( value1 == null && value2 == null ) { 256 return true; 257 } else if ( value1 == null || value2 == null ) { 258 return false; 259 } 260 if ( isMatchCase() ) { 261 return matches( value2.toString(), value1.toString() ); 262 } 263 return matches( value2.toString().toUpperCase(), value1.toString().toUpperCase() ); 264 } 265 266 /** 267 * Checks if a given <tt>String<tt> matches a pattern that is a sequence 268 * of: 269 * <ul> 270 * <li>standard characters</li> 271 * <li>wildcard characters (like * in most shells)</li> 272 * <li>singlechar characters (like ? in most shells)</li> 273 * </ul> 274 * @param pattern the pattern to compare to 275 * @param buffer the <tt>String</tt> to test 276 * @return true, if the <tt>String</tt> matches the pattern 277 */ 278 public boolean matches( String pattern, String buffer ) { 279 // match was successful if both the pattern and the buffer are empty 280 if ( pattern.length() == 0 && buffer.length() == 0 ) 281 return true; 282 283 // build the prefix that has to match the beginning of the buffer 284 // prefix ends at the first (unescaped!) wildcard / singlechar character 285 StringBuffer sb = new StringBuffer(); 286 boolean escapeMode = false; 287 int length = pattern.length(); 288 char specialChar = '\0'; 289 290 for ( int i = 0; i < length; i++ ) { 291 char c = pattern.charAt( i ); 292 293 if ( escapeMode ) { 294 // just append every character (except the escape character) 295 if ( c != escapeChar ) 296 sb.append( c ); 297 escapeMode = false; 298 } else { 299 // escapeChar means: switch to escapeMode 300 if ( c == escapeChar ) 301 escapeMode = true; 302 // wildCard / singleChar means: prefix ends here 303 else if ( c == wildCard || c == singleChar ) { 304 specialChar = c; 305 break; 306 } else 307 sb.append( c ); 308 } 309 } 310 String prefix = sb.toString(); 311 int skip = prefix.length(); 312 313 // the buffer must begin with the prefix or else there is no match 314 if ( !buffer.startsWith( prefix ) ) 315 return false; 316 317 if ( specialChar == wildCard ) { 318 // the prefix is terminated by a wildcard-character 319 pattern = pattern.substring( skip + 1, pattern.length() ); 320 // try to find a match for the rest of the pattern 321 for ( int i = skip; i <= buffer.length(); i++ ) { 322 String rest = buffer.substring( i, buffer.length() ); 323 if ( matches( pattern, rest ) ) 324 return true; 325 } 326 } else if ( specialChar == singleChar ) { 327 // the prefix is terminated by a singlechar-character 328 pattern = pattern.substring( skip + 1, pattern.length() ); 329 if ( skip + 1 > buffer.length() ) 330 return false; 331 String rest = buffer.substring( skip + 1, buffer.length() ); 332 if ( matches( pattern, rest ) ) 333 return true; 334 } else if ( specialChar == '\0' ) { 335 // the prefix is terminated by the end of the pattern 336 if ( buffer.length() == prefix.length() ) 337 return true; 338 } 339 return false; 340 } 341 }