/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* 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:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.editor.synchronization;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.web.bindery.event.shared.EventBus;
import org.eclipse.che.ide.api.editor.EditorPartPresenter;
import org.eclipse.che.ide.api.editor.EditorWithAutoSave;
import org.eclipse.che.ide.api.event.ActivePartChangedEvent;
import org.eclipse.che.ide.api.event.ActivePartChangedHandler;
import org.eclipse.che.ide.api.parts.EditorPartStack;
import org.eclipse.che.ide.api.parts.PartPresenter;
import org.eclipse.che.ide.api.resources.ResourceChangedEvent;
import org.eclipse.che.ide.api.resources.ResourceChangedEvent.ResourceChangedHandler;
import org.eclipse.che.ide.api.resources.ResourceDelta;
import org.eclipse.che.ide.resource.Path;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import static org.eclipse.che.ide.api.resources.ResourceDelta.ADDED;
import static org.eclipse.che.ide.api.resources.ResourceDelta.MOVED_FROM;
import static org.eclipse.che.ide.api.resources.ResourceDelta.MOVED_TO;
/**
* The default implementation of {@link EditorContentSynchronizer}.
* The synchronizer of content for opened files with the same {@link Path}.
* Used to sync the content of opened files in different {@link EditorPartStack}s.
* Note: this implementation disables autosave feature for implementations of {@link EditorWithAutoSave} with the same {@link Path} except
* active editor.
*
* @author Roman Nikitenko
*/
@Singleton
public class EditorContentSynchronizerImpl implements EditorContentSynchronizer, ActivePartChangedHandler,
ResourceChangedHandler {
final Map<Path, EditorGroupSynchronization> editorGroups;
final Provider<EditorGroupSynchronization> editorGroupSyncProvider;
@Inject
public EditorContentSynchronizerImpl(EventBus eventBus,
Provider<EditorGroupSynchronization> editorGroupSyncProvider) {
this.editorGroupSyncProvider = editorGroupSyncProvider;
this.editorGroups = new HashMap<>();
eventBus.addHandler(ActivePartChangedEvent.TYPE, this);
eventBus.addHandler(ResourceChangedEvent.getType(), this);
}
/**
* Begins to track given editor to sync its content with opened files with the same {@link Path}.
*
* @param editor
* editor to sync content
*/
@Override
public void trackEditor(EditorPartPresenter editor) {
Path path = editor.getEditorInput().getFile().getLocation();
if (editorGroups.containsKey(path)) {
editorGroups.get(path).addEditor(editor);
} else {
EditorGroupSynchronization group = editorGroupSyncProvider.get();
editorGroups.put(path, group);
group.addEditor(editor);
}
}
/**
* Stops to track given editor.
*
* @param editor
* editor to stop tracking
*/
@Override
public void unTrackEditor(EditorPartPresenter editor) {
Path path = editor.getEditorInput().getFile().getLocation();
EditorGroupSynchronization group = editorGroups.get(path);
if (group == null) {
return;
}
group.removeEditor(editor);
if (group.getSynchronizedEditors().isEmpty()) {
group.unInstall();
editorGroups.remove(path);
}
}
@Override
public void onActivePartChanged(ActivePartChangedEvent event) {
PartPresenter activePart = event.getActivePart();
if (!(activePart instanceof EditorPartPresenter)) {
return;
}
EditorPartPresenter activeEditor = (EditorPartPresenter)activePart;
Path path = activeEditor.getEditorInput().getFile().getLocation();
if (editorGroups.containsKey(path)) {
editorGroups.get(path).onActiveEditorChanged(activeEditor);
}
}
@Override
public void onResourceChanged(ResourceChangedEvent event) {
final ResourceDelta delta = event.getDelta();
if (delta.getKind() != ADDED || (delta.getFlags() & (MOVED_FROM | MOVED_TO)) == 0) {
return;
}
final Path fromPath = delta.getFromPath();
final Path toPath = delta.getToPath();
if (delta.getResource().isFile()) {
onFileChanged(fromPath, toPath);
} else {
onFolderChanged(fromPath, toPath);
}
}
private void onFileChanged(Path fromPath, Path toPath) {
final EditorGroupSynchronization group = editorGroups.remove(fromPath);
if (group != null) {
editorGroups.put(toPath, group);
}
}
private void onFolderChanged(Path fromPath, Path toPath) {
final Map<Path, EditorGroupSynchronization> newGroups = new HashMap<>(editorGroups.size());
final Iterator<Map.Entry<Path, EditorGroupSynchronization>> iterator = editorGroups.entrySet().iterator();
while (iterator.hasNext()) {
final Map.Entry<Path, EditorGroupSynchronization> entry = iterator.next();
final Path groupPath = entry.getKey();
final EditorGroupSynchronization group = entry.getValue();
if (fromPath.isPrefixOf(groupPath)) {
final Path relPath = groupPath.removeFirstSegments(fromPath.segmentCount());
final Path newPath = toPath.append(relPath);
newGroups.put(newPath, group);
iterator.remove();
}
}
editorGroups.putAll(newGroups);
}
}