package LinGUIne.serialization; import java.io.File; import java.lang.reflect.InvocationTargetException; import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; import org.eclipse.core.runtime.IPath; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; import LinGUIne.model.AnnotationSet; import LinGUIne.model.IProjectData; import LinGUIne.model.Project; import LinGUIne.model.ProjectGroup; import LinGUIne.model.Result; import LinGUIne.model.Project.Subdirectory; import LinGUIne.utilities.ClassUtils; /** * Used to serialize/deserialize Projects to/from JSON. * * @author Kyle Mullins */ public class ProjectTranslator { private static final String ASSOCIATED_DATA_ATTRIB = "AssociatedData"; private static final String TYPE_ATTRIB = "Type"; private static final String RESULTS_ATTRIB = "Results"; private static final String ANNOTATIONS_ATTRIB = "Annotations"; private static final String ORIG_DATA_ATTRIB = "OriginalData"; private static final String GROUPS_ATTRIB = "Groups"; private static final String NAME_ATTRIB = "Name"; private static final String PARENT_ATTRIB = "Parent"; private static final String GROUP_ATTRIB = "Group"; /** * Converts the given Project object to Json format so that it may be * written to disk. * * @param proj The Project object to be converted. * * @return A Json String representing the given Project object. */ public static String toJson(Project proj){ JsonElement json = composeFromProject(proj); return json.toString(); } /** * Converts the given Json String into a Project object. * * @param jsonStr A Json String to be converted to an in-memory * object. * @param parentDir The path to the workspace directory where the * Project is located. * * @return A Project object representing the given Json or null if the Json * could not be parsed. */ public static Project fromJson(String jsonStr, IPath parentDir){ JsonParser parser = new JsonParser(); JsonElement json = parser.parse(jsonStr); return parseProjectFromJson(json, parentDir); } /* * Composing into Json */ /** * Returns the root JsonElement of the given Project object. */ private static JsonElement composeFromProject(Project proj){ JsonObject rootObj = new JsonObject(); rootObj.addProperty(NAME_ATTRIB, proj.getName()); JsonArray groupAry = new JsonArray(); rootObj.add(GROUPS_ATTRIB, groupAry); LinkedList<ProjectGroup> orderedGroups = new LinkedList<ProjectGroup>(); Collection<ProjectGroup> groupsCopy = new HashSet<ProjectGroup>( proj.getGroups()); //Add all top-level groups to the orderedGroups list because they must //be serialized first for(ProjectGroup group: groupsCopy){ if(!group.hasParent()){ orderedGroups.add(group); } } //Iterate through groupsCopy adding all groups for which the parent is //already in orderedGroups, and do this until groupsCopy is empty while(!groupsCopy.isEmpty()){ groupsCopy.removeAll(orderedGroups); for(ProjectGroup group: groupsCopy){ if(orderedGroups.contains(group.getParent())){ orderedGroups.add(group); } } } //Add all of the ProjectGroups in rough order for(ProjectGroup group: orderedGroups){ groupAry.add(composeFromProjectGroup(group)); } JsonArray origDataAry = new JsonArray(); rootObj.add(ORIG_DATA_ATTRIB, origDataAry); LinkedList<IProjectData> annotatedData = new LinkedList<IProjectData>(); //Convert all Original Data to Json and add them for(IProjectData projData: proj.getOriginalData()){ origDataAry.add(composeFromOriginalData(projData, proj)); if(proj.isAnnotated(projData)){ annotatedData.add(projData); } } JsonArray annotationsAry = new JsonArray(); rootObj.add(ANNOTATIONS_ATTRIB, annotationsAry); //Convert all AnnotationSets to Json and add them for(IProjectData annotated: annotatedData){ AnnotationSet annotation = proj.getAnnotation(annotated); annotationsAry.add(composeFromAnnotationSet(annotation, annotated, proj)); } JsonArray resultsAry = new JsonArray(); rootObj.add(RESULTS_ATTRIB, resultsAry); //Convert all Results to Json and add them for(Result result: proj.getResults()){ resultsAry.add(composeFromResult(result, proj.getDataForResult(result), proj)); } return rootObj; } /** * Returns the root JsonElement of the given IProjectData object assuming it * is original data and is in the given Project. */ private static JsonElement composeFromOriginalData(IProjectData projData, Project proj){ JsonObject rootObj = new JsonObject(); rootObj.addProperty(NAME_ATTRIB, projData.getName()); rootObj.addProperty(GROUP_ATTRIB, proj.getGroupFor(projData).getName()); rootObj.addProperty(TYPE_ATTRIB, ClassUtils.serializeClassName(projData.getClass())); return rootObj; } /** * Returns the root JsonElement of the given AnnotationSet which annotates * the given IProjectData, and is in the given Project. */ private static JsonElement composeFromAnnotationSet( AnnotationSet annotations, IProjectData projData, Project proj){ JsonObject rootObj = new JsonObject(); rootObj.addProperty(NAME_ATTRIB, annotations.getName()); rootObj.addProperty(GROUP_ATTRIB, proj.getGroupFor(annotations).getName()); rootObj.addProperty(TYPE_ATTRIB, ClassUtils.serializeClassName(annotations.getClass())); rootObj.addProperty(ASSOCIATED_DATA_ATTRIB, projData.getName()); return rootObj; } /** * Returns the root JsonElement of the given Result which affects the given * collection of IProjectData and is in the given Project. */ private static JsonElement composeFromResult(Result result, Collection<IProjectData> projData, Project proj){ JsonObject rootObj = new JsonObject(); rootObj.addProperty(NAME_ATTRIB, result.getName()); rootObj.addProperty(GROUP_ATTRIB, proj.getGroupFor(result).getName()); rootObj.addProperty(TYPE_ATTRIB, ClassUtils.serializeClassName(result.getClass())); JsonArray assocDataAry = new JsonArray(); rootObj.add(ASSOCIATED_DATA_ATTRIB, assocDataAry); for(IProjectData affectedData: projData){ assocDataAry.add(new JsonPrimitive(affectedData.getName())); } return rootObj; } /** * Returns the root JsonElement of the given ProjectGroup. */ private static JsonElement composeFromProjectGroup(ProjectGroup group){ JsonObject rootObj = new JsonObject(); rootObj.addProperty(NAME_ATTRIB, group.getName()); if(group.hasParent()){ rootObj.addProperty(PARENT_ATTRIB, group.getParent().getName()); } return rootObj; } /* * Parsing from Json */ /** * Parses the Project described by the given JsonElement and returns it. */ private static Project parseProjectFromJson(JsonElement json, IPath parentDir){ Project newProj = new Project(); newProj.setParentDirectory(parentDir); if(json.isJsonObject()){ JsonObject rootObj = json.getAsJsonObject(); JsonElement projName = rootObj.get(NAME_ATTRIB); //Parse Project name if(projName.isJsonPrimitive()){ newProj.setName(projName.getAsString()); } else{ return null; } JsonElement groups = rootObj.get(GROUPS_ATTRIB); //Parse ProjectGroups if(groups.isJsonArray()){ JsonArray groupsAry = groups.getAsJsonArray(); for(JsonElement groupElem: groupsAry){ if(!parseProjectGroupFromJson(groupElem, newProj)){ return null; } } } JsonElement origData = rootObj.get(ORIG_DATA_ATTRIB); //Parse Original ProjectData if(origData.isJsonArray()){ JsonArray origDataAry = origData.getAsJsonArray(); for(JsonElement dataElem: origDataAry){ if(!parseOriginalDataFromJson(dataElem, newProj)){ return null; } } } else{ return null; } JsonElement annotations = rootObj.get(ANNOTATIONS_ATTRIB); //Parse AnnotationSets if(annotations.isJsonArray()){ JsonArray annotationsAry = annotations.getAsJsonArray(); for(JsonElement annotationElem: annotationsAry){ if(!parseAnnotationSetFromJson(annotationElem, newProj)){ return null; } } } else{ return null; } JsonElement results = rootObj.get(RESULTS_ATTRIB); //Parse Results if(results.isJsonArray()){ JsonArray resultsAry = results.getAsJsonArray(); for(JsonElement resultsElem: resultsAry){ if(!parseResultFromJson(resultsElem, newProj)){ return null; } } } else{ return null; } } else{ return null; } return newProj; } /** * Parses the ProjectData described by the given JsonElement and adds it to * the Project being constructed. */ private static boolean parseOriginalDataFromJson(JsonElement json, Project proj){ if(json.isJsonObject()){ JsonObject rootObj = json.getAsJsonObject(); IProjectData newProjData; String name; ProjectGroup parentGroup; JsonElement nameElem = rootObj.get(NAME_ATTRIB); //Parse data name if(nameElem.isJsonPrimitive()){ name = nameElem.getAsString(); } else{ return false; } JsonElement groupElem = rootObj.get(GROUP_ATTRIB); //Parse group name if(groupElem.isJsonPrimitive()){ String groupName = groupElem.getAsString(); parentGroup = proj.getGroup(groupName); if(parentGroup == null){ return false; } } else{ return false; } //TODO: Group paths File dataFile = proj.getSubdirectory(Subdirectory.Data). append(name).toFile(); JsonElement typeElem = rootObj.get(TYPE_ATTRIB); //Parse type and create instance if(typeElem.isJsonPrimitive()){ try { //TODO: Enforce Constructor taking File as param Class<?> clazz = ClassUtils.deserializeClassName(typeElem.getAsString()); newProjData = (IProjectData)clazz.getDeclaredConstructor( File.class).newInstance(dataFile); } catch(ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { return false; } } else{ return false; } proj.addProjectData(newProjData, parentGroup); return true; } return false; } /** * Parses the AnnotationSet described by the given JsonElement and adds it * to the Project being constructed. */ private static boolean parseAnnotationSetFromJson(JsonElement json, Project proj){ if(json.isJsonObject()){ JsonObject rootObj = json.getAsJsonObject(); AnnotationSet newAnnotationSet; String name; JsonElement nameElem = rootObj.get(NAME_ATTRIB); //Parse data name if(nameElem.isJsonPrimitive()){ name = nameElem.getAsString(); } else{ return false; } //Ignoring group parsing for AnnotationSets because they can't be //moved to anything but the default one at the moment File dataFile = proj.getSubdirectory(Subdirectory.Annotations). append(name).toFile(); JsonElement typeElem = rootObj.get(TYPE_ATTRIB); //Parse type and create instance if(typeElem.isJsonPrimitive()){ try{ Class<?> clazz = ClassUtils.deserializeClassName(typeElem.getAsString()); newAnnotationSet = (AnnotationSet)clazz. getDeclaredConstructor(File.class).newInstance( dataFile); } catch(ClassNotFoundException | IllegalArgumentException | SecurityException | InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e){ return false; } } else{ return false; } JsonElement associatedDataElem = rootObj.get(ASSOCIATED_DATA_ATTRIB); //Parse associated data name and add AnnotationSet to Project if(associatedDataElem.isJsonPrimitive()){ IProjectData associatedData = proj.getProjectData( associatedDataElem.getAsString()); proj.addAnnotation(newAnnotationSet, associatedData); } return true; } return false; } /** * Parses the Result described by the given JsonElement and adds it to the * Project being constructed. */ private static boolean parseResultFromJson(JsonElement json, Project proj){ if(json.isJsonObject()){ JsonObject rootObj = json.getAsJsonObject(); Result newResult; String name; ProjectGroup parentGroup; JsonElement nameElem = rootObj.get(NAME_ATTRIB); //Parse data name if(nameElem.isJsonPrimitive()){ name = nameElem.getAsString(); } else{ return false; } JsonElement groupElem = rootObj.get(GROUP_ATTRIB); //Parse group name if(groupElem.isJsonPrimitive()){ String groupName = groupElem.getAsString(); parentGroup = proj.getGroup(groupName); if(parentGroup == null){ return false; } } else{ return false; } //TODO: Group paths File dataFile = proj.getSubdirectory(Subdirectory.Results). append(name).toFile(); JsonElement typeElem = rootObj.get(TYPE_ATTRIB); //Parse type and create instance if(typeElem.isJsonPrimitive()){ try{ Class<?> clazz = ClassUtils.deserializeClassName(typeElem.getAsString()); newResult = (Result)clazz.getDeclaredConstructor(File.class). newInstance(dataFile); } catch(ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e){ return false; } } else{ return false; } JsonElement associatedDataElem = rootObj.get(ASSOCIATED_DATA_ATTRIB); //Parse associated data names and add Result to Project if(associatedDataElem.isJsonArray()){ JsonArray associatedDataAry = associatedDataElem.getAsJsonArray(); LinkedList<IProjectData> associatedData = new LinkedList<IProjectData>(); for(JsonElement dataElem: associatedDataAry){ if(dataElem.isJsonPrimitive()){ associatedData.add(proj.getProjectData( dataElem.getAsString())); } } proj.addResult(newResult, associatedData, parentGroup); } return true; } return false; } /** * Parses the ProjectGroup described by the given JsonElement and adds it to * the Project being constructed. */ private static boolean parseProjectGroupFromJson(JsonElement json, Project proj){ if(json.isJsonObject()){ JsonObject rootObj = json.getAsJsonObject(); String name; JsonElement nameElem = rootObj.get(NAME_ATTRIB); //Parse group name if(nameElem.isJsonPrimitive()){ name = nameElem.getAsString(); } else{ return false; } ProjectGroup newGroup = new ProjectGroup(name); //Parse parent group (if there is one) and set it as parent if(rootObj.has(PARENT_ATTRIB)){ JsonElement parentElem = rootObj.get(PARENT_ATTRIB); if(nameElem.isJsonPrimitive()){ String parentName = parentElem.getAsString(); ProjectGroup parentGroup = proj.getGroup(parentName); if(parentGroup != null){ newGroup.setParent(parentGroup); } else{ return false; } } else{ return false; } } proj.addGroup(newGroup); return true; } return false; } }