/******************************************************************************* * 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. * Stanislav Nichev - initial API and implementation. *******************************************************************************/ package org.eclipse.wst.sse.sieditor.model.reconcile.adapters; import org.eclipse.wst.sse.core.internal.provisional.IModelStateListener; import org.eclipse.wst.sse.core.internal.provisional.INodeAdapter; import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier; import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument; import org.w3c.dom.Attr; import org.w3c.dom.Document; 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.IModelReconcileRegistry; import org.eclipse.wst.sse.sieditor.model.reconcile.adapters.componentsource.IConcreteComponentSource; /** * This adapter workarounds the following eclipse bug: The EMF model is not * correctly updated on DOM model change. * */ public abstract class AbstractModelReconcileAdapter implements IModelStateListener, INodeAdapter, IDisposable { private IModelReconcileRegistry modelReconcileRegistry; protected final IConcreteComponentSource concreteComponentSource; private Document document; public AbstractModelReconcileAdapter(final IConcreteComponentSource concreteComponentSource) { this.concreteComponentSource = concreteComponentSource; } // ========================================================= // adapt/unadapt methods // ========================================================= public void adapt(final Document document) { this.document = document; ((INodeNotifier) document).addAdapter(this); adaptChildElements(document); } /** * Registers this adapter to the given element and all it's children * * @param element */ protected void adapt(final Element element) { if (((INodeNotifier) element).getExistingAdapter(this) == null) { ((INodeNotifier) element).addAdapter(this); adaptChildElements(element); } } /** * Registers this adapter to all children of the given element node * * @param element */ private void adaptChildElements(final Node parentNode) { for (Node child = parentNode.getFirstChild(); child != null; child = child.getNextSibling()) { if (child.getNodeType() == Node.ELEMENT_NODE) { adapt((Element) child); } } } /** * Unregisters this adapter to the given element and all it's children * * @param element */ private void unadapt(final Element element) { if (((INodeNotifier) element).getExistingAdapter(this) != null) { ((INodeNotifier) element).removeAdapter(this); unadaptChildElements(element); } } /** * Unregisters this adapter to all children of the given element node * * @param element */ private void unadaptChildElements(final Node parentNode) { for (Node child = parentNode.getFirstChild(); child != null; child = child.getNextSibling()) { if (child.getNodeType() == Node.ELEMENT_NODE) { unadapt((Element) child); } } } @Override public boolean isAdapterForType(final Object type) { return type == this; } /** * Utility method. Custom implementation should be added here since the * {@link #notifyChanged(INodeNotifier, int, Object, Object, Object, int)} * method is final. */ protected abstract void processNotifyChanged(final INodeNotifier notifier, final int eventType, final Object changedFeature, final Object oldValue, final Object newValue, final int pos); /** * Method is final in order to keep the methods call pattern. */ @Override public final void notifyChanged(final INodeNotifier notifier, final int eventType, final Object changedFeature, final Object oldValue, final Object newValue, final int pos) { redistributeAdapter(eventType, oldValue, newValue); processNotifyChanged(notifier, eventType, changedFeature, oldValue, newValue, pos); } /** * Utility method. Returns the attribute prefix from the given attribute * name. * * @param attr * - the attribute that has changed * @return the prefix of the attribute name */ protected String getPrefix(final Attr attr) { String prefix = null; if (attr.getName().indexOf(":") != -1) { //$NON-NLS-1$ prefix = attr.getLocalName(); } return prefix; } protected void redistributeAdapter(final int eventType, final Object oldValue, final Object newValue) { if (INodeNotifier.ADD == eventType && newValue instanceof Element) { adapt((Element) newValue); } if (INodeNotifier.REMOVE == eventType && oldValue instanceof Element) { unadapt((Element) oldValue); } } public void setModelReconcileRegistry(final IModelReconcileRegistry modelReconcileRegistry) { this.modelReconcileRegistry = modelReconcileRegistry; } public IModelReconcileRegistry getModelReconcileRegistry() { return modelReconcileRegistry; } // /** // * Override, so it has one instance per child class per concreteComponentSource // */ // public abstract int hashCode(); // // /** // * Override, so it has one instance per child class per concreteComponentSource // */ // public abstract boolean equals(final Object obj); @Override public int hashCode() { return this.getClass().hashCode() & concreteComponentSource.hashCode(); } @Override public boolean equals(final Object obj) { if (this.getClass().isInstance(obj)) { return concreteComponentSource.equals(((AbstractModelReconcileAdapter) obj).concreteComponentSource); } return false; } // ========================================================= // dummy interface implementation // ========================================================= @Override public void modelAboutToBeChanged(final IStructuredModel model) { } @Override public void modelAboutToBeReinitialized(final IStructuredModel structuredModel) { } @Override public void modelChanged(final IStructuredModel model) { } @Override public void modelDirtyStateChanged(final IStructuredModel model, final boolean isDirty) { } @Override public void modelReinitialized(final IStructuredModel structuredModel) { } @Override public void modelResourceDeleted(final IStructuredModel model) { } @Override public void modelResourceMoved(final IStructuredModel oldModel, final IStructuredModel newModel) { } @Override public void doDispose() { ((INodeNotifier) document).removeAdapter(this); unadaptChildElements(document); final IStructuredModel structuredModel = ((IDOMDocument) document) .getModel(); structuredModel.removeModelStateListener(this); } }