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-2007 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> with version 1_0.
110         * 
111         * @param config
112         * @return DOM representation of the <code>WFSCapabilities</code>
113         * @throws IOException
114         *             if XML template could not be loaded
115         */
116        public WFSCapabilitiesDocument export( WFSConfiguration config ) {
117    
118            WFSCapabilitiesDocument capabilitiesDocument = new WFSCapabilitiesDocument();
119    
120            capabilitiesDocument.createEmptyDocument( "1.0.0" );
121            Element root = capabilitiesDocument.getRootElement();
122    
123            // Find the default online resource
124            DeegreeParams deegreeParams = config.getDeegreeParams();
125            String defaultOnlineResource = "http://localhost:8080/deegree/services";
126            if ( deegreeParams != null ) {
127                OnlineResource or = deegreeParams.getDefaultOnlineResource();
128                if ( or != null ) {
129                    Linkage link = or.getLinkage();
130                    if ( link != null ) {
131                        URL uri = link.getHref();
132                        if ( uri != null ) {
133                            String tmp = uri.toExternalForm();
134                            if ( !"".equals( tmp.toString() ) ) {
135                                defaultOnlineResource = tmp;
136                            }
137                        }
138                    }
139                }
140            }
141    
142            ServiceIdentification serviceIdentification = config.getServiceIdentification();
143            ServiceProvider serviceProvider = config.getServiceProvider();
144            OperationsMetadata operationsMetadata = config.getOperationsMetadata();
145            FeatureTypeList featureTypeList = config.getFeatureTypeList();
146    
147            if ( serviceIdentification != null ) {
148                String onlineResource = null;
149                if ( serviceProvider != null ) {
150                    SimpleLink resource = serviceProvider.getProviderSite();
151                    URI online = resource.getHref();
152                    onlineResource = online.toASCIIString();
153                } else {
154                    onlineResource = defaultOnlineResource;
155                }
156                appendService( root, serviceIdentification, onlineResource );
157            }
158    
159            // Add the capabilities element
160            if ( operationsMetadata != null ) {
161                appendCapability( root, (WFSOperationsMetadata) operationsMetadata, defaultOnlineResource );
162            }
163    
164            if ( featureTypeList != null ) {
165                appendFeatureTypeList( root, featureTypeList );
166            }
167    
168            FilterCapabilities filterCapabilities = config.getFilterCapabilities();
169            if ( filterCapabilities != null ) {
170                org.deegree.model.filterencoding.XMLFactory.appendFilterCapabilities100( root, filterCapabilities );
171            }
172            return capabilitiesDocument;
173        }
174    
175        /**
176         * Appends the DOM representation of the {@link ServiceIdentification} section to the passed {@link Element}.
177         * 
178         * @param root
179         *            to which to append the service section.
180         * @param serviceIdentification
181         *            bean of the service identification element.
182         * @param onlineResource
183         *            to be used as a default onlineResource.
184         */
185        private void appendService( Element root, ServiceIdentification serviceIdentification, String onlineResource ) {
186    
187            // 'Service'-element
188            Element service = XMLTools.appendElement( root, WFSNS, PRE_WFS + "Service" );
189    
190            // 'Name'-element
191            String tmpValue = serviceIdentification.getName();
192            tmpValue = checkForEmptyValue( tmpValue, PRE_WFS + "Name", "WFS" );
193            XMLTools.appendElement( service, WFSNS, PRE_WFS + "Name", tmpValue );
194    
195            tmpValue = serviceIdentification.getTitle();
196            tmpValue = checkForEmptyValue( tmpValue, PRE_WFS + "Title", "A Web Feature Service" );
197            XMLTools.appendElement( service, WFSNS, PRE_WFS + "Title", tmpValue );
198    
199            tmpValue = serviceIdentification.getAbstract();
200            if ( tmpValue != null && "".equals( tmpValue.trim() ) ) {
201                XMLTools.appendElement( service, WFSNS, PRE_WFS + "Abstract", tmpValue );
202            }
203    
204            Keywords[] keywords = serviceIdentification.getKeywords();
205            appendKeyWords( service, keywords );
206    
207            XMLTools.appendElement( service, WFSNS, PRE_WFS + "OnlineResource", onlineResource );
208    
209            tmpValue = serviceIdentification.getFees();
210            if ( tmpValue == null || "".equals( tmpValue.trim() ) ) {
211                tmpValue = "NONE";
212            }
213            XMLTools.appendElement( service, WFSNS, PRE_WFS + "Fees", tmpValue );
214    
215            String[] constraints = serviceIdentification.getAccessConstraints();
216            StringBuffer sb = new StringBuffer();
217            if ( constraints == null || constraints.length > 0 ) {
218                sb.append( "NONE" );
219            } else {
220                for ( int i = 0; i < constraints.length; ++i ) {
221                    String constraint = constraints[i];
222                    if ( constraint != null && "".equals( constraint.trim() ) ) {
223                        sb.append( constraint );
224                        if ( ( i + 1 ) < constraints.length ) {
225                            sb.append( " " );
226                        }
227                    }
228                }
229                if ( sb.length() == 0 ) {
230                    sb.append( "NONE" );
231                }
232            }
233            XMLTools.appendElement( service, WFSNS, PRE_WFS + "AccessConstraints", sb.toString() );
234    
235        }
236    
237        /**
238         * Appends the wfs:Capability element to the root element
239         * 
240         * @param root
241         * @param operationsMetadata
242         */
243        private void appendCapability( Element root, WFSOperationsMetadata operationsMetadata, String defaultOnlineResource ) {
244            Element capability = XMLTools.appendElement( root, WFSNS, PRE_WFS + "Capability" );
245            Element request = XMLTools.appendElement( capability, WFSNS, PRE_WFS + "Request" );
246            org.deegree.ogcwebservices.getcapabilities.Operation[] ops = operationsMetadata.getOperations();
247            if ( ops != null && ops.length > 0 ) {
248                for ( org.deegree.ogcwebservices.getcapabilities.Operation op : ops ) {
249                    String name = op.getName();
250                    if ( !( name == null || "".equals( name.trim() ) || "GetGMLObject".equals( name.trim() ) ) ) {
251                        name = name.trim();
252    
253    
254                        Element operation = XMLTools.appendElement( request, WFSNS, PRE_WFS + name );
255                        if ( "DescribeFeatureType".equalsIgnoreCase( name ) ) {
256                            Element sdl = XMLTools.appendElement( operation, WFSNS, PRE_WFS + "SchemaDescriptionLanguage" );
257                            XMLTools.appendElement( sdl, WFSNS, PRE_WFS + "XMLSCHEMA" );
258                        } else if ( "GetFeature".equalsIgnoreCase( name ) || "GetFeatureWithLock".equalsIgnoreCase( name ) ) {
259                            Element resultFormat = XMLTools.appendElement( operation, WFSNS, PRE_WFS + "ResultFormat" );
260                            XMLTools.appendElement( resultFormat, WFSNS, PRE_WFS + "GML2" );
261                        }
262                        DCPType[] dcpTypes = op.getDCPs();
263                        if ( dcpTypes != null && dcpTypes.length > 0 ) {
264                            for ( DCPType dcpType : dcpTypes ) {
265                                appendDCPType( operation, dcpType, defaultOnlineResource );
266                            }
267                        } else {
268                            appendDCPType( operation, null, defaultOnlineResource );
269                        }
270                    }
271                }
272            }
273    
274        }
275    
276        /**
277         * Appends the XML representation of the <code>wfs:FeatureTypeList</code>- section to the passed
278         * <code>Element</code>.
279         * 
280         * @param root
281         * @param featureTypeList
282         */
283        private void appendFeatureTypeList( Element root, FeatureTypeList featureTypeList ) {
284            Element featureTypeListNode = XMLTools.appendElement( root, WFSNS, PRE_WFS + "FeatureTypeList" );
285            Operation[] operations = featureTypeList.getGlobalOperations();
286            if ( operations != null ) {
287                Element operationsNode = XMLTools.appendElement( featureTypeListNode, WFSNS, PRE_WFS + "Operations" );
288                for ( int i = 0; i < operations.length; i++ ) {
289                    XMLTools.appendElement( operationsNode, WFSNS, PRE_WFS + operations[i].getOperation() );
290                }
291            }
292            WFSFeatureType[] featureTypes = featureTypeList.getFeatureTypes();
293            if ( featureTypes != null ) {
294                for ( int i = 0; i < featureTypes.length; i++ ) {
295                    appendWFSFeatureType( featureTypeListNode, featureTypes[i] );
296                }
297            }
298        }
299    
300        /**
301         * Appends the DCPType in the WFS namespace... pre-ows stuff.
302         * 
303         * @param operation
304         *            to add the dcptype to.
305         * @param type
306         *            a bean containing necessary information if <code>null</code> a http-get/post dcp with the
307         *            defaultonline resource will be inserted.
308         * @param defaultOnlineResource
309         *            if no dcpType is given or no URL were inserted in the config, this will be inserted.
310         */
311        private void appendDCPType( Element operation, DCPType type, String defaultOnlineResource ) {
312            Element dcpType = XMLTools.appendElement( operation, WFSNS, PRE_WFS + "DCPType" );
313            Element http = XMLTools.appendElement( dcpType, WFSNS, PRE_WFS + "HTTP" );
314            boolean appendDefaultProtocol = true;
315            if ( type != null ) {
316                Protocol pr = type.getProtocol();
317                if ( pr != null ) {
318                    if ( pr instanceof HTTP ) {
319                        HTTP prot = (HTTP) pr;
320                        URL[] getters = prot.getGetOnlineResources();
321                        URL[] posters = prot.getPostOnlineResources();
322                        if ( ( getters != null && getters.length > 0 ) ) {
323                            for ( URL get : getters ) {
324                                appendGetURL( http, get.toExternalForm() );
325                            }
326                        } else {
327                            appendGetURL( http, defaultOnlineResource );
328                        }
329                        if ( posters != null && posters.length > 0 ) {
330                            for ( URL post : posters ) {
331                                appendPostURL( http, post.toExternalForm() );
332                            }
333                        } else {
334                            appendPostURL( http, defaultOnlineResource );
335                        }
336                        appendDefaultProtocol = false;
337                    }
338                }
339            }
340            if ( appendDefaultProtocol ) {
341                appendGetURL( http, defaultOnlineResource );
342                appendPostURL( http, defaultOnlineResource );
343            }
344        }
345    
346        private void appendGetURL( Element http, String resourceURL ) {
347            Element get = XMLTools.appendElement( http, WFSNS, PRE_WFS + "Get" );
348            get.setAttribute( "onlineResource", resourceURL );
349        }
350    
351        private void appendPostURL( Element http, String resourceURL ) {
352            Element post = XMLTools.appendElement( http, WFSNS, PRE_WFS + "Post" );
353            post.setAttribute( "onlineResource", resourceURL );
354        }
355    
356        private void appendKeyWords( Element root, Keywords[] keywords ) {
357            if ( keywords != null && keywords.length > 0 ) {
358                StringBuffer sb = new StringBuffer();
359                for ( int k = 0; k < keywords.length; ++k ) {
360                    String[] words = keywords[k].getKeywords();
361                    if ( words != null && words.length > 0 ) {
362                        for ( int i = 0; i < words.length; ++i ) {
363                            sb.append( words[i] );
364                            if ( ( i + 1 ) < words.length ) {
365                                sb.append( " " );
366                            }
367                        }
368                    }
369                    if ( ( k + 1 ) < keywords.length ) {
370                        sb.append( " " );
371                    }
372                }
373                XMLTools.appendElement( root, WFSNS, PRE_WFS + "Keywords", sb.toString() );
374            }
375        }
376    
377        /**
378         * Appends the XML representation of the <code>WFSFeatureType</code> instance to the passed <code>Element</code>.
379         * 
380         * @param root
381         * @param featureType
382         */
383        private void appendWFSFeatureType( Element root, WFSFeatureType featureType ) {
384    
385            Element featureTypeNode = XMLTools.appendElement( root, WFSNS, PRE_WFS + "FeatureType" );
386    
387            if ( featureType.getName().getPrefix() != null ) {
388                XMLTools.appendNSBinding( featureTypeNode, featureType.getName().getPrefix(), featureType.getName()
389                                                                                                         .getNamespace() );
390            }
391            XMLTools.appendElement( featureTypeNode, WFSNS, PRE_WFS + "Name", featureType.getName().getPrefixedName() );
392            XMLTools.appendElement( featureTypeNode, WFSNS, PRE_WFS + "Title", featureType.getTitle() );
393            String tmpValue = featureType.getAbstract();
394            if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) {
395                XMLTools.appendElement( featureTypeNode, WFSNS, PRE_WFS + "Abstract", tmpValue );
396            }
397            Keywords[] keywords = featureType.getKeywords();
398            appendKeyWords( featureTypeNode, keywords );
399    
400            URI defaultSrs = featureType.getDefaultSRS();
401            if ( defaultSrs != null ) {
402                XMLTools.appendElement( featureTypeNode, WFSNS, PRE_WFS + "SRS", defaultSrs.toASCIIString() );
403            } else if ( featureType.getOtherSrs() != null && featureType.getOtherSrs().length > 0 ) {
404                for ( URI srs : featureType.getOtherSrs() ) {
405                    if ( srs != null ) {
406                        XMLTools.appendElement( featureTypeNode, WFSNS, PRE_WFS + "SRS", srs.toASCIIString() );
407                        break;
408                    }
409                }
410            } else {
411                // defaulting to EPSG:4326 is this correct????
412                LOG.logDebug( "Found no default- or other-SRS, setting to EPGS:4326, is this correct?" );
413                XMLTools.appendElement( featureTypeNode, WFSNS, PRE_WFS + "SRS", "EPSG:4326" );
414            }
415            Operation[] operations = featureType.getOperations();
416            if ( operations != null && operations.length > 0 ) {
417                for ( Operation op : operations ) {
418                    if ( op != null && !"".equals( op.getOperation().trim() )
419                         && !"GetGMLObject".equalsIgnoreCase( op.getOperation().trim() ) ) {
420                        Element opEl = XMLTools.appendElement( featureTypeNode, WFSNS, PRE_WFS + "Operations");
421                        XMLTools.appendElement( opEl, WFSNS, PRE_WFS + op.getOperation().trim());
422                        
423                        
424                    }
425                }
426            }
427    
428            Envelope[] wgs84BoundingBoxes = featureType.getWgs84BoundingBoxes();
429            for ( Envelope bbox : wgs84BoundingBoxes ) {
430                if ( bbox != null && bbox.getMin().getCoordinateDimension() == 2 ) {
431                    Element latlon = XMLTools.appendElement( featureTypeNode, WFSNS, PRE_WFS + "LatLongBoundingBox" );
432                    Position min = bbox.getMin();
433                    Position max = bbox.getMax();
434                    latlon.setAttribute( "minx", Double.toString( min.getX() ) );
435                    latlon.setAttribute( "miny", Double.toString( min.getY() ) );
436                    latlon.setAttribute( "maxx", Double.toString( max.getX() ) );
437                    latlon.setAttribute( "maxy", Double.toString( max.getY() ) );
438                }
439            }
440            MetadataURL[] mdURLs = featureType.getMetadataUrls();
441            if ( mdURLs != null ) {
442                for ( MetadataURL mdURL : mdURLs ) {
443                    if ( mdURL != null && mdURL.getOnlineResource() != null ) {
444                        // first check if the format and type are acceptable.
445                        String format = mdURL.getFormat();
446                        boolean formatOK = false;
447                        if ( format != null && !"".equals( format.trim() ) ) {
448                            format = format.trim();
449                            if ( "XML".equals( format ) || "SGML".equals( format ) || "TXT".equals( format ) ) {
450                                formatOK = true;
451                            }
452                        }
453    
454                        String type = mdURL.getType();
455                        boolean typeOK = false;
456                        if ( formatOK && type != null && !"".equals( type.trim() ) ) {
457                            type = type.trim();
458                            if ( "TC211".equals( type ) || "FGDC".equals( type ) ) {
459                                typeOK = true;
460                            }
461                        }
462                        if ( formatOK && typeOK ) {
463                            Element metadata = XMLTools.appendElement( featureTypeNode,
464                                                                       WFSNS,
465                                                                       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    }