Mercurial > hg > Members > kono > jpf-core
diff src/main/gov/nasa/jpf/util/json/JSONObject.java @ 0:61d41facf527
initial v8 import (history reset)
author | Peter Mehlitz <Peter.C.Mehlitz@nasa.gov> |
---|---|
date | Fri, 23 Jan 2015 10:14:01 -0800 |
parents | |
children | db918c531e6d |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/gov/nasa/jpf/util/json/JSONObject.java Fri Jan 23 10:14:01 2015 -0800 @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2014, United States Government, as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All rights reserved. + * + * The Java Pathfinder core (jpf-core) platform is licensed under the + * Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package gov.nasa.jpf.util.json; + +import gov.nasa.jpf.JPF; +import gov.nasa.jpf.JPFException; +import gov.nasa.jpf.util.JPFLogger; +import gov.nasa.jpf.util.ObjectConverter; +import gov.nasa.jpf.vm.ChoiceGenerator; +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; +import gov.nasa.jpf.vm.ThreadInfo; + +import java.util.HashMap; +import java.util.Set; + +/** + * Object parsed from JSON document. + * @author Ivan Mushketik + */ +public class JSONObject{ + + private static final JPFLogger logger = JPF.getLogger("gov.nasa.jpf.util.json.JSONObject"); + + private HashMap<String, Value> keyValues = new HashMap<String, Value>(); + private HashMap<String, CGCall> cgCalls = new HashMap<String, CGCall>(); + + void addValue(String key, Value value) { + if (keyValues.containsKey(key)) { + throw new JPFException("Attempt to add two nodes with the same key in JSON object"); + } + + keyValues.put(key, value); + } + + /** + * Get value read from JSON document with specified key. + * @param key - value's key. + * @return read value. + */ + public Value getValue(String key) { + return keyValues.get(key); + } + + public String[] getValuesKeys() { + Set<String> valuesKeys = keyValues.keySet(); + String[] result = new String[keyValues.size()]; + + valuesKeys.toArray(result); + return result; + } + + public void addCGCall(String key, CGCall cgCall) { + if (cgCalls.containsKey(key)) { + throw new JPFException("Attempt to add two CG with the same key in JSON object"); + } + + cgCalls.put(key, cgCall); + } + + public CGCall getCGCall(String key) { + return cgCalls.get(key); + } + + public String[] getCGCallsKeys() { + Set<String> cgKeys = cgCalls.keySet(); + String[] result = new String[cgKeys.size()]; + + cgKeys.toArray(result); + return result; + } + + /** + * check if all required ClassInfos for this object have been initialized so + * that the caller can decide if it has to re-execute before proceeding + * + * NOTE - this currently does not support concrete field types that are subtypes + * of the respective field types + */ + public boolean requiresClinitExecution (ClassInfo ci, ThreadInfo ti){ + while (ci != null){ + if (ci.pushRequiredClinits(ti)){ + return true; + } + + for (FieldInfo fi : ci.getDeclaredInstanceFields()) { + ClassInfo ciField = fi.getTypeClassInfo(); + if (requiresClinitExecution(ciField, ti)){ + return true; + } + } + + ci = ci.getSuperClass(); + } + + return false; + } + + //--- the fillers + + // NOTE - (pcm) before calling this method you have to make sure all required + // types are initialized + + public int fillObject (MJIEnv env, ClassInfo ci, ChoiceGenerator<?>[] cgs, String prefix) throws ClinitRequired { + int newObjRef = env.newObject(ci); + ElementInfo ei = env.getHeap().getModifiable(newObjRef); + + // Fill all fields for this class until it has a super class + while (ci != null) { + FieldInfo[] fields = ci.getDeclaredInstanceFields(); + + for (FieldInfo fi : fields) { + String fieldName = fi.getName(); + Value val = getValue(fieldName); + CGCall cgCall = getCGCall(fieldName); + + // If a value was defined in JSON document + if (val != null) { + fillFromValue(fi, ei, val, env, cgs, prefix); + + } else if (cgCall != null) { + // Value of this field should be taken from CG + String cgId = prefix + fieldName; + ChoiceGenerator<?> cg = getCGByID(cgs, cgId); + assert cg != null : "Expected CG with id " + cgId; + + Object cgResult = cg.getNextChoice(); + + if (!fi.isReference()) { + convertPrimititve(ei, fi, cgResult); + } else { + int newFieldRef = ObjectConverter.JPFObjectFromJavaObject(env, cgResult); + ei.setReferenceField(fi, newFieldRef); + } + } else { + logger.warning("Value for field ", fi.getFullName(), " isn't specified"); + } + } + + ci = ci.getSuperClass(); + } + + return newObjRef; + } + + private void fillFromValue(FieldInfo fi, ElementInfo ei, Value val, MJIEnv env, ChoiceGenerator<?>[] cgs, String prefix) { + String fieldName = fi.getName(); + // Handle primitive types + if (!fi.isReference()) { + fillPrimitive(ei, fi, val); + + } else { + if (isArrayType(fi.getType())) { + int newArrRef = createArray(env, fi.getTypeClassInfo(), val, cgs, prefix + fieldName); + ei.setReferenceField(fi, newArrRef); + + } else { + Creator creator = CreatorsFactory.getCreator(fi.getType()); + if (creator != null) { + int newSubObjRef = creator.create(env, fi.getType(), val); + ei.setReferenceField(fi, newSubObjRef); + + } else { + // Not a special case. Fill it recursively + ClassInfo ciField = fi.getTypeClassInfo(); + if (ciField.pushRequiredClinits(env.getThreadInfo())){ + throw new ClinitRequired(ciField); + } + + JSONObject jsonObj = val.getObject(); + int fieldRef = MJIEnv.NULL; + if (jsonObj != null) { + fieldRef = jsonObj.fillObject(env, ciField, cgs, prefix + fieldName); + } + ei.setReferenceField(fi.getName(), fieldRef); + } + } + } + } + + + private static void fillPrimitive(ElementInfo ei, FieldInfo fi, Value val) { + String primitiveName = fi.getType(); + + if (primitiveName.equals("boolean")) { + ei.setBooleanField(fi, val.getBoolean()); + + } else if (primitiveName.equals("byte")) { + ei.setByteField(fi, val.getDouble().byteValue()); + + } else if (primitiveName.equals("short")) { + ei.setShortField(fi, val.getDouble().shortValue()); + + } else if (primitiveName.equals("int")) { + ei.setIntField(fi, val.getDouble().intValue()); + + } else if (primitiveName.equals("long")) { + ei.setLongField(fi, val.getDouble().longValue()); + + } else if (primitiveName.equals("float")) { + ei.setFloatField(fi, val.getDouble().floatValue()); + + } else if (primitiveName.equals("double")) { + ei.setDoubleField(fi, val.getDouble()); + } + } + + public int createArray(MJIEnv env, ClassInfo ciArray, Value value, ChoiceGenerator<?>[] cgs, String prefix) { + Value vals[] = value.getArray(); + + ClassInfo ciElement = ciArray.getComponentClassInfo(); + String arrayElementType = ciElement.getName(); + int arrayRef; + + // Handle arrays of primitive types + if (arrayElementType.equals("boolean")) { + arrayRef = env.newBooleanArray(vals.length); + ElementInfo arrayEI = env.getHeap().getModifiable(arrayRef); + boolean bools[] = arrayEI.asBooleanArray(); + + for (int i = 0; i < vals.length; i++) { + bools[i] = vals[i].getBoolean(); + } + } else if (arrayElementType.equals("byte")) { + arrayRef = env.newByteArray(vals.length); + ElementInfo arrayEI = env.getHeap().getModifiable(arrayRef); + byte bytes[] = arrayEI.asByteArray(); + + for (int i = 0; i < vals.length; i++) { + bytes[i] = vals[i].getDouble().byteValue(); + } + } else if (arrayElementType.equals("short")) { + arrayRef = env.newShortArray(vals.length); + ElementInfo arrayEI = env.getHeap().getModifiable(arrayRef); + short shorts[] = arrayEI.asShortArray(); + + for (int i = 0; i < vals.length; i++) { + shorts[i] = vals[i].getDouble().shortValue(); + } + } else if (arrayElementType.equals("int")) { + arrayRef = env.newIntArray(vals.length); + ElementInfo arrayEI = env.getHeap().getModifiable(arrayRef); + int[] ints = arrayEI.asIntArray(); + + for (int i = 0; i < vals.length; i++) { + ints[i] = vals[i].getDouble().intValue(); + } + } else if (arrayElementType.equals("long")) { + arrayRef = env.newLongArray(vals.length); + ElementInfo arrayEI = env.getHeap().getModifiable(arrayRef); + long[] longs = arrayEI.asLongArray(); + + for (int i = 0; i < vals.length; i++) { + longs[i] = vals[i].getDouble().longValue(); + } + } else if (arrayElementType.equals("float")) { + arrayRef = env.newFloatArray(vals.length); + ElementInfo arrayEI = env.getHeap().getModifiable(arrayRef); + float[] floats = arrayEI.asFloatArray(); + + for (int i = 0; i < vals.length; i++) { + floats[i] = vals[i].getDouble().floatValue(); + } + } else if (arrayElementType.equals("double")) { + arrayRef = env.newDoubleArray(vals.length); + ElementInfo arrayEI = env.getHeap().getModifiable(arrayRef); + double[] doubles = arrayEI.asDoubleArray(); + + for (int i = 0; i < vals.length; i++) { + doubles[i] = vals[i].getDouble(); + } + } else { + // Not an array of primitive types + arrayRef = env.newObjectArray(arrayElementType, vals.length); + ElementInfo arrayEI = env.getModifiableElementInfo(arrayRef); + + Fields fields = arrayEI.getFields(); + + Creator creator = CreatorsFactory.getCreator(arrayElementType); + for (int i = 0; i < vals.length; i++) { + + int newObjRef; + if (creator != null) { + newObjRef = creator.create(env, arrayElementType, vals[i]); + } else{ + if (isArrayType(arrayElementType)) { + newObjRef = createArray(env, ciElement, vals[i], cgs, prefix + "[" + i); + } else { + JSONObject jsonObj = vals[i].getObject(); + if (jsonObj != null) { + newObjRef = jsonObj.fillObject(env, ciElement, cgs, prefix + "[" + i); + } else { + newObjRef = MJIEnv.NULL; + } + } + } + + fields.setReferenceValue(i, newObjRef); + } + } + + return arrayRef; + } + + + private boolean isArrayType(String typeName) { + return typeName.lastIndexOf('[') >= 0; + } + + /** + * This is method is used to set field of primitive type from CG result object + * @param ei - ElementInfo to set field in + * @param fi - FieldInfo of a field we want to set + * @param cgResult - result of CG call + */ + private void convertPrimititve(ElementInfo ei, FieldInfo fi, Object cgResult) { + String primitiveName = fi.getType(); + + if (primitiveName.equals("boolean") && cgResult instanceof Boolean) { + Boolean bool = (Boolean) cgResult; + ei.setBooleanField(fi, bool.booleanValue()); + } else if (cgResult instanceof Number) { + Number number = (Number) cgResult; + + if (primitiveName.equals("byte")) { + ei.setByteField(fi, number.byteValue()); + + } else if (primitiveName.equals("short")) { + ei.setShortField(fi, number.shortValue()); + + } else if (primitiveName.equals("int")) { + ei.setIntField(fi, number.intValue()); + + } else if (primitiveName.equals("long")) { + ei.setLongField(fi, number.longValue()); + + } else if (primitiveName.equals("float")) { + ei.setFloatField(fi, number.floatValue()); + + } else if (primitiveName.equals("double")) { + ei.setDoubleField(fi, number.doubleValue()); + } + } else if (cgResult instanceof Character) { + Character c = (Character) cgResult; + ei.setCharField(fi, c); + + } else { + throw new JPFException("Can't convert " + cgResult.getClass().getCanonicalName() + + " to " + primitiveName); + } + } + + /** + * Get CG from current state CG list by it's ID + * @param cgs - array of CG from current state + * @param id - id of the CG that we search for + * @return - CG with a specified id or null if no id with such name found + */ + private ChoiceGenerator<?> getCGByID(ChoiceGenerator<?>[] cgs, String id) { + if (cgs == null) { + return null; + } + + for (int i = 0; i < cgs.length; i++) { + if (cgs[i].getId().equals(id)) { + return cgs[i]; + } + } + + return null; + } +}