From: <svn...@os...> - 2011-03-30 15:31:19
|
Author: jive Date: 2011-03-30 08:31:06 -0700 (Wed, 30 Mar 2011) New Revision: 36812 Modified: trunk/docs/user/guide/library/main/data.txt trunk/docs/user/guide/library/opengis/index.txt trunk/docs/user/guide/library/opengis/model.txt trunk/modules/library/api/src/main/java/org/geotools/util/Converters.java trunk/modules/library/main/src/main/java/org/geotools/data/DataUtilities.java trunk/modules/library/main/src/main/java/org/geotools/filter/FilterAttributeExtractor.java trunk/modules/library/main/src/main/java/org/geotools/filter/Filters.java trunk/modules/library/main/src/test/java/org/geotools/filter/FilterAttributeExtractorTest.java trunk/modules/library/metadata/src/main/java/org/geotools/factory/GeoTools.java Log: Fix up javadocs and warnings for Converters, lots of Javadocs for DataUtilities and FilterAttributeExtractor, and start to work on DataUtilities docs for the user guide Modified: trunk/docs/user/guide/library/main/data.txt =================================================================== --- trunk/docs/user/guide/library/main/data.txt 2011-03-30 14:02:13 UTC (rev 36811) +++ trunk/docs/user/guide/library/main/data.txt 2011-03-30 15:31:06 UTC (rev 36812) @@ -1,2 +1,419 @@ -data ----- \ No newline at end of file +DataUtilities +------------- + +Working with GIS data can be difficulty. Working with the number of classes in the GeoTools library can also be a bit intimidating. The **DataUtilitis** a facade classes which can help simplify common data wrangling chores. + +In almost all cases the methods for this class act as a front end for the classes provided by gt-main. This documentation makes note of the classes used internally so you can learn how the library is put together and can go look for more control if needed. + +If you do have any commonly used data hacks in your code; please submit a patch request to the issue tracker and we can add more useful methods here. + +References: + +* `DataUtilities <http://docs.geotools.org/latest/javadocs/org/geotools/data/DataUtilities.html>`_ + +URL and File +^^^^^^^^^^^^ + +DataUtilities also gathers up common solutions to problems encountered in Java, with that in mind we have a number of methods for safely handling files and URLs. + +* DataUtilties.fileToURL( file ) +* DataUtilities.urlToFile( url ) + +One of the changes between Java 5 and Java 6 is the deprecation of the File.toURL() method. Earlier version of Java implemented this method incorrectly, and Java 6 offers File.toURI().toURL() as a replacement. + +Problem is that does not help anyone - since when we are provided a URL we cannot be sure if it was constructed correctly. + +These two methods have been extensively tested and can handle odd corner cases such +as files located on windows shared folders. + +We use this method internally to make sure we can understand each URL that is handed to the library. It is available for use in your application as well. + +Here is a scary example of a windows network share:: + + File file = new File("\\\\host\\share\\file"); + URL url = DataUtilities.fileToURL( file ); + +* changeUrlExt(URL, String) +* getParentUrl(URL) +* extendURL(URL, String) + +* checkFileReadable(File, Logger) +* checkDirectory(File) +* excludeFilters(FilenameFilter, FilenameFilter...) +* includeFilters(FilenameFilter, FilenameFilter...) + + Allows FilenameFilters to be combined. + +FeatureType +^^^^^^^^^^^ + +Working with FeatureType can be a bit troubling, as much like Java String it is immutable (cannot be modified once created). + +* reType(SimpleFeatureType, SimpleFeature) +* reType(SimpleFeatureType, SimpleFeature, boolean) + +FeatureType Creation +'''''''''''''''''''' + +DataUtilities provides the following methods to help you quickly create and modify FeatureType information. + +* DataUtilities.createType( typeName, specification ) +* DataUtilities.createType( namespace, typeName, specification) + + This is great for quickly whipping up a FeatureType when making test cases. For more + control use SimpleFeatureTypeBuilder as described in gt-opengis. + + If you just want to quickly describe some information:: + + FeatureType lineType = DataUtilities.createType("LINE", "centerline:LineString,name:\"\",id:0"); + + I admit that looks a bit strange, you can also use a Java class names if it makes you happy:: + + FeatureType schema = DataUtilities.createType("EDGE", "edge:Polygon,name:String,timestamp:java.util.Date"); + + If you need to set the coordinate reference system as well:: + + FeatureType lineType = DataUtilities.createType("LINE", "centerline:LineString:srid=32615,name:\"\",id:0"); + + If you are into names spaces that can be handled as well:: + + FeatureType lineType = DataUtilities.createType("http://somewhere.net/","LINE", "centerline:LineString,name:\"\",id:0"); + + Now we don't want to see you writing code to build up your initial "definition" String, that means you are doing something + general (and dynamic!) and should go figure out SimpleFeatureTypeBuilder and SimpleFeatureBuilder. + +* DataUtilities.spec(FeatureType) + + You can use this method to quickly get a text representation of a FeatureType:: + + System.out.println("FeatureType: " + DataUtilities.spec(featureType)); + + The representation is the same one used by createType above. + +FeatureType Modify +'''''''''''''''''' + +Because a FeatureType cannot be modified once created; all of the following methods +return a modified copy. + +* DataUtilties.createSubType(SimpleFeatureType, String[], CoordinateReferenceSystem) +* DataUtilties.createSubType(SimpleFeatureType, String[], CoordinateReferenceSystem, String, URI) +* DataUtilties.createSubType(SimpleFeatureType, String[]) + + Used to quickly produce a (slightly modified) copy of the provided FeatureType. Used to + recast a FeatureType with a desired CoordinateReferenceSystem or limit a FeatureType + to a specific list of attributes. + + There are actually a couple subType methods depending on how complicated you want to get.:: + + FeatureType schema = DataUtilities.createType("EDGE", "edge:Polygon,name:String"); + CoordinateReferenceSystem crs = CRS.decode( "EPSG:4326" ); + + schema = DataUtilties.createSubType( schema, null, crs ); + + You can also get a bit more complicated and choose exactly which attributes you want.:: + + FeatureType schema = DataUtilities.createType("EDGE", "edge:Polygon,name:String,timestamp:java.util.Date"); + + schema = DataUtilities.subType( schema, new String[]{"edge","name"}, null ); + +FeatureType Summary +''''''''''''''''''' + +FeatureType forms an interesting little data structure as shown in the gt-opengis diagrams. + +The following methods traverse this data structure for you building up a summary to answer specific questions. + +* DataUtilities.addMandatoryProperties( schema, propertyNames ) + + Used to review a FeatureType and add the required properties to an existing list:: + + List<PropertyName> requiredProperties = addMandatoryProperties( schema, null ); + +* compare(schema1, schema2) + + The retype methods allow us to start with an existing FeatureType and produced a + simplified or modified copy. + + This method compares two feature types to sort out if one is a simplification + of the other. + + ====== ========================================================== + value compare + ====== ========================================================== + +1 if schema1 is a sub type/reorder/renamespace of schema2 + 0 if schema1 and schema2 are the same + -1 if schema1 and schema2 are not related + ====== ========================================================== + +* isMatch(AttributeDescriptor, AttributeDescriptor) + + Used to check if values from the two attribute descriptors have a hope of matching. + Both the name and the binding to a Java class are checked. + +Feature +^^^^^^^ + +These methods help you work with the values stored in an individual feature. They can quickly produce a set of default values, or parse a set of provided strings into the +correct Java Objects as needed. + +* template(SimpleFeatureType) +* template(SimpleFeatureType, String) +* template(SimpleFeatureType, Object[]) +* template(SimpleFeatureType, String, Object[]) + + These template methods create a new Feature using of sensible default values + in the event the user supplied null for a mandatory value. + +* defaultValues(SimpleFeatureType) +* defaultValues(SimpleFeatureType, Object[]) +* defaultValue(AttributeDescriptor) +* defaultValue(Class) + + These methods provide sensible default values that are a good starting point for + data entry (if you are giving your user a chance to enter new a new feature by hand). + +* parse(SimpleFeatureType, String, String[]) + + You can quickly parse out a new feature from input text:: + + SimpleFeature feature = DataUtilities.parse( schema, fid, text ); + +* duplicate(Object) + + Performs a deep copy of the provided attribute value. + It is aware of JTS Geometry, and GeoTools constructs such as SimpleFeature + and will take appropriate measures. + +* attributesEqual(Object, Object) + + You can safely compare if two attribute values are equal (without worrying about + Geometry behaving funny):: + + DataUtilities.attributesEquals( feature1.getAttribute(1), feature2.getAttribute(1) ); + +FeatureCollection +^^^^^^^^^^^^^^^^^ + +Creating Features has gotten a lot easier with the advent of SimpleFeatureTypeBuilder. + +You can still run into situations where adapting feature data is useful when calling methods. The DataUtilities class can help by providing wrappers taking your feature information from Arrays, Collections, FeatureReaders and other + +In general we try and work with FeatureReader and FeatureIterator (as these support the +idea of "streaming" information larger than memory). You will find some areas of the code that want to load everything into memory (either as a Collection or Array) often for analysis. + +**FeatureCollection** + +FeatureCollection is used a lot in GeoTools code giving you a chance to use the following methods. + +* DataUtilities.collection( FeatureCollection ) - copies into memory! +* DataUtilities.collection( FeatureReader ) - copies into memory! +* DataUtilities.collection( List<SimpleFeature> ) +* DataUtilities.collection( SimpleFeature ) +* DataUtilities.collection( SimpleFeature[] ) +* DataUtilities.collection( SimpleFeatureIterator ) + +DataUtilities has helper methods to turn almost anything into a FeatureCollection, this +is really helpful when working with an API that expects a FeatureCollection.:: + + Feature[] array; + ... + return DataUtilties.collection( array ); + +These methods are often used to add a single SimpleFeature to a FeatureStore:: + + featureStore.addFeatures( DataUtilities.collection( newFeature ) ); + +Do be careful some of these implementations suck everything into memory! With GIS +data sizes this will eventually break your application. + +* results(SimpleFeature[]) +* results(SimpleFeatureCollection) +* results(FeatureCollection<T, F>) + +These methods convert to a FeatureCollection; but with a twist. They will +produce an error (rather than an empty collection) if the input is null or empty. + +In GeoTools 2.0 FeatureCollection was called **FeatureResults**, these methods are +left over from that time. + +**FeatureCollections** + +Internally these methods use the **FeatureCollections** class to create collection +to hold the content:: + + SimpleFeatureCollection collection = FeatureCollections.newCollection(); + for (SimpleFeature feature : list) { + collection.add(feature); + } + +FeatureSource +''''''''''''' + +FeatureSource is a very capable class, in the worst case the wrappers provided here may need to load everything into memory to get the job done. + +* source(SimpleFeature[]) + + Will wrap a FeatureSource around the provided array allowing the features to + be queried. + +* source(FeatureCollection<SimpleFeatureType, SimpleFeature>) + + In a similar fashion a FeatureSource is wrapped around the provided collection. + + There are optimised implementations for: + + * ListFeatureCollection + * SpatialIndexFeatureCollection + * TreeSetFeatureCollection + + And as fall back of copying everything into a memory using a CollectionDataStore + to hold the resulting feature source. + +* createView(DataStore, Query) + + Creates a light weight "view" that focuses on combining the provided query with + any requests made to the resulting featureSource. + +FeatureReader +''''''''''''' + +FeatureReader is the best class we have to represent the nature of streaming large +quantities of information off disk and through your program. + +The following methods allow you to simulate a FeatureReader using information you +happen to have in memory. + +* DataUtilities.reader(Collection<SimpleFeature>) +* DataUtilities.reader(FeatureCollection) +* DataUtilities.reader(SimpleFeature[]) + +The FeatureReader interface works in a similar manner to Iterator<Feature> with +the benefit of IOExceptions in case you are streaming from disk. + +DataUtilities sill lets you adapt your own collection to this format.:: + + FeatureCollection collection; + + return DataUtilities.reader( collection ); + +Summary +''''''' + +The following methods provide a summary of feature information; often gathering up +boilerplate code that you would otherwise need to cut and paste into your application. + +* DataUtilities.list(FeatureCollection) + + Loads the FeatureCollection into a normal java.util.List + +* DataUtilities.fidSet(FeatureCollection<?, ?>) + + Goes through the FeatureCollection and produced the set of FeatureIds as a simple + Set<String>. + + These identifiers can be used to retrieve features individually. + + An example use is displaying features in a table; by getting a set of identifiers + you can uniquely identify each row, and then only query for the contents of the + features displayed on screen as needed. + +* DataUtilities.bounds(FeatureCollection<? extends FeatureType, ? extends Feature>) + + There are methods to quickly get the bounds from a FeatureSource or FeatureCollection, + but these methods are implementation dependent often making use of header information + or summarising the spatial index in order to get you an answer quickly. + + Use this method to go through each feature one by one and compute a bounds. + +Cast +'''' + +In GeoTools 2.7.0 we introduced the idea of a SimpleFeatureCollection (which is a short hand for FeatureCollection<SimpleFeatureType,SimpleFeature>). + +While this really with learning the library (and the amount of typing required to use it) +we needed to introduce the following methods to help people safely "cast" to a SimpleFeatureCollection when they had a FeatureCollection. + +* DataUtilities.simple(FeatureCollection<SimpleFeatureType, SimpleFeature>) +* DataUtilities.simple(FeatureSource) +* DataUtilities.simple(FeatureStore) +* DataUtilities.simple(FeatureType) +* DataUtilities.simple(FeatureLocking) + +In practice these end up being easy to use:: + + SimpleFeatureCollection features = DataUtilities.simple( collection ); + +Query +^^^^^ + +A common task with gt-main is preparing a Query against a FeatureSource. DataUtilities has a number of methods to help. + +* DataUtilities.mixQueries(Query, Query, String) + + Safely combines two queries in a sensible manner. + +* DataUtilities.resolvePropertyNames(Query, SimpleFeatureType) +* DataUtilities.resolvePropertyNames(Filter, SimpleFeatureType) +* DataUtilities.addMandatoryProperties(SimpleFeatureType, List<PropertyName>) + +SortBy +^^^^^^ + +* DataUtilities.sortComparator(SortBy) + + Creates a Comparator that can be used to sort features as indicated by the Query + SortBy provided. + +Filter and Expression +^^^^^^^^^^^^^^^^^^^^^ + +Part of the fun of preparing a Query is ensuring you ask for the correct values to +perform the task you have in mind. + +We have a number of methods to list required attributes for a Filter or Expression: + +* DataUtilities.atttributeNames( Filter ) +* DataUtilities.atttributeNames( Filter, FeatureType ) +* DataUtilities.attributenNames( Expression ) +* DataUtilities.attributenNames( Expression, FeatureType ) + + The optional FeatureType is used as a reference point and can resolve any ambiguities + between the simple xpath expressions, and the names used in the FeatureType. + + Here is an example of using this information to request a FeatureCollection that + has the required attributes to evaluate the provided expression for every feature.:: + + String attributes[] = DataUtilities.attributeNames( expression ); + Query query = new Query( typeName, Filter.ALL, attributes ); + + SimpleFeatureCollection results = featureSource.features( query ); + +* DataUtilities.propertyNames( Filter ) +* DataUtilities.propertyNames( Filter, FeatureType ) +* DataUtilities.propertyNames( Expression ) +* DataUtilities.propertyNames( Expression, FeatureType ) + + A similar batch of methods using FilterAttributeExtractor to retrieve a + Set<PropertyName>. + + Using a PropertyName is slightly more useful when considering complex XPath + expressions that use namespaces. + +**FilterAttributeExtractor** + +Internally the above methods use FilterAttributeExtractor. You can use this class for +greater control.:: + + FilterAttributeExtractor extract = new FilterAttributeExtractor(null); + + Set<String> names = new HashSet<String>(); + // used to collect names from expression1, expression2, and filter + + expression1.accept(extract, names); + expression2.accept(extract, names); + filter.accept(extract, names); + + String array[] = extract.getAttributeNames(); + Set<String> attributes = extract.getAttributeNameSet(); + Set<PropertyName> properties = extract.getPropertyNameSet(); \ No newline at end of file Modified: trunk/docs/user/guide/library/opengis/index.txt =================================================================== --- trunk/docs/user/guide/library/opengis/index.txt 2011-03-30 14:02:13 UTC (rev 36811) +++ trunk/docs/user/guide/library/opengis/index.txt 2011-03-30 15:31:06 UTC (rev 36812) @@ -18,26 +18,18 @@ parameter unit -GeoTools is all about implementing spatial solutions, and we do our very best to follow *a don't invent here* policy (to avoid going off topic). By referencing standards we are able to use well understood names for common spatial ideas and constructs. +GeoTools is all about implementing spatial solutions, and we do our very best to follow a don't invent here policy (rather than get off topic). By referencing standards we are able to use well understood names for common spatial ideas and constructs. For more information on the standards covered by the library as whole: * :ref:`standards` +Historic relationship with "GeoAPI": -History of GeoTools relationship with GeoAPI --------------------------------------------- - -* GeoAPI was started in 2002 by James McGill (who also set up GeoTools). The was to provide common a API for independent projects like GeoTools, deegree and OpenJump, which would allow the easy exchange of code. - -* Later on, the Open Geospatial Consortium started the "GO-1" project which had a similar goal. The "Geospatial Object" project was led by the Polexis company which was, at the time, based in the United States. - -* Given the similarity between the goals of GO-1 and GeoAPI, the two projects were merged. - -* Polexis produced an official OGC specification based on GeoAPI 2.0: http://www.opengeospatial.org/standards/go - -* Later Polexis was bought by Sys Technology and discontinued its involvement in GeoAPI / GO-1. - +* GeoAPI was started in 2002 James McGill (who also set up GeoTools). The aim at that time was to provide common API for independent projects like GeoTools, deegree and OpenJump, allowing the easy exchange of code. +* Later on, the Open Geospatial consortium started the "GO-1" project with a similar goal. The "Geospatial Object" project was led Polexis which was at the time based in the United States. +* Givin the similarity between GO-1 and GeoAPI goals, we got in touch each other and managed to merge the two projects. +* Polexis produces the following official OGC specification based on GeoAPI 2.0: http://www.opengeospatial.org/standards/go +* Later Polexis was bough by Sys Technology, with the new owner and priorites their investment in GeoAPI / GO-1 stopped. * The GO-1 / GeoAPI working group at OGC was dissolved du to lack of activity. - -* GeoTools contributors gradually took over the GeoAPI project and in GeoTools 2.7 folded these interfaces back into the GeoTools OpenGIS module. +* GeoTools contributors gradually over the GeoAPI project and in GeoTools 2.7 folded these interfaces back into the GeoTools OpenGIS module Modified: trunk/docs/user/guide/library/opengis/model.txt =================================================================== --- trunk/docs/user/guide/library/opengis/model.txt 2011-03-30 14:02:13 UTC (rev 36811) +++ trunk/docs/user/guide/library/opengis/model.txt 2011-03-30 15:31:06 UTC (rev 36812) @@ -4,8 +4,8 @@ GeoTools provides a clear separation between: * data model - **feature** responsible for holding values -* query model - **filter** and **expression** used to select content and retrieve values -* metadata model - **feature type** describing contents in sufficient detail for validation and query construction +* query model - **filter** and **expression** used to select content and drill in and retrieve values +* metadata model - **feature type** describing contents in sufficient details for validation and query construction References: @@ -45,7 +45,7 @@ Filter ^^^^^^ -Use **Filter** to select features that are of interest and the **Expression** to drill down and access information. +Use **Filter** to select features that are of interest and the **Expression** to drill in and access information. .. image:: /images/filter_expression.PNG @@ -69,11 +69,11 @@ There are a couple other ways to access feature content: -* You can construct a filter or expression using the common query language (CQL) +* You can construct a filter or expression using the common query language * You can construct a filter or expression from XML * You can access the feature data structure directly -Here is the same example using CQL and direct feature access:: +Here is the same example using common query language and direct feature access:: Filter filter = CQL.toFilter( "AGE < 12 " ); @@ -89,19 +89,19 @@ FeatureType ^^^^^^^^^^^ -**FeatureType** provides a metadata model, ie. a description of the information stored in the features. +**FeatureType** provides metadata model describing the represented information. This is considered "metadata" as it is a description of the information stored in the features. -FeatureType is used: +FeatureType is used to: -* to access a description of the available attribute names when making a Expression -* when creating a new feature, to ensure that your values are valid +* When accessing information as a description of the available attribute names when making a Expression +* When creating a new feature you can check to ensure your values are valid The type is represented by PropertyType, AttributeType, GeometryType, ComplexType, FeatureType. .. image:: /images/feature_type_model.PNG -This forms a "dynamic type system" indicating that we can describe new types of information at runtime. To make this a complete type system we have support for references (with AssociationType) and methods (with OperationType) although use of these faciities is considered experimental at present. +This forms a "dynamic type system" indicating we can describe new types of information at runtime. To make this a complete type system we have support for references (with AssociationType) and methods (with OperationType) although use of these faciities is considered experimental at present. As shown above a ComplexType contains a list of **properties** each represented as a PropertyDescriptor with a distinct name and property type. @@ -112,17 +112,16 @@ Simple Feature ^^^^^^^^^^^^^^ -Most GIS data does not need a full dynamic type system with associations, operations, multiple values. With this in mind we have a "simple" extension of Feature and FeatureType to represent this kind of information. +Most GIS data does not need a full dynamic type system with associations, operations, multiple values. With this in mind we have a "simple" extension of Feature and FeatureType providing to represent this kind of information. -SimpleFeature may be used when a feature's properties are limited to mandatory GeometryAttribute and Attribute. The following conditions apply: +SimpleFeature may be used when: -* No complex attributes or multiplicity is allowed. -* Attribute values may be null, but each attribute must be represented. -* The order of attribute values is significant, ie. values can be retrieved by attribute name or by attribute order. +* a feature's properties are limited to mandatory GeometryAttribute and Attribute (no complex attributes or multiplicity allowed). +* Attributes values may be null; but each attribute must be represented +* order of attribute values is considered significant allowing values to be looked up by attribute name, or by the order they are listed +* These restrictions match the abilities of a simple shapefile or database table -These restrictions match the abilities of a simple shapefile or database table. - .. image:: /images/feature_simple.PNG Here is an example of constructing a SimpleFeatureType:: Modified: trunk/modules/library/api/src/main/java/org/geotools/util/Converters.java =================================================================== --- trunk/modules/library/api/src/main/java/org/geotools/util/Converters.java 2011-03-30 14:02:13 UTC (rev 36811) +++ trunk/modules/library/api/src/main/java/org/geotools/util/Converters.java 2011-03-30 15:31:06 UTC (rev 36812) @@ -42,10 +42,10 @@ private static final Logger LOGGER = Logging.getLogger(Converters.class); - /** - * Cached list of converter factories - */ - static ConverterFactory[] factories; + /** + * Cached list of converter factories + */ + static ConverterFactory[] factories; /** * The service registry for this manager. @@ -65,25 +65,25 @@ } return registry; } +// /** +// * Used to combine provided hints with global GeoTools defaults. +// * +// * @param hints +// * @return +// */ +// private static Hints addDefaultHints(final Hints hints) { +// return GeoTools.addDefaultHints(hints); +// } - private static Hints addDefaultHints(final Hints hints) { - final Hints completed = GeoTools.getDefaultHints(); - if (hints != null) { - completed.add(hints); - } - return completed; - } - - /** * Returns a set of all available implementations for the {@link ConverterFactory} interface. * * @param hints An optional map of hints, or {@code null} if none. * @return Set of available ConverterFactory implementations. */ - public static synchronized Set getConverterFactories(Hints hints) { - hints = addDefaultHints(hints); - return new LazySet(getServiceRegistry().getServiceProviders( + public static synchronized Set<ConverterFactory> getConverterFactories(Hints hints) { + hints = GeoTools.addDefaultHints(hints); + return new LazySet<ConverterFactory>(getServiceRegistry().getServiceProviders( ConverterFactory.class, null, hints)); } @@ -100,90 +100,102 @@ * * @since 2.5 */ - public static Set<ConverterFactory> getConverterFactories( Class source, Class target ) { - HashSet factories = new HashSet(); + public static Set<ConverterFactory> getConverterFactories( Class<?> source, Class<?> target ) { + HashSet<ConverterFactory> factories = new HashSet<ConverterFactory>(); for (ConverterFactory factory : factories()) { if ( factory.createConverter( source, target, null ) != null ) { factories.add( factory ); } } - return factories; } - /** - * Convenience for {@link #convert(Object, Class, Hints)} - * @param source The object to convert. - * @param target The type of the converted value. - * @return The converted value as an instance of target, or <code>null</code> if a converter - * could not be found - * @since 2.4 - */ - public static <T> T convert( Object source, Class<T> target ) { - return convert( source, target, null ); - } + /** + * Converts an object of a particular type into an object of a different type. + * <p> + * Convenience for {@link #convert(Object, Class, Hints)} + * + * @param source + * The object to convert. + * @param target + * The type of the converted value. + * @return The converted value as an instance of target, or <code>null</code> if a converter + * could not be found + * @since 2.4 + */ + public static <T> T convert(Object source, Class<T> target) { + return convert(source, target, null); + } - /** - * Converts an object of a particular type into an object of a differnt type. - * <p> - * This method uses the {@link ConverterFactory} extension point to find a converter capable - * of performing the conversion. The first converter found is the one used. Using this class - * there is no way to guarantee which converter will be used. - * </p> - * @param source The object to convert. - * @param target The type of the converted value. - * @param hints Any hints for the converter factory. - * - * @return The converted value as an instance of target, or <code>null</code> if a converter - * could not be found. - * - * @since 2.4 - */ - public static <T> T convert( Object source, Class<T> target, Hints hints ) { - //can't convert null - if ( source == null ) - return null; - + /** + * Converts an object of a particular type into an object of a different type. + * <p> + * This method uses the {@link ConverterFactory} extension point to find a converter capable of + * performing the conversion. The first converter found is the one used. Using this class there + * is no way to guarantee which converter will be used. + * </p> + * + * @param source + * The object to convert. + * @param target + * The type of the converted value. + * @param hints + * Any hints for the converter factory. + * + * @return The converted value as an instance of target, or <code>null</code> if a converter + * could not be found. + * + * @since 2.4 + */ + public static <T> T convert(Object source, Class<T> target, Hints hints) { + // can't convert null + if (source == null) + return null; + // handle case of source being an instance of target up front - final Class sourceClass = source.getClass(); - if (sourceClass == target || sourceClass.equals( target ) - || target.isAssignableFrom(sourceClass) ) { - return (T) source; + final Class<?> sourceClass = source.getClass(); + if (sourceClass == target || sourceClass.equals(target) + || target.isAssignableFrom(sourceClass)) { + return target.cast( source ); } - for (ConverterFactory factory : factories()) { - Converter converter = factory.createConverter( sourceClass, target, hints ); - if ( converter != null ) { - try { - T converted = converter.convert( source, target ); - if ( converted != null ) { - return converted; - } - } catch (Exception e) { - if(LOGGER.isLoggable(Level.FINER)) - LOGGER.log(Level.FINER, "Error applying the converter " + converter.getClass() + " on (" + source + "," + target + ")", e); - } - } - } + for (ConverterFactory factory : factories()) { + Converter converter = factory.createConverter(sourceClass, target, hints); + if (converter != null) { + try { + T converted = converter.convert(source, target); + if (converted != null) { + return converted; + } + } catch (Exception e) { + if (LOGGER.isLoggable(Level.FINER)) + LOGGER.log(Level.FINER, + "Error applying the converter " + converter.getClass() + " on (" + + source + "," + target + ")", e); + } + } + } - //a couple of final tries - if ( String.class.equals( target ) ) { - return (T) source.toString(); - } - return null; - } + // a couple of final tries + if (String.class.equals(target)) { + return target.cast( source.toString() ); + } + return null; + } - /** - * Processed the {@link ConverterFactory} extension point. - * - * @return A collection of converter factories. - * @since 2.4 - */ - static ConverterFactory[] factories() { - if(factories == null) { - Collection factoryCollection = getConverterFactories(GeoTools.getDefaultHints()); - factories = (ConverterFactory[]) factoryCollection.toArray(new ConverterFactory[factoryCollection.size()]); - } - return factories; - } + /** + * Processed the {@link ConverterFactory} extension point. + * + * @return A collection of converter factories. + * @since 2.4 + */ + static ConverterFactory[] factories() { + if (factories == null) { + Collection<ConverterFactory> factoryCollection = getConverterFactories(GeoTools + .getDefaultHints()); + factories = (ConverterFactory[]) factoryCollection + .toArray(new ConverterFactory[factoryCollection.size()]); + } + return factories; + } } Modified: trunk/modules/library/main/src/main/java/org/geotools/data/DataUtilities.java =================================================================== --- trunk/modules/library/main/src/main/java/org/geotools/data/DataUtilities.java 2011-03-30 14:02:13 UTC (rev 36811) +++ trunk/modules/library/main/src/main/java/org/geotools/data/DataUtilities.java 2011-03-30 15:31:06 UTC (rev 36812) @@ -302,9 +302,9 @@ } /** - * Traverses the filter and returns any encoutered property names. + * Traverses the filter and returns any encountered property names. * <p> - * The feautre type is supplied as contexts used to lookup expressions in cases where the + * The feature type is supplied as contexts used to lookup expressions in cases where the * attributeName does not match the actual name of the type. * </p> */ @@ -317,20 +317,42 @@ String[] attributeNames = attExtractor.getAttributeNames(); return attributeNames; } - + /** - * Traverses the filter and returns any encoutered property names. - * - * @deprecated use {@link #attributeNames(Filter, FeatureType)}/ + * Traverses the filter and returns any encountered property names. + * <p> + * The feature type is supplied as contexts used to lookup expressions in cases where the + * attributeName does not match the actual name of the type. + * </p> */ + public static Set<PropertyName> propertyNames(Filter filter, final SimpleFeatureType featureType) { + if (filter == null) { + return Collections.emptySet(); + } + FilterAttributeExtractor attExtractor = new FilterAttributeExtractor(featureType); + filter.accept(attExtractor, null); + Set<PropertyName> propertyNames = attExtractor.getPropertyNameSet(); + return propertyNames; + } + + /** + * Traverses the filter and returns any encountered property names. + */ public static String[] attributeNames(Filter filter) { return attributeNames(filter, null); } - + /** - * Traverses the expression and returns any encoutered property names. + * Traverses the filter and returns any encountered property names. + */ + public static Set<PropertyName> propertyNames(Filter filter) { + return propertyNames(filter, null); + } + + /** + * Traverses the expression and returns any encountered property names. * <p> - * The feautre type is supplied as contexts used to lookup expressions in cases where the + * The feature type is supplied as contexts used to lookup expressions in cases where the * attributeName does not match the actual name of the type. * </p> */ @@ -345,15 +367,36 @@ } /** - * Traverses the expression and returns any encoutered property names. - * - * @deprecated use {@link #attributeNames(Expression, FeatureType)}/ + * Traverses the expression and returns any encountered property names. + * <p> + * The feature type is supplied as contexts used to lookup expressions in cases where the + * attributeName does not match the actual name of the type. + * </p> */ + public static Set<PropertyName> propertyNames(Expression expression, final SimpleFeatureType featureType) { + if (expression == null) { + return Collections.emptySet(); + } + FilterAttributeExtractor attExtractor = new FilterAttributeExtractor(featureType); + expression.accept(attExtractor, null); + Set<PropertyName> propertyNames = attExtractor.getPropertyNameSet(); + return propertyNames; + } + /** + * Traverses the expression and returns any encountered property names. + */ public static String[] attributeNames(Expression expression) { return attributeNames(expression, null); } - + /** + * Traverses the expression and returns any encountered property names. + */ + public static Set<PropertyName> propertyNames(Expression expression) { + return propertyNames(expression, null); + } + + /** * Compare operation for FeatureType. * * <p> @@ -447,14 +490,15 @@ } /** - * DOCUMENT ME! + * Quickly check if two descriptors are at all compatible. + * <p> + * This method checks the descriptors name and class binding to see + * if the values have any chance of being compatible. * - * @param a - * DOCUMENT ME! - * @param b - * DOCUMENT ME! - * - * @return DOCUMENT ME! + * @param a descriptor to compare + * @param b descriptor to compare + * + * @return true to the descriptors name and binding class match */ public static boolean isMatch(AttributeDescriptor a, AttributeDescriptor b) { if (a == b) { @@ -1493,14 +1537,22 @@ } /** - * DOCUMENT ME! + * Used to compare if two values are equal. + * <p> + * This method is here to work around the fact that JTS Geometry + * requires a specific method to be called rather than object.equals. * - * @param att - * DOCUMENT ME! - * @param otherAtt - * DOCUMENT ME! + * This method uses: * - * @return DOCUMENT ME! + * <ul> + * <li>Object.equals( Object )</li> + * <li>Geometry.equals( Geometry )</li> + * </ul> + * + * @param att Attribute value + * @param otherAtt Other value + * + * @return True if the values are equal */ public static boolean attributesEqual(Object att, Object otherAtt) { if (att == null) { @@ -1761,19 +1813,16 @@ } /** - * DOCUMENT ME! + * Uses Converers to parse the provided text into the correct + * values to create a feature. * - * @param type - * DOCUMENT ME! - * @param fid - * DOCUMENT ME! - * @param text - * DOCUMENT ME! + * @param type FeatureType + * @param fid Feature ID for new feature + * @param text Text representation of values * - * @return DOCUMENT ME! + * @return newly created feature * * @throws IllegalAttributeException - * DOCUMENT ME! */ public static SimpleFeature parse(SimpleFeatureType type, String fid, String[] text) throws IllegalAttributeException { @@ -2094,14 +2143,14 @@ public static List<PropertyName> addMandatoryProperties (SimpleFeatureType type, List<PropertyName> oldProps) { Iterator<PropertyDescriptor> ii = type.getDescriptors().iterator(); - List<PropertyName> properties = new ArrayList<PropertyName>(); - + List<PropertyName> properties = new ArrayList<PropertyName>(); + while (ii.hasNext()) { PropertyDescriptor descr = ii.next(); PropertyName propName = ff.property (descr.getName()); - if (oldProps.contains(propName)) { + if (oldProps != null && oldProps.contains(propName)) { properties.add(propName); } else if (((descr.getMinOccurs() > 0) && (descr.getMaxOccurs() != 0))) { //mandatory, add it Modified: trunk/modules/library/main/src/main/java/org/geotools/filter/FilterAttributeExtractor.java =================================================================== --- trunk/modules/library/main/src/main/java/org/geotools/filter/FilterAttributeExtractor.java 2011-03-30 14:02:13 UTC (rev 36811) +++ trunk/modules/library/main/src/main/java/org/geotools/filter/FilterAttributeExtractor.java 2011-03-30 15:31:06 UTC (rev 36812) @@ -29,13 +29,24 @@ /** * A simple visitor that extracts every attribute used by a filter or an expression - * + * <p> + * Access to this class is available via: + * <ul> + * <li>DataUtilities.attributeNames( Filter )</li> + * <li>DataUtilities.attributeNames( Filter, FeatureType )</li> + * <li>DataUtilities.attributeNames( Expression )</li> + * <li>DataUtilities.attributeNames( Expression, FeatureType )</li> + * </ul> + * * @author wolf * @source $URL$ */ public class FilterAttributeExtractor extends DefaultFilterVisitor { + /** Last set visited */ protected Set<String> attributeNames = new HashSet<String>(); + protected Set<PropertyName> propertyNames = new HashSet<PropertyName>(); + /** feature type to evaluate against */ protected SimpleFeatureType featureType; @@ -55,7 +66,7 @@ this.featureType = featureType; } /** - * DOCUMENT ME! + * Attributes names found (so far). * * @return an unmofiable set of the attribute names found so far during the visit */ @@ -64,7 +75,17 @@ } /** - * DOCUMENT ME! + * Lists the PropertyNames found so far; useful when dealing with + * cpath expressions involving namespace informaiton. + * + * @return + */ + public Set<PropertyName> getPropertyNameSet() { + return propertyNames; + } + + /** + * Array of attribute names found (so far). * * @return an array of the attribute names found so far during the visit */ @@ -83,6 +104,8 @@ if( data != null && data != attributeNames ){ attributeNames = (Set<String>) data; } + propertyNames.add( expression ); + if (featureType != null) { //evaluate against the feature type instead of using straight name // since the path from the property name may be an xpath or a Modified: trunk/modules/library/main/src/main/java/org/geotools/filter/Filters.java =================================================================== --- trunk/modules/library/main/src/main/java/org/geotools/filter/Filters.java 2011-03-30 14:02:13 UTC (rev 36811) +++ trunk/modules/library/main/src/main/java/org/geotools/filter/Filters.java 2011-03-30 15:31:06 UTC (rev 36812) @@ -59,7 +59,7 @@ /** * Utility class for working with Filters & Expression. * <p> - * To get the full benifit you will need to create an instanceof + * To get the full benefit you will need to create an instanceof * this Object (supports your own custom FilterFactory!). Additional * methods to help create expressions are available. * </p> @@ -67,7 +67,7 @@ * Example use: * <pre><code> * Filters filters = new Filters( factory ); - * filters.duplicate( origional ); + * filters.duplicate( original ); * </code></pre> * The above example creates a copy of the provided Filter, * the factory provided will be used when creating the duplicated @@ -75,7 +75,7 @@ * </p> * <h3>Expression</h3> * <p> - * Expressions form an interesting little semi scripting languge, + * Expressions form an interesting little semi scripting language, * intended for queries. A interesting Feature of Filter as a language * is that it is not strongly typed. This utility class many helper * methods that ease the transition from Strongly typed Java to the more @@ -595,31 +595,51 @@ /** * Inverse of eval, used to softly type supported * types into Text for use as literals. + * <p> + * This method has been superseeded by Converters + * which offers a more general and open ended solution. + * </p> + * @return String representation of provided object */ - public static String puts( Object obj ){ - if( obj == null ) return null; - if( obj instanceof String) return (String) obj; - if( obj instanceof Color ){ - Color color = (Color) obj; - return puts( color ); - } - if( obj instanceof Number ){ - Number number = (Number) obj; - return puts( number.doubleValue() ); - } - return obj.toString(); + public static String puts(Object obj) { + if (obj == null) + return null; + if (obj instanceof String) + return (String) obj; + if (obj instanceof Color) { + Color color = (Color) obj; + return puts(color); + } + if (obj instanceof Number) { + Number number = (Number) obj; + return puts(number.doubleValue()); + } + return obj.toString(); } - - public static String puts( Color color ){ - String redCode = Integer.toHexString(color.getRed()); + + /** + * Inverse of eval, used to softly type supported types into Text for use as literals. + * <p> + * This method has been superseeded by Converters which offers a more general and open ended + * solution. + * </p> + * + * @param color + * @return String representation of provided color. + */ + public static String puts(Color color) { + String redCode = Integer.toHexString(color.getRed()); String greenCode = Integer.toHexString(color.getGreen()); String blueCode = Integer.toHexString(color.getBlue()); - if (redCode.length() == 1) redCode = "0" + redCode; - if (greenCode.length() == 1) greenCode = "0" + greenCode; - if (blueCode.length() == 1) blueCode = "0" + blueCode; - - return "#" + redCode + greenCode + blueCode; + if (redCode.length() == 1) + redCode = "0" + redCode; + if (greenCode.length() == 1) + greenCode = "0" + greenCode; + if (blueCode.length() == 1) + blueCode = "0" + blueCode; + + return "#" + redCode + greenCode + blueCode; } } Modified: trunk/modules/library/main/src/test/java/org/geotools/filter/FilterAttributeExtractorTest.java =================================================================== --- trunk/modules/library/main/src/test/java/org/geotools/filter/FilterAttributeExtractorTest.java 2011-03-30 14:02:13 UTC (rev 36811) +++ trunk/modules/library/main/src/test/java/org/geotools/filter/FilterAttributeExtractorTest.java 2011-03-30 15:31:06 UTC (rev 36812) @@ -18,6 +18,7 @@ import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.Set; import junit.framework.Test; @@ -26,6 +27,7 @@ import org.geotools.factory.CommonFactoryFinder; import org.geotools.feature.SchemaException; +import org.opengis.filter.Filter; import org.opengis.filter.Id; import org.opengis.filter.Or; import org.opengis.filter.PropertyIsEqualTo; @@ -35,12 +37,12 @@ import org.opengis.filter.expression.PropertyName; import org.opengis.filter.spatial.DWithin; import org.opengis.filter.spatial.Equals; +import org.opengis.filter.expression.Expression; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.PrecisionModel; - /** * Unit test for filters. Note that this unit test does not encompass all of filter package, just * the filters themselves. There is a seperate unit test for expressions. @@ -101,7 +103,29 @@ fac = CommonFactoryFinder.getFilterFactory2(null); } - + + public void testPropertyNameSet() throws IllegalFilterException { + Filter filter = fac.equals(fac.property("testString"), fac.literal("test string data")); + Expression expression1 = fac.property("code"); + Expression expression2 = fac.function("length", fac.property("identification")); + + FilterAttributeExtractor extract = new FilterAttributeExtractor(null); + + Set<String> names = new HashSet<String>(); + // used to collect names from expression1, expression2, and filter + + expression1.accept(extract, names); + expression2.accept(extract, names); + filter.accept(extract, names); + + String array[] = extract.getAttributeNames(); + Set<String> attributes = extract.getAttributeNameSet(); + Set<PropertyName> properties = extract.getPropertyNameSet(); + + assertEquals( 3 , array.length ); + assertEquals( 3, attributes.size() ); + assertEquals( 3, properties.size() ); + } /** * Sets up a schema and a test feature. * @@ -120,7 +144,7 @@ fae.clear(); filter.accept(fae, null); - Set attNames = fae.getAttributeNameSet(); + Set<String> attNames = fae.getAttributeNameSet(); assertNotNull(attNames); assertEquals(attNames.size(), names.length); Modified: trunk/modules/library/metadata/src/main/java/org/geotools/factory/GeoTools.java =================================================================== --- trunk/modules/library/metadata/src/main/java/org/geotools/factory/GeoTools.java 2011-03-30 14:02:13 UTC (rev 36811) +++ trunk/modules/library/metadata/src/main/java/org/geotools/factory/GeoTools.java 2011-03-30 15:31:06 UTC (rev 36812) @@ -430,8 +430,21 @@ public static Hints getDefaultHints() { return Hints.getDefaults(false); } - /** + * Used to combine provided hints with global GeoTools defaults. + * + * @param hints + * @return + */ + public static Hints addDefaultHints(final Hints hints) { + final Hints completed = getDefaultHints(); + if (hints != null) { + completed.add(hints); + } + return completed; + } + + /** * Returns the default initial context. * * @param hints An optional set of hints, or {@code null} if none. |