blob: 688a3f0d6d0ba4b14c159625b4da489cf376ee40 [file] [log] [blame]
/*******************************************************************************
* 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.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier;
import org.eclipse.wst.wsdl.Operation;
import org.eclipse.wst.wsdl.Part;
import org.eclipse.wst.wsdl.WSDLPackage;
import org.eclipse.wst.wsdl.XSDSchemaExtensibilityElement;
import org.eclipse.wst.wsdl.util.WSDLConstants;
import org.eclipse.xsd.XSDAttributeDeclaration;
import org.eclipse.xsd.XSDConcreteComponent;
import org.eclipse.xsd.XSDElementDeclaration;
import org.eclipse.xsd.XSDFeature;
import org.eclipse.xsd.XSDImport;
import org.eclipse.xsd.XSDPackage;
import org.eclipse.xsd.XSDSchema;
import org.eclipse.xsd.impl.XSDImportImpl;
import org.eclipse.xsd.util.XSDConstants;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.eclipse.wst.sse.sieditor.model.reconcile.adapters.componentsource.IConcreteComponentSource;
import org.eclipse.wst.sse.sieditor.model.utils.EmfXsdUtils;
/**
* Subclass of the {@link AbstractModelReconcileAdapter}. This adapter is
* responsible for the fixing of problems caused by DOM element attributes
* changes.
*
*/
public class AttributesReconcileAdapter extends AbstractModelReconcileAdapter {
public AttributesReconcileAdapter(final IConcreteComponentSource concreteComponentSource) {
super(concreteComponentSource);
}
@Override
protected void processNotifyChanged(final INodeNotifier notifier, final int eventType, final Object changedFeature,
final Object oldValue, final Object newValue, final int pos) {
if (changedFeature instanceof Attr) {
processAttributeChangeFeature(notifier, (Attr) changedFeature, oldValue, newValue);
}
}
private void processAttributeChangeFeature(final INodeNotifier notifier, final Attr attr, final Object oldValue,
final Object newValue) {
processNameAttribute(notifier, attr.getLocalName());
processRefAttribute(notifier, attr.getLocalName(), newValue);
processSchemaLocationAttribute(notifier, attr.getLocalName(), newValue);
processAttributeStartingWithXMLNS(notifier, oldValue, newValue, attr);
processParameterOrderAttribute(notifier, attr.getLocalName());
processTargetNamespaceAttribute(notifier, attr.getName());
processNamespaceAttribute(notifier, attr.getName());
processElementAttribute(notifier, attr.getLocalName(), newValue);
processTypeAttribute(notifier, attr.getLocalName(), newValue);
processDefaultAttribute(notifier, attr.getLocalName(), newValue);
}
private void processNameAttribute(final INodeNotifier notifier, final String attributeName) {
if (XSDConstants.NAME_ATTRIBUTE.equals(attributeName)) {
final EObject eObject = concreteComponentSource.getConcreteComponentFor((Element) notifier);
if (eObject instanceof XSDElementDeclaration) {
this.getModelReconcileRegistry().setNeedsReconciling(true);
}
}
}
private void processRefAttribute(final INodeNotifier notifier, final String attributeName, final Object newValue) {
if (XSDConstants.REF_ATTRIBUTE.equals(attributeName) && newValue != null) {
/*
* we are in the following scenario: we are setting "ref" to element
* attribute. if the element we are updating has "type" attribute,
* the EMF model does not set it to null. that way, the updated
* element has both "type" and "ref" attributes, which is invalid
*/
final EObject eObject = concreteComponentSource.getConcreteComponentFor((Element) notifier);
if (eObject instanceof XSDConcreteComponent) {
final XSDConcreteComponent element = (XSDConcreteComponent) eObject;
if (element instanceof XSDElementDeclaration) {
((XSDElementDeclaration) element).setTypeDefinition(null);
} else if (element instanceof XSDAttributeDeclaration) {
((XSDAttributeDeclaration) element).setTypeDefinition(null);
}
}
}
}
private void processSchemaLocationAttribute(final INodeNotifier notifier, final String attributeName, final Object newValue) {
if(true) {
return;
}
if (XSDConstants.SCHEMALOCATION_ATTRIBUTE.equals(attributeName)) {
final EObject eObject = concreteComponentSource.getConcreteComponentFor((Element) notifier);
if (eObject instanceof XSDImportImpl) {
final XSDSchema resolvedSchema = resolveImportedSchema(newValue, eObject);
getModelReconcileRegistry().addChangedSchema(resolvedSchema);
getModelReconcileRegistry().setNeedsReconciling(true);
}
}
}
private XSDSchema resolveImportedSchema(final Object newValue, final EObject eObject) {
XSDSchema schema = null;
final XSDImportImpl xsdImport = (XSDImportImpl) eObject;
if (xsdImport.getResolvedSchema() == null && newValue == null) {
xsdImport.reset();
xsdImport.importSchema();
}
schema = xsdImport.getResolvedSchema();
return schema;
}
private void processAttributeStartingWithXMLNS(final INodeNotifier notifier, final Object oldValue, final Object newValue,
final Attr attr) {
if (attr.getName().startsWith(EmfXsdUtils.XMLNS_PREFIX)) {
/*
* here we are updating the "xmlns" attribute. if that's the case,
* we are either updating the schema element of a XSD document, or a
* WSDL definition element, or a WSDL types schema
*/
final EObject eObject = concreteComponentSource.getConcreteComponentFor((Element) notifier);
XSDSchema schema = null;
if (eObject instanceof XSDSchema) {
schema = (XSDSchema) eObject;
} else if (eObject instanceof XSDSchemaExtensibilityElement) {
schema = ((XSDSchemaExtensibilityElement) eObject).getSchema();
}
if (schema != null) {
this.getModelReconcileRegistry().setNeedsReconciling(true);
this.getModelReconcileRegistry().addChangedSchema(schema);
fixSchemaForSchemaState(oldValue, newValue, attr, schema);
}
}
}
private void fixSchemaForSchemaState(final Object oldValue, final Object newValue, final Attr attr, final XSDSchema xsdSchema) {
final String prefix = getPrefix(attr);
if (newValue == null) {
/*
* the new value is null, so we need to remove the prefix/namespace
* from the EMF model map. In some cases (on undo/redo) the EMF
* didn't do that
*/
if (xsdSchema.getQNamePrefixToNamespaceMap().containsKey(prefix)) {
xsdSchema.getQNamePrefixToNamespaceMap().remove(prefix);
}
/*
* notify for the change anyway, since we want to run the
* validation. the EMF model didn't do that as well
*/
xsdSchema.eNotify(new ENotificationImpl((InternalEObject) xsdSchema, Notification.REMOVE, null, null, null));
} else {
/*
* we have non-null value, so we must be updating existing
* namespace, or adding a new one
*/
if (/*XSDConstants.isSchemaForSchemaNamespace(attr.getValue())
&&*/ !attr.getValue().equals(xsdSchema.getQNamePrefixToNamespaceMap().get(prefix))) {
// add the namespace to the map
xsdSchema.getQNamePrefixToNamespaceMap().put(prefix, attr.getValue());
}
/*
* notify for the change anyway, since we want to run the
* validation. the EMF model didn't do that as well
*/
xsdSchema.eNotify(new ENotificationImpl((InternalEObject) xsdSchema, Notification.ADD, null, null, null));
}
}
private void processParameterOrderAttribute(final INodeNotifier notifier, final String attributeName) {
if (WSDLConstants.PARAMETER_ORDER_ATTRIBUTE.equals(attributeName)) {
/* we need to check if we are updating a part name */
final EObject eObject = concreteComponentSource.getConcreteComponentFor((Element) notifier);
if (eObject instanceof Operation) {
/*
* we need to tell the operation EMF object to update itself
* from its corresponding DOM element. this updates the
* parameterOrdering collection of the operation
*/
((Operation) eObject).elementChanged((Element) notifier);
}
}
}
private void processTargetNamespaceAttribute(final INodeNotifier notifier, final String attributeName) {
if (XSDConstants.TARGETNAMESPACE_ATTRIBUTE.equals(attributeName)) {
/*
* we need to prepare the reconciling information. setting
* tagetNamespace "breaks" the EMF WSDL references
*/
this.getModelReconcileRegistry().setNeedsReconciling(true);
if (notifier instanceof Element) {
final EObject eObject = concreteComponentSource.getConcreteComponentFor((Element) notifier);
if (eObject instanceof XSDSchema) {
this.getModelReconcileRegistry().addChangedSchema((XSDSchema) eObject);
} else if (eObject instanceof XSDSchemaExtensibilityElement) {
this.getModelReconcileRegistry().addChangedSchema(((XSDSchemaExtensibilityElement) eObject).getSchema());
}
}
}
}
private void processNamespaceAttribute(final INodeNotifier notifier, final String attributeName) {
if (WSDLConstants.NAMESPACE_ATTRIBUTE.equals(attributeName)) {
this.getModelReconcileRegistry().setNeedsReconciling(true);
/*
* we are updating schema import. this breaks the schema references
* to the changed import. we need to update the information for
* resolve.
*/
if (notifier instanceof Element) {
final EObject eObject = concreteComponentSource.getConcreteComponentFor((Element) notifier);
if (eObject instanceof XSDImport) {
this.getModelReconcileRegistry().addChangedSchema(((XSDImport) eObject).getSchema());
}
}
}
}
private void processElementAttribute(final INodeNotifier notifier, final String attributeName, final Object newValue) {
if (WSDLConstants.ELEMENT_ATTRIBUTE.equals(attributeName)) {
processPartAttribute(notifier, newValue, WSDLPackage.PART__ELEMENT_DECLARATION);
}
}
private void processTypeAttribute(final INodeNotifier notifier, final String attributeName, final Object newValue) {
if (WSDLConstants.TYPE_ATTRIBUTE.equals(attributeName)) {
processPartAttribute(notifier, newValue, WSDLPackage.PART__TYPE_DEFINITION);
processElementTypeAttribute(notifier, newValue);
}
}
private void processPartAttribute(final INodeNotifier notifier, final Object newValue, final int eStructuralFeatureId) {
if (notifier instanceof Element) {
final EObject eObject = concreteComponentSource.getConcreteComponentFor((Element) notifier);
if (eObject instanceof Part) {
this.getModelReconcileRegistry().setNeedsReconciling(true);
final Part part = (Part) eObject;
if (newValue == null) {
part
.eNotify(new ENotificationImpl((InternalEObject) part, Notification.SET, eStructuralFeatureId, null,
null));
}
part.elementChanged(part.getElement());
}
}
}
private void processElementTypeAttribute(final INodeNotifier notifier, final Object newValue) {
if (notifier instanceof Element && newValue != null) {
final EObject eObject = concreteComponentSource.getConcreteComponentFor((Element) notifier);
if (eObject instanceof XSDElementDeclaration) {
final XSDElementDeclaration elementDeclaration = (XSDElementDeclaration) eObject;
if (elementDeclaration.getTypeDefinition() != null && elementDeclaration.getTypeDefinition().eContainer() == null) {
getModelReconcileRegistry().setNeedsReconciling(true);
getModelReconcileRegistry().addChangedSchema(elementDeclaration.getSchema());
}
}
}
}
private void processDefaultAttribute(final INodeNotifier notifier, final String attributeName, final Object newValue) {
if (XSDConstants.DEFAULT_ATTRIBUTE.equals(attributeName)) {
if (notifier instanceof Element) {
final EObject eObject = concreteComponentSource.getConcreteComponentFor((Element) notifier);
((XSDFeature)eObject).setLexicalValue((String)newValue);
eObject.eNotify(new ENotificationImpl((InternalEObject) eObject, Notification.SET, XSDPackage.XSD_FEATURE__VALUE, null,
newValue));
}
}
}
}