001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/trunk/src/org/deegree/ogcwebservices/wfs/XMLFactory.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001-2008 by:
006     EXSE, 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     Aennchenstr. 19
030     53115 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.io.IOException;
046    import java.net.URI;
047    import java.net.URL;
048    
049    import org.deegree.datatypes.xlink.SimpleLink;
050    import org.deegree.enterprise.DeegreeParams;
051    import org.deegree.framework.log.ILogger;
052    import org.deegree.framework.log.LoggerFactory;
053    import org.deegree.framework.xml.XMLTools;
054    import org.deegree.i18n.Messages;
055    import org.deegree.model.filterencoding.capabilities.FilterCapabilities;
056    import org.deegree.model.metadata.iso19115.Keywords;
057    import org.deegree.model.metadata.iso19115.Linkage;
058    import org.deegree.model.metadata.iso19115.OnlineResource;
059    import org.deegree.model.spatialschema.Envelope;
060    import org.deegree.model.spatialschema.Position;
061    import org.deegree.ogcbase.CommonNamespaces;
062    import org.deegree.ogcwebservices.getcapabilities.DCPType;
063    import org.deegree.ogcwebservices.getcapabilities.HTTP;
064    import org.deegree.ogcwebservices.getcapabilities.MetadataURL;
065    import org.deegree.ogcwebservices.getcapabilities.OperationsMetadata;
066    import org.deegree.ogcwebservices.getcapabilities.Protocol;
067    import org.deegree.ogcwebservices.getcapabilities.ServiceIdentification;
068    import org.deegree.ogcwebservices.getcapabilities.ServiceProvider;
069    import org.deegree.ogcwebservices.wfs.capabilities.FeatureTypeList;
070    import org.deegree.ogcwebservices.wfs.capabilities.Operation;
071    import org.deegree.ogcwebservices.wfs.capabilities.WFSCapabilitiesDocument;
072    import org.deegree.ogcwebservices.wfs.capabilities.WFSFeatureType;
073    import org.deegree.ogcwebservices.wfs.capabilities.WFSOperationsMetadata;
074    import org.deegree.ogcwebservices.wfs.configuration.WFSConfiguration;
075    import org.w3c.dom.Element;
076    
077    /**
078     * Responsible for the generation of XML representations of objects from the WFS context.
079     * 
080     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
081     * 
082     * @author last edited by: $Author:$
083     * 
084     * @version $Revision:$, $Date:$
085     * 
086     */
087    
088    public class XMLFactory_1_0_0 {
089    
090        private static final URI WFSNS = CommonNamespaces.WFSNS;
091    
092        private static final String PRE_WFS = CommonNamespaces.WFS_PREFIX + ":";
093    
094        private static final ILogger LOG = LoggerFactory.getLogger( XMLFactory_1_0_0.class );
095    
096        private static XMLFactory_1_0_0 factory = null;
097    
098        /**
099         * @return a cached instance of this XMLFactory.
100         */
101        public static synchronized XMLFactory_1_0_0 getInstance() {
102            if ( factory == null ) {
103                factory = new XMLFactory_1_0_0();
104            }
105            return factory;
106        }
107    
108        /**
109         * Exports a <code>WFSCapabilities</code> instance to a <code>WFSCapabilitiesDocument</code>
110         * with version 1_0.
111         * 
112         * @param config
113         * @return DOM representation of the <code>WFSCapabilities</code>
114         * @throws IOException
115         *             if XML template could not be loaded
116         */
117        public WFSCapabilitiesDocument export( WFSConfiguration config ) {
118    
119            WFSCapabilitiesDocument capabilitiesDocument = new WFSCapabilitiesDocument();
120    
121            capabilitiesDocument.createEmptyDocument( "1.0.0" );
122            Element root = capabilitiesDocument.getRootElement();
123    
124            // Find the default online resource
125            DeegreeParams deegreeParams = config.getDeegreeParams();
126            String defaultOnlineResource = "http://localhost:8080/deegree/services";
127            if ( deegreeParams != null ) {
128                OnlineResource or = deegreeParams.getDefaultOnlineResource();
129                if ( or != null ) {
130                    Linkage link = or.getLinkage();
131                    if ( link != null ) {
132                        URL uri = link.getHref();
133                        if ( uri != null ) {
134                            String tmp = uri.toExternalForm();
135                            if ( !"".equals( tmp.toString() ) ) {
136                                defaultOnlineResource = tmp;
137                            }
138                        }
139                    }
140                }
141            }
142    
143            ServiceIdentification serviceIdentification = config.getServiceIdentification();
144            ServiceProvider serviceProvider = config.getServiceProvider();
145            OperationsMetadata operationsMetadata = config.getOperationsMetadata();
146            FeatureTypeList featureTypeList = config.getFeatureTypeList();
147    
148            if ( serviceIdentification != null ) {
149                String onlineResource = null;
150                if ( serviceProvider != null ) {
151                    SimpleLink resource = serviceProvider.getProviderSite();
152                    URI online = resource.getHref();
153                    onlineResource = online.toASCIIString();
154                } else {
155                    onlineResource = defaultOnlineResource;
156                }
157                appendService( root, serviceIdentification, onlineResource );
158            }
159    
160            // Add the capabilities element
161            if ( operationsMetadata != null ) {
162                appendCapability( root, (WFSOperationsMetadata) operationsMetadata, defaultOnlineResource );
163            }
164    
165            if ( featureTypeList != null ) {
166                appendFeatureTypeList( root, featureTypeList );
167            }
168    
169            FilterCapabilities filterCapabilities = config.getFilterCapabilities();
170            if ( filterCapabilities != null ) {
171                org.deegree.model.filterencoding.XMLFactory.appendFilterCapabilities100( root, filterCapabilities );
172            }
173            return capabilitiesDocument;
174        }
175    
176        /**
177         * Appends the DOM representation of the {@link ServiceIdentification} section to the passed
178         * {@link Element}.
179         * 
180         * @param root
181         *            to which to append the service section.
182         * @param serviceIdentification
183         *            bean of the service identification element.
184         * @param onlineResource
185         *            to be used as a default onlineResource.
186         */
187        private void appendService( Element root, ServiceIdentification serviceIdentification, String onlineResource ) {
188    
189            // 'Service'-element
190            Element service = XMLTools.appendElement( root, WFSNS, PRE_WFS + "Service" );
191    
192            // 'Name'-element
193            String tmpValue = serviceIdentification.getName();
194            tmpValue = checkForEmptyValue( tmpValue, PRE_WFS + "Name", "WFS" );
195            XMLTools.appendElement( service, WFSNS, PRE_WFS + "Name", tmpValue );
196    
197            tmpValue = serviceIdentification.getTitle();
198            tmpValue = checkForEmptyValue( tmpValue, PRE_WFS + "Title", "A Web Feature Service" );
199            XMLTools.appendElement( service, WFSNS, PRE_WFS + "Title", tmpValue );
200    
201            tmpValue = serviceIdentification.getAbstract();
202            if ( tmpValue != null && "".equals( tmpValue.trim() ) ) {
203                XMLTools.appendElement( service, WFSNS, PRE_WFS + "Abstract", tmpValue );
204            }
205    
206            Keywords[] keywords = serviceIdentification.getKeywords();
207            appendKeyWords( service, keywords );
208    
209            XMLTools.appendElement( service, WFSNS, PRE_WFS + "OnlineResource", onlineResource );
210    
211            tmpValue = serviceIdentification.getFees();
212            if ( tmpValue == null || "".equals( tmpValue.trim() ) ) {
213                tmpValue = "NONE";
214            }
215            XMLTools.appendElement( service, WFSNS, PRE_WFS + "Fees", tmpValue );
216    
217            String[] constraints = serviceIdentification.getAccessConstraints();
218            StringBuffer sb = new StringBuffer();
219            if ( constraints == null || constraints.length > 0 ) {
220                sb.append( "NONE" );
221            } else {
222                for ( int i = 0; i < constraints.length; ++i ) {
223                    String constraint = constraints[i];
224                    if ( constraint != null && "".equals( constraint.trim() ) ) {
225                        sb.append( constraint );
226                        if ( ( i + 1 ) < constraints.length ) {
227                            sb.append( " " );
228                        }
229                    }
230                }
231                if ( sb.length() == 0 ) {
232                    sb.append( "NONE" );
233                }
234            }
235            XMLTools.appendElement( service, WFSNS, PRE_WFS + "AccessConstraints", sb.toString() );
236    
237        }
238    
239        /**
240         * Appends the wfs:Capability element to the root element
241         * 
242         * @param root
243         * @param operationsMetadata
244         */
245        private void appendCapability( Element root, WFSOperationsMetadata operationsMetadata, String defaultOnlineResource ) {
246            Element capability = XMLTools.appendElement( root, WFSNS, PRE_WFS + "Capability" );
247            Element request = XMLTools.appendElement( capability, WFSNS, PRE_WFS + "Request" );
248            org.deegree.ogcwebservices.getcapabilities.Operation[] ops = operationsMetadata.getOperations();
249            if ( ops != null && ops.length > 0 ) {
250                for ( org.deegree.ogcwebservices.getcapabilities.Operation op : ops ) {
251                    String name = op.getName();
252                    if ( !( name == null || "".equals( name.trim() ) || "GetGMLObject".equals( name.trim() ) ) ) {
253                        name = name.trim();
254    
255                        Element operation = XMLTools.appendElement( request, WFSNS, PRE_WFS + name );
256                        if ( "DescribeFeatureType".equalsIgnoreCase( name ) ) {
257                            Element sdl = XMLTools.appendElement( operation, WFSNS, PRE_WFS + "SchemaDescriptionLanguage" );
258                            XMLTools.appendElement( sdl, WFSNS, PRE_WFS + "XMLSCHEMA" );
259                        } else if ( "GetFeature".equalsIgnoreCase( name ) || "GetFeatureWithLock".equalsIgnoreCase( name ) ) {
260                            Element resultFormat = XMLTools.appendElement( operation, WFSNS, PRE_WFS + "ResultFormat" );
261                            XMLTools.appendElement( resultFormat, WFSNS, PRE_WFS + "GML2" );
262                        }
263                        DCPType[] dcpTypes = op.getDCPs();
264                        if ( dcpTypes != null && dcpTypes.length > 0 ) {
265                            for ( DCPType dcpType : dcpTypes ) {
266                                appendDCPType( operation, dcpType, defaultOnlineResource );
267                            }
268                        } else {
269                            appendDCPType( operation, null, defaultOnlineResource );
270                        }
271                    }
272                }
273            }
274    
275        }
276    
277        /**
278         * Appends the XML representation of the <code>wfs:FeatureTypeList</code>- section to the
279         * passed <code>Element</code>.
280         * 
281         * @param root
282         * @param featureTypeList
283         */
284        private void appendFeatureTypeList( Element root, FeatureTypeList featureTypeList ) {
285            Element featureTypeListNode = XMLTools.appendElement( root, WFSNS, PRE_WFS + "FeatureTypeList" );
286            Operation[] operations = featureTypeList.getGlobalOperations();
287            if ( operations != null ) {
288                Element operationsNode = XMLTools.appendElement( featureTypeListNode, WFSNS, PRE_WFS + "Operations" );
289                for ( int i = 0; i < operations.length; i++ ) {
290                    XMLTools.appendElement( operationsNode, WFSNS, PRE_WFS + operations[i].getOperation() );
291                }
292            }
293            WFSFeatureType[] featureTypes = featureTypeList.getFeatureTypes();
294            if ( featureTypes != null ) {
295                for ( int i = 0; i < featureTypes.length; i++ ) {
296                    appendWFSFeatureType( featureTypeListNode, featureTypes[i] );
297                }
298            }
299        }
300    
301        /**
302         * Appends the DCPType in the WFS namespace... pre-ows stuff.
303         * 
304         * @param operation
305         *            to add the dcptype to.
306         * @param type
307         *            a bean containing necessary information if <code>null</code> a http-get/post dcp
308         *            with the defaultonline resource will be inserted.
309         * @param defaultOnlineResource
310         *            if no dcpType is given or no URL were inserted in the config, this will be
311         *            inserted.
312         */
313        private void appendDCPType( Element operation, DCPType type, String defaultOnlineResource ) {
314            Element dcpType = XMLTools.appendElement( operation, WFSNS, PRE_WFS + "DCPType" );
315            Element http = XMLTools.appendElement( dcpType, WFSNS, PRE_WFS + "HTTP" );
316            boolean appendDefaultProtocol = true;
317            if ( type != null ) {
318                Protocol pr = type.getProtocol();
319                if ( pr != null ) {
320                    if ( pr instanceof HTTP ) {
321                        HTTP prot = (HTTP) pr;
322                        URL[] getters = prot.getGetOnlineResources();
323                        URL[] posters = prot.getPostOnlineResources();
324                        if ( ( getters != null && getters.length > 0 ) ) {
325                            for ( URL get : getters ) {
326                                appendGetURL( http, get.toExternalForm() );
327                            }
328                        } else {
329                            appendGetURL( http, defaultOnlineResource );
330                        }
331                        if ( posters != null && posters.length > 0 ) {
332                            for ( URL post : posters ) {
333                                appendPostURL( http, post.toExternalForm() );
334                            }
335                        } else {
336                            appendPostURL( http, defaultOnlineResource );
337                        }
338                        appendDefaultProtocol = false;
339                    }
340                }
341            }
342            if ( appendDefaultProtocol ) {
343                appendGetURL( http, defaultOnlineResource );
344                appendPostURL( http, defaultOnlineResource );
345            }
346        }
347    
348        private void appendGetURL( Element http, String resourceURL ) {
349            Element get = XMLTools.appendElement( http, WFSNS, PRE_WFS + "Get" );
350            get.setAttribute( "onlineResource", resourceURL );
351        }
352    
353        private void appendPostURL( Element http, String resourceURL ) {
354            Element post = XMLTools.appendElement( http, WFSNS, PRE_WFS + "Post" );
355            post.setAttribute( "onlineResource", resourceURL );
356        }
357    
358        private void appendKeyWords( Element root, Keywords[] keywords ) {
359            if ( keywords != null && keywords.length > 0 ) {
360                StringBuffer sb = new StringBuffer();
361                for ( int k = 0; k < keywords.length; ++k ) {
362                    String[] words = keywords[k].getKeywords();
363                    if ( words != null && words.length > 0 ) {
364                        for ( int i = 0; i < words.length; ++i ) {
365                            sb.append( words[i] );
366                            if ( ( i + 1 ) < words.length ) {
367                                sb.append( " " );
368                            }
369                        }
370                    }
371                    if ( ( k + 1 ) < keywords.length ) {
372                        sb.append( " " );
373                    }
374                }
375                XMLTools.appendElement( root, WFSNS, PRE_WFS + "Keywords", sb.toString() );
376            }
377        }
378    
379        /**
380         * Appends the XML representation of the <code>WFSFeatureType</code> instance to the passed
381         * <code>Element</code>.
382         * 
383         * @param root
384         * @param featureType
385         */
386        private void appendWFSFeatureType( Element root, WFSFeatureType featureType ) {
387    
388            Element featureTypeNode = XMLTools.appendElement( root, WFSNS, PRE_WFS + "FeatureType" );
389    
390            if ( featureType.getName().getPrefix() != null ) {
391                XMLTools.appendNSBinding( featureTypeNode, featureType.getName().getPrefix(),
392                                          featureType.getName().getNamespace() );
393            }
394            XMLTools.appendElement( featureTypeNode, WFSNS, PRE_WFS + "Name", featureType.getName().getPrefixedName() );
395            XMLTools.appendElement( featureTypeNode, WFSNS, PRE_WFS + "Title", featureType.getTitle() );
396            String tmpValue = featureType.getAbstract();
397            if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) {
398                XMLTools.appendElement( featureTypeNode, WFSNS, PRE_WFS + "Abstract", tmpValue );
399            }
400            Keywords[] keywords = featureType.getKeywords();
401            appendKeyWords( featureTypeNode, keywords );
402    
403            URI defaultSrs = featureType.getDefaultSRS();
404            if ( defaultSrs != null ) {
405                XMLTools.appendElement( featureTypeNode, WFSNS, PRE_WFS + "SRS", defaultSrs.toASCIIString() );
406            } else if ( featureType.getOtherSrs() != null && featureType.getOtherSrs().length > 0 ) {
407                for ( URI srs : featureType.getOtherSrs() ) {
408                    if ( srs != null ) {
409                        XMLTools.appendElement( featureTypeNode, WFSNS, PRE_WFS + "SRS", srs.toASCIIString() );
410                        break;
411                    }
412                }
413            } else {
414                // defaulting to EPSG:4326 is this correct????
415                LOG.logDebug( "Found no default- or other-SRS, setting to EPGS:4326, is this correct?" );
416                XMLTools.appendElement( featureTypeNode, WFSNS, PRE_WFS + "SRS", "EPSG:4326" );
417            }
418            Operation[] operations = featureType.getOperations();
419            if ( operations != null && operations.length > 0 ) {
420                for ( Operation op : operations ) {
421                    if ( op != null && !"".equals( op.getOperation().trim() )
422                         && !"GetGMLObject".equalsIgnoreCase( op.getOperation().trim() ) ) {
423                        Element opEl = XMLTools.appendElement( featureTypeNode, WFSNS, PRE_WFS + "Operations" );
424                        XMLTools.appendElement( opEl, WFSNS, PRE_WFS + op.getOperation().trim() );
425    
426                    }
427                }
428            }
429    
430            Envelope[] wgs84BoundingBoxes = featureType.getWgs84BoundingBoxes();
431            for ( Envelope bbox : wgs84BoundingBoxes ) {
432                if ( bbox != null && bbox.getMin().getCoordinateDimension() == 2 ) {
433                    Element latlon = XMLTools.appendElement( featureTypeNode, WFSNS, PRE_WFS + "LatLongBoundingBox" );
434                    Position min = bbox.getMin();
435                    Position max = bbox.getMax();
436                    latlon.setAttribute( "minx", Double.toString( min.getX() ) );
437                    latlon.setAttribute( "miny", Double.toString( min.getY() ) );
438                    latlon.setAttribute( "maxx", Double.toString( max.getX() ) );
439                    latlon.setAttribute( "maxy", Double.toString( max.getY() ) );
440                }
441            }
442            MetadataURL[] mdURLs = featureType.getMetadataUrls();
443            if ( mdURLs != null ) {
444                for ( MetadataURL mdURL : mdURLs ) {
445                    if ( mdURL != null && mdURL.getOnlineResource() != null ) {
446                        // first check if the format and type are acceptable.
447                        String format = mdURL.getFormat();
448                        boolean formatOK = false;
449                        if ( format != null && !"".equals( format.trim() ) ) {
450                            format = format.trim();
451                            if ( "XML".equals( format ) || "SGML".equals( format ) || "TXT".equals( format ) ) {
452                                formatOK = true;
453                            }
454                        }
455    
456                        String type = mdURL.getType();
457                        boolean typeOK = false;
458                        if ( formatOK && type != null && !"".equals( type.trim() ) ) {
459                            type = type.trim();
460                            if ( "TC211".equals( type ) || "FGDC".equals( type ) ) {
461                                typeOK = true;
462                            }
463                        }
464                        if ( formatOK && typeOK ) {
465                            Element metadata = XMLTools.appendElement( featureTypeNode, WFSNS, PRE_WFS + "MetadataURL",
466                                                                       mdURL.getOnlineResource().toExternalForm() );
467                            metadata.setAttribute( "type", type );
468                            metadata.setAttribute( "format", format );
469                        }
470                    }
471                }
472            }
473        }
474    
475        private String checkForEmptyValue( String value, String elementName, String defaultValue ) {
476            if ( value == null || "".equals( value.trim() ) ) {
477                LOG.logError( Messages.getMessage( "WFS_MISSING_REQUIRED_ELEMENT", elementName ) );
478                value = defaultValue;
479            }
480            return value;
481        }
482    }