001    //$HeadURL$
002    /*----------------------------------------------------------------------------
003     This file is part of deegree, http://deegree.org/
004     Copyright (C) 2001-2009 by:
005       Department of Geography, University of Bonn
006     and
007       lat/lon GmbH
008    
009     This library is free software; you can redistribute it and/or modify it under
010     the terms of the GNU Lesser General Public License as published by the Free
011     Software Foundation; either version 2.1 of the License, or (at your option)
012     any later version.
013     This library is distributed in the hope that it will be useful, but WITHOUT
014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016     details.
017     You should have received a copy of the GNU Lesser General Public License
018     along with this library; if not, write to the Free Software Foundation, Inc.,
019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020    
021     Contact information:
022    
023     lat/lon GmbH
024     Aennchenstr. 19, 53177 Bonn
025     Germany
026     http://lat-lon.de/
027    
028     Department of Geography, University of Bonn
029     Prof. Dr. Klaus Greve
030     Postfach 1147, 53001 Bonn
031     Germany
032     http://www.geographie.uni-bonn.de/deegree/
033    
034     e-mail: info@deegree.org
035    ----------------------------------------------------------------------------*/
036    
037    package org.deegree.ogcwebservices.csw.discovery;
038    
039    import java.io.IOException;
040    import java.net.URI;
041    import java.net.URISyntaxException;
042    import java.net.URL;
043    import java.util.ArrayList;
044    import java.util.HashMap;
045    import java.util.List;
046    import java.util.Map;
047    
048    import org.deegree.datatypes.QualifiedName;
049    import org.deegree.framework.log.ILogger;
050    import org.deegree.framework.log.LoggerFactory;
051    import org.deegree.framework.xml.XMLParsingException;
052    import org.deegree.framework.xml.XMLTools;
053    import org.deegree.i18n.Messages;
054    import org.deegree.model.filterencoding.AbstractFilter;
055    import org.deegree.model.filterencoding.ComplexFilter;
056    import org.deegree.model.filterencoding.Filter;
057    import org.deegree.model.filterencoding.FilterConstructionException;
058    import org.deegree.ogcbase.CommonNamespaces;
059    import org.deegree.ogcbase.ExceptionCode;
060    import org.deegree.ogcbase.PropertyPath;
061    import org.deegree.ogcbase.PropertyPathFactory;
062    import org.deegree.ogcbase.SortProperty;
063    import org.deegree.ogcwebservices.InvalidParameterValueException;
064    import org.deegree.ogcwebservices.MissingParameterValueException;
065    import org.deegree.ogcwebservices.OGCWebServiceException;
066    import org.deegree.ogcwebservices.OperationNotSupportedException;
067    import org.deegree.ogcwebservices.csw.discovery.GetRecords.RESULT_TYPE;
068    import org.w3c.dom.Element;
069    import org.w3c.dom.Node;
070    import org.xml.sax.SAXException;
071    
072    /**
073     *
074     *
075     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
076     * @author last edited by: $Author: poth $
077     *
078     * @version $Revision: 6251 $, $Date: 2007-03-19 16:59:28 +0100 (Mo, 19 Mrz 2007) $
079     */
080    public class GetRecordsDocument_2_0_2 extends GetRecordsDocument {
081    
082        private static final long serialVersionUID = -4987866178075594539L;
083    
084        private static final ILogger LOG = LoggerFactory.getLogger( GetRecordsDocument_2_0_2.class );
085    
086        private static final String XML_TEMPLATE = "GetRecords2.0.2Template.xml";
087    
088        /**
089         * Extracts a <code>GetRecords</code> representation of this object.
090         *
091         * @param id
092         *            unique ID of the request
093         * @return GetRecords representation of this object
094         * @throws MissingParameterValueException
095         * @throws InvalidParameterValueException
096         * @throws OperationNotSupportedException
097         *             if an CqlText constrained is requested
098         * @throws OGCWebServiceException
099         *             if something else went wrong
100         */
101        @Override
102        public GetRecords parse( String id )
103                                throws OGCWebServiceException {
104    
105            // '<csw202:GetRecords>'-element (required)
106            try {
107                Element contextNode = (Element) XMLTools.getRequiredNode( this.getRootElement(), "self::csw202:GetRecords",
108                                                                          nsContext );
109                // 'service'-attribute (mandatory for 2.0.2, must be CSW)
110                String service = XMLTools.getRequiredNodeAsString( contextNode, "@service", nsContext );
111                if ( !"CSW".equals( service ) ) {
112                    throw new OGCWebServiceException( "GetRecordsDocument_2_0_2",
113                                                      Messages.getMessage( "CSW_INVALID_SERVICE_PARAM" ),
114                                                      ExceptionCode.INVALIDPARAMETERVALUE );
115                }
116    
117                boolean isEBRIM = ( contextNode.getOwnerDocument().lookupPrefix(
118                                                                                 CommonNamespaces.OASIS_EBRIMNS.toASCIIString() ) != null );
119    
120                // 'version'-attribute (mandatory for 2.0.2)
121                String version = XMLTools.getRequiredNodeAsString( contextNode, "@version", nsContext );
122                if ( !"2.0.2".equals( version ) ) {
123                    throw new OGCWebServiceException( "GetRecordsDocument_2_0_2",
124                                                      Messages.getMessage( "CSW_NOT_SUPPORTED_VERSION",
125                                                                           GetRecords.DEFAULT_VERSION, "2.0.2", version ),
126                                                      ExceptionCode.INVALIDPARAMETERVALUE );
127                }
128    
129                // 'requestId'-attribute (optional)
130                String requestId = XMLTools.getNodeAsString( contextNode, "@requestId", nsContext, id );
131    
132                // 'resultType'-attribute
133                // type="csw202:ResultType" use="optional" default="hits"
134                String resultTypeString = XMLTools.getNodeAsString( contextNode, "@resultType", nsContext,
135                                                                    GetRecords.RESULT_TYPE_STRING_HITS );
136                RESULT_TYPE resultType = RESULT_TYPE.RESULTS;
137                if ( GetRecords.RESULT_TYPE_STRING_HITS.equalsIgnoreCase( resultTypeString ) ) {
138                    resultType = RESULT_TYPE.HITS;
139                } else if ( GetRecords.RESULT_TYPE_STRING_RESULTS.equalsIgnoreCase( resultTypeString ) ) {
140                    resultType = RESULT_TYPE.RESULTS;
141                } else if ( GetRecords.RESULT_TYPE_STRING_VALIDATE.equalsIgnoreCase( resultTypeString ) ) {
142                    resultType = RESULT_TYPE.VALIDATE;
143                } else {
144                    throw new OGCWebServiceException( Messages.getMessage( "CSW_INVALID_RESULTTYPE", resultTypeString,
145                                                                           GetRecords.RESULT_TYPE_STRING_HITS,
146                                                                           GetRecords.RESULT_TYPE_STRING_RESULTS,
147                                                                           GetRecords.RESULT_TYPE_STRING_VALIDATE ),
148                                                      ExceptionCode.INVALIDPARAMETERVALUE );
149                }
150    
151                // 'outputFormat'-attribute
152                // type="xsd:string" use="optional" default="text/xml"
153                String outputFormat = XMLTools.getNodeAsString( contextNode, "@outputFormat", nsContext,
154                                                                GetRecords.DEFAULT_OUTPUTFORMAT );
155    
156                String defaultOutputSchema = GetRecords.DEFAULT_OUTPUTSCHEMA_202;
157                if ( isEBRIM ) {
158                    defaultOutputSchema = CommonNamespaces.OASIS_EBRIMNS.toASCIIString();
159                }
160                // 'outputSchema'-attribute
161                // type="xsd:anyURI" use="optional" default="OGCCORE"
162                String outputSchema = XMLTools.getNodeAsString( contextNode, "@outputSchema", nsContext,
163                                                                defaultOutputSchema );
164    
165                // 'startPosition'-attribute
166                // type="xsd:positiveInteger" use="optional" default="1"
167                int startPosition = XMLTools.getNodeAsInt( contextNode, "@startPosition", nsContext,
168                                                           GetRecords.DEFAULT_STARTPOSITION );
169                if ( startPosition < 1 ) {
170                    throw new OGCWebServiceException( Messages.getMessage( "CSW_INVALID_STARTPOSITION",
171                                                                           new Integer( startPosition ) ),
172                                                      ExceptionCode.INVALIDPARAMETERVALUE );
173                }
174    
175                // 'maxRecords'-attribute
176                // type="xsd:nonNegativeInteger" use="optional" default="10"
177                int maxRecords = XMLTools.getNodeAsInt( contextNode, "@maxRecords", nsContext,
178                                                        GetRecords.DEFAULT_MAX_RECORDS );
179    
180                // '<csw202:DistributedSearch>'-element (optional)
181                Node distributedSearchElement = XMLTools.getNode( contextNode, "csw202:DistributedSearch", nsContext );
182                int hopCount = GetRecords.DEFAULT_HOPCOUNT;
183                if ( distributedSearchElement != null ) {
184                    hopCount = XMLTools.getNodeAsInt( contextNode, "@hopCount", nsContext, GetRecords.DEFAULT_HOPCOUNT );
185                }
186    
187                // '<csw202:ResponseHandler>'-elements (optional)
188                String rHandler = XMLTools.getNodeAsString( contextNode, "csw202:ResponseHandler", nsContext, null );
189                URI responseHandler = null;
190                if ( rHandler != null ) {
191                    try {
192                        responseHandler = new URI( rHandler );
193                    } catch ( URISyntaxException e ) {
194                        throw new OGCWebServiceException( Messages.getMessage( "CSW_INVALID_RESPONSE_HANDLER", rHandler ),
195                                                          ExceptionCode.INVALIDPARAMETERVALUE );
196                    }
197                    LOG.logWarning( Messages.getMessage( "CSW_NO_REPONSE_HANDLER_IMPLEMENTATION" ) );
198    
199                }
200    
201                // '<csw202:Query>'-elements (required)
202                // List nl = XMLTools.getRequiredNodes( contextNode, "csw202:Query", nsContext );
203                Element queryNode = (Element) XMLTools.getRequiredNode( contextNode, "csw202:Query", nsContext );
204    
205                Map<String, QualifiedName> declaredVariables = new HashMap<String, QualifiedName>();
206                List<QualifiedName> queryTypeNames = new ArrayList<QualifiedName>();
207    
208                // 'typeName'-attribute use="required"
209                String tNames = XMLTools.getRequiredNodeAsString( queryNode, "@typeNames", nsContext );
210                String[] simpleTypeNames = tNames.split( " " );
211                // only bind the prefixes to namespaces if the version is 2.0.0
212                boolean bindTypeNamesToNS = !GetRecords.DEFAULT_VERSION.equals( version );
213                // Find any variables
214                for ( String typeName : simpleTypeNames ) {
215                    findVariablesInTypeName( typeName, queryNode, queryTypeNames, declaredVariables, bindTypeNamesToNS );
216                }
217    
218                // '<csw202:ElementSetName>'-element (optional)
219                Element elementSetNameElement = (Element) XMLTools.getNode( queryNode, "csw202:ElementSetName", nsContext );
220                String elementSetName = null;
221                List<QualifiedName> elementSetNameTypeNames = null;
222                Map<String, QualifiedName> elementSetNameVariables = null;
223                List<PropertyPath> elementNames = new ArrayList<PropertyPath>();
224                // choice construct
225                if ( elementSetNameElement != null ) {
226                    // must contain one of the values 'brief', 'summary' or
227                    // 'full'
228                    elementSetName = XMLTools.getRequiredNodeAsString( elementSetNameElement, "text()", nsContext,
229                                                                       new String[] { "brief", "summary", "full" } );
230                    tNames = elementSetNameElement.getAttribute( "typeNames" );
231                    if ( tNames != null ) {
232                        String[] esnTypeNames = tNames.split( " " );
233                        elementSetNameVariables = new HashMap<String, QualifiedName>();
234                        elementSetNameTypeNames = new ArrayList<QualifiedName>();
235                        for ( String tn : esnTypeNames ) {
236                            if ( tn.trim().startsWith( "$" ) ) {
237                                String tmpVar = tn.trim().substring( 1 );
238                                if ( !declaredVariables.containsKey( tmpVar ) ) {
239                                    String msg = Messages.getMessage( "CSW_ELEMENT_SET_NAME_TYPENAME_ALIAS", tmpVar );
240                                    throw new OGCWebServiceException( msg, ExceptionCode.INVALIDPARAMETERVALUE );
241                                }
242                                elementSetNameVariables.put( tmpVar, declaredVariables.get( tmpVar ) );
243                            } else {
244                                QualifiedName qName = parseQNameFromString( tn.trim(), elementSetNameElement,
245                                                                            bindTypeNamesToNS );
246                                elementSetNameTypeNames.add( qName );
247                            }
248                        }
249                    }
250    
251                } else {
252                    // '<csw202:ElementName>'-element (required, if no
253                    // '<csw202:ElementSetName>' is given)
254                    List<Node> elementNameList = XMLTools.getNodes( queryNode, "csw202:ElementName", nsContext );
255                    if ( elementNameList.size() == 0 ) {
256                        throw new XMLParsingException( Messages.getMessage( "CSW_MISSING_QUERY_ELEMENT(SET)NAME" ) );
257                    }
258                    for ( Node n : elementNameList ) {
259                        QualifiedName elementName = XMLTools.getNodeAsQualifiedName( n, "text()", nsContext, null );
260                        if ( elementName != null ) {
261                            elementNames.add( PropertyPathFactory.createPropertyPath( elementName ) );
262                        }
263                    }
264    
265                }
266    
267                // '<csw202:Constraint>'-element (optional)
268                Element constraintElement = (Element) XMLTools.getNode( queryNode, "csw202:Constraint", nsContext );
269                Filter constraint = null;
270                if ( constraintElement != null ) {
271                    String ver = XMLTools.getRequiredNodeAsString( constraintElement, "@version", nsContext );
272                    if ( !"1.0.0".equals( ver ) && !"1.1.0".equals( ver ) ) {
273                        throw new OGCWebServiceException( Messages.getMessage( "CSW_INVALID_CONSTRAINT_VERSION", ver ),
274                                                          ExceptionCode.INVALIDPARAMETERVALUE );
275                    }
276                    Node filterElement = XMLTools.getNode( constraintElement, "ogc:Filter", nsContext );
277                    if ( filterElement != null ) {
278                        try {
279                            constraint = AbstractFilter.buildFromDOM( (Element) filterElement, false );
280                        } catch ( FilterConstructionException fce ) {
281                            throw new OGCWebServiceException( Messages.getMessage( "CSW_INVALID_CONSTRAINT_CONTENT",
282                                                                                   fce.getMessage() ),
283                                                              ExceptionCode.INVALIDPARAMETERVALUE );
284                        }
285                    } else {
286                        String cqlText = XMLTools.getNodeAsString( constraintElement, "csw202:CqlText", nsContext, null );
287                        if ( cqlText == null ) {
288                            throw new OGCWebServiceException( Messages.getMessage( "CSW_CQL_NOR_FILTER" ),
289                                                              ExceptionCode.INVALIDPARAMETERVALUE );
290                        }
291    
292                        throw new OGCWebServiceException( Messages.getMessage( "CSW_NO_CQL_IMPLEMENTATION" ),
293                                                          ExceptionCode.OPERATIONNOTSUPPORTED );
294                    }
295                }
296                // find undeclared referenced variables used in the filter element.
297                if ( constraint instanceof ComplexFilter ) {
298                    checkReferencedVariables( (ComplexFilter) constraint, declaredVariables );
299                }
300    
301                // '<ogc:SortBy>'-element (optional)
302                Node sortByElement = XMLTools.getNode( queryNode, "ogc:SortBy", nsContext );
303                SortProperty[] sortProperties = null;
304                if ( sortByElement != null ) {
305                    List<Node> sortPropertyList = XMLTools.getNodes( sortByElement, "ogc:SortProperty", nsContext );
306                    if ( sortPropertyList.size() == 0 ) {
307                        throw new OGCWebServiceException( Messages.getMessage( "CSW_NO_SORTPROPERTY_LIST" ),
308                                                          ExceptionCode.INVALIDPARAMETERVALUE );
309    
310                    }
311                    sortProperties = new SortProperty[sortPropertyList.size()];
312                    for ( int j = 0; j < sortPropertyList.size(); j++ ) {
313                        sortProperties[j] = SortProperty.create( (Element) sortPropertyList.get( j ) );
314                    }
315                }
316    
317                Query query = new Query( elementSetName, elementSetNameTypeNames, elementSetNameVariables, elementNames,
318                                         constraint, sortProperties, queryTypeNames, declaredVariables );
319    
320                // in the future the vendorSpecificParameters
321                Map<String, String> vendorSpecificParameters = parseDRMParams( this.getRootElement() );
322                return new GetRecords( requestId, version, vendorSpecificParameters, null, resultType, outputFormat,
323                                       outputSchema, startPosition, maxRecords, hopCount, responseHandler, query );
324            } catch ( XMLParsingException xmlpe ) {
325                LOG.logError( "CatalogGetRecords", xmlpe );
326                throw new OGCWebServiceException( xmlpe.getMessage(), ExceptionCode.MISSINGPARAMETERVALUE );
327            } catch ( URISyntaxException urise ) {
328                LOG.logError( "CatalogGetRecords", urise );
329                throw new OGCWebServiceException( urise.getMessage(), ExceptionCode.MISSINGPARAMETERVALUE );
330            }
331        }
332    
333        @Override
334        /*
335         * (non-Javadoc)
336         *
337         * @see org.deegree.framework.xml.XMLFragment#createEmptyDocument()
338         */
339        void createEmptyDocument()
340                                throws IOException, SAXException {
341            URL url = GetRecordsDocument.class.getResource( XML_TEMPLATE );
342            if ( url == null ) {
343                throw new IOException( "The resource '" + XML_TEMPLATE + " could not be found." );
344            }
345            load( url );
346        }
347    
348    }