//
// Copyright (C) 2006 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA). All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3. The NOSA has been approved by the Open Source
// Initiative. See the file NOSA-1.3-JPF at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//
package gov.nasa.jpf.util;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import cmu.conditional.One;
import de.fosd.typechef.featureexpr.FeatureExpr;
import de.fosd.typechef.featureexpr.FeatureExprFactory;
import gov.nasa.jpf.JPFException;
import gov.nasa.jpf.vm.ClassInfo;
import gov.nasa.jpf.vm.ClinitRequired;
import gov.nasa.jpf.vm.ElementInfo;
import gov.nasa.jpf.vm.FieldInfo;
import gov.nasa.jpf.vm.Fields;
import gov.nasa.jpf.vm.MJIEnv;
/**
* Object transformer from Java objects to JPF objects
* @author Ivan Mushketik
*/
public class ObjectConverter {
/**
* Create JPF object from Java object
* @param env - MJI environment
* @param javaObject - java object that is used to created JPF object from
* @return reference to new JPF object
*/
public static int JPFObjectFromJavaObject(FeatureExpr ctx, MJIEnv env, Object javaObject) throws ClinitRequired {
Class<?> javaClass = javaObject.getClass();
String typeName = javaClass.getName();
int newObjRef = env.newObject(ctx, typeName);
ElementInfo newObjEI = env.getModifiableElementInfo(newObjRef);
ClassInfo ci = env.getClassInfo(newObjRef);
while (ci != null) {
for (FieldInfo fi : ci.getDeclaredInstanceFields()) {
if (!fi.isReference()) {
setJPFPrimitive(ctx, newObjEI, fi, javaObject);
}
else {
try {
Field arrField = getField(fi.getName(), javaClass);
arrField.setAccessible(true);
Object fieldJavaObj = arrField.get(javaObject);
int fieldJPFObjRef;
if (isArrayField(fi)) {
fieldJPFObjRef = getJPFArrayRef(ctx, env, fieldJavaObj);
} else {
fieldJPFObjRef = JPFObjectFromJavaObject(ctx, env, fieldJavaObj);
}
newObjEI.setReferenceField(ctx, fi, new One<>(fieldJPFObjRef));
} catch (NoSuchFieldException nsfx){
throw new JPFException("JPF object creation failed, no such field: " + fi.getFullName(), nsfx);
} catch (IllegalAccessException iax){
throw new JPFException("JPF object creation failed, illegal access: " + fi.getFullName(), iax);
}
}
}
ci = ci.getSuperClass();
}
return newObjRef;
}
@SuppressWarnings("unused")
private Object createObject(String className) {
return null;
}
private static void setJPFPrimitive(FeatureExpr ctx, ElementInfo newObjEI, FieldInfo fi, Object javaObject) {
try {
String jpfTypeName = fi.getType();
Class<?> javaClass = javaObject.getClass();
Field javaField = getField(fi.getName(), javaClass);
javaField.setAccessible(true);
if (jpfTypeName.equals("char")) {
newObjEI.setCharField(ctx, fi, new One<>(javaField.getChar(javaObject)));
}
else if (jpfTypeName.equals("byte")) {
newObjEI.setByteField(ctx, fi, One.valueOf(javaField.getByte(javaObject)));
}
else if (jpfTypeName.equals("short")) {
newObjEI.setShortField(ctx, fi, new One<>(javaField.getShort(javaObject)));
}
else if (jpfTypeName.equals("int")) {
newObjEI.setIntField(ctx, fi, new One<>(javaField.getInt(javaObject)));
}
else if (jpfTypeName.equals("long")) {
newObjEI.setLongField(ctx, fi, new One<>(javaField.getLong(javaObject)));
}
else if (jpfTypeName.equals("float")) {
newObjEI.setFloatField(ctx, fi, new One<>(javaField.getFloat(javaObject)));
}
else if (jpfTypeName.equals("double")) {
newObjEI.setDoubleField(ctx, fi, new One<>(javaField.getDouble(javaObject)));
}
}
catch (Exception ex) {
throw new JPFException(ex);
}
}
private static Field getField(String fieldName, Class<?> javaClass) throws NoSuchFieldException {
while (true) {
try {
Field field = javaClass.getDeclaredField(fieldName);
return field;
} catch (NoSuchFieldException ex) {
// Try to search this field in a super class
javaClass = javaClass.getSuperclass();
// No more super class. Wrong field
if (javaClass == null) {
throw ex;
}
}
}
}
// TODO jens implement all Types
// TODO jens check seems to be to expensive
private static int getJPFArrayRef(FeatureExpr ctx, MJIEnv env, Object javaArr) throws NoSuchFieldException, IllegalAccessException {
Class<?> arrayElementClass = javaArr.getClass().getComponentType();
int javaArrLength = Array.getLength(javaArr);
int arrRef;
if (arrayElementClass == Character.TYPE) {
arrRef = env.newCharArray(FeatureExprFactory.True(), javaArrLength);
ElementInfo charArrRef = env.getModifiableElementInfo(arrRef);
char[] charArr = charArrRef.asCharArray().getValue();
for (int i = 0; i < javaArrLength; i++) {
charArr[i] = Array.getChar(javaArr, i);
}
}
else if (arrayElementClass == Byte.TYPE) {
arrRef = env.newByteArray(javaArrLength);
ElementInfo byteArrRef = env.getModifiableElementInfo(arrRef);
// byte[] byteArr = byteArrRef.asByteArray();
//
// for (int i = 0; i < javaArrLength; i++) {
// byteArr[i] = Array.getByte(javaArr, i);
// }
for (int i = 0; i < javaArrLength; i++) {
byteArrRef.setIntElement(ctx, i, new One<>((int)Array.getByte(javaArr, i)));
}
}
else if (arrayElementClass == Short.TYPE) {
arrRef = env.newShortArray(javaArrLength);
ElementInfo shortArrRef = env.getModifiableElementInfo(arrRef);
// short[] shortArr = shortArrRef.asShortArray();
for (int i = 0; i < javaArrLength; i++) {
shortArrRef.setShortElement(ctx, i, new One<>(Array.getShort(javaArr, i)));
// shortArr[i] = Array.getShort(javaArr, i);
}
}
else if (arrayElementClass == Integer.TYPE) {
arrRef = env.newIntArray(javaArrLength);
ElementInfo intArrRef = env.getModifiableElementInfo(arrRef);
// Conditional<Integer>[] array = intArrRef.asIntArray();
// int[] intArr = new int[array.length];
// for (int i = 0; i < array.length; i++) {
// intArr[i] = array[i].getValue();
// }
//
for (int i = 0; i < javaArrLength; i++) {
intArrRef.setIntElement(ctx, i, One.valueOf(Array.getInt(javaArr, i)));
}
}
else if (arrayElementClass == Long.TYPE) {
arrRef = env.newLongArray(javaArrLength);
ElementInfo longArrRef = env.getModifiableElementInfo(arrRef);
// Conditional<Long>[] longArr = longArrRef.asLongArray();
for (int i = 0; i < javaArrLength; i++) {
longArrRef.setLongElement(ctx, i, new One<>(Array.getLong(javaArr, i)));
}
}
else if (arrayElementClass == Float.TYPE) {
arrRef = env.newFloatArray(javaArrLength);
ElementInfo floatArrRef = env.getModifiableElementInfo(arrRef);
// Conditional<Float>[] floatArr = floatArrRef.asFloatArray();
for (int i = 0; i < javaArrLength; i++) {
floatArrRef.setFloatElement(ctx, i, new One<>(Array.getFloat(javaArr, i)));
// floatArr[i] = Array.getFloat(javaArr, i);
}
}
else if (arrayElementClass == Double.TYPE) {
arrRef = env.newDoubleArray(javaArrLength);
ElementInfo floatArrRef = env.getModifiableElementInfo(arrRef);
// double[] doubleArr = floatArrRef.asDoubleArray();
for (int i = 0; i < javaArrLength; i++) {
floatArrRef.setDoubleElement(ctx, i, new One<>(Array.getDouble(javaArr, i)));
// doubleArr[i] = Array.getDouble(javaArr, i);
}
}
else {
arrRef = env.newObjectArray(arrayElementClass.getCanonicalName(), javaArrLength);
ElementInfo arrayEI = env.getModifiableElementInfo(arrRef);
Fields fields = arrayEI.getFields();
for (int i = 0; i < javaArrLength; i++) {
int newArrElRef;
Object javaArrEl = Array.get(javaArr, i);
if (javaArrEl != null) {
if (javaArrEl.getClass().isArray()) {
newArrElRef = getJPFArrayRef(ctx, env, javaArrEl);
}
else {
newArrElRef = JPFObjectFromJavaObject(ctx, env, javaArrEl);
}
}
else {
newArrElRef = MJIEnv.NULL;
}
fields.setReferenceValue(ctx, i, new One<>(newArrElRef));
}
}
return arrRef;
}
// Do we need this??
public static Object javaObjectFromJPFObject(ElementInfo ei) {
try {
String typeName = ei.getType();
Class<?> clazz = ClassLoader.getSystemClassLoader().loadClass(typeName);
Object javaObject = clazz.newInstance();
ClassInfo ci = ei.getClassInfo();
while (ci != null) {
for (FieldInfo fi : ci.getDeclaredInstanceFields()) {
if (!fi.isReference()) {
setJavaPrimitive(javaObject, ei, fi);
}
}
ci = ci.getSuperClass();
}
return javaObject;
} catch (Exception ex) {
throw new JPFException(ex);
}
}
private static void setJavaPrimitive(Object javaObject, ElementInfo ei, FieldInfo fi) throws NoSuchFieldException, IllegalAccessException {
String primitiveType = fi.getName();
String fieldName = fi.getName();
Class<?> javaClass = javaObject.getClass();
Field javaField = javaClass.getDeclaredField(fieldName);
javaField.setAccessible(true);
if (primitiveType.equals("char")) {
javaField.setChar(javaObject, ei.getCharField(fi).getValue());
}
else if (primitiveType.equals("byte")) {
javaField.setByte(javaObject, ei.getByteField(fi).getValue());
}
else if (primitiveType.equals("short")) {
javaField.setShort(javaObject, ei.getShortField(fi).getValue());
}
else if (primitiveType.equals("int")) {
javaField.setInt(javaObject, ei.getIntField(fi).getValue());
}
else if (primitiveType.equals("long")) {
javaField.setLong(javaObject, ei.getLongField(fi).getValue());
}
else if (primitiveType.equals("float")) {
javaField.setFloat(javaObject, ei.getFloatField(fi).getValue());
}
else if (primitiveType.equals("double")) {
javaField.setDouble(javaObject, ei.getDoubleField(fi).getValue());
}
else {
throw new JPFException("Can't convert " + primitiveType + " to " +
javaField.getClass().getCanonicalName());
}
}
private static boolean isArrayField(FieldInfo fi) {
return fi.getType().lastIndexOf('[') >= 0;
}
}