<commit_before>/***************************************************************************
 *   Copyright (C) 2008-2010 by <NAME> <<EMAIL>>     *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library 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 General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this program; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 *                                                                         *
 *   For details about the authors of this software, see the AUTHORS file. *
 ***************************************************************************/

package jolie;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.regex.Pattern;
import jolie.jap.JapURLConnection;
import jolie.lang.Constants;
import jolie.lang.parse.Scanner;
import jolie.runtime.correlation.CorrelationEngine;

/**
 * A parser for JOLIE's command line arguments,
 * providing methods for accessing them.
 * @author <NAME>
 */
public class CommandLineParser
{
	private final static Pattern pathSeparatorPattern = Pattern.compile( jolie.lang.Constants.pathSeparator );
	private final static Pattern optionSeparatorPattern = Pattern.compile( " " );

	private final int connectionsLimit;
	private final int connectionsCache;
	private final CorrelationEngine.Type correlationAlgorithmType;
	private final String[] includePaths;
	private final String[] optionArgs;
	private final String[] whitepages;
	private final URL[] libURLs;
	private final InputStream programStream;
	private final String programFilepath;
	private final String[] arguments;
	private final Map< String, Scanner.Token > constants = new HashMap< String, Scanner.Token >();
	private final boolean verbose;
	private final JolieClassLoader jolieClassLoader;
	private final boolean isProgramCompiled;
	private final boolean typeCheck;
	private File programDirectory = null;
	
	/**
	 * Returns the arguments passed to the JOLIE program.
	 * @return the arguments passed to the JOLIE program.
	 */
	public String[] arguments()
	{
		return arguments;
	}

	/**
	 * Returns {@code true} if the program is compiled, {@code false} otherwise.
	 * @return {@code true} if the program is compiled, {@code false} otherwise.
	 */
	public boolean isProgramCompiled()
	{
		return isProgramCompiled;
	}

	/**
	 * Returns the file path of the JOLIE program to execute.
	 * @return the file path of the JOLIE program to execute
	 */
	public String programFilepath()
	{
		return programFilepath;
	}

	/**
	 * Returns an InputStream for the program code to execute.
	 * @return an InputStream for the program code to execute
	 */
	public InputStream programStream()
	{
		return programStream;
	}

	/**
	 * Returns the library URLs passed by command line with the -l option.
	 * @return the library URLs passed by command line
	 */
	public URL[] libURLs()
	{
		return libURLs;
	}

	/**
	 * Returns the include paths passed by command line with the -i option.
	 * @return the include paths passed by command line
	 */
	public String[] includePaths()
	{
		return includePaths;
	}

	/**
	 * Returns the connection limit parameter
	 * passed by command line with the -c option.
	 * @return the connection limit parameter passed by command line
	 */
	public int connectionsLimit()
	{
		return connectionsLimit;
	}

	/**
	 * Returns the connection cache parameter
	 * passed by command line with the --conncache option.
	 * @return the connection cache parameter passed by command line
	 */
	public int connectionsCache()
	{
		return connectionsCache;
	}
	
	private static String getOptionString( String option, String description )
	{
		return( '\t' + option + "\t\t" + description + '\n' );
	}
	
	private String getVersionString()
	{
		return( Constants.VERSION + "  " + Constants.COPYRIGHT );
	}

	/**
	 * Returns a map containing the constants defined by command line.
	 * @return a map containing the constants defined by command line
	 */
	public Map< String, Scanner.Token > definedConstants()
	{
		return constants;
	}

        
	protected String getHelpString()
	{
		StringBuilder helpBuilder = new StringBuilder();
		helpBuilder.append( getVersionString() );
		helpBuilder.append( "\n\nUsage: jolie [options] behaviour_file [options] [program arguments]\n\n" );
		helpBuilder.append( "Available options:\n" );
		helpBuilder.append(
				getOptionString( "-h, --help", "Display this help information" ) );
		//TODO include doc for -l and -i
		helpBuilder.append(
				getOptionString( "-C ConstantIdentifier=ConstantValue", "Sets constant ConstantIdentifier to ConstantValue before starting execution" ) );
		helpBuilder.append(
				getOptionString( "--connlimit [number]", "Set the maximum number of active connection threads" ) );
		helpBuilder.append(
				getOptionString( "--conncache [number]", "Set the maximum number of cached persistent output connections" ) );
		helpBuilder.append(
				getOptionString( "--correlationAlgorithm [simple|hash]", "Set the algorithm to use for message correlation" ) );
		helpBuilder.append(
				getOptionString( "--typecheck [true|false]", "Check for correlation and other data related typing errors (default: false)" ) );
		helpBuilder.append(
				getOptionString( "-R [location]", "Add the service at [location] to the registry whitelist for this Jolie program" ) );
		helpBuilder.append(
				getOptionString( "--verbose", "Activate verbose mode" ) );
		helpBuilder.append(
				getOptionString( "--version", "Display this program version information" ) );
		return helpBuilder.toString();
	}

	private void parseCommandLineConstant( String input )
		throws IOException
	{
		try {
			Scanner scanner = new Scanner( new ByteArrayInputStream( input.getBytes() ), new URI( "urn:CommandLine" ) );
			Scanner.Token token = scanner.getToken();
			if ( token.is( Scanner.TokenType.ID ) ) {
				String id = token.content();
				token = scanner.getToken();
				if ( token.isNot( Scanner.TokenType.ASSIGN ) ) {
					throw new IOException( "expected = after constant identifier " + id + ", found token type " + token.type() );
				}
				token = scanner.getToken();
				if ( token.isValidConstant() == false ) {
					throw new IOException( "expected constant value for constant identifier " + id + ", found token type " + token.type() );
				}
				constants.put( id, token );
			} else {
				throw new IOException( "expected constant identifier, found token type " + token.type() );
			}
		} catch( URISyntaxException e ) {
			throw new IOException( e );
		}
	}

	/**
	 * Returns <code>true</code> if the verbose option has been specified, false otherwise.
	 * @return <code>true</code> if the verbose option has been specified, false otherwise
	 */
	public boolean verbose()
	{
		return verbose;
	}

	/**
	 * Returns the type of correlation algorithm that has been specified.
	 * @return the type of correlation algorithm that has been specified.
	 * @see CorrelationAlgorithm
	 */
	public CorrelationEngine.Type correlationAlgorithmType()
	{
		return correlationAlgorithmType;
	}

	/**
	 * Constructor
	 * @param args the command line arguments
	 * @param classLoader the ClassLoader to use for finding resources
	 * @throws jolie.CommandLineException if the command line is not valid or asks for simple information. (like --help and --version)
	 */
	public CommandLineParser( String[] args, ClassLoader parentClassLoader )
		throws CommandLineException, IOException
	{
		this( args, parentClassLoader, ArgumentHandler.DEFAULT_ARGUMENT_HANDLER );
	}

	public CommandLineParser( String[] args, ClassLoader parentClassLoader, ArgumentHandler argHandler )
		throws CommandLineException, IOException
	{
		List< String > argsList = new ArrayList< String >( args.length );
		Collections.addAll( argsList, args );

		String csetAlgorithmName = "simple";
		List< String > optionsList = new ArrayList< String >();
		boolean bVerbose = false;
		boolean bTypeCheck = false; // Default for typecheck
		List< String > programArgumentsList = new ArrayList< String >();
		List< String > whitepageList = new ArrayList< String >();
		LinkedList< String > includeList = new LinkedList< String >();
		List< String > libList = new ArrayList< String >();
		int cLimit = -1;
		int cCache = 100;
		String pwd = new File( "" ).getCanonicalPath();
		includeList.add( pwd );
		includeList.add( "include" );
		libList.add( pwd );
		libList.add( "ext" );
		libList.add( "lib" );
		String olFilepath = null;
		String japUrl = null;
		for( int i = 0; i < argsList.size(); i++ ) {
			if ( "--help".equals( argsList.get( i ) ) || "-h".equals( argsList.get( i ) ) ) {
				throw new CommandLineException( getHelpString() );
			} else if ( "-C".equals( argsList.get( i ) ) ) {
				optionsList.add( argsList.get( i ) );
				i++;
				try {
					parseCommandLineConstant( argsList.get( i ) );
				} catch( IOException e ) {
					throw new CommandLineException( "Invalid constant definition, reason: " + e.getMessage() );
				}
				optionsList.add( argsList.get( i ) );
			} else if ( "-R".equals( argsList.get( i ) ) ) {
				optionsList.add( argsList.get( i ) );
				i++;
				whitepageList.add( argsList.get( i ) );
				optionsList.add( argsList.get( i ) );
			} else if ( "-i".equals( argsList.get( i ) ) ) {
				optionsList.add( argsList.get( i ) );
				i++;
				if ( japUrl != null ) {
					argsList.set( i, argsList.get( i ).replace( "$JAP$", japUrl ) );
				}
				String[] tmp = pathSeparatorPattern.split( argsList.get( i ) );
				Collections.addAll( includeList, tmp );
				optionsList.add( argsList.get( i ) );
			} else if ( "-l".equals( argsList.get( i ) ) ) {
				optionsList.add( argsList.get( i ) );
				i++;
				if ( japUrl != null ) {
					argsList.set( i, argsList.get( i ).replace( "$JAP$", japUrl ) );
				}
				String[] tmp = pathSeparatorPattern.split( argsList.get( i ) );
				Collections.addAll( libList, tmp );
				optionsList.add( argsList.get( i ) );
			} else if ( "--connlimit".equals( argsList.get( i ) ) ) {
				optionsList.add( argsList.get( i ) );
				i++;
				cLimit = Integer.parseInt( argsList.get( i ) );
				optionsList.add( argsList.get( i ) );
			} else if ( "--conncache".equals( argsList.get( i ) ) ) {
				optionsList.add( argsList.get( i ) );
				i++;
				cCache = Integer.parseInt( argsList.get( i ) );
				optionsList.add( argsList.get( i ) );
			} else if ( "--correlationAlgorithm".equals( argsList.get( i ) ) ) {
				optionsList.add( argsList.get( i ) );
				i++;
				csetAlgorithmName = argsList.get( i );
				optionsList.add( argsList.get( i ) );
			} else if ( "--typecheck".equals( argsList.get( i ) ) ) {
				optionsList.add( argsList.get( i ) );
				i++;
				String typeCheckStr = argsList.get( i );
				optionsList.add( argsList.get( i ) );
				if ( "false".equals( typeCheckStr ) ) {
					bTypeCheck = false;
				} else if ( "true".equals( typeCheckStr ) ) {
					bTypeCheck = true;
				}
			} else if ( "--verbose".equals( argsList.get( i ) ) ) {
				optionsList.add( argsList.get( i ) );
				bVerbose = true;
			} else if ( "--version".equals( argsList.get( i ) ) ) {
				throw new CommandLineException( getVersionString() );
			} else if (
				argsList.get( i ).endsWith( ".ol" )
				||
				argsList.get( i ).endsWith( ".iol" )
				||
				argsList.get( i ).endsWith( ".olc" )
			) {
				if ( olFilepath == null ) {
					olFilepath = argsList.get( i );
				} else {
					programArgumentsList.add( argsList.get( i ) );
				}
			} else if ( argsList.get( i ).endsWith( ".jap" ) ) {
				if ( olFilepath == null ) {
					String japFilename = new File( argsList.get( i ) ).getCanonicalPath();
					JarFile japFile = new JarFile( japFilename );
					Manifest manifest = japFile.getManifest();
					olFilepath = parseJapManifestForMainProgram( manifest, japFile );
					libList.add( japFilename );
					Collection< String > japOptions = parseJapManifestForOptions( manifest );
					argsList.addAll( i+1, japOptions );
					japUrl = japFilename + "!";
					programDirectory = new File( japFilename ).getParentFile();
				} else {
					programArgumentsList.add( argsList.get( i ) );
				}
			} else {
				if ( olFilepath == null ) { // It's an unrecognized argument
					int newIndex = argHandler.onUnrecognizedArgument( argsList, i );
					if ( newIndex == i ) {
						// The handler didn't change the index.
						// We abort so to avoid infinite looping.
						throw new CommandLineException( "Unrecognized command line option: " + argsList.get( i ) );
					}
					i = newIndex;
				} else { // They are command line arguments for the Jolie program
					for( ; i < argsList.size(); i++ ) {
						programArgumentsList.add( argsList.get( i ) );
					}
				}
			}
		}

		verbose = bVerbose;
		typeCheck = bTypeCheck;

		correlationAlgorithmType = CorrelationEngine.Type.fromString( csetAlgorithmName );
		if ( correlationAlgorithmType == null ) {
			throw new CommandLineException( "Unrecognized correlation algorithm: " + csetAlgorithmName );
		}
		optionArgs = optionsList.toArray( new String[ optionsList.size() ] );
		arguments = programArgumentsList.toArray( new String[ programArgumentsList.size() ] );
		whitepages = whitepageList.toArray( new String[ whitepageList.size() ] );
		
		if ( olFilepath == null ) {
			throw new CommandLineException( "Input file not specified." );
		} else if ( olFilepath.endsWith( ".olc" ) ) {
			isProgramCompiled = true;
		} else {
			isProgramCompiled = false;
		}
		
		programFilepath = olFilepath;
		
		connectionsLimit = cLimit;
		connectionsCache = cCache;

		List< URL > urls = new ArrayList< URL >();
		for( String path : libList ) {
			if ( path.contains( "!/" ) && !path.startsWith( "jap:" ) && !path.startsWith( "jar:" ) ) {
				path = "jap:file:" + path;
			}
			if ( path.endsWith( ".jar" ) || path.endsWith( ".jap" ) ) {
				if ( path.startsWith( "jap:" ) ) {
					urls.add( new URL( path + "!/" ) );
				} else {
					urls.add( new URL( "jap:file:" + path + "!/" ) );
				}
			} else if ( new File( path ).isDirectory() ) {
				urls.add( new URL( "file:" + path + "/" ) );
			} else if ( path.endsWith( Constants.fileSeparator + "*" ) ) {
				File dir = new File( path.substring( 0, path.length() - 2 ) );
				String jars[] = dir.list( new FilenameFilter() {
					public boolean accept( File dir, String filename ) {
						return filename.endsWith( ".jar" );
					}
				} );
				if ( jars != null ) {
					for( String jarPath : jars ) {
						urls.add( new URL( "jar:file:" + dir.getCanonicalPath() + '/' + jarPath + "!/" ) );
					}
				}
			} else {
				try {
					urls.add( new URL( path ) );
				} catch( MalformedURLException e ) {}
			}
		}
		libURLs = urls.toArray( new URL[]{} );
		jolieClassLoader = new JolieClassLoader( libURLs, parentClassLoader );
		
		programStream = getOLStream( olFilepath, includeList, jolieClassLoader );
		if ( programStream == null ) {
			throw new FileNotFoundException( olFilepath );
		}

		includePaths = includeList.toArray( new String[]{} );
	}

	public File programDirectory()
	{
		return programDirectory;
	}

	public boolean typeCheck()
	{<commit_msg>added .olc automatic launching when .ol file is absent<commit_after>/***************************************************************************
 *   Copyright (C) 2008-2010 by <NAME> <<EMAIL>>     *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library 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 General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this program; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 *                                                                         *
 *   For details about the authors of this software, see the AUTHORS file. *
 ***************************************************************************/

package jolie;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.regex.Pattern;
import jolie.jap.JapURLConnection;
import jolie.lang.Constants;
import jolie.lang.parse.Scanner;
import jolie.runtime.correlation.CorrelationEngine;

/**
 * A parser for JOLIE's command line arguments,
 * providing methods for accessing them.
 * @author <NAME>
 */
public class CommandLineParser
{
	private final static Pattern pathSeparatorPattern = Pattern.compile( jolie.lang.Constants.pathSeparator );
	private final static Pattern optionSeparatorPattern = Pattern.compile( " " );

	private final int connectionsLimit;
	private final int connectionsCache;
	private final CorrelationEngine.Type correlationAlgorithmType;
	private final String[] includePaths;
	private final String[] optionArgs;
	private final String[] whitepages;
	private final URL[] libURLs;
	private final InputStream programStream;
	private final String programFilepath;
	private final String[] arguments;
	private final Map< String, Scanner.Token > constants = new HashMap< String, Scanner.Token >();
	private final boolean verbose;
	private final JolieClassLoader jolieClassLoader;
	private final boolean isProgramCompiled;
	private final boolean typeCheck;
	private File programDirectory = null;
	
	/**
	 * Returns the arguments passed to the JOLIE program.
	 * @return the arguments passed to the JOLIE program.
	 */
	public String[] arguments()
	{
		return arguments;
	}

	/**
	 * Returns {@code true} if the program is compiled, {@code false} otherwise.
	 * @return {@code true} if the program is compiled, {@code false} otherwise.
	 */
	public boolean isProgramCompiled()
	{
		return isProgramCompiled;
	}

	/**
	 * Returns the file path of the JOLIE program to execute.
	 * @return the file path of the JOLIE program to execute
	 */
	public String programFilepath()
	{
		return programFilepath;
	}

	/**
	 * Returns an InputStream for the program code to execute.
	 * @return an InputStream for the program code to execute
	 */
	public InputStream programStream()
	{
		return programStream;
	}

	/**
	 * Returns the library URLs passed by command line with the -l option.
	 * @return the library URLs passed by command line
	 */
	public URL[] libURLs()
	{
		return libURLs;
	}

	/**
	 * Returns the include paths passed by command line with the -i option.
	 * @return the include paths passed by command line
	 */
	public String[] includePaths()
	{
		return includePaths;
	}

	/**
	 * Returns the connection limit parameter
	 * passed by command line with the -c option.
	 * @return the connection limit parameter passed by command line
	 */
	public int connectionsLimit()
	{
		return connectionsLimit;
	}

	/**
	 * Returns the connection cache parameter
	 * passed by command line with the --conncache option.
	 * @return the connection cache parameter passed by command line
	 */
	public int connectionsCache()
	{
		return connectionsCache;
	}
	
	private static String getOptionString( String option, String description )
	{
		return( '\t' + option + "\t\t" + description + '\n' );
	}
	
	private String getVersionString()
	{
		return( Constants.VERSION + "  " + Constants.COPYRIGHT );
	}

	/**
	 * Returns a map containing the constants defined by command line.
	 * @return a map containing the constants defined by command line
	 */
	public Map< String, Scanner.Token > definedConstants()
	{
		return constants;
	}

        
	protected String getHelpString()
	{
		StringBuilder helpBuilder = new StringBuilder();
		helpBuilder.append( getVersionString() );
		helpBuilder.append( "\n\nUsage: jolie [options] behaviour_file [options] [program arguments]\n\n" );
		helpBuilder.append( "Available options:\n" );
		helpBuilder.append(
				getOptionString( "-h, --help", "Display this help information" ) );
		//TODO include doc for -l and -i
		helpBuilder.append(
				getOptionString( "-C ConstantIdentifier=ConstantValue", "Sets constant ConstantIdentifier to ConstantValue before starting execution" ) );
		helpBuilder.append(
				getOptionString( "--connlimit [number]", "Set the maximum number of active connection threads" ) );
		helpBuilder.append(
				getOptionString( "--conncache [number]", "Set the maximum number of cached persistent output connections" ) );
		helpBuilder.append(
				getOptionString( "--correlationAlgorithm [simple|hash]", "Set the algorithm to use for message correlation" ) );
		helpBuilder.append(
				getOptionString( "--typecheck [true|false]", "Check for correlation and other data related typing errors (default: false)" ) );
		helpBuilder.append(
				getOptionString( "-R [location]", "Add the service at [location] to the registry whitelist for this Jolie program" ) );
		helpBuilder.append(
				getOptionString( "--verbose", "Activate verbose mode" ) );
		helpBuilder.append(
				getOptionString( "--version", "Display this program version information" ) );
		return helpBuilder.toString();
	}

	private void parseCommandLineConstant( String input )
		throws IOException
	{
		try {
			Scanner scanner = new Scanner( new ByteArrayInputStream( input.getBytes() ), new URI( "urn:CommandLine" ) );
			Scanner.Token token = scanner.getToken();
			if ( token.is( Scanner.TokenType.ID ) ) {
				String id = token.content();
				token = scanner.getToken();
				if ( token.isNot( Scanner.TokenType.ASSIGN ) ) {
					throw new IOException( "expected = after constant identifier " + id + ", found token type " + token.type() );
				}
				token = scanner.getToken();
				if ( token.isValidConstant() == false ) {
					throw new IOException( "expected constant value for constant identifier " + id + ", found token type " + token.type() );
				}
				constants.put( id, token );
			} else {
				throw new IOException( "expected constant identifier, found token type " + token.type() );
			}
		} catch( URISyntaxException e ) {
			throw new IOException( e );
		}
	}

	/**
	 * Returns <code>true</code> if the verbose option has been specified, false otherwise.
	 * @return <code>true</code> if the verbose option has been specified, false otherwise
	 */
	public boolean verbose()
	{
		return verbose;
	}

	/**
	 * Returns the type of correlation algorithm that has been specified.
	 * @return the type of correlation algorithm that has been specified.
	 * @see CorrelationAlgorithm
	 */
	public CorrelationEngine.Type correlationAlgorithmType()
	{
		return correlationAlgorithmType;
	}

	/**
	 * Constructor
	 * @param args the command line arguments
	 * @param classLoader the ClassLoader to use for finding resources
	 * @throws jolie.CommandLineException if the command line is not valid or asks for simple information. (like --help and --version)
	 */
	public CommandLineParser( String[] args, ClassLoader parentClassLoader )
		throws CommandLineException, IOException
	{
		this( args, parentClassLoader, ArgumentHandler.DEFAULT_ARGUMENT_HANDLER );
	}

	public CommandLineParser( String[] args, ClassLoader parentClassLoader, ArgumentHandler argHandler )
		throws CommandLineException, IOException
	{
		List< String > argsList = new ArrayList< String >( args.length );
		Collections.addAll( argsList, args );

		String csetAlgorithmName = "simple";
		List< String > optionsList = new ArrayList< String >();
		boolean bVerbose = false;
		boolean bTypeCheck = false; // Default for typecheck
		List< String > programArgumentsList = new ArrayList< String >();
		List< String > whitepageList = new ArrayList< String >();
		LinkedList< String > includeList = new LinkedList< String >();
		List< String > libList = new ArrayList< String >();
		int cLimit = -1;
		int cCache = 100;
		String pwd = new File( "" ).getCanonicalPath();
		includeList.add( pwd );
		includeList.add( "include" );
		libList.add( pwd );
		libList.add( "ext" );
		libList.add( "lib" );
		String olFilepath = null;
		String japUrl = null;
		for( int i = 0; i < argsList.size(); i++ ) {
			if ( "--help".equals( argsList.get( i ) ) || "-h".equals( argsList.get( i ) ) ) {
				throw new CommandLineException( getHelpString() );
			} else if ( "-C".equals( argsList.get( i ) ) ) {
				optionsList.add( argsList.get( i ) );
				i++;
				try {
					parseCommandLineConstant( argsList.get( i ) );
				} catch( IOException e ) {
					throw new CommandLineException( "Invalid constant definition, reason: " + e.getMessage() );
				}
				optionsList.add( argsList.get( i ) );
			} else if ( "-R".equals( argsList.get( i ) ) ) {
				optionsList.add( argsList.get( i ) );
				i++;
				whitepageList.add( argsList.get( i ) );
				optionsList.add( argsList.get( i ) );
			} else if ( "-i".equals( argsList.get( i ) ) ) {
				optionsList.add( argsList.get( i ) );
				i++;
				if ( japUrl != null ) {
					argsList.set( i, argsList.get( i ).replace( "$JAP$", japUrl ) );
				}
				String[] tmp = pathSeparatorPattern.split( argsList.get( i ) );
				Collections.addAll( includeList, tmp );
				optionsList.add( argsList.get( i ) );
			} else if ( "-l".equals( argsList.get( i ) ) ) {
				optionsList.add( argsList.get( i ) );
				i++;
				if ( japUrl != null ) {
					argsList.set( i, argsList.get( i ).replace( "$JAP$", japUrl ) );
				}
				String[] tmp = pathSeparatorPattern.split( argsList.get( i ) );
				Collections.addAll( libList, tmp );
				optionsList.add( argsList.get( i ) );
			} else if ( "--connlimit".equals( argsList.get( i ) ) ) {
				optionsList.add( argsList.get( i ) );
				i++;
				cLimit = Integer.parseInt( argsList.get( i ) );
				optionsList.add( argsList.get( i ) );
			} else if ( "--conncache".equals( argsList.get( i ) ) ) {
				optionsList.add( argsList.get( i ) );
				i++;
				cCache = Integer.parseInt( argsList.get( i ) );
				optionsList.add( argsList.get( i ) );
			} else if ( "--correlationAlgorithm".equals( argsList.get( i ) ) ) {
				optionsList.add( argsList.get( i ) );
				i++;
				csetAlgorithmName = argsList.get( i );
				optionsList.add( argsList.get( i ) );
			} else if ( "--typecheck".equals( argsList.get( i ) ) ) {
				optionsList.add( argsList.get( i ) );
				i++;
				String typeCheckStr = argsList.get( i );
				optionsList.add( argsList.get( i ) );
				if ( "false".equals( typeCheckStr ) ) {
					bTypeCheck = false;
				} else if ( "true".equals( typeCheckStr ) ) {
					bTypeCheck = true;
				}
			} else if ( "--verbose".equals( argsList.get( i ) ) ) {
				optionsList.add( argsList.get( i ) );
				bVerbose = true;
			} else if ( "--version".equals( argsList.get( i ) ) ) {
				throw new CommandLineException( getVersionString() );
			} else if (
				argsList.get( i ).endsWith( ".ol" )
				||
				argsList.get( i ).endsWith( ".iol" )
				||
				argsList.get( i ).endsWith( ".olc" )
			) {
				if ( olFilepath == null ) {
					olFilepath = argsList.get( i );
				} else {
					programArgumentsList.add( argsList.get( i ) );
				}
			} else if ( argsList.get( i ).endsWith( ".jap" ) ) {
				if ( olFilepath == null ) {
					String japFilename = new File( argsList.get( i ) ).getCanonicalPath();
					JarFile japFile = new JarFile( japFilename );
					Manifest manifest = japFile.getManifest();
					olFilepath = parseJapManifestForMainProgram( manifest, japFile );
					libList.add( japFilename );
					Collection< String > japOptions = parseJapManifestForOptions( manifest );
					argsList.addAll( i+1, japOptions );
					japUrl = japFilename + "!";
					programDirectory = new File( japFilename ).getParentFile();
				} else {
					programArgumentsList.add( argsList.get( i ) );
				}
			} else {
				if ( olFilepath == null ) { // It's an unrecognized argument
					int newIndex = argHandler.onUnrecognizedArgument( argsList, i );
					if ( newIndex == i ) {
						// The handler didn't change the index.
						// We abort so to avoid infinite looping.
						throw new CommandLineException( "Unrecognized command line option: " + argsList.get( i ) );
					}
					i = newIndex;
				} else { // They are command line arguments for the Jolie program
					for( ; i < argsList.size(); i++ ) {
						programArgumentsList.add( argsList.get( i ) );
					}
				}
			}
		}

		verbose = bVerbose;
		typeCheck = bTypeCheck;

		correlationAlgorithmType = CorrelationEngine.Type.fromString( csetAlgorithmName );
		if ( correlationAlgorithmType == null ) {
			throw new CommandLineException( "Unrecognized correlation algorithm: " + csetAlgorithmName );
		}
		optionArgs = optionsList.toArray( new String[ optionsList.size() ] );
		arguments = programArgumentsList.toArray( new String[ programArgumentsList.size() ] );
		whitepages = whitepageList.toArray( new String[ whitepageList.size() ] );
		
		if ( olFilepath == null ) {
			throw new CommandLineException( "Input file not specified." );
		}
	
		
		connectionsLimit = cLimit;
		connectionsCache = cCache;

		List< URL > urls = new ArrayList< URL >();
		for( String path : libList ) {
			if ( path.contains( "!/" ) && !path.startsWith( "jap:" ) && !path.startsWith( "jar:" ) ) {
				path = "jap:file:" + path;
			}
			if ( path.endsWith( ".jar" ) || path.endsWith( ".jap" ) ) {
				if ( path.startsWith( "jap:" ) ) {
					urls.add( new URL( path + "!/" ) );
				} else {
					urls.add( new URL( "jap:file:" + path + "!/" ) );
				}
			} else if ( new File( path ).isDirectory() ) {
				urls.add( new URL( "file:" + path + "/" ) );
			} else if ( path.endsWith( Constants.fileSeparator + "*" ) ) {
				File dir = new File( path.substring( 0, path.length() - 2 ) );
				String jars[] = dir.list( new FilenameFilter() {
					public boolean accept( File dir, String filename ) {
						return filename.endsWith( ".jar" );
					}
				} );
				if ( jars != null ) {
					for( String jarPath : jars ) {
						urls.add( new URL( "jar:file:" + dir.getCanonicalPath() + '/' + jarPath + "!/" ) );
					}
				}
			} else {
				try {
					urls.add( new URL( path ) );
				} catch( MalformedURLException e ) {}
			}
		}
		libURLs = urls.toArray( new URL[]{} );
		jolieClassLoader = new JolieClassLoader( libURLs, parentClassLoader );
		
                InputStream tmpProgramStream = getOLStream( olFilepath, includeList, jolieClassLoader );
		
		if ( tmpProgramStream == null ) {
                        if ( olFilepath.endsWith(".ol") ) {
                            // try to read the compiled version of the ol file
                            olFilepath = olFilepath + "c";
                            tmpProgramStream = getOLStream( olFilepath, includeList, jolieClassLoader );
                            if ( tmpProgramStream == null ) {
                                throw new FileNotFoundException( olFilepath );
                            }
                        } else {
                            throw new FileNotFoundException( olFilepath );
                        }
		}
                
                if ( olFilepath.endsWith( ".olc" ) ) {
			isProgramCompiled = true;
		} else {
			isProgramCompiled = false;
		}
                programFilepath = olFilepath;
                programStream = tmpProgramStream;

		includePaths = includeList.toArray( new String[]{} );
	}

	public File programDirectory()
	{
		return programDirectory;
	}

	public boolean typeCheck()
	{
{'fraction_non_alphanumeric': 0.1272964683732653, 'fraction_numerical': 0.001882724484528256, 'mean_word_length': 2.8949733885274984, 'pattern_counts': {'":': 0, '<': 59, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 27, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5309957', 'n_tokens_mistral': 10798, 'n_tokens_neox': 9419, 'n_words': 2934}