public class Executable
{
	//
	// Constants
	//

	/**
	 * Prefix prepended to on-the-fly scriptlets stored in the document source.
	 */
	public static final String ON_THE_FLY_PREFIX = "_ON_THE_FLY_";

	//
	// Static operations
	//

	/**
	 * If the executable does not yet exist in the document source, retrieves
	 * the source code and parses it into a compact, optimized, executable.
	 * Parsing requires the appropriate {@link LanguageAdapter} implementations
	 * to be available in the language manager.
	 * 
	 * @param documentName
	 *        The document name
	 * @param parserName
	 *        The parser to use, or null for the default parser
	 * @param parsingContext
	 *        The parsing context
	 * @return A document descriptor with a valid executable as its document
	 * @throws ParsingException
	 *         In case of a parsing error
	 * @throws DocumentException
	 *         In case of a document retrieval error
	 */
	public static DocumentDescriptor<Executable> createOnce( String documentName, String parserName, ParsingContext parsingContext ) throws ParsingException, DocumentException
	{
		DocumentDescriptor<Executable> documentDescriptor = parsingContext.getDocumentSource().getDocument( documentName );
		createOnce( documentDescriptor, parserName, parsingContext );
		return documentDescriptor;
	}

	/**
	 * If the executable does not yet exist in the document descriptor,
	 * retrieves the source code and parses it into a compact, optimized,
	 * executable. Parsing requires the appropriate {@link LanguageAdapter}
	 * implementations to be available in the language manager.
	 * 
	 * @param documentDescriptor
	 *        The document descriptor
	 * @param parserName
	 *        The parser to use, or null for the default parser
	 * @param parsingContext
	 *        The parsing context
	 * @return A new executable or the existing one
	 * @throws ParsingException
	 *         In case of a parsing error
	 * @throws DocumentException
	 *         In case of a document retrieval error
	 */
	public static Executable createOnce( DocumentDescriptor<Executable> documentDescriptor, String parserName, ParsingContext parsingContext ) throws ParsingException, DocumentException
	{
		Executable executable = documentDescriptor.getDocument();
		if( executable == null )
		{
			String defaultLanguageTag = parsingContext.getLanguageManager().getLanguageTagByExtension( documentDescriptor.getDefaultName(), documentDescriptor.getTag(), parsingContext.getDefaultLanguageTag() );
			if( ( defaultLanguageTag != null ) && !defaultLanguageTag.equals( parsingContext.getDefaultLanguageTag() ) )
			{
				parsingContext = new ParsingContext( parsingContext );
				parsingContext.setDefaultLanguageTag( defaultLanguageTag );
			}

			executable = new Executable( documentDescriptor.getDefaultName(), documentDescriptor.getTimestamp(), documentDescriptor.getSourceCode(), parserName, parsingContext );
			Executable existing = documentDescriptor.setDocumentIfAbsent( executable );
			if( existing != null )
				executable = existing;
		}
		return executable;
	}

	/**
	 * Atomically creates a unique on-the-fly document name.
	 * 
	 * @return An on-the-fly document name
	 */
	public static String createOnTheFlyDocumentName()
	{
		return ON_THE_FLY_PREFIX + onTheFlyCounter.getAndIncrement();
	}

	//
	// Construction
	//

	/**
	 * Parses source code into a compact, optimized, executable. Parsing
	 * requires the appropriate {@link LanguageAdapter} implementations to be
	 * available in the language manager.
	 * 
	 * @param documentName
	 *        The document name
	 * @param documentTimestamp
	 *        The executable's document timestamp
	 * @param sourceCode
	 *        The source code
	 * @param parserName
	 *        The parser to use, or null for the default parser
	 * @param parsingContext
	 *        The parsing context
	 * @throws ParsingException
	 *         In case of a parsing error In case of a parsing or compilation
	 *         error
	 * @throws DocumentException
	 *         In case of a document retrieval error
	 * @see LanguageAdapter
	 */
	public Executable( String documentName, long documentTimestamp, String sourceCode, String parserName, ParsingContext parsingContext ) throws ParsingException, DocumentException
	{
		String partition = parsingContext.getPartition();
		if( ( partition == null ) && ( parsingContext.getDocumentSource() != null ) )
			partition = parsingContext.getDocumentSource().getIdentifier();

		this.documentName = documentName;
		this.partition = partition;
		this.documentTimestamp = documentTimestamp;
		this.executableServiceName = parsingContext.getExposedExecutableName();
		this.languageManager = parsingContext.getLanguageManager();

		if( parserName == null )
			parserName = parsingContext.getDefaultParser();

		// Find parser manager
		ParserManager parserManager = parsingContext.getParserManager();
		if( parserManager == null )
		{
			if( commonParserManager == null )
				commonParserManager = new ParserManager( Executable.class.getClassLoader() );
			parserManager = commonParserManager;
		}
		this.parserManager = parserManager;

		// Find parser
		Parser parser = parserManager.getParser( parserName );

		if( parser == null )
			throw new ParsingException( documentName, "Parser not found: " + parserName );

		Collection<ExecutableSegment> segments = parser.parse( sourceCode, parsingContext, this );

		// Flatten list into array
		this.segments = new ExecutableSegment[segments.size()];
		segments.toArray( this.segments );
	}

	//
	// Attributes
	//

	/**
	 * The executable's document name.
	 * 
	 * @return The document name
	 * @see #getPartition()
	 */
	public String getDocumentName()
	{
		return documentName;
	}

	/**
	 * The executable partition. It used in addition to the document name to
	 * calculate unique IDs for documents. Partitioning allows you have the same
	 * document name on multiple partitions.
	 * 
	 * @return The executable partition
	 * @see #getDocumentName()
	 */
	public String getPartition()
	{
		return partition;
	}

	/**
	 * The language manager used to parse, prepare and execute the executable.
	 * 
	 * @return The language manager
	 */
	public LanguageManager getLanguageManager()
	{
		return languageManager;
	}

	/**
	 * The parser manager used to parse the executable.
	 * 
	 * @return The parser manager
	 */
	public ParserManager getParserManager()
	{
		return parserManager;
	}

	/**
	 * User-defined attributes.
	 * 
	 * @return The attributes
	 */
	public ConcurrentMap<String, Object> getAttributes()
	{
		return attributes;
	}

	/**
	 * The default name for the {@link ExecutableService} instance.
	 * 
	 * @return The default executable service name
	 */
	public String getExecutableServiceName()
	{
		return executableServiceName;
	}

	/**
	 * The executable's document timestamp.
	 * 
	 * @return The timestamp
	 */
	public long getDocumentTimestamp()
	{
		return documentTimestamp;
	}

	/**
	 * Timestamp of when the executable last finished executing or entering
	 * successfully, or 0 if it was never executed or entered.
	 * 
	 * @return The timestamp or 0
	 */
	public long getLastUsedTimestamp()
	{
		return lastUsedTimestamp;
	}

	/**
	 * Returns the source code in the trivial case of a "text-with-scriptlets"
	 * executable that contains no scriptlets. Identifying such executables can
	 * save you from making unnecessary calls to
	 * {@link #execute(ExecutionContext, Object, ExecutionController)} in some
	 * situations.
	 * 
	 * @return The source code if it's pure literal text, null if not
	 */
	public String getAsPureLiteral()
	{
		if( segments.length == 1 )
		{
			ExecutableSegment sole = segments[0];
			if( !sole.isProgram )
				return sole.sourceCode;
		}
		return null;
	}

	/**
	 * The enterable execution context for an entering key.
	 * 
	 * @param enteringKey
	 *        The entering key
	 * @return The execution context
	 * @see #makeEnterable(Object, ExecutionContext, Object,
	 *      ExecutionController)
	 * @see ExecutionContext#enter(String, Object...)
	 */
	public ExecutionContext getEnterableExecutionContext( Object enteringKey )
	{
		return enterableExecutionContexts.get( enteringKey );
	}

	/**
	 * The container service stored in the context, if it was set.
	 * 
	 * @param executionContext
	 *        The execution context
	 * @return The container service or null
	 * @see #execute(ExecutionContext, Object, ExecutionController)
	 */
	public Object getContainerService( ExecutionContext executionContext )
	{
		ExecutableService executableService = getExecutableService( executionContext );
		if( executableService != null )
			return executableService.getContainer();
		else
			return null;
	}

	//
	// Operations
	//

	/**
	 * Executes the executable.
	 * 
	 * @param executionContext
	 *        The execution context
	 * @throws ParsingException
	 *         In case of a parsing error
	 * @throws ExecutionException
	 *         In case of an execution error
	 * @throws IOException
	 *         In case of a writing error
	 */
	public void execute( ExecutionContext executionContext ) throws ParsingException, ExecutionException, IOException
	{
		execute( executionContext, null, null );
	}

	/**
	 * Executes the executable.
	 * 
	 * @param executionContext
	 *        The execution context
	 * @param containerService
	 *        The optional container service
	 * @param executionController
	 *        The optional {@link ExecutionController} to be applied to the
	 *        execution context
	 * @throws ParsingException
	 *         In case of a parsing error
	 * @throws ExecutionException
	 *         In case of an execution error
	 * @throws IOException
	 *         In case of a writing error
	 */
	public void execute( ExecutionContext executionContext, Object containerService, ExecutionController executionController ) throws ParsingException, ExecutionException, IOException
	{
		if( executionContext == null )
			throw new ExecutionException( documentName, "Execute does not have an execution context" );

		ExecutionContext oldExecutionContext = executionContext.makeCurrent();

		if( !executionContext.isImmutable() && executionController != null )
			executionController.initialize( executionContext );

		Object oldExecutableService = null;
		if( !executionContext.isImmutable() )
			oldExecutableService = executionContext.getServices().put( executableServiceName, new ExecutableService( executionContext, languageManager, parserManager, containerService ) );

		try
		{
			for( ExecutableSegment segment : segments )
			{
				if( !segment.isProgram )
					// Literal
					executionContext.getWriter().write( segment.sourceCode );
				else
				{
					LanguageAdapter adapter = languageManager.getAdapterByTag( segment.languageTag );
					if( adapter == null )
						throw ParsingException.adapterNotFound( documentName, segment.startLineNumber, segment.startColumnNumber, segment.languageTag );

					if( !executionContext.isImmutable() )
						executionContext.addAdapter( adapter );

					if( !adapter.isThreadSafe() )
						adapter.getLock().lock();

					try
					{
						segment.program.execute( executionContext );
					}
					catch( ParsingException x )
					{
						x.setExectable( this );
						throw x;
					}
					catch( ExecutionException x )
					{
						x.setExectable( this );
						throw x;
					}
					finally
					{
						if( !adapter.isThreadSafe() )
							adapter.getLock().unlock();
					}

					if( !executionContext.isImmutable() )
						executionContext.addAdapter( adapter );
				}
			}
		}
		finally
		{
			if( !executionContext.isImmutable() && oldExecutableService != null )
				executionContext.getServices().put( executableServiceName, oldExecutableService );

			if( !executionContext.isImmutable() && executionController != null )
				executionController.release( executionContext );

			if( oldExecutionContext != null )
				oldExecutionContext.makeCurrent();
		}

		lastUsedTimestamp = System.currentTimeMillis();
	}

	/**
	 * Executes the executable with the current execution context.
	 * 
	 * @param containerService
	 *        The optional container service
	 * @param executionController
	 *        The optional {@link ExecutionController} to be applied to the
	 *        execution context
	 * @throws ParsingException
	 *         In case of a parsing error
	 * @throws ExecutionException
	 *         In case of an execution error
	 * @throws IOException
	 *         In case of a writing error
	 * @see ExecutionContext#getCurrent()
	 */
	public void executeInThread( Object containerService, ExecutionController executionController ) throws ParsingException, ExecutionException, IOException
	{
		execute( ExecutionContext.getCurrent(), containerService, executionController );
	}

	/**
	 * Makes an execution context enterable, in preparation for calling
	 * {@link ExecutionContext#enter(String, Object...)}.
	 * <p>
	 * Note that this can only be done once per entering key for an executable.
	 * If it succeeds and returns true, the execution context should be
	 * considered "consumed" by this executable. At this point it is immutable,
	 * and can only be released by calling {@link #release()} on the executable.
	 * 
	 * @param enteringKey
	 *        The entering key
	 * @param executionContext
	 *        The execution context
	 * @return False if we're already enterable and the execution context was
	 *         not consumed, true if the operation succeeded and execution
	 *         context was consumed
	 * @throws ParsingException
	 *         In case of a parsing error
	 * @throws ExecutionException
	 *         In case of an execution error
	 * @throws IOException
	 *         In case of a writing error
	 */
	public boolean makeEnterable( Object enteringKey, ExecutionContext executionContext ) throws ParsingException, ExecutionException, IOException
	{
		return makeEnterable( enteringKey, executionContext, null, null );
	}

	/**
	 * Makes an execution context enterable, in preparation for calling
	 * {@link ExecutionContext#enter(String, Object...)}.
	 * <p>
	 * Note that this can only be done once per entering key for an executable.
	 * If it succeeds and returns true, the execution context should be
	 * considered "consumed" by this executable. At this point it is immutable,
	 * and can only be released by calling {@link #release()} on the executable.
	 * 
	 * @param enteringKey
	 *        The entering key
	 * @param executionContext
	 *        The execution context
	 * @param containerService
	 *        The optional container service
	 * @param executionController
	 *        The optional {@link ExecutionController} to be applied to the
	 *        execution context
	 * @return False if we're already enterable and the execution context was
	 *         not consumed, true if the operation succeeded and execution
	 *         context was consumed
	 * @throws ParsingException
	 *         In case of a parsing error
	 * @throws ExecutionException
	 *         In case of an execution error
	 * @throws IOException
	 *         In case of a writing error
	 */
	public boolean makeEnterable( Object enteringKey, ExecutionContext executionContext, Object containerService, ExecutionController executionController ) throws ParsingException, ExecutionException, IOException
	{
		if( executionContext.enterableExecutable != null )
			throw new IllegalStateException( "Execution context was already made enterable for another executable" );

		execute( executionContext, containerService, executionController );

		if( enterableExecutionContexts.putIfAbsent( enteringKey, executionContext ) != null )
			return false;

		executionContext.enterableExecutable = this;
		executionContext.enteringKey = enteringKey;
		executionContext.makeImmutable();
		return true;
	}

	/**
	 * Enters the executable at a stored, named location, via the last language
	 * adapter that used the enterable context. According to the language, the
	 * entry point can be a function, method, lambda, closure, etc.
	 * <p>
	 * An execution context must have been previously made enterable by a call
	 * to
	 * {@link #makeEnterable(Object, ExecutionContext, Object, ExecutionController)}
	 * .
	 * 
	 * @param enteringKey
	 *        The entering key
	 * @param entryPointName
	 *        The name of the entry point
	 * @param arguments
	 *        Optional state to pass to the entry point
	 * @return State returned from the entry point or null
	 * @throws ParsingException
	 *         In case of a parsing error
	 * @throws ExecutionException
	 *         In case of an execution error
	 * @throws NoSuchMethodException
	 *         In case the entry point does not exist
	 * @see ExecutionContext#enter(String, Object...)
	 */
	public Object enter( Object enteringKey, String entryPointName, Object... arguments ) throws ParsingException, ExecutionException, NoSuchMethodException
	{
		ExecutionContext enterableExecutionContext = enterableExecutionContexts.get( enteringKey );
		if( enterableExecutionContext == null )
			throw new IllegalStateException( "Executable does not have an enterable execution context for key: " + enteringKey );

		if( enterableExecutionContext.enterableExecutable != this )
			throw new ExecutionException( documentName, "Attempted to enter executable using an uninitialized execution context" );

		Object r = enterableExecutionContext.enter( entryPointName, arguments );
		lastUsedTimestamp = System.currentTimeMillis();
		return r;
	}

	/**
	 * Releases consumed execution contexts.
	 * 
	 * @see #makeEnterable(Object, ExecutionContext, Object,
	 *      ExecutionController)
	 * @see #finalize()
	 */
	public void release()
	{
		for( ExecutionContext enterableExecutionContext : enterableExecutionContexts.values() )
			enterableExecutionContext.release();
	}

	//
	// Object
	//

	@Override
	public String toString()
	{
		return "Executable: " + documentName + ", " + partition + ", " + documentTimestamp;
	}

	// //////////////////////////////////////////////////////////////////////////
	// Protected

	/**
	 * The execution contexts to be used for calls to
	 * {@link #enter(Object, String, Object...)}.
	 * 
	 * @see #makeEnterable(Object, ExecutionContext, Object,
	 *      ExecutionController)
	 */
	protected final ConcurrentMap<Object, ExecutionContext> enterableExecutionContexts = new ConcurrentHashMap<Object, ExecutionContext>();

	//
	// Object
	//

	@Override
	protected void finalize()
	{
		release();
	}

	// //////////////////////////////////////////////////////////////////////////
	// Private

	/**
	 * Used to ensure unique names for on-the-fly scriptlets.
	 */
	private static final AtomicInteger onTheFlyCounter = new AtomicInteger();

	/**
	 * A shared parser manager used when none is specified.
	 */
	private static volatile ParserManager commonParserManager;

	/**
	 * The executable's partition.
	 */
	private final String partition;

	/**
	 * The executable's document name.
	 */
	private final String documentName;

	/**
	 * The executable's document timestamp.
	 */
	private final long documentTimestamp;

	/**
	 * The language manager used to parse, prepare and execute the executable.
	 */
	private final LanguageManager languageManager;

	/**
	 * The parser manager used to parse the executable.
	 */
	private final ParserManager parserManager;

	/**
	 * User-defined attributes.
	 */
	private final ConcurrentMap<String, Object> attributes = new ConcurrentHashMap<String, Object>();

	/**
	 * The segments, which can be programs, scriptlets or plain-text.
	 */
	private final ExecutableSegment[] segments;

	/**
	 * The default name for the {@link ExecutableService} instance.
	 */
	private final String executableServiceName;

	/**
	 * Timestamp of when the executable last finished executing or entering
	 * successfully, or 0 if it was never executed or entered.
	 */
	private volatile long lastUsedTimestamp = 0;

	/**
	 * Get the exposed service for the executable.
	 * 
	 * @param executionContext
	 *        The execution context
	 * @return The executable service
	 * @see #getExecutableServiceName()
	 */
	private ExecutableService getExecutableService( ExecutionContext executionContext )
	{
		return (ExecutableService) executionContext.getServices().get( executableServiceName );
	}
} public class ExecutionContext
{
	//
	// Static attributes
	//

	/**
	 * The execution context set for this thread.
	 * 
	 * @return An execution context or null
	 * @see #makeCurrent()
	 * @see #disconnect()
	 */
	public static ExecutionContext getCurrent()
	{
		ExecutionContext executionContext = ThreadLocalExecutionContext.current.get();
		if( ( executionContext != null ) && executionContext.released )
		{
			disconnect();
			return null;
		}
		else
			return executionContext;
	}

	//
	// Static operations
	//

	/**
	 * Makes sure there is no execution context associated with this thread.
	 * 
	 * @see #makeCurrent()
	 * @see #getCurrent()
	 */
	public static void disconnect()
	{
		ThreadLocalExecutionContext.current.set( null );
	}

	//
	// Construction
	//

	/**
	 * Constructor.
	 */
	public ExecutionContext()
	{
		this( null );
	}

	/**
	 * Constructor.
	 * 
	 * @param writer
	 *        The standard output set for executables using this context
	 */
	public ExecutionContext( Writer writer )
	{
		this( writer, null );
	}

	/**
	 * Constructor.
	 * 
	 * @param writer
	 *        The standard output set for executables using this context
	 * @param errorWriter
	 *        The standard error set for executables using this context
	 */
	public ExecutionContext( Writer writer, Writer errorWriter )
	{
		this.writer = writer;
		this.errorWriter = errorWriter;
	}

	//
	// Attributes
	//

	/**
	 * General-purpose attributes. Useful for configuring special language
	 * features not supported by the context. Additionally, language adapters
	 * and other components along the execution chain might use this to store
	 * contextual state.
	 * <p>
	 * Immutable contexts will return an unmodifiable map.
	 * 
	 * @return The attributes
	 * @see #isImmutable()
	 */
	public Map<String, Object> getAttributes()
	{
		if( released )
			throw new IllegalStateException( "Cannot access released execution context" );

		if( immutable )
			return Collections.unmodifiableMap( attributes );
		else
			return attributes;
	}

	/**
	 * Locations where language adapters might search for extra libraries. The
	 * exact use varies per language adapter. "Location" URIs are often
	 * filesystem directory paths, but some adapters might support paths to
	 * specific library files and network URLs.
	 * <p>
	 * Immutable contexts will return an unmodifiable list.
	 * 
	 * @see File#toURI()
	 * @return The library locations
	 */
	public List<URI> getLibraryLocations()
	{
		if( released )
			throw new IllegalStateException( "Cannot access released execution context" );

		if( immutable )
			return Collections.unmodifiableList( libraryLocations );
		else
			return libraryLocations;
	}

	/**
	 * Services exposed to executables using this context.
	 * <p>
	 * Immutable contexts will return an unmodifiable map.
	 * 
	 * @return The exposed services
	 * @see #isImmutable()
	 */
	public Map<String, Object> getServices()
	{
		if( released )
			throw new IllegalStateException( "Cannot access released execution context" );

		if( immutable )
			return Collections.unmodifiableMap( services );
		else
			return services;
	}

	/**
	 * The standard output set for executables using this context.
	 * 
	 * @return The writer or null
	 */
	public Writer getWriter()
	{
		if( released )
			throw new IllegalStateException( "Cannot access released execution context" );

		return writer;
	}

	/**
	 * The standard output set for executables using this context.
	 * 
	 * @return The writer or the default writer
	 */
	public Writer getWriterOrDefault()
	{
		if( released )
			throw new IllegalStateException( "Cannot access released execution context" );

		return writer != null ? writer : defaultWriter;
	}

	/**
	 * The standard output set for executables using this context.
	 * 
	 * @param writer
	 *        The writer
	 * @return The previous writer or null
	 */
	public Writer setWriter( Writer writer )
	{
		if( released )
			throw new IllegalStateException( "Cannot access released execution context" );
		else if( immutable )
			throw new IllegalStateException( "Cannot modify an immutable execution context" );

		Writer old = this.writer;
		this.writer = writer instanceof PrintWriter ? (PrintWriter) writer : new PrintWriter( writer, true );
		return old;
	}

	/**
	 * The standard error set for executables using this context.
	 * 
	 * @return The error writer or null
	 */
	public Writer getErrorWriter()
	{
		if( released )
			throw new IllegalStateException( "Cannot access released execution context" );

		return errorWriter;
	}

	/**
	 * The standard error set for executables using this context.
	 * 
	 * @return The error writer or the default error writer
	 */
	public Writer getErrorWriterOrDefault()
	{
		if( released )
			throw new IllegalStateException( "Cannot access released execution context" );

		return errorWriter != null ? errorWriter : defaultErrorWriter;
	}

	/**
	 * The standard error set for executables using this context.
	 * 
	 * @param writer
	 *        The writer
	 * @return The previous error writer or null
	 */
	public Writer setErrorWriter( Writer writer )
	{
		if( released )
			throw new IllegalStateException( "Cannot access released execution context" );
		else if( immutable )
			throw new IllegalStateException( "Cannot modify an immutable execution context" );

		Writer old = this.errorWriter;
		this.errorWriter = writer;
		return old;
	}

	/**
	 * The last language adapter used by the context.
	 * 
	 * @return The language adapter or null
	 */
	public LanguageAdapter getAdapter()
	{
		if( released )
			throw new IllegalStateException( "Cannot access released execution context" );

		return languageAdapter;
	}

	/**
	 * Sets a language adapter as a user of this context. The adapter will then
	 * get a chance to release contextual state when {@link #release()} is
	 * called.
	 * 
	 * @param languageAdapter
	 *        The language adapter
	 * @see LanguageAdapter#releaseContext(ExecutionContext)
	 */
	public void addAdapter( LanguageAdapter languageAdapter )
	{
		if( released )
			throw new IllegalStateException( "Cannot access released execution context" );
		else if( immutable )
			throw new IllegalStateException( "Cannot modify an immutable execution context" );

		this.languageAdapter = languageAdapter;
		languageAdapters.add( languageAdapter );
	}

	/**
	 * Non-null if this context is enterable.
	 * 
	 * @return The enterable executable or null
	 * @see #enter(String, Object...)
	 * @see Executable#makeEnterable(Object, ExecutionContext, Object,
	 *      ExecutionController)
	 */
	public Executable getEnterableExecutable()
	{
		if( released )
			throw new IllegalStateException( "Cannot access released execution context" );

		return enterableExecutable;
	}

	/**
	 * Immutable contexts will throw an {@link IllegalStateException} whenever
	 * an attempt is made to change them.
	 * 
	 * @return True if immutable
	 */
	public boolean isImmutable()
	{
		if( released )
			throw new IllegalStateException( "Cannot access released execution context" );

		return immutable;
	}

	//
	// Operations
	//

	/**
	 * Enters the executable at a stored, named location, via the last language
	 * adapter that used this context. According to the language, the entry
	 * point can be a function, method, lambda, closure, etc.
	 * <p>
	 * The context must have been previously made enterable by a call to
	 * {@link Executable#makeEnterable(Object, ExecutionContext, Object, ExecutionController)}
	 * .
	 * 
	 * @param entryPointName
	 *        The name of the entry point
	 * @param arguments
	 *        Optional state to pass to the entry point
	 * @return State returned from the entry point or null
	 * @throws ParsingException
	 *         In case of a parsing error
	 * @throws ExecutionException
	 *         In case of an execution error
	 * @throws NoSuchMethodException
	 *         In case the entry point does not exist
	 * @see #getEnterableExecutable()
	 * @see #getAdapter()
	 */
	public Object enter( String entryPointName, Object... arguments ) throws ParsingException, ExecutionException, NoSuchMethodException
	{
		if( enterableExecutable == null )
			throw new IllegalStateException( "This execution context is not enterable" );

		ExecutionContext oldExecutionContext = makeCurrent();

		LanguageAdapter languageAdapter = this.languageAdapter;
		if( !languageAdapter.isThreadSafe() )
			languageAdapter.getLock().lock();

		try
		{
			return languageAdapter.enter( entryPointName, enterableExecutable, this, arguments );
		}
		catch( ParsingException x )
		{
			x.setExectable( enterableExecutable );
			throw x;
		}
		catch( ExecutionException x )
		{
			x.setExectable( enterableExecutable );
			throw x;
		}
		finally
		{
			if( !languageAdapter.isThreadSafe() )
				languageAdapter.getLock().unlock();

			if( oldExecutionContext != null )
				oldExecutionContext.makeCurrent();
		}
	}

	/**
	 * Makes this context immutable. Any attempt to change it will result in an
	 * {@link IllegalStateException}.
	 * <p>
	 * Calling this method more than once will have no effect. Once made
	 * immutable, execution contexts cannot become mutable again.
	 */
	public void makeImmutable()
	{
		if( released )
			throw new IllegalStateException( "Cannot access released execution context" );

		immutable = true;
	}

	/**
	 * Sets this execution context for this thread.
	 * 
	 * @return The previous current execution context or null
	 * @see #getCurrent()
	 * @see #disconnect()
	 */
	public ExecutionContext makeCurrent()
	{
		if( released )
			throw new IllegalStateException( "Cannot access released execution context" );

		ExecutionContext oldExecutionContext = getCurrent();
		ThreadLocalExecutionContext.current.set( this );
		return oldExecutionContext;
	}

	/**
	 * Calls {@link LanguageAdapter#releaseContext(ExecutionContext)} on all
	 * adapters that have used this context.
	 * <p>
	 * After this call, any attempt to access the context will result in an
	 * {@link IllegalStateException}.
	 */
	public void release()
	{
		if( released )
			throw new IllegalStateException( "Cannot access released execution context" );

		for( LanguageAdapter languageAdapter : languageAdapters )
			languageAdapter.releaseContext( this );

		if( enterableExecutable != null )
			enterableExecutable.enterableExecutionContexts.remove( enteringKey );

		released = true;
	}

	//
	// Object
	//

	@Override
	public String toString()
	{
		return "ExecutionContext: " + ( enterableExecutable != null ? "enterable, " : "non-enterable, " ) + ( immutable ? "immutable, " : "mutable, " ) + ( released ? "released" : "unreleased" );
	}

	// //////////////////////////////////////////////////////////////////////////
	// Protected

	/**
	 * Non-null if this context is enterable.
	 */
	protected Executable enterableExecutable;

	/**
	 * Non-null if this context is enterable.
	 */
	protected Object enteringKey;

	// //////////////////////////////////////////////////////////////////////////
	// Private

	/**
	 * General-purpose attributes. Useful for configuring special language
	 * features not supported by the context. Additionally, language adapters
	 * and other components along the execution chain might use this to store
	 * contextual state.
	 */
	private final Map<String, Object> attributes = new HashMap<String, Object>();

	/**
	 * Locations where language adapters might search for extra libraries. The
	 * exact use varies per language adapter. "Location" URIs are often
	 * filesystem directory paths, but some adapters might support paths to
	 * specific library files and network URLs.
	 */
	private final List<URI> libraryLocations = new ArrayList<URI>();

	/**
	 * Services exposed to executables using this context.
	 */
	private final Map<String, Object> services = new HashMap<String, Object>();

	/**
	 * Language adapters that have used this context.
	 * 
	 * @see #release()
	 */
	private final Set<LanguageAdapter> languageAdapters = new HashSet<LanguageAdapter>();

	/**
	 * The standard output set for executables using this context.
	 */
	private Writer writer;

	/**
	 * The default standard output set for executables using this context.
	 */
	private static volatile Writer defaultWriter = new OutputStreamWriter( System.out );

	/**
	 * The standard error set for executables using this context.
	 */
	private Writer errorWriter;

	/**
	 * The default standard output set for executables using this context.
	 */
	private static volatile Writer defaultErrorWriter = new OutputStreamWriter( System.err );

	/**
	 * The last language adapter used by the context.
	 */
	private LanguageAdapter languageAdapter;

	/**
	 * Whether this context is immutable.
	 */
	private volatile boolean immutable;

	/**
	 * Whether this context has been released.
	 */
	private volatile boolean released;
} public interface LanguageAdapter
{
	//
	// Constants
	//

	/**
	 * Attribute for the name of the language adapter implementation.
	 */
	public static final String NAME = "name";

	/**
	 * Attribute for the version of the language adapter implementation.
	 */
	public static final String VERSION = "version";

	/**
	 * Attribute for the name of the implemented language.
	 */
	public static final String LANGUAGE_NAME = "language.name";

	/**
	 * Attribute for the version of the implemented language.
	 */
	public static final String LANGUAGE_VERSION = "language.version";

	/**
	 * Attribute for standard source code filename extensions.
	 */
	public static final String EXTENSIONS = "extensions";

	/**
	 * Attribute for default source code filename extension.
	 */
	public static final String DEFAULT_EXTENSION = "extension.default";

	/**
	 * Attribute for language tags supported for scriptlets.
	 */
	public static final String TAGS = "tags";

	/**
	 * Default language tag used for scriptlets.
	 */
	public static final String DEFAULT_TAG = "tag.default";

	//
	// Attributes
	//

	/**
	 * The language manager.
	 * 
	 * @return The language manager
	 */
	public LanguageManager getManager();

	/**
	 * The language manager.
	 * 
	 * @param manager
	 *        The manager
	 */
	public void setManager( LanguageManager manager );

	/**
	 * Adapter attributes. Adapter must at least support the keys listed in this
	 * interface, but may support their own special keys.
	 * 
	 * @return The attributes
	 */
	public Map<String, Object> getAttributes();

	/**
	 * Some languages or their script engines are inherently broken when called
	 * by multiple threads. Returning false here makes sure Scripturian respects
	 * this and blocks.
	 * 
	 * @return True if we support being run by concurrent threads
	 */
	public boolean isThreadSafe();

	/**
	 * Some languages are meant for use in one segment only. Return true here
	 * makes sure that Scripturian will not change the current language tag
	 * after each segment parsed by this adapter.
	 * 
	 * @return True if the current language tag should not change
	 */
	public boolean isEphemeral();

	/**
	 * Used when {@link #isThreadSafe()} returns false.
	 * 
	 * @return The lock
	 */
	public Lock getLock();

	/**
	 * Creates source code for outputting literal text to standard output.
	 * 
	 * @param literal
	 *        The literal text
	 * @param executable
	 *        The executable
	 * @return Source code
	 * @throws ParsingException
	 *         In case of a parsing error
	 */
	public String getSourceCodeForLiteralOutput( String literal, Executable executable ) throws ParsingException;

	/**
	 * Creates source code for outputting a source code expression to standard
	 * output.
	 * 
	 * @param expression
	 *        The source code expression
	 * @param executable
	 *        The executable
	 * @return Source code
	 * @throws ParsingException
	 *         In case of a parsing error
	 */
	public String getSourceCodeForExpressionOutput( String expression, Executable executable ) throws ParsingException;

	/**
	 * Creates source code for including the output of a text-with-scriptlets
	 * document.
	 * <p>
	 * For this to work, the executable must have been created with a container
	 * that supports an inclusion command named according to the language
	 * manager attribute
	 * {@link LanguageManager#CONTAINER_INCLUDE_COMMAND_ATTRIBUTE}.
	 * 
	 * @param expression
	 *        The source code expression
	 * @param executable
	 *        The executable
	 * @return Source code
	 * @throws ParsingException
	 *         In case of a parsing error
	 * @see LanguageManager#getAttributes()
	 */
	public String getSourceCodeForExpressionInclude( String expression, Executable executable ) throws ParsingException;

	//
	// Operations
	//

	/**
	 * Turns source code into a program. The intent is for the implementation to
	 * perform the bare minimum required for detecting errors in the source
	 * code. For better initialization, likely at the cost of extra processing
	 * up front, call {@link Program#prepare()} on the program.
	 * 
	 * @param sourceCode
	 *        The source code
	 * @param isScriptlet
	 *        Whether the source code is a scriptlet
	 * @param position
	 *        The program's position in the executable
	 * @param startLineNumber
	 *        The line number in the document for where the program's source
	 *        code begins
	 * @param startColumnNumber
	 *        The column number in the document for where the program's source
	 *        code begins
	 * @param executable
	 *        The executable
	 * @return A program
	 * @throws ParsingException
	 *         In case of a parsing error
	 */
	public Program createProgram( String sourceCode, boolean isScriptlet, int position, int startLineNumber, int startColumnNumber, Executable executable ) throws ParsingException;

	/**
	 * Enters the executable at a stored, named location. According to the
	 * language, the entry point can be a function, method, lambda, closure,
	 * etc.
	 * 
	 * @param entryPointName
	 *        The entry point name
	 * @param executable
	 *        The executable
	 * @param executionContext
	 *        The executable context
	 * @param arguments
	 *        Optional state to pass to the entry point
	 * @return State returned from to the entry point or null
	 * @throws NoSuchMethodException
	 *         In case the entry point does not exist
	 * @throws ParsingException
	 *         In case of a parsing error
	 * @throws ExecutionException
	 *         In case of an execution error
	 */
	public Object enter( String entryPointName, Executable executable, ExecutionContext executionContext, Object... arguments ) throws NoSuchMethodException, ParsingException, ExecutionException;

	/**
	 * Cleans up any resources used for an execution context.
	 * 
	 * @param executionContext
	 *        The execution context
	 * @see ExecutionContext#getAttributes()
	 */
	public void releaseContext( ExecutionContext executionContext );
} public class LanguageManager
{
	//
	// Constants
	//

	/**
	 * The property name for the Scripturian cache path.
	 * 
	 * @see #getCachePath()
	 */
	public static final String SCRIPTURIAN_CACHE_PATH_PROPERTY = "com.threecrickets.scripturian.cache";

	/**
	 * The default Scripturian cache path.
	 * 
	 * @see #getCachePath()
	 */
	public static final String SCRIPTURIAN_CACHE_PATH_DEFAULT = "scripturian/cache";

	/**
	 * The attribute name for the container include command.
	 */
	public static final String CONTAINER_INCLUDE_COMMAND_ATTRIBUTE = "com.threecrickets.scripturian.containerIncludeCommand";

	/**
	 * The attribute prefix for adapter priorities.
	 */
	public static final String ADAPTER_PRIORITY_ATTRIBUTE = "com.threecrickets.scripturian.priority.";

	/**
	 * The default container include command.
	 */
	public static final String DEFAULT_CONTAINER_INCLUDE_COMMAND = "include";

	//
	// Static attributes
	//

	/**
	 * The base path for language adapter caches.
	 * 
	 * @return The cache path
	 */
	public static File getCachePath()
	{
		File cachePath = LanguageManager.cachePath.get();
		if( cachePath == null )
		{
			// Parse properties
			String cachePathProperty = System.getProperty( SCRIPTURIAN_CACHE_PATH_PROPERTY );
			cachePath = new File( cachePathProperty != null ? cachePathProperty : SCRIPTURIAN_CACHE_PATH_DEFAULT );
			if( !LanguageManager.cachePath.compareAndSet( null, cachePath ) )
				cachePath = LanguageManager.cachePath.get();
		}
		return cachePath;
	}

	//
	// Construction
	//

	/**
	 * Adds all language adapters found in the
	 * {@code META-INF/services/com.threecrickets.scripturian.LanguageAdapter}
	 * resource using the current thread's context class loader.
	 * 
	 * @see ServiceLoader
	 */
	public LanguageManager()
	{
		this( Thread.currentThread().getContextClassLoader() );
	}

	/**
	 * Adds all language adapters found in the
	 * {@code META-INF/services/com.threecrickets.scripturian.LanguageAdapter}
	 * resource.
	 * 
	 * @param classLoader
	 *        The class loader
	 * @see ServiceLoader
	 */
	public LanguageManager( ClassLoader classLoader )
	{
		attributes.put( CONTAINER_INCLUDE_COMMAND_ATTRIBUTE, DEFAULT_CONTAINER_INCLUDE_COMMAND );

		// Initialize adapters
		ServiceLoader<LanguageAdapter> adapterLoader = ServiceLoader.load( LanguageAdapter.class, classLoader );
		for( Iterator<LanguageAdapter> i = adapterLoader.iterator(); i.hasNext(); )
		{
			LanguageAdapter adapter;
			try
			{
				adapter = i.next();
			}
			catch( Throwable x )
			{
				// Probably a ClassNotFoundException
				continue;
			}
			addAdapter( adapter );
		}
	}

	//
	// Attributes
	//

	/**
	 * General-purpose attributes for this language manager. Language adapters
	 * can use this for sharing state with other language adapters.
	 * 
	 * @return The attributes
	 */
	public ConcurrentMap<String, Object> getAttributes()
	{
		return attributes;
	}

	/**
	 * Find only those attributes with this prefix.
	 * 
	 * @param prefix
	 *        The attribute prefix
	 * @return The attributes with the prefix
	 */
	public Properties getAttributesAsProperties( String prefix )
	{
		Properties prefixed = new Properties();

		int prefixLength = prefix.length();
		for( Map.Entry<String, Object> entry : attributes.entrySet() )
			if( entry.getKey().startsWith( prefix ) )
				prefixed.put( entry.getKey().substring( prefixLength ), entry.getValue() );

		return prefixed;
	}

	/**
	 * All language adapters. Note that this set is unmodifiable. To add an
	 * adapter use {@link #addAdapter(LanguageAdapter)}
	 * 
	 * @return The adapters
	 */
	public Set<LanguageAdapter> getAdapters()
	{
		return Collections.unmodifiableSet( new HashSet<LanguageAdapter>( adapters.values() ) );
	}

	/**
	 * Gets an adapter by its name.
	 * 
	 * @param name
	 *        The name
	 * @return The language adapter
	 * @throws ParsingException
	 *         In case of a parsing error
	 * @see LanguageAdapter#NAME
	 */
	public LanguageAdapter getAdapterByName( String name ) throws ParsingException
	{
		return adapters.get( name );
	}

	/**
	 * A language adapter for a scriptlet tag.
	 * 
	 * @param tag
	 *        The scriptlet tag
	 * @return The language adapter or null if not found
	 * @throws ParsingException
	 *         In case of a parsing error
	 */
	public LanguageAdapter getAdapterByTag( String tag ) throws ParsingException
	{
		if( tag == null )
			return null;

		Set<LanguageAdapter> adaptersForTag = adaptersByTag.get( tag );
		if( adaptersForTag == null )
			return null;

		adaptersForTag = new HashSet<LanguageAdapter>( adaptersForTag );
		if( adaptersForTag.isEmpty() )
			return null;
		else if( adaptersForTag.size() == 1 )
			return adaptersForTag.iterator().next();
		else
			return getHighestPriorityAdapter( adaptersForTag );
	}

	/**
	 * A language adapter for a document name according to its filename
	 * extension.
	 * 
	 * @param documentName
	 *        The document name
	 * @param defaultExtension
	 *        The default extension to assume in case the document name does not
	 *        have one
	 * @return The language adapter or null if not found
	 * @throws ParsingException
	 *         In case of a parsing error
	 */
	public LanguageAdapter getAdapterByExtension( String documentName, String defaultExtension ) throws ParsingException
	{
		int slash = documentName.lastIndexOf( '/' );
		if( slash != -1 )
			documentName = documentName.substring( slash + 1 );

		int dot = documentName.lastIndexOf( '.' );
		String extension = dot != -1 ? documentName.substring( dot + 1 ) : defaultExtension;
		if( extension == null )
			throw new ParsingException( documentName, "Document name must have an extension" );

		Set<LanguageAdapter> adaptersForExtension = adaptersByExtension.get( extension );
		if( adaptersForExtension == null )
			return null;

		adaptersForExtension = new HashSet<LanguageAdapter>( adaptersForExtension );
		if( adaptersForExtension.isEmpty() )
			return null;
		else if( adaptersForExtension.size() == 1 )
			return adaptersForExtension.iterator().next();
		else
			return getHighestPriorityAdapter( adaptersForExtension );
	}

	/**
	 * A language adapter for a document name according to its filename
	 * extension.
	 * 
	 * @param documentName
	 *        The document name
	 * @param defaultExtension
	 *        The default extension to assume in case the document name does not
	 *        have one
	 * @param defaultTag
	 *        The language tag to use in case a language adapter wasn't found
	 *        according to the extension
	 * @return The default language adapter tag or null if not found
	 * @throws ParsingException
	 *         In case of a parsing error
	 */
	public String getLanguageTagByExtension( String documentName, String defaultExtension, String defaultTag ) throws ParsingException
	{
		LanguageAdapter languageAdapter = getAdapterByExtension( documentName, defaultExtension );
		if( languageAdapter == null )
			languageAdapter = getAdapterByTag( defaultTag );
		if( languageAdapter != null )
			return (String) languageAdapter.getAttributes().get( LanguageAdapter.DEFAULT_TAG );
		else
			return null;
	}

	//
	// Operations
	//

	/**
	 * Adds a language adapter to this manager.
	 * 
	 * @param adapter
	 *        The language adapter
	 */
	public void addAdapter( LanguageAdapter adapter )
	{
		if( adapter.getManager() != null )
			throw new RuntimeException( "Can't add language adapter instance to more than one language manager: " + adapter );

		adapters.put( (String) adapter.getAttributes().get( LanguageAdapter.NAME ), adapter );

		@SuppressWarnings("unchecked")
		Iterable<String> tags = (Iterable<String>) adapter.getAttributes().get( LanguageAdapter.TAGS );
		for( String tag : tags )
		{
			Set<LanguageAdapter> adaptersForTag = adaptersByTag.get( tag );
			if( adaptersForTag == null )
			{
				adaptersForTag = new CopyOnWriteArraySet<LanguageAdapter>();
				Set<LanguageAdapter> existing = adaptersByTag.putIfAbsent( tag, adaptersForTag );
				if( existing != null )
					adaptersForTag = existing;
			}
			adaptersForTag.add( adapter );
		}

		@SuppressWarnings("unchecked")
		Iterable<String> extensions = (Iterable<String>) adapter.getAttributes().get( LanguageAdapter.EXTENSIONS );
		for( String extension : extensions )
		{
			Set<LanguageAdapter> adaptersForExtension = adaptersByExtension.get( extension );
			if( adaptersForExtension == null )
			{
				adaptersForExtension = new CopyOnWriteArraySet<LanguageAdapter>();
				Set<LanguageAdapter> existing = adaptersByExtension.putIfAbsent( extension, adaptersForExtension );
				if( existing != null )
					adaptersForExtension = existing;
			}
			adaptersForExtension.add( adapter );
		}

		adapter.setManager( this );
	}

	//
	// Object
	//

	@Override
	public String toString()
	{
		return "LanguageManager: " + cachePath.get();
	}

	// //////////////////////////////////////////////////////////////////////////
	// Private

	/**
	 * The base path for language adapter caches.
	 */
	private static final AtomicReference<File> cachePath = new AtomicReference<File>();

	/**
	 * General-purpose attributes for this language manager.
	 */
	private final ConcurrentMap<String, Object> attributes = new ConcurrentHashMap<String, Object>();

	/**
	 * The language adapters.
	 */
	private final ConcurrentMap<String, LanguageAdapter> adapters = new ConcurrentHashMap<String, LanguageAdapter>();

	/**
	 * A map of language tags to their {@link LanguageAdapter} instances.
	 */
	private final ConcurrentMap<String, Set<LanguageAdapter>> adaptersByTag = new ConcurrentHashMap<String, Set<LanguageAdapter>>();

	/**
	 * A map of filename extensions to their {@link LanguageAdapter} instances.
	 */
	private final ConcurrentMap<String, Set<LanguageAdapter>> adaptersByExtension = new ConcurrentHashMap<String, Set<LanguageAdapter>>();

	/**
	 * Finds the highest priority adapter in a set.
	 * <p>
	 * The last adapter used in the current execution context always has the
	 * highest priority.
	 * 
	 * @param adapters
	 *        The adapters
	 * @return The highest priority adapter
	 */
	private LanguageAdapter getHighestPriorityAdapter( Set<LanguageAdapter> adapters )
	{
		ExecutionContext executionContext = ExecutionContext.getCurrent();
		if( executionContext != null )
		{
			LanguageAdapter lastAdapter = executionContext.getAdapter();
			if( adapters.contains( lastAdapter ) )
				return lastAdapter;
		}

		int highestPriority = Integer.MIN_VALUE;
		LanguageAdapter highestPriorityAdapter = null;

		for( LanguageAdapter adapter : adapters )
		{
			String attribute = ADAPTER_PRIORITY_ATTRIBUTE + adapter.getAttributes().get( LanguageAdapter.NAME );

			int priority = 0;
			Object priorityObject = attributes.get( attribute );
			if( priorityObject instanceof Number )
				priority = ( (Number) priorityObject ).intValue();
			else if( priorityObject != null )
			{
				try
				{
					priority = Integer.parseInt( priorityObject.toString() );
					attributes.put( attribute, priority );
				}
				catch( NumberFormatException x )
				{
				}
			}

			if( priority > highestPriority )
			{
				highestPriority = priority;
				highestPriorityAdapter = adapter;
			}
		}

		return highestPriorityAdapter;
	}
} public interface Program
{
	//
	// Attributes
	//

	/**
	 * The source code.
	 * 
	 * @return The source code
	 */
	public String getSourceCode();

	//
	// Operations
	//

	/**
	 * The optional "preparation" sub-phase is intended to speed up usage of
	 * later phases at the expense of higher cost during creation. It would be
	 * most useful if the executable is intended to be reused. In many
	 * implementations, "preparation" would involve compiling the code, and
	 * possibly caching the results on disk.
	 * 
	 * @throws PreparationException
	 *         In case of a preparation error
	 */
	public void prepare() throws PreparationException;

	/**
	 * Executes the program.
	 * 
	 * @param executionContext
	 *        The execution context
	 * @throws ParsingException
	 *         In case of a parsing error
	 * @throws ExecutionException
	 *         In case of an execution error
	 */
	public void execute( ExecutionContext executionContext ) throws ParsingException, ExecutionException;
} public class ExecutionException extends Exception
{
	//
	// Construction
	//

	public ExecutionException( String documentName, int lineNumber, int columnNumber, String message )
	{
		super( message );
		stack.add( new StackFrame( documentName, lineNumber, columnNumber ) );
	}

	public ExecutionException( String documentName, int lineNumber, int columnNumber, String message, Throwable cause )
	{
		super( message != null ? message : cause.getClass().getCanonicalName(), cause );
		stack.add( new StackFrame( documentName, lineNumber, columnNumber ) );
	}

	public ExecutionException( String documentName, int lineNumber, int columnNumber, Throwable cause )
	{
		super( cause );
		stack.add( new StackFrame( documentName, lineNumber, columnNumber ) );
	}

	public ExecutionException( String documentName, String message, Throwable cause )
	{
		super( message != null ? message : cause.getClass().getCanonicalName(), cause );
		stack.add( new StackFrame( documentName ) );
	}

	public ExecutionException( String documentName, String message )
	{
		super( message );
		stack.add( new StackFrame( documentName ) );
	}

	public ExecutionException( String message, Throwable cause )
	{
		super( message != null ? message : cause.getClass().getCanonicalName(), cause );
	}

	public ExecutionException( ParsingException parsingException )
	{
		this( parsingException.getCause() != null ? parsingException.getCause().getMessage() : parsingException.getMessage(), parsingException );
		stack.addAll( parsingException.getStack() );
	}

	//
	// Attributes
	//

	/**
	 * The associated executable.
	 * 
	 * @return The associated executable
	 * @see #setExectable(Executable)
	 */
	public Executable getExecutable()
	{
		return executable;
	}

	/**
	 * @param executable
	 *        The executable
	 * @see #getExecutable()
	 */
	public void setExectable( Executable executable )
	{
		this.executable = executable;
	}

	/**
	 * The call stack.
	 * 
	 * @return The call stack
	 */
	public List<StackFrame> getStack()
	{
		return stack;
	}

	// //////////////////////////////////////////////////////////////////////////
	// Private

	private static final long serialVersionUID = 1L;

	/**
	 * The associated executable.
	 */
	private Executable executable;

	/**
	 * The call stack.
	 */
	private final ArrayList<StackFrame> stack = new ArrayList<StackFrame>();
} public class LanguageAdapterException extends ParsingException
{
	//
	// Construction
	//

	public LanguageAdapterException( Class<? extends LanguageAdapter> adapter, String documentName, int lineNumber, int columnNumber, String message )
	{
		super( documentName, lineNumber, columnNumber, message );
		this.adapter = adapter;
	}

	public LanguageAdapterException( Class<? extends LanguageAdapter> adapter, String documentName, int lineNumber, int columnNumber, String message, Throwable cause )
	{
		super( documentName, lineNumber, columnNumber, message, cause );
		this.adapter = adapter;
	}

	public LanguageAdapterException( Class<? extends LanguageAdapter> adapter, String documentName, int lineNumber, int columnNumber, Throwable cause )
	{
		super( documentName, lineNumber, columnNumber, cause );
		this.adapter = adapter;
	}

	public LanguageAdapterException( Class<? extends LanguageAdapter> adapter, String documentName, String message, Throwable cause )
	{
		super( documentName, message, cause );
		this.adapter = adapter;
	}

	public LanguageAdapterException( Class<? extends LanguageAdapter> adapter, String documentName, String message )
	{
		super( documentName, message );
		this.adapter = adapter;
	}

	public LanguageAdapterException( Class<? extends LanguageAdapter> adapter, String message, Throwable cause )
	{
		super( message, cause );
		this.adapter = adapter;
	}

	public LanguageAdapterException( Class<? extends LanguageAdapter> adapter, Throwable cause )
	{
		super( cause );
		this.adapter = adapter;
	}

	public LanguageAdapterException( Class<? extends LanguageAdapter> adapter, String message )
	{
		super( message );
		this.adapter = adapter;
	}

	//
	// Attributes
	//

	/**
	 * The adapter adapter class.
	 * 
	 * @return The adapter adapter class
	 */
	public Class<? extends LanguageAdapter> getLanguage()
	{
		return adapter;
	}

	// //////////////////////////////////////////////////////////////////////////
	// Private

	private static final long serialVersionUID = 1L;

	/**
	 * The language adapter class.
	 */
	private final Class<? extends LanguageAdapter> adapter;
} public class ParsingException extends Exception
{
	//
	// Static operations
	//

	public static ParsingException adapterNotFound( String documentName, int lineNumber, int columnNumber, String languageTag )
	{
		return new ParsingException( documentName, lineNumber, columnNumber, "Adapter not available for language: " + languageTag );
	}

	//
	// Construction
	//

	public ParsingException( String documentName, int lineNumber, int columnNumber, String message )
	{
		super( message );
		stack.add( new StackFrame( documentName, lineNumber, columnNumber ) );
	}

	public ParsingException( String documentName, int lineNumber, int columnNumber, String message, Throwable cause )
	{
		super( message != null ? message : cause.getClass().getCanonicalName(), cause );
		stack.add( new StackFrame( documentName, lineNumber, columnNumber ) );
	}

	public ParsingException( String documentName, int lineNumber, int columnNumber, Throwable cause )
	{
		super( cause );
		stack.add( new StackFrame( documentName, lineNumber, columnNumber ) );
	}

	public ParsingException( String documentName, String message, Throwable cause )
	{
		super( message != null ? message : cause.getClass().getCanonicalName(), cause );
		stack.add( new StackFrame( documentName ) );
	}

	public ParsingException( String documentName, String message )
	{
		super( message );
		stack.add( new StackFrame( documentName ) );
	}

	public ParsingException( String message, Throwable cause )
	{
		super( message != null ? message : cause.getClass().getCanonicalName(), cause );
	}

	public ParsingException( String message )
	{
		super( message );
	}

	public ParsingException( Throwable cause )
	{
		super( cause );
	}

	//
	// Attributes
	//

	/**
	 * The associated executable.
	 * 
	 * @return The associated executable
	 * @see #setExectable(Executable)
	 */
	public Executable getExecutable()
	{
		return executable;
	}

	/**
	 * @param executable
	 *        The executable
	 * @see #getExecutable()
	 */
	public void setExectable( Executable executable )
	{
		this.executable = executable;
	}

	/**
	 * The call stack.
	 * 
	 * @return The call stack
	 */
	public List<StackFrame> getStack()
	{
		return stack;
	}

	// //////////////////////////////////////////////////////////////////////////
	// Private

	private static final long serialVersionUID = 1L;

	/**
	 * The associated executable.
	 */
	private Executable executable;

	/**
	 * The call stack.
	 */
	private final ArrayList<StackFrame> stack = new ArrayList<StackFrame>();
} public abstract class ScripturianUtil
{
	/**
	 * True if we are in a Windows operating system.
	 */
	public static boolean IS_WINDOWS = System.getProperty( "os.name" ).startsWith( "Windows" );

	/**
	 * The line separator.
	 */
	public static String LINE_SEPARATOR = System.getProperty( "line.separator" );

	/**
	 * Gets the filename extension (whatever is after the period), or null if it
	 * doesn't have one.
	 * 
	 * @param file
	 *        The file
	 * @return The filename extension or null
	 */
	public static String getExtension( File file )
	{
		String filename = file.getName();
		int period = filename.lastIndexOf( '.' );
		if( period != -1 )
			return filename.substring( period + 1 );
		else
			return null;
	}

	/**
	 * Reads a file into a string.
	 * 
	 * @param file
	 *        The file
	 * @param charset
	 *        The charset (null to use default JVM charset; not recommended!)
	 * @return The string read from the file
	 * @throws IOException
	 */
	public static String getString( File file, Charset charset ) throws IOException
	{
		if( IS_WINDOWS )
		{
			// Note: There is no way to force the release of a MappedByteBuffer.
			// Unfortunately, under Windows this causes the file to remain
			// locked against writing. Since this is very annoying during
			// development, we will avoid using NIO for Windows. :(
			//
			// See: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4724038

			FileInputStream stream = new FileInputStream( file );
			try
			{
				if( charset == null )
					charset = Charset.defaultCharset();
				BufferedReader reader = new BufferedReader( new InputStreamReader( stream, charset ) );
				try
				{
					long length = file.length();
					if( length > Integer.MAX_VALUE )
						throw new IOException( "File too big: " + file.getName() );
					StringWriter contents = new StringWriter( (int) length );
					String line;
					while( ( line = reader.readLine() ) != null )
					{
						contents.append( line );
						contents.append( LINE_SEPARATOR );
					}
					return contents.toString();
				}
				finally
				{
					reader.close();
				}
			}
			finally
			{
				stream.close();
			}
		}
		else
		{
			FileInputStream stream = new FileInputStream( file );
			try
			{
				FileChannel channel = stream.getChannel();
				try
				{
					MappedByteBuffer buffer = channel.map( FileChannel.MapMode.READ_ONLY, 0, channel.size() );
					if( charset == null )
						charset = Charset.defaultCharset();
					return charset.decode( buffer ).toString();
				}
				finally
				{
					channel.close();
				}
			}
			finally
			{
				stream.close();
			}
		}
	}

	/**
	 * Reads a file into a byte array.
	 * 
	 * @param file
	 *        The file
	 * @return The byte array
	 * @throws IOException
	 */
	public static byte[] getBytes( File file ) throws IOException
	{
		FileInputStream stream = new FileInputStream( file );
		try
		{
			long length = file.length();

			if( length > Integer.MAX_VALUE )
				throw new IOException( "File too big: " + file.getName() );

			byte[] bytes = new byte[(int) length];
			ByteBuffer buffer = ByteBuffer.wrap( bytes );

			FileChannel channel = stream.getChannel();
			try
			{
				channel.read( buffer );
				return bytes;
			}
			finally
			{
				channel.close();
			}
		}
		finally
		{
			stream.close();
		}
	}

	/**
	 * Gets the value of a switch-style command line argument.
	 * 
	 * @param name
	 *        The argument name
	 * @param arguments
	 *        The arguments
	 * @param defaultValue
	 *        The default value
	 * @return The value or the default value if not found
	 */
	public static String getSwitchArgument( String name, String[] arguments, String defaultValue )
	{
		name = "--" + name + "=";
		for( String argument : arguments )
			if( argument.startsWith( name ) )
				return argument.substring( name.length() );

		return defaultValue;
	}

	/**
	 * Gets the value of a command line argument, while skipping switch-style
	 * arguments.
	 * 
	 * @param index
	 *        The index of the non-switch argument
	 * @param arguments
	 *        The arguments
	 * @param defaultValue
	 *        The default value
	 * @return The value or the default value if not found
	 */
	public static String getNonSwitchArgument( int index, String[] arguments, String defaultValue )
	{
		int i = 0;
		for( String argument : arguments )
			if( !argument.startsWith( "--" ) )
				if( index == i++ )
					return argument;

		return defaultValue;
	}

	/**
	 * Calculates a JVM class file for aprogram based on executable partition,
	 * executable document name and program position. <i>You must synchronize
	 * access to this file</i> via the <code>synchronize</code> keyword, in
	 * order to guarantee that another thread will not be writing to the file at
	 * the same time. For this to work, File instances are guaranteed to be
	 * unique in this VM per combination of executable partition, document name
	 * and program position are the same.
	 * 
	 * @param subdirectory
	 *        The cache subdirectory
	 * @param executable
	 *        The executable
	 * @param position
	 *        The program's position in the executable
	 * @return The file
	 */
	public static File getFileForProgramClass( File subdirectory, Executable executable, int position )
	{
		return getFileForProgram( subdirectory, executable, position, CLASS_SUFFIX );
	}

	/**
	 * Calculates a file for a program based on executable partition, executable
	 * document name and program position. <i>You must synchronize access to
	 * this file</i> via the <code>synchronize</code> keyword, in order to
	 * guarantee that another thread will not be writing to the file at the same
	 * time. For this to work, File instances are guaranteed to be unique in
	 * this VM per combination of executable partition, document name and
	 * program position are the same.
	 * 
	 * @param subdirectory
	 *        The cache subdirectory
	 * @param executable
	 *        The executable
	 * @param position
	 *        The program's position in the executable
	 * @param suffix
	 *        The file's suffix
	 * @return The file
	 */
	public static File getFileForProgram( File subdirectory, Executable executable, int position, String suffix )
	{
		String partition = executable.getPartition();

		if( File.separatorChar != '/' )
			partition = partition.replace( '/', File.separatorChar );

		String filename = partition + executable.getDocumentName();
		filename = filename.replace( '-', '_' ).replace( '.', '$' ).replace( ':', '$' ).replace( ' ', '$' );
		filename += "$" + position + "$" + executable.getDocumentTimestamp() + suffix;

		File file = new File( subdirectory, filename );
		File existing = programFiles.get( file.getPath() );
		if( existing != null )
			file = existing;
		else
		{
			existing = programFiles.putIfAbsent( file.getPath(), file );
			if( existing != null )
				file = existing;
		}
		return file;
	}

	/**
	 * Calculates a JVM classname for a program based on executable partition,
	 * executable document name and program position.
	 * 
	 * @param executable
	 *        The executable
	 * @param position
	 *        The program's position in the executable
	 * @return The classname
	 */
	public static String getClassnameForProgram( Executable executable, int position )
	{
		String classname = executable.getPartition() + executable.getDocumentName();

		if( classname.startsWith( File.separator ) )
			classname = classname.substring( 1 );

		classname = classname.replace( '-', '_' ).replace( '.', '$' ).replace( ':', '$' ).replace( ' ', '$' ).replace( "//", "." ).replace( '/', '.' ).replace( "..", "." );
		if( File.separatorChar != '/' )
			classname = classname.replace( File.separator + File.separator, "." ).replace( File.separatorChar, '.' );
		classname += "$" + position + "$" + executable.getDocumentTimestamp();
		return classname;
	}

	/**
	 * Returns the file with its path normalized (all relative ".." and "."
	 * segments resolved).
	 * 
	 * @param file
	 *        The file
	 * @return The normalized file
	 */
	public static File getNormalizedFile( File file )
	{
		return new File( file.toURI().normalize() );
	}

	/**
	 * Returns the path of one file relative to another.
	 * 
	 * @param target
	 *        The target file
	 * @param base
	 *        The base file
	 * @return The target's file relative to the base file
	 */
	public static File getRelativeFile( File target, File base )
	{
		return new File( getRelativePath( target.getAbsolutePath(), base.getAbsolutePath() ) );
	}

	/**
	 * Returns the path of one file relative to another.
	 * 
	 * @param target
	 *        The target path
	 * @param base
	 *        The base path
	 * @return The target's path relative to the base path
	 */
	public static String getRelativePath( String target, String base )
	{
		// See:
		// http://stackoverflow.com/questions/204784/how-to-construct-a-relative-path-in-java-from-two-absolute-paths-or-urls

		String split = Pattern.quote( File.separator );
		String[] baseSegments = base.split( split );
		String[] targetSegments = target.split( split );
		StringBuilder result = new StringBuilder();

		// Skip common segments
		int index = 0;
		for( ; index < targetSegments.length && index < baseSegments.length; ++index )
		{
			if( !targetSegments[index].equals( baseSegments[index] ) )
				break;
		}

		// Backtrack to base directory
		int length = baseSegments.length;
		if( index != length )
		{
			for( int i = index; i < length; ++i )
			{
				// "." segments have no effect
				if( !baseSegments[i].equals( "." ) )
					result.append( ".." + File.separator );
			}
		}

		for( ; index < targetSegments.length; ++index )
			result.append( targetSegments[index] + File.separator );

		// Remove final path separator
		if( !target.endsWith( File.separator ) )
			result.delete( result.length() - 1, result.length() );

		return result.toString();
	}

	/**
	 * Calls include on the executable container.
	 * <p>
	 * Internally uses reflection with caching of looked-up methods.
	 * 
	 * @param manager
	 *        The language manager
	 * @param executable
	 *        The executable
	 * @param executionContext
	 *        The execution context
	 * @param documentName
	 *        The document name
	 */
	public static void containerInclude( LanguageManager manager, Executable executable, ExecutionContext executionContext, String documentName )
	{
		Object container = executable.getContainerService( executionContext );
		if( container != null )
		{
			Class<?> containerClass = container.getClass();
			Method includeMethod = includeMethods.get( containerClass );
			if( includeMethod == null )
			{
				String containerIncludeCommand = (String) manager.getAttributes().get( LanguageManager.CONTAINER_INCLUDE_COMMAND_ATTRIBUTE );
				try
				{
					includeMethod = containerClass.getMethod( containerIncludeCommand, new Class[]
					{
						String.class
					} );
				}
				catch( SecurityException x )
				{
				}
				catch( NoSuchMethodException x )
				{
				}
			}

			try
			{
				includeMethod.invoke( container, documentName );
			}
			catch( IllegalArgumentException x )
			{
			}
			catch( IllegalAccessException x )
			{
			}
			catch( InvocationTargetException x )
			{
			}
		}
	}

	/**
	 * Wraps literal strings in double quotes, escaping special characters with
	 * backslashes.
	 * 
	 * @param string
	 *        The string
	 * @return The resulting string
	 */
	public static String doubleQuotedLiteral( String string )
	{
		return "\"" + replace( string, PRINT_ESCAPE_PATTERNS, PRINT_ESCAPE_REPLACEMENTS ) + "\"";
	}

	/**
	 * Replaces all occurrences of several regular expressions in order.
	 * 
	 * @param string
	 *        The string
	 * @param patterns
	 *        An array of regular expressions
	 * @param replacements
	 *        An array of replacement strings (must be the same length and order
	 *        as patterns)
	 * @return The resulting string
	 */
	public static String replace( String string, Pattern[] patterns, String[] replacements )
	{
		for( int i = 0, length = patterns.length; i < length; i++ )
			string = patterns[i].matcher( string ).replaceAll( replacements[i] );
		return string;
	}

	// //////////////////////////////////////////////////////////////////////////
	// Private

	private static final String CLASS_SUFFIX = ".class";

	private static Pattern[] PRINT_ESCAPE_PATTERNS = new Pattern[]
	{
		Pattern.compile( "\\\\" ), Pattern.compile( "\\n" ), Pattern.compile( "\\r" ), Pattern.compile( "\\t" ), Pattern.compile( "\\f" ), Pattern.compile( "\\\"" )
	};

	private static String[] PRINT_ESCAPE_REPLACEMENTS = new String[]
	{
		Matcher.quoteReplacement( "\\\\" ), Matcher.quoteReplacement( "\\n" ), Matcher.quoteReplacement( "\\r" ), Matcher.quoteReplacement( "\\t" ), Matcher.quoteReplacement( "\\f" ), Matcher.quoteReplacement( "\\\"" )
	};

	/**
	 * Used by {@link #getFileForProgramClass(File, Executable, int)} to
	 * guarantee uniqueness.
	 */
	private static final ConcurrentMap<String, File> programFiles = new ConcurrentHashMap<String, File>();

	/**
	 * Cache of container include methods.
	 * 
	 * @see #containerInclude(LanguageManager, Executable, ExecutionContext,
	 *      String)
	 */
	private static final ConcurrentMap<Class<?>, Method> includeMethods = new ConcurrentHashMap<Class<?>, Method>();

	/**
	 * Disallow inheritance.
	 */
	private ScripturianUtil()
	{
	}
} public class WriterOutputStream extends OutputStream
{
	//
	// Construction
	//

	/**
	 * Constructor.
	 * 
	 * @param writer
	 *        The underlying writer
	 */
	public WriterOutputStream( Writer writer )
	{
		this( writer, null );
	}

	/**
	 * Constructor.
	 * 
	 * @param writer
	 *        The underlying writer
	 * @param encoding
	 *        The encoding
	 */
	public WriterOutputStream( Writer writer, String encoding )
	{
		this.writer = writer;
		this.encoding = encoding;
	}

	//
	// Writer
	//

	@Override
	public void close() throws IOException
	{
		writer.close();
	}

	@Override
	public void flush() throws IOException
	{
		writer.flush();
	}

	@Override
	public void write( byte[] b ) throws IOException
	{
		if( encoding == null )
			writer.write( new String( b ) );
		else
			writer.write( new String( b, encoding ) );
	}

	@Override
	public void write( byte[] b, int off, int len ) throws IOException
	{
		if( encoding == null )
			writer.write( new String( b, off, len ) );
		else
			writer.write( new String( b, off, len, encoding ) );
	}

	@Override
	public synchronized void write( int b ) throws IOException
	{
		buffer[0] = (byte) b;
		write( buffer );
	}

	// //////////////////////////////////////////////////////////////////////////
	// Private

	/**
	 * The underlying writer.
	 */
	private final Writer writer;

	/**
	 * The optional encoding.
	 */
	private final String encoding;

	/**
	 * Work buffer.
	 */
	private final byte[] buffer = new byte[1];
}
import java.io.File;
import java.io.PrintStream;
import java.net.URI;
import java.util.Arrays;
import org.luaj.vm2.Globals;
import org.luaj.vm2.Lua;
import org.luaj.vm2.LuaError;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.TwoArgFunction;
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
import org.luaj.vm2.lib.jse.JsePlatform;
import com.threecrickets.scripturian.Executable;
import com.threecrickets.scripturian.ExecutionContext;
import com.threecrickets.scripturian.LanguageAdapter;
import com.threecrickets.scripturian.LanguageManager;
import com.threecrickets.scripturian.Program;
import com.threecrickets.scripturian.exception.ExecutionException;
import com.threecrickets.scripturian.exception.LanguageAdapterException;
import com.threecrickets.scripturian.exception.ParsingException;
import com.threecrickets.scripturian.internal.ScripturianUtil;
import com.threecrickets.scripturian.internal.WriterOutputStream;
/**
 * Copyright 2009-2017 Three Crickets LLC.
 * <p>
 * The contents of this file are subject to the terms of the LGPL version 3.0:
 * http://www.gnu.org/copyleft/lesser.html
 * <p>
 * Alternatively, you can obtain a royalty free commercial license with less
 * limitations, transferable or non-transferable, directly from Three Crickets
 * at http://threecrickets.com/
 */

package com.threecrickets.scripturian.adapter;




/**
 * A {@link LanguageAdapter} that supports the Lua language as implemented by <a
 * href="http://luaj.org/luaj/README.html">Luaj</a>.
 * 
 * @author Tal Liron
 */
public class LuajAdapter extends LanguageAdapterBase
{
	//
	// Constant
	//

	/**
	 * The Luaj instance context attribute.
	 */
	public static final String LUAJ_GLOBALS = LuajAdapter.class.getCanonicalName() + ".globals";

	/**
	 * The default base directory for cached executables.
	 */
	public static final String LUAJ_CACHE_DIR = "lua";

	//
	// Static operations
	//

	/**
	 * Creates an execution exception.
	 * 
	 * @param documentName
	 *        The document name
	 * @param x
	 *        The exception
	 * @return The execution exception
	 */
	public static ExecutionException createExecutionException( String documentName, LuaError x )
	{
		if( x.getCause() != null )
			return new ExecutionException( documentName, x.getCause() );
		else
			return new ExecutionException( documentName, x );
	}

	//
	// Construction
	//

	/**
	 * Constructor.
	 * 
	 * @throws LanguageAdapterException
	 *         In case of an initialization error
	 */
	public LuajAdapter() throws LanguageAdapterException
	{
		super( "Luaj", getImplementationVersion(), "Lua", "", Arrays.asList( "lua" ), "lua", Arrays.asList( "lua", "luaj" ), "luaj" );
	}

	//
	// Attributes
	//

	/**
	 * Gets the Luaj globals instance associated with the execution context,
	 * creating it if it doesn't exist. Each execution context is guaranteed to
	 * have its own Luaj globals instance. The globals instance is updated to
	 * match the writers and services in the execution context.
	 * 
	 * @param executionContext
	 *        The execution context
	 * @return The Luaj globals
	 */
	public Globals getGlobals( ExecutionContext executionContext )
	{
		Globals globals = (Globals) executionContext.getAttributes().get( LUAJ_GLOBALS );

		if( globals == null )
		{
			globals = JsePlatform.standardGlobals();
			executionContext.getAttributes().put( LUAJ_GLOBALS, globals );
		}

		// Standard output and error
		globals.STDOUT = new PrintStream( new WriterOutputStream( executionContext.getWriterOrDefault() ), true );
		globals.STDERR = new PrintStream( new WriterOutputStream( executionContext.getErrorWriterOrDefault() ), true );

		// Append library locations to the Ruby class loader
		StringBuilder path = new StringBuilder();
		for( URI uri : executionContext.getLibraryLocations() )
		{
			try
			{
				File file = new File( uri );
				if( path.length() > 0 )
					path.append( ';' );
				path.append( file.getPath() );
				path.append( "/?.lua" );
			}
			catch( IllegalArgumentException x )
			{
				// URI is not a file
			}
		}
		globals.package_.setLuaPath( path.toString() );

		// Define services as entries in metatable
		LuaTable metatable = new LuaTable();
		metatable.rawset( LuaValue.INDEX, new GetService( executionContext ) );
		globals.setmetatable( metatable );

		return globals;
	}

	/**
	 * The base directory for cached executables.
	 * 
	 * @return The cache directory
	 */
	public File getCacheDir()
	{
		return new File( LanguageManager.getCachePath(), LUAJ_CACHE_DIR );
	}

	//
	// LanguageAdapter
	//

	@Override
	public String getSourceCodeForLiteralOutput( String literal, Executable executable ) throws ParsingException
	{
		literal = ScripturianUtil.doubleQuotedLiteral( literal );