Quick Search
Browse
Pages
Blog
Labels
Attachments
Mail
Advanced
What’s New
Space Directory
Feed Builder
Keyboard Shortcuts
Confluence Gadgets
Log In
Dashboard
GeoTools
Copy Page
You are not logged in. Any changes you make will be marked as
anonymous
. You may want to
Log In
if you already have an account. You can also
Sign Up
for a new account.
This page is being edited by
.
Paragraph
Paragraph
Heading 1
Heading 2
Heading 3
Heading 4
Heading 5
Heading 6
Preformatted
Quote
Bold
Italic
Underline
More colours
Strikethrough
Subscript
Superscript
Monospace
Clear Formatting
Bullet list
Numbered list
Outdent
Indent
Align left
Align center
Align right
Link
Table
Insert
Insert Content
Image
Link
Attachment
Symbol
Emoticon
Wiki Markup
Horizontal rule
tinymce.confluence.insert_menu.macro_desc
Info
JIRA Issue
Status
Gallery
Tasklist
Table of Contents
Other Macros
Page Layout
No Layout
Two column (simple)
Two column (simple, left sidebar)
Two column (simple, right sidebar)
Three column (simple)
Two column
Two column (left sidebar)
Two column (right sidebar)
Three column
Three column (left and right sidebars)
Undo
Redo
Find/Replace
Keyboard Shortcuts Help
<table class="confluenceTable"><tbody><tr><th class="confluenceTh">Motivation:</th><td class="confluenceTd"><em>Add support for ComplexFeatures. GeoTools should be able to parse a complex WFS response and construct a set of Feature objects from it.</em></td></tr><tr><th class="confluenceTh"><p>Contact:</p></th><td class="confluenceTd"><p><a class="confluence-link confluence-userlink" data-username="adambrown" href="/display/~adambrown" data-linked-resource-id="228185561" data-linked-resource-type="userinfo" username="adambrown" data-linked-resource-default-alias="Adam Brown" data-base-url="http://docs.codehaus.org">Adam Brown</a></p></td></tr><tr><th class="confluenceTh"><p>Tracker:</p></th><td class="confluenceTd"><a href="http://jira.codehaus.org/browse/GEOT-4147">ComplexFeature Parsing & Building Support</a></td></tr><tr><td class="highlight confluenceTd" colspan="1"><p>Tagline:</p></td><td colspan="1" class="confluenceTd"><p>complex features FTW</p></td></tr><tr><td class="highlight confluenceTd" colspan="1"><strong>Location:</strong></td><td colspan="1" class="confluenceTd"><p><a href="https://github.com/Adam-Brown/geotools">https://github.com/Adam-Brown/geotools</a></p></td></tr></tbody></table><table class="wysiwyg-macro" data-macro-name="section" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e3NlY3Rpb259&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="RICH_TEXT"><tr><td class="wysiwyg-macro-body"><p>This page represents the <strong>current</strong> plan; for discussion please check the tracker link above.</p><table class="wysiwyg-macro" data-macro-name="column" data-macro-parameters="width=70%" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvbHVtbjp3aWR0aD03MCV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="RICH_TEXT"><tr><td class="wysiwyg-macro-body"><img class="editor-inline-macro" src="/plugins/servlet/confluence/placeholder/macro?definition=e3RvY30&locale=en_GB&version=2" data-macro-name="toc"></td></tr></table><table class="wysiwyg-macro" data-macro-name="column" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvbHVtbn0&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="RICH_TEXT"><tr><td class="wysiwyg-macro-body"><p>Children:</p><p><img class="editor-inline-macro" src="/plugins/servlet/confluence/placeholder/macro?definition=e2NoaWxkcmVufQ&locale=en_GB&version=2" data-macro-name="children"></p></td></tr></table></td></tr></table><p>Attachments:</p><p><img class="editor-inline-macro" src="/plugins/servlet/confluence/placeholder/macro?definition=e2F0dGFjaG1lbnRzOnVwbG9hZD1mYWxzZX0&locale=en_GB&version=2" data-macro-name="attachments" data-macro-parameters="upload=false"></p><h1>Description</h1><p>GeoTool's lack of support for parsing XML into complex features limits its utility as a GIS toolkit.</p><p>I propose to add this functionality by augmenting the codebase such that there are complex-compatible analogues or alternatives to all the classes necessary for WFS-based communications. Existing type hierarchies will be modifed and added to to provide this new functionality. In making these changes I will strive for maximum reuse of existing code and will follow the coding patterns and API conventions currently employed; as such, the code for performing a complex feature request will be much like the code for a simple feature request. Breaking changes will be eschewed.</p><p>Central to this work is a new XmlComplexFeatureParser which will use an AttributeBuilder and a ComplexFeatureBuilder to create objects that represent the content of WFS XML responses.</p><p>The addition of the following classes has had varying levels of impact on their encompassing type hierarchies:</p><ul><li>WFSContentDataAccess</li><li>WFSDataAccessFactory</li><li>XmlComplexFeatureParser</li><li>ComplexFeatureBuilder</li></ul><p>The proposed type hierachies can be seen alongside their previous forms here: <a class="confluence-link unresolved" data-filename="ChangedTypeHierarchies.pdf" data-linked-resource-default-alias="ChangedTypeHierarchies.pdf" href="#">ChangedTypeHierarchies.pdf</a> (<a href="https://www.seegrid.csiro.au/wiki/ASRDC/ChangedTypeHierarchies">ChangedTypeHierarchies Seegrid Wiki</a>).</p><h1>Resources</h1><p><a href="https://www.seegrid.csiro.au/wiki/ASRDC/GMLConsumptionLibrary">GML Consumption Library Use cases</a></p><h1>Status</h1><p>Voting has started:</p><ul><li><a class="confluence-link confluence-userlink" data-username="aaime" href="/display/~aaime" data-linked-resource-id="787" data-linked-resource-type="userinfo" username="aaime" data-linked-resource-default-alias="Andrea Aime" data-base-url="http://docs.codehaus.org">Andrea Aime</a>:</li><li><a class="confluence-link confluence-userlink" data-username="bencaradocdavies" href="/display/~bencaradocdavies" data-linked-resource-id="60850191" data-linked-resource-type="userinfo" username="bencaradocdavies" data-linked-resource-default-alias="Ben Caradoc-Davies" data-base-url="http://docs.codehaus.org">Ben Caradoc-Davies</a>: +1</li><li><a class="confluence-link confluence-userlink" data-username="christian.mueller@nvoe.at" href="/display/~christian.mueller@nvoe.at" data-linked-resource-id="75366406" data-linked-resource-type="userinfo" username="christian.mueller@nvoe.at" data-linked-resource-default-alias="Christian Mueller" data-base-url="http://docs.codehaus.org">Christian Mueller</a>:</li><li><a class="confluence-link confluence-userlink" data-username="ianturton" href="/display/~ianturton" data-linked-resource-id="800" data-linked-resource-type="userinfo" username="ianturton" data-linked-resource-default-alias="Ian Turton" data-base-url="http://docs.codehaus.org">Ian Turton</a>:</li><li><a class="confluence-link confluence-userlink" data-username="jdeolive" href="/display/~jdeolive" data-linked-resource-id="14894" data-linked-resource-type="userinfo" username="jdeolive" data-linked-resource-default-alias="Justin Deoliveira" data-base-url="http://docs.codehaus.org">Justin Deoliveira</a>:</li><li><a class="confluence-link confluence-userlink" data-username="jive" href="/display/~jive" data-linked-resource-id="1159" data-linked-resource-type="userinfo" username="jive" data-linked-resource-default-alias="Jody Garnett" data-base-url="http://docs.codehaus.org">Jody Garnett</a>:</li><li><a class="confluence-link confluence-userlink" data-username="simboss" href="/display/~simboss" data-linked-resource-id="10743" data-linked-resource-type="userinfo" username="simboss" data-linked-resource-default-alias="Simone Giannecchini" data-base-url="http://docs.codehaus.org">Simone Giannecchini</a>:</li></ul><h1>Tasks</h1><table class="confluenceTable"><tbody><tr><th class="confluenceTh"><p> </p></th><td class="confluenceTd"><p>no progress</p></td><th class="confluenceTh"><p><img class="emoticon emoticon-tick" data-emoticon-name="tick" border="0" src="/s/en_GB/3278/15/_/images/icons/emoticons/check.png" alt="(tick)" title="(tick)" /></p></th><td class="confluenceTd"><p>done</p></td><th class="confluenceTh"><p><img class="emoticon emoticon-cross" data-emoticon-name="cross" border="0" src="/s/en_GB/3278/15/_/images/icons/emoticons/error.png" alt="(error)" title="(error)" /></p></th><td class="confluenceTd"><p>impeded</p></td><th class="confluenceTh"><p><img class="emoticon emoticon-warning" data-emoticon-name="warning" border="0" src="/s/en_GB/3278/15/_/images/icons/emoticons/warning.png" alt="(warning)" title="(warning)" /></p></th><td class="confluenceTd"><p>lack mandate/funds/time</p></td><td class="confluenceTd"><p><img class="emoticon emoticon-question" data-emoticon-name="question" border="0" src="/s/en_GB/3278/15/_/images/icons/emoticons/help_16.png" alt="(question)" title="(question)" /></p></td><td class="confluenceTd"><p>volunteer needed</p></td></tr></tbody></table><ol><li><img class="emoticon emoticon-tick" data-emoticon-name="tick" border="0" src="/s/en_GB/3278/15/_/images/icons/emoticons/check.png" alt="(tick)" title="(tick)" /> WFSDataAccessFactory integrated into 'Factory' hierarchy.</li><li><img class="emoticon emoticon-tick" data-emoticon-name="tick" border="0" src="/s/en_GB/3278/15/_/images/icons/emoticons/check.png" alt="(tick)" title="(tick)" /> WFSContentDataAccess integrated into 'DataAccess' hierarcy.</li><li><img class="emoticon emoticon-tick" data-emoticon-name="tick" border="0" src="/s/en_GB/3278/15/_/images/icons/emoticons/check.png" alt="(tick)" title="(tick)" /> A new 'GetParser<...>' hierarchy created to supplant existing 'GetFeatureParser' hierarchy. XmlComplexFeatureParser integrated into this.</li><li><img class="emoticon emoticon-tick" data-emoticon-name="tick" border="0" src="/s/en_GB/3278/15/_/images/icons/emoticons/check.png" alt="(tick)" title="(tick)" /> A new 'FeatureBuilder<...>' hierarchy created to generalise functionality from 'SimpleFeatureBuilder' so that it can be used by a new ComplexFeatureBuilder class.</li><li><img class="emoticon emoticon-tick" data-emoticon-name="tick" border="0" src="/s/en_GB/3278/15/_/images/icons/emoticons/check.png" alt="(tick)" title="(tick)" /> Create an AttributeBuilder.</li><li><img class="emoticon emoticon-tick" data-emoticon-name="tick" border="0" src="/s/en_GB/3278/15/_/images/icons/emoticons/check.png" alt="(tick)" title="(tick)" /> Create a ComplexFeatureBuilder.</li><li><img class="emoticon emoticon-tick" data-emoticon-name="tick" border="0" src="/s/en_GB/3278/15/_/images/icons/emoticons/check.png" alt="(tick)" title="(tick)" /> Complete the XmlComplexFeatureParser.</li><li>Update documentation. (in progress)</li></ol><p> </p><h1>API Changes</h1><h2>Public API for making WFS request</h2><h5>BEFORE (Example of SIMPLE feature request)</h5><table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>// 1. Configure: String getCapabilitiesURL = "http://services.auscope.org/geodesy/wfs?REQUEST=GetCapabilities"; Map<String, Serializable> connectionParameters = new HashMap<String, Serializable>(); connectionParameters.put("WFSDataStoreFactory:GET_CAPABILITIES_URL", getCapabilitiesURL); connectionParameters.put("WFSDataStoreFactory:TIMEOUT", 0); // 2. Find suitable DataStore: DataStore dataStore = DataStoreFinder.getDataStore(connectionParameters); // 3. Declare the typeName you're interested in (this could be done by iterating through the typeNames in dataStore.getTypeNames()): String typeName = "ngcp:GnssStation"; // 4. Get the FeatureSource: FeatureSource<SimpleFeatureType, SimpleFeature> featureSource = dataStore.getFeatureSource(typeName); // 5. Get the (Simple)FeatureType (AKA schema): SimpleFeatureType schema = dataStore.getSchema(typeName); // 6. Create a Query String geomName = schema.getGeometryDescriptor().getLocalName(); FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2(GeoTools.getDefaultHints()); Object polygon = JTS.toGeometry(new Envelope(-30, -32, 115, 116)); Intersects filter = ff.intersects(ff.property(geomName), ff.literal(polygon)); // STATIONNO could have been extracted from schema.getDescriptors(), see note on 3, above. Query query = new Query(typeName, filter, new String[] { "STATIONNO", geomName }); query.setCoordinateSystem(schema.getGeometryDescriptor().getCoordinateReferenceSystem()); // 7. Get the features and their corresponding types: FeatureCollection<SimpleFeatureType, SimpleFeature> features = featureSource.getFeatures(query); // 8. Iterate over and interact with the items: FeatureIterator<SimpleFeature> iterator = features.features(); try { while (iterator.hasNext()) { Feature feature = (Feature) iterator.next(); for (Property property : feature.getProperties()) { System.out.println(property); } } } finally { iterator.close(); } </pre></td></tr></table><h5>AFTER (Example of COMPLEX feature request)</h5><table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>// 1. Configure: String getCapabilitiesURL = "http://nvclwebservices.vm.csiro.au/geoserverBH/wfs?REQUEST=GetCapabilities"; Map<String, Serializable> connectionParameters = new HashMap<String, Serializable>(); connectionParameters.put("WFSDataStoreFactory:WFS_GET_CAPABILITIES_URL", getCapabilitiesURL); connectionParameters.put("WFSDataStoreFactory:TIMEOUT", 0); connectionParameters.put("WFSDataStoreFactory:PROTOCOL", false); /** #A: Require a Factory with this level of compliance. **/ connectionParameters.put("WFSDataStoreFactory:GML_COMPLIANCE_LEVEL", 2); connectionParameters.put("WFSDataStoreFactory:MAXFEATURES", 2); /** #B: Specify the location of the folder to be used by app-schema-resolver. **/ connectionParameters.put("WFSDataStoreFactory:SCHEMA_CACHE_LOCATION", "C:/Adams/GitHub_GeoTools_GeoTools/schema_cache"); // 2. Find suitable DataAccess: /** #C: Notice that these classes are the non-Simple forms. **/ DataAccess<FeatureType, Feature> dataAccess = DataAccessFinder.getDataStore(connectionParameters); // 3. Declare the type you're interested in (this could be done by iterating through the typeNames in dataStore.getTypeNames()). Name nameToRetrieve = new NameImpl("urn:cgi:xmlns:CGI:GeoSciML:2.0", ":", "Borehole"); // 4. Get the FeatureSource (WFSContentComplexFeatureSource): FeatureSource<FeatureType, Feature> featureSource = dataAccess.getFeatureSource(nameToRetrieve); // 5. Get the FeatureType (AKA schema): FeatureType schema = dataAccess.getSchema(nameToRetrieve); // 6. Create Query using the schema. Query query = new Query("gsml:Borehole"); query.setCoordinateSystem(schema.getGeometryDescriptor().getCoordinateReferenceSystem()); // 7. Get the features and their corresponding types: FeatureCollection<FeatureType, Feature> features = featureSource.getFeatures(query); // 8. Iterate over the features and display them: FeatureIterator<Feature> iterator = features.features(); try { while (iterator.hasNext()) { Feature feature = iterator.next(); for (Property property : feature.getProperties()) { System.out.println(property); } } } finally { iterator.close(); }</pre></td></tr></table><p>Take note of the comments enclosed by double asterix, i.e; /** <em>comment</em> **/. They point out some important differences between the two code samples. Each one has letter identifier preceded by a hash. I will hearafter refer back to these identifiers to elaborate more on the code they annotate:</p><ul><li>#A: The 'getDataStore' method iterates through a collection of factories until it finds one that meets the requirements of the connectionParameters passed in. I added a new optional GML_COMPLIANCE_LEVEL parameter to be passed through to impose a restriction on the type of DataAccess object that can be passed back. I.e. one that supports complex features.</li><li>#B: When dealing with complex features we can utilise the app-schema-cache to cache schemata.</li><li>#C: I created WFSDataAccessFactory which conforms to GML_COMPLIANCE_LEVEL 2. It creates and returns a WFSContentDataAccess object. WFSContentDataAccess borrows extensively from WFSContentDataStore with notable exceptions being its lack of reliance on simple types in methods such as getSchema, updateSchema, etc.</li></ul><h2>Comparison and Contrast between SimpleFeatureBuilder, ComplexFeatureBuilder usage</h2><blockquote><p>The builders are used by their corresponding XML feature parsers to associate attributes with names.</p></blockquote><h5>SimpleFeatureBuilder</h5><table class="wysiwyg-macro" data-macro-name="code" data-macro-parameters="language=java|theme=Eclipse|title=SimpleFeatureBuilder" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6dGl0bGU9U2ltcGxlRmVhdHVyZUJ1aWxkZXJ8dGhlbWU9RWNsaXBzZXxsYW5ndWFnZT1qYXZhfQ&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>this.builder = new SimpleFeatureBuilder(this.targetType); // Repeat for each attribute builder.set(descriptor.getLocalName(), attributeValue); // Once you've added all the attributes you build the feature itself SimpleFeature feature = builder.buildFeature(fid);</pre></td></tr></table><p>The attributeValue, above, is created by a static Converters.convert(...) method which takes a raw text value and a binding (Java Class object).</p><h5>ComplexFeatureBuilder</h5><table class="wysiwyg-macro" data-macro-name="code" data-macro-parameters="language=java|theme=Eclipse|title=SimpleFeatureBuilder" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6dGl0bGU9U2ltcGxlRmVhdHVyZUJ1aWxkZXJ8dGhlbWU9RWNsaXBzZXxsYW5ndWFnZT1qYXZhfQ&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>this.builder = new ComplexFeatureBuilder(this.targetType); // Repeat for each property builder.append(descriptor.getLocalName(), propertyValue); // Once the properties are added you build the complex feature itself Feature feature = builder.buildFeature(fid);</pre></td></tr></table><p>In the case of ComplexFeatures the propertyValue, above, is constructed with an AttributeBuilder and bound to a Java Class object.</p><h2>Excerpt from ComplexFeatureBuilderTests</h2><table class="wysiwyg-macro" data-macro-name="code" data-macro-parameters="language=java|theme=Eclipse|title=Excerpt from ComplexFeatureBuilderTests" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6dGl0bGU9RXhjZXJwdCBmcm9tIENvbXBsZXhGZWF0dXJlQnVpbGRlclRlc3RzfHRoZW1lPUVjbGlwc2V8bGFuZ3VhZ2U9amF2YX0&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>@Test public void test_append_validNameInvalidValueClass_throwsIllegalArgumentException() { // Arrange ComplexFeatureBuilder builder = new ComplexFeatureBuilder(BRIDGE_TYPE); // Act try { builder.append(LOCATION, londonBridge); // Passing in londonBridge instead of a location. fail("Expected IllegalArgumentException but it wasn't thrown."); } catch (IllegalArgumentException iae) { String expectedMessage = "The value provided contains an object of 'class java.lang.String' but the method expects an object of 'class com.vividsolutions.jts.geom.Geometry'."; if (iae.getMessage().compareTo(expectedMessage) != 0) { fail("Expected IllegalArgumentExceptionMessage to say: '" + expectedMessage + "' but got: '" + iae.getMessage() + "'"); } // Assert (This is the expected exception). return; } catch (Exception e) { fail("Expected IllegalArgumentException but it wasn't thrown; got " + e.getClass() + " instead. " + e.getMessage()); } } @Test public void test_append_validNameButNullValue_throwsIllegalArgumentException() { // Arrange ComplexFeatureBuilder builder = new ComplexFeatureBuilder(BRIDGE_TYPE); // Act try { builder.append(BRIDGE_NAME, null); // Passing a null reference for a non-nillable type. fail("Expected IllegalArgumentException but it wasn't thrown."); } catch (IllegalArgumentException iae) { String expectedMessage = "The value provided is a null reference but the property descriptor 'AttributeDescriptorImpl urn:Bridge:Test:1.1:bridgeName <string:String> 0:1' is non-nillable."; if (iae.getMessage().compareTo(expectedMessage) != 0) { fail("Expected IllegalArgumentExceptionMessage to say: '" + expectedMessage + "' but got: '" + iae.getMessage() + "'"); } // Assert (This is the expected exception). return; } catch (Exception e) { fail("Expected IllegalArgumentException but it wasn't thrown; got " + e.getClass() + " instead. " + e.getMessage()); } } @Test public void test_append_validNameValidValue_valueShouldBeAddedToTheMap() { // Arrange ComplexFeatureBuilder builder = new ComplexFeatureBuilder(BRIDGE_TYPE); // Act builder.append(BRIDGE_NAME, londonBridge); Object actualValue = builder.values.get(BRIDGE_NAME).get(0); // Assert Assert.assertSame(londonBridge, actualValue); } @Test public void test_append_exceedMaxOccursLimit_throwsIndexOutOfBoundsException() { // Arrange ComplexFeatureBuilder builder = new ComplexFeatureBuilder(BRIDGE_TYPE); builder.append(BRIDGE_NAME, londonBridge); // Act try { builder.append(BRIDGE_NAME, londonBridge); // Add it once too many times. fail("Expected IndexOutOfBoundsException but it wasn't thrown."); } catch (IndexOutOfBoundsException iae) { String expectedMessage = "You can't add another object with the name of 'urn:Bridge:Test:1.1:bridgeName' because you already have the maximum number (1) allowed by the property descriptor."; if (iae.getMessage().compareTo(expectedMessage) != 0) { fail("Expected IndexOutOfBoundsException to say: '" + expectedMessage + "' but got: '" + iae.getMessage() + "'"); } // Assert (This is the expected exception). return; } catch (Exception e) { fail("Expected IndexOutOfBoundsException but it wasn't thrown; got " + e.getClass() + " instead. " + e.getMessage()); } } @Test public void test_buildFeature_validInput_buildsFeature() { // Arrange ComplexFeatureBuilder builder = new ComplexFeatureBuilder(BRIDGE_TYPE); AttributeImpl description = new AttributeImpl("description", DESCRIPTION_DESCRIPTOR, null); builder.append(BRIDGE_NAME, londonBridge); builder.append(LOCATION, location); builder.append(DESCRIPTION, description); // Act Feature feature = builder.buildFeature("id"); // Assert assertNotNull(feature); assertSame(londonBridge, feature.getProperty(BRIDGE_NAME)); assertSame(location, feature.getProperty(LOCATION)); assertSame(description, feature.getProperty(DESCRIPTION)); } @Test public void test_buildFeature_missingDescription_descriptionDefaultsToNull() { // Arrange ComplexFeatureBuilder builder = new ComplexFeatureBuilder(BRIDGE_TYPE); builder.append(BRIDGE_NAME, londonBridge); builder.append(LOCATION, location); // Act Feature feature = builder.buildFeature("id"); // Assert assertNull(feature.getProperty(DESCRIPTION).getValue()); } @Test public void test_buildFeature_noLocationSet_throwsIllegalStateException() { // Arrange ComplexFeatureBuilder builder = new ComplexFeatureBuilder(BRIDGE_TYPE); // Deliberately not setting location builder.append(BRIDGE_NAME, londonBridge); // Act try { Feature feature = builder.buildFeature("id"); fail("expected an exception"); } catch (IllegalStateException ise) { String expectedMessage = "Failed to build feature 'urn:Bridge:Test:1.1:Bridge'; its property 'urn:Bridge:Test:1.1:location' requires at least 1 occurrence(s) but number of occurrences was 0."; if (ise.getMessage().compareTo(expectedMessage) != 0) { fail("Expected IllegalStateException to say: '" + expectedMessage + "' but got: '" + ise.getMessage() + "'"); } // Assert (This is the expected exception). return; } catch (Exception e) { fail("Expected IllegalStateException but it wasn't thrown; got " + e.getClass() + " instead. " + e.getMessage()); } }</pre></td></tr></table><p> </p><h1>Documentation Changes</h1><ul class="alternate"><li>New documentation will be required to cover the steps involved in processing a complex WFS response.</li><li><a href="http://docs.geotools.org/latest/userguide/library/data/wfs.html">http://docs.geotools.org/latest/userguide/library/data/wfs.html</a><ul><li>Add details of new connection parameter 'GML_COMPLIANCE_LEVEL'.</li><li>Add a new example of a WFS request showing the retrieval and consumption of a complex version.</li></ul></li></ul>
Please type the word appearing in the picture.
Attachments
Labels
Location
Watch this page
< Edit
Preview >
Loading…
Save
Cancel
Next hint
search
attachments
weblink
advanced