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