/*******************************************************************************
* Copyright (c) 2010 SAP AG.
* 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:
* Emil Simeonov - initial API and implementation.
* Dimitar Donchev - initial API and implementation.
* Dimitar Tenev - initial API and implementation.
* Nevena Manova - initial API and implementation.
* Georgi Konstantinov - initial API and implementation.
*******************************************************************************/
package org.eclipse.wst.sse.sieditor.model;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.transaction.Transaction;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.emf.transaction.impl.InternalTransaction;
import org.eclipse.emf.transaction.impl.TransactionalEditingDomainImpl;
import org.eclipse.emf.workspace.AbstractEMFOperation;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.xml.core.internal.document.XMLModelNotifier;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.eclipse.wst.sse.sieditor.core.common.IDisposable;
import org.eclipse.wst.sse.sieditor.core.common.Logger;
import org.eclipse.wst.sse.sieditor.model.api.IModelRoot;
import org.eclipse.wst.sse.sieditor.model.i18n.Messages;
public class XMLModelNotifierWrapper implements XMLModelNotifier, IDisposable {
private XMLModelNotifier internalNotifier = null;
private IModelRoot modelRoot = null;
private final Set<Node> changedNodes = new HashSet<Node>();
private boolean interceptChanges = false;
private List<Notification> collectedNotifications;
private IStructuredModel structuredModel;
public List<Notification> getCollectedNotifications() {
if (collectedNotifications != null) {
return collectedNotifications;
}
return Collections.emptyList();
}
public void clearCollectedNotifications() {
if (collectedNotifications != null) {
collectedNotifications.clear();
}
}
public XMLModelNotifierWrapper(final XMLModelNotifier notifier, final IModelRoot modelRoot) {
this.internalNotifier = notifier;
this.modelRoot = modelRoot;
}
/**
* True - will suppress calls to inner XML notifier.
*
* @param interceptChanges
* @see #endChanging()
*/
public void setInterceptChanges(final boolean interceptChanges) {
this.interceptChanges = interceptChanges;
}
/**
* Recorded changed DOM nodes during source edit.
*/
public Set<Node> getChangedNodes() {
return changedNodes;
}
public void attrReplaced(final Element element, final Attr newAttr, final Attr oldAttr) {
internalNotifier.attrReplaced(element, newAttr, oldAttr);
}
public void beginChanging() {
internalNotifier.beginChanging();
}
public void beginChanging(final boolean newModel) {
internalNotifier.beginChanging(newModel);
}
public void cancelPending() {
internalNotifier.cancelPending();
}
public void childReplaced(final Node parentNode, final Node newChild, final Node oldChild) {
internalNotifier.childReplaced(parentNode, newChild, oldChild);
}
public void editableChanged(final Node node) {
internalNotifier.editableChanged(node);
}
@Override
public void endChanging() {
if (interceptChanges) {
return;
}
final TransactionalEditingDomain editingDomain = modelRoot.getEnv().getEditingDomain();
// happens when close editor, and do not save contents
if (editingDomain == null) {
internalNotifier.endChanging();
return;
}
final Map<String, Boolean> options = new HashMap<String, Boolean>();
options.put(Transaction.OPTION_NO_VALIDATION, Boolean.TRUE);
// this command should not notify SIE Model
final AbstractEMFOperation command = new AbstractEMFOperation(editingDomain, Messages.XMLModelNotifierWrapper_0, options) {
private Set<Notification> newNotifications;
@Override
protected IStatus doExecute(final IProgressMonitor monitor, final IAdaptable info) throws ExecutionException {
final InternalTransaction activeTransaction = ((TransactionalEditingDomainImpl) getEditingDomain())
.getActiveTransaction();
if (activeTransaction != null) {
final Set<Notification> oldNotifications = new HashSet<Notification>(activeTransaction.getNotifications());
internalNotifier.endChanging();
newNotifications = new HashSet<Notification>(activeTransaction.getNotifications());
newNotifications.removeAll(oldNotifications);
} else {
Logger.logWarning("We are notifying the wrapped XMLModelNotifier but we do not have active transaction"); //$NON-NLS-1$
internalNotifier.endChanging();
}
return Status.OK_STATUS;
}
@Override
protected void didCommit(final Transaction transaction) {
if (transaction.getParent() != null) {
for (final Notification notification : newNotifications) {
((InternalTransaction) transaction.getParent()).add(notification);
}
newNotifications.clear();
}
super.didCommit(transaction);
}
};
try {
command.setReuseParentTransaction(true);
command.execute(null, null);
} catch (final ExecutionException e) {
throw new RuntimeException(e);
}
}
public void endTagChanged(final Element element) {
internalNotifier.endTagChanged(element);
}
public boolean hasChanged() {
return internalNotifier.hasChanged();
}
public boolean isChanging() {
return internalNotifier.isChanging();
}
public void propertyChanged(final Node node) {
internalNotifier.propertyChanged(node);
}
public void startTagChanged(final Element element) {
internalNotifier.startTagChanged(element);
}
public void structureChanged(final Node node) {
internalNotifier.structureChanged(node);
}
public void valueChanged(final Node node) {
internalNotifier.valueChanged(node);
changedNodes.add(node);
}
@Override
public void doDispose() {
((IDOMModel) structuredModel).setModelNotifier(internalNotifier);
}
public void registerTo(IStructuredModel structuredModel) {
this.structuredModel = structuredModel;
((IDOMModel) structuredModel).setModelNotifier(this);
}
}