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 }