001    //$HeadURL: $
002    /*----------------    FILE HEADER  ------------------------------------------
003     This file is part of deegree.
004     Copyright (C) 2001-2008 by:
005     Department of Geography, University of Bonn
006     http://www.giub.uni-bonn.de/deegree/
007     lat/lon GmbH
008     http://www.lat-lon.de
009    
010     This library is free software; you can redistribute it and/or
011     modify it under the terms of the GNU Lesser General Public
012     License as published by the Free Software Foundation; either
013     version 2.1 of the License, or (at your option) any later version.
014     This library is distributed in the hope that it will be useful,
015     but WITHOUT ANY WARRANTY; without even the implied warranty of
016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017     Lesser General Public License for more details.
018     You should have received a copy of the GNU Lesser General Public
019     License along with this library; if not, write to the Free Software
020     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021     Contact:
022    
023     Andreas Poth
024     lat/lon GmbH
025     Aennchenstr. 19
026     53177 Bonn
027     Germany
028     E-Mail: poth@lat-lon.de
029    
030     Prof. Dr. Klaus Greve
031     Department of Geography
032     University of Bonn
033     Meckenheimer Allee 166
034     53115 Bonn
035     Germany
036     E-Mail: greve@giub.uni-bonn.de
037     ---------------------------------------------------------------------------*/
038    
039    package org.deegree.owscommon_1_1_0;
040    
041    import static org.deegree.framework.xml.XMLTools.getElement;
042    import static org.deegree.framework.xml.XMLTools.getElements;
043    import static org.deegree.framework.xml.XMLTools.getNodeAsString;
044    import static org.deegree.framework.xml.XMLTools.getNodesAsStringList;
045    import static org.deegree.framework.xml.XMLTools.getRequiredElement;
046    import static org.deegree.framework.xml.XMLTools.getRequiredNodeAsString;
047    import static org.deegree.ogcbase.CommonNamespaces.XLINK_PREFIX;
048    
049    import java.util.ArrayList;
050    import java.util.List;
051    
052    import org.deegree.framework.util.Pair;
053    import org.deegree.framework.xml.XMLParsingException;
054    import org.deegree.ogcbase.CommonNamespaces;
055    import org.deegree.ogcwebservices.getcapabilities.OGCCapabilitiesDocument;
056    import org.w3c.dom.Element;
057    
058    /**
059     * <code>OWSCapabilitesDocument</code> parses the ows_1_1_0 Capabilities profile.
060     * 
061     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
062     * 
063     * @author last edited by: $Author:$
064     * 
065     * @version $Revision:$, $Date:$
066     * 
067     */
068    
069    public abstract class OWSCommonCapabilitiesDocument extends OGCCapabilitiesDocument {
070    
071        protected static String PRE_OWS = CommonNamespaces.OWS_1_1_0PREFIX + ":";
072    
073        /**
074         * @param serviceID
075         *            element to parse from
076         * @return the ServiceIdentification bean or <code>null</code> if ServiceIdentification element is
077         *         <code>null</code>;
078         * @throws XMLParsingException
079         *             if something could not be parsed.
080         */
081        public ServiceIdentification parseServiceIdentification()
082                                                                 throws XMLParsingException {
083            Element serviceIdentification = getElement( getRootElement(), PRE_OWS + "ServiceIdentification", nsContext );
084            if ( serviceIdentification == null ) {
085                return null;
086            }
087            List<String> title = getNodesAsStringList( serviceIdentification, PRE_OWS + "Title", nsContext );
088            List<String> abstracts = getNodesAsStringList( serviceIdentification, PRE_OWS + "Abstract", nsContext );
089            List<Element> kws = getElements( serviceIdentification, PRE_OWS + "Keywords", nsContext );
090            // Pair<List<keywords>, codetype>
091            List<Keywords> keywords = new ArrayList<Keywords>( ( ( kws == null ) ? 0 : kws.size() ) );
092            if ( kws != null ) {
093                for ( Element keyword : kws ) {
094                    keywords.add( parseKeywords( keyword ) );
095                }
096            }
097            Pair<String, String> serviceType = new Pair<String, String>( getRequiredNodeAsString( serviceIdentification,
098                                                                                               PRE_OWS + "ServiceType",
099                                                                                               nsContext ),
100                                                                      getNodeAsString( serviceIdentification,
101                                                                                       PRE_OWS + "ServiceType/@codeSpace",
102                                                                                       nsContext,
103                                                                                       null ) );
104            List<String> serviceTypeVersions = getNodesAsStringList( serviceIdentification,
105                                                              PRE_OWS + "ServiceTypeVersion",
106                                                              nsContext );
107            if ( serviceTypeVersions.size() == 0 ) {
108                throw new XMLParsingException( PRE_OWS + "ServiceTypeVersion must be present at least once." );
109            }
110            List<String> profiles = getNodesAsStringList( serviceIdentification, PRE_OWS + "Profile", nsContext );
111            String fees = getNodeAsString( serviceIdentification, PRE_OWS + "Fees", nsContext, null );
112            List<String> accessConstraints = getNodesAsStringList( serviceIdentification,
113                                                                   PRE_OWS + "AccessConstraints",
114                                                                   nsContext );
115            return new ServiceIdentification( title,
116                                              abstracts,
117                                              keywords,
118                                              serviceType,
119                                              serviceTypeVersions,
120                                              profiles,
121                                              fees,
122                                              accessConstraints );
123        }
124    
125        /**
126         * @return the ServiceProvider bean or <code>null</code> if the ServiceProvider element is <code>null</code>;
127         * @throws XMLParsingException
128         */
129        public ServiceProvider parseServiceProvider()
130                                                     throws XMLParsingException {
131            Element serviceProvider = getElement( getRootElement(), PRE_OWS + "ServiceProvider", nsContext );
132            if ( serviceProvider == null ) {
133                return null;
134            }
135            String providerName = getRequiredNodeAsString( serviceProvider, PRE_OWS + "ProviderName", nsContext );
136            String providerSite = getNodeAsString( serviceProvider,
137                                                   PRE_OWS + "ProviderSite/@" + XLINK_PREFIX + ":href",
138                                                   nsContext,
139                                                   null );
140            ServiceContact serviceContact = parseServiceContact( getRequiredElement( serviceProvider,
141                                                                                     PRE_OWS + "ServiceContact",
142                                                                                     nsContext ) );
143            return new ServiceProvider( providerName, providerSite, serviceContact );
144        }
145    
146        /**
147         * @param operationsMetadata
148         * @return the OperationsMetadata bean or <code>null</code> if the OperationsMetadata element is <code>null</code>;
149         * @throws XMLParsingException
150         */
151        public OperationsMetadata parseOperationsMetadata()
152                                                           throws XMLParsingException {
153            Element operationsMetadata = getElement( getRootElement(), PRE_OWS + "OperationsMetadata", nsContext );
154            if ( operationsMetadata == null ) {
155                return null;
156            }
157            List<Operation> operations = parseOperations( getElements( operationsMetadata, PRE_OWS + "Operation", nsContext ) );
158            List<Element> params = getElements( operationsMetadata, PRE_OWS + "Parameter", nsContext );
159            List<DomainType> parameters = new ArrayList<DomainType>();
160            if ( params != null && params.size() > 0 ) {
161                for ( Element param : params ) {
162                    DomainType t = parseDomainType( param );
163                    if ( t != null ) {
164                        parameters.add( t );
165                    }
166                }
167            }
168    
169            List<Element> consts = getElements( operationsMetadata, PRE_OWS + "Constraint", nsContext );
170            List<DomainType> constraints = new ArrayList<DomainType>();
171            if ( params != null && params.size() > 0 ) {
172                for ( Element ce : consts ) {
173                    DomainType t = parseDomainType( ce );
174                    if ( t != null ) {
175                        constraints.add( t );
176                    }
177                }
178            }
179            Element extendedCapabilities = getElement( operationsMetadata, PRE_OWS + "ExtendedCapabilities", nsContext );
180    
181            return new OperationsMetadata( operations, parameters, constraints, extendedCapabilities );
182    
183        }
184    
185        /**
186         * @param operationElements
187         * @return
188         * @throws XMLParsingException
189         */
190        protected List<Operation> parseOperations( List<Element> operationElements )
191                                                                                    throws XMLParsingException {
192            if ( operationElements == null || operationElements.size() < 2 ) {
193                throw new XMLParsingException( "At least two " + PRE_OWS
194                                               + "operations must be defined in the "
195                                               + PRE_OWS
196                                               + "OperationMetadata element." );
197            }
198            List<Operation> operations = new ArrayList<Operation>( operationElements.size() );
199            for ( Element op : operationElements ) {
200                /**
201                 * from owsOperationsMetadata<br/> Unordered list of Distributed Computing Platforms (DCPs) supported for
202                 * this operation. At present, only the HTTP DCP is defined, so this element will appear only once.
203                 */
204                Element dcp = getRequiredElement( op, PRE_OWS + "DCP", nsContext );
205                Element http = getRequiredElement( dcp, PRE_OWS + "HTTP", nsContext );
206                List<Element> getters = getElements( http, PRE_OWS + "Get", nsContext );
207                List<Pair<String, List<DomainType>>> getURLs = new ArrayList<Pair<String, List<DomainType>>>();
208                if ( getters != null && getters.size() > 0 ) {
209                    for ( Element get : getters ) {
210                        Pair<String, List<DomainType>> t = parseHTTPChild( get );
211                        if ( t != null ) {
212                            getURLs.add( t );
213                        }
214                    }
215                }
216    
217                List<Element> posts = getElements( http, PRE_OWS + "Post", nsContext );
218                List<Pair<String, List<DomainType>>> postURLs = new ArrayList<Pair<String, List<DomainType>>>();
219                if ( posts != null && posts.size() > 0 ) {
220                    for ( Element post : posts ) {
221                        Pair<String, List<DomainType>> t = parseHTTPChild( post );
222                        if ( t != null ) {
223                            postURLs.add( t );
224                        }
225                    }
226                }
227    
228                List<Element> params = getElements( op, PRE_OWS + "Parameter", nsContext );
229                List<DomainType> parameters = new ArrayList<DomainType>();
230                if ( params != null && params.size() > 0 ) {
231                    for ( Element param : params ) {
232                        DomainType t = parseDomainType( param );
233                        if ( t != null ) {
234                            parameters.add( t );
235                        }
236                    }
237                }
238    
239                List<Element> consts = getElements( op, PRE_OWS + "Constraint", nsContext );
240                List<DomainType> constraints = new ArrayList<DomainType>();
241                if ( params != null && params.size() > 0 ) {
242                    for ( Element ce : consts ) {
243                        DomainType t = parseDomainType( ce );
244                        if ( t != null ) {
245                            constraints.add( t );
246                        }
247                    }
248                }
249                List<Pair<String, String>> metadataAttribs = parseMetadatas( getElements( op,
250                                                                                          PRE_OWS + "MetaData",
251                                                                                          nsContext ) );
252                String name = getRequiredNodeAsString( op, "@name", nsContext );
253                operations.add( new Operation( getURLs, postURLs, parameters, constraints, metadataAttribs, name ) );
254            }
255            return operations;
256        }
257    
258        /**
259         * parses the post or get information beneath a http element.
260         * 
261         * @param getOrPost
262         * @return the pair containing the xlink, list<contraint>. or <code>null</code> if attribute and elements were
263         *         not found.
264         * @throws XMLParsingException
265         */
266    
267        protected Pair<String, List<DomainType>> parseHTTPChild( Element getOrPost )
268                                                                                    throws XMLParsingException {
269            if ( getOrPost == null ) {
270                return null;
271            }
272            String httpURL = getNodeAsString( getOrPost, "@" + XLINK_PREFIX + ":href", nsContext, null );
273            List<DomainType> getConstraints = null;
274    
275            List<Element> getConstElements = getElements( getOrPost, PRE_OWS + "Constraint", nsContext );
276            if ( getConstElements != null && getConstElements.size() > 0 ) {
277                for ( Element gce : getConstElements ) {
278                    DomainType getConstraint = parseDomainType( gce );
279                    if ( getConstraint != null ) {
280                        // create a list instance.
281                        if ( getConstraints == null ) {
282                            getConstraints = new ArrayList<DomainType>( getConstElements.size() );
283                        }
284                        getConstraints.add( getConstraint );
285                    }
286                }
287            }
288            if ( getConstraints != null && httpURL != null ) {
289                return new Pair<String, List<DomainType>>( httpURL, getConstraints );
290            }
291            return null;
292        }
293    
294        /**
295         * @param domainType
296         * @return
297         * @throws XMLParsingException
298         */
299        private DomainType parseDomainType( Element domainType )
300                                                                throws XMLParsingException {
301            if ( domainType == null ) {
302                return null;
303            }
304            boolean allowedValues = getElement( domainType, PRE_OWS + "AllowedValues", nsContext ) != null;
305            List<String> values = null;
306            List<Range> ranges = null;
307            if ( allowedValues ) {
308                values = getNodesAsStringList( domainType, PRE_OWS + "AllowedValues/" + PRE_OWS + "Value", nsContext );
309                ranges = parseRanges( getElements( domainType, PRE_OWS + "AllowedValues/" + PRE_OWS + "Range", nsContext ) );
310                if ( ( values == null || values.size() == 0 ) && ( ranges == null || ranges.size() == 0 ) ) {
311                    throw new XMLParsingException( "One of the following values must be defined in an " + PRE_OWS
312                                                   + "AllowedValues: - "
313                                                   + PRE_OWS
314                                                   + "Values or "
315                                                   + PRE_OWS
316                                                   + "Range" );
317                }
318            }
319            boolean anyValue = getElement( domainType, PRE_OWS + "AnyValue", nsContext ) != null;
320            boolean noValues = getElement( domainType, PRE_OWS + "NoValues", nsContext ) != null;
321            Pair<String, String> valuesReference = parseDomainMetadataType( getElement( domainType,
322                                                                                        PRE_OWS + "ValuesReference",
323                                                                                        nsContext ) );
324            if ( valuesReference != null && valuesReference.second == null ) {
325                throw new XMLParsingException( "The reference attribute of the " + PRE_OWS
326                                               + "DomatainType/"
327                                               + PRE_OWS
328                                               + "ValuesReference must be set." );
329            }
330            if ( !( allowedValues || anyValue || noValues || valuesReference != null ) ) {
331                throw new XMLParsingException( "One of the following values must be defined in an " + PRE_OWS
332                                               + "DomainType: - "
333                                               + PRE_OWS
334                                               + "AllowedValues, "
335                                               + PRE_OWS
336                                               + "AnyValue, "
337                                               + PRE_OWS
338                                               + "NoValues or "
339                                               + PRE_OWS
340                                               + "ValuesReference" );
341            }
342            String defaultValue = getNodeAsString( domainType, PRE_OWS + "DefaultValue", nsContext,null );
343            Pair<String, String> meaning = parseDomainMetadataType( getElement( domainType,
344                                                                                PRE_OWS + "Meaning",
345                                                                                nsContext ) );
346            Pair<String, String> dataType = parseDomainMetadataType( getElement( domainType,
347                                                                                  PRE_OWS + "DataType",
348                                                                                  nsContext ) );
349            Element valuesUnit = getElement( domainType, PRE_OWS + "ValuesUnit", nsContext );
350            Pair<String, String> uom = null;
351            Pair<String, String> referenceSystem  = null;
352            if( valuesUnit != null ){
353                uom = parseDomainMetadataType( getElement( valuesUnit, PRE_OWS + "UOM", nsContext ) );
354                referenceSystem = parseDomainMetadataType( getElement( valuesUnit,
355                                                                       PRE_OWS + "ReferenceSystem",
356                                                                       nsContext ) );
357                if( uom == null && referenceSystem == null ){
358                    throw new XMLParsingException( "Either " + PRE_OWS + "UOM or " + PRE_OWS + "ReferenceSystem are required in a " + PRE_OWS + "ValuesUnit element.");
359                }
360            }
361            List<Pair<String, String>> metadataAttribs = parseMetadatas( getElements( domainType,
362                                                                                      PRE_OWS + "MetaData",
363                                                                                      nsContext ) );
364    
365            String name = getRequiredNodeAsString( domainType, "@name", nsContext );
366    
367            return new DomainType( values,
368                                   ranges,
369                                   anyValue,
370                                   noValues,
371                                   valuesReference,
372                                   defaultValue,
373                                   meaning,
374                                   dataType,
375                                   uom,
376                                   referenceSystem,
377                                   metadataAttribs,
378                                   name );
379    
380        }
381    
382        /**
383         * @param metadataElements
384         *            to be parsed.
385         * @return a List of pairs of optional <xlink:href, about> attributes, or an empty list <code>null</code> if no
386         *         elements were found.
387         */
388        protected List<Pair<String, String>> parseMetadatas( List<Element> metadataElements ) {
389            if ( metadataElements == null || metadataElements.size() == 0 ) {
390                return null;
391            }
392            List<Pair<String, String>> result = new ArrayList<Pair<String, String>>( metadataElements.size() );
393            for ( Element mde : metadataElements ) {
394                Pair<String, String> md = parseMetaData( mde );
395                if ( md != null ) {
396                    result.add( md );
397                }
398            }
399            if ( result.size() == 0 ) {
400                return null;
401            }
402            return result;
403    
404        }
405    
406        /**
407         * @param metadataElement
408         *            to be parsed.
409         * @return the pair of optional <xlink:href, about> attributes.
410         */
411        protected Pair<String, String> parseMetaData( Element metadataElement ) {
412            if ( metadataElement == null ) {
413                return null;
414            }
415            String metadataHref = metadataElement.getAttributeNS( CommonNamespaces.XLNNS.toASCIIString(), "href" );
416            metadataHref = "".equals( metadataHref ) ? null : metadataHref;
417            String metadataAbout = metadataElement.getAttribute( "about" );
418            metadataAbout = "".equals( metadataAbout ) ? null : metadataAbout;
419            if ( metadataAbout != null || metadataAbout != null ) {
420                return new Pair<String, String>( metadataHref, metadataAbout );
421            }
422            return null;
423        }
424    
425        /**
426         * Parses the domain metadata type and it's reference attribute and puts them in a pair, like name, reference, which
427         * may be null
428         * 
429         * @param domainMDElements
430         * @return a list of pairs containing the values or <code>null</code> if given list is null or it does not contain
431         *         any domain elements value.
432         * @throws XMLParsingException
433         */
434        protected List<Pair<String, String>> parseDomainMetadataTypes( List<Element> domainMDElements )
435                                                                                                       throws XMLParsingException {
436            if ( domainMDElements == null || domainMDElements.size() == 0 ) {
437                return null;
438            }
439            List<Pair<String, String>> domainMDTypes = new ArrayList<Pair<String, String>>( domainMDElements.size() );
440            for ( Element mdT : domainMDElements ) {
441                Pair<String, String> t = parseDomainMetadataType( mdT );
442                if ( t != null ) {
443                    domainMDTypes.add( t );
444                }
445            }
446            return ( domainMDTypes.size() == 0 ) ? null : domainMDTypes;
447        }
448    
449        /**
450         * Parses the domain metadata type and it's reference attribute and puts them in a pair, like name, reference, the
451         * latter may be null
452         * 
453         * @param domainMDElement
454         *            to parse
455         * @return a pair containing the values or <code>null</code> if given element is null or it does not contain a
456         *         value.
457         * @throws XMLParsingException
458         */
459        protected Pair<String, String> parseDomainMetadataType( Element domainMDElement )
460                                                                                         throws XMLParsingException {
461            if ( domainMDElement == null ) {
462                return null;
463            }
464            String valuesReference = getNodeAsString( domainMDElement, ".", nsContext, null );
465            String reference = getNodeAsString( domainMDElement, "@reference", nsContext, null );
466            if ( valuesReference != null ) {
467                return new Pair<String, String>( valuesReference, reference );
468            }
469            return null;
470        }
471    
472        /**
473         * @param rangeElements
474         * @return the ranges or <code>null</code> if no elements were given.
475         * @throws XMLParsingException
476         */
477        private List<Range> parseRanges( List<Element> rangeElements )
478                                                                      throws XMLParsingException {
479            if ( rangeElements == null || rangeElements.size() == 0 ) {
480                return null;
481            }
482            List<Range> ranges = new ArrayList<Range>( rangeElements.size() );
483            for ( Element range : rangeElements ) {
484                String minimumValue = getNodeAsString( range,
485                                                       PRE_OWS + "Range/" + PRE_OWS + "MinimumValue",
486                                                       nsContext,
487                                                       null );
488                String maximumValue = getNodeAsString( range,
489                                                       PRE_OWS + "Range/" + PRE_OWS + "MaximumValue",
490                                                       nsContext,
491                                                       null );
492                String spacing = getNodeAsString( range, PRE_OWS + "Range/" + PRE_OWS + "Spacing", nsContext, null );
493                String rangeClosure = getNodeAsString( range, PRE_OWS + "Range/@rangeClosure", nsContext, "closed" );
494                ranges.add( new Range( minimumValue, maximumValue, spacing, rangeClosure ) );
495            }
496            return ranges;
497        }
498    
499        /**
500         * @param serviceContact
501         * @return
502         * @throws XMLParsingException
503         */
504        protected ServiceContact parseServiceContact( Element serviceContact )
505                                                                              throws XMLParsingException {
506            String individualName = getNodeAsString( serviceContact, PRE_OWS + "IndividualName", nsContext, null );
507            String positionName = getNodeAsString( serviceContact, PRE_OWS + "PositionName", nsContext, null );
508            ContactInfo contactInfo = parseContactInfo( getElement( serviceContact, PRE_OWS + "ContactInfo", nsContext ) );
509            Pair<String, String> role = null;
510            String roleS = getNodeAsString( serviceContact, PRE_OWS + "Role", nsContext, null );
511            if ( roleS != null ) {
512                role = new Pair<String, String>( roleS, getNodeAsString( serviceContact, PRE_OWS + "Role/@codeSpace", nsContext, null ) );
513            }
514            return new ServiceContact( individualName, positionName, contactInfo, role );
515        }
516    
517        /**
518         * @param contactInfo
519         * @return the contactinfo or <code>null</code> if all underlying elements are empty.
520         * @throws XMLParsingException
521         */
522        protected ContactInfo parseContactInfo( Element contactInfo )
523                                                                     throws XMLParsingException {
524            if ( contactInfo == null ) {
525                return null;
526            }
527    
528            Pair<List<String>, List<String> > phone = null;
529            Element phoneElement = getElement( contactInfo, PRE_OWS + "Phone", nsContext );
530            if ( phoneElement != null ) {
531                phone = new Pair<List<String>, List<String>>(getNodesAsStringList( phoneElement, PRE_OWS + "Voice", nsContext ),  getNodesAsStringList( phoneElement, PRE_OWS + "Facsimile", nsContext ) );
532            }
533    
534            List<String> deliveryPoint = null;
535            String city = null;
536            String administrativeArea = null;
537            String postalCode = null;
538            String country = null;
539            List<String> electronicMailAddress = null;
540            Element address = getElement( contactInfo, PRE_OWS + "Address", nsContext );
541            boolean hasAdress = false;
542            if ( address != null ) {
543                deliveryPoint = getNodesAsStringList( address, PRE_OWS + "DeliveryPoint", nsContext );
544                city = getNodeAsString( address, PRE_OWS + "City", nsContext, null );
545                administrativeArea = getNodeAsString( address, PRE_OWS + "AdministrativeArea", nsContext, null );
546                postalCode = getNodeAsString( address, PRE_OWS + "PostalCode", nsContext, null );
547                country = getNodeAsString( address, PRE_OWS + "Country", nsContext, null );
548                electronicMailAddress = getNodesAsStringList( address, PRE_OWS + "ElectronicMailAddress", nsContext );
549                if ( electronicMailAddress.size() == 0 ) {
550                    electronicMailAddress = null;
551                }
552                hasAdress = ( deliveryPoint != null || city != null
553                              || administrativeArea != null
554                              || postalCode != null
555                              || country != null || electronicMailAddress != null );
556            }
557    
558            String onlineResource = getNodeAsString( contactInfo,
559                                                     PRE_OWS + "OnlineResource/@" + XLINK_PREFIX + ":href",
560                                                     nsContext,
561                                                     null );
562            String hoursOfService = getNodeAsString( contactInfo, PRE_OWS + "HoursOfService", nsContext, null );
563            String contactInstructions = getNodeAsString( contactInfo, PRE_OWS + "ContactInstructions", nsContext, null );
564            if ( (phone != null) && !hasAdress && onlineResource == null && hoursOfService == null && contactInstructions == null ) {
565                return null;
566            }
567            return new ContactInfo( phone, hasAdress, deliveryPoint, city, administrativeArea, postalCode, country, electronicMailAddress,onlineResource, hoursOfService, contactInstructions );
568        }
569    
570        /**
571         * Return the ows_1_1_0 keywords..
572         * 
573         * @param keywords
574         *            element to parse from
575         * @return a <List<Keywords>,Type> pair.
576         * @throws XMLParsingException
577         */
578        protected Keywords parseKeywords( Element keywords )
579                                                            throws XMLParsingException {
580            if ( keywords == null ) {
581                return null;
582            }
583            String codetype = getNodeAsString( keywords, PRE_OWS + "Type", nsContext, null );
584            String typeSpace = null;
585            Pair<String, String> type = null;
586            if ( codetype != null ) {
587                typeSpace = getNodeAsString( keywords, PRE_OWS + "Type/@codeSpace", nsContext, null );
588                type = new Pair<String, String>( codetype, typeSpace );
589            }
590            return new Keywords( getNodesAsStringList( keywords, PRE_OWS + "Keyword", nsContext ), type );
591    
592        }
593    
594    }