001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/ogcwebservices/csw/discovery/GetRecords.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.csw.discovery;
044    
045    import java.io.StringReader;
046    import java.net.URI;
047    import java.net.URISyntaxException;
048    import java.util.Map;
049    
050    import org.deegree.framework.log.ILogger;
051    import org.deegree.framework.log.LoggerFactory;
052    import org.deegree.framework.util.StringTools;
053    import org.deegree.framework.xml.XMLTools;
054    import org.deegree.i18n.Messages;
055    import org.deegree.model.filterencoding.AbstractFilter;
056    import org.deegree.model.filterencoding.Filter;
057    import org.deegree.ogcbase.ExceptionCode;
058    import org.deegree.ogcbase.SortProperty;
059    import org.deegree.ogcwebservices.InvalidParameterValueException;
060    import org.deegree.ogcwebservices.MissingParameterValueException;
061    import org.deegree.ogcwebservices.OGCWebServiceException;
062    import org.deegree.ogcwebservices.OperationNotSupportedException;
063    import org.deegree.ogcwebservices.csw.AbstractCSWRequest;
064    import org.w3c.dom.Document;
065    import org.w3c.dom.Element;
066    
067    /**
068     * Class representation of a <code>GetRecords</code> request.
069     * <p>
070     * The primary means of resource discovery in the general model are the two operations search and
071     * present. In the HTTP protocol binding these are combined in the form of the mandatory
072     * <code>GetRecords</code> operation, which does a search.
073     * <p>
074     * Parameters specific to the <code>GetRecords</code> -request (omitting REQUEST, SERVICE and
075     * VERSION): <table border="1">
076     * <tr>
077     * <th>Name</th>
078     * <th>Occurences</th>
079     * <th>Function</th>
080     * </tr>
081     * <tr>
082     * <td align="center">NAMESPACE</td>
083     * <td align="center">0|1</td>
084     * <td>The NAMESPACE parameter is included in the KVP encoding to allow clients to bind any
085     * namespace prefixes that might be used for qualified names specified in other parameters. For
086     * example, the typeName parameter may include qualified names of the form namespace prefix:name.
087     * The value of the NAMESPACE parameter is a comma separated list of character strings of the form
088     * [namespace prefix:] namespace url. Not including the name namespace prefix binds the specified
089     * URL to the default namespace. As in XML, only one default namespace may be bound. This parameter
090     * is not required for the XML encoding since XML includes a mechanism for binding namespace
091     * prefixes.</td>
092     * </tr>
093     * <tr>
094     * <td align="center">resultType</td>
095     * <td align="center">0|1 (default: RESULTS)</td>
096     * <td>The resultType parameter may have the values HITS, RESULTS or VALIDATE and is used to
097     * indicate whether the catalogue service returns the full result set, the number of hits the query
098     * found or validates the request. If the resultType parameter is set to HITS, the catalogue service
099     * shall return an empty &lt;GetRecordsResponse&gt; element with the numberOfRecordsMatched
100     * attribute set to indicate the number of hits. The other attributes may be set to zero or not
101     * specified at all if they are optional. If the resultType parameter is set to HITS, then the
102     * values for the parameters outputFormat and outputSchema (if specified) shall be ignored since no
103     * actual records will be returned. If the resultType parameter is set to RESULTS, the catalogue
104     * service should generate a complete response with the &lt;GetRecordsResponse&gt; element
105     * containing the result set for the request. If the resultType parameter is set to VALIDATE, the
106     * catalogue service shall validate the request and return an empty &lt;GetRecordsResponse&gt;. All
107     * mandatory attributes may be given a value of zero and all optional attributes may be omitted. If
108     * the request does not validate then a service exception shall be raised as describe in Subclause
109     * 10.3.2.3.</td>
110     * </tr>
111     * <tr>
112     * <td align="center">outputFormat</td>
113     * <td align="center">0|1 (default: text/xml)</td>
114     * <td>The outputFormat parameter is used to control the format of the output that is generated in
115     * response to a GetRecords request. Its value must be a MIME type. The default value, "text/xml",
116     * means that the output shall be an XML document. All registries shall at least support XML as an
117     * output format. Other output formats may be supported and may include output formats such as TEXT
118     * (MIME type text/plain), or HTML (MIME type text/html). The list of output formats that a CSW
119     * instance provides must be advertised in the Capabilities document. In the case where the output
120     * format is text/xml, the CSW must generate an XML document that validates against a schema
121     * document that is specified in the output document via the xsi:schemaLocation attribute defined in
122     * XML.</td>
123     * </tr>
124     * <tr>
125     * <td align="center">outputSchema</td>
126     * <td align="center">0|1 (default: OGCCORE)</td>
127     * <td>The outputSchema parameter is used to indicate the schema of the output that is generated in
128     * response to a GetRecords request. The default value for this parameter shall be OGCCORE
129     * indicating that the schema for the core returnable properties (as defined in subclause 6.3.3)
130     * shall be used. Application profiles may define additional values for outputSchema and may
131     * redefine the default value but all profiles must support the value OGCCORE. Examples values for
132     * the outputSchema parameter might be FGDC, or ISO19119, ISO19139 or ANZLIC. The list of supported
133     * output schemas must be advertised in the capabilities document.</tr>
134     * <tr>
135     * <td align="center">startPosition</td>
136     * <td align="center">0|1 (default: 1)</td>
137     * <td>The startPosition paramater is used to indicate at which record position the catalogue
138     * should start generating output. The default value is 1 meaning it starts at the first record in
139     * the result set.</td>
140     * </tr>
141     * <tr>
142     * <td align="center">maxRecords</td>
143     * <td align="center">0|1 (default: 10)</td>
144     * <td>The maxRecords parameter is used to define the maximum number of records that should be
145     * returned from the result set of a query. If it is not specified, then 10 records shall be
146     * returned. If its value is set to zero, then the behavior is indentical to setting
147     * "resultType=HITS" as described above.</td>
148     * </tr>
149     * <tr>
150     * <td align="center">typeName</td>
151     * <td align="center">1</td>
152     * <td>The typeName parameter is a list of record type names that define a set of metadata record
153     * element names which will be constrained in the predicate of the query. In addition, all or some
154     * of the these names may be specified in the query to define which metadata record elements the
155     * query should present in the response to the GetRecords operation.</td>
156     * </tr>
157     * <tr>
158     * <td align="center">ElementSetName / ElementName</td>
159     * <td align="center">* (default: 10)</td>
160     * <td>The ElementName parameter is used to specify one or more metadata record elements that the
161     * query should present in the response to the a GetRecords operation. Well known sets of element
162     * may be named, in which case the ElementSetName parameter may be used (e.g.brief, summary or
163     * full). If neither parameter is specified, then a CSW shall present all metadata record elements.
164     * As mentioned above, if the outputFormat parameter is set to text/xml, then the response to the
165     * GetRecords operation shall validate against a schema document that is referenced in the response
166     * using the xmlns attributes. If the set of metadata record elements that the client specifies in
167     * the query in insufficient to generate a valid XML response document, a CSW may augment the list
168     * of elements presented to the client in order to be able to generate a valid document. Thus a
169     * client application should expect to receive more than the requested elements if the output format
170     * is set to XML. </td>
171     * </tr>
172     * <tr>
173     * <td align="center">CONSTRAINTLANGUAGE / Constraint</td>
174     * <td align="center">0|1</td>
175     * <td>Each request encoding (XML and KVP) has a specific mechanism for specifying the predicate
176     * language that will be used to constrain a query. In the XML encoding, the element
177     * &lt;Constraint&gt; is used to define the query predicate. The root element of the content of the
178     * &lt;Constraint&gt; element defines the predicate language that is being used. Two possible root
179     * elements are &lt;ogc:Filter&gt; for the OGC XML filter encoding, and &lt;csw:CqlText&gt; for a
180     * common query language string. An example predicate specification in the XML encoding is:
181     * 
182     * &lt;Constraint&gt; &lt;CqlText&gt;prop1!=10&lt;/CqlText&gt; &lt;/Constraint&gt;
183     * 
184     * In the KVP encoding, the parameter CONSTRAINTLANGUAGE is used to specify the predicate language
185     * being used. The Constraint parameter is used to specify the actual predicate. For example, to
186     * specify a CQL predicate, the following parameters would be set in the KVP encoding: <br>
187     * 
188     * ...CONSTRAINTLANGUAGE=CQL_TEXT&amp;CONSTRAINT=&quot;prop1!=10&quot;...
189     * 
190     * </td>
191     * </tr>
192     * <tr>
193     * <td align="center">SortBy</td>
194     * <td align="center">0|1</td>
195     * <td>The result set may be sorted by specifying one or more metadata record elements upon which
196     * to sort. In KVP encoding, the SORTBY parameter is used to specify the list of sort elements. The
197     * value for the SORTBY parameter is a comma-separated list of metadata record element names upon
198     * which to sort the result set. The format for each element in the list shall be either element
199     * name:A indicating that the element values should be sorted in ascending order or element name:D
200     * indicating that the element values should be sorted in descending order. For XML encoded
201     * requests, the &lt;ogc:SortBy&gt; element is used to specify a list of sort metadata record
202     * elements. The attribute sortOrder is used to specify the sort order for each element. Valid
203     * values for the sortOrder attribute are ASC indicating an ascending sort and DESC indicating a
204     * descending sort.</td>
205     * </tr>
206     * <tr>
207     * <td align="center">DistributedSearch / hopCount</td>
208     * <td align="center">0|1 (default: FALSE)</td>
209     * <td>The DistributedSearch parameter may be used to indicate that the query should be
210     * distributed. The default query behaviour, if the DistributedSearch parameter is set to FALSE (or
211     * is not specified at all), is to execute the query on the local server. In the XML encoding, if
212     * the &lt;DistributedSearch&gt; element is not specified then the query is executed on the local
213     * server. <br>
214     * <br>
215     * The hopCount parameter controls the distributed query behaviour by limiting the maximum number of
216     * message hops before the search is terminated. Each catalogue decrements this value by one when
217     * the request is received and does not propagate the request if the hopCount=0.</td>
218     * </tr>
219     * <tr>
220     * <td align="center">ResponseHandler</td>
221     * <td align="center">0|1</td>
222     * <td>The ResponseHandler parameter is a flag that indicates how the GetRecords operation should
223     * be processed by a CSW. If the parameter is not present, then the GetRecords operation is
224     * processed synchronously meaning that the client sends the GetRecords request to a CSW and waits
225     * to receive a valid response or exception message. The CSW immediately processes the GetRecords
226     * request while the client waits for a response. The problem with this mode of operation is that
227     * the client may timeout waiting for the CSW to process the request. If the ResponseHandler
228     * parameter is present, the GetRecords operation is processed asynchronously. In this case, the CSW
229     * responds immediately to a client's request with an acknowledgment message that tells the client
230     * that the request has been received and validated, and notification of completion will be sent to
231     * the URI specified as the value of the ResponseHandler parameter.</td>
232     * </tr>
233     * </table>
234     * 
235     * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a>
236     * @version $Revision: 7506 $
237     * 
238     * 
239     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a>
240     * @author <a href="mailto:tfr@users.sourceforge.net">Torsten Friebe </a>
241     * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a>
242     * 
243     * @author last edited by: $Author: apoth $
244     * 
245     * @version $Revision: 7506 $, $Date: 2007-06-06 18:09:41 +0200 (Mi, 06 Jun 2007) $
246     */
247    
248    public class GetRecords extends AbstractCSWRequest {
249    
250        private static final long serialVersionUID = 2796229558893029054L;
251    
252        private static final ILogger LOG = LoggerFactory.getLogger( GetRecords.class );
253    
254        protected static final String DEFAULT_OUTPUTFORMAT = "application/xml";
255    
256        protected static final String DEFAULT_OUTPUTSCHEMA = "csw:Record";
257    
258        protected static final int DEFAULT_STARTPOSITION = 1;
259    
260        protected static final int DEFAULT_MAX_RECORDS = 10;
261    
262        protected static final int DEFAULT_HOPCOUNT = 2;
263    
264        protected static final String DEFAULT_VERSION = "2.0.0";
265    
266        /**
267         * defining HITS as String
268         */
269        public static String RESULT_TYPE_STRING_HITS = "HITS";
270    
271        /**
272         * defining VALIDATE as String
273         */
274        public static String RESULT_TYPE_STRING_VALIDATE = "VALIDATE";
275    
276        /**
277         * defining RESULTS as String
278         */
279        public static String RESULT_TYPE_STRING_RESULTS = "RESULTS";
280    
281        private RESULT_TYPE resultType = RESULT_TYPE.RESULTS;
282    
283        // keys are Strings (namespace prefix or "" for default namespace), values
284        // are URIs
285        private Map<String, URI> namespace;
286    
287        private String outputFormat;
288    
289        private String outputSchema;
290    
291        private int startPosition;
292    
293        private int maxRecords;
294    
295        private int hopCount;
296    
297        private URI responseHandler;
298    
299        //private Query[] queries;
300    
301        private Query query;
302    
303        /**
304         * Creates a new <code>GetRecords</code> instance.
305         * 
306         * @param id
307         * @param version
308         * @param vendorSpecificParameters
309         * @param namespace
310         * @param resultType
311         * @param outputFormat
312         * @param outputSchema
313         * @param startPosition
314         * @param maxRecords
315         * @param hopCount
316         * @param responseHandler
317         * @param query
318         */
319        public GetRecords( String id, String version, Map<String, String> vendorSpecificParameters,
320                           Map<String, URI> namespace, RESULT_TYPE resultType, String outputFormat,
321                           String outputSchema, int startPosition, int maxRecords, int hopCount,
322                           URI responseHandler, Query query ) {
323            super( version, id, vendorSpecificParameters );
324            this.namespace = namespace;
325            this.resultType = resultType;
326            this.outputFormat = outputFormat;
327            this.outputSchema = outputSchema;
328            this.startPosition = startPosition;
329            this.maxRecords = maxRecords;
330            this.hopCount = hopCount;
331            this.responseHandler = responseHandler;
332            this.query = query;
333        }
334    
335        /**
336         * creates a GetRecords request from the XML fragment passed. The passed element must be valid
337         * against the OGC CSW 2.0 GetRecords schema.
338         * 
339         * TODO respect namespaces (use QualifiedNames) for type names
340         * 
341         * @param id
342         *            unique ID of the request
343         * @param root
344         *            root element of the GetRecors request
345         * @return a GetRecords instance with given id and parsed values from the root element
346         * @throws MissingParameterValueException
347         *             if a required parameter was not set
348         * @throws InvalidParameterValueException
349         *             if a parameter is invalid
350         * @throws OGCWebServiceException
351         *             if something went wrong while creating the Request
352         */
353        public static GetRecords create( String id, Element root )
354                                throws MissingParameterValueException, InvalidParameterValueException,
355                                OGCWebServiceException {
356    
357            GetRecordsDocument document = new GetRecordsDocument();
358            document.setRootElement( root );
359            GetRecords ogcRequest = document.parse( id );
360    
361            return ogcRequest;
362        }
363    
364        /**
365         * Creates a new <code>GetRecords</code> instance from the values stored in the submitted Map.
366         * Keys (parameter names) in the Map must be uppercase.
367         * 
368         * @TODO evaluate vendorSpecificParameter
369         * 
370         * @param kvp
371         *            Map containing the parameters
372         * @return a GetRecords instance with given id and values from the kvp
373         * @exception InvalidParameterValueException
374         * @exception MissingParameterValueException
375         * @throws OperationNotSupportedException
376         *             if an CQL_TEXT constrain is requested
377         */
378        public static GetRecords create( Map<String, String> kvp )
379                                throws InvalidParameterValueException, MissingParameterValueException,
380                                OperationNotSupportedException {
381    
382    
383            // String version = "2.0.0";
384            //Map<String, String> vendorSpecificParameters = null;
385            // RESULT_TYPE resultType = RESULT_TYPE.HITS;
386            // String outputFormat = "text/xml";
387            // String outputSchema = "OGCCORE";
388            // int startPosition = 1;
389            // int maxRecords = 10;
390            // int hopCount = 2;
391    
392            String service = getParam( "SERVICE", kvp, "CSW" );
393            if ( !"CSW".equals( service ) ) {
394                throw new InvalidParameterValueException(
395                                                          "GetRecordDocument",
396                                                          Messages.getMessage( "CSW_INVALID_SERVICE_PARAM" ),
397                                                          ExceptionCode.INVALIDPARAMETERVALUE );
398            }
399    
400            String id = getParam( "ID", kvp, "" );
401            LOG.logDebug( "GetRecordRequest id=" + id );
402            
403            
404            String version = getParam( "VERSION", kvp, DEFAULT_VERSION );
405            if ( ! (DEFAULT_VERSION.equals( version ) ||
406                                    "2.0.1".equals( version ) ||
407                                    "2.0.2".equals( version ) ) ) {
408                throw new InvalidParameterValueException("GetRecords",
409                                                          Messages.getMessage(
410                                                                               "CSW_NOT_SUPPORTED_VERSION",
411                                                                               GetRecords.DEFAULT_VERSION,
412                                                                               "2.0.1",
413                                                                               "2.0.2",
414                                                                               version ),
415                                                                               ExceptionCode.INVALIDPARAMETERVALUE ) ;
416            }
417    
418            // extract namespace mappings
419            Map<String, URI> namespaceMappings = getNSMappings( getParam( "NAMESPACE", kvp, null ) );
420    
421            String resultTypeString = getParam( "RESULTTYPE", kvp, RESULT_TYPE_STRING_HITS );
422            RESULT_TYPE resultType = RESULT_TYPE.HITS;
423            if ( RESULT_TYPE_STRING_HITS.equalsIgnoreCase( resultTypeString ) ) {
424                resultType = RESULT_TYPE.HITS;
425            } else if ( RESULT_TYPE_STRING_RESULTS.equalsIgnoreCase( resultTypeString ) ) {
426                resultType = RESULT_TYPE.RESULTS;
427            } else if ( RESULT_TYPE_STRING_VALIDATE.equalsIgnoreCase( resultTypeString ) ) {
428                resultType = RESULT_TYPE.VALIDATE;
429            } else {
430                throw new InvalidParameterValueException(
431                                                          "GetRecords",
432                                                          Messages.getMessage(
433                                                                               "CSW_INVALID_RESULTTYPE",
434                                                                               resultTypeString,
435                                                                               GetRecords.RESULT_TYPE_STRING_HITS,
436                                                                               GetRecords.RESULT_TYPE_STRING_RESULTS,
437                                                                               GetRecords.RESULT_TYPE_STRING_VALIDATE ),
438                                                          ExceptionCode.INVALIDPARAMETERVALUE );
439            }
440    
441            String outputFormat = getParam( "OUTPUTFORMAT", kvp, DEFAULT_OUTPUTFORMAT );
442            String outputSchema = getParam( "OUTPUTSCHEMA", kvp, DEFAULT_OUTPUTSCHEMA );
443            int startPosition = getParamAsInt( "STARTPOSITION", kvp, DEFAULT_STARTPOSITION );
444            if ( startPosition < 1 ) {
445                String msg = Messages.getMessage( "CSW_INVALID_STARTPOSITION",
446                                                  new Integer( startPosition ) );
447                throw new InvalidParameterValueException( msg );
448            }
449            int maxRecords = getParamAsInt( "MAXRECORDS", kvp, DEFAULT_MAX_RECORDS );
450    
451            if ( maxRecords < 0 ) {
452                maxRecords = DEFAULT_MAX_RECORDS;
453            }
454    
455            // build one Query object for each specified typeName
456            String tmp = getRequiredParam( "TYPENAMES", kvp );
457            String[] typeNames = StringTools.toArray( tmp, ",", false );
458            if ( typeNames.length == 0 ) {
459                throw new MissingParameterValueException( "Mandatory parameter 'TYPENAMES' is missing!" );
460            }
461    
462            String elementSetName = kvp.remove( "ELEMENTSETNAME" );
463            if ( elementSetName == null ) {
464                elementSetName = kvp.remove( "ELEMENTNAME" );
465            } else {
466                String test = kvp.remove( "ELEMENTNAME" );
467                if( test != null ){
468                    LOG.logInfo( Messages.getMessage( "CSW_ELEMENT_SET_NAME_DUPLICATE") );
469                }
470            }
471            String[] elementNames = null;
472            if( elementSetName != null ){
473                elementNames = StringTools.toArray( elementSetName, ",", false );
474                if ( elementNames.length == 0 ) {
475                    elementNames = null;
476                }
477            } 
478            if ( elementNames == null ){
479                elementNames = new String[]{"Full"};
480            }
481            
482            String constraintString = kvp.remove( "CONSTRAINT" );
483            Filter constraint = null;
484            String constraintLanguage = null;
485            if ( constraintString != null ) {
486                // build Filter object (from CONSTRAINT parameter)
487                constraintLanguage = kvp.remove( "CONSTRAINTLANGUAGE" );
488                if ( constraintLanguage != null ) {
489                    if ( "CQL_TEXT".equalsIgnoreCase( constraintLanguage.trim() ) ) {
490                        throw new OperationNotSupportedException(
491                                                                  Messages.getMessage( "CSW_NO_CQL_IMPLEMENTATION" ) );
492                    } else if ( !"FILTER".equalsIgnoreCase( constraintLanguage.trim() ) ) {
493                        throw new InvalidParameterValueException(
494                                                                  Messages.getMessage(
495                                                                                       "CSW_INVALID_CONSTRAINT_LANGUAGE",
496                                                                                       constraintLanguage.trim() ) );
497                    }
498                } else {
499                    throw new InvalidParameterValueException(
500                                                             Messages.getMessage( "CSW_CQL_NOR_FILTER_KVP" ) );
501                }
502    
503                try {
504                    Document doc = XMLTools.parse( new StringReader( constraintString ) );
505                    Element element = doc.getDocumentElement();
506                    constraint = AbstractFilter.buildFromDOM( element );
507                } catch ( Exception e ) {
508                    String msg = "An error occured when parsing the 'CONSTRAINT' parameter "
509                                 + "Filter expression: " + e.getMessage();
510                    throw new InvalidParameterValueException( msg );
511                }
512            } 
513    
514    
515            SortProperty[] sortProperties = SortProperty.create( kvp.remove( "SORTBY" ) );
516    
517    //        Query[] queries = new Query[typeNames.length];
518    //        for ( int i = 0; i < typeNames.length; i++ ) {
519                Query query = new Query( elementSetName, elementNames, constraint, sortProperties,
520                                        typeNames );
521    //        }
522    
523            // find out if the query should be performed locally or in a distributed
524            // fashion
525            int hopCount = DEFAULT_HOPCOUNT;
526            String distributedSearch = getParam( "DISTRIBUTEDSEARCH", kvp, "false" );
527            if ( distributedSearch.equalsIgnoreCase( "true" ) ) {
528                hopCount = getParamAsInt( "HOPCOUNT", kvp, DEFAULT_HOPCOUNT );
529            }
530    
531            String rHandler = kvp.remove( "RESPONSEHANDLER" );
532            URI responseHandler = null;
533            if ( rHandler != null ) {
534                try {
535                    responseHandler = new URI( rHandler );
536                } catch ( URISyntaxException e ) {
537                    throw new InvalidParameterValueException(
538                                                              Messages.getMessage(
539                                                                                   "CSW_INVALID_RESPONSE_HANDLER",
540                                                                                   rHandler ) );
541                }
542                throw new OperationNotSupportedException(
543                                                          Messages.getMessage( "CSW_NO_REPONSE_HANDLER_IMPLEMENTATION" ) );
544    
545            }
546    
547            return new GetRecords( id, version, kvp, namespaceMappings,
548                                   resultType, outputFormat, outputSchema, startPosition, maxRecords,
549                                   hopCount, responseHandler, query );
550        }
551    
552        /**
553         * Used to specify a namespace and its prefix. Format must be [ <prefix>:] <url>. If the prefix
554         * is not specified then this is the default namespace
555         * <p>
556         * Zero or one (Optional) ; Include value for each distinct namespace used by all qualified
557         * names in the request. If not included, all qualified names are in default namespace
558         * <p>
559         * The NAMESPACE parameter is included in the KVP encoding to allow clients to bind any
560         * namespace prefixes that might be used for qualified names specified in other parameters. For
561         * example, the typeName parameter may include qualified names of the form namespace
562         * prefix:name.
563         * <p>
564         * The value of the NAMESPACE parameter is separated list of character strings of the form
565         * [namespace prefix:]namespace url. Not including the name namespace prefix binds the specified
566         * URL to the default namespace. As in XML, only one default namespace may be bound.
567         * 
568         * @return the mapped namespaces or <code>null</code> if all qualified names are in default
569         *         namespace.
570         * 
571         */
572        public Map<String, URI> getNamespace() {
573            return this.namespace;
574        }
575    
576        /**
577         * The resultType parameter may have the values HITS, RESULTS or VALIDATE and is used to
578         * indicate whether the catalogue service returns the full result set, the number of hits the
579         * query found or validates the request.
580         * <p>
581         * If the resultType parameter is set to HITS, the catalogue service shall return an empty
582         * &lt;GetRecordsResponse&gt;element with the numberOfRecordsMatched attribute set to indicate
583         * the number of hits. The other attributes may be set to zero or not specified at all if they
584         * are optional.
585         * <p>
586         * If the resultType parameter is set to HITS, then the values for the parameters outputFormat
587         * and outputSchema (if specified) shall be ignored since no actual records will be returned
588         * <p>
589         * If the resultType parameter is set to RESULTS, the catalogue service should generate a
590         * complete response with the &lt;GetRecordsResponse&gt;element containing the result set for
591         * the request
592         * <p>
593         * If the resultType parameter is set to VALIDATE, the catalogue service shall validate the
594         * request and return an empty &lt;GetRecordsResponse&gt;. All mandatory attributes may be given
595         * a value of zero and all optional attributes may be omitted. If the request does not validate
596         * then a service exception shall be raised
597         * 
598         * @return one of HITS, RESULTS or VALIDATE
599         * 
600         */
601        public RESULT_TYPE getResultType() {
602            return this.resultType;
603        }
604    
605        /**
606         * The resultType parameter may have the values HITS, RESULTS or VALIDATE and is used to
607         * indicate whether the catalogue service returns the full result set, the number of hits the
608         * query found or validates the request.
609         * <p>
610         * If the resultType parameter is set to HITS, the catalogue service shall return an empty
611         * &lt;GetRecordsResponse&gt;element with the numberOfRecordsMatched attribute set to indicate
612         * the number of hits. The other attributes may be set to zero or not specified at all if they
613         * are optional.
614         * <p>
615         * If the resultType parameter is set to HITS, then the values for the parameters outputFormat
616         * and outputSchema (if specified) shall be ignored since no actual records will be returned
617         * <p>
618         * If the resultType parameter is set to RESULTS, the catalogue service should generate a
619         * complete response with the &lt;GetRecordsResponse&gt;element containing the result set for
620         * the request
621         * <p>
622         * If the resultType parameter is set to VALIDATE, the catalogue service shall validate the
623         * request and return an empty &lt;GetRecordsResponse&gt;. All mandatory attributes may be given
624         * a value of zero and all optional attributes may be omitted. If the request does not validate
625         * then a service exception shall be raised
626         * 
627         * @return the resulttype as a String, one of "HITS", "VALIDATE" or "RESULTS"
628         * 
629         */
630        public String getResultTypeAsString() {
631            String resultTypeString = null;
632            switch ( this.resultType ) {
633            case HITS: {
634                resultTypeString = RESULT_TYPE_STRING_HITS;
635                break;
636            }
637            case RESULTS: {
638                resultTypeString = RESULT_TYPE_STRING_RESULTS;
639                break;
640            }
641            case VALIDATE: {
642                resultTypeString = RESULT_TYPE_STRING_VALIDATE;
643                break;
644            }
645            }
646            return resultTypeString;
647        }
648    
649        /**
650         * sets the resultType of a request. This may be useful to perform a request first with
651         * resultType = HITS to determine the total number of records matching a query and afterwards
652         * performing the same request with resultType = RESULTS (and maxRecords &lt; number of matched
653         * records).
654         * 
655         * @param resultType
656         */
657        public void setResultType( RESULT_TYPE resultType ) {
658            this.resultType = resultType;
659        }
660    
661        /**
662         * returns <= 0 if no distributed search shall be performed. otherwise the recursion depht is
663         * returned.
664         * <p>
665         * The hopCount parameter controls the distributed query behaviour by limiting the maximum
666         * number of message hops before the search is terminated. Each catalogue decrements this value
667         * by one when the request is received and does not propagate the request if the hopCount=0
668         * 
669         * @return <= 0 if no distributed search shall be performed. otherwise the recursion depht is
670         *         returned.
671         * 
672         */
673        public int getHopCount() {
674            return this.hopCount;
675        }
676    
677        /**
678         * Value is Mime type;The only value that must be supported is text/xml. Other suppored values
679         * may include text/html and text/plain
680         * <p>
681         * The outputFormat parameter is used to control the format of the output that is generated in
682         * response to a GetRecords request. Its value must be a MIME type. The default value,
683         * "text/xml", means that the output shall be an XML document. All registries shall at least
684         * support XML as an output format. Other output formats may be supported and may include output
685         * formats such as TEXT (MIME type text/plain), or HTML (MIME type text/html). The list of
686         * output formats that a CSW instance provides must be advertised in the Capabilities document
687         * <p>
688         * In the case where the output format is text/xml, the CSW must generate an XML document that
689         * validates against a schema document that is specified in the output document via the
690         * xsi:schemaLocation attribute defined in XML
691         * 
692         * @return Value is a Mime type
693         * 
694         */
695        public String getOutputFormat() {
696            return this.outputFormat;
697        }
698    
699        /**
700         * The outputSchema parameter is used to indicate the schema of the output that is generated in
701         * response to a GetRecords request. The default value for this parameter shall be OGCCORE
702         * indicating that the schema for the core returnable properties shall be used. Application
703         * profiles may define additional values for outputSchema and may redefine the default value but
704         * all profiles must support the value OGCCORE
705         * <p>
706         * Examples values for the outputSchema parameter might be FGDC, or ISO19119, ISO19139 or
707         * ANZLIC. The list of supported output schemas must be advertised in the capabilities document
708         * 
709         * @return The default value for this parameter shall be OGCCORE
710         * 
711         */
712        public String getOutputSchema() {
713            return this.outputSchema;
714        }
715    
716        /**
717         * @return the number of the first returned dataset. Zero or one (Optional)Default value is 1.
718         *         If startPosition > the number of datasets satisfying the constraint, no dataset will
719         *         be returned
720         * 
721         */
722        public int getStartPosition() {
723            return this.startPosition;
724        }
725    
726        /**
727         * @return The maxRecords parameter. It is used to define the maximum number of records that
728         *         should be returned from the result set of a query. If it is not specified, then 10
729         *         records shall be returned. If its value is set to zero, then the behavior is
730         *         indentical to setting "resultType=HITS"
731         * 
732         */
733        public int getMaxRecords() {
734            return this.maxRecords;
735        }
736    
737        /**
738         * @return an Array of Strings containing only one single uri.toString() with the location of
739         *         the responsehandler.
740         * @deprecated the spec only defines one reponsehandler, the preferred way to get the
741         *             ResponseHandler is the {@link #getResponseHandler()} method.
742         */
743        @Deprecated
744        public String[] getResponseHandlers() {
745            if ( responseHandler != null ) {
746                return new String[] { responseHandler.toString() };
747            }
748            return new String[0];
749        }
750    
751        /**
752         * @return the location of a response adress to which an asynchronous result may be sent.
753         */
754        public URI getResponseHandler() {
755            return responseHandler;
756        }
757    
758        /**
759         * @return returns an Array respresentation of the single query object. 
760         * @deprecated this method is solely for backward compatibility please use the {@link #getQuery()} method instead.
761         */
762        @Deprecated
763        public Query[] getQueries() {
764            return new Query[]{query};
765        }
766    
767        /**
768         * @return the query object.
769         */
770        public Query getQuery() {
771            return query;
772        }
773        
774        /**
775         * @see #getQuery()
776         * @param query
777         */
778        public void setQuery(Query query) {
779            this.query = query;
780        }
781    
782        /**
783         * The <code>RESULT_TYPE</code> a simple enum which defines some result values of a GetRecord.
784         * 
785         * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
786         * 
787         * @author last edited by: $Author: apoth $
788         * 
789         * @version $Revision: 7506 $, $Date: 2007-06-06 18:09:41 +0200 (Mi, 06 Jun 2007) $
790         * 
791         */
792    
793        public static enum RESULT_TYPE {
794            /**
795             * HITS, the catalogue service shall return an empty &lt;GetRecordsResponse&gt;element with
796             * the numberOfRecordsMatched attribute set to indicate the number of hits
797             */
798            HITS,
799            /**
800             * VALIDATE, the catalogue service shall validate the request
801             */
802            VALIDATE,
803            /**
804             * RESULTS, the catalogue service should generate a complete response with the
805             * &lt;GetRecordsResponse&gt;element containing the result set for the request
806             */
807            RESULTS
808        }
809    }