001: //=============================================================================
002: //=== Copyright (C) 2001-2007 Food and Agriculture Organization of the
003: //=== United Nations (FAO-UN), United Nations World Food Programme (WFP)
004: //=== and United Nations Environment Programme (UNEP)
005: //===
006: //=== This program is free software; you can redistribute it and/or modify
007: //=== it under the terms of the GNU General Public License as published by
008: //=== the Free Software Foundation; either version 2 of the License, or (at
009: //=== your option) any later version.
010: //===
011: //=== This program is distributed in the hope that it will be useful, but
012: //=== WITHOUT ANY WARRANTY; without even the implied warranty of
013: //=== MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: //=== General Public License for more details.
015: //===
016: //=== You should have received a copy of the GNU General Public License
017: //=== along with this program; if not, write to the Free Software
018: //=== Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
019: //===
020: //=== Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2,
021: //=== Rome - Italy. email: geonetwork@osgeo.org
022: //==============================================================================
023:
024: package org.fao.geonet.kernel.csw.services;
025:
026: import java.util.ArrayList;
027: import java.util.HashSet;
028: import java.util.Iterator;
029: import java.util.List;
030: import java.util.Map;
031: import java.util.Set;
032: import java.util.StringTokenizer;
033: import jeeves.server.context.ServiceContext;
034: import jeeves.utils.Util;
035: import jeeves.utils.Xml;
036: import org.fao.geonet.constants.Geonet;
037: import org.fao.geonet.csw.common.Csw;
038: import org.fao.geonet.csw.common.Csw.ConstraintLanguage;
039: import org.fao.geonet.csw.common.Csw.ElementSetName;
040: import org.fao.geonet.csw.common.Csw.OutputSchema;
041: import org.fao.geonet.csw.common.Csw.ResultType;
042: import org.fao.geonet.csw.common.Csw.TypeName;
043: import org.fao.geonet.csw.common.exceptions.CatalogException;
044: import org.fao.geonet.csw.common.exceptions.InvalidParameterValueEx;
045: import org.fao.geonet.csw.common.exceptions.MissingParameterValueEx;
046: import org.fao.geonet.csw.common.exceptions.NoApplicableCodeEx;
047: import org.fao.geonet.kernel.csw.CatalogService;
048: import org.fao.geonet.kernel.csw.services.getrecords.SearchController;
049: import org.fao.geonet.kernel.csw.services.getrecords.SortField;
050: import org.jdom.Element;
051: import org.z3950.zing.cql.CQLNode;
052: import org.z3950.zing.cql.CQLParser;
053:
054: //=============================================================================
055:
056: public class GetRecords extends AbstractOperation implements
057: CatalogService {
058: //---------------------------------------------------------------------------
059: //---
060: //--- Constructor
061: //---
062: //---------------------------------------------------------------------------
063:
064: public GetRecords() {
065: }
066:
067: //---------------------------------------------------------------------------
068: //---
069: //--- API methods
070: //---
071: //---------------------------------------------------------------------------
072:
073: public String getName() {
074: return "GetRecords";
075: }
076:
077: //---------------------------------------------------------------------------
078:
079: public Element execute(Element request, ServiceContext context)
080: throws CatalogException {
081: checkService(request);
082: checkVersion(request);
083: checkOutputFormat(request);
084:
085: int startPos = getStartPosition(request);
086: int maxRecords = getMaxRecords(request);
087: int hopCount = getHopCount(request);
088:
089: Element query = request.getChild("Query", Csw.NAMESPACE_CSW);
090:
091: ResultType resultType = ResultType.parse(request
092: .getAttributeValue("resultType"));
093: ;
094: OutputSchema outSchema = OutputSchema.parse(request
095: .getAttributeValue("outputSchema"));
096: ElementSetName setName = getElementSetName(query,
097: ElementSetName.FULL);
098: Set<String> elemNames = getElementNames(query);
099: Set<TypeName> typeNames = getTypeNames(request);
100: Element filterExpr = getFilterExpression(request, context);
101: List<SortField> sortFields = getSortFields(request);
102:
103: if (resultType == ResultType.VALIDATE)
104: throw new InvalidParameterValueEx("resultType", resultType
105: .toString());
106:
107: Element response = new Element(getName() + "Response",
108: Csw.NAMESPACE_CSW);
109:
110: Element status = new Element("SearchStatus", Csw.NAMESPACE_CSW);
111: status.setAttribute("status", "complete");
112:
113: SearchController sc = new SearchController();
114:
115: response.addContent(status);
116: response.addContent(sc.search(context, startPos, maxRecords,
117: hopCount, resultType, outSchema, setName, typeNames,
118: filterExpr, sortFields, elemNames));
119:
120: return response;
121: }
122:
123: //---------------------------------------------------------------------------
124:
125: public Element adaptGetRequest(Map<String, String> params)
126: throws CatalogException {
127: String service = params.get("service");
128: String version = params.get("version");
129: String resultType = params.get("resulttype");
130: String outputFormat = params.get("outputformat");
131: String outputSchema = params.get("outputschema");
132: String startPosition = params.get("startposition");
133: String maxRecords = params.get("maxrecords");
134: String hopCount = params.get("hopcount");
135: String distribSearch = params.get("distributedsearch");
136: String typeNames = params.get("typenames");
137: String elemSetName = params.get("elementsetname");
138: String elemName = params.get("elementname");
139: String constraint = params.get("constraint");
140: String constrLang = params.get("constraintlanguage");
141: String constrLangVer = params
142: .get("constraint_language_version");
143: String sortby = params.get("sortby");
144:
145: //--- build POST request
146:
147: Element request = new Element(getName(), Csw.NAMESPACE_CSW);
148:
149: setAttrib(request, "service", service);
150: setAttrib(request, "version", version);
151: setAttrib(request, "resultType", resultType);
152: setAttrib(request, "outputFormat", outputFormat);
153: setAttrib(request, "outputSchema", outputSchema);
154: setAttrib(request, "startPosition", startPosition);
155: setAttrib(request, "maxRecords", maxRecords);
156:
157: if (distribSearch != null && distribSearch.equals("true")) {
158: Element ds = new Element("DistributedSearch",
159: Csw.NAMESPACE_CSW);
160: ds.setText("TRUE");
161:
162: if (hopCount != null)
163: ds.setAttribute("hopCount", hopCount);
164:
165: request.addContent(ds);
166: }
167:
168: //------------------------------------------------------------------------
169: //--- build query element
170:
171: Element query = new Element("Query", Csw.NAMESPACE_CSW);
172: request.addContent(query);
173:
174: if (typeNames != null)
175: setAttrib(query, "typeNames", typeNames.replace(',', ' '));
176:
177: //--- these 2 are in mutual exclusion
178:
179: addElement(query, "ElementSetName", elemSetName);
180: fill(query, "ElementName", elemName);
181:
182: //------------------------------------------------------------------------
183: //--- handle constraint
184:
185: ConstraintLanguage language = ConstraintLanguage
186: .parse(constrLang);
187:
188: if (constraint != null) {
189: Element constr = new Element("Constraint",
190: Csw.NAMESPACE_CSW);
191: query.addContent(constr);
192:
193: if (language == ConstraintLanguage.CQL)
194: addElement(constr, "CqlText", constraint);
195: else
196: try {
197: constr
198: .addContent(Xml.loadString(constraint,
199: false));
200: } catch (Exception e) {
201: e.printStackTrace();
202: throw new NoApplicableCodeEx(
203: "Constraint is not a valid xml");
204: }
205:
206: setAttrib(constr, "version", constrLangVer);
207: }
208:
209: //------------------------------------------------------------------------
210: //--- handle sortby
211:
212: if (sortby != null) {
213: Element sortBy = new Element("SortBy", Csw.NAMESPACE_OGC);
214: query.addContent(sortBy);
215:
216: StringTokenizer st = new StringTokenizer(sortby, ",");
217:
218: while (st.hasMoreTokens()) {
219: String sortInfo = st.nextToken();
220: String field = sortInfo.substring(0,
221: sortInfo.length() - 2);
222: boolean ascen = sortInfo.endsWith(":A");
223:
224: Element sortProp = new Element("SortProperty",
225: Csw.NAMESPACE_OGC);
226: sortBy.addContent(sortProp);
227:
228: Element propName = new Element("PropertyName",
229: Csw.NAMESPACE_OGC).setText(field);
230: Element sortOrder = new Element("SortOrder",
231: Csw.NAMESPACE_OGC).setText(ascen ? "ASC"
232: : "DESC");
233:
234: sortProp.addContent(propName);
235: sortProp.addContent(sortOrder);
236: }
237: }
238:
239: return request;
240: }
241:
242: //---------------------------------------------------------------------------
243: //---
244: //--- Private methods
245: //---
246: //---------------------------------------------------------------------------
247:
248: private void checkOutputFormat(Element request)
249: throws InvalidParameterValueEx {
250: String format = request.getAttributeValue("outputFormat");
251:
252: if (format == null)
253: return;
254:
255: if (!format.equals("application/xml"))
256: throw new InvalidParameterValueEx("outputFormat", format);
257: }
258:
259: //---------------------------------------------------------------------------
260:
261: private int getStartPosition(Element request)
262: throws InvalidParameterValueEx {
263: String start = request.getAttributeValue("startPosition");
264:
265: if (start == null)
266: return 1;
267:
268: try {
269: int value = Integer.parseInt(start);
270:
271: if (value >= 1)
272: return value;
273: } catch (NumberFormatException e) {
274: }
275:
276: throw new InvalidParameterValueEx("startPosition", start);
277: }
278:
279: //---------------------------------------------------------------------------
280:
281: private int getMaxRecords(Element request)
282: throws InvalidParameterValueEx {
283: String max = request.getAttributeValue("maxRecords");
284:
285: if (max == null)
286: return 10;
287:
288: try {
289: int value = Integer.parseInt(max);
290:
291: if (value >= 1)
292: return value;
293: } catch (NumberFormatException e) {
294: }
295:
296: throw new InvalidParameterValueEx("maxRecords", max);
297: }
298:
299: //---------------------------------------------------------------------------
300: //--- a return value >= 0 means that a distributed search was requested
301: //--- otherwise the method returns -1
302:
303: private int getHopCount(Element request)
304: throws InvalidParameterValueEx {
305: Element ds = request.getChild("DistributedSearch",
306: Csw.NAMESPACE_CSW);
307:
308: if (ds == null)
309: return -1;
310:
311: String hopCount = ds.getAttributeValue("hopCount");
312:
313: if (hopCount == null)
314: return 2;
315:
316: try {
317: int value = Integer.parseInt(hopCount);
318:
319: if (value >= 0)
320: return value;
321: } catch (NumberFormatException e) {
322: }
323:
324: throw new InvalidParameterValueEx("hopCount", hopCount);
325: }
326:
327: //---------------------------------------------------------------------------
328:
329: private Set<TypeName> getTypeNames(Element request)
330: throws CatalogException {
331: Element query = request.getChild("Query", Csw.NAMESPACE_CSW);
332:
333: return TypeName.parse(query.getAttributeValue("typeNames"));
334: }
335:
336: //---------------------------------------------------------------------------
337:
338: private Element getFilterExpression(Element request,
339: ServiceContext context) throws CatalogException {
340: Element query = request.getChild("Query", Csw.NAMESPACE_CSW);
341: Element constr = query
342: .getChild("Constraint", Csw.NAMESPACE_CSW);
343:
344: if (constr == null)
345: return null;
346:
347: Element filter = constr.getChild("Filter", Csw.NAMESPACE_OGC);
348: Element cql = constr.getChild("CqlText", Csw.NAMESPACE_CSW);
349:
350: if (filter == null && cql == null)
351: throw new NoApplicableCodeEx(
352: "Missing filter expression or cql query");
353:
354: String version = constr.getAttributeValue("version");
355:
356: if (version == null)
357: throw new MissingParameterValueEx("version");
358:
359: if (filter != null && !version.equals(Csw.FILTER_VERSION))
360: throw new InvalidParameterValueEx("version", version);
361:
362: return (filter != null) ? filter : convertCQL(cql.getText(),
363: context);
364: }
365:
366: //---------------------------------------------------------------------------
367:
368: private List<SortField> getSortFields(Element request) {
369: ArrayList<SortField> al = new ArrayList<SortField>();
370:
371: Element query = request.getChild("Query", Csw.NAMESPACE_CSW);
372:
373: if (query == null)
374: return al;
375:
376: Element sortBy = query.getChild("SortBy", Csw.NAMESPACE_OGC);
377:
378: if (sortBy == null)
379: return al;
380:
381: List list = sortBy.getChildren();
382:
383: for (int i = 0; i < list.size(); i++) {
384: Element el = (Element) list.get(i);
385:
386: String field = el.getChildText("PropertyName",
387: Csw.NAMESPACE_OGC);
388: String order = el.getChildText("SortOrder",
389: Csw.NAMESPACE_OGC);
390:
391: al.add(new SortField(field, "DESC".equals(order)));
392: }
393:
394: return al;
395: }
396:
397: //---------------------------------------------------------------------------
398:
399: private Element convertCQL(String cql, ServiceContext context)
400: throws CatalogException {
401: String xmlCql = getCqlXmlString(cql, context);
402: context.debug("Received CQL:\n" + xmlCql);
403:
404: Element xml = getCqlXmlElement(xmlCql, context);
405: String styleSheet = context.getAppPath() + Geonet.Path.CSW
406: + Geonet.File.CQL_TO_FILTER;
407:
408: Element filter = getFilter(xml, styleSheet, context);
409: context.debug("Transformed CQL gives the following filter:\n"
410: + Xml.getString(filter));
411:
412: return filter;
413: }
414:
415: //---------------------------------------------------------------------------
416:
417: private String getCqlXmlString(String cql, ServiceContext context)
418: throws InvalidParameterValueEx {
419: try {
420: CQLParser parser = new CQLParser();
421: CQLNode root = parser.parse(cql);
422:
423: return root.toXCQL(0);
424: } catch (Exception e) {
425: context.error("Error parsing CQL : " + e);
426: context.error(" (C) CQL is :\n" + cql);
427:
428: throw new InvalidParameterValueEx("CqlText", cql);
429: }
430: }
431:
432: //---------------------------------------------------------------------------
433:
434: private Element getCqlXmlElement(String cqlXml,
435: ServiceContext context) throws NoApplicableCodeEx {
436: try {
437: return Xml.loadString(cqlXml, false);
438: } catch (Exception e) {
439: context.error("Bad CQL XML : " + e);
440: context.error(" (C) CQL XML is :\n" + cqlXml);
441:
442: throw new NoApplicableCodeEx("Bad CQL XML : " + cqlXml);
443: }
444: }
445:
446: //---------------------------------------------------------------------------
447:
448: private Element getFilter(Element cql, String styleSheet,
449: ServiceContext context) throws NoApplicableCodeEx {
450: try {
451: return Xml.transform(cql, styleSheet);
452: } catch (Exception e) {
453: context.error("Error during CQL to Filter conversion : "
454: + e);
455: context.error(" (C) StackTrace\n" + Util.getStackTrace(e));
456:
457: throw new NoApplicableCodeEx(
458: "Error during CQL to Filter conversion : " + e);
459: }
460: }
461:
462: //---------------------------------------------------------------------------
463:
464: private Set<String> getElementNames(Element query) {
465: if (query == null)
466: return null;
467:
468: Iterator i = query.getChildren("ElementName",
469: query.getNamespace()).iterator();
470:
471: if (!i.hasNext())
472: return null;
473:
474: HashSet<String> hs = new HashSet<String>();
475:
476: while (i.hasNext()) {
477: Element elem = (Element) i.next();
478:
479: hs.add(elem.getText());
480: }
481:
482: return hs;
483: }
484: }
485:
486: //=============================================================================
|