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