001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/ogcwebservices/csw/discovery/XMLFactory.java $
002 /*---------------- FILE HEADER ------------------------------------------
003
004 This file is part of deegree.
005 Copyright (C) 2001-2006 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.csw.discovery;
044
045 import java.io.IOException;
046 import java.net.URI;
047 import java.net.URISyntaxException;
048 import java.util.Date;
049 import java.util.HashMap;
050 import java.util.List;
051 import java.util.Map;
052 import java.util.Set;
053
054 import org.deegree.datatypes.QualifiedName;
055 import org.deegree.framework.log.ILogger;
056 import org.deegree.framework.log.LoggerFactory;
057 import org.deegree.framework.util.StringTools;
058 import org.deegree.framework.util.TimeTools;
059 import org.deegree.framework.xml.XMLException;
060 import org.deegree.framework.xml.XMLParsingException;
061 import org.deegree.framework.xml.XMLTools;
062 import org.deegree.ogcbase.CommonNamespaces;
063 import org.deegree.ogcbase.PropertyPath;
064 import org.deegree.ogcbase.SortProperty;
065 import org.deegree.ogcwebservices.OGCWebServiceException;
066 import org.w3c.dom.Attr;
067 import org.w3c.dom.Document;
068 import org.w3c.dom.Element;
069 import org.w3c.dom.NamedNodeMap;
070 import org.w3c.dom.Node;
071 import org.w3c.dom.NodeList;
072
073 /**
074 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a>
075 * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a>
076 * @author last edited by: $Author: mschneider $
077 *
078 * @version $Revision: 7457 $, $Date: 2007-06-05 11:59:46 +0200 (Di, 05 Jun 2007) $
079 */
080 public class XMLFactory extends org.deegree.ogcbase.XMLFactory {
081
082 private static final ILogger LOG = LoggerFactory.getLogger( XMLFactory.class );
083
084 private static final URI CSWNS = CommonNamespaces.CSWNS;
085
086 private static final URI OGCNS = CommonNamespaces.OGCNS;
087
088 /**
089 * Exports a <code>GetRecordsResponse</code> instance to a <code>GetRecordsResponseDocument</code>.
090 *
091 * @param response
092 * @return DOM representation of the <code>GetRecordsResponse</code>
093 * @throws IOException
094 * if XML template could not be loaded
095 */
096 public static GetRecordsResultDocument export( GetRecordsResult response ) throws XMLException {
097
098 GetRecordsResultDocument responseDocument = new GetRecordsResultDocument();
099
100 try {
101 responseDocument.createEmptyDocument();
102 Element rootElement = responseDocument.getRootElement();
103 Document doc = rootElement.getOwnerDocument();
104
105 // set required namespaces
106 Element recordRespRoot = response.getSearchResults().getRecords().getOwnerDocument().getDocumentElement();
107 NamedNodeMap nnm = recordRespRoot.getAttributes();
108 for ( int i = 0; i < nnm.getLength(); i++ ) {
109 Node node = nnm.item( i );
110 if ( node instanceof Attr ) {
111 rootElement.setAttribute( node.getNodeName(), node.getNodeValue() );
112 }
113 }
114 // 'version'-attribute
115 String version = response.getVersion();
116 if ( version == null || "".equals( version.trim() ) ) {
117 version = "2.0.0";
118 }
119 rootElement.setAttribute( "version", version );
120
121 // 'RequestId'-element (optional)
122 if ( response.getRequest().getId() != null ) {
123 Element requestIdElement = doc.createElementNS( CSWNS.toString(), "csw:RequestId" );
124 requestIdElement.appendChild( doc.createTextNode( response.getRequest().getId() ) );
125 rootElement.appendChild( requestIdElement );
126 }
127
128 // 'SearchStatus'-element (required)
129 Element searchStatusElement = doc.createElementNS( CSWNS.toString(), "csw:SearchStatus" );
130 // 'status'-attribute (required)
131 searchStatusElement.setAttribute( "status", response.getSearchStatus().getStatus() );
132 // 'timestamp'-attribute (optional)
133 if ( response.getSearchStatus().getTimestamp() != null ) {
134 Date date = response.getSearchStatus().getTimestamp();
135 String time = TimeTools.getISOFormattedTime( date );
136 searchStatusElement.setAttribute( "timestamp", time );
137 }
138 rootElement.appendChild( searchStatusElement );
139
140 // 'SeachResults'-element (required)
141 Element searchResultsElement = doc.createElementNS( CSWNS.toString(), "csw:SearchResults" );
142 SearchResults results = response.getSearchResults();
143
144 // 'resultSetId'-attribute (optional)
145 if ( results.getResultSetId() != null ) {
146 searchResultsElement.setAttribute( "resultSetId", results.getResultSetId().toString() );
147 }
148 // 'elementSet'-attribute (optional)
149 if ( results.getElementSet() != null ) {
150 searchResultsElement.setAttribute( "elementSet", results.getElementSet().toString() );
151 }
152 // 'recordSchema'-attribute (optional)
153 if ( results.getRecordSchema() != null ) {
154 searchResultsElement.setAttribute( "recordSchema", results.getRecordSchema().toString() );
155 }
156 // 'numberOfRecordsMatched'-attribute (required)
157 searchResultsElement.setAttribute( "numberOfRecordsMatched", "" + results.getNumberOfRecordsMatched() );
158 // 'numberOfRecordsReturned'-attribute (required)
159 searchResultsElement.setAttribute( "numberOfRecordsReturned", "" + results.getNumberOfRecordsReturned() );
160 // 'nextRecord'-attribute (required)
161 searchResultsElement.setAttribute( "nextRecord", "" + results.getNextRecord() );
162 // 'expires'-attribute (optional)
163 if ( results.getExpires() != null ) {
164 Date date = results.getExpires();
165 String time = TimeTools.getISOFormattedTime( date );
166 searchResultsElement.setAttribute( "expires", time );
167 }
168 // append all children of the records container node
169 NodeList nl = results.getRecords().getChildNodes();
170 for ( int i = 0; i < nl.getLength(); i++ ) {
171 Node copy = doc.importNode( nl.item( i ), true );
172 searchResultsElement.appendChild( copy );
173 }
174 rootElement.appendChild( searchResultsElement );
175 } catch ( Exception e ) {
176 LOG.logError( e.getMessage(), e );
177 throw new XMLException( e.getMessage() );
178 }
179 return responseDocument;
180 }
181
182 /**
183 * Exports a instance of {@link GetRecordByIdResult} to a {@link GetRecordByIdResultDocument}.
184 *
185 * @param response
186 * @return
187 * @throws XMLParsingException
188 */
189 public static GetRecordByIdResultDocument export( GetRecordByIdResult response ) throws XMLException {
190
191 GetRecordByIdResultDocument doc = new GetRecordByIdResultDocument();
192 try {
193 doc.createEmptyDocument();
194 Document owner = doc.getRootElement().getOwnerDocument();
195 if ( response != null && response.getRecord() != null ) {
196 Node copy = owner.importNode( response.getRecord(), true );
197 doc.getRootElement().appendChild( copy );
198 }
199 } catch ( Exception e ) {
200 LOG.logError( e.getMessage(), e );
201 throw new XMLException( e.getMessage() );
202 }
203
204 return doc;
205 }
206
207 /**
208 * Exports a <code>DescribeRecordResponse</code> instance to a <code>DescribeRecordResponseDocument</code>.
209 *
210 * @param response
211 * @return DOM representation of the <code>DescribeRecordResponse</code>
212 * @throws IOException
213 * if XML template could not be loaded
214 */
215 public static DescribeRecordResultDocument export( DescribeRecordResult response ) throws XMLException {
216
217 DescribeRecordResultDocument responseDocument = new DescribeRecordResultDocument();
218
219 try {
220 responseDocument.createEmptyDocument();
221 Element rootElement = responseDocument.getRootElement();
222 Document doc = rootElement.getOwnerDocument();
223
224 // 'SchemaComponent'-elements (required)
225 SchemaComponent[] components = response.getSchemaComponents();
226 for ( int i = 0; i < components.length; i++ ) {
227 Element schemaComponentElement = doc.createElementNS( CSWNS.toString(), "csw:SchemaComponent" );
228
229 // 'targetNamespace'-attribute (required)
230 schemaComponentElement.setAttribute( "targetNamespace", components[i].getTargetNamespace().toString() );
231
232 // 'parentSchema'-attribute (optional)
233 if ( components[i].getParentSchema() != null ) {
234 schemaComponentElement.setAttribute( "parentSchema", components[i].getParentSchema().toString() );
235 }
236
237 // 'schemaLanguage'-attribute (required)
238 schemaComponentElement.setAttribute( "schemaLanguage", components[i].getSchemaLanguage().toString() );
239
240 XMLTools.insertNodeInto( components[i].getSchema().getRootElement(), schemaComponentElement );
241 rootElement.appendChild( schemaComponentElement );
242 }
243 } catch ( Exception e ) {
244 LOG.logError( e.getMessage(), e );
245 throw new XMLException( e.getMessage() );
246 }
247 return responseDocument;
248 }
249
250 /**
251 * Exports a <code>GetRecords</code> instance to a <code>GetRecordsDocument</code>.
252 *
253 * @param request
254 * @return DOM representation of the <code>GetRecords</code>
255 * @throws XMLException
256 * if some elements could not be appended
257 * @throws OGCWebServiceException
258 * if an error occurred while creating the xml-representation of the GetRecords bean.
259 */
260 public static GetRecordsDocument export( GetRecords request ) throws XMLException,
261 OGCWebServiceException {
262
263 GetRecordsDocument getRecordsDocument = new GetRecordsDocument();
264 try {
265 getRecordsDocument.createEmptyDocument();
266 } catch ( Exception e ) {
267 throw new XMLException( e.getMessage() );
268 }
269 Element rootElement = getRecordsDocument.getRootElement();
270 Document doc = rootElement.getOwnerDocument();
271
272 // 'version'-attribute
273 rootElement.setAttribute( "version", request.getVersion() );
274
275 // 'resultType'-attribute
276 rootElement.setAttribute( "resultType", request.getResultTypeAsString() );
277
278 // 'outputFormat'-attribute
279 rootElement.setAttribute( "outputFormat", request.getOutputFormat() );
280
281 // 'outputSchema'-attribute
282 rootElement.setAttribute( "outputSchema", request.getOutputSchema() );
283
284 // 'startPosition'-attribute
285 rootElement.setAttribute( "startPosition", "" + request.getStartPosition() );
286
287 // 'maxRecords'-attribute
288 rootElement.setAttribute( "maxRecords", "" + request.getMaxRecords() );
289
290 // '<csw:DistributedSearch>'-element
291 if ( request.getHopCount() != -1 ) {
292 Element distributedSearchElement = doc.createElementNS( CSWNS.toString(), "csw:DistributedSearch" );
293
294 // 'hopCount'-attribute
295 distributedSearchElement.setAttribute( "hopCount", "" + request.getHopCount() );
296 rootElement.appendChild( distributedSearchElement );
297 }
298
299 // '<csw:ResponseHandler>'-elements (optional)
300 URI responseHandler = request.getResponseHandler();
301 if ( responseHandler != null ) {
302 Element responseHandlerElement = doc.createElementNS( CSWNS.toString(), "csw:ResponseHandler" );
303 responseHandlerElement.appendChild( doc.createTextNode( responseHandler.toASCIIString() ) );
304 rootElement.appendChild( responseHandlerElement );
305
306 }
307
308 // '<csw:Query>'-elements (required)
309 Query query = request.getQuery();
310 if ( query != null ) {
311 LOG.logDebug( "Adding the csw:Query element to the csw:GetRecords document" );
312 Element queryElement = doc.createElementNS( CSWNS.toString(), "csw:Query" );
313
314 // 'typeName'-attribute
315 // Testing for the list of typenames.
316 List<QualifiedName> typeNames = query.getTypeNamesAsList();
317 Map<String, QualifiedName> aliases = new HashMap<String, QualifiedName>( query.getDeclaredTypeNameVariables() );
318 if ( typeNames.size() > 0 ) {
319 appendTypeNamesAttribute( rootElement, queryElement, typeNames, aliases );
320 } else {
321
322 String s = StringTools.arrayToString( query.getTypeNames(), ',' );
323 queryElement.setAttribute( "typeNames", s );
324 }
325
326 // '<csw:ElementSetName>'-element (optional)
327 if ( query.getElementSetName() != null ) {
328 Element elementSetNameElement = doc.createElementNS( CSWNS.toString(), "csw:ElementSetName" );
329 List<QualifiedName> elementSetNameTypeNamesList = query.getElementSetNameTypeNamesList();
330 if ( query.getElementSetNameVariables() != null && query.getElementSetNameVariables().size() > 0 ) {
331 throw new OGCWebServiceException( "The elementSetName element in a csw:GetRecords request may not refrerence variables (aka. aliases), aborting request" );
332 }
333 if ( elementSetNameTypeNamesList.size() > 0 ) {
334 appendTypeNamesAttribute( rootElement, elementSetNameElement, elementSetNameTypeNamesList, null );
335 }
336 elementSetNameElement.appendChild( doc.createTextNode( query.getElementSetName() ) );
337 queryElement.appendChild( elementSetNameElement );
338 }
339
340 // '<csw:ElementName>'-elements (optional)
341 if ( query.getElementNamesAsPropertyPaths() != null ) {
342 List<PropertyPath> elementNames = query.getElementNamesAsPropertyPaths();
343 for ( int j = 0; j < elementNames.size(); j++ ) {
344 Element elementNameElement = doc.createElementNS( CSWNS.toString(), "csw:ElementName" );
345 elementNameElement.appendChild( doc.createTextNode( elementNames.get( j ).getAsString() ) );
346 queryElement.appendChild( elementNameElement );
347 }
348 }
349
350 // '<csw:Constraint>'-element (optional)
351 if ( query.getContraint() != null ) {
352 Element constraintElement = doc.createElementNS( CSWNS.toString(), "csw:Constraint" );
353 constraintElement.setAttribute( "version", "1.0.0" );
354 org.deegree.model.filterencoding.XMLFactory.appendFilter( constraintElement, query.getContraint() );
355 queryElement.appendChild( constraintElement );
356 }
357
358 // '<ogc:SortBy>'-element (optional)
359 SortProperty[] sortProperties = query.getSortProperties();
360 if ( sortProperties != null && sortProperties.length != 0 ) {
361 Element sortByElement = doc.createElementNS( OGCNS.toString(), "ogc:SortBy" );
362
363 // '<ogc:SortProperty>'-elements
364 for ( int j = 0; j < sortProperties.length; j++ ) {
365 Element sortPropertiesElement = doc.createElementNS( OGCNS.toString(), "ogc:SortProperty" );
366
367 // '<ogc:PropertyName>'-element (required)
368 Element propertyNameElement = doc.createElementNS( OGCNS.toString(), "ogc:PropertyName" );
369 appendPropertyPath( propertyNameElement, sortProperties[j].getSortProperty() );
370
371 // '<ogc:SortOrder>'-element (optional)
372 Element sortOrderElement = doc.createElementNS( OGCNS.toString(), "ogc:SortOrder" );
373 Node tn = doc.createTextNode( sortProperties[j].getSortOrder() ? "ASC" : "DESC" );
374 sortOrderElement.appendChild( tn );
375
376 sortPropertiesElement.appendChild( propertyNameElement );
377 sortPropertiesElement.appendChild( sortOrderElement );
378 sortByElement.appendChild( sortPropertiesElement );
379 }
380 queryElement.appendChild( sortByElement );
381 }
382 rootElement.appendChild( queryElement );
383 }
384 return getRecordsDocument;
385 }
386
387 /**
388 *
389 * @param rootElement
390 * the first node of the Docuement
391 * @param toBeInserted
392 * to which the typeNames attribute will be appended
393 * @param typeNames
394 * to be inserted into the toBeInserted element
395 * @param aliases
396 * may be <code>null</code>, if not each typeName must have exactly one alias defined, which will be
397 * inserted after the typename (e.g. typename=$o). If map must contain a mapping from variable to
398 * qualifiedName (e.g. [o, typeName]);
399 */
400 protected static void appendTypeNamesAttribute( Element rootElement,
401 Element toBeInserted,
402 List<QualifiedName> typeNames,
403 Map<String, QualifiedName> aliases ) {
404 if ( !typeNames.isEmpty() ) {
405 for ( QualifiedName qName : typeNames ) {
406 LOG.logDebug ( "found typeName: " + qName );
407 }
408 LOG.logDebug( "for the element: " + toBeInserted.getNodeName()
409 + " we are trying to set the typeNames attribute." );
410 StringBuffer sb = new StringBuffer( );
411 int count = 0;
412 for ( QualifiedName qName : typeNames ) {
413 if ( qName.getLocalName() != null ) {
414 URI ns = qName.getNamespace();
415 String prefix = qName.getPrefix();
416 if ( ns != null && prefix != null ) {
417 URI boundNS = null;
418 try {
419 boundNS = XMLTools.getNamespaceForPrefix( prefix, toBeInserted );
420 } catch ( URISyntaxException e ) {
421 // why for crying out loud an UriSyntax exception while lookin up stuff (without giving
422 // an
423 // uri).
424 }
425 LOG.logDebug( "ElementSetName/@typeNames: Found the namespace " + boundNS
426 + " for the prefix: "
427 + prefix
428 + " from typename (localname) : "
429 + qName.getLocalName() );
430 if ( boundNS == null ) {
431 if ( CommonNamespaces.OASIS_EBRIMNS.equals( ns ) ) {
432 XMLTools.appendNSBinding( rootElement, "rim", ns );
433 LOG.logDebug( toBeInserted.getLocalName() + "/@typeName: While no namespace was bound to the prefix: "
434 + prefix
435 + " the namespace: "
436 + ns
437 + " has been bound to 'rim' in the the root element." );
438 } else {
439 XMLTools.appendNSBinding( rootElement, prefix, ns );
440 LOG.logDebug( toBeInserted.getLocalName() + "/@typeName: While no namespace was bound to the prefix: "
441 + prefix
442 + " the namespace: "
443 + ns
444 + " has been bound to '"
445 + prefix
446 + "' in the the root element." );
447 }
448
449 }
450 }
451 String typeName = prefix;
452 if ( typeName != null ) {
453 typeName += ":" + qName.getLocalName();
454 }
455 sb.append( typeName );
456 if ( aliases != null ) {
457 if ( aliases.containsValue( qName ) ) {
458 Set<String> keys = aliases.keySet();
459 for ( String key : keys ) {
460 if ( aliases.get( key ).equals( qName ) ) {
461 sb.append( "=" ).append( key );
462 aliases.remove( key );
463 break;
464 }
465 }
466 } else if ( aliases.size() > 0 ) {
467 LOG.logError( "No variable mapping found for typename: " + typeName
468 + " this may not be, because every single typename must or no typename may have a variable!" );
469 }
470 }
471 if ( ++count < typeNames.size() ) {
472 sb.append( " " );
473 }
474 }
475 }
476 if ( !"null".equals( sb.toString().trim() ) && !"".equals( sb.toString().trim() ) ) {
477 LOG.logDebug( "for the element: " + toBeInserted.getNodeName()
478 + " we are settin the typeNames attribute to: "
479 + sb.toString() );
480 toBeInserted.setAttribute( "typeNames", sb.toString() );
481 }
482 }
483 }
484
485 /**
486 * Exports a <code>GetRecordById</code> instance to a <code>GetRecordByIdDocument</code>.
487 *
488 * @param request
489 * @return DOM representation of the <code>GetRecordById</code>
490 * @throws IOException
491 * if XML template could not be loaded
492 */
493 public static GetRecordByIdDocument export( GetRecordById request ) throws XMLException {
494
495 GetRecordByIdDocument getRecordByIdDoc = new GetRecordByIdDocument();
496 try {
497 getRecordByIdDoc.createEmptyDocument();
498 } catch ( Exception e ) {
499 throw new XMLException( e.getMessage() );
500 }
501 Element rootElement = getRecordByIdDoc.getRootElement();
502 Document doc = rootElement.getOwnerDocument();
503
504 // 'version'-attribute
505 rootElement.setAttribute( "version", request.getVersion() );
506
507 String[] ids = request.getIds();
508 for ( int i = 0; i < ids.length; i++ ) {
509 Element idElement = doc.createElementNS( CSWNS.toString(), "csw:Id" );
510 idElement.appendChild( doc.createTextNode( ids[i] ) );
511 rootElement.appendChild( idElement );
512 }
513
514 String elementSetName = request.getElementSetName();
515 if ( elementSetName != null ) {
516 Element esnElement = doc.createElementNS( CSWNS.toString(), "csw:ElementSetName" );
517 esnElement.appendChild( doc.createTextNode( elementSetName ) );
518 rootElement.appendChild( esnElement );
519 }
520
521 return getRecordByIdDoc;
522 }
523 }