/*
 *   
 *
 * Copyright  1990-2006 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program 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. 
 * 
 * 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 version 2 for more details (a copy is
 * included at /legal/license.txt). 
 * 
 * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions. 
 */

package util;

import components.*;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.File;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Enumeration;
import java.util.Vector;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipEntry;
import jcc.Const;

/*
 * ClassReader reads classes from a variety of sources
 * and, in all cases, creates ClassInfo structures for them.
 * It can read single class files, mclass files, entire zip
 * files or a member of a zip file.
 */
public class ClassReader {

    int verbosity;
    ConstantPool t;

    public ClassReader( ConstantPool cp, int verb ){
	t = cp;
	verbosity = verb;
    }

    private int
    getMagic( InputStream file ) throws IOException {
	DataInputStream data = new DataInputStream( file );
	int n = 0;
	file.mark( 4 );
	try {
	    n = data.readInt();
	} finally {
	    file.reset();
	}
	return n;
    }

    /*
     * To read the contents of a single, named class or mclass file.
     * The two cases are distinguished by magic number.
     * The return value is the number of classes read.
     * ClassInfo classes for the newly read classes are added
     * to the argument Vector.
     */
    public int
    readFile (String fileName, Vector done) throws IOException
    {
	InputStream infile;
	infile = new BufferedInputStream(new FileInputStream( fileName ) );
	return readStream(fileName, infile, done);

    }

    /*
     * To read the contents of an entire named zip (or Jar) file.
     * Each element that is a class or mclass file is read.
     * Others are silently ignored.
     * The return value is the number of classes read.
     * ClassInfo classes for the newly read classes are added
     * to the argument Vector.
     */
    public int
    readZip (String fileName, Vector done) throws IOException
    { 
	ZipEntry ent;
	int i = 0;
	ZipInputStream zip = 
	    new ZipInputStream(new FileInputStream(fileName));
	while ((ent = zip.getNextEntry()) != null) { 
	    String name = ent.getName();
	    if (!ent.isDirectory() && 
		(name.endsWith(".class") || name.endsWith(".mclass"))) {
		try { 
                    byte buffer[] = readZipEntry(zip);
		    i += readStream(name, new ByteArrayInputStream(buffer), 
				    done);
		} catch (IOException e) { 
		    System.out.println(Localizer.getString("classreader.failed_on", name));
		}
	    }
	}
	return i;
    }

    byte[] readZipEntry(ZipInputStream zip) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buff = new byte[1024];
        int len;

        while (true) {
            len = zip.read(buff, 0, buff.length);
            if (len == -1) {
                break;
            }
            baos.write(buff, 0, len);
        }

        return baos.toByteArray();
    }

    /*
     * To read the contents of a class or mclass file from
     * a given input stream.
     * The two cases are distinguished by magic number.
     * The return value is the number of classes read.
     * ClassInfo classes for the newly read classes are added
     * to the argument Vector.
     * The inputName argument is used only for error messages.
     * This can be used for reading a zip file element or
     * from single opened file.
     */
    public int
    readStream( String inputName, InputStream infile, Vector done ) throws IOException {
	int magicNumber = getMagic( infile );

	int ndone = 0;
	if ( magicNumber == Const.JAVA_MAGIC ){
	    /*
	     * We have a solo class.
	     */
	    ClassFile f = new ClassFile( inputName, infile, verbosity>=2 );
	    if ( verbosity != 0 )
		System.out.println(
		    Localizer.getString("classreader.reading_classfile", 
		                        inputName));
	    if (f.readClassFile( t ) ){
		f.clas.externalize( t );
		done.addElement( f.clas );
		ndone+=1;
	    } else {
		throw new DataFormatException(
		    Localizer.getString("classreader.read_of_class_file", 
		                         inputName));
	    }
	} else {
	    throw new DataFormatException(
		Localizer.getString("classreader.file_has_bad_magic_number", 
				     inputName, Integer.toString(magicNumber)));
	}
	try {
	    infile.close();
	} catch ( Throwable x ){ }
	return ndone;
    }

    /*
     * To read a single, named class.
     * The return value is the number of classes read.
     * ClassInfo classes for the newly read class is added
     * to the argument Vector.
     * The class to be read is found using the ClassFileFinder,
     * which, if successful, will give us an InputStream, either
     * of a file from its search path, or of a zip file element
     * from its search path.
     */
    public int readClass( String classname, ClassFileFinder finder, Vector done ) 
    {
	InputStream f = finder.findClassFile( classname );
	if ( f == null ) return 0;
	try {
	    return readStream( classname, f, done );
	} catch ( IOException e ){
	    e.printStackTrace();
	    return 0;
	}
    }

}
