package no.hal.scxml.generator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import no.hal.scxml.ScxmlxtStandaloneSetup;
import no.hal.scxml.scxmlxt.AbstractState;
import no.hal.scxml.scxmlxt.AbstractUriLiteral;
import no.hal.scxml.scxmlxt.ScxmlxtFactory;
import no.hal.scxml.scxmlxt.ScxmlxtPackage;
import no.hal.scxml.scxmlxt.State;
import no.hal.scxml.scxmlxt.StateMachine;
import no.hal.scxml.scxmlxt.VarDef;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil.Copier;
import org.eclipse.emf.ecore.xmi.impl.EcoreResourceFactoryImpl;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import org.eclipse.emf.ecore.xmi.impl.XMLResourceFactoryImpl;
import org.eclipse.xtext.resource.XtextResourceSet;
import com.google.inject.Injector;
public class ScxmlxtGenerator {
private static final String SCXMLXT = "scxmlxt";
private EObject model;
private URI scxmlxtBaseUri;
private ResourceSet resourceSet;
private Injector injector;
private ResourceSet scxmlxtResourceSet;
public ScxmlxtGenerator(EObject model, URI scxmlxtBaseUri) {
super();
this.model = model;
this.scxmlxtBaseUri = scxmlxtBaseUri;
this.resourceSet = new ResourceSetImpl();
this.injector = new ScxmlxtStandaloneSetup().createInjectorAndDoEMFRegistration();
this.scxmlxtResourceSet = injector.getInstance(XtextResourceSet.class);
}
public URIConverter getUriConverter() {
return resourceSet.getURIConverter();
}
// private StatemachineRuntimeSupport statemachineRuntimeSupport = new StatemachineRuntimeSupport();
private Map<String, Object> rootVariables = new HashMap<String, Object>();
public Map<String, Object> getRootVariables() {
return rootVariables;
}
public final static String MODEL_RESOURCE_VARIABLE_NAME = "thisModelResource";
private Resource modelResource;
public StateMachine generateScxmlxt() {
modelResource = model.eResource();
// runtimeStateResource = (getUriConverter().exists(stateModel, null) ? resourceSet.getResource(stateModel, true) : resourceSet.createResource(stateModel));
// statemachineRuntimeSupport.setRuntimeStates(runtimeStateResource);
StateMachine stateMachine = ScxmlxtFactory.eINSTANCE.createStateMachine();
rootVariables.put(MODEL_RESOURCE_VARIABLE_NAME, modelResource);
// rootVariables.put("thisRuntimeStateResource", runtimeStateResource);
generateScxmlxt(modelResource.getContents().iterator(), stateMachine);
return stateMachine;
}
private void generateScxmlxt(Iterator<EObject> eObjects, AbstractState parent) {
while (eObjects.hasNext()) {
EObject eObject = eObjects.next();
State state = getEObjectState(eObject);
if (state != null) {
state.setName(getStateName(eObject, parent));
state.setInitialTransition(ScxmlxtFactory.eINSTANCE.createInitialTransition());
parent.getStates().add(state);
}
generateScxmlxt(eObject.eContents().iterator(), (state != null ? state : parent));
}
}
public State getEObjectState(EObject eObject) {
List<EClass> eClasses = new ArrayList<EClass>();
eClasses.add(eObject.eClass());
collectSuperTypes(eClasses);
State state = null;
MergingCopier copier = new MergingCopier();
// merging is implemented by means of adding/overwriting features
// hence, superTypes should be handled first
Collections.reverse(eClasses);
for (EClass eClass: eClasses) {
state = getEObjectState(eObject, eClass, state, copier);
}
return state;
}
private void collectSuperTypes(List<EClass> superTypes) {
for (int i = 0; i < superTypes.size(); i++) {
EClass eClass = superTypes.get(i);
for (EClass superType: eClass.getESuperTypes()) {
if (! superTypes.contains(superType)) {
superTypes.add(superType);
}
}
}
}
private Map<URI, StateMachine> uriStateMachineMap = new HashMap<URI, StateMachine>();
public static String getFullName(AbstractState abstractState, EObject root) {
String name = null;
while (abstractState instanceof State && abstractState != root) {
State state = (State)abstractState;
name = (name == null ? state.getName() : state.getName() + "." + name);
abstractState = (AbstractState)abstractState.eContainer();
}
return name;
}
private static class MergingCopier extends Copier {
private EObject root;
public void setRoot(EObject root) {
this.root = root;
}
private Map<String, EObject> fullNameMap = new HashMap<String, EObject>();
protected EObject createCopy(EObject eObject) {
// check if eObject has the same name as an existing one in mergeInto
String fullName = (eObject instanceof State ? getFullName((State)eObject, root) : null);
if (fullName != null && fullNameMap.containsKey(fullName)) {
// in this case, reuse the existing copy
return fullNameMap.get(fullName);
}
EObject copy = super.createCopy(eObject);
if (fullName != null) {
fullNameMap.put(fullName, copy);
}
return copy;
}
}
private State getEObjectState(EObject eObject, EClass eClass, State mergeInto, MergingCopier copier) {
URI scxmlxtUri = URI.createURI(eClass.getName()).appendFileExtension(SCXMLXT).resolve(scxmlxtBaseUri);
StateMachine stateMachine = uriStateMachineMap.get(scxmlxtUri);
if (stateMachine == null) {
try {
Resource scxmlxtResource = scxmlxtResourceSet.getResource(scxmlxtUri, true);
stateMachine = (StateMachine)scxmlxtResource.getContents().get(0);
uriStateMachineMap.put(scxmlxtUri, stateMachine);
} catch (Exception e) {
}
}
if (stateMachine != null) {
State state = (mergeInto != null ? mergeInto : ScxmlxtFactory.eINSTANCE.createState());
// add a variable referencing the owning eObject
// should be initialized first, so other variables can use it
state.getVariables().add(createEObjectVariable(eObject, eClass));
// copy content
copier.setRoot(stateMachine);
state.getStates().addAll(copier.copyAll(stateMachine.getStates()));
state.getTransitions().addAll(copier.copyAll(stateMachine.getTransitions()));
state.getVariables().addAll(copier.copyAll(stateMachine.getVariables()));
copier.copyReferences();
return state;
}
return null;
}
private String getEObjectVariableName(EClass eClass) {
return "this" + eClass.getName();
}
public boolean isEObjectVariableName(String name, EObject eObject) {
String prefix = "this";
return name.startsWith(prefix) && name.length() > prefix.length();
}
private VarDef createEObjectVariable(EObject eObject, EClass eClass) {
Resource resource = eObject.eResource();
URI uri = resource.getURI().appendFragment(resource.getURIFragment(eObject));
return createUriVariable(getEObjectVariableName(eClass), ScxmlxtFactory.eINSTANCE.createEObjectUriLiteral(), uri);
}
private VarDef createUriVariable(String name, AbstractUriLiteral uriLiteral, URI uri) {
VarDef var = ScxmlxtFactory.eINSTANCE.createVarDef();
var.setName(name);
uriLiteral.setUri(uri);
var.setInit(uriLiteral);
return var;
}
private String getStateName(EObject eObject, AbstractState parent) {
String name = null;
if (name == null) {
EStructuralFeature nameFeature = eObject.eClass().getEStructuralFeature("name");
if (nameFeature != null) {
Object nameValue = eObject.eGet(nameFeature);
if (nameValue != null) {
name = nameValue.toString();
}
}
}
if (name == null) {
name = eObject.eClass().getName();
EStructuralFeature eContainingFeature = eObject.eContainingFeature();
if (eContainingFeature != null) {
name += ".@" + eContainingFeature.getName();
Object siblingsValue = eObject.eGet(eContainingFeature);
if (siblingsValue instanceof List<?>) {
List<?> siblings = (List<?>)siblingsValue;
if (siblings.size() > 1) {
name += "." + siblings.indexOf(eObject);
}
}
}
}
if (name == null) {
Resource resource = eObject.eResource();
if (resource != null) {
name = resource.getURIFragment(eObject);
}
}
return name;
}
public static void main(String[] args) {
Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("xml", new XMLResourceFactoryImpl());
Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("xmi", new XMIResourceFactoryImpl());
Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("ecore", new EcoreResourceFactoryImpl());
EPackage.Registry.INSTANCE.put(ScxmlxtPackage.eNS_URI, ScxmlxtPackage.eINSTANCE);
URI baseUri = URI.createURI(String.valueOf(ScxmlxtGenerator.class.getResource("TestGame.ecore")));
URI model = URI.createURI("Game1.xmi").resolve(baseUri);
ScxmlxtGenerator scxmlxtGenerator = new ScxmlxtGenerator(new ResourceSetImpl().getResource(model, true).getContents().get(0), baseUri);
scxmlxtGenerator.generateScxmlxt();
}
}