| /******************************************************************************* |
| * Copyright (c) 2005, 2013 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.ant.internal.launching.debug; |
| |
| import java.util.HashMap; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Stack; |
| import java.util.Vector; |
| |
| import org.apache.tools.ant.BuildEvent; |
| import org.apache.tools.ant.Location; |
| import org.apache.tools.ant.Project; |
| import org.apache.tools.ant.Target; |
| import org.apache.tools.ant.Task; |
| import org.apache.tools.ant.taskdefs.MacroInstance; |
| import org.eclipse.ant.internal.core.IAntCoreConstants; |
| import org.eclipse.ant.internal.launching.debug.model.DebugMessageIds; |
| |
| public class AntDebugState { |
| |
| private static final String fgAntTaskName = "ant"; //$NON-NLS-1$ |
| private static final String fgAntCallTaskName = "antcall"; //$NON-NLS-1$ |
| |
| private IDebugBuildLogger fLogger; |
| private Stack<Task> fTasks = new Stack<Task>(); |
| private Map<Task, Object> fTaskToProxies = new HashMap<Task, Object>(); |
| private Task fCurrentTask; |
| private Task fStepOverTask; |
| private Task fStepIntoTask; |
| private Task fLastTaskFinished; |
| |
| // properties set before execution |
| private Map<String, Object> fInitialProperties = null; |
| private Map<String, Object> fProperties = null; |
| |
| private Map<Project, Vector<?>> fProjectToTargetNames = null; |
| private Map<Project, Map<Target, Vector<Target>>> fProjectToMapOfTargetToBuildSequence = null; |
| private Stack<Target> fTargetsToExecute = new Stack<Target>(); |
| private Stack<Target> fTargetsExecuting = new Stack<Target>(); |
| |
| private boolean fConsiderTargetBreakpoints = false; |
| private boolean fShouldSuspend; |
| private boolean fClientSuspend = false; |
| private boolean fStepIntoSuspend = false; |
| private boolean fIsAfterTaskEvent = false; |
| |
| public AntDebugState(IDebugBuildLogger logger) { |
| fLogger = logger; |
| } |
| |
| public void buildStarted() { |
| fProjectToTargetNames = new HashMap<Project, Vector<?>>(); |
| fProjectToMapOfTargetToBuildSequence = new HashMap<Project, Map<Target, Vector<Target>>>(); |
| } |
| |
| /** |
| * Call-back from {@link org.eclipse.ant.internal.launching.runtime.logger.AntProcessDebugBuildLogger} when the build has finished |
| * |
| * @since 1.0.1 |
| */ |
| public void buildFinished() { |
| if (fProjectToTargetNames != null) { |
| fProjectToTargetNames.clear(); |
| } |
| if (fProjectToMapOfTargetToBuildSequence != null) { |
| fProjectToMapOfTargetToBuildSequence.clear(); |
| } |
| fTargetsExecuting.clear(); |
| fTargetsToExecute.clear(); |
| if (fInitialProperties != null) { |
| fInitialProperties.clear(); |
| } |
| if (fProperties != null) { |
| fProperties.clear(); |
| } |
| if (fTaskToProxies != null) { |
| fTaskToProxies.clear(); |
| } |
| if (fTasks != null) { |
| fTasks.clear(); |
| } |
| } |
| |
| public void waitIfSuspended() { |
| fLogger.waitIfSuspended(); |
| } |
| |
| public Task getLastTaskFinished() { |
| return fLastTaskFinished; |
| } |
| |
| private void setLastTaskFinished(Task lastTaskFinished) { |
| fLastTaskFinished = lastTaskFinished; |
| |
| } |
| |
| public Task getCurrentTask() { |
| return fCurrentTask; |
| } |
| |
| public void setCurrentTask(Task currentTask) { |
| fCurrentTask = currentTask; |
| |
| } |
| |
| private Map<String, Object> getInitialProperties() { |
| return fInitialProperties; |
| } |
| |
| public Task getStepOverTask() { |
| return fStepOverTask; |
| } |
| |
| public void setStepOverTask(Task stepOverTask) { |
| fStepOverTask = stepOverTask; |
| |
| } |
| |
| private boolean considerTargetBreakpoints() { |
| return fConsiderTargetBreakpoints; |
| } |
| |
| private void setConsiderTargetBreakpoints(boolean considerTargetBreakpoints) { |
| fConsiderTargetBreakpoints = considerTargetBreakpoints; |
| } |
| |
| private Stack<Task> getTasks() { |
| return fTasks; |
| } |
| |
| public void setShouldSuspend(boolean shouldSuspend) { |
| fShouldSuspend = shouldSuspend; |
| } |
| |
| public boolean shouldSuspend() { |
| return fShouldSuspend; |
| } |
| |
| private Map<Target, Vector<Target>> getTargetToBuildSequence(Project project) { |
| return fProjectToMapOfTargetToBuildSequence.get(project); |
| } |
| |
| public void setTargetToExecute(Target target) { |
| if (target == null) { |
| fTargetsToExecute.pop(); |
| } else { |
| fTargetsToExecute.push(target); |
| } |
| } |
| |
| public void setTargetExecuting(Target target) { |
| if (target == null) { |
| if (!fTargetsExecuting.isEmpty()) { |
| fTargetsExecuting.pop(); |
| } |
| } else { |
| fTargetsExecuting.push(target); |
| } |
| } |
| |
| private Target getTargetToExecute() { |
| if (fTargetsToExecute.isEmpty()) { |
| return null; |
| } |
| return fTargetsToExecute.peek(); |
| } |
| |
| private Target getTargetExecuting() { |
| if (fTargetsExecuting.isEmpty()) { |
| return null; |
| } |
| return fTargetsExecuting.peek(); |
| } |
| |
| public boolean isStepIntoSuspend() { |
| return isAfterTaskEvent() && fStepIntoSuspend; |
| } |
| |
| public void setStepIntoSuspend(boolean stepIntoSuspend) { |
| fStepIntoSuspend = stepIntoSuspend; |
| } |
| |
| public boolean isClientSuspend() { |
| return fClientSuspend; |
| } |
| |
| public void setClientSuspend(boolean clientSuspend) { |
| fClientSuspend = clientSuspend; |
| } |
| |
| public Task getStepIntoTask() { |
| return fStepIntoTask; |
| } |
| |
| public void setStepIntoTask(Task stepIntoTask) { |
| fStepIntoTask = stepIntoTask; |
| } |
| |
| public void resume() { |
| fLogger.notifyAll(); |
| } |
| |
| public Map<String, Object> getProperties() { |
| return fProperties; |
| } |
| |
| public Location getBreakpointLocation() { |
| if (isAfterTaskEvent() && getCurrentTask() != null) { |
| return getCurrentTask().getLocation(); |
| } |
| if (considerTargetBreakpoints()) { |
| Target targetExecuting = getTargetExecuting(); |
| if (targetExecuting != null) { |
| return getLocation(targetExecuting); |
| } |
| } |
| return null; |
| } |
| |
| private boolean isAfterTaskEvent() { |
| return fIsAfterTaskEvent; |
| } |
| |
| private void setAfterTaskEvent(boolean isAfterTaskEvent) { |
| fIsAfterTaskEvent = isAfterTaskEvent; |
| } |
| |
| public void taskStarted(BuildEvent event) { |
| setAfterTaskEvent(true); |
| if (getInitialProperties() == null) {// implicit or top level target does not fire targetStarted() |
| fInitialProperties = event.getProject().getProperties(); |
| } |
| |
| setCurrentTask(event.getTask()); |
| setConsiderTargetBreakpoints(false); |
| Stack<Task> tasks = getTasks(); |
| if (!tasks.isEmpty()) { |
| // cache the parent task proxy as when that task is started or finished the |
| // proxy is not yet available or is nulled out |
| Task parentTask = tasks.peek(); |
| Object proxy = parentTask.getRuntimeConfigurableWrapper().getProxy(); |
| if (proxy != null) { |
| fTaskToProxies.put(parentTask, proxy); |
| } |
| } |
| tasks.push(getCurrentTask()); |
| waitIfSuspended(); |
| } |
| |
| public void taskFinished() { |
| Stack<Task> tasks = getTasks(); |
| if (!tasks.empty()) { |
| Task lastTask = tasks.pop(); |
| setLastTaskFinished(lastTask); |
| setCurrentTask(null); |
| String taskName = lastTask.getTaskName(); |
| |
| if (getStepOverTask() != null) { |
| if ((fgAntCallTaskName.equals(taskName) || fgAntTaskName.equals(taskName)) |
| && (!fgAntCallTaskName.equals(getStepOverTask().getTaskName()) && !fgAntTaskName.equals(getStepOverTask().getTaskName()))) { |
| setShouldSuspend(true); |
| } else if (fTaskToProxies.remove(lastTask) instanceof MacroInstance) { |
| setShouldSuspend(true); |
| } |
| } |
| } |
| waitIfSuspended(); |
| } |
| |
| public void stepOver() { |
| setStepOverTask(getCurrentTask()); |
| if (getCurrentTask() == null) { |
| // stepping over target breakpoint |
| setShouldSuspend(true); |
| } |
| resume(); |
| } |
| |
| public void targetStarted(BuildEvent event) { |
| setAfterTaskEvent(false); |
| Project eventProject = event.getProject(); |
| if (getInitialProperties() == null) { |
| fInitialProperties = eventProject.getProperties(); |
| } |
| if (fProjectToTargetNames.get(eventProject) == null) { |
| Object ref = eventProject.getReference("eclipse.ant.targetVector"); //$NON-NLS-1$ |
| if (ref != null) { |
| fProjectToTargetNames.put(eventProject, (Vector<?>) ref); |
| HashMap<Target, Vector<Target>> targetToBuildSequence = new HashMap<Target, Vector<Target>>(); |
| setTargetToExecute(initializeBuildSequenceInformation(event, targetToBuildSequence)); |
| fProjectToMapOfTargetToBuildSequence.put(eventProject, targetToBuildSequence); |
| } |
| } |
| |
| setTargetExecuting(event.getTarget()); |
| if (event.getTarget().equals(getTargetToExecute())) { |
| // the dependencies of the target to execute have been met |
| // prepare for the next target |
| Vector<?> targets = fProjectToTargetNames.get(eventProject); |
| if (!targets.isEmpty()) { |
| setTargetToExecute(eventProject.getTargets().get(targets.remove(0))); |
| } else { |
| setTargetToExecute(null); |
| } |
| } |
| setConsiderTargetBreakpoints(true); |
| } |
| |
| public int getLineNumber(Location location) { |
| try { // succeeds with Ant newer than 1.6 |
| return location.getLineNumber(); |
| } |
| catch (NoSuchMethodError e) { |
| // Ant before 1.6 |
| String locationString = location.toString(); |
| if (locationString.length() == 0) { |
| return 0; |
| } |
| // filename: lineNumber: ("c:\buildfile.xml: 12: ") |
| int lastIndex = locationString.lastIndexOf(':'); |
| int index = locationString.lastIndexOf(':', lastIndex - 1); |
| if (index != -1) { |
| try { |
| return Integer.parseInt(locationString.substring(index + 1, lastIndex)); |
| } |
| catch (NumberFormatException nfe) { |
| return 0; |
| } |
| } |
| return 0; |
| } |
| } |
| |
| public static Location getLocation(Target target) { |
| try {// succeeds with Ant newer than 1.6.2 |
| return target.getLocation(); |
| } |
| catch (NoSuchMethodError e) { |
| return Location.UNKNOWN_LOCATION; |
| } |
| } |
| |
| public String getFileName(Location location) { |
| try {// succeeds with Ant newer than 1.6 |
| return location.getFileName(); |
| } |
| catch (NoSuchMethodError e) { |
| // Ant before 1.6 |
| String locationString = location.toString(); |
| if (locationString.length() == 0) { |
| return null; |
| } |
| // filename: lineNumber: ("c:\buildfile.xml: 12: ") |
| int lastIndex = locationString.lastIndexOf(':'); |
| int index = locationString.lastIndexOf(':', lastIndex - 1); |
| if (index == -1) { |
| index = lastIndex; // only the filename is known |
| } |
| if (index != -1) { |
| // bug 84403 |
| // if (locationString.startsWith("file:")) { |
| // return FileUtils.newFileUtils().fromURI(locationString); |
| // } |
| // remove file: |
| return locationString.substring(5, index); |
| } |
| return null; |
| } |
| } |
| |
| private void appendToStack(StringBuffer stackRepresentation, String targetName, String taskName, Location location) { |
| stackRepresentation.append(targetName); |
| stackRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); |
| stackRepresentation.append(taskName); |
| stackRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); |
| |
| stackRepresentation.append(getFileName(location)); |
| stackRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); |
| stackRepresentation.append(getLineNumber(location)); |
| stackRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); |
| } |
| |
| public void marshalStack(StringBuffer stackRepresentation) { |
| Stack<Task> tasks = getTasks(); |
| |
| stackRepresentation.append(DebugMessageIds.STACK); |
| stackRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); |
| |
| Target targetToExecute = getTargetToExecute(); |
| Target targetExecuting = getTargetExecuting(); |
| |
| Project projectExecuting = null; |
| if (targetExecuting != null) { |
| projectExecuting = targetExecuting.getProject(); |
| } else if (!tasks.empty()) { // no target...must be a task |
| Task task = tasks.peek(); |
| projectExecuting = task.getProject(); |
| } |
| |
| if (!isAfterTaskEvent()) { |
| appendToStack(stackRepresentation, targetExecuting.getName(), IAntCoreConstants.EMPTY_STRING, getLocation(targetExecuting)); |
| } |
| for (int i = tasks.size() - 1; i >= 0; i--) { |
| Task task = tasks.get(i); |
| if (task.getProject() == projectExecuting) { |
| appendToStack(stackRepresentation, task.getOwningTarget().getName(), task.getTaskName(), task.getLocation()); |
| } else { |
| // sub build target dependencies |
| String targetName = task.getOwningTarget().getName(); |
| if (targetName != null && targetName.length() != 0) { // skip for implicit target |
| Iterator<Target> itr = fTargetsToExecute.iterator(); |
| while (itr.hasNext()) { |
| Target target = itr.next(); |
| if (target.getProject() != projectExecuting) { |
| targetToExecute = target; |
| continue; |
| } |
| marshalTargetDependancyStack(stackRepresentation, target, targetExecuting); |
| } |
| } |
| projectExecuting = task.getProject(); |
| targetExecuting = task.getOwningTarget(); |
| appendToStack(stackRepresentation, targetExecuting.getName(), task.getTaskName(), task.getLocation()); |
| } |
| } |
| |
| // target dependency stack |
| marshalTargetDependancyStack(stackRepresentation, targetToExecute, targetExecuting); |
| } |
| |
| private void marshalTargetDependancyStack(StringBuffer stackRepresentation, Target targetToExecute, Target targetExecuting) { |
| if (targetToExecute != null) { |
| Vector<Target> buildSequence = getTargetToBuildSequence(targetToExecute.getProject()).get(targetToExecute); |
| int startIndex = buildSequence.indexOf(targetExecuting) + 1; |
| int dependancyStackDepth = buildSequence.indexOf(targetToExecute); |
| |
| Target stackTarget; |
| for (int i = startIndex; i <= dependancyStackDepth; i++) { |
| stackTarget = buildSequence.get(i); |
| if (stackTarget.dependsOn(targetExecuting.getName())) { |
| appendToStack(stackRepresentation, stackTarget.getName(), IAntCoreConstants.EMPTY_STRING, getLocation(stackTarget)); |
| } |
| } |
| } |
| } |
| |
| public void marshallProperties(StringBuffer propertiesRepresentation, boolean escapeLineSep) { |
| Stack<Task> tasks = getTasks(); |
| if (tasks.isEmpty()) { |
| return; |
| } |
| propertiesRepresentation.append(DebugMessageIds.PROPERTIES); |
| propertiesRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); |
| |
| Project project = tasks.peek().getProject(); |
| Map<String, Object> lastProperties = getProperties(); |
| |
| Map<String, Object> currentProperties = project.getProperties(); |
| if (lastProperties != null && currentProperties.size() == lastProperties.size()) { |
| // no new properties |
| return; |
| } |
| |
| Map<String, Object> initialProperties = getInitialProperties(); |
| Map<String, Object> currentUserProperties = project.getUserProperties(); |
| String originalPropertyName; |
| String propertyValue; |
| for (String propertyName : currentProperties.keySet()) { |
| originalPropertyName = propertyName; |
| if (lastProperties == null || lastProperties.get(propertyName) == null) { // new property |
| if (escapeLineSep) { |
| propertyName = escapeLineSeparator(propertyName); |
| } |
| propertiesRepresentation.append(propertyName.length()); |
| propertiesRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); |
| propertiesRepresentation.append(propertyName); |
| propertiesRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); |
| propertyValue = (String) currentProperties.get(originalPropertyName); |
| if (escapeLineSep) { |
| propertyValue = escapeLineSeparator(propertyValue); |
| } |
| propertiesRepresentation.append(propertyValue.length()); |
| propertiesRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); |
| propertiesRepresentation.append(propertyValue); |
| propertiesRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); |
| propertiesRepresentation.append(getPropertyType(initialProperties, currentUserProperties, originalPropertyName)); |
| propertiesRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); |
| } |
| } |
| propertiesRepresentation.deleteCharAt(propertiesRepresentation.length() - 1); |
| fProperties = currentProperties; |
| } |
| |
| private int getPropertyType(Map<String, Object> initialProperties, Map<String, Object> currentUserProperties, String propertyName) { |
| if (initialProperties.get(propertyName) != null) { // properties set before the start of the build |
| if (currentUserProperties.get(propertyName) == null) { |
| return DebugMessageIds.PROPERTY_SYSTEM; |
| } |
| return DebugMessageIds.PROPERTY_USER; |
| } else if (currentUserProperties.get(propertyName) == null) { |
| return DebugMessageIds.PROPERTY_RUNTIME; |
| } else { |
| return DebugMessageIds.PROPERTY_USER; |
| } |
| } |
| |
| private String escapeLineSeparator(String stringToEscape) { |
| if (!(stringToEscape.indexOf('\r') != -1 || stringToEscape.indexOf('\n') != -1 || stringToEscape.indexOf("\\r") != -1 || stringToEscape.indexOf("\\n") != -1)) { //$NON-NLS-1$ //$NON-NLS-2$ |
| return stringToEscape; |
| } |
| StringBuffer escapedValue = new StringBuffer(stringToEscape); |
| for (int i = 0; i < escapedValue.length(); i++) { |
| switch (escapedValue.charAt(i)) { |
| case '\r': |
| escapedValue.replace(i, i + 1, "\\r"); //$NON-NLS-1$ |
| i++; |
| break; |
| case '\n': |
| escapedValue.replace(i, i + 1, "\\n"); //$NON-NLS-1$ |
| i++; |
| break; |
| case '\\': |
| if (escapedValue.charAt(i + 1) == 'r' || escapedValue.charAt(i + 1) == 'n') { |
| escapedValue.replace(i, i + 1, "\\\\"); //$NON-NLS-1$ |
| i++; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| return escapedValue.toString(); |
| } |
| |
| private Target initializeBuildSequenceInformation(BuildEvent event, Map<Target, Vector<Target>> targetToBuildSequence) { |
| Project antProject = event.getProject(); |
| Vector<String> targets = (Vector<String>) antProject.getReference("eclipse.ant.targetVector"); //$NON-NLS-1$ |
| if (targets == null) { |
| return null; |
| } |
| Hashtable<String, Target> allTargets = antProject.getTargets(); |
| Vector<Target> sortedTargets; |
| for (String targetName : targets) { |
| sortedTargets = antProject.topoSort(targetName, allTargets); |
| targetToBuildSequence.put(allTargets.get(targetName), sortedTargets); |
| } |
| // the target to execute |
| return allTargets.get(targets.remove(0)); |
| } |
| } |