public class VariantSyncPlugin extends AbstractUIPlugin {

	// The plug-in ID
	public static final String PLUGIN_ID = "de.tubs.variantsync.core"; //$NON-NLS-1$

	// The shared instance
	private static VariantSyncPlugin plugin;
	private static ResourceChangeHandler listener = new ResourceChangeHandler();
	private static ConfigurationProjectManager configurationProjectManager = new ConfigurationProjectManager();

	private List<IEventListener> listeners = new ArrayList<>();

	/**
	 * The constructor
	 */
	public VariantSyncPlugin() {}

	/*
	 * (non-Javadoc)
	 * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework. BundleContext)
	 */
	public void start(BundleContext ctxt) throws Exception {
		super.start(ctxt);
		plugin = this;

		DeltaFactoryManager
				.setExtensionLoader(new EclipseExtensionLoader<>(PLUGIN_ID, IDeltaFactory.extensionPointID, IDeltaFactory.extensionID, IDeltaFactory.class));

		configurationProjectManager.initalize();

		Display.getDefault().syncExec(new Runnable() {

			@Override
			public void run() {
				listenForActiveClass();
			}
		});
		addResourceChangeListener();
	}

	/*
	 * (non-Javadoc)
	 * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework. BundleContext)
	 */
	public void stop(BundleContext ctxt) throws Exception {
		configurationProjectManager.terminate();
		plugin = null;
		super.stop(ctxt);
	}

	/**
	 * Returns the shared instance
	 *
	 * @return the shared instance
	 */
	public static VariantSyncPlugin getDefault() {
		return plugin;
	}

	public static IWorkspaceRoot getWorkspace() {
		return ResourcesPlugin.getWorkspace().getRoot();
	}

	public static IWorkbenchWindow getActiveWorkbenchWindow() {
		return getDefault().getWorkbench().getActiveWorkbenchWindow();
	}

	public static Shell getShell() {
		return PlatformUI.getWorkbench().getModalDialogShellProvider().getShell();
	}

	public static ConfigurationProjectManager getConfigurationProjectManager() {
		return configurationProjectManager;
	}

	public static ConfigurationProject getActiveConfigurationProject() {
		return configurationProjectManager.getActiveConfigurationProject();
	}

	public static FeatureContextManager getActiveFeatureContextManager() {
		return configurationProjectManager.getActiveConfigurationProject().getFeatureContextManager();
	}

	public static MappingManager getActiveMappingManager() {
		return configurationProjectManager.getActiveConfigurationProject().getMappingManager();
	}

	public static PatchesManager getActivePatchesManager() {
		return configurationProjectManager.getActiveConfigurationProject().getPatchesManager();
	}

	/**
	 * Returns an image descriptor for the image file at the given plug-in relative path.
	 * 
	 * @param path the path
	 * @return the image descriptor
	 */
	public ImageDescriptor getImageDescriptor(String path) {
		return imageDescriptorFromPlugin(PLUGIN_ID, path);
	}

	/**
	 * Always good to have this static method as when dealing with IResources having a interface to get the editor is very handy
	 *
	 * @return
	 */
	public static ITextEditor getEditor() {
		if (getActiveWorkbenchWindow().getActivePage().getActiveEditor() instanceof ITextEditor) {
			return (ITextEditor) getActiveWorkbenchWindow().getActivePage().getActiveEditor();
		} else {
			return null;
		}
	}

	public static IFile getEditorInput() {
		if (VariantSyncPlugin.getActiveWorkbenchWindow() == null) return null;
		IEditorPart editorPart = VariantSyncPlugin.getActiveWorkbenchWindow().getActivePage().getActiveEditor();
		if (editorPart == null || !(editorPart instanceof IFileEditorInput)) return null;
		return ((IFileEditorInput) editorPart.getEditorInput()).getFile();
	}

	public static void addResourceChangeListener() {
		ResourcesPlugin.getWorkspace().addResourceChangeListener(listener);
		try {
			listener.registerSaveParticipant();
		} catch (CoreException e) {
			e.printStackTrace();
		}
	}

	public static void removeResourceChangeListener() {
		ResourcesPlugin.getWorkspace().removeResourceChangeListener(listener);
	}

	/**
	 * Listen whether the active file in the java editor changes.
	 */
	public void listenForActiveClass() {
		IWorkbench wb = PlatformUI.getWorkbench();
		IWorkbenchWindow ww = wb.getActiveWorkbenchWindow();

		PartAdapter adapter = new PartAdapter();
		ww.getPartService().addPartListener(adapter);
	}

	public static void addNature(IProject project) {
		VariantSyncProgressMonitor progressMonitor = new VariantSyncProgressMonitor("Adding VariantSync nature to " + project.getName());
		try {
			IProjectDescription description = project.getDescription();
			String[] natures = description.getNatureIds();

			String[] newNatures = new String[natures.length + 1];
			System.arraycopy(natures, 0, newNatures, 1, natures.length);

			newNatures[0] = Variant.NATURE_ID;

			description.setNatureIds(newNatures);
			progressMonitor.setSubTaskName("Add nature");
			project.setDescription(description, progressMonitor);
			progressMonitor.setSubTaskName("Refresh resources");
			project.refreshLocal(IResource.DEPTH_INFINITE, progressMonitor);
		} catch (CoreException e) {
			LogOperations.logError("", e);
		}
	}

	public void addListener(IEventListener listener) {
		this.listeners.add(listener);

	}

	public void fireEvent(VariantSyncEvent event) {
		System.out.println(event);
		for (IEventListener listener : listeners) {
			listener.propertyChange(event);
		}
	}

	public void removeListener(IEventListener listener) {
		this.listeners.remove(listener);
	}
} public class MappingManager extends AManager implements ISaveableManager {

	private HashMap<IProject, List<SourceFile>> codeMappings = new HashMap<>();
	private ConfigurationProject configurationProject;

	public MappingManager(ConfigurationProject configurationProject) {
		this.configurationProject = configurationProject;
	}

	private boolean isActive;

	public boolean isActive() {
		return this.isActive;
	}

	public void setActive(boolean status) {
		this.isActive = status;
		if (isActive) {
			fireEvent(new VariantSyncEvent(this, EventType.CONTEXT_RECORDING_START, null,
					VariantSyncPlugin.getConfigurationProjectManager().getActiveConfigurationProject()));
		} else {
			fireEvent(new VariantSyncEvent(this, EventType.CONTEXT_RECORDING_STOP,
					VariantSyncPlugin.getConfigurationProjectManager().getActiveConfigurationProject(), null));
		}
	}

	public SourceFile getMapping(IFile file) {
		if (file != null && codeMappings.containsKey(file.getProject())) {
			for (SourceFile sourceFile : codeMappings.get(file.getProject())) {
				if (sourceFile.getFile().getFullPath().equals(file.getFullPath())) {
					return sourceFile;
				}
			}
		}
		return null;
	}

	public HashMap<IProject, List<SourceFile>> getCodeMappings() {
		return codeMappings;
	}

	public void setCodeMappings(HashMap<IProject, List<SourceFile>> codeMappings) {
		this.codeMappings = codeMappings;
	}

	public void addCodeMapping(IProject project, List<SourceFile> files) {
		this.codeMappings.put(project, files);
	}

	public void addCodeMapping(IFile file, SourceFile sourceFile) {
		List<SourceFile> sourceFiles = codeMappings.get(file.getProject());
		if (sourceFiles == null || sourceFiles.isEmpty()) {
			sourceFiles = new ArrayList<>();
			addCodeMapping(file.getProject(), sourceFiles);
		}
		for (SourceFile sf : sourceFiles) {
			if (sf.getFile().getFullPath().equals(file.getFullPath())) {
				sf = sourceFile;
				return;
			}
		}
		sourceFiles.add(sourceFile);
	}

	@Override
	public void reset() {
		codeMappings.clear();
	}

	@Override
	public void load() {
		for (IProject project : configurationProject.getVariants()) {
			List<SourceFile> sourceFiles = new ArrayList<>();
			FileHandler.load(Paths.get(project.getFile(CodeMappingFormat.FILENAME).getLocationURI()), sourceFiles, new CodeMappingFormat(project));
			if (!sourceFiles.isEmpty()) {
				addCodeMapping(project, sourceFiles);
			}
		}
	}

	@Override
	public void save() {
		for (IProject project : codeMappings.keySet()) {
			FileHandler.save(Paths.get(project.getFile(CodeMappingFormat.FILENAME).getLocationURI()), codeMappings.get(project),
					new CodeMappingFormat(project));
		}
	}

} public class CodeMapping {

	private String code;
	private IVariantSyncMarker variantSyncMarker;

	/**
	 * Constructor
	 * @param code
	 * @param variantSyncMarker
	 */
	public CodeMapping(String code, IVariantSyncMarker variantSyncMarker) {
		this.code = code;
		this.variantSyncMarker = variantSyncMarker;
	}

	/**
	 * Returns mapped code
	 * @return code
	 */
	public String getCode() {
		return code;
	}

	/**
	 * Sets mapped code
	 * @param code
	 */
	public void setCode(String code) {
		this.code = code;
	}

	/**
	 * Returns marker information
	 * @return variantSyncMarker
	 */
	public IVariantSyncMarker getMarkerInformation() {
		return variantSyncMarker;
	}

	/**
	 * Sets marker information
	 * @param variantSyncMarker
	 */
	public void setMarkerInformation(IVariantSyncMarker variantSyncMarker) {
		this.variantSyncMarker = variantSyncMarker;
	}

} public class ConfigurationProject extends AManager implements ISaveableManager {

	private IFeatureProject configurationProject = null;

	private FeatureContextManager featureContextManager = new FeatureContextManager(this);
	private MappingManager mappingManager = new MappingManager(this);
	private PatchesManager patchesManager = new PatchesManager(this);

	private List<IProject> projects = new ArrayList<>();

	public IFeatureProject getFeatureProject() {
		return configurationProject != null ? configurationProject.getProject().exists() ? configurationProject : null : null;
	}

	public void setFeatureProject(IFeatureProject configurationProject) {
		this.configurationProject = configurationProject;
		fireEvent(new VariantSyncEvent(this, EventType.CONFIGURATIONPROJECT_SET, null, configurationProject));
	}

	public List<String> getVariantNames() {
		List<String> projectNames = new ArrayList<>();
		for (IProject project : this.projects) {
			projectNames.add(project.getName());
		}
		return projectNames;
	}

	public List<IProject> getVariants() {
		return projects;
	}

	public IProject getVariant(String name) {
		for (IProject project : this.projects) {
			if (project.getName().equals(name)) return project;
		}
		return null;
	}

	public void setVariants(List<IProject> projects) {
		this.projects = projects;
	}

	public void addVariant(IProject project) {
		this.projects.add(project);
	}

	public Configuration getConfigurationForVariant(IProject project) {
		if (project != null) {
			for (IFile configPath : configurationProject.getAllConfigurations()) {
				String configFileName = configPath.getName();
				String configName = configFileName.substring(0, configFileName.lastIndexOf('.'));
				System.out.println("[ConfigurationProject.getConfigurationForVariant] Check name equality Project(" + project.getName() + ") with Config(" + configName + ")");
				if (configName.equals(project.getName())) {
					ConfigurationManager configurationManager = ConfigurationManager.getInstance(Paths.get(configPath.getRawLocationURI()));
					if (configurationManager != null) return configurationManager.getObject();
				}
			}
		}
		return null;
	}

	public Iterable<IFeature> getFeatures() {
		return configurationProject.getFeatureModel().getFeatures();
	}

	@Override
	public void reset() {
		projects.clear();
	}

	public FeatureContextManager getFeatureContextManager() {
		return featureContextManager;
	}

	public MappingManager getMappingManager() {
		return mappingManager;
	}

	public PatchesManager getPatchesManager() {
		return patchesManager;
	}

	@Override
	public void save() {
		featureContextManager.save();
		mappingManager.save();
		patchesManager.save();
	}

	@Override
	public void load() {
		featureContextManager.load();
		mappingManager.load();
		patchesManager.load();
	}

} public class SourceFile {

	private IFile resource;
	List<CodeMapping> mappings = new ArrayList<>();
	
	public SourceFile(IFile res) {
		this.resource = res;
	}
	
	public SourceFile(IFile res, List<CodeMapping> mappings) {
		this.resource = res;
		this.mappings = mappings;
	}

	public IFile getFile() {
		return resource;
	}
	
	public void setFile(IFile res) {
		this.resource = res;
	}

	public List<CodeMapping> getMappings() {
		return mappings;
	}
	
	public void addMapping(CodeMapping mapping) {
		this.mappings.add(mapping);
	}
	
	public void setMapping(List<CodeMapping> mappings) {
		this.mappings = mappings;
	}
	
} @SuppressWarnings("rawtypes")
public class DeltaFactoryManager extends ExtensionManager<IDeltaFactory> {

	private static DeltaFactoryManager instance = new DeltaFactoryManager();

	public DeltaFactoryManager() {
		setExtensionLoaderInternal(new CoreExtensionLoader<>(getDefaultFactory().getClass()));
		addExtension(getDefaultFactory());
	}

	public static DeltaFactoryManager getInstance() {
		return instance;
	}

	public static void setExtensionLoader(IExtensionLoader<IDeltaFactory> extensionLoader) {
		instance.setExtensionLoaderInternal(extensionLoader);
	}

	/**
	 * Returns the factory for a given id
	 * 
	 * @param id
	 * @return factory which has the given id
	 * @throws NoSuchExtensionException
	 */
	public static IDeltaFactory getFactoryById(String id) throws NoSuchExtensionException {
		return instance.getExtension(id);
	}

	/**
	 * Returns the specific factory for the given file. If no factory supports the file then the default factory is returned
	 * 
	 * @param file
	 * @return factory which supports the file
	 * @throws NoSuchExtensionException
	 */
	public IDeltaFactory getFactoryByFile(IFile file) throws NoSuchExtensionException {
		for (IDeltaFactory factory : instance.getExtensions()) {
			if (factory.isSupported(file)) {
				return factory;
			}
		}
		return getDefaultFactory();
	}

	/**
	 * Returns the default factory (line-based diffs)
	 * 
	 * @return factory for line-based diffs
	 */
	public static IDeltaFactory getDefaultFactory() {
		return DefaultDeltaFactory.getInstance();
	}

} public interface IDelta<T> extends Cloneable {

	/**
	 * Specifies the type of the delta.
	 *
	 */
	public enum DELTATYPE {
		/** An addition was made */
		ADDED,
		/** An deletion was made */
		REMOVED,
		/** A change was made */
		CHANGED
	}

	/**
	 * Returns original delta
	 * 
	 * @return T - original delta
	 */
	T getOriginal();

	/**
	 * Returns original as string. Do not use this method outside of saving or loading!
	 * 
	 * @return
	 */
	String getOriginalAsString();

	/**
	 * Sets orginal delta
	 * 
	 * @param original - original delta
	 */
	void setOriginal(T original);

	/**
	 * Sets original from string. Do not use this method outside of saving or loading!
	 * 
	 * @param original
	 */
	void setOriginalFromString(String original);

	/**
	 * Returns revised delta
	 * 
	 * @return T - revised delta
	 */
	T getRevised();

	/**
	 * Returns revised as string. Do not use this method outside of saving or loading!
	 * 
	 * @return
	 */
	String getRevisedAsString();

	/**
	 * Sets revised delta
	 * 
	 * @param revised - revised delta
	 */
	void setRevised(T revised);

	/**
	 * Sets revised from string. Do not use this method outside of saving or loading!
	 * 
	 * @param revised
	 */
	void setRevisedFromString(String revised);

	/**
	 * Returns delta type
	 * 
	 * @return TYPE - type
	 */
	DELTATYPE getType();

	/**
	 * Sets type of delta
	 * 
	 * @param type - type
	 */
	void setType(DELTATYPE type);

	/**
	 * Returns feature
	 * 
	 * @return FeatureContext - feature context
	 */
	String getContext();

	/**
	 * Sets feature expression of delta
	 * 
	 * @param feature - FeatureContext
	 */
	void setContext(String feature);

	/**
	 * Returns resource
	 */
	IFile getResource();

	IPatch<?> getPatch();

	void setPatch(IPatch<?> patch);

	IProject getProject();

	void setProject(IProject project);

	String getFactoryId();

	/**
	 * 
	 * @return
	 */
	boolean isSynchronizedProject(IProject project);

	boolean isSynchronizedProject(String projectName);

	void addSynchronizedProject(IProject project);

	List<IProject> getSynchronizedProjects();

	void setSynchronizedProjects(List<IProject> projects);

	/**
	 * 
	 * @return
	 */
	long getTimestamp();

	void setTimestamp(long timestamp);

	String getProperty(String key);

	HashMap<String, String> getProperties();

	void addProperty(String key, String obj);

	String getRepresentation();

	static DELTATYPE DELTATYPE(int i) {
		switch (i) {
		case 1:
			return DELTATYPE.ADDED;
		case 2:
			return DELTATYPE.REMOVED;
		case 4:
			return DELTATYPE.CHANGED;
		default:
			return null;
		}
	}
} public interface IDeltaFactory<T> extends IExtension {

	public static String extensionPointID = "DeltaFactory";

	public static String extensionID = "deltaFactory";

	String getName();

	/**
	 * Creates a empty delta object
	 * 
	 * @param res
	 * @return
	 */
	IDelta<T> createDelta(IFile file);

	/**
	 * Creates a delta object for a resource
	 * 
	 * @param res - resource
	 * @param timestamp - timestamp
	 * @param kind - type of change
	 * @return patch object
	 */
	List<IDelta<T>> createDeltas(IFile file, long timestamp, DELTATYPE kind) throws DiffException;

	/**
	 * Creates patch object from a changed resource.
	 * 
	 * @param res - resource
	 * @param oldState - last history state
	 * @param kind - type of change
	 * @return patch object
	 */
	List<IDelta<T>> createDeltas(IFile file, IFileState oldState, long timestamp, DELTATYPE kind) throws DiffException;

	/**
	 * Creates a delta copy with different resource.
	 * 
	 * @param res - new file pointer
	 * @param originalDelta - delta to copy
	 * @return delta copy with new file pointer
	 */
	IDelta<T> createDeltas(IFile res, IDelta<?> originalDelta);

	/**
	 * Creates a delta copy with different resource.
	 * 
	 * @param res - new file pointer
	 * @param originalDelta - delta to copy
	 * @return delta copy with new file pointer
	 */
	List<IDelta<T>> createDeltas(IFile original, IFile revised) throws DiffException;

	/**
	 * Patches a resource with a given patch.
	 * 
	 * @param res - resource
	 * @param patch - patch
	 * @return patched temp resource
	 */
	IFile applyDelta(IFile file, IDelta<T> patch);

	/**
	 * Unpatches a revised resource for a given patch.
	 * 
	 * @param res - the resource
	 * @param patch - patch
	 * @return original resource
	 */
	IFile reverseDelta(IFile file, IDelta<T> patch);

	/**
	 * Verifies that the given patch can be applied to the given resource
	 * 
	 * @param res - the resource
	 * @param delta - the patch to verify
	 * @return true if the patch can be applied
	 */
	boolean verifyDelta(IFile file, IDelta<T> delta);

	/**
	 * Checks whether the file is supported
	 * 
	 * @param file
	 * @return true if the file is supported
	 */
	boolean isSupported(IFile file);

	/**
	 * Returns the marker handler for deltas of this type
	 * 
	 * @return
	 */
	IMarkerHandler<T> getMarkerHandler();

} public interface IMarkerHandler<T> {

	List<IVariantSyncMarker> getMarkers(IFile file, int offset, int length);

	List<IVariantSyncMarker> getMarkersForDelta(IFile file, IDelta<T> delta);

	List<IVariantSyncMarker> getMarkersForDeltas(IFile file, List<IDelta<T>> deltas);

	boolean updateMarkerForDelta(SourceFile sourceFile, IDelta<T> delta, List<IVariantSyncMarker> variantSyncMarkers);
} public interface IVariantSyncMarker {

	/**
	 * Returns marker id.
	 * 
	 * @return markerId
	 */
	long getMarkerId();

	/**
	 * Sets marker id.
	 * 
	 * @param markerId - id of marker
	 */
	void setMarkerId(long markerId);

	/**
	 * Returns offset.
	 * 
	 * @return offset
	 */
	int getOffset();

	/**
	 * Sets offset.
	 * 
	 * @param offset
	 */
	void setOffset(int offset);

	/**
	 * Returns length.
	 * 
	 * @return length
	 */
	int getLength();

	/**
	 * Sets length.
	 * 
	 * @param length
	 */
	void setLength(int length);

	/**
	 * Returns feature context.
	 * 
	 * @return feature context
	 */
	String getContext();

	/**
	 * Sets feature expression.
	 * 
	 * @param context - feature context
	 */
	void setContext(String context);

	/**
	 * Returns whether the complete line is annotated
	 * 
	 * @return true, if the complete line is annotated
	 */
	boolean isLine();

	/**
	 * Sets whether the complete line is annotated
	 * 
	 * @param true, if the complete line is annotated
	 */
	void setLine(boolean isLine);
}
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IFile;
import de.ovgu.featureide.fm.core.ExtensionManager.NoSuchExtensionException;
import de.tubs.variantsync.core.VariantSyncPlugin;
import de.tubs.variantsync.core.managers.MappingManager;
import de.tubs.variantsync.core.managers.data.CodeMapping;
import de.tubs.variantsync.core.managers.data.ConfigurationProject;
import de.tubs.variantsync.core.managers.data.SourceFile;
import de.tubs.variantsync.core.patch.DeltaFactoryManager;
import de.tubs.variantsync.core.patch.interfaces.IDelta;
import de.tubs.variantsync.core.patch.interfaces.IDeltaFactory;
import de.tubs.variantsync.core.patch.interfaces.IMarkerHandler;
import de.tubs.variantsync.core.utilities.IVariantSyncMarker;
import de.tubs.variantsync.core.utilities.LogOperations;
package de.tubs.variantsync.core.monitor;




@SuppressWarnings({ "unchecked", "rawtypes" })
public class CodeMappingHandler {

	/**
	 * Creates mappings for given deltas
	 * 
	 * @param deltas
	 */
	public static void addCodeMappingsForDeltas(List<IDelta<?>> deltas) {
		for (IDelta delta : deltas) {
			try {
				// Get factory and marker handler for delta
				IDeltaFactory<?> deltaFactory = DeltaFactoryManager.getFactoryById(delta.getFactoryId());
				IMarkerHandler markerHandler = deltaFactory.getMarkerHandler();
				List<IVariantSyncMarker> variantSyncMarkers = markerHandler.getMarkersForDelta(delta.getResource(), delta);

				// Get current context
				ConfigurationProject configurationProject = VariantSyncPlugin.getConfigurationProjectManager().getActiveConfigurationProject();
				MappingManager mappingManager = configurationProject.getMappingManager();