// $Id: control.cpp,v 1.4 1999/03/10 19:59:21 shields Exp $
copyright notice

#include "config.h"
#include <sys/stat.h>
#include "control.h"
#include "scanner.h"
#include "parser.h"
#include "semantic.h"
#include "unicode.h"
#include "error.h"
#include "bytecode.h"


Control::Control(ArgumentExpander &arguments, Option &option_) : return_code(0),
                                                                 option(option_),
                                                                 dot_classpath_index(0),
                                                                 system_semantic(NULL),
                                                                 semantic(1024),
                                                                 needs_body_work(1024),
                                                                 type_trash_bin(1024),
                                                                 input_java_file_set(1021),
                                                                 input_class_file_set(1021),
                                                                 expired_file_set(),
                                                                 recompilation_file_set(1021),
                                                                 Object_type(NULL),
                                                                 Cloneable_type(NULL),
                                                                 Serializable_type(NULL),
                                                                 String_type(NULL),
                                                                 Void_type(NULL),
                                                                 Boolean_type(NULL),
                                                                 Byte_type(NULL),
                                                                 Short_type(NULL),
                                                                 Character_type(NULL),
                                                                 Integer_type(NULL),
                                                                 Long_type(NULL),
                                                                 Float_type(NULL),
                                                                 Double_type(NULL),
                                                                 Class_type(NULL),
                                                                 StringBuffer_type(NULL),
                                                                 Throwable_type(NULL),
                                                                 RuntimeException_type(NULL),
                                                                 Error_type(NULL),
                                                                 int_pool(&bad_value),
                                                                 long_pool(&bad_value),
                                                                 float_pool(&bad_value),
                                                                 double_pool(&bad_value),
                                                                 Utf8_pool(&bad_value)

#ifdef TEST
                                                               , line_count(0), 
                                                                 class_files_read(0),
                                                                 class_files_written(0),
                                                                 class_file_id(0),
                                                                 input_files_processed(0)
#endif
{Initialize top level variables in the Control class, 
 adding a set of names to the global NameLookupTable
    ProcessGlobalNameSymbols();
 Create the unnamed package and set up global names.
    ProcessUnnamedPackage();

    ProcessPath();

    ProcessSystemInformation();

    //
    // Instantiate a scanner and a parser and initialize the static members for the semantic processors.
    //
    scanner = new Scanner(*this);
    parser = new Parser();
    SemanticError::StaticInitializer();

    //
    // Process all file names specified in command line
    //
    ProcessNewInputFiles(input_java_file_set, arguments, option.first_file_index);

    //
    // For each input file, copy it into the input_files array and process its package declaration.
    //
    StoragePool *ast_pool = new StoragePool(64); // how much space do we need? estimate 64 tokens.
    FileSymbol **input_files = new FileSymbol*[input_java_file_set.Size() + 1];
    int num_files = 0;
    for (FileSymbol *file_symbol = (FileSymbol *) input_java_file_set.FirstElement();
                     file_symbol;
                     file_symbol = (FileSymbol *) input_java_file_set.NextElement())
    {
        input_files[num_files++] = file_symbol;
        scanner -> Scan(file_symbol);
        if (file_symbol -> lex_stream) // did we have a successful scan!
        {
            AstPackageDeclaration *package_declaration = parser -> PackageHeaderParse(file_symbol -> lex_stream, ast_pool);
            ProcessPackageDeclaration(file_symbol, package_declaration);
            ast_pool -> Reset();
        }
    }

    //
    // 
    //
    FileSymbol *main_file_clone;
    if (num_files > 0)
        main_file_clone = input_files[0] -> Clone();
    else
    {
        //
        // Some name, any name !!! We use dot_name_symbol as a bad file name because
        // no file can be named ".".
        //
        FileSymbol *file_symbol = classpath[dot_classpath_index] -> RootDirectory() -> InsertFileSymbol(dot_name_symbol);
        file_symbol -> directory_symbol = classpath[dot_classpath_index] -> RootDirectory();
        file_symbol -> SetJava();

        main_file_clone = file_symbol -> Clone();
    }

    main_file_clone -> semantic = new Semantic(*this, main_file_clone);
    system_semantic = main_file_clone -> semantic;
    scanner -> SetUp(main_file_clone);

#ifdef WIN32_FILE_SYSTEM
    //
    //
    //
    if (option.BadMainDisk())
    {
        system_semantic -> ReportSemError(SemanticError::NO_CURRENT_DIRECTORY,
                                          0,
                                          0);
    }
#endif

    //
    //
    //
    for (int o = 0; o < option.bad_options.Length(); o++)
    {
        system_semantic -> ReportSemError((SemanticError::SemanticErrorKind) option.bad_options[o] -> kind,
                                          0,
                                          0,
                                          option.bad_options[o] -> name);
    }

    //
    //
    //
    for (int l = 0; l < bad_zip_filenames.Length(); l++)
    {
        system_semantic -> ReportSemError(SemanticError::CANNOT_OPEN_ZIP_FILE,
                                          0,
                                          0,
                                          bad_zip_filenames[l]);
    }

    //
    //
    //
    if (system_package -> directory.Length() == 0)
    {
        system_semantic -> ReportSemError(SemanticError::PACKAGE_NOT_FOUND,
                                          0,
                                          0,
                                          StringConstant::US_java_SL_lang);
    }

    //
    // When the -d option is specified, create the relevant
    // directories if they don't already exist.
    //
    if (option.directory)
    {
        if (! ::SystemIsDirectory(option.directory))
        {
            for (char *ptr = option.directory; *ptr; ptr++)
            {
                char delimiter = *ptr;
                if (delimiter == U_SLASH)
                {
                    *ptr = U_NULL;

                    if (! ::SystemIsDirectory(option.directory))
                        ::SystemMkdir(option.directory);

                    *ptr = delimiter;
                }
            }

            ::SystemMkdir(option.directory);

            if (! ::SystemIsDirectory(option.directory))
            {
                int length = strlen(option.directory);
                wchar_t *name = new wchar_t[length + 1];
                for (int i = 0; i < length; i++)
                    name[i] = option.directory[i];
                name[length] = U_NULL;
                system_semantic -> ReportSemError(SemanticError::CANNOT_OPEN_DIRECTORY,
                                                  0,
                                                  0,
                                                  name);
                delete [] name;
            }
        }
    }

    //
    //
    //
    for (int m = 0; m < bad_input_filenames.Length(); m++)
    {
        system_semantic -> ReportSemError(SemanticError::BAD_INPUT_FILE,
                                          0,
                                          0,
                                          bad_input_filenames[m]);
    }

    //
    //
    //
    for (int n = 0; n < unreadable_input_filenames.Length(); n++)
    {
        system_semantic -> ReportSemError(SemanticError::UNREADABLE_INPUT_FILE,
                                          0,
                                          0,
                                          unreadable_input_filenames[n]);
    }

    //
    //
    //
    if (system_semantic -> NumErrors() > 0)
        system_semantic -> PrintMessages();
    else
    {
        system_semantic -> PrintMessages(); // there might be some warnings we want to print...

        //
        //
        //
        input_java_file_set.SetEmpty();
        for (int i = 0; i < num_files; i++)
        {
            FileSymbol *file_symbol = input_files[i];
            if (! input_java_file_set.IsElement(file_symbol))
                ProcessFile(file_symbol);
        }

        //
        // Clean up all the file that have just been compiled i this new batch.
        //
        for (FileSymbol *file_symbol = (FileSymbol *) input_java_file_set.FirstElement();
                         file_symbol;
                         file_symbol = (FileSymbol *) input_java_file_set.NextElement())
        {
            CleanUp(file_symbol);
        }

        //
        // If more messages were added to system_semantic, print them... 
        //
        system_semantic -> PrintMessages();
        if (system_semantic -> return_code > 0)
            return_code = 1;

        //
        // If the incremental flag is on, check to see if the user wants us to recompile.
        //
        if (option.incremental)
        {
            option.depend = false; // The depend flag should only be in effect in the first pass

            for (bool recompile = IncrementalRecompilation(); recompile; recompile = IncrementalRecompilation())
            {
                return_code = 0; // reset the return code as we may compile clean in this pass
                system_semantic -> return_code = 0;

                //
                //
                //
                for (int m = 0; m < bad_input_filenames.Length(); m++)
                {
                    system_semantic -> ReportSemError(SemanticError::BAD_INPUT_FILE,
                                                      0,
                                                      0,
                                                      bad_input_filenames[m]);
                }

                //
                //
                //
                for (int n = 0; n < unreadable_input_filenames.Length(); n++)
                {
                    system_semantic -> ReportSemError(SemanticError::UNREADABLE_INPUT_FILE,
                                                      0,
                                                      0,
                                                      unreadable_input_filenames[n]);
                }

                FileSymbol *file_symbol;

                num_files = 0;
                delete [] input_files; // delete previous copy 
                input_files = new FileSymbol*[recompilation_file_set.Size()];
                for (file_symbol = (FileSymbol *) recompilation_file_set.FirstElement();
                     file_symbol;
                     file_symbol = (FileSymbol *) recompilation_file_set.NextElement())
                {
                    input_java_file_set.RemoveElement(file_symbol);
                    input_files[num_files++] = file_symbol;

                    LexStream *lex_stream = file_symbol -> lex_stream;
                    if (lex_stream)
                    {
                        AstPackageDeclaration *package_declaration = parser -> PackageHeaderParse(lex_stream, ast_pool);
                        ProcessPackageDeclaration(file_symbol, package_declaration);
                        ast_pool -> Reset();
                    }
                }

                //
                // If a file has been erased, remove it from the input file set.
                //
                for (file_symbol = (FileSymbol *) expired_file_set.FirstElement();
                     file_symbol;
                     file_symbol = (FileSymbol *) expired_file_set.NextElement())
                {
                    input_java_file_set.RemoveElement(file_symbol);
                }

                //
                // Reset the global objects before recompiling this new batch.
                //
                expired_file_set.SetEmpty();
                recompilation_file_set.SetEmpty();
                type_trash_bin.Reset();

                //
                // For each file that should be recompiled, process it if it has not
                // already been dragged in by dependence.
                //
                for (int k = 0; k < num_files; k++)
                {
                    FileSymbol *file_symbol = input_files[k];
                    if (! input_java_file_set.IsElement(file_symbol))
                        ProcessFile(file_symbol);
                }

                //
                // Clean up all the file that have just been compiled i this new batch.
                //
                for (file_symbol = (FileSymbol *) input_java_file_set.FirstElement();
                     file_symbol;
                     file_symbol = (FileSymbol *) input_java_file_set.NextElement())
                {
                    CleanUp(file_symbol);
                }

                //
                // If any system error or warning was detected, print it...
                //
                system_semantic -> PrintMessages();
                if (system_semantic -> return_code > 0)
                    return_code = 1;
            }
        }

        //
        // Are we supposed to generate Makefiles?
        //
        if (option.makefile)
        {
            SymbolSet *candidates = new SymbolSet(input_java_file_set.Size() + input_class_file_set.Size());
            *candidates = input_java_file_set;
            candidates -> Union(input_class_file_set);

            TypeDependenceChecker *dependence_checker = new TypeDependenceChecker((Control *) this, *candidates, type_trash_bin);
            dependence_checker -> PartialOrder();
            dependence_checker -> OutputDependences();
            delete dependence_checker;

            delete candidates;
        }
    }

    delete ast_pool;

    delete main_file_clone; // delete the clone of the main source file...

#ifdef NO_LEAKS
    delete [] input_files;
#endif

    return;
}


Control::~Control()
{
#ifdef NO_LEAKS
    for (int i = 0; i < bad_zip_filenames.Length(); i++)
        delete [] bad_zip_filenames[i];

    for (int j = 0; j < bad_input_filenames.Length(); j++)
        delete [] bad_input_filenames[j];

    for (int k = 0; k < unreadable_input_filenames.Length(); k++)
        delete [] unreadable_input_filenames[k];

    for (int l = 0; l < root_directories.Length(); l++)
        delete root_directories[l];

    delete scanner;
    delete parser;
    delete system_semantic;
#endif

#ifdef TEST
    if (option.debug_dump_lex || option.debug_dump_ast)
    {
        cout << line_count << " source lines read\n\n"
             << class_files_read << " \".class\" files read\n"
             << class_files_written << " \".class\" files written\n" 
             << input_files_processed << " \".java\" files processed\n";
    }
#endif
}


PackageSymbol *Control::ProcessPackage(wchar_t *name)
{
    int name_length = wcslen(name);
    wchar_t *package_name = new wchar_t[name_length];
    int length;
    for (length = 0; length < name_length && name[length] != U_SLASH; length++)
         package_name[length] = name[length];
    NameSymbol *name_symbol = FindOrInsertName(package_name, length);

    PackageSymbol *package_symbol = external_table.FindPackageSymbol(name_symbol);
    if (! package_symbol)
    {
        package_symbol = external_table.InsertPackageSymbol(name_symbol, NULL);
        FindPathsToDirectory(package_symbol);
    }

    while(length < name_length)
    {
        int start = ++length;
        for (int i = 0; length < name_length && name[length] != U_SLASH; i++, length++)
             package_name[i] = name[length];
        name_symbol = FindOrInsertName(package_name, length - start);
        PackageSymbol *subpackage_symbol = package_symbol -> FindPackageSymbol(name_symbol);
        if (! subpackage_symbol)
        {
            subpackage_symbol = package_symbol -> InsertPackageSymbol(name_symbol);
            FindPathsToDirectory(subpackage_symbol);
        }
        package_symbol = subpackage_symbol;
    }

    delete [] package_name;

    return package_symbol;
}


//
// When searching for a subdirectory in a zipped file, it must already be present in the
// hierarchy.
//
DirectorySymbol *Control::FindSubdirectory(PathSymbol *path_symbol, wchar_t *name, int name_length)
{
    wchar_t *directory_name = new wchar_t[name_length + 1];

    DirectorySymbol *directory_symbol = path_symbol -> RootDirectory();
    for (int start = 0, end; directory_symbol && start < name_length; start = end + 1)
    {
        end = start;
        for (int i = 0; end < name_length && name[end] != U_SLASH; i++, end++)
             directory_name[i] = name[end];
        NameSymbol *name_symbol = FindOrInsertName(directory_name, end - start);
        directory_symbol = directory_symbol -> FindDirectorySymbol(name_symbol);
    }

    delete [] directory_name;

    return directory_symbol;
}


//
// When searching for a directory in the system, if it is not already present in the hierarchy
// insert it and attempt to read it from the system...
//
#ifdef UNIX_FILE_SYSTEM
    DirectorySymbol *Control::ProcessSubdirectories(wchar_t *source_name, int source_name_length)
    {
        DirectorySymbol *directory_symbol = classpath[dot_classpath_index] -> RootDirectory();

        int name_length = (source_name_length < 0 ? 0 : source_name_length);
        wchar_t *name = new wchar_t[name_length + 1];
        for (int i = 0; i < name_length; i++)
            name[i] = source_name[i];
        name[name_length] = U_NULL;

        int end;
        for (end = 0; end < name_length && name[end] == U_SLASH; end++) // keep all extra leading '/'
            ;

        //
        //
        //
        wchar_t *directory_name = new wchar_t[name_length];
        int length;
        if (end > 0)
        {
            for (length = 0; length < end; length++)
                directory_name[length] = name[length];
            NameSymbol *name_symbol = FindOrInsertName(directory_name, length);
            DirectorySymbol *subdirectory_symbol = directory_symbol -> FindDirectorySymbol(name_symbol);
            if (! subdirectory_symbol)
                subdirectory_symbol = directory_symbol -> InsertAndReadDirectorySymbol(name_symbol);
            directory_symbol = subdirectory_symbol;
        }

        //
        //
        //
        for (int start = end; start < name_length; start = end)
        {
            for (length = 0; end < name_length && name[end] != U_SLASH; length++, end++)
                directory_name[length] = name[end];

            if (length != 1 || directory_name[0] != U_DOT) // Not the current directory
            {
                if (length == 2 && directory_name[0] == U_DOT && directory_name[1] == U_DOT) // keep the current directory
                {
                    if (directory_symbol -> Identity() == dot_name_symbol || directory_symbol -> Identity() == dot_dot_name_symbol)
                    {
                        DirectorySymbol *subdirectory_symbol = directory_symbol -> FindDirectorySymbol(dot_dot_name_symbol);
                        if (! subdirectory_symbol)
                            subdirectory_symbol = directory_symbol -> InsertAndReadDirectorySymbol(dot_dot_name_symbol);
                        directory_symbol = subdirectory_symbol;
                    }
                    else directory_symbol = directory_symbol -> owner -> DirectoryCast();
                }
                else
                {
                    NameSymbol *name_symbol = FindOrInsertName(directory_name, length);
                    DirectorySymbol *subdirectory_symbol = directory_symbol -> FindDirectorySymbol(name_symbol);
                    if (! subdirectory_symbol)
                        subdirectory_symbol = directory_symbol -> InsertAndReadDirectorySymbol(name_symbol);
                    directory_symbol = subdirectory_symbol;
                }
            }

            for (end++; end < name_length && name[end] == U_SLASH; end++) // skip all extra '/'
                ;
        }

        delete [] directory_name;
        delete [] name;

        return directory_symbol;
    }
#elif defined(WIN32_FILE_SYSTEM)
    DirectorySymbol *Control::ProcessSubdirectories(wchar_t *source_name, int source_name_length)
    {
        DirectorySymbol *directory_symbol = classpath[dot_classpath_index] -> RootDirectory();

        int name_length = (source_name_length < 0 ? 0 : source_name_length);
        wchar_t *name = new wchar_t[name_length + 1];
        char *input_name = new char[name_length + 1];
        for (int i = 0; i < name_length; i++)
            input_name[i] = name[i] = source_name[i];
        input_name[name_length] = name[name_length] = U_NULL;

        if (name_length >= 2 && Case::IsAsciiAlpha(input_name[0]) && input_name[1] == U_COLON) // a disk was specified
        {
            char disk = input_name[0];
            option.SaveCurrentDirectoryOnDisk(disk);
            if (SetCurrentDirectory(input_name))
            {
                DWORD directory_length = GetCurrentDirectory(0, input_name); // first, get the right size
                char *full_directory_name = new char[directory_length + 1];  // allocate the directory
                DWORD length = GetCurrentDirectory(directory_length, full_directory_name);
                if (length <= directory_length)
                {
                    for (char *ptr = full_directory_name; *ptr; ptr++)
                        *ptr = (*ptr != U_BACKSLASH ? *ptr : (char) U_SLASH); // turn '\' to '/'.

                    char *current_directory = option.GetMainCurrentDirectory();
                    int prefix_length = strlen(current_directory);
                    int start = (prefix_length <= length &&
                                 Case::StringSegmentEqual(current_directory, full_directory_name, prefix_length) &&
                                 (full_directory_name[prefix_length] == U_SLASH || full_directory_name[prefix_length] == U_NULL)
                                                ? prefix_length + 1
                                                : 0);

                    if (start > length)
                         name_length = 0;
                    else if (start <= length) // note that we can assert that (start != length)
                    {
                        delete [] name;
                        name_length = length - start;
                        name = new wchar_t[name_length + 1];
                        for (int k = 0, i = start; i < length; i++, k++)
                            name[k] = full_directory_name[i];
                        name[name_length] = U_NULL;
                    }
                }

                delete [] full_directory_name;
            }

            option.ResetCurrentDirectoryOnDisk(disk);  // reset the current directory on this disk
            option.SetMainCurrentDirectory(); // reset the real current directory...
        }

        //
        //
        //
        int end;
        if (name_length > 2 && Case::IsAsciiAlpha(name[0]) && name[1] == U_COLON && name[2] == U_SLASH)
            end = 3;
        else
        {
            for (end = 0; end < name_length && name[end] == U_SLASH; end++) // keep all extra leading '/'
                ;
        }

        //
        //
        //
        wchar_t *directory_name = new wchar_t[name_length];
        int length;
        if (end > 0)
        {
            for (length = 0; length < end; length++)
                directory_name[length] = name[length];
            NameSymbol *name_symbol = FindOrInsertName(directory_name, length);
            DirectorySymbol *subdirectory_symbol = directory_symbol -> FindDirectorySymbol(name_symbol);
            if (! subdirectory_symbol)
                subdirectory_symbol = directory_symbol -> InsertAndReadDirectorySymbol(name_symbol);
            directory_symbol = subdirectory_symbol;
        }

        //
        //
        //
        for (int start = end; start < name_length; start = end)
        {
            for (length = 0; end < name_length && name[end] != U_SLASH; length++, end++)
                directory_name[length] = name[end];

            if (length != 1 || directory_name[0] != U_DOT) // Not the current directory
            {
                if (length == 2 && directory_name[0] == U_DOT && directory_name[1] == U_DOT) // keep the current directory
                {
                    if (directory_symbol -> Identity() == dot_name_symbol || directory_symbol -> Identity() == dot_dot_name_symbol)
                    {
                        DirectorySymbol *subdirectory_symbol = directory_symbol -> FindDirectorySymbol(dot_dot_name_symbol);
                        if (! subdirectory_symbol)
                            subdirectory_symbol = directory_symbol -> InsertAndReadDirectorySymbol(dot_dot_name_symbol);
                        directory_symbol = subdirectory_symbol;
                    }
                    else directory_symbol = directory_symbol -> owner -> DirectoryCast();
                }
                else
                {
                    char *entry_name = new char[length + 1];
                    for (int i = 0; i < length; i++)
                        entry_name[i] = directory_name[i];
                    entry_name[length] = U_NULL;
                    DirectoryEntry *entry = directory_symbol -> FindCaseInsensitiveEntry(entry_name, length);
                    if (entry)
                    {
                        for (int k = 0; k < length; k++)
                            directory_name[k] = entry -> name[k];
                    }
                    delete [] entry_name;

                    NameSymbol *name_symbol = FindOrInsertName(directory_name, length);
                    DirectorySymbol *subdirectory_symbol = directory_symbol -> FindDirectorySymbol(name_symbol);
                    if (! subdirectory_symbol)
                        subdirectory_symbol = directory_symbol -> InsertAndReadDirectorySymbol(name_symbol);
                    directory_symbol = subdirectory_symbol;
                }
            }

            for (end++; end < name_length && name[end] == U_SLASH; end++) // skip all extra '/'
                ;
        }

        delete [] directory_name;
        delete [] name;
        delete [] input_name;

        return directory_symbol;
    }
#endif


void Control::ProcessNewInputFiles(SymbolSet &file_set, ArgumentExpander &arguments, int first_index)
{
    for (int i = 0; i < bad_input_filenames.Length(); i++)
        delete [] bad_input_filenames[i];
    bad_input_filenames.Reset();

    for (int k = 0; k < unreadable_input_filenames.Length(); k++)
        delete [] unreadable_input_filenames[k];
    unreadable_input_filenames.Reset();

    //
    // Process all file names specified in command line
    //
    for (int j = first_index; j < arguments.argc; j++)
    {
        char *file_name = arguments.argv[j];
        int file_name_length = strlen(file_name);

        wchar_t *name = new wchar_t[file_name_length + 1];
        for (int i = 0; i < file_name_length; i++)
            name[i] = (file_name[i] != U_BACKSLASH ? file_name[i] : (wchar_t) U_SLASH); // change backslash to forward slash.
        name[file_name_length] = U_NULL;

        //
        // File must be of the form xxx.java where xxx is a
        // character string consisting of at least one character.
        //
        if (file_name_length < FileSymbol::java_suffix_length ||
            (! FileSymbol::IsJavaSuffix(&file_name[file_name_length - FileSymbol::java_suffix_length])))
            bad_input_filenames.Next() = name;
        else
        {
            FileSymbol *file_symbol = FindOrInsertJavaInputFile(name, file_name_length - FileSymbol::java_suffix_length);

            if (! file_symbol)
                unreadable_input_filenames.Next() = name;
            else
            {
                delete [] name;
                file_set.AddElement(file_symbol);
            }
        }
    }

    return;
}


FileSymbol *Control::FindOrInsertJavaInputFile(DirectorySymbol *directory_symbol, NameSymbol *file_name_symbol)
{
    FileSymbol *file_symbol = NULL;

    int length = file_name_symbol -> Utf8NameLength() + FileSymbol::java_suffix_length;
    char *java_name = new char[length + 1]; // +1 for \0
    strcpy(java_name, file_name_symbol -> Utf8Name());
    strcat(java_name, FileSymbol::java_suffix);

    DirectoryEntry *entry = directory_symbol -> FindEntry(java_name, length);
    if (entry)
    {
        file_symbol = directory_symbol -> FindFileSymbol(file_name_symbol);

        if (! file_symbol)
        {
            file_symbol = directory_symbol -> InsertFileSymbol(file_name_symbol);
            file_symbol -> directory_symbol = directory_symbol;
            file_symbol -> SetJava();
        }

        file_symbol -> mtime = entry -> Mtime();
    }

    delete [] java_name;

    return file_symbol;
}


FileSymbol *Control::FindOrInsertJavaInputFile(wchar_t *name, int name_length)
{
    FileSymbol *file_symbol = NULL;

    //
    // The name has been preprocessed so that if it contains any
    // slashes it is a forward slash. In the loop below we look
    // for the occurrence of the first slash (if any) that separates
    // the file name from its directory name.
    //
    DirectorySymbol *directory_symbol;
    NameSymbol *file_name_symbol;
#ifdef UNIX_FILE_SYSTEM
    int len;
    for (len = name_length - 1; len >= 0 && name[len] != U_SLASH; len--)
        ;
    directory_symbol = ProcessSubdirectories(name, len);
    file_name_symbol = FindOrInsertName(&name[len + 1], name_length - (len + 1));
#elif defined(WIN32_FILE_SYSTEM)
    int len;
    for (len = name_length - 1; len >= 0 && name[len] != U_SLASH && name[len] != U_COLON; len--)
        ;
    directory_symbol = ProcessSubdirectories(name, (name[len] == U_COLON ? len + 1 : len));
    file_name_symbol = FindOrInsertName(&name[len + 1], name_length - (len + 1));
#endif

    for (int i = 1; i < classpath.Length(); i++)
    {
        if (i == dot_classpath_index) // the current directory (.).
        {
            file_symbol = FindOrInsertJavaInputFile(directory_symbol, file_name_symbol);
            if (file_symbol)
                break;
        }
        else if (classpath[i] -> IsZip())
        {
            DirectorySymbol *directory_symbol = FindSubdirectory(classpath[i], name, len);
            if (directory_symbol)
            {
                file_symbol = directory_symbol -> FindFileSymbol(file_name_symbol);
                if (file_symbol && file_symbol -> IsJava())
                     break;
                else file_symbol = NULL;
            }
        }
    }

    //
    // If the file was found, return it; otherwise, in case the (.) directory was not specified in the
    // classpath, search for the file in it...
    //
    return (file_symbol ? file_symbol : FindOrInsertJavaInputFile(directory_symbol, file_name_symbol));
}


PackageSymbol *Control::FindOrInsertPackage(LexStream *lex_stream, AstExpression *name)
{
    PackageSymbol *package;

    AstFieldAccess* field_access = name -> FieldAccessCast();
    if (field_access)
    {
        package = FindOrInsertPackage(lex_stream, field_access -> base);
        NameSymbol *name_symbol = (NameSymbol *) lex_stream -> NameSymbol(field_access -> identifier_token);
        PackageSymbol *subpackage = package -> FindPackageSymbol(name_symbol);
        if (! subpackage)
            subpackage = package -> InsertPackageSymbol(name_symbol);
        package = subpackage;
    }
    else
    {
        AstSimpleName *simple_name = (AstSimpleName *) name;
        NameSymbol *name_symbol = (NameSymbol *) lex_stream -> NameSymbol(simple_name -> identifier_token);
        package = external_table.FindPackageSymbol(name_symbol);
        if (! package)
            package = external_table.InsertPackageSymbol(name_symbol, NULL);
    }

    FindPathsToDirectory(package);

    return package;
}


void Control::ProcessFile(FileSymbol *file_symbol)
{
    ProcessHeaders(file_symbol);

    //
    // As long as there are new bodies, ...
    //
    for (int i = 0; i < needs_body_work.Length(); i++)
    {
assert(semantic.Length() == 0);
        ProcessBodies(needs_body_work[i]);
    }
    needs_body_work.Reset();

    return;
}


void Control::ProcessHeaders(FileSymbol *file_symbol)
{
    input_java_file_set.AddElement(file_symbol);

    bool initial_invocation = (semantic.Length() == 0);

    if (option.verbose)
    {
        cout << "[read ";
        Unicode::Cout(file_symbol -> FileName());
        cout << "]\n";
    }

    if (! file_symbol -> lex_stream)
         scanner -> Scan(file_symbol);
    else file_symbol -> lex_stream -> Reset();

    if (file_symbol -> lex_stream) // do we have a successful scan!
    {
        if (! file_symbol -> compilation_unit)
            file_symbol -> compilation_unit = parser -> HeaderParse(file_symbol -> lex_stream);
        //
        // If we have a compilation unit, analyze it, process its types.
        //
        if (file_symbol -> compilation_unit)
        {
assert(! file_symbol -> semantic);
            if (! file_symbol -> package)
                ProcessPackageDeclaration(file_symbol, file_symbol -> compilation_unit -> package_declaration_opt);
            file_symbol -> semantic = new Semantic(*this, file_symbol);
            semantic.Next() = file_symbol -> semantic;
            file_symbol -> semantic -> ProcessTypeNames();
        }
    }

    if (initial_invocation)
        ProcessMembers();

    return;
}


void Control::ProcessMembers()
{
    Tuple<TypeSymbol *> partially_ordered_types(1024);
    SymbolSet needs_member_work(101);
    TypeCycleChecker cycle_checker(partially_ordered_types);
    TopologicalSort topological_sorter(needs_member_work, partially_ordered_types);

    int start = 0;
    while (start < semantic.Length())
    {
        needs_member_work.SetEmpty();

        do
        {
            //
            // Check whether or not there are cycles in this new batch of types.
            // Create a partial order of the types (cycles are ordered arbitrarily)
            // and place the result in partially_ordered_types.
            //
            cycle_checker.PartialOrder(semantic, start);
            start = semantic.Length(); // next starting point

            // 
            // Process the extends and implements clauses.
            // 
            for (int j = 0; j < partially_ordered_types.Length(); j++)
            {
                TypeSymbol *type = partially_ordered_types[j];
                needs_member_work.AddElement(type);
                type -> ProcessTypeHeaders();
                type -> semantic_environment -> sem -> types_to_be_processed.AddElement(type);
            }
        } while (start < semantic.Length());

        //
        // Patially order the collection of types in needs_member_work and place the result
        // in partially_ordered_types. This reordering is based on the complete "supertype"
        // information computed in ProcessTypeHeaders.
        //
        topological_sorter.Sort();
        for (int i = 0; i < partially_ordered_types.Length(); i++)
        {
            TypeSymbol *type = partially_ordered_types[i];
            needs_body_work.Next() = type;
            type -> ProcessMembers();
        }
    }

    semantic.Reset();

    return;
}



void Control::ProcessBodies(TypeSymbol *type)
{
    Semantic *sem = type -> semantic_environment -> sem;

    if (type -> declaration && (! sem -> compilation_unit -> BadCompilationUnitCast()))
    {
        AstInterfaceDeclaration *interface_declaration = type -> declaration -> InterfaceDeclarationCast();
        AstClassDeclaration *class_declaration = type -> declaration -> ClassDeclarationCast();

#ifdef WIN32_FILE_SYSTEM
        if (! type -> file_symbol -> IsZip())
        {
            int length = type -> Utf8NameLength() + FileSymbol::class_suffix_length;
            char *classfile_name = new char[length + 1]; // +1 for "\0"
            strcpy(classfile_name, type -> Utf8Name());
            strcat(classfile_name, FileSymbol::class_suffix);

            DirectorySymbol *directory = type -> file_symbol -> OutputDirectory();
            DirectoryEntry *entry = directory -> FindCaseInsensitiveEntry(classfile_name, length);

            //
            // If no entry exists for the classfile name, add it as we are about to create
            // such a file. If an entry is found and it is not identical (in a case-sensitive test)
            // to the name of the type, issue an appropriate message.
            //
            if (! entry)
                directory -> InsertEntry(classfile_name, length);
            else if (strcmp(classfile_name, entry -> name) != 0)
            {
                wchar_t *entry_name = new wchar_t[entry -> length + 1];
                for (int i = 0; i < length; i++)
                    entry_name[i] = entry -> name[i];
                entry_name[entry -> length] = U_NULL;
                LexStream::TokenIndex identifier_token = (interface_declaration ? interface_declaration -> identifier_token
                                                                                : class_declaration -> identifier_token);

                sem -> ReportSemError(SemanticError::FILE_FILE_CONFLICT,
                                      identifier_token,
                                      identifier_token,
                                      type -> Name(),
                                      entry_name,
                                      directory -> Name());

                delete [] entry_name;
            }

            delete [] classfile_name;
        }
#endif

        if (interface_declaration)
        {
            if (! parser -> InitializerParse(sem -> lex_stream, interface_declaration))
                sem -> compilation_unit -> kind = Ast::BAD_COMPILATION; // recall that syntax errors were detected
            else
            {
                type -> CompleteSymbolTable();
                if (! parser -> BodyParse(sem -> lex_stream, interface_declaration))
                     sem -> compilation_unit -> kind = Ast::BAD_COMPILATION; // mark that syntax errors were detected
                else type -> ProcessExecutableBodies();
            }
        }
        else
        {
            if (! parser -> InitializerParse(sem -> lex_stream, class_declaration -> class_body))
                sem -> compilation_unit -> kind = Ast::BAD_COMPILATION; // recall that syntax errors were detected
            else
            {
                type -> CompleteSymbolTable();
                if (! parser -> BodyParse(sem -> lex_stream, class_declaration -> class_body))
                     sem -> compilation_unit -> kind = Ast::BAD_COMPILATION; // mark that syntax errors were detected.
                else type -> ProcessExecutableBodies();
            }
        }

        if ((sem -> NumErrors() == 0) && (! sem -> compilation_unit -> BadCompilationUnitCast()))
        {
            Tuple<TypeSymbol *> *types = new Tuple<TypeSymbol *>(1024);
            types -> Next() = type;

            for (int j = 0; j < type -> num_anonymous_types(); j++)
            {
                types -> Next() = type -> AnonymousType(j);
            }

            if (type -> local)
            {
                for (TypeSymbol *local_type = (TypeSymbol *) type -> local -> FirstElement();
                                 local_type;
                                 local_type = (TypeSymbol *) type -> local -> NextElement())
                {
                    types -> Next() = local_type;
                }
            }

            if (type -> non_local)
            {
                for (TypeSymbol *non_local_type = (TypeSymbol *) type -> non_local -> FirstElement();
                                 non_local_type;
                                 non_local_type = (TypeSymbol *) type -> non_local -> NextElement())
                {
                    types -> Next() = non_local_type;
                }
            }

            for (int k = 0; k < types -> Length(); k++)
            {
                TypeSymbol *type = (*types)[k];

                if (option.bytecode)
                {
                    ByteCode *code = new ByteCode(type);
                    if (type -> ACC_INTERFACE())
                         code -> CompileInterface(type);
                    else code -> CompileClass(type);

                    delete code;
                }

                delete type -> semantic_environment;
                type -> semantic_environment = NULL;

                if (type -> ACC_INTERFACE())
                {
                    AstInterfaceDeclaration *interface_declaration = (AstInterfaceDeclaration *) type -> declaration;
                    interface_declaration -> semantic_environment = NULL;
                }
                else
                {
                    AstClassDeclaration *class_declaration = type -> declaration -> ClassDeclarationCast();

                    if (class_declaration)
                        class_declaration -> semantic_environment = NULL;
                }
            }

            delete types;
        }
    }

    sem -> types_to_be_processed.RemoveElement(type);
    if (sem -> types_to_be_processed.Size() == 0) // all types belonging to this compilation unit have been processed.
        CleanUp(sem -> source_file_symbol);

    return;
}


//
// Introduce the main package and the current package.
// This procedure is invoked directly only while doing
// an incremental compilation.
//
void Control::ProcessPackageDeclaration(FileSymbol *file_symbol, AstPackageDeclaration *package_declaration)
{
    file_symbol -> package = (package_declaration
                                     ? FindOrInsertPackage(file_symbol -> lex_stream, package_declaration -> name)
                                     : unnamed_package);

    for (int i = 0; i < file_symbol -> lex_stream -> NumTypes(); i++)
    {
        LexStream::TokenIndex identifier_token = file_symbol -> lex_stream -> Next(file_symbol -> lex_stream -> Type(i));
        if (file_symbol -> lex_stream -> Kind(identifier_token) == TK_Identifier)
        {
            NameSymbol *name_symbol = (NameSymbol *) file_symbol -> lex_stream -> NameSymbol(identifier_token);
            if (! file_symbol -> package -> FindTypeSymbol(name_symbol))
            {
                TypeSymbol *type = file_symbol -> package -> InsertOuterTypeSymbol(name_symbol);
                type -> file_symbol = file_symbol;
                type -> outermost_type = type;
                type -> SetOwner(file_symbol -> package);
                type -> SetSignature(*this);
                type -> MarkSourcePending();

                //
                // If this type is contained in the unnamed package add it to the set
                // unnamed_package_types if a type of similar name was not already there.
                //
                if ((! package_declaration) && (unnamed_package_types.Image(type -> Identity()) == NULL))
                    unnamed_package_types.AddElement(type);
            }
        }
    }

    return;
}


void Control::CleanUp(FileSymbol *file_symbol)
{
    Semantic *sem = file_symbol -> semantic;

    if (sem)
    {
#ifdef TEST
        if (option.debug_dump_lex)
        {
            sem -> lex_stream -> Reset(); // rewind input and ...
            sem -> lex_stream -> Dump();  // dump it!
        }
        if (option.debug_dump_ast)
            sem -> compilation_unit -> Print(*sem -> lex_stream);
#endif
        sem -> PrintMessages();
        if (sem -> return_code > 0)
            return_code = 1;

        file_symbol -> CleanUp();
    }

    return;
}