/** * <copyright> * </copyright> * * */ package org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug; /** * The DebuggerListener receives commands from the Eclipse Debug framework and * sends these commands to a debuggable process (e.g., an interpreter or generated * code). */ public class MtextDebuggerListener<ResultType, ContextType> implements Runnable { private static final Class<?>[] PRIMITIVE_TYPES = new Class<?>[] { String.class, Integer.class, int.class, Long.class, long.class, Boolean.class, boolean.class, Float.class, float.class, Double.class, double.class, Byte.class, byte.class, Short.class, short.class, Character.class, char.class, }; private final static class ArrayPartition { private final Object array; private final int startIndex; private final int endIndex; public ArrayPartition(Object array, int startIndex, int endIndex) { super(); this.array = array; this.startIndex = startIndex; this.endIndex = endIndex; } public int getStartIndex() { return startIndex; } public int getEndIndex() { return endIndex; } public Object getArray() { return array; } } private boolean stop = false; private org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.AbstractMtextDebuggable debuggable; /** * The last object id that was used. */ private long id = 0; /** * This map maps object ids to pairs of object names and object values. */ private java.util.Map<Long, org.feature.multi.perspective.mapping.viewmapping.resource.mtext.util.MtextPair<String, Object>> objectMap = new java.util.LinkedHashMap<Long, org.feature.multi.perspective.mapping.viewmapping.resource.mtext.util.MtextPair<String, Object>>(); private org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.MtextDebugCommunicationHelper communicationHelper = new org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.MtextDebugCommunicationHelper(); private int requestPort; public MtextDebuggerListener(int requestPort) { super(); this.requestPort = requestPort; } public void run() { try { runDebugger(); } catch (java.io.IOException e) { e.printStackTrace(); } } private void runDebugger() throws java.io.IOException { java.net.ServerSocket server = new java.net.ServerSocket(requestPort); java.net.Socket accept = server.accept(); java.io.InputStream inputStream = accept.getInputStream(); java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(inputStream)); java.io.PrintStream output = new java.io.PrintStream(accept.getOutputStream()); org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.MtextDebugMessage command; while (!stop) { command = communicationHelper.receive(reader); if (command == null) { break; } if (command.hasType(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.EMtextDebugMessageTypes.EXIT)) { debuggable.terminate(); stop = true; } else if (command.hasType(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.EMtextDebugMessageTypes.RESUME)) { debuggable.resume(); } else if (command.hasType(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.EMtextDebugMessageTypes.STEP_OVER)) { debuggable.stepOver(); } else if (command.hasType(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.EMtextDebugMessageTypes.STEP_INTO)) { debuggable.stepInto(); } else if (command.hasType(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.EMtextDebugMessageTypes.STEP_RETURN)) { debuggable.stepReturn(); } else if (command.hasType(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.EMtextDebugMessageTypes.ADD_LINE_BREAKPOINT)) { String location = command.getArgument(0); int line = Integer.parseInt(command.getArgument(1)); debuggable.addLineBreakpoint(location, line); } else if (command.hasType(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.EMtextDebugMessageTypes.REMOVE_LINE_BREAKPOINT)) { String location = command.getArgument(0); int line = Integer.parseInt(command.getArgument(1)); debuggable.removeLineBreakpoint(location, line); } else if (command.hasType(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.EMtextDebugMessageTypes.GET_STACK)) { final String[] stack = debuggable.getStack(); String controlStack = org.feature.multi.perspective.mapping.viewmapping.resource.mtext.util.MtextStringUtil.encode('#', stack); org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.MtextDebugMessage message = new org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.MtextDebugMessage(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.EMtextDebugMessageTypes.GET_STACK_RESPONSE, new String[] {controlStack}); communicationHelper.sendEvent(message, output); } else if (command.hasType(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.EMtextDebugMessageTypes.GET_FRAME_VARIABLES)) { String stackFrame = command.getArgument(0); java.util.Map<String, Object> frameVariables = debuggable.getFrameVariables(stackFrame); java.util.List<String> topVariableIDs = new java.util.ArrayList<String>(); for (String name : frameVariables.keySet()) { Object value = frameVariables.get(name); long id = getObjectID(name, value); topVariableIDs.add(Long.toString(id)); } org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.MtextDebugMessage message = new org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.MtextDebugMessage(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.EMtextDebugMessageTypes.GET_FRAME_VARIABLES_RESPONSE, topVariableIDs); communicationHelper.sendEvent(message, output); } else if (command.hasType(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.EMtextDebugMessageTypes.GET_VARIABLES)) { String[] arguments = command.getArguments(); Long[] requestedIDs = new Long[arguments.length]; int i = 0; for (String argument : arguments) { requestedIDs[i++] = Long.parseLong(argument); } // create variable strings String[] varStrings = new String[arguments.length]; i = 0; for (Long requestedID : requestedIDs) { Object next = objectMap.get(requestedID).getRight(); String varString = convertToString(requestedID, next); varStrings[i++] = varString; } org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.MtextDebugMessage message = new org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.MtextDebugMessage(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.EMtextDebugMessageTypes.GET_VARIABLES_RESPONSE, varStrings); communicationHelper.sendEvent(message, output); } else { System.out.println("ERROR: Unrecognized command (" + command + ")!"); output.append("Unrecognized command!"); } } // closing server server.close(); } private String convertToString(long id, Object object) { String name = objectMap.get(id).getLeft(); java.util.Map<String, Object> properties = new java.util.LinkedHashMap<String, Object>(); properties.put("!name", name); properties.put("!id", Long.toString(id)); String valueString = object == null ? "null" : object.toString(); if (object != null) { if (object instanceof org.eclipse.emf.ecore.EObject) { org.eclipse.emf.ecore.EObject eObject = (org.eclipse.emf.ecore.EObject) object; org.eclipse.emf.ecore.EClass eClass = eObject.eClass(); String eClassName = eClass.getName(); valueString = eClassName + " (id=" + id + ")"; properties.put("!type", eClassName); java.util.List<org.eclipse.emf.ecore.EStructuralFeature> features = eClass.getEAllStructuralFeatures(); for (org.eclipse.emf.ecore.EStructuralFeature feature : features) { Object value = eObject.eGet(feature); String featureName = feature.getName(); long valueID = getObjectID(featureName, value); properties.put(featureName, Long.toString(valueID)); } } else if (object instanceof ArrayPartition) { ArrayPartition partition = (ArrayPartition) object; valueString = ""; // if there is only a single partition, the elements of the array are directly // used a children for (int i = partition.getStartIndex(); i < partition.getEndIndex(); i++) { Object array = partition.getArray(); Object objectAtIndex = java.lang.reflect.Array.get(array, i); String fieldName = "[" + i + "]"; long valueID = getObjectID(fieldName, objectAtIndex); properties.put(fieldName, Long.toString(valueID)); } } else { Class<? extends Object> javaClass = object.getClass(); valueString = javaClass.getSimpleName() + " (id=" + id + ")"; if (!isPrimitiveTypeClass(javaClass)) { addFields(object, properties, javaClass); } else { valueString = object.toString(); } if (javaClass.isArray()) { int length = java.lang.reflect.Array.getLength(object); int partitions = getPartitionCount(length); Class<?> componentType = javaClass.getComponentType(); valueString = componentType.getName() + "[" + length + "] (id=" + id + ")"; if (partitions == 1) { // if there is only a single partition, the elements of the array are directly // used a children for (int i = 0; i < length; i++) { Object objectAtIndex = java.lang.reflect.Array.get(object, i); String fieldName = "[" + i + "]"; long valueID = getObjectID(fieldName, objectAtIndex); properties.put(fieldName, Long.toString(valueID)); } } else { // if there are multiple partitions, we introduce artificial objects that // represent partitions of the array for (int i = 0; i < partitions; i++) { int startIndex = i * 100; int endIndex = Math.min((i + 1) * 100, length); String fieldName = "[" + startIndex + ".." + (endIndex - 1) + "]"; ArrayPartition newPartition = new ArrayPartition(object, startIndex, endIndex); long valueID = getObjectID(fieldName, newPartition); properties.put(fieldName, Long.toString(valueID)); } } } } } properties.put("!valueString", valueString); return org.feature.multi.perspective.mapping.viewmapping.resource.mtext.util.MtextStringUtil.convertToString(properties); } private int getPartitionCount(int arraySize) { int partitionSize = 100; int numPartitions = arraySize / partitionSize; int remainder = arraySize % partitionSize; if (remainder > 0) { numPartitions++; } return numPartitions; } private void addFields(Object object, java.util.Map<String, Object> properties, Class<?> javaClass) { java.lang.reflect.Field[] fields = javaClass.getDeclaredFields(); for (java.lang.reflect.Field field : fields) { // here we should check the settings of the debug view if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) { continue; } try { field.setAccessible(true); Object value = field.get(object); String fieldName = field.getName(); long valueID = getObjectID(fieldName, value); properties.put(fieldName, Long.toString(valueID)); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } Class<?> superclass = javaClass.getSuperclass(); if (superclass != null) { addFields(object, properties, superclass); } } private boolean isPrimitiveTypeClass(Class<?> javaClass) { for (Class<?> clazz : PRIMITIVE_TYPES) { if (clazz.getName().equals(javaClass.getName())) { return true; } } return false; } private long getObjectID(String name, Object value) { org.feature.multi.perspective.mapping.viewmapping.resource.mtext.util.MtextPair<String, Object> pair = new org.feature.multi.perspective.mapping.viewmapping.resource.mtext.util.MtextPair<String, Object>(name, value); if (objectMap.containsValue(pair)) { for (Long nextID : objectMap.keySet()) { Object next = objectMap.get(nextID); if (pair.equals(next)) { return nextID; } } // This should not happen, because objectMap.containsValue() was true. Maybe there // is a fault equals() method? assert false; return -1; } else { long usedID = id; objectMap.put(usedID, pair); id++; return usedID; } } public org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.AbstractMtextDebuggable getDebuggable() { return debuggable; } public void setDebuggable(org.feature.multi.perspective.mapping.viewmapping.resource.mtext.debug.AbstractMtextDebuggable debuggable) { this.debuggable = debuggable; } }