/* * Copyright 2007 Wishnu Prasetya. * * This file is part of T2. * T2 is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License (GPL) as published by the * Free Software Foundation; either version 3 of the License, or any * later version. * * T2 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 * for more details. * * A copy of the GNU General Public License can be found in T2 distribution. * If it is missing, see http://www.gnu.org/licenses. */ package Sequenic.T2.Seq; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.List; import Sequenic.T2.Pool; import Sequenic.T2.Engines.Util; import Sequenic.T2.export.Visitor; /** * An object of this class is a meta representation of a test-step in T2. * When executed it will e.g. update a specified field, or call a method. */ public abstract class TraceStep extends METARUN { /** * Obtain the name of the step, as far as this makes sense. */ public abstract String getName(); /** * Execute this trace step, and produce reports as well if reporters is * not null. */ public abstract ExecResult exec(Class CUT, Pool pool, Object targetObj, int stepNumber, List classinvs, ReportersPool reporters ); /** * As the other exec, but without reporting. */ public ExecResult exec(Class CUT, Pool pool, Object targetObj, int stepNumber, List classinvs ) { ExecResult res = exec(CUT, pool, targetObj, stepNumber, classinvs, ReportersPool.NULLreporter); return res; } public void visit(Visitor visitor) { visitor.visit(this); } /* =========================== * FOR SERIALIZATION * =========================== */ public abstract void write(ObjectOutput out) throws IOException; /** * This class can serialize UPDATE_FIELD and CALL_METHOD. * It can also represent an Obsolet version of these TraceSteps. * * Also see the resolve method. * * @author Jeiel Schalkwijk * */ static final class ObsoleteTraceStep extends TraceStep { //method parameters private final MkValStep[] params; //Either the values for the field, or the method receiver. Null if static method. private final MkValStep val; // For field: [className, fieldName] // For method: [className, methodName] ++ [parameter types] private final StringList types; private final int typeCode; //typeCodes private static final int FIELD = 0; private static final int STATIC_METHOD = 1; private static final int METHOD = 2; ObsoleteTraceStep(StringList types, MkValStep val) { this.types = types; this.val = val; this.params = null; typeCode = FIELD; } ObsoleteTraceStep(StringList types, MkValStep[] params, MkValStep receiver) { this.types = types; this.val = receiver; this.params = params; typeCode = receiver == null ? STATIC_METHOD : METHOD; //Sanity check: if(types.getStrings().length != params.length + 2) throw new IllegalArgumentException( "Invalid CALL_METHOD, params.length != paramTypes.length"); } @Override public boolean isObsolete() { return true; } /** If the TraceStep (and UPDATE_FIELD or CALL_METHOD) is not obsolete, it is returned. * Otherwise, this is returned. * * @return */ TraceStep resolve() { try { if(val != null && val.isObsolete()) return this; String[] types = this.types.getStrings(); Class C = Util.classOf(types[0]); if(typeCode==FIELD) { //UPDATE_FIELD case Field field = Util.getFieldNoNull(C, types[1]); return new UPDATE_FIELD(field, val); } else { //CALL_METHOD case for(MkValStep m : params) if(m.isObsolete()) return this; Class[] paramTypes_ = new Class[params.length]; for (int i = 0; i < paramTypes_.length; i++) { paramTypes_[i] = Util.classOf(types[i+2]); } Method method = Util.getMethodNoNull(C, types[1], paramTypes_); return new CALL_METHOD(method, val, params); } } catch (ClassNotFoundException e) { } catch (NoSuchFieldException e) { } catch (NoSuchMethodException e) { } //Either a class, method or field does not exist anymore. //this Trace step is obsolete. return this; } @Override public String toString() { if(typeCode == FIELD) return "Serialized, obsolete UPDATE_FIELD: "; return "Serialized, obsolete CALL_METHOD: "; } @Override public ExecResult exec(Class CUT, Pool pool, Object targetObj, int stepNumber, List classinvs, ReportersPool reporters) { throw new UnsupportedOperationException("Obsolete Trace step can not be executed"); } @Override public String getName() { return types.getStrings()[1]; } public void write(ObjectOutput out) throws IOException { //Commong for all three types out.write(typeCode); out.writeObject(types); if(typeCode==FIELD) { //Serialize an UPDATE_FIELD by writing val. Externalize_MkValStep.write(val, out); } else { //Serialize a CALL_METHOD //If the method is not static, write out the receiver if(typeCode==METHOD) Externalize_MkValStep.write(val, out); //Write out the parameters of the method. for(int i = 0; i < params.length; i++) Externalize_MkValStep.write(params[i],out); } } } public static TraceStep read(ObjectInput in) throws IOException, ClassNotFoundException { //Common steps int typeCode = in.readByte(); StringList types = (StringList) in.readObject(); //UPDATE_FIELD case if(typeCode==ObsoleteTraceStep.FIELD) return new ObsoleteTraceStep(types, Externalize_MkValStep.read(in)).resolve(); //CALL_METHOD case //receiver is null if the method is static MkValStep receiver = typeCode==ObsoleteTraceStep.METHOD ? Externalize_MkValStep.read(in) : null; MkValStep[] params = new MkValStep[types.getStrings().length-2]; for(int i = 0; i