// ============================================================================
//
// Copyright (C) 2006-2016 Talend Inc. - www.talend.com
//
// This source code is available under agreement available at
// %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt
//
// You should have received a copy of the agreement
// along with this program; if not, write to Talend SA
// 9 rue Pages 92150 Suresnes, France
//
// ============================================================================
package com.amalto.workbench.actions;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.util.EList;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Event;
import org.eclipse.xsd.XSDComplexTypeDefinition;
import org.eclipse.xsd.XSDCompositor;
import org.eclipse.xsd.XSDDerivationMethod;
import org.eclipse.xsd.XSDElementDeclaration;
import org.eclipse.xsd.XSDFactory;
import org.eclipse.xsd.XSDIdentityConstraintCategory;
import org.eclipse.xsd.XSDIdentityConstraintDefinition;
import org.eclipse.xsd.XSDModelGroup;
import org.eclipse.xsd.XSDParticle;
import org.eclipse.xsd.XSDSimpleTypeDefinition;
import org.eclipse.xsd.XSDTerm;
import org.eclipse.xsd.XSDTypeDefinition;
import org.eclipse.xsd.XSDXPathDefinition;
import org.eclipse.xsd.XSDXPathVariety;
import org.eclipse.xsd.impl.XSDParticleImpl;
import org.eclipse.xsd.util.XSDSchemaBuildingTools;
import com.amalto.workbench.dialogs.ComplexTypeInputDialog;
import com.amalto.workbench.editors.DataModelMainPage;
import com.amalto.workbench.i18n.Messages;
import com.amalto.workbench.image.EImage;
import com.amalto.workbench.image.ImageCache;
import com.amalto.workbench.utils.Util;
public class XSDChangeToComplexTypeAction extends UndoAction implements SelectionListener {
private Log log = LogFactory.getLog(XSDChangeToComplexTypeAction.class);
protected boolean isConcept = false;
protected XSDElementDeclaration decl = null;
protected ComplexTypeInputDialog dialog = null;
XSDElementDeclaration declNew = null;
String typeName = null;
String superTypeName;
boolean isAbstract;
boolean isChoice = false;
boolean isAll = false;
boolean showDlg = true;
private boolean isXSDModelGroup = false;
public XSDChangeToComplexTypeAction(DataModelMainPage page, boolean isXSDModelGroup) {
super(page);
this.isXSDModelGroup = isXSDModelGroup;
setImageDescriptor(ImageCache.getImage(EImage.COMPLEX_ALL.getPath()));
if (isXSDModelGroup) {
setText(Messages.XSDChangeToCXX_Text1);
} else {
setText(Messages.XSDChangeToCXX_Text2);
}
setToolTipText(Messages.XSDChangeToCXX_ActionTip);
setDescription(getToolTipText());
}
public XSDChangeToComplexTypeAction(DataModelMainPage page, XSDElementDeclaration dec, String name, boolean choice,
boolean all, boolean isAbstract, String superTypeName) {
this(page, false);
this.superTypeName = superTypeName;
this.isAbstract = isAbstract;
declNew = dec;
showDlg = false;
typeName = name;
isChoice = choice;
isAll = all;
}
@Override
public IStatus doAction() {
try {
IStructuredSelection selection = (IStructuredSelection) page.getTreeViewer().getSelection();
isConcept = false;
TreePath tPath = null;
if (((TreeSelection) selection).getPaths().length > 0) {
tPath = ((TreeSelection) selection).getPaths()[0];
}
// fliu
// add declNew to support convert action invoked from new concept/new element menu, in this case
// declNew is the new created one not the selected one in tree vew
if (declNew != null) {
decl = declNew;
checkConcept();
} else if (selection.getFirstElement() instanceof XSDModelGroup) {
for (int i = 0; i < tPath.getSegmentCount(); i++) {
if (tPath.getSegment(i) instanceof XSDElementDeclaration) {
decl = (XSDElementDeclaration) tPath.getSegment(i);
} else if (tPath.getSegment(i) instanceof XSDParticle) {
decl = (XSDElementDeclaration) ((XSDParticle) tPath.getSegment(i)).getTerm();
}
}
checkConcept();
} else if (selection.getFirstElement() instanceof XSDElementDeclaration) {
decl = (XSDElementDeclaration) selection.getFirstElement();
// check if concept or "just" element
checkConcept();
} else if (selection.getFirstElement() instanceof XSDParticle) {
// if it's a particle,it should change the element of its
// content
decl = (XSDElementDeclaration) ((XSDParticle) selection.getFirstElement()).getContent();
} else {
// if(selection.getFirstElement() instanceof XSDParticle )
if (selection.getFirstElement() != null) {
// a sub element
decl = (XSDElementDeclaration) ((XSDParticle) selection.getFirstElement()).getTerm();
}
}
// /save current Type Definition
// XSDTypeDefinition current = decl.getTypeDefinition();
List<XSDComplexTypeDefinition> types = Util.getComplexTypes(decl.getSchema());
if (showDlg) {
if (decl.getTypeDefinition() instanceof XSDComplexTypeDefinition) {
boolean confirm = MessageDialog.openConfirm(page.getSite().getShell(), Messages.Warning,
Messages.XSDChangeToCXX_ChangeToAnotherTypeWarning);
if (!confirm) {
return Status.CANCEL_STATUS;
}
}
if (tPath != null) {
for (int i = 0; i < tPath.getSegmentCount(); i++) {
if (tPath.getSegment(i) instanceof XSDElementDeclaration) {
XSDTypeDefinition type = (((XSDElementDeclaration) tPath.getSegment(i)).getTypeDefinition());
if (!type.equals(decl.getTypeDefinition())) {
types.remove(type);
}
}
if (tPath.getSegment(i) instanceof XSDParticle) {
XSDTypeDefinition type = ((XSDElementDeclaration) (((XSDParticle) tPath.getSegment(i)).getTerm()))
.getTypeDefinition();
if (!type.equals(decl.getTypeDefinition())) {
types.remove(type);
}
}
}
}
dialog = new ComplexTypeInputDialog(this, page.getSite().getShell(), "", schema, decl.getTypeDefinition(), types,//$NON-NLS-1$
isXSDModelGroup);
dialog.setBlockOnOpen(true);
int ret = dialog.open();
if (ret == Dialog.CANCEL) {
return Status.CANCEL_STATUS;
}
}
if (!showDlg && !validateType()) {
return Status.CANCEL_STATUS;
}
XSDFactory factory = XSDSchemaBuildingTools.getXSDFactory();
boolean anonymous = (typeName == null) || ("".equals(typeName));//$NON-NLS-1$
boolean alreadyExists = false;
XSDComplexTypeDefinition complexType = null;
// the sub element created if needed
XSDParticle subParticle = null;
XSDParticle groupParticle = null;
XSDElementDeclaration subElement = null;
// check if already exist
// add by ymli; fix the bug:0012278;
XSDElementDeclaration parent = null;
Object pObject = Util.getParent(decl);
if (pObject instanceof XSDElementDeclaration) {
parent = (XSDElementDeclaration) pObject;
}
if (!anonymous) {
List<XSDComplexTypeDefinition> list = Util.getComplexTypes(schema);
if (typeName.lastIndexOf(" : ") != -1) {//$NON-NLS-1$
typeName = typeName.substring(0, typeName.lastIndexOf(" : "));//$NON-NLS-1$
}
for (XSDComplexTypeDefinition td : list) {
if ((td.getName().equals(typeName))) {
alreadyExists = true;
complexType = td;
break;
}
}
} else {
XSDComplexTypeDefinition declComplexType = null;
if (parent != null && decl.getTypeDefinition() instanceof XSDComplexTypeDefinition) {
declComplexType = (XSDComplexTypeDefinition) decl.getTypeDefinition();
}
if (declComplexType != null && declComplexType.getSchema() != null && declComplexType.getName() == null) {
alreadyExists = true;
}
if (decl.getTypeDefinition() instanceof XSDSimpleTypeDefinition) {
alreadyExists = false;
}
}
if (alreadyExists) {
XSDParticle partCnt = (XSDParticle) complexType.getContentType();
partCnt.unsetMaxOccurs();
partCnt.unsetMinOccurs();
XSDTypeDefinition superType = null;
for (XSDTypeDefinition type : types) {
if (type.getName().equals(superTypeName)) {
superType = type;
break;
}
}
if (superType != null) {
XSDModelGroup mdlGrp = (XSDModelGroup) partCnt.getTerm();
boolean status = updateCompositorType(superType, mdlGrp);
if (!status) {
return Status.CANCEL_STATUS;
}
complexType.setDerivationMethod(XSDDerivationMethod.EXTENSION_LITERAL);
complexType.setBaseTypeDefinition(superType);
}
if (isAbstract) {
complexType.setAbstract(isAbstract);
} else {
complexType.unsetAbstract();
}
if (parent != null) {
parent.updateElement();
}
if (complexType != null) {
complexType.updateElement();
}
} else {// Create if does not exist
// add an element declaration
subElement = factory.createXSDElementDeclaration();
if (declNew != null) {
// crate a new entity
if (declNew.getName() != null) {
subElement.setName(declNew.getName() + "Id");//$NON-NLS-1$
}
} else {
// create a complex element
subElement.setName("subelement");//$NON-NLS-1$
}
subElement.setTypeDefinition(schema.resolveSimpleTypeDefinition(schema.getSchemaForSchemaNamespace(), "string"));//$NON-NLS-1$
subParticle = factory.createXSDParticle();
subParticle.unsetMaxOccurs();
subParticle.unsetMinOccurs();
subParticle.setContent(subElement);
subParticle.updateElement();
// create group
XSDModelGroup group = factory.createXSDModelGroup();
if (isChoice) {
group.setCompositor(XSDCompositor.CHOICE_LITERAL);
} else if (isAll) {
group.setCompositor(XSDCompositor.ALL_LITERAL);
} else {
group.setCompositor(XSDCompositor.SEQUENCE_LITERAL);
}
group.getContents().add(0, subParticle);
group.updateElement();
// create the complex type
complexType = factory.createXSDComplexTypeDefinition();
if (!anonymous) {
XSDTypeDefinition superType = null;
for (XSDTypeDefinition type : types) {
if (type.getName().equals(superTypeName)) {
superType = type;
break;
}
}
complexType.setName(typeName);
if (superType != null) {
complexType.setDerivationMethod(XSDDerivationMethod.EXTENSION_LITERAL);
complexType.setBaseTypeDefinition(superType);
updateCompositorType(superType, group);
}
if (isAbstract) {
complexType.setAbstract(isAbstract);
} else {
complexType.unsetAbstract();
}
schema.getContents().add(complexType);
}
complexType.updateElement();
// add the group
groupParticle = factory.createXSDParticle();
groupParticle.unsetMaxOccurs();
groupParticle.unsetMinOccurs();
groupParticle.setContent(group);
groupParticle.updateElement();
complexType.setContent(groupParticle);
complexType.updateElement();
}// end if NOT already exusts
// set complex type to concept
if (anonymous) {
decl.setAnonymousTypeDefinition(complexType);
} else {
decl.setTypeDefinition(complexType);
}
if (isConcept) {
buildUniqueKey(factory, decl, complexType, anonymous, alreadyExists);
}// if isConcept
decl.updateElement();
schema.update();
page.refresh();
declNew = null;
page.markDirty();
} catch (Exception e) {
log.error(e.getMessage(), e);
MessageDialog.openError(page.getSite().getShell(), Messages._Error,
Messages.bind(Messages.XSDChangeToCXX_ErrorMsg1, e.getLocalizedMessage()));
return Status.CANCEL_STATUS;
}
return Status.OK_STATUS;
}
private void buildUniqueKey(XSDFactory factory, XSDElementDeclaration declaration, XSDComplexTypeDefinition complexType,
boolean anonymous, boolean alreadyExists) {
if (factory == null || declaration == null || complexType == null) {
return;
}
// remove exisiting unique key(s)
removeExistUniqueKey(declaration);
// add new unique key with first element declaration name
if (anonymous || !alreadyExists) {
createUniqueKey(factory, declaration, complexType);
}
}
private void removeExistUniqueKey(XSDElementDeclaration declaration) {
List<XSDIdentityConstraintDefinition> keys = new ArrayList<XSDIdentityConstraintDefinition>();
EList<XSDIdentityConstraintDefinition> list = declaration.getIdentityConstraintDefinitions();
for (XSDIdentityConstraintDefinition icd : list) {
if (icd.getIdentityConstraintCategory().equals(XSDIdentityConstraintCategory.UNIQUE_LITERAL)) {
keys.add(icd);
}
}
declaration.getIdentityConstraintDefinitions().removeAll(keys);
}
private void createUniqueKey(XSDFactory factory, XSDElementDeclaration declaration, XSDComplexTypeDefinition complexType) {
List<String> fields = getPKFields(complexType);
if (!fields.isEmpty()) {
XSDIdentityConstraintDefinition uniqueKey = factory.createXSDIdentityConstraintDefinition();
uniqueKey.setIdentityConstraintCategory(XSDIdentityConstraintCategory.UNIQUE_LITERAL);
uniqueKey.setName(declaration.getName());
XSDXPathDefinition selector = factory.createXSDXPathDefinition();
selector.setVariety(XSDXPathVariety.SELECTOR_LITERAL);
selector.setValue(".");//$NON-NLS-1$
uniqueKey.setSelector(selector);
for (String fieldName : fields) {
XSDXPathDefinition field = factory.createXSDXPathDefinition();
field.setVariety(XSDXPathVariety.FIELD_LITERAL);
field.setValue(fieldName);
uniqueKey.getFields().add(field);
}
declaration.getIdentityConstraintDefinitions().add(uniqueKey);
}
}
/**
* return all defined pk field names or return the first element's name
*/
private List<String> getPKFields(XSDComplexTypeDefinition complexType) {
List<String> fields = getUsablePKFields(complexType, getElementDecarations());
if (fields.isEmpty()) {
XSDElementDeclaration firstElement = getFirstElement((XSDComplexTypeDefinition) complexType.getRootType());
if (firstElement != null) {
fields.add(firstElement.getName());
}
}
return fields;
}
private EList<XSDElementDeclaration> getElementDecarations() {
return schema.getElementDeclarations();
}
private List<String> getUsablePKFields(XSDComplexTypeDefinition complexType, List<XSDElementDeclaration> elementDeclarations) {
List<String> fields = new ArrayList<String>();
List<String> definedPKs = new ArrayList<String>();
XSDComplexTypeDefinition rootType = (XSDComplexTypeDefinition) complexType.getRootType();
for (XSDElementDeclaration decla : elementDeclarations) {
if (decla.getTypeDefinition() instanceof XSDComplexTypeDefinition) {
XSDComplexTypeDefinition typeDefinition = (XSDComplexTypeDefinition) decla.getTypeDefinition();
XSDTypeDefinition arootType = typeDefinition.getRootTypeDefinition();
if (arootType == rootType) {
recordFields(decla, definedPKs);
}
}
}
if (definedPKs.size() > 0) {
List<XSDComplexTypeDefinition> allSuperComplexTypes = Util.getAllSuperComplexTypes(complexType);
for (int i = allSuperComplexTypes.size() - 1; i >= 0; i--) {
XSDComplexTypeDefinition cTypeDef = allSuperComplexTypes.get(i);
if (cTypeDef.getContent() instanceof XSDParticle) {
XSDParticleImpl particle = (XSDParticleImpl) cTypeDef.getContent();
if (particle.getTerm() instanceof XSDModelGroup) {
XSDModelGroup group = (XSDModelGroup) particle.getTerm();
EList<XSDParticle> particles = group.getParticles();
for (XSDParticle part : particles) {
if (part.getTerm() instanceof XSDElementDeclaration) {
XSDElementDeclaration xsdDecl = (XSDElementDeclaration) part.getTerm();
if (definedPKs.contains(xsdDecl.getName())) {
fields.add(xsdDecl.getName());
}
}
}
}
}
}
}
return fields;
}
private void recordFields(XSDElementDeclaration decla, List<String> definedPKs) {
EList<XSDIdentityConstraintDefinition> idConstraintDefs = decla.getIdentityConstraintDefinitions();
if (idConstraintDefs != null) {
for (XSDIdentityConstraintDefinition idCDef : idConstraintDefs) {
if (idCDef.getIdentityConstraintCategory().equals(XSDIdentityConstraintCategory.UNIQUE_LITERAL)) {
EList<XSDXPathDefinition> xsdXPath = idCDef.getFields();
for (XSDXPathDefinition xpath : xsdXPath) {
if (!definedPKs.contains(xpath.getValue()) && xpath.getVariety() == XSDXPathVariety.FIELD_LITERAL
&& !xpath.getValue().equals(".")) { //$NON-NLS-1$
definedPKs.add(xpath.getValue());
}
}
}
}
}
}
private XSDElementDeclaration getFirstElement(XSDComplexTypeDefinition rootType) {
if (rootType.getContent() instanceof XSDParticle) {
if (((XSDParticle) rootType.getContent()).getTerm() instanceof XSDModelGroup) {
XSDModelGroup group = (XSDModelGroup) ((XSDParticle) rootType.getContent()).getTerm();
EList<XSDParticle> gpl = group.getContents();
XSDElementDeclaration firstDecl = null;
for (XSDParticle part : gpl) {
if (part.getTerm() instanceof XSDElementDeclaration) {
firstDecl = (XSDElementDeclaration) part.getTerm();
return firstDecl;
}
}
}
}
return null;
}
private boolean updateCompositorType(XSDTypeDefinition superType, XSDModelGroup currentGroup) {
XSDParticle superTypeParticle = superType.getComplexType();
XSDTerm term = superTypeParticle.getTerm();
if (term instanceof XSDModelGroup) {
XSDModelGroup group = (XSDModelGroup) term;
if (group.getCompositor() == XSDCompositor.ALL_LITERAL || currentGroup.getCompositor() == XSDCompositor.ALL_LITERAL) {
if (MessageDialog.openConfirm(null, Messages._ChangeToSequenceType, Messages._ComplexTypeToSequence)) {
group.setCompositor(XSDCompositor.SEQUENCE_LITERAL);
superTypeParticle.updateElement();
currentGroup.setCompositor(XSDCompositor.SEQUENCE_LITERAL);
currentGroup.updateElement();
return true;
}
return false;
}
}
return true;
}
private void checkConcept() {
EList<XSDIdentityConstraintDefinition> l = decl.getIdentityConstraintDefinitions();
for (XSDIdentityConstraintDefinition icd : l) {
if (icd.getIdentityConstraintCategory().equals(XSDIdentityConstraintCategory.UNIQUE_LITERAL)) {
isConcept = true;
break;
}
}
}
@Override
public void runWithEvent(Event event) {
super.runWithEvent(event);
}
/********************************
* Listener to input dialog
*/
public void widgetDefaultSelected(SelectionEvent e) {
}
public void widgetSelected(SelectionEvent e) {
if (dialog.getReturnCode() == -1) {
return;
}
superTypeName = dialog.getSuperName();
isAbstract = dialog.isAbstract();
typeName = dialog.getTypeName();
isChoice = dialog.isChoice();
isAll = dialog.isAll();
if (!validateType()) {
return;
}
dialog.close();
}
private boolean validateType() {
if (!"".equals(typeName)) {//$NON-NLS-1$
EList<XSDTypeDefinition> list = schema.getTypeDefinitions();
for (XSDTypeDefinition td : list) {
if (td.getName().equals(typeName)) {
if (td instanceof XSDSimpleTypeDefinition) {
MessageDialog.openError(page.getSite().getShell(), Messages._Error,
Messages.bind(Messages.XSDChangeToCXX_ErrorMsg2, typeName));
return false;
}
}
}// for
}
return true;
}
}