code: package gr.uom.java.ast.visualization;
  
  import gr.uom.java.ast.ClassObject;
  import gr.uom.java.ast.FieldInstructionObject;
  import gr.uom.java.ast.FieldObject;
  import gr.uom.java.ast.MethodInvocationObject;
  import gr.uom.java.ast.MethodObject;
  import gr.uom.java.ast.decomposition.cfg.PlainVariable;
  
  import java.util.LinkedHashMap;
  import java.util.LinkedHashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  public class GodClassVisualizationData implements VisualizationData {
  	//the MethodObject keys correspond to the methods suggested to be extracted in a new class
  	private Map<MethodObject, Map<MethodInvocationObject, Integer>> internalMethodInvocationMap;
  	private Map<MethodObject, Map<MethodInvocationObject, Integer>> externalMethodInvocationMap;
  	private Map<MethodObject, Map<FieldInstructionObject, Integer>> internalFieldReadMap;
  	private Map<MethodObject, Map<FieldInstructionObject, Integer>> internalFieldWriteMap;
  	private Map<MethodObject, Map<FieldInstructionObject, Integer>> externalFieldReadMap;
  	private Map<MethodObject, Map<FieldInstructionObject, Integer>> externalFieldWriteMap;
  	private Set<MethodObject> extractedMethods;
  	private Set<FieldObject> extractedFields;
  	private ClassObject sourceClass;
  	
  	public GodClassVisualizationData(ClassObject sourceClass, Set<MethodObject> extractedMethods, Set<FieldObject> extractedFields) {
  		this.sourceClass = sourceClass;
  		this.extractedMethods = extractedMethods;
  		this.extractedFields = extractedFields;
  		this.internalMethodInvocationMap = new LinkedHashMap<MethodObject, Map<MethodInvocationObject, Integer>>();
  		this.externalMethodInvocationMap = new LinkedHashMap<MethodObject, Map<MethodInvocationObject, Integer>>();
  		this.internalFieldReadMap = new LinkedHashMap<MethodObject, Map<FieldInstructionObject, Integer>>();
  		this.internalFieldWriteMap = new LinkedHashMap<MethodObject, Map<FieldInstructionObject, Integer>>();
  		this.externalFieldReadMap = new LinkedHashMap<MethodObject, Map<FieldInstructionObject, Integer>>();
  		this.externalFieldWriteMap = new LinkedHashMap<MethodObject, Map<FieldInstructionObject, Integer>>();
  		
  		for(MethodObject method : extractedMethods) {
  			List<MethodInvocationObject> methodInvocations = method.getNonDistinctInvokedMethodsThroughThisReference();
  			for(MethodInvocationObject invocation : methodInvocations) {
  				if(isInvocationToExtractedMethod(invocation, extractedMethods)) {
  					insertToMap(method, invocation, internalMethodInvocationMap);
  				}
  				else {
  					insertToMap(method, invocation, externalMethodInvocationMap);
  				}
  			}
  			List<FieldInstructionObject> fieldInstructions = method.getFieldInstructions();
  			List<PlainVariable> fieldAccesses = method.getNonDistinctUsedFieldsThroughThisReference();
  			for(PlainVariable fieldAccess : fieldAccesses) {
  				FieldInstructionObject fieldInstruction = findFieldInstruction(fieldAccess, fieldInstructions);
  				//exclude accesses to static fields
  				if(!fieldInstruction.isStatic()) {
  					if(isAccessToExtractedField(fieldInstruction, extractedFields)) {
  						insertToMap(method, fieldInstruction, internalFieldReadMap, 1);
  					}
  					else {
  						insertToMap(method, fieldInstruction, externalFieldReadMap, 1);
  					}
  				}
  			}
  			List<PlainVariable> fieldWrites = method.getNonDistinctDefinedFieldsThroughThisReference();
  			for(PlainVariable fieldWrite : fieldWrites) {
  				FieldInstructionObject fieldInstruction = findFieldInstruction(fieldWrite, fieldInstructions);
  				//exclude accesses to static fields
  				if(!fieldInstruction.isStatic()) {
  					if(isAccessToExtractedField(fieldInstruction, extractedFields)) {
  						insertToMap(method, fieldInstruction, internalFieldWriteMap, 1);
  					}
  					else {
  						insertToMap(method, fieldInstruction, externalFieldWriteMap, 1);
  					}
  				}
  			}
  		}
  		for(MethodObject method : extractedMethods) {
  			//replace getter method calls in Source class with field accesses in Extracted class
  			Map<MethodInvocationObject, Integer> methodInvocationMap = externalMethodInvocationMap.get(method);
  			if(methodInvocationMap != null) {
  				Set<MethodInvocationObject> invocationsToBeRemoved = new LinkedHashSet<MethodInvocationObject>();
  				for(MethodInvocationObject invocation : methodInvocationMap.keySet()) {
  					int count = methodInvocationMap.get(invocation);
  					MethodObject methodDeclaration = sourceClass.getMethod(invocation);
  					if(methodDeclaration != null) {
  						FieldInstructionObject getterFieldInstruction = methodDeclaration.isGetter();
  						if(getterFieldInstruction != null) {
  							if(isAccessToExtractedField(getterFieldInstruction, extractedFields)) {
  								//remove getter method calls in source class
  								invocationsToBeRemoved.add(invocation);
  								//add field reads in extracted class
  								insertToMap(method, getterFieldInstruction, internalFieldReadMap, count);
  							}
  						}
  						FieldInstructionObject setterFieldInstruction = methodDeclaration.isSetter();
  						if(setterFieldInstruction != null) {
  							if(isAccessToExtractedField(setterFieldInstruction, extractedFields)) {
  								//remove setter method calls in source class
  								invocationsToBeRemoved.add(invocation);
  								//add field writes in extracted class
  								insertToMap(method, setterFieldInstruction, internalFieldWriteMap, count);
  							}
  						}
  					}
  				}
  				for(MethodInvocationObject invocation : invocationsToBeRemoved) {
  					methodInvocationMap.remove(invocation);
  				}
  				if(methodInvocationMap.isEmpty()){
  					externalMethodInvocationMap.remove(method);
  				}
  			}
  		}
  	}
  
  	private void insertToMap(MethodObject method, MethodInvocationObject methodInvocation, 
  			Map<MethodObject, Map<MethodInvocationObject, Integer>> map) {
  		if(map.containsKey(method)) {
  			Map<MethodInvocationObject, Integer> invocationMap = map.get(method);
  			if(invocationMap.containsKey(methodInvocation)) {
  				invocationMap.put(methodInvocation, invocationMap.get(methodInvocation) + 1);
  			}
  			else {
  				invocationMap.put(methodInvocation, 1);
  			}
  		}
  		else {
  			Map<MethodInvocationObject, Integer> invocationMap = new LinkedHashMap<MethodInvocationObject, Integer>();
  			invocationMap.put(methodInvocation, 1);
  			map.put(method, invocationMap);
  		}
  	}
  
  	private void insertToMap(MethodObject method, FieldInstructionObject fieldInstruction,
  			Map<MethodObject, Map<FieldInstructionObject, Integer>> map, int count) {
  		if(map.containsKey(method)) {
  			Map<FieldInstructionObject, Integer> fieldAccessMap = map.get(method);
  			if(fieldAccessMap.containsKey(fieldInstruction)) {
  				fieldAccessMap.put(fieldInstruction, fieldAccessMap.get(fieldInstruction) + count);
  			}
  			else {
  				fieldAccessMap.put(fieldInstruction, count);
  			}
  		}
  		else {
  			Map<FieldInstructionObject, Integer> fieldAccessMap = new LinkedHashMap<FieldInstructionObject, Integer>();
  			fieldAccessMap.put(fieldInstruction, count);
  			map.put(method, fieldAccessMap);
  		}
  	}
  
  	private boolean isInvocationToExtractedMethod(MethodInvocationObject invocation, Set<MethodObject> extractedMethods) {
  		for(MethodObject method : extractedMethods) {
  			if(method.equals(invocation))
  				return true;
  		}
  		return false;
  	}
  
  	private boolean isAccessToExtractedField(FieldInstructionObject instruction, Set<FieldObject> extractedFields) {
  		for(FieldObject field : extractedFields) {
  			if(field.equals(instruction))
  				return true;
  		}
  		return false;
  	}
  
  	private FieldInstructionObject findFieldInstruction(PlainVariable variable, List<FieldInstructionObject> fieldInstructions) {
  		for(FieldInstructionObject fieldInstruction : fieldInstructions) {
  			if(fieldInstruction.getSimpleName().resolveBinding().getKey().equals(variable.getVariableBindingKey()))
  				return fieldInstruction;
  		}
  		return null;
  	}
  
  	public Map<MethodObject, Map<MethodInvocationObject, Integer>> getInternalMethodInvocationMap() {
  		return internalMethodInvocationMap;
  	}
  
  	public Map<MethodObject, Map<MethodInvocationObject, Integer>> getExternalMethodInvocationMap() {
  		return externalMethodInvocationMap;
  	}
  
  	public Map<MethodObject, Map<FieldInstructionObject, Integer>> getInternalFieldReadMap() {
  		return internalFieldReadMap;
  	}
  
  	public Map<MethodObject, Map<FieldInstructionObject, Integer>> getInternalFieldWriteMap() {
  		return internalFieldWriteMap;
  	}
  
  	public Map<MethodObject, Map<FieldInstructionObject, Integer>> getExternalFieldReadMap() {
  		return externalFieldReadMap;
  	}
  
  	public Map<MethodObject, Map<FieldInstructionObject, Integer>> getExternalFieldWriteMap() {
  		return externalFieldWriteMap;
  	}
  
  	public Set<MethodObject> getExtractedMethods() {
  		return extractedMethods;
  	}
  
  	public Set<FieldObject> getExtractedFields() {
  		return extractedFields;
  	}
  
  	public ClassObject getSourceClass() {
  		return sourceClass;
  	}
  
  	public String toString() {
  		StringBuilder sb = new StringBuilder();
  		sb.append("--FIELD READS FROM EXTRACTED METHODS TO EXTRACTED FIELDS--").append("\n");
  		sb.append(fieldAccessMapToString(getInternalFieldReadMap()));
  		sb.append("--FIELD WRITES FROM EXTRACTED METHODS TO EXTRACTED FIELDS--").append("\n");
  		sb.append(fieldAccessMapToString(getInternalFieldWriteMap()));
  		sb.append("--METHOD CALLS BETWEEN EXTRACTED METHODS--").append("\n");
  		sb.append(methodCallMapToString(getInternalMethodInvocationMap()));
  		sb.append("\n");
  		sb.append("--FIELD READS FROM EXTRACTED METHODS TO SOURCE CLASS FIELDS--").append("\n");
  		sb.append(fieldAccessMapToString(getExternalFieldReadMap()));
  		sb.append("--FIELD WRITES FROM EXTRACTED METHODS TO SOURCE CLASS FIELDS--").append("\n");
  		sb.append(fieldAccessMapToString(getExternalFieldWriteMap()));
  		sb.append("--METHOD CALLS FROM EXTRACTED METHODS TO SOURCE CLASS METHODS--").append("\n");
  		sb.append(methodCallMapToString(getExternalMethodInvocationMap()));
  		
  		return sb.toString();
  	}
  
  	private String methodCallMapToString(Map<MethodObject, Map<MethodInvocationObject, Integer>> map) {
  		StringBuilder sb = new StringBuilder();
  		for(MethodObject method : map.keySet()) {
  			sb.append(method).append("\n");
  			Map<MethodInvocationObject, Integer> invocationMap = map.get(method);
  			for(MethodInvocationObject invocation : invocationMap.keySet()) {
  				sb.append("\t").append(invocation).append(" : ").append(invocationMap.get(invocation)).append("\n");
  			}
  		}
  		return sb.toString();
  	}
  
  	private String fieldAccessMapToString(Map<MethodObject, Map<FieldInstructionObject, Integer>> map) {
  		StringBuilder sb = new StringBuilder();
  		for(MethodObject method : map.keySet()) {
  			sb.append(method).append("\n");
  			Map<FieldInstructionObject, Integer> fieldAccessMap = map.get(method);
  			for(FieldInstructionObject instruction : fieldAccessMap.keySet()) {
  				sb.append("\t").append(instruction).append(" : ").append(fieldAccessMap.get(instruction)).append("\n");
  			}
  		}
  		return sb.toString();
  	}
  
  	public int getDistinctTargetDependencies() {
  		Set<FieldInstructionObject> fields = new LinkedHashSet<FieldInstructionObject>();
  		for(MethodObject key : internalFieldReadMap.keySet()) {
  			Map<FieldInstructionObject, Integer> value = internalFieldReadMap.get(key);
  			fields.addAll(value.keySet());
  		}
  		for(MethodObject key : internalFieldWriteMap.keySet()) {
  			Map<FieldInstructionObject, Integer> value = internalFieldWriteMap.get(key);
  			fields.addAll(value.keySet());
  		}
  		Set<MethodInvocationObject> methods = new LinkedHashSet<MethodInvocationObject>();
  		for(MethodObject key : internalMethodInvocationMap.keySet()) {
  			Map<MethodInvocationObject, Integer> value = internalMethodInvocationMap.get(key);
  			methods.addAll(value.keySet());
  		}
  		return fields.size() + methods.size();
  	}
  
  	public int getDistinctSourceDependencies() {
  		Set<FieldInstructionObject> fields = new LinkedHashSet<FieldInstructionObject>();
  		for(MethodObject key : externalFieldReadMap.keySet()) {
  			Map<FieldInstructionObject, Integer> value = externalFieldReadMap.get(key);
  			fields.addAll(value.keySet());
  		}
  		for(MethodObject key : externalFieldWriteMap.keySet()) {
  			Map<FieldInstructionObject, Integer> value = externalFieldWriteMap.get(key);
  			fields.addAll(value.keySet());
  		}
  		Set<MethodInvocationObject> methods = new LinkedHashSet<MethodInvocationObject>();
  		for(MethodObject key : externalMethodInvocationMap.keySet()) {
  			Map<MethodInvocationObject, Integer> value = externalMethodInvocationMap.get(key);
  			methods.addAll(value.keySet());
  		}
  		return fields.size() + methods.size();
  	}
  
  	public boolean containsNonAccessedFieldInExtractedClass() {
  		Set<FieldInstructionObject> fields = new LinkedHashSet<FieldInstructionObject>();
  		for(MethodObject key : internalFieldReadMap.keySet()) {
  			Map<FieldInstructionObject, Integer> value = internalFieldReadMap.get(key);
  			fields.addAll(value.keySet());
  		}
  		for(MethodObject key : internalFieldWriteMap.keySet()) {
  			Map<FieldInstructionObject, Integer> value = internalFieldWriteMap.get(key);
  			fields.addAll(value.keySet());
  		}
  		for(FieldObject field : extractedFields) {
  			if(!fields.contains(field.generateFieldInstruction())) {
  				return true;
  			}
  		}
  		return false;
  	}
  }
  