/******************************************************************************* * 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.command.emf.xsd; import java.text.MessageFormat; import java.util.HashMap; import java.util.List; import java.util.Map; 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.util.EList; import org.eclipse.xsd.XSDAnnotation; import org.eclipse.xsd.XSDAttributeUse; import org.eclipse.xsd.XSDComplexTypeContent; import org.eclipse.xsd.XSDComplexTypeDefinition; import org.eclipse.xsd.XSDConcreteComponent; import org.eclipse.xsd.XSDDerivationMethod; import org.eclipse.xsd.XSDFactory; import org.eclipse.xsd.XSDParticle; import org.eclipse.xsd.XSDSimpleTypeDefinition; import org.eclipse.xsd.XSDTypeDefinition; import org.eclipse.xsd.impl.XSDConcreteComponentImpl; import org.eclipse.wst.sse.sieditor.command.common.AbstractCompositeNotificationOperation; import org.eclipse.wst.sse.sieditor.command.common.AbstractNotificationOperation; import org.eclipse.wst.sse.sieditor.model.Activator; import org.eclipse.wst.sse.sieditor.model.api.IModelRoot; import org.eclipse.wst.sse.sieditor.model.api.IXSDModelRoot; import org.eclipse.wst.sse.sieditor.model.i18n.Messages; import org.eclipse.wst.sse.sieditor.model.utils.EmfXsdUtils; import org.eclipse.wst.sse.sieditor.model.xsd.api.IStructureType; import org.eclipse.wst.sse.sieditor.model.xsd.api.IType; import org.eclipse.wst.sse.sieditor.model.xsd.impl.AbstractType; public class SetStructureTypeBaseTypeCompositeCommand extends AbstractCompositeNotificationOperation { private final IType iNewBaseType; private final IStructureType iComplexType; private ImportSchemaCommand importSchemaCommand; private SetStructureTypeBaseTypeCommandInternal setBaseTypeCommand; public SetStructureTypeBaseTypeCompositeCommand(final IModelRoot root, final IStructureType iComplexType, final IType iNewBaseType) { super(root, iComplexType, Messages.SetBaseTypeCommand_set_base_type_command_label); this.iComplexType = iComplexType; this.iNewBaseType = iNewBaseType; } @Override public AbstractNotificationOperation getNextOperation(final List<AbstractNotificationOperation> subOperations) { if (importSchemaCommand == null) { importSchemaCommand = new ImportSchemaCommand((IXSDModelRoot) iComplexType.getModelRoot(), iComplexType.getParent(), (AbstractType) iNewBaseType); return importSchemaCommand; } if (setBaseTypeCommand == null) { setBaseTypeCommand = new SetStructureTypeBaseTypeCommandInternal(root, iComplexType, iNewBaseType); return setBaseTypeCommand; } return null; } @Override public boolean canExecute() { return modelObject != null && iNewBaseType != null; } // ========================================================= // complex type internal command // ========================================================= private class SetStructureTypeBaseTypeCommandInternal extends AbstractNotificationOperation { private final IType iNewBaseType; private final IStructureType iComplexType; public SetStructureTypeBaseTypeCommandInternal(final IModelRoot root, final IStructureType iComplexType, final IType iNewBaseType) { super(root, iComplexType, Messages.SetBaseTypeCommand_set_base_type_command_label); this.iComplexType = iComplexType; this.iNewBaseType = iNewBaseType; } @Override public IStatus run(final IProgressMonitor monitor, final IAdaptable info) throws ExecutionException { final XSDComplexTypeDefinition xsdComplexType = (XSDComplexTypeDefinition) iComplexType.getComponent(); final XSDTypeDefinition xsdNewBaseType = (XSDTypeDefinition) iNewBaseType.getComponent(); final XSDTypeDefinition xsdOldBaseType = xsdComplexType.getBaseTypeDefinition(); if (areEqual(xsdOldBaseType, xsdNewBaseType)) { return Status.OK_STATUS; } final IStatus status = resolveNewBaseType(xsdNewBaseType); if (!status.isOK()) { return status; } processNewSimpleTypeDefinition(xsdComplexType, xsdNewBaseType); processNewComplexTypeDefinition(xsdComplexType, xsdNewBaseType); return Status.OK_STATUS; } /** * Helper method. Checks if the new type to set is the same as the old * type. * * @param complexType * @param newBaseType * @return */ private boolean areEqual(final XSDTypeDefinition oldBaseType, final XSDTypeDefinition newBaseType) { return newBaseType.getName() != null && newBaseType.getName().equals(oldBaseType.getName()) && newBaseType.getTargetNamespace() != null && newBaseType.getTargetNamespace().equals(oldBaseType.getTargetNamespace()); } private void processNewSimpleTypeDefinition(final XSDComplexTypeDefinition complexType, final XSDTypeDefinition newBaseType) { if (isContentChangingFromComplexToSimpleContent(complexType.getContent(), newBaseType)) { updateFromComplexToSimpleContent(complexType); } if (newBaseType instanceof XSDSimpleTypeDefinition) { if (shouldUpdateDerivationMethodToExtension(complexType)) { complexType.setDerivationMethod(XSDDerivationMethod.EXTENSION_LITERAL); } ((XSDSimpleTypeDefinition) complexType.getContent()).setBaseTypeDefinition((XSDSimpleTypeDefinition) newBaseType); complexType.setBaseTypeDefinition(newBaseType); } } private void processNewComplexTypeDefinition(final XSDComplexTypeDefinition complexType, final XSDTypeDefinition newBaseType) { if (isContentChangingFromSimpleToComplexContent(complexType, newBaseType)) { updateFromSimpleToComplexContent(complexType); } if (newBaseType instanceof XSDComplexTypeDefinition) { if (shouldUpdateDerivationMethodToExtension(complexType)) { complexType.setDerivationMethod(XSDDerivationMethod.EXTENSION_LITERAL); } complexType.setBaseTypeDefinition(newBaseType); } } private boolean shouldUpdateDerivationMethodToExtension(final XSDComplexTypeDefinition complexType) { return null == complexType.getDerivationMethod() || complexType.getDerivationMethod() != XSDDerivationMethod.EXTENSION_LITERAL; } // ========================================================= // complex to simple content update methods // ========================================================= private boolean isContentChangingFromComplexToSimpleContent(final XSDComplexTypeContent complexTypeContent, final XSDTypeDefinition newBaseType) { return newBaseType instanceof XSDSimpleTypeDefinition && (null == complexTypeContent || complexTypeContent instanceof XSDParticle); } private XSDComplexTypeContent updateFromComplexToSimpleContent(final XSDComplexTypeDefinition complexType) { final EList<XSDConcreteComponent> oldXsdContents = ((XSDConcreteComponentImpl) complexType).getXSDContents(); final Map<XSDAttributeUse, XSDAnnotation> clonedAttributes = getClonedAttributes(oldXsdContents); complexType.setContent(null); complexType.setDerivationMethod(XSDDerivationMethod.EXTENSION_LITERAL); final XSDComplexTypeContent complexTypeContent = EmfXsdUtils.getXSDFactory().createXSDSimpleTypeDefinition(); complexType.setContent(complexTypeContent); complexType.getAttributeContents().clear(); addAttributes(complexType, clonedAttributes); complexType.elementChanged(complexType.getElement()); return complexTypeContent; } /** * Helper method. Returns collection of the cloned attributes in the * oldXsdContents */ private Map<XSDAttributeUse, XSDAnnotation> getClonedAttributes(final EList<XSDConcreteComponent> oldXsdContents) { final Map<XSDAttributeUse, XSDAnnotation> attributes = new HashMap<XSDAttributeUse, XSDAnnotation>(); for (final XSDConcreteComponent component : oldXsdContents) { if (component instanceof XSDAttributeUse) { final XSDAttributeUse oldXsdAttributeUse = (XSDAttributeUse) component; final XSDAnnotation oldAnnotation = oldXsdAttributeUse.getAttributeDeclaration().getAnnotation(); final XSDAttributeUse newAttributeUse = (XSDAttributeUse) oldXsdAttributeUse.cloneConcreteComponent(true, false); attributes.put(newAttributeUse, oldAnnotation); } } return attributes; } // ========================================================= // simple to complex content update methods // ========================================================= private boolean isContentChangingFromSimpleToComplexContent(final XSDComplexTypeDefinition complexType, final XSDTypeDefinition newBaseType) { return newBaseType instanceof XSDComplexTypeDefinition && (null == complexType.getContent() || complexType.getContent() instanceof XSDSimpleTypeDefinition); } private void updateFromSimpleToComplexContent(final XSDComplexTypeDefinition complexType) { final EList<XSDConcreteComponent> oldXsdContents = ((XSDConcreteComponentImpl) complexType).getXSDContents(); final Map<XSDAttributeUse, XSDAnnotation> clonedAttributes = getClonedAttributes(oldXsdContents); complexType.setBaseTypeDefinition(null); complexType.setContent(null); final XSDParticle particle = XSDFactory.eINSTANCE.createXSDParticle(); complexType.setContent(particle); complexType.getAttributeContents().clear(); addAttributes(complexType, clonedAttributes); complexType.elementChanged(complexType.getElement()); } private void addAttributes(final XSDComplexTypeDefinition complexType, final Map<XSDAttributeUse, XSDAnnotation> clonedAttributes) { // add attributes one-by-one so that notifications are correctly // fired for (final XSDAttributeUse attribute : clonedAttributes.keySet()) { complexType.getAttributeContents().add(attribute); EmfXsdUtils.copyAnnotations(clonedAttributes.get(attribute), attribute.getAttributeDeclaration().getAnnotation(), attribute.getSchema().getDocument()); } } // ========================================================= // resolve helpers // ========================================================= private IStatus resolveNewBaseType(final XSDTypeDefinition newBaseType) throws ExecutionException { if (newBaseType.getTargetNamespace() == null) { return new Status(IStatus.ERROR, Activator.PLUGIN_ID, Messages.SetStructureTypeCommand_cannot_refer_type_with_no_namespace); } final IType resolvedType = iComplexType.getParent().resolveType((AbstractType) iNewBaseType); if (resolvedType == null) { return new Status(IStatus.ERROR, Activator.PLUGIN_ID, MessageFormat.format( Messages.SetStructureTypeCommand_msg_can_not_resolve_type_X, iNewBaseType.getName())); } return Status.OK_STATUS; } @Override public boolean canExecute() { return modelObject != null && iNewBaseType != null; } } }