001    //$HeadURL: svn+ssh://developername@svn.wald.intevation.org/deegree/base/trunk/src/org/deegree/ogcwebservices/csw/discovery/GetRecordsDocument.java $
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.util.StringTools;
052    import org.deegree.framework.xml.XMLParsingException;
053    import org.deegree.framework.xml.XMLTools;
054    import org.deegree.i18n.Messages;
055    import org.deegree.model.filterencoding.AbstractFilter;
056    import org.deegree.model.filterencoding.AbstractOperation;
057    import org.deegree.model.filterencoding.ComplexFilter;
058    import org.deegree.model.filterencoding.Expression;
059    import org.deegree.model.filterencoding.Filter;
060    import org.deegree.model.filterencoding.FilterConstructionException;
061    import org.deegree.model.filterencoding.LogicalOperation;
062    import org.deegree.model.filterencoding.Operation;
063    import org.deegree.model.filterencoding.PropertyIsBetweenOperation;
064    import org.deegree.model.filterencoding.PropertyIsCOMPOperation;
065    import org.deegree.model.filterencoding.PropertyIsInstanceOfOperation;
066    import org.deegree.model.filterencoding.PropertyIsLikeOperation;
067    import org.deegree.model.filterencoding.PropertyIsNullOperation;
068    import org.deegree.model.filterencoding.PropertyName;
069    import org.deegree.model.filterencoding.SpatialOperation;
070    import org.deegree.ogcbase.CommonNamespaces;
071    import org.deegree.ogcbase.ExceptionCode;
072    import org.deegree.ogcbase.PropertyPath;
073    import org.deegree.ogcbase.PropertyPathFactory;
074    import org.deegree.ogcbase.SortProperty;
075    import org.deegree.ogcwebservices.InvalidParameterValueException;
076    import org.deegree.ogcwebservices.MissingParameterValueException;
077    import org.deegree.ogcwebservices.OGCWebServiceException;
078    import org.deegree.ogcwebservices.OperationNotSupportedException;
079    import org.deegree.ogcwebservices.csw.AbstractCSWRequestDocument;
080    import org.deegree.ogcwebservices.csw.discovery.GetRecords.RESULT_TYPE;
081    import org.w3c.dom.Element;
082    import org.w3c.dom.Node;
083    import org.w3c.dom.NodeList;
084    import org.xml.sax.SAXException;
085    
086    /**
087     * Represents an XML GetRecords document of an OGC CSW 2.0.0 and 2.0.1 compliant service.
088     *
089     * @author <a href="mailto:tfr@users.sourceforge.net">Torsten Friebe </a>
090     * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a>
091     * @author last edited by: $Author: apoth $
092     *
093     * @version $Revision: 9307 $, $Date: 2007-12-21 08:37:43 +0100 (Fr, 21 Dez 2007) $
094     */
095    public class GetRecordsDocument extends AbstractCSWRequestDocument {
096    
097        private static final long serialVersionUID = 2796229558893029054L;
098    
099        private static final ILogger LOG = LoggerFactory.getLogger( GetRecordsDocument.class );
100    
101        private static final String XML_TEMPLATE = "GetRecordsTemplate.xml";
102    
103        /**
104         * Extracts a <code>GetRecords</code> representation of this object.
105         *
106         * @param id
107         *            unique ID of the request
108         * @return GetRecords representation of this object
109         * @throws MissingParameterValueException
110         * @throws InvalidParameterValueException
111         * @throws OperationNotSupportedException
112         *             if an CqlText constrained is requested
113         * @throws OGCWebServiceException
114         *             if something else went wrong
115         */
116        public GetRecords parse( String id )
117                                throws OGCWebServiceException {
118    
119            // '<csw:GetRecords>'-element (required)
120            try {
121                Element contextNode = (Element) XMLTools.getRequiredNode( this.getRootElement(), "self::csw:GetRecords",
122                                                                          nsContext );
123                // 'service'-attribute (optional, must be CSW)
124                String service = XMLTools.getNodeAsString( contextNode, "@service", nsContext, "CSW" );
125                if ( !"CSW".equals( service ) ) {
126                    throw new OGCWebServiceException( "GetRecordsDocument",
127                                                      Messages.getMessage( "CSW_INVALID_SERVICE_PARAM" ),
128                                                      ExceptionCode.INVALIDPARAMETERVALUE );
129                }
130    
131                String defaultVersion = GetRecords.DEFAULT_VERSION;
132                boolean isEBRIM = ( contextNode.getOwnerDocument().lookupPrefix(
133                                                                                 CommonNamespaces.OASIS_EBRIMNS.toASCIIString() ) != null );
134                if ( !isEBRIM ) {
135                    isEBRIM = isEbrimDefined( contextNode );
136                }
137                LOG.logDebug( "GetRecordsDocument: For the namespaceDefinition of the ebrim catalogue, the value is: "
138                              + isEBRIM );
139                if ( isEBRIM ) {
140                    defaultVersion = "2.0.1";
141                }
142    
143                // 'version'-attribute (optional)
144                String version = XMLTools.getNodeAsString( contextNode, "@version", nsContext, defaultVersion );
145                if ( !( GetRecords.DEFAULT_VERSION.equals( version ) || "2.0.1".equals( version ) ) ) {
146                    throw new OGCWebServiceException( "GetRecordsDocument",
147                                                      Messages.getMessage( "CSW_NOT_SUPPORTED_VERSION",
148                                                                           GetRecords.DEFAULT_VERSION, "2.0.1", version ),
149                                                      ExceptionCode.INVALIDPARAMETERVALUE );
150                }
151    
152                // 'requestId'-attribute (optional)
153                String requestId = XMLTools.getNodeAsString( contextNode, "@requestId", nsContext, id );
154    
155                // 'resultType'-attribute
156                // type="csw:ResultType" use="optional" default="hits"
157                String resultTypeString = XMLTools.getNodeAsString( contextNode, "@resultType", nsContext,
158                                                                    GetRecords.RESULT_TYPE_STRING_HITS );
159                RESULT_TYPE resultType = RESULT_TYPE.RESULTS;
160                if ( GetRecords.RESULT_TYPE_STRING_HITS.equalsIgnoreCase( resultTypeString ) ) {
161                    resultType = RESULT_TYPE.HITS;
162                } else if ( GetRecords.RESULT_TYPE_STRING_RESULTS.equalsIgnoreCase( resultTypeString ) ) {
163                    resultType = RESULT_TYPE.RESULTS;
164                } else if ( GetRecords.RESULT_TYPE_STRING_VALIDATE.equalsIgnoreCase( resultTypeString ) ) {
165                    resultType = RESULT_TYPE.VALIDATE;
166                } else {
167                    throw new OGCWebServiceException( Messages.getMessage( "CSW_INVALID_RESULTTYPE", resultTypeString,
168                                                                           GetRecords.RESULT_TYPE_STRING_HITS,
169                                                                           GetRecords.RESULT_TYPE_STRING_RESULTS,
170                                                                           GetRecords.RESULT_TYPE_STRING_VALIDATE ),
171                                                      ExceptionCode.INVALIDPARAMETERVALUE );
172                }
173    
174                // 'outputFormat'-attribute
175                // type="xsd:string" use="optional" default="text/xml"
176                String outputFormat = XMLTools.getNodeAsString( contextNode, "@outputFormat", nsContext,
177                                                                GetRecords.DEFAULT_OUTPUTFORMAT );
178    
179                String defaultOutputSchema = GetRecords.DEFAULT_OUTPUTSCHEMA;
180                if ( isEBRIM ) {
181                    defaultOutputSchema = CommonNamespaces.OASIS_EBRIMNS.toASCIIString();
182                }
183                // 'outputSchema'-attribute
184                // type="xsd:anyURI" use="optional" default="OGCCORE"
185                String outputSchema = XMLTools.getNodeAsString( contextNode, "@outputSchema", nsContext,
186                                                                defaultOutputSchema );
187    
188                // 'startPosition'-attribute
189                // type="xsd:positiveInteger" use="optional" default="1"
190                int startPosition = XMLTools.getNodeAsInt( contextNode, "@startPosition", nsContext,
191                                                           GetRecords.DEFAULT_STARTPOSITION );
192                if ( startPosition < 1 ) {
193                    throw new OGCWebServiceException( Messages.getMessage( "CSW_INVALID_STARTPOSITION",
194                                                                           new Integer( startPosition ) ),
195                                                      ExceptionCode.INVALIDPARAMETERVALUE );
196                }
197    
198                // 'maxRecords'-attribute
199                // type="xsd:nonNegativeInteger" use="optional" default="10"
200                int maxRecords = XMLTools.getNodeAsInt( contextNode, "@maxRecords", nsContext,
201                                                        GetRecords.DEFAULT_MAX_RECORDS );
202    
203                // '<csw:DistributedSearch>'-element (optional)
204                Node distributedSearchElement = XMLTools.getNode( contextNode, "csw:DistributedSearch", nsContext );
205                int hopCount = GetRecords.DEFAULT_HOPCOUNT;
206                if ( distributedSearchElement != null ) {
207                    hopCount = XMLTools.getNodeAsInt( contextNode, "@hopCount", nsContext, GetRecords.DEFAULT_HOPCOUNT );
208                }
209    
210                // '<csw:ResponseHandler>'-elements (optional)
211                String rHandler = XMLTools.getNodeAsString( contextNode, "csw:ResponseHandler", nsContext, null );
212                URI responseHandler = null;
213                if ( rHandler != null ) {
214                    try {
215                        responseHandler = new URI( rHandler );
216                    } catch ( URISyntaxException e ) {
217                        throw new OGCWebServiceException( Messages.getMessage( "CSW_INVALID_RESPONSE_HANDLER", rHandler ),
218                                                          ExceptionCode.INVALIDPARAMETERVALUE );
219                    }
220                    LOG.logWarning( Messages.getMessage( "CSW_NO_REPONSE_HANDLER_IMPLEMENTATION" ) );
221    
222                }
223    
224                // '<csw:Query>'-elements (required)
225                // List nl = XMLTools.getRequiredNodes( contextNode, "csw:Query", nsContext );
226                Element queryNode = (Element) XMLTools.getRequiredNode( contextNode, "csw:Query", nsContext );
227    
228                Map<String, QualifiedName> declaredVariables = new HashMap<String, QualifiedName>();
229                List<QualifiedName> queryTypeNames = new ArrayList<QualifiedName>();
230    
231                // 'typeName'-attribute use="required"
232                String tNames = XMLTools.getRequiredNodeAsString( queryNode, "@typeNames", nsContext );
233                String[] simpleTypeNames = tNames.split( " " );
234                // only bind the prefixes to namespaces if the version is 2.0.0
235                boolean bindTypeNamesToNS = !GetRecords.DEFAULT_VERSION.equals( version );
236                // Find any variables
237                for ( String typeName : simpleTypeNames ) {
238                    findVariablesInTypeName( typeName, queryNode, queryTypeNames, declaredVariables, bindTypeNamesToNS );
239                }
240    
241                // '<csw:ElementSetName>'-element (optional)
242                Element elementSetNameElement = (Element) XMLTools.getNode( queryNode, "csw:ElementSetName", nsContext );
243                String elementSetName = null;
244                List<QualifiedName> elementSetNameTypeNames = null;
245                Map<String, QualifiedName> elementSetNameVariables = null;
246                List<PropertyPath> elementNames = new ArrayList<PropertyPath>();
247                // choice construct
248                if ( elementSetNameElement != null ) {
249                    // must contain one of the values 'brief', 'summary' or
250                    // 'full'
251                    elementSetName = XMLTools.getRequiredNodeAsString( elementSetNameElement, "text()", nsContext,
252                                                                       new String[] { "brief", "summary", "full" } );
253                    tNames = elementSetNameElement.getAttribute( "typeNames" );
254                    if ( tNames != null ) {
255                        String[] esnTypeNames = tNames.split( " " );
256                        elementSetNameVariables = new HashMap<String, QualifiedName>();
257                        elementSetNameTypeNames = new ArrayList<QualifiedName>();
258                        for ( String tn : esnTypeNames ) {
259                            if ( tn.trim().startsWith( "$" ) ) {
260                                String tmpVar = tn.trim().substring( 1 );
261                                if ( !declaredVariables.containsKey( tmpVar ) ) {
262                                    throw new OGCWebServiceException(
263                                                                      Messages.getMessage(
264                                                                                           "CSW_ELEMENT_SET_NAME_TYPENAME_ALIAS",
265                                                                                           tmpVar ),
266                                                                      ExceptionCode.INVALIDPARAMETERVALUE );
267                                }
268                                elementSetNameVariables.put( tmpVar, declaredVariables.get( tmpVar ) );
269                            } else {
270                                QualifiedName qName = parseQNameFromString( tn.trim(), elementSetNameElement,
271                                                                            bindTypeNamesToNS );
272                                elementSetNameTypeNames.add( qName );
273                            }
274                        }
275                    }
276    
277                } else {
278                    // '<csw:ElementName>'-element (required, if no
279                    // '<csw:ElementSetName>' is given)
280                    List<Node> elementNameList = XMLTools.getNodes( queryNode, "csw:ElementName", nsContext );
281                    if ( elementNameList.size() == 0 ) {
282                        throw new XMLParsingException( Messages.getMessage( "CSW_MISSING_QUERY_ELEMENT(SET)NAME" ) );
283                    }
284                    for ( Node n : elementNameList ) {
285                        QualifiedName elementName = XMLTools.getNodeAsQualifiedName( n, "text()", nsContext, null );
286                        if ( elementName != null ) {
287                            elementNames.add( PropertyPathFactory.createPropertyPath( elementName ) );
288                        }
289                    }
290    
291                }
292    
293                // '<csw:Constraint>'-element (optional)
294                Element constraintElement = (Element) XMLTools.getNode( queryNode, "csw:Constraint", nsContext );
295                Filter constraint = null;
296                if ( constraintElement != null ) {
297                    String ver = XMLTools.getRequiredNodeAsString( constraintElement, "@version", nsContext );
298                    if ( !"1.0.0".equals( ver ) && !"1.1.0".equals( ver ) ) {
299                        throw new OGCWebServiceException( Messages.getMessage( "CSW_INVALID_CONSTRAINT_VERSION", ver ),
300                                                          ExceptionCode.INVALIDPARAMETERVALUE );
301                    }
302                    Node filterElement = XMLTools.getNode( constraintElement, "ogc:Filter", nsContext );
303                    if ( filterElement != null ) {
304                        try {
305                            constraint = AbstractFilter.buildFromDOM( (Element) filterElement, false );
306                        } catch ( FilterConstructionException fce ) {
307                            throw new OGCWebServiceException( Messages.getMessage( "CSW_INVALID_CONSTRAINT_CONTENT",
308                                                                                   fce.getMessage() ),
309                                                              ExceptionCode.INVALIDPARAMETERVALUE );
310                        }
311                    } else {
312                        String cqlText = XMLTools.getNodeAsString( constraintElement, "csw:CqlText", nsContext, null );
313                        if ( cqlText == null ) {
314                            throw new OGCWebServiceException( Messages.getMessage( "CSW_CQL_NOR_FILTER" ),
315                                                              ExceptionCode.INVALIDPARAMETERVALUE );
316                        }
317    
318                        throw new OGCWebServiceException( Messages.getMessage( "CSW_NO_CQL_IMPLEMENTATION" ),
319                                                          ExceptionCode.OPERATIONNOTSUPPORTED );
320                    }
321                }
322                // find undeclared referenced variables used in the filter element.
323                if ( constraint instanceof ComplexFilter ) {
324                    checkReferencedVariables( (ComplexFilter) constraint, declaredVariables );
325                }
326    
327                // '<ogc:SortBy>'-element (optional)
328                Node sortByElement = XMLTools.getNode( queryNode, "ogc:SortBy", nsContext );
329                SortProperty[] sortProperties = null;
330                if ( sortByElement != null ) {
331                    List<Node> sortPropertyList = XMLTools.getNodes( sortByElement, "ogc:SortProperty", nsContext );
332                    if ( sortPropertyList.size() == 0 ) {
333                        throw new OGCWebServiceException( Messages.getMessage( "CSW_NO_SORTPROPERTY_LIST" ),
334                                                          ExceptionCode.INVALIDPARAMETERVALUE );
335    
336                    }
337                    sortProperties = new SortProperty[sortPropertyList.size()];
338                    for ( int j = 0; j < sortPropertyList.size(); j++ ) {
339                        sortProperties[j] = SortProperty.create( (Element) sortPropertyList.get( j ) );
340                    }
341                }
342    
343                Query query = new Query( elementSetName, elementSetNameTypeNames, elementSetNameVariables, elementNames,
344                                         constraint, sortProperties, queryTypeNames, declaredVariables );
345    
346                // in the future the vendorSpecificParameters
347                Map<String, String> vendorSpecificParameters = parseDRMParams( this.getRootElement() );
348                return new GetRecords( requestId, version, vendorSpecificParameters, null, resultType, outputFormat,
349                                       outputSchema, startPosition, maxRecords, hopCount, responseHandler, query );
350            } catch ( XMLParsingException xmlpe ) {
351                LOG.logError( "CatalogGetRecords", xmlpe );
352                throw new OGCWebServiceException( xmlpe.getMessage(), ExceptionCode.INVALIDPARAMETERVALUE );
353            } catch ( URISyntaxException urise ) {
354                LOG.logError( "CatalogGetRecords", urise );
355                throw new OGCWebServiceException( urise.getMessage(), ExceptionCode.INVALIDPARAMETERVALUE );
356            }
357        }
358    
359        /**
360         * @param contextNode
361         * @return true if the namespace "urn:oasis:names:tc:ebxml- regrep:xsd:rim:3.0" was found in one
362         *         of the nodes of the dom-tree.
363         */
364        protected boolean isEbrimDefined( Node contextNode ) {
365    
366            boolean isEbRim = contextNode.lookupPrefix( CommonNamespaces.OASIS_EBRIMNS.toASCIIString() ) != null;
367            if ( !isEbRim ) {
368                NodeList nl = contextNode.getChildNodes();
369                for ( int i = 0; i < nl.getLength(); ++i ) {
370                    isEbRim = isEbrimDefined( nl.item( i ) );
371                    if ( isEbRim ) {
372                        return true;
373                    }
374                }
375            }
376            return isEbRim;
377        }
378    
379        /**
380         * Helper method to find any declared variables in given Query/@typeNames
381         *
382         * @param typeName
383         *            the type name to test
384         * @param queryNode
385         *            the querynode (used to find a given prefix)
386         * @param typeNames
387         *            a list to save the typeName (as QualifiedNames) in
388         * @param variables
389         *            a Map containing the vars/QualifiedName mappings
390         * @param bindTypeNameToNS
391         *            if the namespaces should be bounded to the typeNames
392         * @throws URISyntaxException
393         *             if the prefix is not bound to a namespace
394         * @throws OGCWebServiceException
395         *             if a variable name is unambiguous
396         */
397        public void findVariablesInTypeName( String typeName, Node queryNode, List<QualifiedName> typeNames,
398                                             Map<String, QualifiedName> variables, boolean bindTypeNameToNS )
399                                throws OGCWebServiceException, URISyntaxException {
400            LOG.logDebug( "testing for variables in typeName: " + typeName );
401            int variableIndex = typeName.lastIndexOf( '=' );
402            String tmpTypeName = typeName;
403            if ( variableIndex != -1 ) {
404                // find the typeNames
405                tmpTypeName = typeName.substring( 0, variableIndex ).trim();
406                LOG.logDebug( "typeName contains variables" );
407            }
408    
409            // creating the qualified name
410            QualifiedName qName = parseQNameFromString( tmpTypeName, queryNode, bindTypeNameToNS );
411            typeNames.add( qName );
412            if ( variableIndex != -1 ) {
413                if ( ( variableIndex + 1 ) < typeName.length() ) {
414                    // find the variables which should be referenced with the $-sign
415                    String allVars = typeName.substring( variableIndex + 1 );
416                    String[] vars = allVars.split( "," );
417                    for ( String var : vars ) {
418                        LOG.logDebug( "found var: " + var );
419                        if ( variables.put( var.trim(), qName ) != null ) {
420                            String out = Messages.getMessage( "CSW_AMBIGUOUS_VARIABLE_DEF", var.trim() );
421                            throw new OGCWebServiceException( "GetRecords", out, ExceptionCode.INVALIDPARAMETERVALUE );
422                        }
423                    }
424                }
425            }
426        }
427    
428        /**
429         * @param typeName
430         *            to be transformed to a QName
431         * @param queryNode
432         *            needed to get the namespace
433         * @param bindTypeNameToNS
434         *            if true the namespace will be bound to the qualified name
435         * @return a QualifiedName representing the typeName
436         * @throws URISyntaxException
437         */
438        public QualifiedName parseQNameFromString( String typeName, Node queryNode, boolean bindTypeNameToNS )
439                                throws URISyntaxException {
440            int prefixIndex = typeName.indexOf( ':' );
441            String preFix = null;
442            URI nameSpace = null;
443            String localName = typeName;
444            if ( prefixIndex != -1 ) {
445                preFix = typeName.substring( 0, prefixIndex ).trim();
446                if ( bindTypeNameToNS ) {
447                    LOG.logDebug( "Trying to find namespace binding for the prefix: " + preFix + " on node queryNode: "
448                                  + queryNode.getNodeName() );
449                    nameSpace = XMLTools.getNamespaceForPrefix( preFix, queryNode );
450                } else {
451                    LOG.logDebug( "Not binding namespaces for the prefix: " + preFix + " on node queryNode: "
452                                  + queryNode.getNodeName() + " because the version of the GetRecordsRequest is not 2.0.2" );
453                }
454                // for version 2.0.0 no namespace checkin is required following versions should check if
455                // the returned namespace is null.
456                if ( ( prefixIndex + 1 ) < typeName.length() ) {
457                    localName = typeName.substring( prefixIndex + 1 ).trim();
458                } else {
459                    localName = typeName.substring( prefixIndex ).trim();
460                }
461            }
462            LOG.logDebug( "found prefix: " + preFix );
463            LOG.logDebug( "found localName: " + localName );
464            LOG.logDebug( "found namespace: " + nameSpace );
465            return new QualifiedName( preFix, localName, nameSpace );
466        }
467    
468        /**
469         * Iterates over the Operations of a complexfilter to find if non declared variables are used.
470         *
471         * @param constraint
472         * @param variables
473         * @throws OGCWebServiceException
474         */
475        protected void checkReferencedVariables( ComplexFilter constraint, Map<String, QualifiedName> variables )
476                                throws OGCWebServiceException {
477            AbstractOperation topOperation = (AbstractOperation) constraint.getOperation();
478            if ( topOperation instanceof LogicalOperation ) {
479                List<Operation> operations = ( (LogicalOperation) topOperation ).getArguments();
480                for ( Operation op : operations ) {
481                    findNonDeclaredVariables( (AbstractOperation) op, variables );
482                }
483            } else {
484                findNonDeclaredVariables( topOperation, variables );
485            }
486        }
487    
488        /**
489         * (Recursively) finds a reference to a non declared variable in the propertyname of the given
490         * operation.
491         *
492         * @param operation
493         *            to be checked
494         * @param variables
495         *            which were declared
496         * @throws OGCWebServiceException
497         *             if such a reference is found
498         */
499        protected void findNonDeclaredVariables( AbstractOperation operation, Map<String, QualifiedName> variables )
500                                throws OGCWebServiceException {
501            if ( operation instanceof LogicalOperation ) {
502                List<Operation> operations = ( (LogicalOperation) operation ).getArguments();
503                for ( Operation op : operations ) {
504                    findNonDeclaredVariables( (AbstractOperation) op, variables );
505                }
506            } else if ( operation instanceof SpatialOperation ) {
507                findNonDeclaredVariables( ( (SpatialOperation) operation ).getPropertyName(), variables );
508            } else {
509                if ( operation instanceof PropertyIsBetweenOperation ) {
510                    findNonDeclaredVariables( ( (PropertyIsBetweenOperation) operation ).getPropertyName(), variables );
511                } else if ( operation instanceof PropertyIsCOMPOperation ) {
512                    Expression expr = ( (PropertyIsCOMPOperation) operation ).getFirstExpression();
513                    if ( expr instanceof PropertyName ) {
514                        findNonDeclaredVariables( ( (PropertyName) expr ), variables );
515                    }
516                    expr = ( (PropertyIsCOMPOperation) operation ).getSecondExpression();
517                    if ( expr instanceof PropertyName ) {
518                        findNonDeclaredVariables( ( (PropertyName) expr ), variables );
519                    }
520                } else if ( operation instanceof PropertyIsInstanceOfOperation ) {
521                    findNonDeclaredVariables( ( (PropertyIsInstanceOfOperation) operation ).getPropertyName(), variables );
522                } else if ( operation instanceof PropertyIsLikeOperation ) {
523                    findNonDeclaredVariables( ( (PropertyIsLikeOperation) operation ).getPropertyName(), variables );
524                } else if ( operation instanceof PropertyIsNullOperation ) {
525                    findNonDeclaredVariables( ( (PropertyIsNullOperation) operation ).getPropertyName(), variables );
526                }
527            }
528        }
529    
530        /**
531         * Parse the string representation of the the propertyname to find a variable reference to a non
532         * declared Variable.
533         *
534         * @param propName
535         *            to check
536         * @param variables
537         *            which were declared
538         * @throws InvalidParameterValueException
539         *             if such a reference was found.
540         */
541        protected void findNonDeclaredVariables( PropertyName propName, Map<String, QualifiedName> variables )
542                                throws OGCWebServiceException {
543            String propertyPath = propName.toString();
544            String[] foundVariables = StringTools.extractStrings( propertyPath, "$", "/" );
545            if ( foundVariables != null && foundVariables.length > 0 ) {
546                LOG.logDebug( "found following variables in properertyName: " + propName.toString() );
547                for ( String var : foundVariables ) {
548                    LOG.logDebug( "variable: " + var );
549                    if ( !variables.containsKey( var ) ) {
550                        throw new OGCWebServiceException( Messages.getMessage( "CSW_VARIABLE_NOT_DEFINED", var ),
551                                                          ExceptionCode.INVALIDPARAMETERVALUE );
552                    }
553                }
554            }
555        }
556    
557        /*
558         * (non-Javadoc)
559         *
560         * @see org.deegree.framework.xml.XMLFragment#createEmptyDocument()
561         */
562        void createEmptyDocument()
563                                throws IOException, SAXException {
564            URL url = GetRecordsDocument.class.getResource( XML_TEMPLATE );
565            if ( url == null ) {
566                throw new IOException( "The resource '" + XML_TEMPLATE + " could not be found." );
567            }
568            load( url );
569        }
570    }