/*
 * Copyright (c) 1994, 2004, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.tools.javac;

import sun.tools.java.*;
import sun.tools.tree.*;

import java.io.IOException;
import java.io.InputStream;
import java.util.Vector;
import java.util.Enumeration;

/**
 * Batch file parser, this needs more work.
 *
 * WARNING: The contents of this source file are not part of any
 * supported API.  Code that depends on them does so at its own risk:
 * they are subject to change or removal without notice.
 */
@Deprecated
public
class BatchParser extends Parser {
    /**
     * The current package
     */
    protected Identifier pkg;

    /**
     * The current imports
     */
    protected Imports imports;

    /**
     * The classes defined in this file
     */
    protected Vector<SourceClass> classes;


    /**
     * The current class
     */
    protected SourceClass sourceClass;

    /**
     * The toplevel environment
     */
    protected Environment toplevelEnv;

    /**
     * Create a batch file parser
     */
    public BatchParser(Environment env, InputStream in) throws IOException {
        super(env, in);

        imports = new Imports(env);
        classes = new Vector<>();
        toplevelEnv = imports.newEnvironment(env);
    }

    /**
     * Package declaration
     */
    public void packageDeclaration(long where, IdentifierToken t) {
        Identifier nm = t.getName();
        //System.out.println("package " + nm);
        if (pkg == null) {
            // This code has been changed to pass an IdentifierToken,
            // rather than an Identifier, to setCurrentPackage().  Imports
            // now needs the location of the token.
            pkg = t.getName();
            imports.setCurrentPackage(t);
        } else {
            env.error(where, "package.repeated");
        }
    }

    /**
     * Import class
     */
    public void importClass(long pos, IdentifierToken t) {
        //System.out.println("import class " + t);
        imports.addClass(t);
    }

    /**
     * Import package
     */
    public void importPackage(long pos, IdentifierToken t) {
        //System.out.println("import package " + t);
        imports.addPackage(t);
    }

    /**
     * Define class
     */
    public ClassDefinition beginClass(long where, String doc, int mod,
                                      IdentifierToken t,
                                      IdentifierToken sup,
                                      IdentifierToken interfaces[]) {

        // If this class is nested, the modifier bits set here will
        // be copied into the 'SourceMember' object for the inner class
        // created during the call to 'makeClassDefinition' below.
        // When writing the class file, we will look there for the
        // 'untransformed' modifiers.  The modifiers in the ClassDefinition
        // object will end up as the 'transformed' modifiers.  Note that
        // there are some bits set here that are not legal class modifiers
        // according to the JVMS, e.g., M_PRIVATE and M_STATIC.  These are
        // masked off while writing the class file, but are preserved in
        // the InnerClasses attributes.

        if (tracing) toplevelEnv.dtEnter("beginClass: " + sourceClass);

        SourceClass outerClass = sourceClass;

        if (outerClass == null && pkg != null) {
            t = new IdentifierToken(t.getWhere(),
                                    Identifier.lookup(pkg, t.getName()));
        }

        // The defaults for anonymous and local classes should be documented!

        if ((mod & M_ANONYMOUS) != 0) {
            mod |= (M_FINAL | M_PRIVATE);
        }
        if ((mod & M_LOCAL) != 0) {
            mod |= M_PRIVATE;
        }

        // Certain modifiers are implied as follows:
        //
        // 1.  Any interface (nested or not) is implicitly deemed to be abstract,
        //     whether it is explicitly marked so or not.  (Java 1.0.)
        // 2.  A interface which is a member of a type is implicitly deemed to
        //     be static, whether it is explicitly marked so or not.  (InnerClasses)
        // 3a. A type which is a member of an interface is implicitly deemed
        //     to be public, whether it is explicitly marked so or not. (InnerClasses)
        // 3b. A type which is a member of an interface is implicitly deemed
        //     to be static, whether it is explicitly marked so or not. (InnerClasses)

        if ((mod & M_INTERFACE) != 0) {
            // Rule 1.
            mod |= M_ABSTRACT;
            if (outerClass != null) {
                // Rule 2.
                mod |= M_STATIC;
            }
        }

        if (outerClass != null && outerClass.isInterface()) {
            // Rule 3a.
            // For interface members, neither 'private' nor 'protected'
            // are legal modifiers.  We avoid setting M_PUBLIC in some
            // cases in order to avoid interfering with error detection
            // and reporting.  This is patched up, after reporting an
            // error, by 'SourceClass.addMember'.
            if ((mod & (M_PRIVATE | M_PROTECTED)) == 0)
                mod |= M_PUBLIC;
            // Rule 3b.
            mod |= M_STATIC;
        }

        // For nested classes, we must transform 'protected' to 'public'
        // and 'private' to package scope.  This must be done later,
        // because any modifiers set here will be copied into the
        // 'MemberDefinition' for the nested class, which must represent
        // the original untransformed modifiers.  Also, compile-time
        // checks should be performed against the actual, untransformed
        // modifiers.  This is in contrast to transformations that implement
        // implicit modifiers, such as M_STATIC and M_FINAL for fields
        // of interfaces.

        sourceClass = (SourceClass)
            toplevelEnv.makeClassDefinition(toplevelEnv, where, t,
                                            doc, mod, sup,
                                            interfaces, outerClass);

        sourceClass.getClassDeclaration().setDefinition(sourceClass, CS_PARSED);
        env = new Environment(toplevelEnv, sourceClass);

        if (tracing) toplevelEnv.dtEvent("beginClass: SETTING UP DEPENDENCIES");

        // The code which adds artificial dependencies between
        // classes in the same source file has been moved to
        // BatchEnvironment#parseFile().

        if (tracing) toplevelEnv.dtEvent("beginClass: ADDING TO CLASS LIST");

        classes.addElement(sourceClass);

        if (tracing) toplevelEnv.dtExit("beginClass: " + sourceClass);

        return sourceClass;
    }

    /**
     * Report the current class under construction.
     */
    public ClassDefinition getCurrentClass() {
        return sourceClass;
    }

    /**
     * End class
     */
    public void endClass(long where, ClassDefinition c) {

        if (tracing) toplevelEnv.dtEnter("endClass: " + sourceClass);

        // c == sourceClass; don't bother to check
        sourceClass.setEndPosition(where);
        SourceClass outerClass = (SourceClass) sourceClass.getOuterClass();
        sourceClass = outerClass;
        env = toplevelEnv;
        if (sourceClass != null)
            env = new Environment(env, sourceClass);

        if (tracing) toplevelEnv.dtExit("endClass: " + sourceClass);
    }

    /**
     * Define a method
     */
    public void defineField(long where, ClassDefinition c,
                            String doc, int mod, Type t,
                            IdentifierToken name, IdentifierToken args[],
                            IdentifierToken exp[], Node val) {
        // c == sourceClass; don't bother to check
        Identifier nm = name.getName();
        // Members that are nested classes are not created with 'defineField',
        // so these transformations do not apply to them.  See 'beginClass' above.
        if (sourceClass.isInterface()) {
            // Members of interfaces are implicitly public.
            if ((mod & (M_PRIVATE | M_PROTECTED)) == 0)
                // For interface members, neither 'private' nor 'protected'
                // are legal modifiers.  Avoid setting M_PUBLIC in some cases
                // to avoid interfering with later error detection.  This will
                // be fixed up after the error is reported.
                mod |= M_PUBLIC;
            // Methods of interfaces are implicitly abstract.
            // Fields of interfaces are implicitly static and final.
            if (t.isType(TC_METHOD)) {
                mod |= M_ABSTRACT;
            } else {
                mod |= M_STATIC | M_FINAL;
            }
        }
        if (nm.equals(idInit)) {
            // The parser reports "idInit" when in reality it has found
            // that there is no method name at all present.
            // So, decide if it's really a constructor, or a syntax error.
            Type rt = t.getReturnType();
            Identifier retname = !rt.isType(TC_CLASS) ? idStar /*no match*/
                                                      : rt.getClassName();
            Identifier clsname = sourceClass.getLocalName();
            if (clsname.equals(retname)) {
                t = Type.tMethod(Type.tVoid, t.getArgumentTypes());
            } else if (clsname.equals(retname.getFlatName().getName())) {
                // It appears to be a constructor with spurious qualification.
                t = Type.tMethod(Type.tVoid, t.getArgumentTypes());
                env.error(where, "invalid.method.decl.qual");
            } else if (retname.isQualified() || retname.equals(idStar)) {
                // It appears to be a type name with no method name.
                env.error(where, "invalid.method.decl.name");
                return;
            } else {
                // We assume the type name is missing, even though the
                // simple name that's present might have been intended
                // to be a type:  "String (){}" vs. "toString(){}".
                env.error(where, "invalid.method.decl");
                return;
            }
        }

        if (args == null && t.isType(TC_METHOD)) {
            args = new IdentifierToken[0];
        }

        if (exp == null && t.isType(TC_METHOD)) {
            exp = new IdentifierToken[0];
        }

        MemberDefinition f = env.makeMemberDefinition(env, where, sourceClass,
                                                    doc, mod, t, nm,
                                                    args, exp, val);
        if (env.dump()) {
            f.print(System.out);
        }
    }
}
