<commit_before>/******************************************************************************
 * Copyright (c) 2000-2015 <NAME> AB
 * 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
 ******************************************************************************/
package org.eclipse.titan.designer.core;

import java.io.File;
import java.net.URI;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.ICommand;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IWorkspaceDescription;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.core.runtime.preferences.IPreferencesService;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.titan.common.logging.ErrorReporter;
import org.eclipse.titan.common.path.PathConverter;
import org.eclipse.titan.common.path.TITANPathUtilities;
import org.eclipse.titan.common.utils.Cygwin;
import org.eclipse.titan.designer.Activator;
import org.eclipse.titan.designer.GeneralConstants;
import org.eclipse.titan.designer.consoles.TITANConsole;
import org.eclipse.titan.designer.consoles.TITANDebugConsole;
import org.eclipse.titan.designer.core.makefile.ExternalMakefileGenerator;
import org.eclipse.titan.designer.core.makefile.InternalMakefileGenerator;
import org.eclipse.titan.designer.decorators.TITANDecorator;
import org.eclipse.titan.designer.graphics.ImageCache;
import org.eclipse.titan.designer.license.LicenseValidator;
import org.eclipse.titan.designer.parsers.GlobalParser;
import org.eclipse.titan.designer.parsers.GlobalProjectStructureTracker;
import org.eclipse.titan.designer.parsers.ProjectSourceParser;
import org.eclipse.titan.designer.preferences.PreferenceConstantValues;
import org.eclipse.titan.designer.preferences.PreferenceConstants;
import org.eclipse.titan.designer.productUtilities.ProductConstants;
import org.eclipse.titan.designer.properties.IPropertyChangeListener;
import org.eclipse.titan.designer.properties.PropertyNotificationManager;
import org.eclipse.titan.designer.properties.data.MakeAttributesData;
import org.eclipse.titan.designer.properties.data.MakefileCreationData;
import org.eclipse.titan.designer.properties.data.ProjectBuildPropertyData;
import org.eclipse.ui.progress.IProgressConstants;

/**
 * The heart of the build system.
 * 
 * @author <NAME>
 */
public final class TITANBuilder extends IncrementalProjectBuilder {
	private static final String TITAN_GIF = "titan.gif";
	public static final String BUILDER_ID = ProductConstants.PRODUCT_ID_DESIGNER + ".core.TITANBuilder";
	static final String EMPTY_STRING = "";
	static final String APOSTROPHE = "'";
	static final String TRUE = "true";
	static final String FALSE = "false";
	static final String ERROR = "error";
	static final String BUILD_PROCESS = "Build process";
	static final String MAKEFILEGENERATOR = "ttcn3_makefilegen";
	static final String BIN_DIRECTORY = "bin";
	static final String MAKE = "make";
	static final String DEP = "dep";
	static final String CHECK = "check";
	static final String CLEAN = "clean";
	static final String MAKE_CHECK = "make check";
	static final String MAKE_COMPILE = "make compile";
	static final String MAKE_DEP = "make dep";
	static final String MAKE_ALL = "make all";
	static final String MAKE_OBJECTS = "make objects";
	static final String MAKE_SHARED_OBJECTS = "make shared_objects";
	static final String MAKE_CLEAN = "make clean";
	static final String CREATE_DEPENDENCY = "Create dependency";
	static final String CREATE_MAKEFILE = "Create makefile";
	static final String MAKEFILE = "Makefile";
	static final String TEMPORARY_MAKEFILE = "Makefile.tmp";
	static final String REMOVE = "rm";
	static final String MOVE = "mv";
	static final String FORCE_EXECUTION = "-f";
	static final String RENAMING_MAKEFILE = "Renaming " + TEMPORARY_MAKEFILE + " to " + MAKEFILE;
	static final String RUNNING_UPDATER = "Running updater script";
	static final String REMOVING_MAKEFILE = "Removing " + MAKEFILE;
	static final String REMOVING_TEMPORARY_MAKEFILE = "Removing " + TEMPORARY_MAKEFILE;
	static final String REMOVING_EXECUTABLE = "Removing outdated executable";
	static final String REMOVE_COMPILE_FILE = "removing compile file";
	static final String BUILD_WITHOUT_MAKEFILE_ERROR = "The build process is not able to execute,"
			+ " as a Makefile was not found in the working directory and automatic Makefile generation is not enabled for project ";
	static final String CLEAN_WITHOUT_MAKEFILE_ERROR = "make clean for project {0} was not issued as the Makefile is missing.";
	static final String PROCESSINGUNITSTOUSENOTSET = "Note:"
			+ " The processing units to use property of this project is not set, but could enhance build speed.";
	static final String FEWPROCESSINGUNITTOUSESET = "Note:"
			+ " The build process is set to use only {0} parallel processing units, while there are {1} available.";

	private static final String MISSING_CYGWIN = "Build failed. No cygwin installation found.";


	private static IPropertyChangeListener listener = new IPropertyChangeListener() {

		@Override
		public void propertyChanged(final IResource resouce) {
			WorkspaceJob refreshJob = new WorkspaceJob("Refreshing built resources") {
				@Override
				public IStatus runInWorkspace(final IProgressMonitor monitor) {
					boolean proceedingOK = SymbolicLinkHandler.createSymlinks(resouce);
					if (proceedingOK) {
						proceedingOK = TITANBuilder.removeExecutable(resouce.getProject());
					}
					if (proceedingOK) {
						proceedingOK = TITANBuilder.removeMakefile(resouce.getProject());
					}
					if (proceedingOK) {
						TITANBuilder.invokeBuild(resouce.getProject());
					}

					return Status.OK_STATUS;
				}
			};
			refreshJob.setPriority(Job.LONG);
			refreshJob.setUser(false);
			refreshJob.setSystem(true);
			refreshJob.setProperty(IProgressConstants.ICON_PROPERTY, ImageCache.getImageDescriptor("titan.gif"));
			refreshJob.schedule();
		}
	};
	static {
		PropertyNotificationManager.addListener(listener);
	}

	/**
	 * This method returns a resource delta visitor that has visited the
	 * project resource delta.
	 * 
	 * 
	 * @param delta
	 *                - the delta to be visited by the visitor
	 * @return the TITANBuilderResourceDeltaVisitor that has visited the
	 *         project resource delta
	 */
	private TITANBuilderResourceDeltaVisitor getDeltaVisitor(final IResourceDelta delta) {
		TITANBuilderResourceDeltaVisitor visitor = new TITANBuilderResourceDeltaVisitor(delta);
		if (delta != null) {
			try {
				delta.accept(visitor);
			} catch (CoreException e) {
				ErrorReporter.logExceptionStackTrace("While visiting resource delta: " + delta, e);
			}
		}
		return visitor;
	}

	/**
	 * Removes the executable of the provided project.
	 * 
	 * @param project
	 *                the project whose executable must be removed
	 * @return true if the operation was successful, false otherwise.
	 */
	public static boolean removeExecutable(final IProject project) {
		return removeExecutable(project, new ArrayList<IProject>(), false);
	}

	/**
	 * Removes the executable of the provided project.
	 * 
	 * @param project
	 *                the project whose executable must be removed
	 * @param processedProjects
	 *                the list of projects already processed, to stop
	 *                cycles.
	 * @return true if the operation was successful, false otherwise.
	 */
	private static boolean removeExecutable(final IProject project, final List<IProject> processedProjects, final boolean sync) {
		try {
			String targetExecutable = project.getPersistentProperty(new QualifiedName(ProjectBuildPropertyData.QUALIFIER,
					MakefileCreationData.TARGET_EXECUTABLE_PROPERTY));
			return removeExecutable(project, targetExecutable, processedProjects, sync);
		} catch (CoreException e) {
			ErrorReporter.logExceptionStackTrace("Target executable setting could not be read", e);
		}

		return false;
	}

	/**
	 * Removes the out dated executable. This must be done when the name or
	 * the execution mode of the generated executable changes.
	 * 
	 * @param project
	 *                the project whose executable must be removed
	 * @param targetExecutable
	 *                the name of the executable to be deleted
	 * @param processedProjects
	 *                the list of projects already processed, to stop
	 *                cycles.
	 * @return true if the operation was successful, false otherwise.
	 */
	public static boolean removeExecutable(final IProject project, final String targetExecutable, final List<IProject> processedProjects) {
		return removeExecutable(project, targetExecutable, processedProjects, false);
	}

	/**
	 * Removes the out dated executable. This must be done when the name or
	 * the execution mode of the generated executable changes.
	 * 
	 * @param project
	 *                the project whose executable must be removed
	 * @param targetExecutable
	 *                the name of the executable to be deleted
	 * @param processedProjects
	 *                the list of projects already processed, to stop
	 *                cycles.
	 * @param sync
	 *                if true, the function will not return until the
	 *                makefile is removed
	 * @return true if the operation was successful, false otherwise.
	 */
	public static boolean removeExecutable(final IProject project, final String targetExecutable, final List<IProject> processedProjects,
			final boolean sync) {
		if (!isBuilderEnabled(project)) {
			return true;
		}

		if (processedProjects.contains(project)) {
			return true;
		}
		processedProjects.add(project);

		String realTargetExecutable = targetExecutable;

		List<String> command;
		IPath workingDir = ProjectBasedBuilder.getProjectBasedBuilder(project).getWorkingDirectoryPath(false);
		if (workingDir == null || !workingDir.toFile().exists()) {
			return true;
		}

		if (EMPTY_STRING.equals(realTargetExecutable)) {
			realTargetExecutable = project.getName();
			if (Platform.OS_WIN32.equals(Platform.getOS())) {
				realTargetExecutable += ".exe";
			}
		}
		if (realTargetExecutable != null && realTargetExecutable.length() > 0) {
			// start preparing the command, which will remove the
			// executable
			command = new ArrayList<String>();
			command.add(REMOVE);
			command.add(FORCE_EXECUTION);

			boolean reportDebugInformation = Platform.getPreferencesService().getBoolean(ProductConstants.PRODUCT_ID_DESIGNER,
					PreferenceConstants.DISPLAYDEBUGINFORMATION, false, null);

			if ((new File(realTargetExecutable)).exists()) {
				// if the executable to be removed is given with
				// full path
				command.add(APOSTROPHE
						+ PathConverter.convert(realTargetExecutable, reportDebugInformation, TITANDebugConsole.getConsole())
						+ APOSTROPHE);
			} else if ((new File(workingDir.toOSString() + File.separatorChar + realTargetExecutable)).exists()) {
				// if the executable to be removed is given only
				// with a file name
				command.add(APOSTROPHE
						+ PathConverter.convert(workingDir.toOSString() + File.separatorChar + realTargetExecutable,
								reportDebugInformation, TITANDebugConsole.getConsole()) + APOSTROPHE);
			} else {
				// the file does not seem to exist, so it does
				// not need to be removed ;)
				return true;
			}

			// create and schedule the job, that will execute the
			// command
			TITANJob job = new TITANJob(BUILD_PROCESS, new HashMap<String, IFile>(), workingDir.toFile(), project);
			job.setPriority(Job.DECORATE);
			job.setUser(true);
			job.addCommand(command, REMOVING_EXECUTABLE);
			if (sync) {
				job.runInWorkspace(new NullProgressMonitor());
			} else {
				job.setRule(project);
				job.schedule();
			}
		}

		IProject[] referencedProjects = ProjectBasedBuilder.getProjectBasedBuilder(project).getReferencingProjects();
		for (IProject tempProject : referencedProjects) {
			removeExecutable(tempProject, processedProjects, sync);
		}

		return true;
	}

	/**
	 * Forces the refresh of the Makefile. This must be done when some
	 * project settings changed. (but only in automatic project management
	 * mode)
	 * 
	 * @param project
	 *                the project whose Makefile must be removed.
	 * 
	 * @return true if the operation was successful, false otherwise.
	 */
	public static boolean removeMakefile(final IProject project) {
		return removeMakefile(project, new ArrayList<IProject>());
	}

	/**
	 * Forces the refresh of the Makefile. This must be done when some
	 * project settings changed. (but only in automatic project management
	 * mode)
	 * 
	 * @param project
	 *                the project whose Makefile must be removed.
	 * @param processedProjects
	 *                the list of projects already processed, to stop
	 *                cycles.
	 * 
	 * @return true if the operation was successful, false otherwise.
	 */
	private static boolean removeMakefile(final IProject project, final List<IProject> processedProjects) {
		if (!isBuilderEnabled(project)) {
			return true;
		}

		if (processedProjects.contains(project)) {
			return true;
		}
		processedProjects.add(project);

		String needsMakefile = null;

		try {
			needsMakefile = project.getPersistentProperty(new QualifiedName(ProjectBuildPropertyData.QUALIFIER,
					ProjectBuildPropertyData.GENERATE_MAKEFILE_PROPERTY));
		} catch (CoreException e) {
			ErrorReporter.logExceptionStackTrace("While checking wheather to generate makefile or not", e);
		}

		IPath workingDir = ProjectBasedBuilder.getProjectBasedBuilder(project).getWorkingDirectoryPath(true);
		if (workingDir == null || !workingDir.toFile().exists()) {
			return true;
		}

		if (TRUE.equals(needsMakefile)) {
			boolean usesInternal;
			try {
				usesInternal = "true".equals(project.getPersistentProperty(new QualifiedName(ProjectBuildPropertyData.QUALIFIER,
						ProjectBuildPropertyData.GENERATE_INTERNAL_MAKEFILE_PROPERTY)));
			} catch (CoreException e) {
				usesInternal = false;
			}
			if (usesInternal) {
				// in case of the internal Makefile generator it
				// is better to run it and refresh the Makefile
				// instead of just deleting it
				// this looks better on the user interface as
				// the file does not disappears from the user.
				InternalMakefileGenerator makefileGenerator = new InternalMakefileGenerator();
				makefileGenerator.generateMakefile(project);
			} else {
				File file = new File(workingDir.toOSString() + File.separatorChar + MAKEFILE);
				if (file.exists()) {
					file.delete();
				}
			}
		}

		IProject[] referencedProjects = ProjectBasedBuilder.getProjectBasedBuilder(project).getReferencingProjects();
		for (IProject tempProject : referencedProjects) {
			removeMakefile(tempProject, processedProjects);
		}

		return true;
	}

	/**
	 * Cleans the provided project for a rebuild. Involves cleaning,
	 * removing the makefile and touching the project.
	 * 
	 * @param project
	 * @param sync
	 * */
	public static void cleanProjectForRebuild(final IProject project, final boolean sync) {
		if (project == null || project.getWorkspace() == null) {
			// mystic error
			return;
		}

		WorkspaceJob refreshJob = new WorkspaceJob("Build") {
			@Override
			public IStatus runInWorkspace(final IProgressMonitor monitor) {
				try {
					project.build(IncrementalProjectBuilder.CLEAN_BUILD, new NullProgressMonitor());
				} catch (CoreException e) {
					ErrorReporter.logExceptionStackTrace("While cleaning `" + project.getName() + "'", e);
				}
				TITANBuilder.removeMakefile(project, new ArrayList<IProject>());
				ProjectBasedBuilder.setForcedMakefileRebuild(project);
				ProjectBasedBuilder.setForcedBuild(project);
				try {
					project.touch(null);
				} catch (CoreException e) {
					ErrorReporter.logExceptionStackTrace("While touching `" + project.getName() + "'", e);
				}

				return Status.OK_STATUS;<commit_msg>add support for regenerating the Makefile<commit_after>/******************************************************************************
 * Copyright (c) 2000-2015 <NAME> AB
 * 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
 ******************************************************************************/
package org.eclipse.titan.designer.core;

import java.io.File;
import java.net.URI;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.ICommand;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IWorkspaceDescription;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.core.runtime.preferences.IPreferencesService;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.titan.common.logging.ErrorReporter;
import org.eclipse.titan.common.path.PathConverter;
import org.eclipse.titan.common.path.TITANPathUtilities;
import org.eclipse.titan.common.utils.Cygwin;
import org.eclipse.titan.designer.Activator;
import org.eclipse.titan.designer.GeneralConstants;
import org.eclipse.titan.designer.consoles.TITANConsole;
import org.eclipse.titan.designer.consoles.TITANDebugConsole;
import org.eclipse.titan.designer.core.makefile.ExternalMakefileGenerator;
import org.eclipse.titan.designer.core.makefile.InternalMakefileGenerator;
import org.eclipse.titan.designer.decorators.TITANDecorator;
import org.eclipse.titan.designer.graphics.ImageCache;
import org.eclipse.titan.designer.license.LicenseValidator;
import org.eclipse.titan.designer.parsers.GlobalParser;
import org.eclipse.titan.designer.parsers.GlobalProjectStructureTracker;
import org.eclipse.titan.designer.parsers.ProjectSourceParser;
import org.eclipse.titan.designer.preferences.PreferenceConstantValues;
import org.eclipse.titan.designer.preferences.PreferenceConstants;
import org.eclipse.titan.designer.productUtilities.ProductConstants;
import org.eclipse.titan.designer.properties.IPropertyChangeListener;
import org.eclipse.titan.designer.properties.PropertyNotificationManager;
import org.eclipse.titan.designer.properties.data.MakeAttributesData;
import org.eclipse.titan.designer.properties.data.MakefileCreationData;
import org.eclipse.titan.designer.properties.data.ProjectBuildPropertyData;
import org.eclipse.ui.progress.IProgressConstants;

/**
 * The heart of the build system.
 * 
 * @author <NAME>
 */
public final class TITANBuilder extends IncrementalProjectBuilder {
	private static final String TITAN_GIF = "titan.gif";
	public static final String BUILDER_ID = ProductConstants.PRODUCT_ID_DESIGNER + ".core.TITANBuilder";
	static final String EMPTY_STRING = "";
	static final String APOSTROPHE = "'";
	static final String TRUE = "true";
	static final String FALSE = "false";
	static final String ERROR = "error";
	static final String BUILD_PROCESS = "Build process";
	static final String MAKEFILEGENERATOR = "ttcn3_makefilegen";
	static final String BIN_DIRECTORY = "bin";
	static final String MAKE = "make";
	static final String DEP = "dep";
	static final String CHECK = "check";
	static final String CLEAN = "clean";
	static final String MAKE_CHECK = "make check";
	static final String MAKE_COMPILE = "make compile";
	static final String MAKE_DEP = "make dep";
	static final String MAKE_ALL = "make all";
	static final String MAKE_OBJECTS = "make objects";
	static final String MAKE_SHARED_OBJECTS = "make shared_objects";
	static final String MAKE_CLEAN = "make clean";
	static final String CREATE_DEPENDENCY = "Create dependency";
	static final String CREATE_MAKEFILE = "Create makefile";
	static final String MAKEFILE = "Makefile";
	static final String TEMPORARY_MAKEFILE = "Makefile.tmp";
	static final String REMOVE = "rm";
	static final String MOVE = "mv";
	static final String FORCE_EXECUTION = "-f";
	static final String RENAMING_MAKEFILE = "Renaming " + TEMPORARY_MAKEFILE + " to " + MAKEFILE;
	static final String RUNNING_UPDATER = "Running updater script";
	static final String REMOVING_MAKEFILE = "Removing " + MAKEFILE;
	static final String REMOVING_TEMPORARY_MAKEFILE = "Removing " + TEMPORARY_MAKEFILE;
	static final String REMOVING_EXECUTABLE = "Removing outdated executable";
	static final String REMOVE_COMPILE_FILE = "removing compile file";
	static final String BUILD_WITHOUT_MAKEFILE_ERROR = "The build process is not able to execute,"
			+ " as a Makefile was not found in the working directory and automatic Makefile generation is not enabled for project ";
	static final String CLEAN_WITHOUT_MAKEFILE_ERROR = "make clean for project {0} was not issued as the Makefile is missing.";
	static final String PROCESSINGUNITSTOUSENOTSET = "Note:"
			+ " The processing units to use property of this project is not set, but could enhance build speed.";
	static final String FEWPROCESSINGUNITTOUSESET = "Note:"
			+ " The build process is set to use only {0} parallel processing units, while there are {1} available.";

	private static final String MISSING_CYGWIN = "Build failed. No cygwin installation found.";


	private static IPropertyChangeListener listener = new IPropertyChangeListener() {

		@Override
		public void propertyChanged(final IResource resouce) {
			WorkspaceJob refreshJob = new WorkspaceJob("Refreshing built resources") {
				@Override
				public IStatus runInWorkspace(final IProgressMonitor monitor) {
					boolean proceedingOK = SymbolicLinkHandler.createSymlinks(resouce);
					if (proceedingOK) {
						proceedingOK = TITANBuilder.removeExecutable(resouce.getProject());
					}
					if (proceedingOK) {
						proceedingOK = TITANBuilder.removeMakefile(resouce.getProject());
					}
					if (proceedingOK) {
						TITANBuilder.invokeBuild(resouce.getProject());
					}

					return Status.OK_STATUS;
				}
			};
			refreshJob.setPriority(Job.LONG);
			refreshJob.setUser(false);
			refreshJob.setSystem(true);
			refreshJob.setProperty(IProgressConstants.ICON_PROPERTY, ImageCache.getImageDescriptor("titan.gif"));
			refreshJob.schedule();
		}
	};
	static {
		PropertyNotificationManager.addListener(listener);
	}

	/**
	 * This method returns a resource delta visitor that has visited the
	 * project resource delta.
	 * 
	 * 
	 * @param delta
	 *                - the delta to be visited by the visitor
	 * @return the TITANBuilderResourceDeltaVisitor that has visited the
	 *         project resource delta
	 */
	private TITANBuilderResourceDeltaVisitor getDeltaVisitor(final IResourceDelta delta) {
		TITANBuilderResourceDeltaVisitor visitor = new TITANBuilderResourceDeltaVisitor(delta);
		if (delta != null) {
			try {
				delta.accept(visitor);
			} catch (CoreException e) {
				ErrorReporter.logExceptionStackTrace("While visiting resource delta: " + delta, e);
			}
		}
		return visitor;
	}

	/**
	 * Removes the executable of the provided project.
	 * 
	 * @param project
	 *                the project whose executable must be removed
	 * @return true if the operation was successful, false otherwise.
	 */
	public static boolean removeExecutable(final IProject project) {
		return removeExecutable(project, new ArrayList<IProject>(), false);
	}

	/**
	 * Removes the executable of the provided project.
	 * 
	 * @param project
	 *                the project whose executable must be removed
	 * @param processedProjects
	 *                the list of projects already processed, to stop
	 *                cycles.
	 * @return true if the operation was successful, false otherwise.
	 */
	private static boolean removeExecutable(final IProject project, final List<IProject> processedProjects, final boolean sync) {
		try {
			String targetExecutable = project.getPersistentProperty(new QualifiedName(ProjectBuildPropertyData.QUALIFIER,
					MakefileCreationData.TARGET_EXECUTABLE_PROPERTY));
			return removeExecutable(project, targetExecutable, processedProjects, sync);
		} catch (CoreException e) {
			ErrorReporter.logExceptionStackTrace("Target executable setting could not be read", e);
		}

		return false;
	}

	/**
	 * Removes the out dated executable. This must be done when the name or
	 * the execution mode of the generated executable changes.
	 * 
	 * @param project
	 *                the project whose executable must be removed
	 * @param targetExecutable
	 *                the name of the executable to be deleted
	 * @param processedProjects
	 *                the list of projects already processed, to stop
	 *                cycles.
	 * @return true if the operation was successful, false otherwise.
	 */
	public static boolean removeExecutable(final IProject project, final String targetExecutable, final List<IProject> processedProjects) {
		return removeExecutable(project, targetExecutable, processedProjects, false);
	}

	/**
	 * Removes the out dated executable. This must be done when the name or
	 * the execution mode of the generated executable changes.
	 * 
	 * @param project
	 *                the project whose executable must be removed
	 * @param targetExecutable
	 *                the name of the executable to be deleted
	 * @param processedProjects
	 *                the list of projects already processed, to stop
	 *                cycles.
	 * @param sync
	 *                if true, the function will not return until the
	 *                makefile is removed
	 * @return true if the operation was successful, false otherwise.
	 */
	public static boolean removeExecutable(final IProject project, final String targetExecutable, final List<IProject> processedProjects,
			final boolean sync) {
		if (!isBuilderEnabled(project)) {
			return true;
		}

		if (processedProjects.contains(project)) {
			return true;
		}
		processedProjects.add(project);

		String realTargetExecutable = targetExecutable;

		List<String> command;
		IPath workingDir = ProjectBasedBuilder.getProjectBasedBuilder(project).getWorkingDirectoryPath(false);
		if (workingDir == null || !workingDir.toFile().exists()) {
			return true;
		}

		if (EMPTY_STRING.equals(realTargetExecutable)) {
			realTargetExecutable = project.getName();
			if (Platform.OS_WIN32.equals(Platform.getOS())) {
				realTargetExecutable += ".exe";
			}
		}
		if (realTargetExecutable != null && realTargetExecutable.length() > 0) {
			// start preparing the command, which will remove the
			// executable
			command = new ArrayList<String>();
			command.add(REMOVE);
			command.add(FORCE_EXECUTION);

			boolean reportDebugInformation = Platform.getPreferencesService().getBoolean(ProductConstants.PRODUCT_ID_DESIGNER,
					PreferenceConstants.DISPLAYDEBUGINFORMATION, false, null);

			if ((new File(realTargetExecutable)).exists()) {
				// if the executable to be removed is given with
				// full path
				command.add(APOSTROPHE
						+ PathConverter.convert(realTargetExecutable, reportDebugInformation, TITANDebugConsole.getConsole())
						+ APOSTROPHE);
			} else if ((new File(workingDir.toOSString() + File.separatorChar + realTargetExecutable)).exists()) {
				// if the executable to be removed is given only
				// with a file name
				command.add(APOSTROPHE
						+ PathConverter.convert(workingDir.toOSString() + File.separatorChar + realTargetExecutable,
								reportDebugInformation, TITANDebugConsole.getConsole()) + APOSTROPHE);
			} else {
				// the file does not seem to exist, so it does
				// not need to be removed ;)
				return true;
			}

			// create and schedule the job, that will execute the
			// command
			TITANJob job = new TITANJob(BUILD_PROCESS, new HashMap<String, IFile>(), workingDir.toFile(), project);
			job.setPriority(Job.DECORATE);
			job.setUser(true);
			job.addCommand(command, REMOVING_EXECUTABLE);
			if (sync) {
				job.runInWorkspace(new NullProgressMonitor());
			} else {
				job.setRule(project);
				job.schedule();
			}
		}

		IProject[] referencedProjects = ProjectBasedBuilder.getProjectBasedBuilder(project).getReferencingProjects();
		for (IProject tempProject : referencedProjects) {
			removeExecutable(tempProject, processedProjects, sync);
		}

		return true;
	}

	/**
	 * Forces the refresh of the Makefile. This must be done when some
	 * project settings changed. (but only in automatic project management
	 * mode)
	 * 
	 * @param project
	 *                the project whose Makefile must be removed.
	 * 
	 * @return true if the operation was successful, false otherwise.
	 */
	public static boolean removeMakefile(final IProject project) {
		return removeMakefile(project, new ArrayList<IProject>());
	}

	/**
	 * Forces the refresh of the Makefile. This must be done when some
	 * project settings changed. (but only in automatic project management
	 * mode)
	 * 
	 * @param project
	 *                the project whose Makefile must be removed.
	 * @param processedProjects
	 *                the list of projects already processed, to stop
	 *                cycles.
	 * 
	 * @return true if the operation was successful, false otherwise.
	 */
	private static boolean removeMakefile(final IProject project, final List<IProject> processedProjects) {
		if (!isBuilderEnabled(project)) {
			return true;
		}

		if (processedProjects.contains(project)) {
			return true;
		}
		processedProjects.add(project);

		String needsMakefile = null;

		try {
			needsMakefile = project.getPersistentProperty(new QualifiedName(ProjectBuildPropertyData.QUALIFIER,
					ProjectBuildPropertyData.GENERATE_MAKEFILE_PROPERTY));
		} catch (CoreException e) {
			ErrorReporter.logExceptionStackTrace("While checking wheather to generate makefile or not", e);
		}

		IPath workingDir = ProjectBasedBuilder.getProjectBasedBuilder(project).getWorkingDirectoryPath(true);
		if (workingDir == null || !workingDir.toFile().exists()) {
			return true;
		}

		if (TRUE.equals(needsMakefile)) {
			boolean usesInternal;
			try {
				usesInternal = "true".equals(project.getPersistentProperty(new QualifiedName(ProjectBuildPropertyData.QUALIFIER,
						ProjectBuildPropertyData.GENERATE_INTERNAL_MAKEFILE_PROPERTY)));
			} catch (CoreException e) {
				usesInternal = false;
			}
			if (usesInternal) {
				// in case of the internal Makefile generator it
				// is better to run it and refresh the Makefile
				// instead of just deleting it
				// this looks better on the user interface as
				// the file does not disappears from the user.
				InternalMakefileGenerator makefileGenerator = new InternalMakefileGenerator();
				makefileGenerator.generateMakefile(project);
			} else {
				File file = new File(workingDir.toOSString() + File.separatorChar + MAKEFILE);
				if (file.exists()) {
					file.delete();
				}
			}
		}

		IProject[] referencedProjects = ProjectBasedBuilder.getProjectBasedBuilder(project).getReferencingProjects();
		for (IProject tempProject : referencedProjects) {
			removeMakefile(tempProject, processedProjects);
		}

		return true;
	}

	/**
	 * Forces the refresh of the Makefile. This must be done when some
	 * project settings changed. (but only in automatic project management
	 * mode)
	 * 
	 * @param project
	 *                the project whose Makefile must be removed.
	 * 
	 * @return true if the operation was successful, false otherwise.
	 */
	public static boolean regenerateMakefile(final IProject project) {
		return regenerateMakefile(project, new ArrayList<IProject>());
	}

	/**
	 * Forces the refresh of the Makefile. This must be done when some
	 * project settings changed. (but only in automatic project management
	 * mode)
	 * 
	 * @param project
	 *                the project whose Makefile must be removed.
	 * @param processedProjects
	 *                the list of projects already processed, to stop
	 *                cycles.
	 * 
	 * @return true if the operation was successful, false otherwise.
	 */
	private static boolean regenerateMakefile(final IProject project, final List<IProject> processedProjects) {
		if (!isBuilderEnabled(project)) {
			return true;
		}

		if (processedProjects.contains(project)) {
			return true;
		}
		processedProjects.add(project);

		String needsMakefile = null;

		try {
			needsMakefile = project.getPersistentProperty(new QualifiedName(ProjectBuildPropertyData.QUALIFIER,
					ProjectBuildPropertyData.GENERATE_MAKEFILE_PROPERTY));
		} catch (CoreException e) {
			ErrorReporter.logExceptionStackTrace("While checking wheather to generate makefile or not", e);
		}

		IPath workingDir = ProjectBasedBuilder.getProjectBasedBuilder(project).getWorkingDirectoryPath(true);
		if (workingDir == null || !workingDir.toFile().exists()) {
			return true;
		}

		if (TRUE.equals(needsMakefile)) {
			boolean usesInternal;
			try {
				usesInternal = "true".equals(project.getPersistentProperty(new QualifiedName(ProjectBuildPropertyData.QUALIFIER,
						ProjectBuildPropertyData.GENERATE_INTERNAL_MAKEFILE_PROPERTY)));
			} catch (CoreException e) {
				usesInternal = false;
			}
			if (usesInternal) {
				// in case of the internal Makefile generator it
				// is better to run it and refresh the Makefile
				// instead of just deleting it
				// this looks better on the user interface as
				// the file does not disappears from the user.
				InternalMakefileGenerator makefileGenerator = new InternalMakefileGenerator();
				makefileGenerator.generateMakefile(project);
			} else {
				final TITANJob buildJob = new TITANJob(BUILD_PROCESS, new HashMap<String, IFile>(), workingDir.toFile(), project);
				buildJob.setPriority(Job.DECORATE);
				buildJob.setUser(true);
				buildJob.setRule(project);
				
				List<String> command = ExternalMakefileGenerator.createMakefilGeneratorCommand(project);
				buildJob.addCommand(command, CREATE_MAKEFILE);
				
				buildJob.schedule();
			}
		}

		IProject[] referencedProjects = ProjectBasedBuilder.getProjectBasedBuilder(project).getReferencingProjects();
		for (IProject tempProject : referencedProjects) {
			removeMakefile(tempProject, processedProjects);
		}

		return true;
	}

	/**
	 * Cleans the provided project for a rebuild. Involves cleaning,
	 * removing the makefile and touching the project.
	 * 
	 * @param project
	 * @param sync
	 * */
	public static void cleanProjectForRebuild(final IProject project, final boolean sync) {
		if (project == null || project.getWorkspace() == null) {
			// mystic error
			return;
		}

		WorkspaceJob refreshJob = new WorkspaceJob("Build") {
			@Override
			public IStatus runInWorkspace(final IProgressMonitor monitor) {
				try {
					project.build(IncrementalProjectBuilder.CLEAN_BUILD, new NullProgressMonitor());
				} catch (CoreException e) {
					ErrorReporter.logExceptionStackTrace("While cleaning `" + project.getName() + "'", e);
				}
				TITANBuilder.removeMakefile(project, new ArrayList<IProject>());
				ProjectBasedBuilder.setForcedMakefileRebuild(project);
				ProjectBasedBuilder.setForcedBuild(project);
				try {
					project.touch(null);
				} catch (CoreException e) {
					ErrorReporter.logExceptionStackTrace("While touching `" + project.getName() + "'", e);
				}

				return Status.OK_STATUS;
{'fraction_non_alphanumeric': 0.09618609107603865, 'fraction_numerical': 0.0010113645436882867, 'mean_word_length': 4.229505915100905, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6037900', 'n_tokens_mistral': 11583, 'n_tokens_neox': 10164, 'n_words': 2876}