/*******************************************************************************

* Copyright (c) 2006 IONA Technologies PLC

* All rights reserved. This program and the accompanying materials

* are made available under the terms of the Eclipse Public License v1.0

* which accompanies this distribution, and is available at

* http://www.eclipse.org/legal/epl-v10.html

* 

* Contributors:

*     IONA Technologies PLC - initial API and implementation

*******************************************************************************/
package org.eclipse.stp.sc.xmlvalidator.classbuilder;

import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Modifier;

import org.eclipse.stp.sc.xmlvalidator.classbuilder.inst.JInstruction;

/**
 * @author jma
 * This builder is used to generate java class in bytecode format
 * it contains a bytecode template library to mapping rule expression to java language.
 * There are some existing open source byte code generation libraries. But we are re-inventing the 
 * wheel here because:
 *  <Li> This package contains byte code libraries for xpath. Our validation rules are based on that</Li>
 *  <Li> The main complex for byte code generation are for those instruction part. The rest parts in class file
 *  are easy to deal with.</Li>
 */
public class ClassBuilder {
	
	static boolean DUMP_CLASS_FLAG = System.getProperty("dump.class") != null
	                      && System.getProperty("dump.class").equals("true");
	
	static String DUMP_CLASS_DIR = System.getProperty("dump.dir");
	static String CLASS_EXT = ".class";
	static String DEF_EXT = ".def";
	
	public JClass createClass(String clsName) {
		JClass cls = new JClass(clsName);
		return cls;
	}
	
	public JClass createInterface(String clsName) {
		JClass cls = new JClass(clsName, Modifier.PUBLIC | Modifier.INTERFACE);
		return cls;
	}
	
	/**
	 * generate the the ByteCode of the user defined class
	 * @param cls
	 * @return
	 */
	public byte[] build(JClass cls) {
		JByteCodeVisitor visitor = new JByteCodeVisitor();
        try {
            cls.accept(visitor);
            byte[] clsBytes = visitor.classOS.toByteArray();
            if (DUMP_CLASS_FLAG) {
            	String path = ".";
            	if (DUMP_CLASS_DIR != null && !DUMP_CLASS_DIR.equals("")) {
            		path = DUMP_CLASS_DIR;
            	}
            	String fileName = path + File.separator + cls.getName();
                //write class definition
                FileOutputStream dedout = new FileOutputStream(fileName + DEF_EXT);
                dedout.write(cls.toString().getBytes());
                dedout.close();
                //write class byte code
                FileOutputStream clsout = new FileOutputStream(fileName + CLASS_EXT);
                clsout.write(clsBytes);
                clsout.close();
            }
            return clsBytes;
        } catch (Exception e) {
        	e.printStackTrace();
        }
	    return null;	
	}
	
	
	
	
    public static void main(String[] args) {
    	ClassBuilder builder = new ClassBuilder();
        //JClass helloCls = builder.createInterface("Hello");
        JClass helloCls = builder.createClass("Hello");
        JCodeFactory factory = new JCodeFactory(helloCls.pool);

        
        JMethod con = new JMethod("<init>");
        
        con.addCode(factory.createLoadThis());
        con.addCode(factory.createInvoke(java.lang.Object.class.getName(), con));
        con.addCode(factory.createCodeStep(JInstruction.RETURN));
        
        helloCls.addMethod(con);
        

        JMethod sayHi = new JMethod("sayHi");
        sayHi.addParameter(new JParam("value", String.class));
        sayHi.setReturnType(void.class);
        sayHi.addException(Exception.class);

        //codestep for test
        System.out.println("Hello Johnson");
        
        
        JField system_out_field = new JField("out", java.io.PrintStream.class);
        
        system_out_field.setModifiers(Modifier.STATIC | Modifier.PUBLIC);
        
        sayHi.addCode(factory.createGetStaticField("java.lang.System", system_out_field));
        sayHi.addCode(factory.createLoadConst("Hello, Johnson"));
        JMethod println_method = new JMethod("println");
        println_method.addParameter(new JParam("", String.class));
        println_method.setReturnType(void.class);
        sayHi.addCode(factory.createInvokeVtl(java.io.PrintStream.class.getName(), println_method));
        sayHi.addCode(factory.createCodeStep(JInstruction.RETURN));
         
 
        helloCls.addMethod(sayHi);
        //JField field1 = new JField("id", int.class);
        //helloCls.addField(field1);
        /*
        0:   new     #5; //class HelloWorld
        3:   dup
        4:   invokespecial   #6; //Method "<init>":()V
        7:   astore_1
        8:   aload_1
        9:   ldc     #7; //String
        11:  invokevirtual   #8; //Method sayHi:(Ljava/lang/String;)V
        14:  return
        */
        JMethod mainMethod = new JMethod("main");
        mainMethod.setModifiers(Modifier.STATIC | Modifier.PUBLIC);
        JParam mainParam = new JParam("args", String[].class);
        mainMethod.parameters.add(mainParam);
        mainMethod.setReturnType(void.class);
        mainMethod.addCodes(factory.createNew("Hello"));
        mainMethod.addCode(factory.createALoad(1));
        mainMethod.addCode(factory.createLoadConst(""));
        
        mainMethod.addCode(factory.createInvokeVtl("Hello", sayHi));
        mainMethod.addCode(factory.createCodeStep(JInstruction.RETURN));
        
        helloCls.addMethod(mainMethod);
        
        builder.build(helloCls);
        
    }
}
