/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001-2006 by:
EXSE, Department of Geography, University of Bonn
http://www.giub.uni-bonn.de/deegree/
lat/lon GmbH
http://www.lat-lon.de
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
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.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact:
Andreas Poth
lat/lon GmbH
Aennchenstr. 19
53115 Bonn
Germany
E-Mail: poth@lat-lon.de
Prof. Dr. Klaus Greve
Department of Geography
University of Bonn
Meckenheimer Allee 166
53115 Bonn
Germany
E-Mail: greve@giub.uni-bonn.de
---------------------------------------------------------------------------*/
package org.deegree.model.filterencoding;
import org.deegree.framework.xml.ElementList;
import org.deegree.framework.xml.XMLTools;
import org.deegree.model.feature.Feature;
import org.w3c.dom.Element;
/**
* Encapsulates the information of a <PropertyIsLike>-element (as defined in Filter DTD).
*
* @author Markus Schneider
* @version 10.08.2002
*/
public class PropertyIsLikeOperation extends ComparisonOperation {
private PropertyName propertyName;
private Literal literal;
// attributes of <PropertyIsLike>
private char wildCard;
private char singleChar;
private char escapeChar;
private boolean matchCase;
/**
*
* @param propertyName
* @param literal
* @param wildCard
* @param singleChar
* @param escapeChar
*/
public PropertyIsLikeOperation( PropertyName propertyName, Literal literal, char wildCard,
char singleChar, char escapeChar ) {
this(propertyName, literal, wildCard, singleChar, escapeChar, true );
}
/**
*
* @param propertyName
* @param literal
* @param wildCard
* @param singleChar
* @param escapeChar
* @param matchCase
*/
public PropertyIsLikeOperation( PropertyName propertyName, Literal literal, char wildCard,
char singleChar, char escapeChar, boolean matchCase ) {
super( OperationDefines.PROPERTYISLIKE );
this.propertyName = propertyName;
this.literal = literal;
this.wildCard = wildCard;
this.singleChar = singleChar;
this.escapeChar = escapeChar;
this.matchCase = matchCase;
}
public char getWildCard() {
return wildCard;
}
public char getSingleChar() {
return singleChar;
}
public char getEscapeChar() {
return escapeChar;
}
/**
* returns matchCase flag
*/
public boolean isMatchCase() {
return matchCase;
}
/**
* Given a DOM-fragment, a corresponding Operation-object is built. This method recursively
* calls other buildFromDOM () - methods to validate the structure of the DOM-fragment.
*
* @throws FilterConstructionException
* if the structure of the DOM-fragment is invalid
*/
public static Operation buildFromDOM( Element element ) throws FilterConstructionException {
// check if root element's name equals 'PropertyIsLike'
if ( !element.getLocalName().equals( "PropertyIsLike" ) ) {
throw new FilterConstructionException( "Name of element does not equal 'PropertyIsLike'!" );
}
ElementList children = XMLTools.getChildElements( element );
if ( children.getLength() != 2 ) {
throw new FilterConstructionException( "'PropertyIsLike' requires exactly 2 elements!" );
}
PropertyName propertyName = (PropertyName) PropertyName.buildFromDOM( children.item( 0 ) );
Literal literal = (Literal) Literal.buildFromDOM( children.item( 1 ) );
// determine the needed attributes
String wildCard = element.getAttribute( "wildCard" );
if ( wildCard == null || wildCard.length() == 0 ) {
throw new FilterConstructionException( "wildCard-Attribute is unspecified!" );
}
if ( wildCard.length() != 1 ) {
throw new FilterConstructionException( "wildCard-Attribute must be exactly one character!" );
}
// This shouldn't be necessary and can actually cause problems...
// // always use '%' as wildcard because this is compliant to SQL databases
// literal = new Literal( StringTools.replace( literal.getValue(), wildCard, "%", true ) );
// wildCard = "%";
String singleChar = element.getAttribute( "singleChar" );
if ( singleChar == null || singleChar.length() == 0 ) {
throw new FilterConstructionException( "singleChar-Attribute is unspecified!" );
}
if ( singleChar.length() != 1 ) {
throw new FilterConstructionException( "singleChar-Attribute must be exactly one character!" );
}
String escapeChar = element.getAttribute( "escape" );
if ( escapeChar == null || escapeChar.length() == 0 ) {
escapeChar = element.getAttribute( "escapeChar" );
}
if ( escapeChar == null || escapeChar.length() == 0 ) {
throw new FilterConstructionException( "escape-Attribute is unspecified!" );
}
if ( escapeChar.length() != 1 ) {
throw new FilterConstructionException( "escape-Attribute must be exactly one character!" );
}
boolean matchCase = true;
String tmp = element.getAttribute( "matchCase" );
if ( tmp != null && tmp.length() > 0 ) {
try {
matchCase = Boolean.parseBoolean(tmp);
} catch (Exception e) {};
}
return new PropertyIsLikeOperation( propertyName, literal, wildCard.charAt( 0 ),
singleChar.charAt( 0 ), escapeChar.charAt( 0 ), matchCase );
}
/**
* returns the name of the property that shall be compared to the literal
*
*/
public PropertyName getPropertyName() {
return propertyName;
}
/**
* returns the literal the property shall be compared to
*
*/
public Literal getLiteral() {
return literal;
}
/** Produces an indented XML representation of this object. */
public StringBuffer toXML() {
StringBuffer sb = new StringBuffer( 500 );
sb.append( "<ogc:" ).append( getOperatorName() ).append( " wildCard=\"" ).append( wildCard );
sb.append( "\" singleChar=\"" ).append( singleChar ).append( "\" escape=\"" );
sb.append( escapeChar ).append( "\" matchCase=\"" ).append( matchCase).append( "\">" );
sb.append( propertyName.toXML() ).append( literal.toXML() );
sb.append( "</ogc:" ).append( getOperatorName() ).append( ">" );
return sb;
}
/**
* Calculates the <tt>PropertyIsLike</tt>'s logical value based on the certain property
* values of the given <tt>Feature</tt>.
* <p>
*
* @param feature
* that determines the property values
* @return true, if the <tt>Literal</tt> matches the <tt>PropertyName</tt>'s value
* @throws FilterEvaluationException
* if the evaluation could not be performed (for example a specified Property did
* not exist)
*/
public boolean evaluate( Feature feature ) throws FilterEvaluationException {
Object value1 = null;
Object value2 = null;
try {
value1 = propertyName.evaluate( feature );
value2 = literal.getValue();
} catch (Exception e) {
e.printStackTrace();
}
if ( isMatchCase() ) {
return matches( value2.toString(), value1.toString() );
}
return matches( value2.toString().toUpperCase(), value1.toString().toUpperCase() );
}
/**
* Checks if a given <tt>String<tt> matches a pattern that is a sequence
* of:
* <ul>
* <li>standard characters</li>
* <li>wildcard characters (like * in most shells)</li>
* <li>singlechar characters (like ? in most shells)</li>
* </ul>
* @param pattern the pattern to compare to
* @param buffer the <tt>String</tt> to test
* @return true, if the <tt>String</tt> matches the pattern
*/
public boolean matches( String pattern, String buffer ) {
// match was successful if both the pattern and the buffer are empty
if ( pattern.length() == 0
&& buffer.length() == 0 )
return true;
// build the prefix that has to match the beginning of the buffer
// prefix ends at the first (unescaped!) wildcard / singlechar character
StringBuffer sb = new StringBuffer();
boolean escapeMode = false;
int length = pattern.length();
char specialChar = '\0';
for (int i = 0; i < length; i++) {
char c = pattern.charAt( i );
if ( escapeMode ) {
// just append every character (except the escape character)
if ( c != escapeChar )
sb.append( c );
escapeMode = false;
} else {
// escapeChar means: switch to escapeMode
if ( c == escapeChar )
escapeMode = true;
// wildCard / singleChar means: prefix ends here
else if ( c == wildCard
|| c == singleChar ) {
specialChar = c;
break;
} else
sb.append( c );
}
}
String prefix = sb.toString();
int skip = prefix.length();
// the buffer must begin with the prefix or else there is no match
if ( !buffer.startsWith( prefix ) )
return false;
if ( specialChar == wildCard ) {
// the prefix is terminated by a wildcard-character
pattern = pattern.substring( skip + 1, pattern.length() );
// try to find a match for the rest of the pattern
for (int i = skip; i <= buffer.length(); i++) {
String rest = buffer.substring( i, buffer.length() );
if ( matches( pattern, rest ) )
return true;
}
} else if ( specialChar == singleChar ) {
// the prefix is terminated by a singlechar-character
pattern = pattern.substring( skip + 1, pattern.length() );
if ( skip + 1 > buffer.length() )
return false;
String rest = buffer.substring( skip + 1, buffer.length() );
if ( matches( pattern, rest ) )
return true;
} else if ( specialChar == '\0' ) {
// the prefix is terminated by the end of the pattern
if ( buffer.length() == prefix.length() )
return true;
}
return false;
}
}
/* ********************************************************************
Changes to this class. What the people have been up to:
$Log: PropertyIsLikeOperation.java,v $
Revision 1.14 2006/11/21 17:58:43 poth
useless import removed
Revision 1.13 2006/11/09 22:41:26 mschneider
Removed % hack.
Revision 1.12 2006/10/05 17:06:52 poth
ensured that '%' will be used as wildcard / setter methods removed
Revision 1.11 2006/06/30 13:48:18 poth
bug fix - export of 'matchCase' attribute added / missing footer added
********************************************************************** */