<commit_before>/*
 *  eXist Open Source Native XML Database
 *  Copyright (C) 2001-09 <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: BuiltinFunctions.java 9598 2009-07-31 05:45:57Z ixitar $
 */
package org.exist.xquery.modules.context;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;

import org.apache.log4j.Logger;

import org.exist.dom.QName;
import org.exist.xquery.Function;
import org.exist.xquery.Cardinality;
import org.exist.xquery.Dependency;
import org.exist.xquery.FunctionSignature;
import org.exist.xquery.Profiler;
import org.exist.xquery.XPathException;
import org.exist.xquery.XQueryContext;
import org.exist.xquery.XQueryWatchDog;
import org.exist.xquery.value.FunctionParameterSequenceType;
import org.exist.xquery.value.FunctionReturnSequenceType;
import org.exist.xquery.value.IntegerValue;
import org.exist.xquery.value.Item;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.SequenceType;
import org.exist.xquery.value.StringValue;
import org.exist.xquery.value.Type;


public class ContextAttributes extends Function 
{
	public final static String ATTRIBUTES_CONTEXTVAR 			= "_eXist_xquery_context_attributes";
		
	protected static final Logger logger = Logger.getLogger( ContextAttributes.class );
	
	protected static final FunctionParameterSequenceType ATTRIBUTE_NAME_PARAM 	= new FunctionParameterSequenceType( "name", Type.STRING, Cardinality.EXACTLY_ONE, "The attribute name" );
	protected static final FunctionParameterSequenceType ATTRIBUTE_VALUE_PARAM 	= new FunctionParameterSequenceType( "value", Type.ITEM, Cardinality.ZERO_OR_MORE, "The value to be stored in the context by attribute name" );
	protected static final FunctionParameterSequenceType XQUERY_ID_PARAM 		= new FunctionParameterSequenceType( "xquery-id", Type.INTEGER, Cardinality.EXACTLY_ONE, "The XQuery ID" );

	public final static FunctionSignature signatures[] = {
		
        new FunctionSignature(
            new QName( "get-attribute", ContextModule.NAMESPACE_URI, ContextModule.PREFIX ),
            "Returns the value associated with the given name, which was stored in the XQuery" +
            "context. This function is useful for storing temporary information if you don't have " +
			"a servlet request or session, that is you're running an XQuery as a scheduled task.",
            new SequenceType[] {
                ATTRIBUTE_NAME_PARAM
            },
            new FunctionReturnSequenceType( Type.ITEM, Cardinality.ZERO_OR_MORE, "The attribute value" )
        ),
		
        new FunctionSignature(
            new QName( "set-attribute", ContextModule.NAMESPACE_URI, ContextModule.PREFIX ),
            "Set the value of an XQuery context attribute with the specified name " +
			"This function is useful for storing temporary information if you don't have " +
			"a servlet request or session, that is you're running an XQuery as a scheduled task.",
            new SequenceType[] {
               ATTRIBUTE_NAME_PARAM,
			   ATTRIBUTE_VALUE_PARAM
            },
            new FunctionReturnSequenceType( Type.ITEM, Cardinality.EMPTY, "Returns an empty sequence" )
		),
		
		 new FunctionSignature(
            new QName( "get-attribute", ContextModule.NAMESPACE_URI, ContextModule.PREFIX ),
            "Returns the value associated with the given name, which was stored in the XQuery" +
            "context for the XQuery with the provided id. (dba role only). This function can be sued for simple inter-XQuery communication.",
            new SequenceType[] {
                ATTRIBUTE_NAME_PARAM,
				XQUERY_ID_PARAM
            },
            new FunctionReturnSequenceType( Type.ITEM, Cardinality.ZERO_OR_MORE, "The attribute value" )
        ),
		
        new FunctionSignature(
            new QName( "set-attribute", ContextModule.NAMESPACE_URI, ContextModule.PREFIX ),
            "Set the value of an XQuery context attribute with the specified name for the XQuery with the provided id. (dba role only)" +
			"This function can be used for simple inter-XQuery communication.",
            new SequenceType[] {
			  ATTRIBUTE_NAME_PARAM,
			  ATTRIBUTE_VALUE_PARAM,
			  XQUERY_ID_PARAM
            },
            new FunctionReturnSequenceType( Type.ITEM, Cardinality.EMPTY, "Returns an empty sequence" )
		)
    };
	
	
	public ContextAttributes( XQueryContext context, FunctionSignature signature ) 
	{
		super( context, signature );
	}
	
	
	/* (non-Javadoc)
	 * @see org.exist.xquery.Expression#eval(org.exist.dom.DocumentSet, org.exist.xquery.value.Sequence, org.exist.xquery.value.Item)
	 */
	public Sequence eval( Sequence contextSequence, Item contextItem ) throws XPathException 
	{
		Sequence ret = Sequence.EMPTY_SEQUENCE;
		
		if( context.getProfiler().isEnabled() ) {
			context.getProfiler().start( this );       
			context.getProfiler().message( this, Profiler.DEPENDENCIES, "DEPENDENCIES", Dependency.getDependenciesName( this.getDependencies() ) );
			
			if( contextSequence != null ) {
				context.getProfiler().message( this, Profiler.START_SEQUENCES, "CONTEXT SEQUENCE", contextSequence );
			}
			
			if( contextItem != null ) {
				context.getProfiler().message( this, Profiler.START_SEQUENCES, "CONTEXT ITEM", contextItem.toSequence() );
			}
		}
		
		// get attribute name parameter
		
		String attribName = getArgument( 0 ).eval( contextSequence, contextItem ).getStringValue();
		
        if( isCalledAs( "get-attribute" ) ) {
			if( getArgumentCount() > 1 ) {
				long xqueryID 	= ((IntegerValue)(getArgument( 1 ).eval( contextSequence, contextItem ).itemAt( 0 ))).getLong();
				
				ret =  retrieveAttribute( getForeignContext( xqueryID ), attribName );
			} else {
           		ret =  retrieveAttribute( context, attribName );
			}
        } else {
			Sequence attribValue = getArgument( 1 ).eval( contextSequence, contextItem );
			
			if( getArgumentCount() > 2 ) {
				long xqueryID 	= ((IntegerValue)(getArgument( 2 ).eval( contextSequence, contextItem ).itemAt( 0 ))).getLong();
				
				ret = storeAttribute( getForeignContext( xqueryID ), attribName, attribValue );
			} else {
        		ret = storeAttribute( context, attribName, attribValue );
			}
        }
		
		return( ret );
    }
	
	//***************************************************************************
	//*
	//*    Foreign Context Methods
	//*
	//***************************************************************************/
	
	private XQueryContext getForeignContext( long id ) throws XPathException 
	{
		XQueryContext 	foreignContext = null;
			
		if( !context.getUser().hasDbaRole() ) {
			throw( new XPathException( this, "Permission denied, calling user '" + context.getUser().getName() + "' must be a DBA to access foreign contexts" ) );
		}
		
		if( id != 0 ) {
            XQueryWatchDog watchdogs[] = getContext().getBroker().getBrokerPool().getProcessMonitor().getRunningXQueries();
			
            for( int i = 0; i < watchdogs.length; i++ ) {
	        	XQueryContext ctx = watchdogs[i].getContext();
	        	
	      		if( id == ctx.hashCode() ) {
	      			if( !watchdogs[i].isTerminating() ) {
	      				foreignContext = ctx;
	      		 	}
	      			break;
	      		}
	        }
	    }
			
		if( foreignContext == null ) {
			throw( new XPathException( this, "Foreign XQuery with id: " + id + " not found or is terminating" ) );
		}

		return( foreignContext );
	}
	
		
	//***************************************************************************
	//*
	//*    Context Attribute Methods
	//*
	//***************************************************************************/
	
	/**
	 * Retrieves a previously stored Attribute from the Context of an XQuery
	 * 
	 * @param context 			The Context of the XQuery containing the attribute
	 * @param key	 			The key of the attribute to retrieve from the Context of the XQuery
	 */
	private Sequence retrieveAttribute( XQueryContext context, String key ) 
	{
		Sequence	attribute = Sequence.EMPTY_SEQUENCE;
		
		synchronized( context ) {
			// get the existing attributes map from the context
			
			HashMap<String, Sequence> attributes = (HashMap)context.getXQueryContextVar( ATTRIBUTES_CONTEXTVAR );
			
			if( attributes != null ) {
				attribute = attributes.get( key );
			}
		}
		
		return( (Sequence)attribute );
	}


	/**
	 * Stores an attribute in the Context of an XQuery
	 * 
	 * @param context 			The Context of the XQuery to store the attribute in
	 * @param key	 			The key of the attribute to store in the Context of the XQuery
	 * @param attribute 		The attribute to store
	 * 
	 * @return empty sequence
	 */
	private Sequence storeAttribute( XQueryContext context, String key, Sequence attribute ) 
	{
		synchronized( context ) {
			// get the existing attributes map from the context
			
			HashMap<String, Sequence> attributes = (HashMap)context.getXQueryContextVar( ATTRIBUTES_CONTEXTVAR );
			
			if( attributes == null ) {
				// if there is no attributes map, create a new one
				attributes = new HashMap<String, Sequence>();
			}<commit_msg>Fixed NPE when getting an attribute that had not been set previously<commit_after>/*
 *  eXist Open Source Native XML Database
 *  Copyright (C) 2001-09 <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: BuiltinFunctions.java 9598 2009-07-31 05:45:57Z ixitar $
 */
package org.exist.xquery.modules.context;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;

import org.apache.log4j.Logger;

import org.exist.dom.QName;
import org.exist.xquery.Function;
import org.exist.xquery.Cardinality;
import org.exist.xquery.Dependency;
import org.exist.xquery.FunctionSignature;
import org.exist.xquery.Profiler;
import org.exist.xquery.XPathException;
import org.exist.xquery.XQueryContext;
import org.exist.xquery.XQueryWatchDog;
import org.exist.xquery.value.FunctionParameterSequenceType;
import org.exist.xquery.value.FunctionReturnSequenceType;
import org.exist.xquery.value.IntegerValue;
import org.exist.xquery.value.Item;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.SequenceType;
import org.exist.xquery.value.StringValue;
import org.exist.xquery.value.Type;


public class ContextAttributes extends Function 
{
	public final static String ATTRIBUTES_CONTEXTVAR 			= "_eXist_xquery_context_attributes";
		
	protected static final Logger logger = Logger.getLogger( ContextAttributes.class );
	
	protected static final FunctionParameterSequenceType ATTRIBUTE_NAME_PARAM 	= new FunctionParameterSequenceType( "name", Type.STRING, Cardinality.EXACTLY_ONE, "The attribute name" );
	protected static final FunctionParameterSequenceType ATTRIBUTE_VALUE_PARAM 	= new FunctionParameterSequenceType( "value", Type.ITEM, Cardinality.ZERO_OR_MORE, "The value to be stored in the context by attribute name" );
	protected static final FunctionParameterSequenceType XQUERY_ID_PARAM 		= new FunctionParameterSequenceType( "xquery-id", Type.INTEGER, Cardinality.EXACTLY_ONE, "The XQuery ID" );

	public final static FunctionSignature signatures[] = {
		
        new FunctionSignature(
            new QName( "get-attribute", ContextModule.NAMESPACE_URI, ContextModule.PREFIX ),
            "Returns the value associated with the given name, which was stored in the XQuery" +
            "context. This function is useful for storing temporary information if you don't have " +
			"a servlet request or session, that is you're running an XQuery as a scheduled task.",
            new SequenceType[] {
                ATTRIBUTE_NAME_PARAM
            },
            new FunctionReturnSequenceType( Type.ITEM, Cardinality.ZERO_OR_MORE, "The attribute value" )
        ),
		
        new FunctionSignature(
            new QName( "set-attribute", ContextModule.NAMESPACE_URI, ContextModule.PREFIX ),
            "Set the value of an XQuery context attribute with the specified name " +
			"This function is useful for storing temporary information if you don't have " +
			"a servlet request or session, that is you're running an XQuery as a scheduled task.",
            new SequenceType[] {
               ATTRIBUTE_NAME_PARAM,
			   ATTRIBUTE_VALUE_PARAM
            },
            new FunctionReturnSequenceType( Type.ITEM, Cardinality.EMPTY, "Returns an empty sequence" )
		),
		
		 new FunctionSignature(
            new QName( "get-attribute", ContextModule.NAMESPACE_URI, ContextModule.PREFIX ),
            "Returns the value associated with the given name, which was stored in the XQuery" +
            "context for the XQuery with the provided id. (dba role only). This function can be sued for simple inter-XQuery communication.",
            new SequenceType[] {
                ATTRIBUTE_NAME_PARAM,
				XQUERY_ID_PARAM
            },
            new FunctionReturnSequenceType( Type.ITEM, Cardinality.ZERO_OR_MORE, "The attribute value" )
        ),
		
        new FunctionSignature(
            new QName( "set-attribute", ContextModule.NAMESPACE_URI, ContextModule.PREFIX ),
            "Set the value of an XQuery context attribute with the specified name for the XQuery with the provided id. (dba role only)" +
			"This function can be used for simple inter-XQuery communication.",
            new SequenceType[] {
			  ATTRIBUTE_NAME_PARAM,
			  ATTRIBUTE_VALUE_PARAM,
			  XQUERY_ID_PARAM
            },
            new FunctionReturnSequenceType( Type.ITEM, Cardinality.EMPTY, "Returns an empty sequence" )
		)
    };
	
	
	public ContextAttributes( XQueryContext context, FunctionSignature signature ) 
	{
		super( context, signature );
	}
	
	
	/* (non-Javadoc)
	 * @see org.exist.xquery.Expression#eval(org.exist.dom.DocumentSet, org.exist.xquery.value.Sequence, org.exist.xquery.value.Item)
	 */
	public Sequence eval( Sequence contextSequence, Item contextItem ) throws XPathException 
	{
		Sequence ret = Sequence.EMPTY_SEQUENCE;
		
		if( context.getProfiler().isEnabled() ) {
			context.getProfiler().start( this );       
			context.getProfiler().message( this, Profiler.DEPENDENCIES, "DEPENDENCIES", Dependency.getDependenciesName( this.getDependencies() ) );
			
			if( contextSequence != null ) {
				context.getProfiler().message( this, Profiler.START_SEQUENCES, "CONTEXT SEQUENCE", contextSequence );
			}
			
			if( contextItem != null ) {
				context.getProfiler().message( this, Profiler.START_SEQUENCES, "CONTEXT ITEM", contextItem.toSequence() );
			}
		}
		
		// get attribute name parameter
		
		String attribName = getArgument( 0 ).eval( contextSequence, contextItem ).getStringValue();
		
        if( isCalledAs( "get-attribute" ) ) {
			if( getArgumentCount() > 1 ) {
				long xqueryID 	= ((IntegerValue)(getArgument( 1 ).eval( contextSequence, contextItem ).itemAt( 0 ))).getLong();
				
				ret =  retrieveAttribute( getForeignContext( xqueryID ), attribName );
			} else {
           		ret =  retrieveAttribute( context, attribName );
			}
        } else {
			Sequence attribValue = getArgument( 1 ).eval( contextSequence, contextItem );
			
			if( getArgumentCount() > 2 ) {
				long xqueryID 	= ((IntegerValue)(getArgument( 2 ).eval( contextSequence, contextItem ).itemAt( 0 ))).getLong();
				
				ret = storeAttribute( getForeignContext( xqueryID ), attribName, attribValue );
			} else {
        		ret = storeAttribute( context, attribName, attribValue );
			}
        }
		
		return( ret );
    }
	
	//***************************************************************************
	//*
	//*    Foreign Context Methods
	//*
	//***************************************************************************/
	
	private XQueryContext getForeignContext( long id ) throws XPathException 
	{
		XQueryContext 	foreignContext = null;
			
		if( !context.getUser().hasDbaRole() ) {
			throw( new XPathException( this, "Permission denied, calling user '" + context.getUser().getName() + "' must be a DBA to access foreign contexts" ) );
		}
		
		if( id != 0 ) {
            XQueryWatchDog watchdogs[] = getContext().getBroker().getBrokerPool().getProcessMonitor().getRunningXQueries();
			
            for( int i = 0; i < watchdogs.length; i++ ) {
	        	XQueryContext ctx = watchdogs[i].getContext();
	        	
	      		if( id == ctx.hashCode() ) {
	      			if( !watchdogs[i].isTerminating() ) {
	      				foreignContext = ctx;
	      		 	}
	      			break;
	      		}
	        }
	    }
			
		if( foreignContext == null ) {
			throw( new XPathException( this, "Foreign XQuery with id: " + id + " not found or is terminating" ) );
		}

		return( foreignContext );
	}
	
		
	//***************************************************************************
	//*
	//*    Context Attribute Methods
	//*
	//***************************************************************************/
	
	/**
	 * Retrieves a previously stored Attribute from the Context of an XQuery
	 * 
	 * @param context 			The Context of the XQuery containing the attribute
	 * @param key	 			The key of the attribute to retrieve from the Context of the XQuery
	 */
	private Sequence retrieveAttribute( XQueryContext context, String key ) 
	{
		Sequence	attribute = Sequence.EMPTY_SEQUENCE;
		
		synchronized( context ) {
			// get the existing attributes map from the context
			
			HashMap<String, Sequence> attributes = (HashMap)context.getXQueryContextVar( ATTRIBUTES_CONTEXTVAR );
			
			if( attributes != null ) {
				Sequence value = attributes.get( key );
				
				if( value != null ) {
					attribute = value;
				}
			}
		}
		
		return( (Sequence)attribute );
	}


	/**
	 * Stores an attribute in the Context of an XQuery
	 * 
	 * @param context 			The Context of the XQuery to store the attribute in
	 * @param key	 			The key of the attribute to store in the Context of the XQuery
	 * @param attribute 		The attribute to store
	 * 
	 * @return empty sequence
	 */
	private Sequence storeAttribute( XQueryContext context, String key, Sequence attribute ) 
	{
		synchronized( context ) {
			// get the existing attributes map from the context
			
			HashMap<String, Sequence> attributes = (HashMap)context.getXQueryContextVar( ATTRIBUTES_CONTEXTVAR );
			
			if( attributes == null ) {
				// if there is no attributes map, create a new one
				attributes = new HashMap<String, Sequence>();
			}
{'fraction_non_alphanumeric': 0.11952150674471876, 'fraction_numerical': 0.004479511326037159, 'mean_word_length': 3.3149571710959806, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1921613', 'n_tokens_mistral': 6023, 'n_tokens_neox': 5349, 'n_words': 1667}