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