/*******************************************************************************
* 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.
* Keshav Veerapaneni - initial API and implementation.
*******************************************************************************/
package org.eclipse.wst.sse.sieditor.command.emf.xsd;
import java.text.MessageFormat;
import java.util.Iterator;
import java.util.List;
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.xsd.XSDComplexTypeContent;
import org.eclipse.xsd.XSDComplexTypeDefinition;
import org.eclipse.xsd.XSDCompositor;
import org.eclipse.xsd.XSDConstrainingFacet;
import org.eclipse.xsd.XSDDerivationMethod;
import org.eclipse.xsd.XSDElementDeclaration;
import org.eclipse.xsd.XSDEnumerationFacet;
import org.eclipse.xsd.XSDFactory;
import org.eclipse.xsd.XSDModelGroup;
import org.eclipse.xsd.XSDNamedComponent;
import org.eclipse.xsd.XSDPackage;
import org.eclipse.xsd.XSDParticle;
import org.eclipse.xsd.XSDSimpleTypeDefinition;
import org.eclipse.xsd.XSDTypeDefinition;
import org.eclipse.xsd.XSDWhiteSpaceFacet;
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.i18n.Messages;
import org.eclipse.wst.sse.sieditor.model.utils.EmfXsdUtils;
import org.eclipse.wst.sse.sieditor.model.utils.ISimpleTypeFacetsUtils;
import org.eclipse.wst.sse.sieditor.model.utils.SimpleTypeFacetsUtils;
import org.eclipse.wst.sse.sieditor.model.xsd.api.ISchema;
import org.eclipse.wst.sse.sieditor.model.xsd.api.ISimpleType;
import org.eclipse.wst.sse.sieditor.model.xsd.api.IType;
import org.eclipse.wst.sse.sieditor.model.xsd.impl.AbstractType;
import org.eclipse.wst.sse.sieditor.model.xsd.impl.SimpleType;
import org.eclipse.wst.sse.sieditor.model.xsd.impl.UnresolvedType;
/**
* Sets the basetype for a structure type and a simple type. This also ensures
* that the component for which we are setting the baseType is in proper state
* for setting the respt baseType.
*
* @pre null != baseType <code>&&</code> <BR>
* !sourceType instanceof {@link XSDElementDeclaration} <code>&&</code><BR>
* (baseType instanceof {@link XSDSimpleTypeDefinition}<code>||</code><BR>
* baseType instanceof {@link XSDComplexTypeDefinition})
*/
public class SetBaseTypeCommand extends AbstractNotificationOperation {
private IType _baseType;
private final IType _type;
public SetBaseTypeCommand(final IModelRoot root, final IType type, final IType baseType) {
super(root, type, Messages.SetBaseTypeCommand_set_base_type_command_label);
this._type = type;
this._baseType = baseType;
}
@Override
public IStatus run(final IProgressMonitor monitor, final IAdaptable info) throws ExecutionException {
final ISchema schema = _type.getParent();
IType resolvedType = null;
try {
resolvedType = schema.resolveType((AbstractType) _baseType);
} catch (final ExecutionException e) {
return new Status(IStatus.ERROR, Activator.PLUGIN_ID, String.format("Cannot resolve type %s.", _baseType.getName())); //$NON-NLS-1$
}
if (resolvedType instanceof UnresolvedType) {
return new Status(IStatus.ERROR, Activator.PLUGIN_ID, MessageFormat.format(
Messages.SetBaseTypeCommand_msg_can_not_resolve_base_type_X, _baseType.getName()));
}
if (_type instanceof ISimpleType) {
if (resolvedType instanceof SimpleType) {
final XSDNamedComponent type = resolvedType.getComponent();
if (type instanceof XSDSimpleTypeDefinition) {
_baseType = resolvedType;
} else {
return new Status(IStatus.ERROR, Activator.PLUGIN_ID, MessageFormat.format(
Messages.SetBaseTypeCommand_msg_base_type_must_be_simple_X, _baseType.getName()));
}
} else {
return new Status(IStatus.ERROR, Activator.PLUGIN_ID, MessageFormat.format(Messages.SetBaseTypeCommand_0,
_baseType.getName()));
}
} else {// structure type
if (resolvedType.getComponent() instanceof XSDElementDeclaration) {
return new Status(IStatus.ERROR, Activator.PLUGIN_ID, MessageFormat.format(
Messages.SetBaseTypeCommand_msg_base_type_must_be_simple_X, _baseType.getName()));
}
}
final XSDNamedComponent component = (XSDNamedComponent) modelObject.getComponent();
XSDTypeDefinition type = null;
final XSDPackage xsdPackage = EmfXsdUtils.getXSDPackage();
final XSDFactory factory = EmfXsdUtils.getXSDFactory();
final XSDTypeDefinition baseType = (XSDTypeDefinition) _baseType.getComponent();
// Do initial cleanup
if (component instanceof XSDElementDeclaration) {
final XSDElementDeclaration element = (XSDElementDeclaration) component;
type = element.getTypeDefinition();
if (null == type || null != type.getName()) {
// If Element refers to global type un-set type reference
component.eUnset(xsdPackage.getXSDElementDeclaration_TypeDefinition());
type = null;
}
/*
* BaseType -> ST CT AnonymousType -> null Create Anonymous ST
* Create Anonymous CT ST - Create Anonymous CT CT - -
*/
if (null == type && baseType instanceof XSDSimpleTypeDefinition) {
element.setAnonymousTypeDefinition(type = factory.createXSDSimpleTypeDefinition());
} else if (baseType instanceof XSDComplexTypeDefinition && (null == type || type instanceof XSDSimpleTypeDefinition)) {
element.eUnset(xsdPackage.getXSDElementDeclaration_AnonymousTypeDefinition());
element.setAnonymousTypeDefinition(type = factory.createXSDComplexTypeDefinition());
final XSDComplexTypeDefinition complexType = (XSDComplexTypeDefinition) type;
complexType.setDerivationMethod(XSDDerivationMethod.EXTENSION_LITERAL);
final XSDParticle content = factory.createXSDParticle();
final XSDModelGroup modelGroup = factory.createXSDModelGroup();
modelGroup.setCompositor(XSDCompositor.SEQUENCE_LITERAL);
content.setContent(modelGroup);
complexType.setContent(content);
}
} else if (component instanceof XSDTypeDefinition) {
type = (XSDTypeDefinition) component;
} else if (!(component instanceof XSDSimpleTypeDefinition)) {
throw new RuntimeException("component has to be XSDElementDeclaration or XSDComplexTypeDefinition"); //$NON-NLS-1$
}
// Set the Base Type
if (type instanceof XSDSimpleTypeDefinition && baseType instanceof XSDSimpleTypeDefinition) {
updateSimpleTypeFacets((XSDSimpleTypeDefinition) type, (XSDSimpleTypeDefinition) baseType);
((XSDSimpleTypeDefinition) type).setBaseTypeDefinition((XSDSimpleTypeDefinition) baseType);
} else if (type instanceof XSDComplexTypeDefinition) {
final XSDComplexTypeDefinition complexType = (XSDComplexTypeDefinition) type;
XSDComplexTypeContent content = complexType.getContent();
/*
* Do initial cleanup BaseType -> ST CT content -> null Create ST
* Create PT ST - Create PT PT Create ST -
*/
if (baseType instanceof XSDSimpleTypeDefinition && (null == content || content instanceof XSDParticle)) {
complexType.setContent(null);
complexType.setDerivationMethod(XSDDerivationMethod.EXTENSION_LITERAL);
final XSDComplexTypeContent complexTypeContent = factory.createXSDSimpleTypeDefinition();
complexType.setContent(content = complexTypeContent);
} else {
if (baseType instanceof XSDComplexTypeDefinition) {
if (null == content || content instanceof XSDSimpleTypeDefinition) {
complexType.eUnset(xsdPackage.getXSDComplexTypeDefinition_Content());
final XSDParticle particle = factory.createXSDParticle();
final XSDModelGroup modelGroup = factory.createXSDModelGroup();
modelGroup.setCompositor(XSDCompositor.SEQUENCE_LITERAL);
particle.setContent(modelGroup);
complexType.setContent(particle);
}
}
}
if (baseType instanceof XSDSimpleTypeDefinition) {
((XSDSimpleTypeDefinition) content).setBaseTypeDefinition((XSDSimpleTypeDefinition) baseType);
// DOIT check this
complexType.setBaseTypeDefinition(baseType);
if (complexType.getContent() instanceof XSDSimpleTypeDefinition)
complexType.setDerivationMethod(XSDDerivationMethod.EXTENSION_LITERAL);
} else if (baseType instanceof XSDComplexTypeDefinition) {
if (null == complexType.getDerivationMethod()) {
complexType.setDerivationMethod(XSDDerivationMethod.EXTENSION_LITERAL);
}
complexType.setBaseTypeDefinition(baseType);
}
}
return Status.OK_STATUS;
}
@Override
public boolean canExecute() {
return modelObject != null && _baseType != null;
}
/**
* Utility method. Checks if the simple type can reuse at least one of the
* facets from the old type
*
* @param oldType
* - the old type we are changing
* @param newType
* - the new type we are setting
* @return <code>true</code> if at least one facet from the old type can be
* reused. <code>false</code> otherwise
*/
public static boolean canReuseAtLeastOneFacet(final XSDSimpleTypeDefinition oldType, final XSDSimpleTypeDefinition newType) {
if (getSimpleTypeFacets().areLengthFacetsSupported(newType)) {
if ((oldType.getLengthFacet() != null) || (oldType.getMinLengthFacet() != null)
|| (oldType.getMaxLengthFacet() != null)) {
return true;
}
}
if (getSimpleTypeFacets().areExclusiveFacetsSupported(newType) && (oldType.getMinExclusiveFacet() != null
|| oldType.getMaxExclusiveFacet() != null)) {
return true;
}
if (getSimpleTypeFacets().areInclusiveFacetsSupported(newType)
&& (oldType.getMinInclusiveFacet() != null || oldType.getMaxInclusiveFacet() != null)) {
return true;
}
if (getSimpleTypeFacets().isFractionDigitsFacetSupported(newType) && oldType.getFractionDigitsFacet() != null) {
return true;
}
if (getSimpleTypeFacets().isTotalDigitsFacetSupported(newType) && oldType.getTotalDigitsFacet() != null) {
return true;
}
if (getSimpleTypeFacets().isEnumerationFacetSupported(newType) && !oldType.getEnumerationFacets().isEmpty()) {
return true;
}
if (getSimpleTypeFacets().isWhitespaceFacetSupported(newType)) {
// if one of the whitespace facets is fixed - no need to keep it
final XSDWhiteSpaceFacet whiteSpaceFacet = oldType.getWhiteSpaceFacet();
final XSDWhiteSpaceFacet newWhiteSpaceFacet = newType.getWhiteSpaceFacet();
if (whiteSpaceFacet != null) {
if ((!whiteSpaceFacet.isFixed()) && ((newWhiteSpaceFacet == null) || (!newWhiteSpaceFacet.isFixed()))) {
return true;
}
}
}
if (!oldType.getPatternFacets().isEmpty()) {
return true;
}
return false;
}
private void updateSimpleTypeFacets(final XSDSimpleTypeDefinition type, final XSDSimpleTypeDefinition baseType) {
final List<XSDConstrainingFacet> facets = type.getFacetContents();
if (!getSimpleTypeFacets().areLengthFacetsSupported(baseType)) {
removeFacet(facets, type.getLengthFacet());
removeFacet(facets, type.getMinLengthFacet());
removeFacet(facets, type.getMaxLengthFacet());
}
if (!getSimpleTypeFacets().areInclusiveFacetsSupported(baseType)) {
removeFacet(facets, type.getMinInclusiveFacet());
removeFacet(facets, type.getMaxInclusiveFacet());
}
if(!getSimpleTypeFacets().areExclusiveFacetsSupported(baseType)){
removeFacet(facets, type.getMinExclusiveFacet());
removeFacet(facets, type.getMaxExclusiveFacet());
}
if (!getSimpleTypeFacets().isFractionDigitsFacetSupported(baseType)) {
removeFacet(facets, type.getFractionDigitsFacet());
}
if (!getSimpleTypeFacets().isTotalDigitsFacetSupported(baseType)) {
removeFacet(facets, type.getTotalDigitsFacet());
}
if (!getSimpleTypeFacets().isEnumerationFacetSupported(baseType)) {
for (final Iterator<XSDConstrainingFacet> it = facets.iterator(); it.hasNext();) {
if (it.next() instanceof XSDEnumerationFacet) {
it.remove();
}
}
}
if (!getSimpleTypeFacets().isWhitespaceFacetSupported(baseType)) {
removeFacet(facets, type.getWhiteSpaceFacet());
}
}
private void removeFacet(final List<XSDConstrainingFacet> facets, final XSDConstrainingFacet facet) {
if (facet != null) {
facets.remove(facet);
}
}
/**
* Utility method. Checks if the given element is of simple type. If it is
* and the old facets can be reused by the new simple type, executes the
* {@link SetBaseTypeCommand}. <br>
* If the old facets cannot be reused, returns <code>null</code>. <br>
* <br>
* <i>NOTE: The set base type command is not executed through the
* environment, but directly through its run method.</i>
*
* @param elementType
* - the element type that we are changing
* @param newType
* - the new type we are trying to set
* @param monitor
* - the progress monitor to reuse in the
* {@link SetBaseTypeCommand} execution
* @param info
* @param root
* @return the status of the {@link SetBaseTypeCommand} execution or
* <code>null</code> if the {@link SetBaseTypeCommand} was not
* executed
* @throws ExecutionException
*/
public static IStatus setBaseTypeIfFacetsCanBeReused(final IType elementType, final IType newType,
final IProgressMonitor monitor, final IAdaptable info, final IModelRoot root) throws ExecutionException {
if ((elementType != null) && elementType.isAnonymous() && (elementType instanceof SimpleType)
&& (elementType.getBaseType() instanceof SimpleType) && (newType instanceof SimpleType)) {
// if one of the constraints can be reused - set restriction base
// type
if (SetBaseTypeCommand.canReuseAtLeastOneFacet((XSDSimpleTypeDefinition) elementType.getComponent(),
(XSDSimpleTypeDefinition) newType.getComponent())) {
final SetBaseTypeCommand baseTypeCommand = new SetBaseTypeCommand(root, elementType, newType);
return baseTypeCommand.run(monitor, info);
}
}
return null;
}
// =========================================================
// helpers
// =========================================================
private static ISimpleTypeFacetsUtils getSimpleTypeFacets() {
return SimpleTypeFacetsUtils.instance();
}
}