Source Code Cross Referenced for FeatureDisambiguator.java in  » GIS » deegree » org » deegree » ogcwebservices » wfs » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » GIS » deegree » org.deegree.ogcwebservices.wfs 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/ogcwebservices/wfs/FeatureDisambiguator.java $
002:        /*----------------    FILE HEADER  ------------------------------------------
003:
004:         This file is part of deegree.
005:         Copyright (C) 2001-2008 by:
006:         Department of Geography, University of Bonn
007:         http://www.giub.uni-bonn.de/deegree/
008:         lat/lon GmbH
009:         http://www.lat-lon.de
010:
011:         This library is free software; you can redistribute it and/or
012:         modify it under the terms of the GNU Lesser General Public
013:         License as published by the Free Software Foundation; either
014:         version 2.1 of the License, or (at your option) any later version.
015:
016:         This library is distributed in the hope that it will be useful,
017:         but WITHOUT ANY WARRANTY; without even the implied warranty of
018:         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
019:         Lesser General Public License for more details.
020:
021:         You should have received a copy of the GNU Lesser General Public
022:         License along with this library; if not, write to the Free Software
023:         Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
024:
025:         Contact:
026:
027:         Andreas Poth
028:         lat/lon GmbH
029:         Aennchenstraße 19
030:         53177 Bonn
031:         Germany
032:         E-Mail: poth@lat-lon.de
033:
034:         Prof. Dr. Klaus Greve
035:         Department of Geography
036:         University of Bonn
037:         Meckenheimer Allee 166
038:         53115 Bonn
039:         Germany
040:         E-Mail: greve@giub.uni-bonn.de
041:        
042:         ---------------------------------------------------------------------------*/
043:        package org.deegree.ogcwebservices.wfs;
044:
045:        import java.util.ArrayList;
046:        import java.util.Collection;
047:        import java.util.HashMap;
048:        import java.util.HashSet;
049:        import java.util.List;
050:        import java.util.Map;
051:        import java.util.Set;
052:        import java.util.UUID;
053:
054:        import org.deegree.framework.log.ILogger;
055:        import org.deegree.framework.log.LoggerFactory;
056:        import org.deegree.i18n.Messages;
057:        import org.deegree.io.datastore.DatastoreException;
058:        import org.deegree.io.datastore.schema.MappedFeatureType;
059:        import org.deegree.io.datastore.schema.MappedPropertyType;
060:        import org.deegree.model.feature.Feature;
061:        import org.deegree.model.feature.FeatureCollection;
062:        import org.deegree.model.feature.FeatureFactory;
063:        import org.deegree.model.feature.FeatureProperty;
064:        import org.deegree.model.feature.XLinkedFeatureProperty;
065:        import org.deegree.model.feature.schema.PropertyType;
066:        import org.deegree.model.spatialschema.Geometry;
067:
068:        /**
069:         * Responsible for the normalization of feature collections that are going to be persisted (i.e.
070:         * inserted into a {@link org.deegree.io.datastore Datastore}). This is necessary, because it is up
071:         * to WFS clients whether feature ids (gml:id attribute) are provided in an insert/update request or
072:         * not.
073:         * <p>
074:         * After processing, the resulting feature collection meets the following requirements:
075:         * <ul>
076:         * <li>Every member feature (and subfeature) has a unique feature id that disambiguates it. Note
077:         * that this id is only momentarily valid, and that the final feature id is generated by the
078:         * <code>FeatureIdAssigner</code> class in a later step.</li>
079:         * <li>Features that are equal according to the annotated schema (deegreewfs:IdentityPart
080:         * declarations) are represented by the same feature instance.</li>
081:         * <li>Complex feature properties use xlink to specify their content if necessary.</li>
082:         * <li>Root features in the feature collection never use xlinks.</li>
083:         * </ul>
084:         * 
085:         * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider</a>
086:         * @author last edited by: $Author: apoth $
087:         * 
088:         * @version $Revision: 9345 $, $Date: 2007-12-27 08:22:25 -0800 (Thu, 27 Dec 2007) $
089:         */
090:        class FeatureDisambiguator {
091:
092:            private static final ILogger LOG = LoggerFactory
093:                    .getLogger(FeatureDisambiguator.class);
094:
095:            private FeatureCollection fc;
096:
097:            private Map<MappedFeatureType, Set<Feature>> ftMap;
098:
099:            // key: feature id, value: feature instance (representer for all features with same id)
100:            private Map<String, Feature> representerMap = new HashMap<String, Feature>();
101:
102:            /**
103:             * Creates a new <code>FeatureDisambiguator</code> to disambiguate the given feature
104:             * collection.
105:             * 
106:             * @param fc
107:             *            feature collection to disambiguate
108:             */
109:            FeatureDisambiguator(FeatureCollection fc) {
110:                this .fc = fc;
111:                this .ftMap = buildFeatureTypeMap(fc);
112:            }
113:
114:            /**
115:             * Checks if any anonymous features (without id) are present in the feature collection.
116:             * 
117:             * @return true, if one or more anonymous features are present, false otherwise
118:             */
119:            boolean checkForAnonymousFeatures() {
120:                for (MappedFeatureType ft : this .ftMap.keySet()) {
121:                    for (Feature feature : this .ftMap.get(ft)) {
122:                        if (feature.getId() == null
123:                                || feature.getId().equals("")) {
124:                            return true;
125:                        }
126:                    }
127:                }
128:                return false;
129:            }
130:
131:            /**
132:             * Merges all "equal" feature (and subfeature) instances in the associated feature collection
133:             * that do not have a feature id. Afterwards, every feature (and subfeature) in the collection
134:             * has a unique feature id.
135:             * <p>
136:             * It is considered an error if there is more than root feature with the same id after the
137:             * identification of equal features.
138:             * <p>
139:             * Furthermore, there is always only one feature instance with a certain id, i.e. "equal"
140:             * features are represented by the same feature instance.
141:             * 
142:             * @return "merged" feature collection
143:             * @throws DatastoreException
144:             */
145:            FeatureCollection mergeFeatures() throws DatastoreException {
146:
147:                assignFIDsAndRepresenters();
148:                checkForEqualRootFeatures();
149:                replaceDuplicateFeatures();
150:                return this .fc;
151:            }
152:
153:            /**
154:             * Assigns a (temporarily) feature id to every anonymous feature (or subfeature) of the given
155:             * type in the feature collection.
156:             * <p>
157:             * Also builds the <code>representerMap</code>, so each feature id is mapped to the feature
158:             * instance that is used as the single representer for all features instances with this id.
159:             * <p>
160:             * It is ensured that for each feature id that is associated with a root feature of the
161:             * collection, the root feature is used as the representing feature instance. This is important
162:             * to guarantee that the root features in the collection represent themselves and need not to be
163:             * replaced in {@link #replaceDuplicateFeatures()}.
164:             * 
165:             * @throws DatastoreException
166:             */
167:            private void assignFIDsAndRepresenters() throws DatastoreException {
168:
169:                for (MappedFeatureType ft : this .ftMap.keySet()) {
170:                    assignFIDs(ft);
171:                }
172:
173:                // ensure that every root feature is the "representer" for it's feature id
174:                for (int i = 0; i < this .fc.size(); i++) {
175:                    Feature rootFeature = this .fc.getFeature(i);
176:                    String fid = rootFeature.getId();
177:                    this .representerMap.put(fid, rootFeature);
178:                }
179:            }
180:
181:            /**
182:             * Assigns a (temporarily) feature id to every anonymous feature (or subfeature) of the given
183:             * type in the feature collection.
184:             * <p>
185:             * Also builds the <code>representerMap</code>, so every feature id is mapped to a single
186:             * feature instance that will represent it everywhere in the collection.
187:             * 
188:             * @param ft
189:             * @throws DatastoreException
190:             */
191:            private void assignFIDs(MappedFeatureType ft)
192:                    throws DatastoreException {
193:
194:                Collection<Feature> features = this .ftMap.get(ft);
195:
196:                LOG.logDebug("Identifying " + features.size()
197:                        + " features of type '" + ft.getName() + "'.");
198:
199:                for (Feature feature : features) {
200:                    // only find features "equal" to feature, if feature does not have an id yet
201:                    if (feature.getId() == null || "".equals(feature.getId())) {
202:                        List<Feature> equalFeatures = new ArrayList<Feature>();
203:                        equalFeatures.add(feature);
204:
205:                        for (Feature otherFeature : features) {
206:                            if (compareFeatures(feature, otherFeature,
207:                                    new HashMap<Feature, List<Feature>>())) {
208:                                LOG
209:                                        .logDebug("Found matching features of type: '"
210:                                                + ft.getName() + "'.");
211:                                equalFeatures.add(otherFeature);
212:                            }
213:                        }
214:                        assignSameFID(equalFeatures);
215:                    }
216:                }
217:
218:                for (Feature feature : features) {
219:                    String fid = feature.getId();
220:                    if (this .representerMap.get(fid) == null) {
221:                        this .representerMap.put(fid, feature);
222:                    }
223:                }
224:            }
225:
226:            /**
227:             * Assigns the same feature id to every feature in the given list of "equal" features.
228:             * <p>
229:             * If any feature in the list has a feature id assigned to it already, this feature id is used.
230:             * If no feature has a feature id, a new feature id (a UUID) is generated.
231:             * 
232:             * @param equalFeatures
233:             * @throws DatastoreException
234:             */
235:            private void assignSameFID(List<Feature> equalFeatures)
236:                    throws DatastoreException {
237:
238:                LOG.logDebug("Found " + equalFeatures.size()
239:                        + " 'equal' features of type "
240:                        + equalFeatures.get(0).getFeatureType().getName());
241:
242:                String fid = null;
243:
244:                // check if any feature has a fid already
245:                for (Feature feature : equalFeatures) {
246:                    String otherFid = feature.getId();
247:                    if (otherFid != null && !otherFid.equals("")) {
248:                        if (fid != null && !fid.equals(otherFid)) {
249:                            String msg = Messages.getMessage(
250:                                    "WFS_IDENTICAL_FEATURES", feature
251:                                            .getFeatureType().getName(), fid,
252:                                    otherFid);
253:                            throw new DatastoreException(msg);
254:                        }
255:                        fid = otherFid;
256:                    }
257:                }
258:
259:                if (fid == null) {
260:                    fid = UUID.randomUUID().toString();
261:                    this .representerMap.put(fid, equalFeatures.get(0));
262:                }
263:
264:                // assign fid to every "equal" feature
265:                for (Feature feature : equalFeatures) {
266:                    feature.setId(fid);
267:                }
268:            }
269:
270:            /**
271:             * Determines whether two feature instances are "equal" according to the annotated schema
272:             * (deegreewfs:IdentityPart declarations).
273:             * 
274:             * @param feature1
275:             * @param feature2
276:             * @param compareMap
277:             * @return true, if the two features are "equal", false otherwise
278:             */
279:            private boolean compareFeatures(Feature feature1, Feature feature2,
280:                    Map<Feature, List<Feature>> compareMap) {
281:
282:                LOG
283:                        .logDebug("feature1: " + feature1.getName() + " id="
284:                                + feature1.getId() + " hashCode="
285:                                + feature1.hashCode());
286:                LOG
287:                        .logDebug("feature2: " + feature2.getName() + " id="
288:                                + feature2.getId() + " hashCode="
289:                                + feature2.hashCode());
290:
291:                // same feature instance? -> equal
292:                if (feature1 == feature2) {
293:                    return true;
294:                }
295:
296:                // same feature id -> equal / different feature id -> not equal
297:                String fid1 = feature1.getId();
298:                String fid2 = feature2.getId();
299:                if (fid1 != null && fid2 != null && !"".equals(fid1)
300:                        && !"".equals(fid2)) {
301:                    return fid1.equals(fid2);
302:                }
303:
304:                // different feature types? -> not equal
305:                MappedFeatureType ft = (MappedFeatureType) feature1
306:                        .getFeatureType();
307:                if (feature2.getFeatureType() != ft) {
308:                    return false;
309:                }
310:
311:                // feature id is identity part? -> not equal (unique ids for all anonymous features)
312:                if (ft.getGMLId().isIdentityPart()) {
313:                    return false;
314:                }
315:
316:                // there is already a compareFeatures() call with these features on the stack
317:                // -> end recursion
318:                List<Feature> features = compareMap.get(feature1);
319:                if (features == null) {
320:                    features = new ArrayList<Feature>();
321:                    compareMap.put(feature1, features);
322:                } else {
323:                    for (Feature feature : features) {
324:                        if (feature2 == feature) {
325:                            return true;
326:                        }
327:                    }
328:                }
329:                features.add(feature2);
330:
331:                features = compareMap.get(feature2);
332:                if (features == null) {
333:                    features = new ArrayList<Feature>();
334:                    compareMap.put(feature2, features);
335:                } else {
336:                    for (Feature feature : features) {
337:                        if (feature1 == feature) {
338:                            return true;
339:                        }
340:                    }
341:                }
342:                features.add(feature1);
343:
344:                // check every "relevant" property for equality
345:                PropertyType[] properties = ft.getProperties();
346:                for (int i = 0; i < properties.length; i++) {
347:                    MappedPropertyType propertyType = (MappedPropertyType) properties[i];
348:                    if (propertyType.isIdentityPart()) {
349:                        if (!compareProperties(propertyType, feature1,
350:                                feature2, compareMap)) {
351:                            LOG
352:                                    .logDebug("Not equal: values for property '"
353:                                            + propertyType.getName()
354:                                            + " do not match.");
355:                            return false;
356:                        }
357:                    }
358:                }
359:                return true;
360:            }
361:
362:            /**
363:             * Determines whether two feature instances have the same content in the specified property.
364:             * 
365:             * @param propertyType
366:             * @param feature1
367:             * @param feature2
368:             * @param compareMap
369:             * @return true, if the properties are "equal", false otherwise
370:             */
371:            private boolean compareProperties(MappedPropertyType propertyType,
372:                    Feature feature1, Feature feature2,
373:                    Map<Feature, List<Feature>> compareMap) {
374:
375:                FeatureProperty[] props1 = feature1.getProperties(propertyType
376:                        .getName());
377:                FeatureProperty[] props2 = feature2.getProperties(propertyType
378:                        .getName());
379:
380:                if (props1 != null && props2 != null) {
381:                    if (props1.length != props2.length) {
382:                        return false;
383:                    }
384:                    // TODO handle different orders of multi properties
385:                    for (int j = 0; j < props1.length; j++) {
386:                        Object value1 = props1[j].getValue();
387:                        Object value2 = props2[j].getValue();
388:
389:                        if (value1 == null && value2 == null) {
390:                            continue;
391:                        } else if (!(value1 != null && value2 != null)) {
392:                            return false;
393:                        }
394:
395:                        if (value1 instanceof  Feature) {
396:                            // complex valued property (subfeature)
397:                            if (!(value2 instanceof  Feature)) {
398:                                return false;
399:                            }
400:                            Feature subfeature1 = (Feature) value1;
401:                            Feature subfeature2 = (Feature) value2;
402:
403:                            if (!compareFeatures(subfeature1, subfeature2,
404:                                    compareMap)) {
405:                                return false;
406:                            }
407:                        } else {
408:                            if (value1 instanceof  Geometry) {
409:                                String msg = "Check for equal geometry properties is not implemented yet. "
410:                                        + "Do not set 'identityPart' to true in geometry property "
411:                                        + "definitions.";
412:                                throw new RuntimeException(msg);
413:                            }
414:                            // simple valued property
415:                            if (!value1.equals(value2)) {
416:                                return false;
417:                            }
418:                        }
419:                    }
420:                } else if (!(props1 == null && props2 == null)) {
421:                    return false;
422:                }
423:                return true;
424:            }
425:
426:            /**
427:             * Checks that there are no root features in the collection that are "equal".
428:             * <p>
429:             * After disambiguation these features have the same feature id.
430:             * 
431:             * @throws DatastoreException
432:             */
433:            private void checkForEqualRootFeatures() throws DatastoreException {
434:                Set<String> rootFIDs = new HashSet<String>();
435:                for (int i = 0; i < fc.size(); i++) {
436:                    String fid = fc.getFeature(i).getId();
437:                    if (rootFIDs.contains(fid)) {
438:                        String msg = Messages
439:                                .getMessage("WFS_SAME_ROOT_FEATURE_ID");
440:                        throw new DatastoreException(msg);
441:                    }
442:                    rootFIDs.add(fid);
443:                }
444:            }
445:
446:            /**
447:             * Determines the feature type of all features (and subfeatures) in the given feature collection
448:             * and builds a lookup map.
449:             * 
450:             * @param fc
451:             * @return lookup map that maps each feature instance to it's feature type
452:             */
453:            private Map<MappedFeatureType, Set<Feature>> buildFeatureTypeMap(
454:                    FeatureCollection fc) {
455:                LOG.logDebug("Building feature map.");
456:                Map<MappedFeatureType, Set<Feature>> ftMap = new HashMap<MappedFeatureType, Set<Feature>>();
457:                for (int i = 0; i < fc.size(); i++) {
458:                    addToFeatureTypeMap(fc.getFeature(i), ftMap);
459:                }
460:                return ftMap;
461:            }
462:
463:            /**
464:             * Recursively adds the given feature (and it's subfeatures) to the given map. To cope with
465:             * cyclic features, the recursion ends if the feature instance is already present in the map.
466:             * 
467:             * @param feature
468:             *            feature instance to add
469:             * @param ftMap
470:             */
471:            private void addToFeatureTypeMap(Feature feature,
472:                    Map<MappedFeatureType, Set<Feature>> ftMap) {
473:
474:                MappedFeatureType ft = (MappedFeatureType) feature
475:                        .getFeatureType();
476:                Set<Feature> features = ftMap.get(ft);
477:                if (features == null) {
478:                    features = new HashSet<Feature>();
479:                    ftMap.put(ft, features);
480:                } else {
481:                    if (features.contains(feature)) {
482:                        return;
483:                    }
484:                }
485:                features.add(feature);
486:
487:                // recurse into subfeatures
488:                FeatureProperty[] properties = feature.getProperties();
489:                for (int i = 0; i < properties.length; i++) {
490:                    Object propertyValue = properties[i].getValue();
491:                    if (propertyValue instanceof  Feature) {
492:                        Feature subFeature = (Feature) propertyValue;
493:                        addToFeatureTypeMap(subFeature, ftMap);
494:                    }
495:                }
496:            }
497:
498:            /**
499:             * Ensures that all features with the same feature id refer to the same feature instance.
500:             * <p>
501:             * Xlinked content is used for every subfeature that has been encountered already (or is a root
502:             * feature of the collection).
503:             * <p>
504:             * Root features are never replaced, because {@link #assignFIDsAndRepresenters()} ensures that
505:             * root features always represent themselves.
506:             */
507:            private void replaceDuplicateFeatures() {
508:
509:                Set<String> xlinkFIDs = new HashSet<String>();
510:
511:                // ensure that root features are always referred to by xlink properties
512:                for (int i = 0; i < this .fc.size(); i++) {
513:                    Feature feature = this .fc.getFeature(i);
514:                    xlinkFIDs.add(feature.getId());
515:                }
516:
517:                for (int i = 0; i < this .fc.size(); i++) {
518:                    Feature feature = this .fc.getFeature(i);
519:                    replaceDuplicateFeatures(feature, xlinkFIDs);
520:                }
521:            }
522:
523:            /**
524:             * Replaces all subfeatures of the given feature instance by their "representers".
525:             * <p>
526:             * Xlinked content is used for every subfeature that has been encountered already (or that is a
527:             * root feature of the collection).
528:             * 
529:             * @param feature
530:             * @param xlinkFIDs
531:             */
532:            private void replaceDuplicateFeatures(Feature feature,
533:                    Set<String> xlinkFIDs) {
534:
535:                LOG.logDebug("Replacing in feature: '" + feature.getName()
536:                        + "', " + feature.getId());
537:                xlinkFIDs.add(feature.getId());
538:
539:                FeatureProperty[] properties = feature.getProperties();
540:                for (int i = 0; i < properties.length; i++) {
541:                    Object value = properties[i].getValue();
542:                    if (value != null && value instanceof  Feature) {
543:
544:                        Feature subfeature = (Feature) value;
545:                        String fid = subfeature.getId();
546:                        subfeature = this .representerMap.get(fid);
547:
548:                        FeatureProperty oldProperty = properties[i];
549:                        FeatureProperty newProperty = null;
550:
551:                        if (!xlinkFIDs.contains(fid)) {
552:                            // first occurence in feature collection
553:                            LOG.logDebug("Not-XLink feature property: " + fid);
554:                            newProperty = FeatureFactory.createFeatureProperty(
555:                                    oldProperty.getName(), subfeature);
556:                            replaceDuplicateFeatures(subfeature, xlinkFIDs);
557:                        } else {
558:                            // successive occurence in feature collection (use XLink)
559:                            LOG.logDebug("XLink feature property: " + fid);
560:                            newProperty = new XLinkedFeatureProperty(
561:                                    oldProperty.getName(), fid);
562:                            newProperty.setValue(subfeature);
563:                        }
564:                        feature.replaceProperty(oldProperty, newProperty);
565:                    }
566:                }
567:            }
568:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.