001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2002-2006, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.filter;
017:
018: import java.util.Iterator;
019:
020: import org.geotools.factory.CommonFactoryFinder;
021: import org.opengis.filter.Filter;
022: import org.opengis.filter.FilterFactory;
023:
024: /**
025: * Determines which parts of a Filter can be turned into valid SQL statements.
026: * Given a filter it constructs two filters, one of the supported parts of the
027: * filter passed in, one of the unsupported. If one of the constructed
028: * filters is null (ie the whole filter is supported or unsupported), it is
029: * the clients responsibility to deal with that. The SQLUnpacker should be
030: * tightly coordinated with the SQLEncoder. The SQLEncoder passes its
031: * Capabilities (ie which filters it can encode and which it can't) to the
032: * Unpacker, and the Unpacker returns a supported filter, which should be
033: * passed to the Encoder as the Encoder Capabilities claimed to fully support
034: * everything in the supported filter. The unsupported filter should be used
035: * after the SQL statement is executed, testing each feature in the result set
036: * with the contains method.
037: *
038: * <p>
039: * This Unpacker can likely be easily used with any Encoder that has a
040: * FilterCapabilities of the actions it can perform. May want to rename it
041: * FilterUnpacker, as it is likely generic enough, but for now this name
042: * should be fine, to emphasize that the SQLEncoder needs to be closely linked
043: * to it to work properly.
044: * </p>
045: *
046: * @author Chris Holmes, TOPP
047: *
048: * @task REVISIT: The getSupported getUnsupported is clunky and dangerous, as
049: * clients could be using this and do an unpack, get the unsupported
050: * filter, and then do another unpack, and want to get the first
051: * supported filter, and would get the second instead. This is likely
052: * in a getFeatures when the unpacker is held by the class. So for now
053: * clients should construct an unpacker whenever they want to use it.
054: * This is obviously less than ideal. So this should be revisited. One
055: * way is fir the unpack methods to return FilterPairs, for the clients
056: * to deal with themselves. I'm not sure that this is the best
057: * semantic, and it exposes an inner class that really has no other use,
058: * so it could be nice to do it a better way. Another option is to have
059: * static methods SQLUnpacker.getSupported(Filter, splitType,
060: * capabilities), or something to that effect. Or non static, but pass
061: * the filter in each time. If anyone is looking at this class and has
062: * suggestions email the list, as we should think this through more, but
063: * I've other pressing tasks.
064: * @deprecated please use PostPreProcessFilterSplittingVisitor
065: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/jdbc/src/main/java/org/geotools/filter/SQLUnpacker.java $
066: */
067: public class SQLUnpacker {
068: /**
069: * FilterPair is an inner class, for holding the unsupported and supported
070: * filters
071: */
072: private FilterPair pair;
073:
074: /** The types of Filters that should be part of the supported Filter */
075: private FilterCapabilities capabilities;
076:
077: private FilterFactory ff = CommonFactoryFinder
078: .getFilterFactory(null);
079:
080: /**
081: * Constructor with FilterCapabilities from the Encoder used in conjunction
082: * with this Unpacker.
083: *
084: * @param capabilities what FilterTypes should be supported.
085: */
086: public SQLUnpacker(FilterCapabilities capabilities) {
087: this .capabilities = capabilities;
088: }
089:
090: /**
091: * Performs the unpacking of a filter, for the cases when ANDs can be split
092: * and ORs can not. To get the results of the unpacking getUnsupported
093: * and getSupported must be called before another unpacking is done.
094: *
095: * @param filter to be unpacked, split on ANDs
096: */
097: public void unPackAND(org.opengis.filter.Filter filter) {
098: pair = doUnPack(filter, AbstractFilter.LOGIC_AND);
099: }
100:
101: /**
102: * Performs the unpacking of a filter, for the cases when ORs can be split
103: * and ANDs can not. To get the results of the unpacking getUnsupported
104: * and getSupported must be called before another unpacking is done.
105: *
106: * @param filter to be unpacked, split on ANDs
107: */
108: public void unPackOR(Filter filter) {
109: pair = doUnPack(filter, AbstractFilter.LOGIC_OR);
110: }
111:
112: /**
113: * After an unPack has been called, returns the resulting Filter of the
114: * unsupported parts of the unPacked filter. If the unPacked filter is
115: * fully supported this returns a null, it is the client's responsibility
116: * to deal with it. If there are multiple unsupported subfilters they are
117: * ANDed together.
118: *
119: * @return A filter of the unsupported parts of the unPacked filter.
120: */
121: public Filter getUnSupported() {
122: return pair.getUnSupported();
123: }
124:
125: /**
126: * After an unPack has been called, returns the resulting Filter of the
127: * supported parts of the unPacked filter. If the unPacked filter is not
128: * supported at all this returns a null, it is the client's responsibility
129: * to deal with it. If there are multiple supported subfilters they are
130: * ANDed together.
131: *
132: * @return A filter of the supported parts of the unPacked filter.
133: */
134: public Filter getSupported() {
135: return pair.getSupported();
136: }
137:
138: /**
139: * Performs the actual recursive unpacking of the filter. Can do the
140: * unpacking on either AND or OR filters.
141: *
142: * @param filter the filter to be split
143: * @param splitType the short representation of the logic filter to
144: * recursively unpack.
145: *
146: * @return A filter of the unsupported parts of the unPacked filter.
147: */
148: private FilterPair doUnPack(org.opengis.filter.Filter filter,
149: short splitType) {
150: /*
151: * Implementation notes: This is recursive, so it's worth explaining.
152: * The base cases are either the filter is fully supported, ie all of
153: * its subFilters are supported, and thus it can be totally encoded,
154: * or it is not supported. The recursive cases are when the filter is
155: * not fully supported and it is an AND or a NOT filter. In these
156: * cases the filter can be split up, and each subfilter can return
157: * some supported filters and some unsupported filters. If it is an
158: * OR filter and not fully supported we can descend no further, as
159: * each part of the OR needs to be tested, we can't put part in the
160: * SQL statement and part in the filter. So if it is an AND filter,
161: * we get teh subFilters and call doUnPack on each subFilter,
162: * combining the Unsupported and Supported FilterPairs of each
163: * subFilter into a single filter pair, which is the pair we will
164: * return. If a subfilter in turn is an AND with its own subfilters,
165: * they return their own unSupported and Supported filters, because it
166: * will eventually hit the base case. The base cases return null for
167: * half of the filter pair, and return the filter for the other half,
168: * depending on if it's unsupported or supported. For the NOT filter,
169: * it just descends further, unpacking the filter inside the NOT, and
170: * then tacking NOTs on the supported and unsupported sub filters.
171: * ---addition: No longer just ANDs supported. ORs can be split,
172: * same as previous paragraph, but switch ORs with ANDs, there are
173: * cases, such as the delete statement, where we have to split on ORs
174: * and can't on ANDs (opposite of get statement). Should work the
175: * same, just a different logic filter.
176: */
177: FilterPair retPair;
178: FilterPair subPair;
179: Filter subSup = null; //for logic iteration
180: Filter subUnSup = null; //for logic iteration
181: Filter retSup = null; //for return pair
182: Filter retUnSup = null; //for return pair
183:
184: if (filter == null) {
185: return new FilterPair(null, null);
186: }
187:
188: if (capabilities.fullySupports(filter)) {
189: retSup = filter;
190: } else {
191: short type = Filters.getFilterType(filter);
192:
193: if ((type == splitType) && capabilities.supports(splitType)) {
194: //REVISIT: one special case not covered, when capabilities
195: //does not support AND and it perfectly splits the filter
196: //into unsupported and supported
197: Iterator filters = ((LogicFilter) filter)
198: .getFilterIterator();
199:
200: while (filters.hasNext()) {
201: Filter next = (Filter) filters.next();
202:
203: subPair = doUnPack(next, splitType);
204:
205: subSup = subPair.getSupported();
206: subUnSup = subPair.getUnSupported();
207: retSup = combineFilters(retSup, subSup, splitType);
208: retUnSup = combineFilters(retUnSup, subUnSup,
209: splitType);
210: }
211: } else if ((type == AbstractFilter.LOGIC_NOT)
212: && capabilities.supports(AbstractFilter.LOGIC_NOT)) {
213: Iterator filters = ((LogicFilter) filter)
214: .getFilterIterator();
215:
216: //NOT only has one, so just get filters.next()
217: subPair = doUnPack((Filter) filters.next(), splitType);
218: subSup = subPair.getSupported();
219: subUnSup = subPair.getUnSupported();
220:
221: if (subSup != null) {
222: retSup = ff.not(subSup);
223: }
224:
225: if (subUnSup != null) {
226: retUnSup = ff.not(subUnSup);
227: }
228: } else { //it's not supported and has no logic subfilters to be split.
229: retUnSup = filter;
230: }
231: }
232:
233: retPair = new FilterPair(retSup, retUnSup);
234:
235: return retPair;
236: }
237:
238: /**
239: * Combines two filters, which may be null, into one. If one is null and
240: * the other not, it returns the one that's not. If both are null returns
241: * null.
242: *
243: * @param filter1 one filter to be combined.
244: * @param filter2 the other filter to be combined.
245: * @param splitType the short representation of the logic filter to
246: * recursively unpack.
247: *
248: * @return the resulting combined filter.
249: */
250: private Filter combineFilters(Filter filter1, Filter filter2,
251: short splitType) {
252: Filter retFilter;
253:
254: if (filter1 != null) {
255: if (filter2 != null) {
256: if (splitType == AbstractFilter.LOGIC_AND) {
257: retFilter = Filters.and(ff, filter1, filter2);
258: } else { //OR and AND only split types, this must be or.
259: retFilter = Filters.or(ff, filter1, filter2);
260: }
261: } else {
262: retFilter = filter1;
263: }
264: } else {
265: if (filter2 != null) {
266: retFilter = filter2;
267: } else {
268: retFilter = null;
269: }
270: }
271:
272: return retFilter;
273: }
274:
275: /**
276: * An inner class to hold a pair of Filters. Reasoning behind inner class
277: * is that if made public it would clutter up the filter folder with a
278: * FilterPair, which seems like it might be widely used, but is only
279: * necessary for one class. To return filter pairs would be slightly
280: * cleaner, in terms of writing code, but this way is cleaner for the
281: * geotools filter module.
282: */
283: private class FilterPair {
284: /** half of the filter pair */
285: private Filter supported;
286:
287: /** the other half */
288: private Filter unSupported;
289:
290: /**
291: * Constructor takes the two filters of the pair.
292: *
293: * @param supported The filter that can be encoded.
294: * @param unSupported the filter that can't be encoded.
295: */
296: public FilterPair(Filter supported, Filter unSupported) {
297: this .supported = supported;
298: this .unSupported = unSupported;
299: }
300:
301: /**
302: * Accessor method.
303: *
304: * @return the supported Filter.
305: */
306: public Filter getSupported() {
307: return supported;
308: }
309:
310: /**
311: * Accessor method.
312: *
313: * @return the unSupported Filter.
314: */
315: public Filter getUnSupported() {
316: return unSupported;
317: }
318: }
319: }
|