/* See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* Esri Inc. licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.esri.gpt.catalog.search;
import com.esri.gpt.catalog.discovery.*;
import com.esri.gpt.framework.context.RequestContext;
import com.esri.gpt.framework.geometry.Envelope;
import com.esri.gpt.framework.util.Val;
import com.esri.gpt.framework.xml.DomUtil;
import com.esri.gpt.framework.xml.XmlIoUtil;
import com.esri.gpt.server.csw.provider.components.CswNamespaces;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Generates a CSW GetRecords request from a discovery query.
*/
public class GetRecordsGenerator {
/** instance variables ====================================================== */
private Document dom;
private String elementSetName = "full";
private String filterVersion = "1.1.0";
private String resultType = "RESULTS";
private String typeNames = "csw:Record";
private String service = "CSW";
private String version = "2.0.2";
/** constructors ============================================================ */
/**
* Constructs with a an active request context.
* @param context the request context
*/
public GetRecordsGenerator(RequestContext context) {}
/** properties ============================================================== */
/**
* Gets the XML document being constructed.
* @return the document
*/
private Document getDom() {
return this.dom;
}
/**
* Sets the XML document being constructed.
* @param dom the document
*/
private void setDom(Document dom) {
this.dom = dom;
}
/**
* Gets the CSW element set name to return.
* <br/>Default = "full"
* @return the element set name
*/
public String getElementSetName() {
return this.elementSetName;
}
/**
* Sets the CSW element set name to return.
* <br/>Default = "full"
* @param elementSetName the element set name
*/
public void setElementSetName(String elementSetName) {
this.elementSetName = Val.chkStr(elementSetName);
}
/**
* Gets the CSW filter version.
* <br/>Default = "1.1.0"
* @return the filter version
*/
public String getFilterVersion() {
return this.filterVersion;
}
/**
* Sets the CSW filter version.
* <br/>Default = "1.1.0"
* <br/>Modifying the filter version will not change the generation logic.
* @param version the filter version
*/
public void setFilterVersion(String version) {
this.filterVersion = Val.chkStr(version);
}
/**
* Gets the CSW result type.
* <br/>Default = "RESULTS"
* @return the result type
*/
public String getResultType() {
return this.resultType;
}
/**
* Sets the CSW result type.
* <br/>Default = "RESULTS"
* @param resultType the result type
*/
public void setResultType(String resultType) {
this.resultType = Val.chkStr(resultType);
}
/**
* Gets the OGC service type.
* <br/>Default = "CSW"
* @return the OGC service type
*/
public String getService() {
return this.service;
}
/**
* Sets the OGC service type.
* <br/>Default = "CSW"
* <br/>Modifying the OGC service type not change the generation logic.
* @param service the OGC service type
*/
public void setService(String service) {
this.service = Val.chkStr(service);
}
/**
* Gets the CSW type names to query.
* <br/>Default = "csw:Record"
* @return the type names
*/
public String getTypeNames() {
return this.typeNames;
}
/**
* Sets the CSW type names to query.
* <br/>Default = "csw:Record"
* @param typeNames the type names
*/
public void setTypeNames(String typeNames) {
this.typeNames = Val.chkStr(typeNames);
}
/**
* Gets the CSW version.
* <br/>Default = "2.0.2"
* @return the CSW version
*/
public String getVersion() {
return this.version;
}
/**
* Sets the CSW version.
* <br/>Default = "2.0.2"
* <br/>Modifying the CSW version will not change the generation logic.
* @param version the CSW version
*/
public void setVersion(String version) {
this.version = Val.chkStr(version);
}
/** methods ================================================================= */
/**
* Appends a logical clause to an XML parent element.
* @param parent the parent element to which the clause will be appended
* @param logicalClause the logical clause to append
*/
private void appendLogicalClause(Element parent, LogicalClause logicalClause) {
if ((logicalClause != null) && (logicalClause.getClauses().size() > 0)) {
Element elLogical = null;
if (logicalClause instanceof LogicalClause.LogicalAnd) {
elLogical = getDom().createElementNS(CswNamespaces.URI_OGC,"ogc:And");
} else if (logicalClause instanceof LogicalClause.LogicalOr) {
elLogical = getDom().createElementNS(CswNamespaces.URI_OGC,"ogc:Or");
} else if (logicalClause instanceof LogicalClause.LogicalNot) {
elLogical = getDom().createElementNS(CswNamespaces.URI_OGC,"ogc:Not");
}
if (elLogical != null) {
for (DiscoveryClause clause: logicalClause.getClauses()) {
if (clause == null) {
// TODO: throw Exception
} else if (clause instanceof LogicalClause) {
appendLogicalClause(elLogical,(LogicalClause)clause);
} else if (clause instanceof PropertyClause) {
appendPropertyClause(elLogical,(PropertyClause)clause);
} else if (clause instanceof SpatialClause) {
appendSpatialClause(elLogical,(SpatialClause)clause);
} else {
// TODO: throw Exception
}
}
parent.appendChild(elLogical);
}
}
}
/**
* Appends a property clause to an XML parent element.
* @param parent the parent element to which the clause will be appended
* @param propertyClause the property clause to append
*/
private void appendPropertyClause(Element parent, PropertyClause propertyClause) {
// initialize
Element elClause;
Element elLiteral;
Element elLower;
Element elUpper;
String sName = propertyClause.getTarget().getMeaning().getDcElement().getElementName();
Element elName = getDom().createElementNS(CswNamespaces.URI_OGC,"ogc:PropertyName");
elName.setTextContent(sName);
// between clause
if (propertyClause instanceof PropertyClause.PropertyIsBetween) {
PropertyClause.PropertyIsBetween between;
between = (PropertyClause.PropertyIsBetween)propertyClause;
elClause = getDom().createElementNS(CswNamespaces.URI_OGC,"ogc:PropertyIsBetween");
elLower = getDom().createElementNS(CswNamespaces.URI_OGC,"ogc:LowerBoundary");
elUpper = getDom().createElementNS(CswNamespaces.URI_OGC,"ogc:UpperBoundary");
elLower.setTextContent(between.getLowerBoundary());
elUpper.setTextContent(between.getUpperBoundary());
elClause.appendChild(elName);
elClause.appendChild(elLower);
elClause.appendChild(elUpper);
parent.appendChild(elClause);
// is null clause
} else if (propertyClause instanceof PropertyClause.PropertyIsNull) {
elClause = getDom().createElementNS(CswNamespaces.URI_OGC,"ogc:PropertyIsNull");
elClause.appendChild(elName);
parent.appendChild(elClause);
// remaining clauses
} else {
String opName = "";
if (propertyClause instanceof PropertyClause.PropertyIsEqualTo) {
opName = "PropertyIsEqualTo";
} else if (propertyClause instanceof PropertyClause.PropertyIsGreaterThan) {
opName = "PropertyIsGreaterThan";
} else if (propertyClause instanceof PropertyClause.PropertyIsGreaterThanOrEqualTo) {
opName = "PropertyIsGreaterThanOrEqualTo";
} else if (propertyClause instanceof PropertyClause.PropertyIsLessThan) {
opName = "PropertyIsLessThan";
} else if (propertyClause instanceof PropertyClause.PropertyIsLessThanOrEqualTo) {
opName = "PropertyIsLessThanOrEqualTo";
} else if (propertyClause instanceof PropertyClause.PropertyIsLike) {
opName = "PropertyIsLike";
} else if (propertyClause instanceof PropertyClause.PropertyIsNotEqualTo) {
opName = "PropertyIsNotEqualTo";
} else {
// TODO: throw Exception
//String sErr = "Unrecognized property clause type: ";
//throw new DiscoveryException(sErr+propertyClause.getClass().getName());
}
// make and append the clause
elClause = getDom().createElementNS(CswNamespaces.URI_OGC,"ogc:"+opName);
elLiteral = getDom().createElementNS(CswNamespaces.URI_OGC,"ogc:Literal");
elLiteral.setTextContent(propertyClause.getLiteral());
elClause.appendChild(elName);
elClause.appendChild(elLiteral);
parent.appendChild(elClause);
// add "like" attributes
if (propertyClause instanceof PropertyClause.PropertyIsLike) {
elClause.setAttribute("wildCard","*");
elClause.setAttribute("escape","\\");
elClause.setAttribute("singleChar","?");
}
}
}
/**
* Appends a sort by clause to an XML parent element.
* @param parent the parent element to which the clause will be appended
* @param sortables the sortables to append
*/
private void appendSortByClause(Element parent, Sortables sortables) {
if ((sortables != null) && (sortables.size() > 0)) {
Element elSortBy = getDom().createElementNS(CswNamespaces.URI_OGC,"ogc:SortBy");
for (Sortable sortable: sortables) {
Element elSort = getDom().createElementNS(CswNamespaces.URI_OGC,"ogc:SortProperty");
Element elName = getDom().createElementNS(CswNamespaces.URI_OGC,"ogc:PropertyName");
Element elOrder = getDom().createElementNS(CswNamespaces.URI_OGC,"ogc:SortOrder");
elName.setTextContent(sortable.getMeaning().getDcElement().getElementName());
elOrder.setTextContent(sortable.getDirection().name());
elSort.appendChild(elName);
elSort.appendChild(elOrder);
elSortBy.appendChild(elSort);
}
parent.appendChild(elSortBy);
}
}
/**
* Appends a spatial clause to an XML parent element.
* @param parent the parent element to which the clause will be appended
* @param spatialClause the spatial clause to append
*/
private void appendSpatialClause(Element parent, SpatialClause spatialClause) {
// check the envelope
Envelope envelope = spatialClause.getBoundingEnvelope();
if ((envelope == null) || envelope.isEmpty()) {
// TODO: throw Exception
//String sErr = "The SpatialClause.boundingEnvelope is empty.";
//throw new DiscoveryException(sErr);
}
String opName= "";
if (spatialClause instanceof SpatialClause.GeometryBBOXIntersects) {
opName = "BBOX";
} else if (spatialClause instanceof SpatialClause.GeometryContains) {
opName = "Contains";
} else if (spatialClause instanceof SpatialClause.GeometryIntersects) {
opName = "Intersects";
} else if (spatialClause instanceof SpatialClause.GeometryIsDisjointTo) {
opName = "Disjoint";
} else if (spatialClause instanceof SpatialClause.GeometryIsEqualTo) {
opName = "Equal";
} else if (spatialClause instanceof SpatialClause.GeometryIsWithin) {
opName = "Within";
} else if (spatialClause instanceof SpatialClause.GeometryOverlaps) {
opName = "Overlaps";
} else {
// TODO: throw Exception
//sErr = "Unrecognized spatial clause type: ";
//throw new DiscoveryException(sErr+spatialClause.getClass().getName());
}
// make and append the spatial clause
Element elClause = getDom().createElementNS(CswNamespaces.URI_OGC,"ogc:"+opName);
String sName = spatialClause.getTarget().getMeaning().getDcElement().getElementName();
Element elName = getDom().createElementNS(CswNamespaces.URI_OGC,"ogc:PropertyName");
elName.setTextContent(sName);
elClause.appendChild(elName);
Element elEnv = getDom().createElementNS(CswNamespaces.URI_GML,"gml:Envelope");
Element elLower = getDom().createElementNS(CswNamespaces.URI_GML,"gml:lowerCorner");
Element elUpper = getDom().createElementNS(CswNamespaces.URI_GML,"gml:upperCorner");
elLower.setTextContent(envelope.getMinX()+" "+envelope.getMinY());
elUpper.setTextContent(envelope.getMaxX()+" "+envelope.getMaxY());
elEnv.appendChild(elLower);
elEnv.appendChild(elUpper);
elClause.appendChild(elEnv);
parent.appendChild(elClause);
}
/**
* Generates a CSW GetRecords request string from a DiscoveryQuery.
* @param query the discovery query
* @return the CSW GetRecords request string
* @throws Exception if an exception occurs
*/
public String generateCswRequest(DiscoveryQuery query) throws Exception {
// make the XML document and root element
DiscoveryFilter filter = query.getFilter();
setDom(DomUtil.newDocument());
Element root = getDom().createElementNS(CswNamespaces.URI_CSW,"csw:GetRecords");
//root.setAttribute("xmlns:dc", CswNamespaces.URI_DC);
//root.setAttribute("xmlns:dct",CswNamespaces.URI_DCT);
root.setAttribute("xmlns:gml",CswNamespaces.URI_GML);
root.setAttribute("xmlns:ows",CswNamespaces.URI_OWS);
root.setAttribute("xmlns:ogc",CswNamespaces.URI_OGC);
//root.setAttribute("xmlns:dcmiBox",CswNamespaces.URI_dcmiBox);
//root.setAttribute("xmlns:xsd",CswNamespaces.URI_XSD);
getDom().appendChild(root);
// append service and version
root.setAttribute("service",getService());
root.setAttribute("version",getVersion());
// append result type, start position and max records
root.setAttribute("resultType",getResultType());
root.setAttribute("startPosition",""+filter.getStartRecord());
root.setAttribute("maxRecords",""+filter.getMaxRecords());
// append the query element
Element elQuery = getDom().createElementNS(CswNamespaces.URI_CSW,"csw:Query");
elQuery.setAttribute("typeNames",getTypeNames());
root.appendChild(elQuery);
// append the element set name
Element elSetName = getDom().createElementNS(CswNamespaces.URI_CSW,"csw:ElementSetName");
elSetName.setTextContent(getElementSetName());
elQuery.appendChild(elSetName);
// append the constraint element
Element elConstraint = getDom().createElementNS(CswNamespaces.URI_CSW,"csw:Constraint");
elConstraint.setAttribute("version",getFilterVersion());
elQuery.appendChild(elConstraint);
// append the filter element
Element elFilter = getDom().createElementNS(CswNamespaces.URI_OGC,"ogc:Filter");
elConstraint.appendChild(elFilter);
appendLogicalClause(elFilter,filter.getRootClause());
// append the sort by element
appendSortByClause(elQuery,query.getSortables());
return XmlIoUtil.domToString(getDom());
}
/**
* Generates a CSW GetRecordById request.
* @param id the record ID
* @return the CSW GetRecordById request string
* @throws Exception if an exception occurs
*/
public String generateCswByIdRequest(String id) throws Exception {
// make the XML document and root element
setDom(DomUtil.newDocument());
Element root = getDom().createElementNS(CswNamespaces.URI_CSW,"csw:GetRecordById");
root.setAttribute("xmlns:gml",CswNamespaces.URI_GML);
root.setAttribute("xmlns:ows",CswNamespaces.URI_OWS);
root.setAttribute("xmlns:ogc",CswNamespaces.URI_OGC);
getDom().appendChild(root);
// append service and version
root.setAttribute("service",getService());
root.setAttribute("version",getVersion());
// append the id element
Element elId = getDom().createElementNS(CswNamespaces.URI_CSW,"csw:Id");
elId.setTextContent(id);
root.appendChild(elId);
// append the element set name
Element elSetName = getDom().createElementNS(CswNamespaces.URI_CSW,"csw:ElementSetName");
elSetName.setTextContent(getElementSetName());
root.appendChild(elSetName);
return XmlIoUtil.domToString(getDom());
}
}