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