Statistics
| Revision:

root / branches / Mobile_Compatible_Hito_1 / libFMap_data / src / org / gvsig / data / vectorial / filter / FilterUtils.java @ 21563

History | View | Annotate | Download (12.6 KB)

1
/*
2
 *    GeoTools - OpenSource mapping toolkit
3
 *    http://geotools.org
4
 *    (C) 2005-2006, GeoTools Project Managment Committee (PMC)
5
 *        
6
 *    This library is free software; you can redistribute it and/or
7
 *    modify it under the terms of the GNU Lesser General Public
8
 *    License as published by the Free Software Foundation;
9
 *    version 2.1 of the License.
10
 *
11
 *    This library is distributed in the hope that it will be useful,
12
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 *    Lesser General Public License for more details.
15
 */
16
package org.gvsig.data.vectorial.filter;
17

    
18
import java.awt.Color;
19
import java.lang.reflect.Constructor;
20
import java.lang.reflect.InvocationTargetException;
21
import java.util.List;
22

    
23
import org.opengis.filter.expression.Expression;
24
import org.opengis.filter.expression.Function;
25
import org.opengis.filter.expression.Literal;
26

    
27
/**
28
 * Utility class for working with Filters & Expression.
29
 * <p>
30
 * To get the full benifit you will need to create an instanceof
31
 * this Object (supports your own custom FilterFactory!). Additional
32
 * methods to help create expressions are available.
33
 * </p>
34
 * <p>
35
 * Example use:
36
 * <pre><code>
37
 * Filters filters = new Filters( factory );
38
 * filters.duplicate( origional );
39
 * </code></pre>
40
 * The above example creates a copy of the provided Filter,
41
 * the factory provided will be used when creating the duplicated
42
 * content.
43
 * </p>
44
 * <h3>Expression</h3>
45
 * <p>
46
 * Expressions form an interesting little semi scripting languge,
47
 * intended for queries.  A interesting Feature of Filter as a language
48
 * is that it is not strongly typed. This utility class many helper
49
 * methods that ease the transition from Strongly typed Java to the more
50
 * relaxed setting of Expression where most everything can be a string.
51
 * </p>
52
 * <pre><code>
53
 * double sum = Filters.number( Object ) + Filters.number( Object );
54
 * </code></pre>
55
 * The above example will support the conversion of many things into a format
56
 * suitable for addition - the complete list is something like:
57
 * <ul>
58
 * <li>Any instance of Number
59
 * <li>"1234" - aka Integer
60
 * <li>"#FFF" - aka Integer 
61
 * <li>"123.0" - aka Double
62
 * </ul>
63
 * A few things (like Geometry and "ABC") will not be considered addative.
64
 * </p>
65
 * In general the scope of these functions should be similar to that
66
 * allowed by the XML Atomic Types, aka those that can be seperated by
67
 * whitespace to form a list.
68
 * </p>
69
 * <p>
70
 * We do our best to be forgiving, any Java class which takes a String as
71
 * a constructor can be tried, and toString() assumed to be the inverse. This
72
 * lets many things (like URL and Date) function without modification.
73
 * </p>
74
 * 
75
 * @author Jody Garnett, Refractions Research
76
 * @since GeoTools 2.2.M3
77
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.2/modules/library/main/src/main/java/org/geotools/filter/Filters.java $
78
 */
79
public class FilterUtils {
80
        /** <code>NOTFOUND</code> indicates int value was unavailable */
81
        public static final int NOTFOUND = -1;
82
        
83
//        
84
//    /**
85
//     * Deep copy the filter.
86
//     * <p>
87
//     * Filter objects are mutable, when copying a rich
88
//     * data structure (like SLD) you will need to duplicate
89
//     * the Filters referenced therein.
90
//     * </p>
91
//     */
92
//    public Filter duplicate( Filter filter ){
93
//            DuplicatorFilterVisitor xerox = new DuplicatorFilterVisitor( ff );        
94
//            FilterUtils.accept( filter, xerox );
95
//            return (Filter) xerox.getCopy();
96
//            
97
//    }
98
        
99
    
100
    /**
101
     * Uses number( expr ), will turn result into an interger, or NOTFOUND
102
     *
103
     * @param expr
104
     *
105
     * @return int value of first Number, or NOTFOUND
106
     */
107
    public static int asInt( Expression expr ) {
108
        Number number = (Number) asType(expr, Number.class);
109
         
110
        if (number != null) {
111
            return number.intValue();
112
        }
113

    
114
        //look for a string
115
        String string = (String) asType(expr,String.class);
116
        if (string != null) {
117
                //try parsing into a integer
118
                try {
119
                        return Integer.parseInt(string);
120
                }
121
                catch(NumberFormatException e) {}
122
        }
123
        
124
        //no dice
125
        return NOTFOUND;
126
    }
127

    
128
    /**
129
     * Uses string( expr ), will turn result into a String
130
     *
131
     * @param expr
132
     *
133
     * @return value of first String
134
     */
135
    public static String asString(Expression expr) {
136
        String string = (String) asType(expr, String.class);
137

    
138
        return string;
139
    }
140

    
141
    /**
142
     * Uses number( expr ), will turn result into an interger, or NaN.
143
     *
144
     * @param expr
145
     *
146
     * @return int value of first Number, or Double.NaN
147
     */
148
    public static double asDouble(Expression expr) {
149
        Number number = (Number) asType(expr, Number.class);
150

    
151
        if (number != null) {
152
            return number.doubleValue();
153
        }
154
        
155
        //try for a string
156
        String string = (String) asType(expr,String.class);
157
        if (string != null) {
158
                //try parsing into a double
159
                try {
160
                        return Double.parseDouble(string);
161
                }
162
                catch(NumberFormatException e) {}
163
        }
164

    
165
        //too bad
166
        return Double.NaN;
167
    }
168
    
169
    /**
170
     * Navigate through the expression seaching for TYPE.
171
     * 
172
     * <p>
173
     * This will work even with dynamic expression that would normall require a
174
     * feature. It works especially well when the Expression is a Literal
175
     * literal (which is usually the case).
176
     * </p>
177
     * 
178
     * <p>
179
     * If you have a specific Feature, please do this:
180
     * <pre><code>
181
     * Object value = expr.getValue( feature );
182
     * return value instanceof Color ? (Color) value : null;
183
     * </code></pre>
184
     * </p>
185
     *
186
     * @param expr This only really works for downcasting literals to a value
187
     * @param Target type
188
     *
189
     * @return expr smunched into indicated type
190
     */
191
    public static Object asType(Expression expr, Class TYPE) {
192
        // TODO use the new converters stuff
193
        if (expr == null) {
194
            return null;
195
        }
196
        else if (expr instanceof Literal) {
197
                        Literal literal = (Literal) expr;
198
            Object value = literal.getValue();
199

    
200
            if (TYPE.isInstance(value)) {
201
                return value;
202
            }
203
        }
204
        else if (expr instanceof Function) {
205
                        Function function = (Function) expr;
206
                        List params = function.getParameters();
207
            // JG - fix me this looks really wrong?
208
            // taking the first parameter that matches?
209
            if ( params != null && params.size() != 0 ) {
210
                for (int i = 0; i < params.size(); i++) {
211
                    Expression e = (Expression) params.get(i);
212
                    Object value = asType(e, TYPE);
213

    
214
                    if (value != null) {
215
                        return value;
216
                    }
217
                }
218
            }
219
        }
220
        else {
221
            try { // this is a bad idea, not expected to work much
222
                Object value = expr.evaluate(null, TYPE );
223

    
224
                if (TYPE.isInstance(value)) {
225
                    return value;
226
                }
227
            } catch (NullPointerException expected) {
228
                return null; // well that was not unexpected
229
            } catch (Throwable ignore) { // I did say that was a bad idea                
230
            }
231
        }
232
        return null; // really need a Feature to acomplish this one
233
    }
234
    
235
    /**
236
     * Treat provided value as a Number, used for math opperations.
237
     * <p>
238
     * This function allows for the non stongly typed Math Opperations
239
     * favoured by the Expression standard.
240
     * </p>
241
     * <p>
242
     * Able to hanle:
243
     * <ul>
244
     * <li>null - to NaN
245
     * <li>Number
246
     * <li>String - valid Integer and Double encodings
247
     * </ul>
248
     * 
249
     * </p>
250
     * @param value
251
     * @return double or Double.NaN;
252
     * @throws IllegalArgumentException For non numerical among us -- like Geometry 
253
     */
254
    public static double number(Object value) {
255
            if( value == null ) return Double.NaN;
256
            if( value instanceof Number ){
257
                    Number number = (Number) value;
258
                    return number.doubleValue();
259
            }
260
            if( value instanceof String ){
261
                    String text = (String) value;
262
                    try {
263
                                Number number = (Number) gets( text, Number.class );
264
                                return number.doubleValue();
265
                        } catch (Throwable e) {
266
                                throw new IllegalArgumentException("Unable to decode '"+text+"' as a number" );                                
267
                        }                    
268
            }
269
            if( value instanceof Expression ){
270
                    throw new IllegalArgumentException("Cannot deal with un evaulated Expression");
271
            }
272
            throw new IllegalArgumentException("Unable to evaulate "+value.getClass()+" in a numeric context");
273
    }
274
    
275
    /**
276
     * Used to upcovnert a "Text Value" into the provided TYPE.
277
     * <p>
278
     * Used to tread softly on the Java typing system, because
279
     * Filter/Expression is not strongly typed. Values in in
280
     * Expression land are often not the the real Java Objects
281
     * we wish they were - it is reall a small, lax, query
282
     * language and Java objects need a but of help getting
283
     * through.
284
     * <p>
285
     * </p>
286
     * A couple notes:
287
     * <ul>
288
     * <li>Usual trick of reflection for a Constructors that
289
     *     supports a String parameter is used as a last ditch effort.
290
     *     </li>
291
     * <li>will do its best to turn Object into the indicated Class
292
     * <li>will be used for ordering literals against attribute values
293
     *     are calculated at runtime (like Date.)
294
     * </ul>
295
     * Remember Strong typing is for whimps who know what they are
296
     * doing ahead of time. Real programmers let their program
297
     * learn at runtime... :-)
298
     * </p>
299
     * 
300
     * @param text
301
     * @param TYPE
302
     * @throws open set of Throwable reflection for TYPE( String ) 
303
     */
304
    public static Object gets( String text, Class TYPE ) throws Throwable {
305
            if( text == null ) return null;
306
            if( TYPE == String.class ) return text;
307
            if( TYPE == Integer.class ) {
308
                    return Integer.decode( text );                    
309
            }
310
            if( TYPE == Double.class ){
311
                    return Double.valueOf( text );
312
            }
313
            if( TYPE == Number.class ){
314
                    try {
315
                            return Double.valueOf( text );
316
                    }
317
                    catch( NumberFormatException ignore ){
318
                    }
319
                    return Integer.decode( text );                    
320
            }
321
            if( TYPE == Color.class ){
322
                    return new Color( Integer.decode( text ).intValue() );
323
            }            
324
            try {
325
                        Constructor create = TYPE.getConstructor( new Class[]{String.class});
326
                        return create.newInstance( new Object[]{ text } );
327
                } catch (SecurityException e) {
328
                        // hates you
329
                } catch (NoSuchMethodException e) {
330
                        // nope
331
                } catch (IllegalArgumentException e) {
332
                        // should not occur
333
                } catch (InstantiationException e) {
334
                        // should not occur, perhaps the class was abstract?
335
                        // eg. Number.class is a bad idea
336
                } catch (IllegalAccessException e) {
337
                        // hates you
338
                } catch (InvocationTargetException e) {
339
                        // should of worked but we got a real problem,
340
                        // an actual problem
341
                        throw e.getCause();
342
                }            
343
            return null;
344
    }
345
    
346
    public static String puts( double number ){
347
            if( Math.rint(number) == number ){
348
                    return Integer.toString( (int) number );
349
            }
350
            return Double.toString( number );            
351
    }
352
    /**
353
     * Inverse of eval, used to softly type supported
354
     * types into Text for use as literals.
355
     */
356
    public static String puts( Object obj ){
357
            if( obj == null ) return null;
358
            if( obj instanceof String) return (String) obj;            
359
            if( obj instanceof Color ){
360
                    Color color = (Color) obj;
361
                    return puts( color );
362
            }
363
            if( obj instanceof Number ){
364
                    Number number = (Number) obj;
365
                    return puts( number.doubleValue() );                    
366
            }
367
            return obj.toString();
368
    }
369
    
370
    public static String puts( Color color ){
371
            String redCode = Integer.toHexString(color.getRed());
372
        String greenCode = Integer.toHexString(color.getGreen());
373
        String blueCode = Integer.toHexString(color.getBlue());
374

    
375
        if (redCode.length() == 1) redCode = "0" + redCode;
376
        if (greenCode.length() == 1) greenCode = "0" + greenCode;
377
        if (blueCode.length() == 1) blueCode = "0" + blueCode;
378
        
379
        return "#" + redCode + greenCode + blueCode;  
380
    }
381
}
382