blob: 4b1eb8ac3bd8747d08326819f6799c98fc4e7630 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2010 BMW Car IT, Technische Universitaet Muenchen, and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* BMW Car IT - Initial API and implementation
* Technische Universitaet Muenchen - Major refactoring and extension
*******************************************************************************/
package org.eclipse.emf.edapt.history.recorder;
import static org.eclipse.emf.common.notify.Notification.SET;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EContentAdapter;
import org.eclipse.emf.edapt.spi.history.History;
/**
* Recorder to store and restore links from the history to the metamodel that
* have been lost due to {@link ResourceSet}-wide deletes.
*
* @author herrmama
* @author $Author$
* @version $Rev$
* @levd.rating RED Rev:
*/
public class HistoryChangeRecorder {
/**
* History for which changes should be recorded
*/
private final History history;
/**
* Adapter listening to the resource contents
*/
private final EContentAdapter adapter;
/**
* Whether the recorder is currently recording
*/
private boolean recording;
/**
* Links that need to restored in the history.
*/
private final List<Link> links = new ArrayList<Link>();
/**
* Constructor
*/
public HistoryChangeRecorder(History history) {
this.history = history;
adapter = new EContentAdapter() {
@Override
public void notifyChanged(Notification notification) {
switch (notification.getEventType()) {
case SET:
handleSet(notification);
break;
}
super.notifyChanged(notification);
}
};
recording = false;
beginRecording();
}
/**
* Handle SET notification
*/
private void handleSet(Notification notification) {
if (notification.getFeature() instanceof EReference) {
final EReference reference = (EReference) notification.getFeature();
if (reference.isContainment()) {
return;
}
final EObject element = (EObject) notification.getNotifier();
final EObject oldValue = (EObject) notification.getOldValue();
if (notification.getNewValue() == null && oldValue != null) {
if (oldValue.eClass().getEPackage() == EcorePackage.eINSTANCE) {
final Link link = new Link(element, reference, oldValue);
links.add(link);
}
}
}
}
/**
* Start the recorder, unless already started
*/
public void beginRecording() {
if (!isRecording()) {
history.eAdapters().add(adapter);
recording = true;
} else {
throw new IllegalStateException("Recorder is already started"); //$NON-NLS-1$
}
}
/** Returns recording. */
public boolean isRecording() {
return recording;
}
/**
* Stop the recorder, unless already stopped
*/
public void endRecording() {
if (isRecording()) {
history.eAdapters().remove(adapter);
for (final Link link : links) {
link.restore();
}
recording = false;
} else {
throw new IllegalStateException("Recorder is already stopped"); //$NON-NLS-1$
}
}
/**
* Link of an element to another one.
*/
private class Link {
/**
* Source of the link
*/
private final EObject element;
/**
* Reference of the link
*/
private final EReference reference;
/**
* Target of the link
*/
private final EObject value;
/**
* Constructor
*/
public Link(EObject element, EReference reference, EObject value) {
this.element = element;
this.reference = reference;
this.value = value;
}
/**
* Restore the link
*/
public void restore() {
element.eSet(reference, value);
}
}
}