/*
 *
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

package org.apache.flex.compiler.internal.units;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.apache.flex.abc.ABCConstants;
import org.apache.flex.abc.ABCEmitter;
import org.apache.flex.abc.instructionlist.InstructionList;
import org.apache.flex.abc.semantics.Name;
import org.apache.flex.abc.semantics.Nsset;
import org.apache.flex.compiler.problems.UnresolvedClassReferenceProblem;
import org.apache.commons.io.IOUtils;

import org.apache.flex.compiler.common.DependencyType;
import org.apache.flex.compiler.common.Multiname;
import org.apache.flex.compiler.constants.IASLanguageConstants;
import org.apache.flex.compiler.definitions.IDefinition;
import org.apache.flex.compiler.definitions.INamespaceDefinition;
import org.apache.flex.compiler.definitions.references.INamespaceReference;
import org.apache.flex.compiler.definitions.references.IReference;
import org.apache.flex.compiler.definitions.references.IResolvedQualifiersReference;
import org.apache.flex.compiler.definitions.references.ReferenceFactory;
import org.apache.flex.compiler.internal.abc.ClassGeneratorHelper;
import org.apache.flex.compiler.internal.definitions.ClassDefinition;
import org.apache.flex.compiler.internal.definitions.PackageDefinition;
import org.apache.flex.compiler.internal.embedding.EmbedData;
import org.apache.flex.compiler.internal.projects.CompilerProject;
import org.apache.flex.compiler.internal.projects.DefinitionPriority;
import org.apache.flex.compiler.internal.projects.FlexProject;
import org.apache.flex.compiler.internal.resourcebundles.PropertiesFileParser;
import org.apache.flex.compiler.internal.resourcebundles.ResourceBundleUtils;
import org.apache.flex.compiler.internal.scopes.ASFileScope;
import org.apache.flex.compiler.internal.scopes.PackageScope;
import org.apache.flex.compiler.internal.scopes.TypeScope;
import org.apache.flex.compiler.internal.tree.as.ClassReferenceNode;
import org.apache.flex.compiler.internal.tree.as.EmbedNode;
import org.apache.flex.compiler.internal.tree.as.ExpressionNodeBase;
import org.apache.flex.compiler.internal.tree.as.LiteralNode;
import org.apache.flex.compiler.internal.tree.properties.ResourceBundleEntryNode;
import org.apache.flex.compiler.internal.tree.properties.ResourceBundleFileNode;
import org.apache.flex.compiler.internal.units.requests.ABCBytesRequestResult;
import org.apache.flex.compiler.internal.units.requests.FileScopeRequestResultBase;
import org.apache.flex.compiler.internal.units.requests.SWFTagsRequestResult;
import org.apache.flex.compiler.internal.units.requests.SyntaxTreeRequestResult;
import org.apache.flex.compiler.problems.CodegenInternalProblem;
import org.apache.flex.compiler.problems.ICompilerProblem;
import org.apache.flex.compiler.problems.InternalCompilerProblem;
import org.apache.flex.compiler.scopes.IASScope;
import org.apache.flex.compiler.targets.ITarget.TargetType;
import org.apache.flex.compiler.tree.as.IASNode;
import org.apache.flex.compiler.tree.as.IFileNodeAccumulator;
import org.apache.flex.compiler.units.requests.IABCBytesRequestResult;
import org.apache.flex.compiler.units.requests.IFileScopeRequestResult;
import org.apache.flex.compiler.units.requests.IOutgoingDependenciesRequestResult;
import org.apache.flex.compiler.units.requests.ISWFTagsRequestResult;
import org.apache.flex.compiler.units.requests.ISyntaxTreeRequestResult;
import org.apache.flex.swc.ISWCFileEntry;
import org.apache.flex.utils.FilenameNormalization;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;

/**
 * This is a compilation unit that handles .properties file compilation.
 */
public class ResourceBundleCompilationUnit extends CompilationUnitBase
{
    /**
     * Parent folder name for properties files in a swc. We read properties
     * files off of this folder while reading a swc and copy them into this
     * folder while writing a swc.
     */
    public static final String LOCALE = "locale";

    /**
     * Qualified bundle name for the properties file processed 
     * by this compilation unit.
     */
    private final String bundleNameInColonSyntax;

    /**
     * Locale of the properties file processed by this compilation unit or
     * <code>null</code> if the file is not locale dependent.
     */
    private final String locale;

    /**
     * SWC entry for the properties file processed by this compilation unit if
     * it comes from a swc, <code>null</code> if it doesn't come from a SWC.
     */
    private final ISWCFileEntry fileEntry;

    /**
     * Constructor.
     * 
     * @param project project this compilation unit is associated with
     * @param path path of the properties file
     * @param basePriority base priority
     * @param qname qualified name for the properties file that will be
     * processed by this comp unit
     * @param locale the locale this compilation unit depends on or <code>null</code>
     * if the compilation unit is not locale dependent
     */
    public ResourceBundleCompilationUnit(final CompilerProject project,
                                         final String path,
                                         final DefinitionPriority.BasePriority basePriority,
                                         final String qname, 
                                         final String locale)
    {
        super(project, path, basePriority,
                getQnames(project, qname, locale));
        this.locale = locale;
        this.bundleNameInColonSyntax = ResourceBundleUtils.convertBundleNameToColonSyntax(qname);
        this.fileEntry = null;
    }

    /**
     * Constructor. This constructor should be used for properties files that
     * come from a swc.
     * 
     * @param project project this compilation unit is associated with
     * @param fileEntry swc entry for the properties file that will be processed
     * by this compilation unit
     * @param qname qualified name for the properties file that will be
     * processed by this comp unit
     * @param locale the locale this compilation unit depends on or <code>null</code>
     * if the compilation unit is not locale dependent
     */
    public ResourceBundleCompilationUnit(final CompilerProject project,
                                         final ISWCFileEntry fileEntry,
                                         final String qname, 
                                         final String locale)
    {
        super(project, fileEntry.getContainingSWCPath(), DefinitionPriority.BasePriority.LIBRARY_PATH,
                getQnames(project, qname, locale));
        this.bundleNameInColonSyntax =  ResourceBundleUtils.convertBundleNameToColonSyntax(qname);
        this.fileEntry = fileEntry;
        this.locale = locale;
    }
    
    /**
     * utility to get the qnames promised by this compilation unit

     */
    private static Collection<String> getQnames(CompilerProject project, String qname, String locale)
    {
        //if this comp unit is not locale dependent, then create qnames  for each locale project targets
        Collection<String> locales = (locale == null) ? ((FlexProject)project).getLocales() : Collections.<String>singleton(locale);
        
        
        // For each local we are using, add the qnames promised for that locale
        ArrayList<String> qnames = new ArrayList<String>();
        for(String loc : locales)
        {
            String qualifiedName = ResourceBundleUtils.getQualifiedName(loc, qname); //determine qualified name
            qnames.add(qualifiedName);
        }
        return qnames;
    }

    @Override
    public UnitType getCompilationUnitType()
    {
        return UnitType.RESOURCE_UNIT;
    }
   
    /**
     * Returns the name of the bundle processed by this compilation unit. Bundle
     * name is the qualified name that is used when referencing this bundle in
     * action script. 
     * 
     * For qualified names, "colon syntax" is used such as foo.bar:xyz for 
     * "../foo/bar/xyz.properties" file.
     * 
     * @return the name of the bundle processed by this compilation unit
     */
    public String getBundleNameInColonSyntax()
    {
        return bundleNameInColonSyntax;
    }

    /**
     * Returns the locale of the properties file associated with this
     * compilation unit depends on or <code>null</code> if the file is not
     * locale dependent.
     * 
     * @return the locale this compilation unit depends on or <code>null</code>
     * if the compilation unit is not locale dependent.
     */
    public String getLocale()
    {
        return locale;
    }

    /**
     * Returns the flex project that contains this compilation unit.
     * 
     * @return the flex project.
     */
    private FlexProject getFlexProject()
    {
        return (FlexProject)getProject();
    }

    @Override
    protected ISyntaxTreeRequestResult handleSyntaxTreeRequest() throws InterruptedException
    {
        startProfile(Operation.GET_SYNTAX_TREE);
        try
        {
            getProject().clearScopeCacheForCompilationUnit(this);
            
            final Collection<ICompilerProblem> problems = new LinkedList<ICompilerProblem>();
            
            PropertiesFileParser parser = new PropertiesFileParser(getProject().getWorkspace());
            final ResourceBundleFileNode fileNode = parser.parse(getFileName(), this.locale, getFileReader(problems), problems);

            ASFileScope fileScope = createFileScope(fileNode);
            addScopeToProjectScope(new ASFileScope[] { fileScope });

            return new SyntaxTreeRequestResult(fileNode, ImmutableSet.<String>of(), getRootFileSpecification().getLastModified(), problems);
        }
        finally
        {
            stopProfile(Operation.GET_SYNTAX_TREE);
        }
    }

    @Override
    protected IFileScopeRequestResult handleFileScopeRequest() throws InterruptedException
    {
        startProfile(Operation.GET_FILESCOPE);
        try
        {
            ISyntaxTreeRequestResult syntaxTreeResult = getSyntaxTreeRequest().get();
            final ResourceBundleFileNode rootNode = (ResourceBundleFileNode)syntaxTreeResult.getAST();

            IASScope fileScope = rootNode.getScope();
            assert fileScope instanceof ASFileScope : "Expect ASFileScope as the top-level scope, but found " + fileScope.getClass();

            return new FileScopeRequestResultBase(Collections.<ICompilerProblem> emptyList(), Collections.singleton(fileScope)) {
                
                @Override
                public IDefinition getMainDefinition(String qname)
                {
                    assert qname != null : "Excpect QName.";
                    for (final IDefinition def : definitions)
                    {
                        if (qname.equals(def.getQualifiedName()))
                        {
                            return def;
                        }
                    }
                    return null;
                }
            };
        }
        finally
        {
            stopProfile(Operation.GET_FILESCOPE);
        }
    }

    @Override
    protected IABCBytesRequestResult handleABCBytesRequest() throws InterruptedException
    {
        ISyntaxTreeRequestResult syntaxTreeResult = getSyntaxTreeRequest().get();
        final IASNode rootNode = syntaxTreeResult.getAST();

        startProfile(Operation.GET_ABC_BYTES);
        try
        {
            final Collection<ICompilerProblem> problems = new LinkedList<ICompilerProblem>();
            final ABCEmitter emitter = new ABCEmitter();

            // TODO: hook this up to something in the settings - how do we access settings from here?
            emitter.visit(ABCConstants.VERSION_ABC_MAJOR_FP10, ABCConstants.VERSION_ABC_MINOR_FP10);

            byte[] generatedBytes = null;
            try
            {
                for (IDefinition def : getDefinitionPromises())
                {
                    String qualifiedClassName = def.getQualifiedName();
                    String locale = ResourceBundleUtils.getLocale(qualifiedClassName);

                    generateABCForBundle(emitter, (ResourceBundleFileNode)rootNode, qualifiedClassName, bundleNameInColonSyntax, locale, problems);
                }

                generatedBytes = emitter.emit();
            }
            catch (Exception ex)
            {
                problems.add(new CodegenInternalProblem(rootNode, ex));
            }

            Set<EmbedData> embeds = new HashSet<EmbedData>();
            EmbedCompilationUnitFactory.collectEmbedDatas(getProject(), (IFileNodeAccumulator)rootNode, embeds, problems);

            return new ABCBytesRequestResult(generatedBytes, problems.toArray(new ICompilerProblem[0]), embeds);
        }
        finally
        {
            stopProfile(Operation.GET_ABC_BYTES);
        }
    }

    @Override
    protected ISWFTagsRequestResult handleSWFTagsRequest() throws InterruptedException
    {
        // Fulfill other requests before profiling this request.
        final IABCBytesRequestResult abc = getABCBytesRequest().get();

        startProfile(Operation.GET_SWF_TAGS);
        try
        {
            String tagName = getDefinitionPromises().get(0).getQualifiedName();
            return new SWFTagsRequestResult(abc.getABCBytes(), tagName, abc.getEmbeds());
        }
        finally
        {
            stopProfile(Operation.GET_SWF_TAGS);
        }
    }

    @Override
    protected IOutgoingDependenciesRequestResult handleOutgoingDependenciesRequest () throws InterruptedException
    {
        final ResourceBundleFileNode fileNode = (ResourceBundleFileNode)getSyntaxTreeRequest().get().getAST();

        startProfile(Operation.GET_SEMANTIC_PROBLEMS);
        try
        {
            Collection<ICompilerProblem> problems = new ArrayList<ICompilerProblem>();

            updateEmbedCompilationUnitDependencies(fileNode.getEmbedNodes(), problems);
            
            // Kick off code generation to add all the dependencies found by code generation.
            getABCBytesRequest().get();

            //Add dependency to 'mx.resources.ResourceBundle' since we want it to be picked up.
            FlexProject project = getFlexProject();
            IResolvedQualifiersReference resourceBundleClassRef = ReferenceFactory.packageQualifiedReference(
                    getProject().getWorkspace(), project.getResourceBundleClass());
            resourceBundleClassRef.resolve(project, this, DependencyType.INHERITANCE);

            return new IOutgoingDependenciesRequestResult()
            {
                @Override
                public ICompilerProblem[] getProblems()
                {
                    return IOutgoingDependenciesRequestResult.NO_PROBLEMS;
                }
            };
        }
        finally
        {
            stopProfile(Operation.GET_SEMANTIC_PROBLEMS);
        }
    }

    @Override
    public void waitForBuildFinish(final Collection<ICompilerProblem> problems, TargetType targetType) throws InterruptedException
    {
        assert problems != null : "Expected 'problems'. Do not ignore problems.";
        Collections.addAll(problems, getSyntaxTreeRequest().get().getProblems());
        Collections.addAll(problems, getFileScopeRequest().get().getProblems());
        Collections.addAll(problems, getOutgoingDependenciesRequest().get().getProblems());

        //Properties files doesn't get compiled down to abc while generating a swc. 
        //Therefore, do the following steps for only swf case.
        if (TargetType.SWF.equals(targetType))
        {
            Collections.addAll(problems, getABCBytesRequest().get().getProblems());
            Collections.addAll(problems, getSWFTagsRequest().get().getProblems());
        }
    }

    @Override
    public void startBuildAsync(TargetType targetType)
    {
        getSyntaxTreeRequest();
        getFileScopeRequest();
        getOutgoingDependenciesRequest();

        //Properties files doesn't get compiled down to abc while generating a swc. 
        //Therefore, do the following steps for only swf case.
        if (TargetType.SWF.equals(targetType))
        {
            getABCBytesRequest();
            getSWFTagsRequest();
        }
    }

    /**
     * Get the time-stamp of the properties file processed by this compilation
     * unit.
     * 
     * @return time stamp in milliseconds from epoch time.
     */
    public long getFileLastModified()
    {
        if (fileEntry != null)
            return fileEntry.getLastModified();

        return getRootFileSpecification().getLastModified();
    }

    /**
     * Returns the content of the file processed by this compilation unit.
     * 
     * @return byte array that represents the content or <code>null</code> if
     * any problem occurs.
     */
    public byte[] getFileContent(final Collection<ICompilerProblem> problems)
    {
        Reader reader = null;
        try
        {
            reader = getFileReader(problems);

            return IOUtils.toByteArray(reader);
        }
        catch (IOException ex)
        {
            problems.add(new InternalCompilerProblem(ex));
        }
        finally
        {
            if (reader != null)
            {
                try
                {
                    reader.close();
                }
                catch (IOException ex)
                {
                    //ignore
                }
            }
        }

        return null;
    }

    /**
     * Returns the file name that should be dispayed with errors message generated from errors in the
     * properties file.
     * 
     * @return File name as a string.
     */
    private String getFileName()
    {
        if (fileEntry != null)
            return FilenameNormalization.normalize(getAbsoluteFilename() + ":" + fileEntry.getPath());
        return getAbsoluteFilename();
    }
    
    /**
     * Returns the {@link Reader} for the contents of file that is processed by
     * this compilation unit.
     * 
     * @return file reader or <code>null</code> if any problem occurs.
     */
    private Reader getFileReader(final Collection<ICompilerProblem> problems)
    {
        try
        {
            if (fileEntry != null)
            {
                InputStream in = fileEntry.createInputStream();
                if (in != null)
                {
                    return new InputStreamReader(in, "UTF-8");
                }
            }
            else
            {
                return getRootFileSpecification().createReader();
            }
        }
        catch (IOException ex)
        {
            problems.add(new InternalCompilerProblem(ex));
        }

        return null;
    }

    /**
     * Create the file scope for this compilation unit.
     * 
     * @param fileNode file node of this compilation unit
     * @return root file scope for this compilation unit
     */
    private ASFileScope createFileScope(final ResourceBundleFileNode fileNode)
    {
        List<IDefinition> definitions = getDefinitionPromises(); 
        ASFileScope fileScope = new ASFileScope(fileNode);

        String packageName = Multiname.getPackageNameForQName(definitions.get(0).getQualifiedName());
        PackageScope packageScope = new PackageScope(fileScope, packageName);
        packageScope.setContainingScope(fileScope);

        PackageDefinition packageDefinition = new PackageDefinition(packageName);
        packageDefinition.setContainedScope(packageScope);

        fileScope.addDefinition(packageDefinition);
        
        for(IDefinition def : definitions)
        {
            Multiname mname = Multiname.crackDottedQName(getProject(), def.getQualifiedName());
            INamespaceDefinition packageNS = Iterables.getOnlyElement(mname.getNamespaceSet());

            ClassDefinition classDefinition = new ClassDefinition(mname.getBaseName(), (INamespaceReference)packageNS);
            IReference baseClass = ReferenceFactory.packageQualifiedReference(getProject().getWorkspace(), getFlexProject().getResourceBundleClass());
            classDefinition.setBaseClassReference(baseClass);
            classDefinition.setExcludedClass();

            TypeScope classScope = new TypeScope(packageScope, classDefinition);
            classScope.setContainingDefinition(classDefinition);
            classDefinition.setContainedScope(classScope);
            classDefinition.setupThisAndSuper();

            packageScope.addDefinition(classDefinition);
        }

        return fileScope;
    }

    /**
     * Generates abc for this compilation unit and the specified locale. AS
     * equivalent of a generated class looks like this: 
     * 
     * ----------------
     * package mypackage 
     * { 
     *      import mx.resources.ResourceBundle; 
     *      
     *      public class en_US$myfile_properties extends ResourceBundle 
     *      { 
     *          public function en_US$myfile_properties() { 
     *              super("en_US", "myfile"); 
     *          } 
     *          
     *          override protected function getContent():Object { 
     *              return {
     *                      "name": "Falcon",
     *                      "version": "1.0", 
     *                      "motto": "Awesome '{0}' ever.", 
     *                      "classref": org.apache.flex.foo 
     *                      "embededAsset" : embed_properties_awesome_jpg_1808423157
     *              }; 
     *          }
     *      }
     * }
     * --------------
     * 
     * @param emitter emitter object to use to generate abc code
     * @param fileNode file node of this compilation unit
     * @param qualifiedClassName qualified name of the class to generate abc for
     * @param bundleName name of the properties file processed by this compilation unit
     * @param locale locale of the properties file
     * @param problems problems collection that is used to collect problems
     */
    private void generateABCForBundle(final ABCEmitter emitter, final ResourceBundleFileNode fileNode,
            final String qualifiedClassName, final String bundleName, final String locale,
            final Collection<ICompilerProblem> problems)
    {

        FlexProject project = getFlexProject();

        //this class extends "mx.resources.ResourceBundle"
        IResolvedQualifiersReference resourceBundleReference = ReferenceFactory.packageQualifiedReference(
                project.getWorkspace(), project.getResourceBundleClass());

        //Create constructor instruction list
        InstructionList constructorInstructionList = new InstructionList();
        constructorInstructionList.addInstruction(ABCConstants.OP_getlocal0);
        constructorInstructionList.addInstruction(ABCConstants.OP_pushstring, locale);
        constructorInstructionList.addInstruction(ABCConstants.OP_pushstring, bundleName);
        constructorInstructionList.addInstruction(ABCConstants.OP_constructsuper, 2);
        constructorInstructionList.addInstruction(ABCConstants.OP_returnvoid);

        
        IResolvedQualifiersReference mainClassRef = ReferenceFactory.packageQualifiedReference(
                project.getWorkspace(), qualifiedClassName);
        
        ClassGeneratorHelper classGen = new ClassGeneratorHelper(project, emitter,
                mainClassRef.getMName(),
                (ClassDefinition)resourceBundleReference.resolve(project),
                Collections.<Name> emptyList(), Collections.<Name> emptyList(),
                constructorInstructionList, true);

        //Create method body for getContents
        InstructionList bodyInstructionList = new InstructionList();
        bodyInstructionList.addInstruction(ABCConstants.OP_getlocal0);
        bodyInstructionList.addInstruction(ABCConstants.OP_pushscope);

        //Create key value pair entries "key":"value"
        int entryCount = 0;
        for (int i = 0; i < fileNode.getChildCount(); i++)
        {
            IASNode node = fileNode.getChild(i);
            if (node instanceof ResourceBundleEntryNode)
            {
                entryCount++;
                ResourceBundleEntryNode entryNode = (ResourceBundleEntryNode)node;

                //push key
                bodyInstructionList.addInstruction(ABCConstants.OP_pushstring, entryNode.getKeyNode().getValue());

                //push value
                ExpressionNodeBase valueNode = entryNode.getValueNode();
                switch (valueNode.getNodeID())
                {

                    case LiteralStringID:
                        bodyInstructionList.addInstruction(ABCConstants.OP_pushstring,
                                ((LiteralNode)valueNode).getValue());
                        break;

                    case ClassReferenceID:
                        ClassReferenceNode crn = (ClassReferenceNode)valueNode;
                        if (crn.getName() != null)
                        {

                            IResolvedQualifiersReference refClass = ReferenceFactory.packageQualifiedReference(project.getWorkspace(), crn.getName());

                            if (refClass.resolve(project, crn.getASScope(), DependencyType.EXPRESSION, true) == null)
                            {
                                ICompilerProblem problem = new UnresolvedClassReferenceProblem(crn, crn.getName());
                                problems.add(problem);
                            }
                        }
                        String className = crn.getName();
                        if(className == null) 
                        {
                            bodyInstructionList.addInstruction(ABCConstants.OP_pushnull);
                        } 
                        else 
                        {
                            IResolvedQualifiersReference classRef = ReferenceFactory.packageQualifiedReference(
                                    project.getWorkspace(), className);

                            bodyInstructionList.addInstruction(ABCConstants.OP_getlex, classRef.getMName());
                        }
                        
                        break;

                    case EmbedID:
                        EmbedNode embedNode = (EmbedNode)valueNode;
                        try
                        {
                            String name = embedNode.getName(project, problems);
                            IResolvedQualifiersReference embedClassRef = ReferenceFactory.packageQualifiedReference(
                                    project.getWorkspace(), name);
                            bodyInstructionList.addInstruction(ABCConstants.OP_getlex, embedClassRef.getMName());
                        }
                        catch (InterruptedException ex)
                        {
                            problems.add(new CodegenInternalProblem(embedNode, ex));
                        }
                        break;

                    default:
                        //This shouldn't happen. Should we handle this case by collecting a problem? 
                }
            }
        }

        bodyInstructionList.addInstruction(ABCConstants.OP_newobject, entryCount);
        bodyInstructionList.addInstruction(ABCConstants.OP_returnvalue);

        Name getContentsMethodName = new Name(ABCConstants.CONSTANT_Qname,
                new Nsset(classGen.getProtectedNamespace()), "getContent");

        //Create getContents method
        classGen.addITraitsMethod(getContentsMethodName, Collections.<Name> emptyList(),
                new Name(IASLanguageConstants.Object), Collections.<Object> emptyList(),
                false, false, true, bodyInstructionList);

        classGen.finishScript();
    }

}
