/*******************************************************************************
* Copyright (c) 2010 Herman Lee.
* 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:
* Herman Lee - initial API and implementation
******************************************************************************/
package ca.uwaterloo.gsd.fsml.codeAssist;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Vector;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.edit.provider.ItemPropertyDescriptor;
import ca.uwaterloo.gsd.fsml.core.Cause;
import ca.uwaterloo.gsd.fsml.core.FSMLMappingException;
import ca.uwaterloo.gsd.fsml.core.MarkerDescriptor;
import ca.uwaterloo.gsd.fsml.core.Markers;
import ca.uwaterloo.gsd.fsml.core.Queries;
import ca.uwaterloo.gsd.fsml.ecore.FSMLEcoreUtil;
import ca.uwaterloo.gsd.fsml.fsml.Model;
import ca.uwaterloo.gsd.fsml.fsml.ModelContainer;
import ca.uwaterloo.gsd.fsml.sync.ClassSyncItem;
import ca.uwaterloo.gsd.fsml.sync.ReconciliationDecision;
import ca.uwaterloo.gsd.fsml.sync.StructuralFeatureSyncItem;
import ca.uwaterloo.gsd.fsml.sync.SyncFactory;
import ca.uwaterloo.gsd.fsml.sync.SyncItem;
import ca.uwaterloo.gsd.fsml.sync.SynchronizationState;
public class FSMLProposalsAction {
Vector<SyncItem> codeGenTasks;
EList<EStructuralFeature> positionProposalStructuralFeatures;
List<EObject>contextForPositionProposalStructuralFeatures;
Resource resource;
public FSMLProposalsAction(Resource resource,Vector<SyncItem> codeGenTasks, EList<EStructuralFeature> positionProposalStructuralFeatures, List<EObject> contextForPositionProposalStructuralFeatures){
this.codeGenTasks =codeGenTasks;
this.resource = resource;
this.positionProposalStructuralFeatures = positionProposalStructuralFeatures;
this.contextForPositionProposalStructuralFeatures = contextForPositionProposalStructuralFeatures;
}
public EObject forwardFeatureRepresentedAsReference(
EStructuralFeature feature, EClass concreteClass, EObject eObject,
ClassSyncItem parentClassSyncItem,
NullProgressMonitor progressMonitor) throws FSMLMappingException {
EObject newConcreteClassInstance = EcoreUtil.create(concreteClass);
if (feature.isMany())
((EList) eObject.eGet(feature)).add(newConcreteClassInstance);
else
eObject.eSet(feature, newConcreteClassInstance);
ClassSyncItem classSyncItem = SyncFactory.eINSTANCE
.createClassSyncItem();
classSyncItem.setModel(newConcreteClassInstance);
classSyncItem
.setReconciliationDecision(ReconciliationDecision.ENFORCE_LITERAL);
classSyncItem
.setSynchronizationState(SynchronizationState.ADDED_MODEL_LITERAL);
//need the parent since the processField() in Mapping is getting the context from the parent
parentClassSyncItem.getSyncItems().add(classSyncItem);
EObject parentEObject = parentClassSyncItem.getModel();
EObject container = parentEObject.eContainer();
while (container!=null){
ClassSyncItem ancestorClassSyncItem = SyncFactory.eINSTANCE
.createClassSyncItem();
ancestorClassSyncItem.setModel(container);
ancestorClassSyncItem
.setReconciliationDecision(ReconciliationDecision.ENFORCE_LITERAL);
ancestorClassSyncItem
.setSynchronizationState(SynchronizationState.ADDED_MODEL_LITERAL);
ancestorClassSyncItem.getSyncItems().add(parentClassSyncItem);
parentClassSyncItem = ancestorClassSyncItem;
container = container.eContainer();
}
//mechanism to auto generate field names
// EAnnotation annotation = FSMLEcoreUtil.getEAnnotation(concreteClass, JavaMappingInterpreter.CONTEXT_FIELD);
//
// if (annotation!=null){
// EAttribute nameFeature = (EAttribute) FSMLEcoreUtil.findEStructuralFeatureWithAnnotations(concreteClass, new String[] {JavaMappingInterpreter.QUERY_FIELD_NAME} );
// String fieldName = (String) newConcreteClassInstance.eGet(nameFeature);
// if (fieldName==null){
// //create a default variable name for the field since it doesn't already have one
// String name = "a"+newConcreteClassInstance.eClass().getName();
// if (variableNames.contains(name)){
// int count = 1;
// while (variableNames.contains(name+count)){
// count++;
// }
// name = name+count;
// }
//// newConcreteClassInstance.eSet(nameFeature, name);
// }
// }
//non-containment reference such as ones used for where constraint checking
if (newConcreteClassInstance.eContainmentFeature()==null ){
if (checkConstraint(classSyncItem,(EReference)feature,eObject,parentEObject)){
codeGenTasks.add(classSyncItem);
} else {
return null;
}
} else {
codeGenTasks.add(classSyncItem);
}
EList<EStructuralFeature> allStructuralFeatures = concreteClass
.getEAllStructuralFeatures();
EAnnotation constraints = concreteClass
.getEAnnotation("constraints");
String choiceFromFeatureGroup=null;
if (constraints != null && concreteClass.getEAnnotation(FSMLEcoreUtil.FEATURE_GROUP)!=null) {
//if (false){
choiceFromFeatureGroup = (String)FSMLEcoreUtil.getChoiceFromFeatureGroup(allStructuralFeatures);
} else {
prepareSubProposals(concreteClass, allStructuralFeatures,newConcreteClassInstance);
}
//else {
//forward all the mandatory features
//first forward all the mandatory non-containment references (constraints)
//then forward all the other mandatory features
for (EStructuralFeature structuralFeature : allStructuralFeatures) {
if (structuralFeature.getLowerBound() > 0 || (choiceFromFeatureGroup!=null && choiceFromFeatureGroup.equals(structuralFeature.getName()))) {
if (structuralFeature instanceof EReference){
Collection<EClass> concreteClasses = FSMLEcoreUtil
.getSubclassesOfEClass(
((EReference) structuralFeature)
.getEReferenceType(), true);
for (EClass class1 : concreteClasses) {
EObject newConcreteClassInstance2 = EcoreUtil.create(class1);
if (newConcreteClassInstance2.eContainmentFeature()!=null){
continue;
}
newConcreteClassInstance=forwardFeatureRepresentedAsReference(structuralFeature,
class1, newConcreteClassInstance,
classSyncItem, progressMonitor);
}
}
}
}
for (EStructuralFeature structuralFeature : allStructuralFeatures) {
if (structuralFeature.getLowerBound() > 0) {
//also forward all the mandatory features
if (structuralFeature instanceof EAttribute) {
forwardFeatureRepresentedAsAttribute(structuralFeature,
classSyncItem, progressMonitor);
} else if (structuralFeature instanceof EReference) {
Collection<EClass> concreteClasses = FSMLEcoreUtil
.getSubclassesOfEClass(
((EReference) structuralFeature)
.getEReferenceType(), true);
for (EClass class1 : concreteClasses) {
EObject newConcreteClassInstance2 = EcoreUtil.create(class1);
if (newConcreteClassInstance2.eContainmentFeature()==null){
continue;
}
newConcreteClassInstance=forwardFeatureRepresentedAsReference(structuralFeature,
class1, newConcreteClassInstance,
classSyncItem, progressMonitor);
}
}
}
}
return newConcreteClassInstance;
}
public boolean forwardFeatureRepresentedAsAttribute(
EStructuralFeature feature, ClassSyncItem parentClassSyncItem,
NullProgressMonitor progressMonitor) throws FSMLMappingException {
//TOOD: need to set the model
StructuralFeatureSyncItem featureSyncItem = SyncFactory.eINSTANCE
.createStructuralFeatureSyncItem();
featureSyncItem.setStructuralFeature(feature);
featureSyncItem
.setReconciliationDecision(ReconciliationDecision.ENFORCE_LITERAL);
featureSyncItem
.setSynchronizationState(SynchronizationState.ADDED_MODEL_LITERAL);
//need the parent since the processField() in Mapping is getting the context from the parent
parentClassSyncItem.getSyncItems().add(featureSyncItem);
EObject parentEObject = parentClassSyncItem.getModel();
EObject container = parentEObject.eContainer();
while (container!=null){
ClassSyncItem ancestorClassSyncItem = SyncFactory.eINSTANCE
.createClassSyncItem();
ancestorClassSyncItem.setModel(container);
ancestorClassSyncItem
.setReconciliationDecision(ReconciliationDecision.ENFORCE_LITERAL);
ancestorClassSyncItem
.setSynchronizationState(SynchronizationState.ADDED_MODEL_LITERAL);
ancestorClassSyncItem.getSyncItems().add(parentClassSyncItem);
parentClassSyncItem = ancestorClassSyncItem;
container = container.eContainer();
}
// if (feature.getEAnnotation(JavaMappingInterpreter.QUERY_TYPED_WITH)!=null){
// for (VarDesc varDesc : variablesInScope) {
// if (varDesc.type.toString().equals(feature.getEAnnotation(JavaMappingInterpreter.QUERY_TYPED_WITH).getDetails().get(JavaMappingInterpreter.DETAIL_NAME))) {
// getLinkedProposalModel()
// .getPositionGroup("FSMLProposalLinkedPosition"+0, true)
// .addProposal(varDesc.name,image,1);
// }
// }
// }else if (feature.getEType().getName().equals("EString")) {
// //TODO: String type = retrieveTypeOfArgumentFromSignature(syncItem, index);
// for (VarDesc varDesc : variablesInScope) {
// if (varDesc.type.toString().equals("String")) {
// getLinkedProposalModel()
// .getPositionGroup("FSMLProposalLinkedPosition"+0, true)
// .addProposal(varDesc.name,image,1);
// }
// }
//
// }
codeGenTasks.add(featureSyncItem);
return false;
}
public void prepareSubProposals(EClass concreteClass,
EList<EStructuralFeature> allStructuralFeatures,EObject newConcreteClassInstance) {
//check whether we are in a feature group
//if we are, propose features in the feature group using FSMLPositionProposal
EAnnotation featureGroup = concreteClass
.getEAnnotation(FSMLEcoreUtil.FEATURE_GROUP);
if (featureGroup != null) {
positionProposalStructuralFeatures.addAll(allStructuralFeatures);
contextForPositionProposalStructuralFeatures.add(newConcreteClassInstance);
}
}
//return true if forward should be run after constraint check
private boolean checkConstraint(ClassSyncItem syncItem, EReference feature, EObject element,EObject parentEObject) throws FSMLMappingException {
EObject newConcreteClassInstance = syncItem.getModel();
EAnnotation annotation = feature.getEAnnotation(Queries.CONSTRAINT_WHERE);
if (annotation != null) {
// find potential targets
boolean foundTargetObject = false;
Collection<EObject> targets = ItemPropertyDescriptor.getReachableObjectsOfType(
element, feature.getEReferenceType());
EList<EObject> fsmlModel = resource.getContents();
Model assertedModel = ((ModelContainer) fsmlModel.get(0))
.getAssertedModel();
String attributePath = (String) annotation.getDetails().get(Queries.DETAIL_ATTRIBUTE);
if (attributePath == null)
return false;
String equalsToPath = (String) annotation.getDetails().get(Queries.DETAIL_EQUALS_TO);
String containsPath = (String) annotation.getDetails().get(Queries.DETAIL_CONTAINS);
String inPath = (String) annotation.getDetails().get(Queries.DETAIL_IN);
if (equalsToPath == null && containsPath == null && inPath == null)
return false;
FSMLEcoreUtil.NavigationResult target = null;
String equalsToValue = null;
EList equalsToList = null;
if (equalsToPath != null){
target = FSMLEcoreUtil.navigateToEObject(element, equalsToPath);
equalsToValue = (String) target.eObject.eGet(target.eAttribute);
}
else if (containsPath != null){
target = FSMLEcoreUtil.navigateToEObject(element, containsPath);
equalsToValue = (String) target.eObject.eGet(target.eAttribute);
}
else if (inPath != null){
target = FSMLEcoreUtil.navigateToEObject(element, inPath);
equalsToList = (EList)target.eObject.eGet(target.eAttribute);
}
else{
return false ;
}
// if (equalsToValue == null && (equalsToList == null|| equalsToList.isEmpty())){
// return;
// }
assert (attributePath != null);
// check for an additional 'andWhere' clause
EAnnotation and = feature.getEAnnotation(Queries.CONSTRAINT_AND);
String attribute2Path = null;
String equalsTo2Path = null;
String equalsTo2Value = null;
if (and != null) {
attribute2Path = (String) and.getDetails().get(Queries.DETAIL_ATTRIBUTE);
equalsTo2Path = (String) and.getDetails().get(Queries.DETAIL_EQUALS_TO);
FSMLEcoreUtil.NavigationResult target2 = FSMLEcoreUtil.navigateToEObject(element, equalsTo2Path);
equalsTo2Value = (String) target2.eObject.eGet(target2.eAttribute);
}
EAnnotation andParentIs = feature.getEAnnotation(Queries.CONSTRAINT_AND_PARENT_IS);
String parentClassName = null;
if (andParentIs != null)
parentClassName = (String) andParentIs.getDetails().get(Queries.DETAIL_INSTANCE_OF);
ArrayList<EObject> possibleTargetEObject = new ArrayList<EObject>() ;
ArrayList<EAttribute> possibleTargetEAttribute = new ArrayList<EAttribute>();
// find the target(s) satisfying the constraint
for (Object object : targets) {
if (object instanceof EObject) {
if (assertedModel!=null){
EObject targetEContainer = ((EObject)object).eContainer();
while (targetEContainer!=null && !targetEContainer.equals(assertedModel)){
targetEContainer = targetEContainer.eContainer();
}
if (targetEContainer==null){
continue;
}
} else {
break;
}
EObject eObject = (EObject) object;
EClass eClass = eObject.eClass();
FSMLEcoreUtil.NavigationResult attributeTarget = FSMLEcoreUtil.navigateToEObject(eObject, attributePath);
if (attributeTarget.errorMessage != null)
throw new FSMLMappingException(Cause.MODEL_NAVIGATION_ERROR, feature, attributeTarget.errorMessage);
EAttribute eAttribute = attributeTarget.eAttribute;
if (eAttribute==null || attributeTarget.eObject==null){
continue;
}
if (equalsToPath != null) {
// handle only single-valued attributes
String auxValue = (String) attributeTarget.eObject.eGet(eAttribute);
if (FSMLEcoreUtil.valuesEqual(equalsToValue, auxValue)||equalsToValue==null) {
if (equalsToValue==null){
//do something
}
if (and != null) {
FSMLEcoreUtil.NavigationResult target3 = FSMLEcoreUtil.navigateToEObject(eObject, attribute2Path);
if (target3.eObject == null || target3.eAttribute == null)
continue;
//FSMLEcoreUtil.NavigationResult attribute2Target = FSMLEcoreUtil.navigateToEObject(element, attribute2Path);
EAttribute eAttribute2 = target3.eAttribute;
String auxValue2 = (String) target3.eObject.eGet(eAttribute2);
if (equalsTo2Value!=null && !FSMLEcoreUtil.valuesEqual(equalsTo2Value, auxValue2))
continue;
}
if (andParentIs != null) {
if (eObject.eContainer()==null||!eObject.eContainer().eClass().getName().equals(parentClassName))
continue;
}
foundTargetObject = true;
if (feature.isMany())
((EList) element.eGet(feature)).add(eObject);
else {
element.eSet(feature, eObject);
}
}
//need to iterate all the targets if
if (equalsToValue!=null && !feature.isMany()){
continue;
} else if (equalsToValue==null) {
possibleTargetEObject.add(attributeTarget.eObject);
possibleTargetEAttribute.add(attributeTarget.eAttribute);
}
}
else if (containsPath != null) {
// handle only multi-valued attributes
if (attributeTarget.errorMessage != null)
throw new FSMLMappingException(Cause.MODEL_NAVIGATION_ERROR, attributeTarget.errorMessage);
else {
EList auxValues = (EList) attributeTarget.eObject.eGet(attributeTarget.eAttribute);
if (auxValues.contains(equalsToValue)) {
foundTargetObject = true;
if (feature.isMany())
((EList) element.eGet(feature)).add(eObject);
else {
element.eSet(feature, eObject);
break;
}
}
}
}
else if (inPath != null){
// handle only single-valued attributes contained in list.
String auxValue = (String) attributeTarget.eObject.eGet(eAttribute);
if (equalsToList.contains(auxValue)){
foundTargetObject = true;
if (feature.isMany())
((EList) element.eGet(feature)).add(eObject);
else {
element.eSet(feature, eObject);
break;
}
}
}
}
}//for
if (possibleTargetEObject.size()!=0){
String attributeValueWithSuggestionsWizard = FSMLEcoreUtil.getAttributeValueWithSuggestionsWizard(possibleTargetEObject,possibleTargetEAttribute);
if (attributeValueWithSuggestionsWizard.equals("new value")){
attributeValueWithSuggestionsWizard= null;
while (attributeValueWithSuggestionsWizard==null){
attributeValueWithSuggestionsWizard = (String)FSMLEcoreUtil.getAttributeValueFromAttributeValueWizard(target.eAttribute);
}
foundTargetObject=false;
target.eObject.eSet(target.eAttribute, attributeValueWithSuggestionsWizard);
equalsToValue = attributeValueWithSuggestionsWizard;
} else { //use a preexisting value
for (int i=0;i<possibleTargetEObject.size();i++){
if (possibleTargetEObject.get(i).eGet(possibleTargetEAttribute.get(i)).equals(attributeValueWithSuggestionsWizard)){
syncItem.setModel(possibleTargetEObject.get(i));
if (feature.isMany())
((EList) element.eGet(feature)).add(possibleTargetEObject.get(i));
else {
element.eSet(feature, possibleTargetEObject.get(i));
break;
}
}
}
target.eObject.eSet(target.eAttribute, attributeValueWithSuggestionsWizard);
return false;
}
}
if (!foundTargetObject){
//create one ourselves
//the following check maybe necessary
//if (parentEObject!=null){
if (andParentIs!=null){
parentClassName = (String) andParentIs.getDetails().get(Queries.DETAIL_INSTANCE_OF);
TreeIterator<EObject> allContents = assertedModel.eAllContents();
while (allContents.hasNext()){
EObject parentCandidate = allContents.next();
if (parentCandidate.eClass().getName().equals(parentClassName)){
checkConstraintHelper(newConcreteClassInstance, feature,
attributePath, equalsToValue,
parentCandidate);
break;
}
}//end while
}
else { //try the where/and constraints for ../ attributes
if (attributePath.startsWith("../") ||
attribute2Path.startsWith("../")){
String targetAttributePath;
String targetEqualsToValue;
if (attributePath.startsWith("../")){
targetAttributePath = attributePath;
targetEqualsToValue = equalsToValue;
} else {
targetAttributePath = attribute2Path;
targetEqualsToValue = equalsTo2Value;
}
for (Object object : targets) {
EObject eObject = (EObject) object;
EClass eClass = eObject.eClass();
FSMLEcoreUtil.NavigationResult attributeTarget = FSMLEcoreUtil.navigateToEObject(eObject, targetAttributePath);
if (attributeTarget.eObject == null || attributeTarget.eAttribute == null)
continue;
EAttribute eAttribute = attributeTarget.eAttribute;
if (targetEqualsToValue != null) {
// handle only single-valued attributes
String auxValue = (String) attributeTarget.eObject.eGet(eAttribute);
if (FSMLEcoreUtil.valuesEqual(targetEqualsToValue, auxValue)) {
checkConstraintHelper(newConcreteClassInstance, feature,
attributePath, equalsToValue,
attributeTarget.eObject);
break;
}
}
}
}
}
//} //end if (parentEObject)
}//end if (!foundTargetObject)
return true;
}
return false;
}
//this helper method sets the context for checkConstraints
private void checkConstraintHelper(EObject newConcreteClassInstance, EReference feature,
String attributePath, String equalsToValue, EObject parentCandidate)
throws FSMLMappingException {
List<IMarker> markers = Markers.INSTANCE.getMarkers(FSMLEcoreUtil.getFSMLId(parentCandidate, null));
if (markers.size()!=0){
try {
int markerStartPos = ((Integer)markers.get(0).getAttribute(MarkerDescriptor.ATTRIBUTE_CHAR_START)).intValue();
int markerEndPos = ((Integer)markers.get(0).getAttribute(MarkerDescriptor.ATTRIBUTE_CHAR_END)).intValue();
if (markers.get(0).getResource().getFileExtension().equals("xml")){
Queries.INSTANCE.associateContext(markerStartPos,markerEndPos,parentCandidate);
EList<EStructuralFeature> allStructuralFeatures = parentCandidate.eClass().getEAllStructuralFeatures();
for (EStructuralFeature structuralFeature : allStructuralFeatures) {
if (structuralFeature.getEType().equals(feature.getEType())){
if (structuralFeature.isMany())
((EList) parentCandidate.eGet(structuralFeature)).add(newConcreteClassInstance);
else
parentCandidate.eSet(structuralFeature, newConcreteClassInstance);
FSMLEcoreUtil.NavigationResult attributeTarget = FSMLEcoreUtil.navigateToEObject(newConcreteClassInstance, attributePath);
if (attributeTarget.errorMessage != null)
throw new FSMLMappingException(Cause.MODEL_NAVIGATION_ERROR, feature, attributeTarget.errorMessage);
EAttribute eAttribute = attributeTarget.eAttribute;
attributeTarget.eObject.eSet(eAttribute,equalsToValue);
}
}
}
} catch (CoreException e) {
e.printStackTrace();
}
}
}
}