<commit_before>/*
 *  eXist Open Source Native XML Database
 *  Copyright (C) 2001-06 <NAME>
 *  <EMAIL>
 *  http://exist.sourceforge.net
 *  
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *  
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *  
 *  $Id$
 */
package org.exist.xquery;

import java.util.Iterator;
import java.util.List;

import org.exist.Namespaces;
import org.exist.dom.QName;
import org.exist.xquery.functions.ExtNear;
import org.exist.xquery.functions.ExtPhrase;
import org.exist.xquery.parser.XQueryAST;
import org.exist.xquery.value.StringValue;
import org.exist.xquery.value.Type;

public class FunctionFactory {

	public static final String ENABLE_JAVA_BINDING_ATTRIBUTE = "enable-java-binding";
	public static final String PROPERTY_ENABLE_JAVA_BINDING = "xquery.enable-java-binding";
	public static final String DISABLE_DEPRECATED_FUNCTIONS_ATTRIBUTE = "disable-deprecated-functions";
	public static final String PROPERTY_DISABLE_DEPRECATED_FUNCTIONS = "xquery.disable-deprecated-functions";	
	public static final boolean DISABLE_DEPRECATED_FUNCTIONS_BY_DEFAULT = false;
	
	/**
	 * Create a function call. 
	 * 
	 * This method handles all calls to built-in or user-defined
	 * functions. It also deals with constructor functions and
	 * optimizes some function calls like starts-with, ends-with or
	 * contains. 
	 */
	public static Expression createFunction(XQueryContext context, XQueryAST ast, PathExpr parent, List params)
		throws XPathException {
		QName qname = null;
		try {
			qname = QName.parse(context, ast.getText(), context.getDefaultFunctionNamespace());
		} catch(XPathException e) {
            e.setLocation(ast.getLine(), ast.getColumn());
			throw e;
		}
		String local = qname.getLocalName();
		String uri = qname.getNamespaceURI();
		Expression step = null;
		if(uri.equals(Function.BUILTIN_FUNCTION_NS)) {
			//TODO : move to text:near()
			// near(node-set, string)
			if (local.equals("near")) {
				if (params.size() < 2)
					throw new XPathException(ast.getLine(), ast.getColumn(), "XPST0017: Function near() requires two arguments");
				PathExpr p1 = (PathExpr) params.get(1);
				if (p1.getLength() == 0)
					throw new XPathException(ast.getLine(), ast.getColumn(), "Second argument to near is empty");
				Expression e1 = p1.getExpression(0);
				ExtNear near = new ExtNear(context);
                near.setLocation(ast.getLine(), ast.getColumn());
				near.addTerm(e1);
				near.setPath((PathExpr) params.get(0));

				if (params.size() > 2) {
				    p1 = (PathExpr) params.get(2);
				    if (p1.getLength() == 0) {
					throw new XPathException(ast.getLine(), ast.getColumn(), "Max distance argument to near is empty");
				    }
				    near.setMaxDistance(p1);
				    
				    if (params.size() == 4) {
					p1 = (PathExpr) params.get(3);
					if (p1.getLength() == 0) {
					    throw new XPathException(ast.getLine(), ast.getColumn(), "Min distance argument to near is empty");
					}
					near.setMinDistance(p1);
				    }
				}
				
				step = near;
			}
	
			// phrase(node-set, string)
            if (local.equals("phrase")) {
                if (params.size() < 2)
                    throw new XPathException(ast.getLine(), ast.getColumn(), "XPST0017: Function phrase() requires two arguments");
                PathExpr p1 = (PathExpr) params.get(1);
                if (p1.getLength() == 0)
                    throw new XPathException(ast.getLine(), ast.getColumn(), "Second argument to phrase is empty");
                Expression e1 = p1.getExpression(0);
                ExtPhrase phrase = new ExtPhrase(context);
                phrase.setLocation(ast.getLine(), ast.getColumn());
                phrase.addTerm(e1);
                phrase.setPath((PathExpr) params.get(0));                                          
                step = phrase;
            }
			
			// starts-with(node-set, string)
			if (local.equals("starts-with")) {
				if (params.size() < 2)
					throw new XPathException(ast.getLine(), ast.getColumn(), "XPST0017: Function starts-with() requires two or three arguments");
				if (params.size() > 3)
					throw new XPathException(ast.getLine(), ast.getColumn(), "XPST0017: Function starts-with() requires two or three arguments");
				PathExpr p0 = (PathExpr) params.get(0);
				PathExpr p1 = (PathExpr) params.get(1);
				if (p1.getLength() == 0)
					throw new XPathException(ast.getLine(), ast.getColumn(), "Second argument to starts-with is empty");
				GeneralComparison op = 
					new GeneralComparison(context, p0, p1, Constants.EQ, Constants.TRUNC_RIGHT);
                op.setLocation(ast.getLine(), ast.getColumn());
				//TODO : not sure for parent -pb
	            context.getProfiler().message(parent, Profiler.OPTIMIZATIONS, "OPTIMIZATION",  
	            "Rewritten start-with as a general comparison with a right truncations");				
				if (params.size() == 3) {
					op.setCollation( (Expression)params.get(2));
				} else {
					op.setCollation( new StringValue( "?strength=identical" ) );
				}
				step = op;
			}
	
			// ends-with(node-set, string)
			if (local.equals("ends-with")) {
				if (params.size() < 2)
					throw new XPathException(ast.getLine(), ast.getColumn(), "XPST0017 : Function ends-with() requires two or three arguments");
				if (params.size() > 3)
					throw new XPathException(ast.getLine(), ast.getColumn(), "XPST0017 : Function ends-with() requires two or three arguments");
				PathExpr p0 = (PathExpr) params.get(0);
				PathExpr p1 = (PathExpr) params.get(1);
				if (p1.getLength() == 0)
					throw new XPathException(ast.getLine(), ast.getColumn(), "Second argument to ends-with is empty");
				GeneralComparison op =
					new GeneralComparison(context, p0, p1, Constants.EQ, Constants.TRUNC_LEFT);
				//TODO : not sure for parent -pb
	            context.getProfiler().message(parent, Profiler.OPTIMIZATIONS, "OPTIMIZATION",  
	            "Rewritten ends-with as a general comparison with a left truncations");
                op.setLocation(ast.getLine(), ast.getColumn());
				if (params.size() == 3) {
					op.setCollation( (Expression)params.get(2));
				} else {
					op.setCollation( new StringValue( "?strength=identical" ) );
				}
				step = op;
			}
	
			// contains(node-set, string)
			if (local.equals("contains")) {
				if (params.size() < 2)
					throw new XPathException(ast.getLine(), ast.getColumn(), "XPST0017: Function contains() requires two or three arguments");
				if (params.size() > 3)
					throw new XPathException(ast.getLine(), ast.getColumn(), "XPST0017: Function contains() requires two or three arguments");
				PathExpr p0 = (PathExpr) params.get(0);
				PathExpr p1 = (PathExpr) params.get(1);
				if (p1.getLength() == 0)
					throw new XPathException(ast.getLine(), ast.getColumn(), "Second argument to contains is empty");
				GeneralComparison op =
					new GeneralComparison(context, p0, p1, Constants.EQ, Constants.TRUNC_BOTH);
				//TODO : not sure for parent -pb
	            context.getProfiler().message(parent, Profiler.OPTIMIZATIONS, "OPTIMIZATION",  
	            "Rewritten contains as a general comparison with left and right truncations");
                op.setLocation(ast.getLine(), ast.getColumn());
				if (params.size() == 3) {
					op.setCollation( (Expression)params.get(2));
				} else {
					op.setCollation( new StringValue( "?strength=identical" ) );
				}<commit_msg>[bugfix] do not assume a collation for contain, starts-with or ends-with if none was specified. using a collation costs performance.<commit_after>/*
 *  eXist Open Source Native XML Database
 *  Copyright (C) 2001-06 <NAME>
 *  <EMAIL>
 *  http://exist.sourceforge.net
 *  
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *  
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *  
 *  $Id$
 */
package org.exist.xquery;

import java.util.Iterator;
import java.util.List;

import org.exist.Namespaces;
import org.exist.dom.QName;
import org.exist.xquery.functions.ExtNear;
import org.exist.xquery.functions.ExtPhrase;
import org.exist.xquery.parser.XQueryAST;
import org.exist.xquery.value.StringValue;
import org.exist.xquery.value.Type;

public class FunctionFactory {

	public static final String ENABLE_JAVA_BINDING_ATTRIBUTE = "enable-java-binding";
	public static final String PROPERTY_ENABLE_JAVA_BINDING = "xquery.enable-java-binding";
	public static final String DISABLE_DEPRECATED_FUNCTIONS_ATTRIBUTE = "disable-deprecated-functions";
	public static final String PROPERTY_DISABLE_DEPRECATED_FUNCTIONS = "xquery.disable-deprecated-functions";	
	public static final boolean DISABLE_DEPRECATED_FUNCTIONS_BY_DEFAULT = false;
	
	/**
	 * Create a function call. 
	 * 
	 * This method handles all calls to built-in or user-defined
	 * functions. It also deals with constructor functions and
	 * optimizes some function calls like starts-with, ends-with or
	 * contains. 
	 */
	public static Expression createFunction(XQueryContext context, XQueryAST ast, PathExpr parent, List params)
		throws XPathException {
		QName qname = null;
		try {
			qname = QName.parse(context, ast.getText(), context.getDefaultFunctionNamespace());
		} catch(XPathException e) {
            e.setLocation(ast.getLine(), ast.getColumn());
			throw e;
		}
		String local = qname.getLocalName();
		String uri = qname.getNamespaceURI();
		Expression step = null;
		if(uri.equals(Function.BUILTIN_FUNCTION_NS)) {
			//TODO : move to text:near()
			// near(node-set, string)
			if (local.equals("near")) {
				if (params.size() < 2)
					throw new XPathException(ast.getLine(), ast.getColumn(), "XPST0017: Function near() requires two arguments");
				PathExpr p1 = (PathExpr) params.get(1);
				if (p1.getLength() == 0)
					throw new XPathException(ast.getLine(), ast.getColumn(), "Second argument to near is empty");
				Expression e1 = p1.getExpression(0);
				ExtNear near = new ExtNear(context);
                near.setLocation(ast.getLine(), ast.getColumn());
				near.addTerm(e1);
				near.setPath((PathExpr) params.get(0));

				if (params.size() > 2) {
				    p1 = (PathExpr) params.get(2);
				    if (p1.getLength() == 0) {
					throw new XPathException(ast.getLine(), ast.getColumn(), "Max distance argument to near is empty");
				    }
				    near.setMaxDistance(p1);
				    
				    if (params.size() == 4) {
					p1 = (PathExpr) params.get(3);
					if (p1.getLength() == 0) {
					    throw new XPathException(ast.getLine(), ast.getColumn(), "Min distance argument to near is empty");
					}
					near.setMinDistance(p1);
				    }
				}
				
				step = near;
			}
	
			// phrase(node-set, string)
            if (local.equals("phrase")) {
                if (params.size() < 2)
                    throw new XPathException(ast.getLine(), ast.getColumn(), "XPST0017: Function phrase() requires two arguments");
                PathExpr p1 = (PathExpr) params.get(1);
                if (p1.getLength() == 0)
                    throw new XPathException(ast.getLine(), ast.getColumn(), "Second argument to phrase is empty");
                Expression e1 = p1.getExpression(0);
                ExtPhrase phrase = new ExtPhrase(context);
                phrase.setLocation(ast.getLine(), ast.getColumn());
                phrase.addTerm(e1);
                phrase.setPath((PathExpr) params.get(0));                                          
                step = phrase;
            }
			
			// starts-with(node-set, string)
			if (local.equals("starts-with")) {
				if (params.size() < 2)
					throw new XPathException(ast.getLine(), ast.getColumn(), "XPST0017: Function starts-with() requires two or three arguments");
				if (params.size() > 3)
					throw new XPathException(ast.getLine(), ast.getColumn(), "XPST0017: Function starts-with() requires two or three arguments");
				PathExpr p0 = (PathExpr) params.get(0);
				PathExpr p1 = (PathExpr) params.get(1);
				if (p1.getLength() == 0)
					throw new XPathException(ast.getLine(), ast.getColumn(), "Second argument to starts-with is empty");
				GeneralComparison op = 
					new GeneralComparison(context, p0, p1, Constants.EQ, Constants.TRUNC_RIGHT);
                op.setLocation(ast.getLine(), ast.getColumn());
				//TODO : not sure for parent -pb
	            context.getProfiler().message(parent, Profiler.OPTIMIZATIONS, "OPTIMIZATION",  
	            "Rewritten start-with as a general comparison with a right truncations");				
				if (params.size() == 3) {
					op.setCollation( (Expression)params.get(2));
				}
				step = op;
			}
	
			// ends-with(node-set, string)
			if (local.equals("ends-with")) {
				if (params.size() < 2)
					throw new XPathException(ast.getLine(), ast.getColumn(), "XPST0017 : Function ends-with() requires two or three arguments");
				if (params.size() > 3)
					throw new XPathException(ast.getLine(), ast.getColumn(), "XPST0017 : Function ends-with() requires two or three arguments");
				PathExpr p0 = (PathExpr) params.get(0);
				PathExpr p1 = (PathExpr) params.get(1);
				if (p1.getLength() == 0)
					throw new XPathException(ast.getLine(), ast.getColumn(), "Second argument to ends-with is empty");
				GeneralComparison op =
					new GeneralComparison(context, p0, p1, Constants.EQ, Constants.TRUNC_LEFT);
				//TODO : not sure for parent -pb
	            context.getProfiler().message(parent, Profiler.OPTIMIZATIONS, "OPTIMIZATION",  
	            "Rewritten ends-with as a general comparison with a left truncations");
                op.setLocation(ast.getLine(), ast.getColumn());
				if (params.size() == 3) {
					op.setCollation( (Expression)params.get(2));
				}
				step = op;
			}
	
			// contains(node-set, string)
			if (local.equals("contains")) {
				if (params.size() < 2)
					throw new XPathException(ast.getLine(), ast.getColumn(), "XPST0017: Function contains() requires two or three arguments");
				if (params.size() > 3)
					throw new XPathException(ast.getLine(), ast.getColumn(), "XPST0017: Function contains() requires two or three arguments");
				PathExpr p0 = (PathExpr) params.get(0);
				PathExpr p1 = (PathExpr) params.get(1);
				if (p1.getLength() == 0)
					throw new XPathException(ast.getLine(), ast.getColumn(), "Second argument to contains is empty");
				GeneralComparison op =
					new GeneralComparison(context, p0, p1, Constants.EQ, Constants.TRUNC_BOTH);
				//TODO : not sure for parent -pb
	            context.getProfiler().message(parent, Profiler.OPTIMIZATIONS, "OPTIMIZATION",  
	            "Rewritten contains as a general comparison with left and right truncations");
                op.setLocation(ast.getLine(), ast.getColumn());
				if (params.size() == 3) {
					op.setCollation( (Expression)params.get(2));
				}
{'fraction_non_alphanumeric': 0.12355672690763052, 'fraction_numerical': 0.01443273092369478, 'mean_word_length': 3.091655969191271, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '120517', 'n_tokens_mistral': 5579, 'n_tokens_neox': 4734, 'n_words': 1358}