/*******************************************************************************
* Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC, Canada.
* 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:
* The Chisel Group, University of Victoria
*******************************************************************************/
package net.sourceforge.tagsea.c.waypoints;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import net.sourceforge.tagsea.TagSEAModelException;
import net.sourceforge.tagsea.c.CWaypointUtils;
import net.sourceforge.tagsea.c.CWaypointsPlugin;
import net.sourceforge.tagsea.c.ICWaypointsConstants;
import net.sourceforge.tagsea.c.resources.internal.FileWaypointRefreshJob;
import net.sourceforge.tagsea.core.IWaypoint;
import net.sourceforge.tagsea.core.IWaypointChangeEvent;
import net.sourceforge.tagsea.core.WaypointDelta;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.ListDialog;
/**
* A class that saves dirty editors and synchronizes stale waypoints before a refactor
* operation.
* @author Del Myers
*/
//note this class is assumed to be run internally by the java waypoint delegate, so it does it's own updating of waypoints, etc.
class DirtyEditorSynchronizer implements IRunnableWithProgress {
private WaypointDelta delta;
//a list of waypoints that could not be synchronized.
private List<IWaypoint> badWaypoints;
private List<IWaypointChangeEvent> newEvents;
public DirtyEditorSynchronizer(WaypointDelta delta) {
this.delta = delta;
badWaypoints = new ArrayList<IWaypoint>();
}
/* (non-Javadoc)
* @see org.eclipse.jface.operation.IRunnableWithProgress#run(org.eclipse.core.runtime.IProgressMonitor)
*/
public void run(final IProgressMonitor monitor) throws InvocationTargetException,
InterruptedException {
monitor.beginTask("Saving Editors...", 210);
List<ITextFileBuffer> dirtyBuffers = new ArrayList<ITextFileBuffer>();
for (IWaypointChangeEvent event : delta.changes) {
if ((ICWaypointsConstants.C_WAYPOINT.equals(event.getWaypoint().getType()))) {
IFile file = CWaypointUtils.getFile(event.getWaypoint());
if (file != null && file.exists()) {
ITextFileBuffer buffer =
FileBuffers.getTextFileBufferManager().getTextFileBuffer(file.getLocation());
if (buffer != null && buffer.isDirty() && !dirtyBuffers.contains(buffer)) {
dirtyBuffers.add(buffer);
}
}
}
}
//get the relevant dirty editors.
IEditorPart[] editors = getDirtyEditors();
List<IEditorPart> editorsToSave = new ArrayList<IEditorPart>();
for (ITextFileBuffer buffer : dirtyBuffers) {
for (IEditorPart editor : editors) {
if (editor.getEditorInput() instanceof IFileEditorInput) {
IPath location = ((IFileEditorInput)editor.getEditorInput()).getFile().getFullPath();
if (buffer.getLocation().equals(location)) {
if (!editorsToSave.contains(editor)) {
editorsToSave.add(editor);
}
}
}
}
}
monitor.worked(10);
if (editorsToSave.size() == 0) {
newEvents = new ArrayList<IWaypointChangeEvent>(Arrays.asList(delta.getChanges()));
monitor.done();
return;
}
openSaveDialog(editorsToSave.toArray(new IEditorPart[editorsToSave.size()]));
final int work = 100/editorsToSave.size();
LinkedList<IFile> filesToRefresh = new LinkedList<IFile>();
for (final IEditorPart part : editorsToSave) {
Display.getDefault().syncExec(new Runnable(){
public void run() {
part.doSave(new SubProgressMonitor(monitor, work));
}
});
//update the file.
IFile file = ((IFileEditorInput)part.getEditorInput()).getFile();
filesToRefresh.add(file);
}
if (filesToRefresh.size() > 0) {
FileWaypointRefreshJob refresher = new FileWaypointRefreshJob(filesToRefresh);
//refresh the file. This is necessary so that the waypoints can be synchronized.
CWaypointDelegate delegate =
(CWaypointDelegate) CWaypointsPlugin.getCWaypointDelegate();
delegate.internalRun(refresher, true);
}
synchronizeWaypoints(new SubProgressMonitor(monitor, 100, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK));
monitor.done();
}
/**
* Synchronizes the previous waypoints in the delta with the ones that currently exist.
* @param monitor
*/
private void synchronizeWaypoints(SubProgressMonitor monitor) {
List<IWaypointChangeEvent> events = new LinkedList<IWaypointChangeEvent>(Arrays.asList(delta.changes));
HashMap<IWaypoint, IWaypoint> oldNewMap = new HashMap<IWaypoint, IWaypoint>();
monitor.beginTask("Synchronizing waypoints...", events.size());
for (int i = 0; i < events.size(); i++) {
IWaypointChangeEvent event = events.get(i);
if ((ICWaypointsConstants.C_WAYPOINT.equals(event.getType()))) {
if (!event.getWaypoint().exists()) {
IFile file = CWaypointUtils.getFile(event.getWaypoint());
IWaypoint newWaypoint = oldNewMap.get(event.getWaypoint());
if (!badWaypoints.contains(event.getWaypoint())) {
if (newWaypoint == null && file != null && file.exists()) {
try {
newWaypoint = findNewWaypoint(event.getWaypoint(), file);
oldNewMap.put(event.getWaypoint(), newWaypoint);
} catch (TagSEAModelException e) {
//do nothing, leave as null.
}
}
if (newWaypoint == null) {
//if it is still null, flag the waypoint for removal;
badWaypoints.add(event.getWaypoint());
} else {
// replace the event in the list.
events.remove(i);
events.add(i, new ReplacedWaypointEvent(event, newWaypoint));
}
}
}
}
monitor.worked(1);
}
//run through the list and remove the bad waypoints.
for (int i = 0; i < events.size(); i++) {
IWaypointChangeEvent event = events.get(i);
if (badWaypoints.contains(event.getWaypoint())) {
events.remove(i);
i--;
}
}
this.newEvents = events;
monitor.done();
}
/**
* Gets the waypoint changes that are applicable after the saving is complete.
* @return the waypoint changes that are applicable after the saving is complete.
*/
public IWaypointChangeEvent[] getWaypointChanges() {
return newEvents.toArray(new IWaypointChangeEvent[newEvents.size()]);
}
/**
* Finds a waypoint with the exact same attributes as the oldWaypoint and
* returns that as a new waypoint, or null if an equal waypoint could not
* be found.
* @param waypoint
* @param file
* @return
* @throws TagSEAModelException
*/
private IWaypoint findNewWaypoint(IWaypoint oldWaypoint, IFile file) throws TagSEAModelException {
IWaypoint[] waypoints = CWaypointsPlugin.getCWaypointsForFile(file);
for (IWaypoint waypoint : waypoints) {
String[] attributes = waypoint.getAttributes();
boolean equal = true;
for (String key : attributes) {
Object oldv = oldWaypoint.getValue(key);
Object newv = waypoint.getValue(key);
equal &= (oldv != null) && (newv != null);
if (equal) {
equal &= oldv.equals(newv);
}
if (!equal) {
break;
}
}
if (equal) {
return waypoint;
}
}
return null;
}
/**
* @param parts
*/
private void openSaveDialog(final IEditorPart[] parts) {
Display.getDefault().syncExec(new Runnable(){
public void run() {
ListDialog dialog = new ListDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());
dialog.setTitle("Save Editors");
dialog.setAddCancelButton(false);
dialog.setLabelProvider(createDialogLabelProvider());
dialog.setMessage("The following resources will be saved before continuing.");
dialog.setContentProvider(new ArrayContentProvider());
dialog.setInput(parts);
dialog.open();
}
});
}
private ILabelProvider createDialogLabelProvider() {
return new LabelProvider() {
public Image getImage(Object element) {
return ((IEditorPart) element).getTitleImage();
}
public String getText(Object element) {
return ((IEditorPart) element).getTitle();
}
};
}
IEditorPart[] getDirtyEditors() {
final Set<IEditorInput> inputs= new HashSet<IEditorInput>();
final List<IEditorPart> result= new ArrayList<IEditorPart>(0);
Display.getDefault().syncExec(new Runnable(){
public void run() {
IWorkbench workbench= PlatformUI.getWorkbench();
IWorkbenchWindow[] windows= workbench.getWorkbenchWindows();
for (int i= 0; i < windows.length; i++) {
IWorkbenchPage[] pages= windows[i].getPages();
for (int x= 0; x < pages.length; x++) {
IEditorPart[] editors= pages[x].getDirtyEditors();
for (int z= 0; z < editors.length; z++) {
IEditorPart ep= editors[z];
IEditorInput input= ep.getEditorInput();
if (!inputs.contains(input)) {
inputs.add(input);
result.add(ep);
}
}
}
}
}
});
return (IEditorPart[])result.toArray(new IEditorPart[result.size()]);
}
}