/******************************************************************************* * 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.javaMappingInterpreter.analysis.impl; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.URI; 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.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.jdt.core.IJavaProject; import ca.uwaterloo.gsd.fsml.core.Cause; import ca.uwaterloo.gsd.fsml.core.FSMLMappingException; import ca.uwaterloo.gsd.fsml.ecore.FSMLEcoreUtil; import ca.uwaterloo.gsd.fsml.fsml.provider.FsmlEditPlugin; import ca.uwaterloo.gsd.fsml.implModel.Default; import ca.uwaterloo.gsd.fsml.implModel.ImplModelPackage; import ca.uwaterloo.gsd.fsml.implModel.Variant; import ca.uwaterloo.gsd.fsml.javaMappingInterpreter.JavaMappingInterpreter; import ca.uwaterloo.gsd.fsml.javaMappingInterpreter.analysis.IJavaImplVariantManager; public class JavaImplVariantManager implements IJavaImplVariantManager { public static final String modelName = JavaImplVariantManagerConstants.MODEL_NAME + JavaImplVariantManagerConstants.MODEL_FILE_EXTENSION; protected IJavaProject project = null; protected Resource resourceImplModel = null; private EClass implModelEClass = null; private EObject implModelEObject = null; private EList<EObject> existingResourceModel; private HashSet<String> overridedDefaultsMappingKeys = new HashSet<String>(); private ResourceSet resourceSet; //since in forward, a new model is not created and defaults may have already been set in the current model //but the finish method will still be called, the following flag is necessary private boolean skipFinish = true; public JavaImplVariantManager(IJavaProject project) { this.project = project; this.initialize(); } /** * Called before starting the analysis, so that the manager has a chance to initialize */ public void init() { } private void initialize() { try { // Create a resource set // resourceSet = new ResourceSetImpl(); skipFinish = false; IFile implModelFile = ResourcesPlugin.getWorkspace().getRoot() .getFile(project.getPath().append(modelName)); URI implModelURI = URI.createPlatformResourceURI(implModelFile .getFullPath().toString(), true); if (implModelFile.exists()) { existingResourceModel = resourceSet.getResource(implModelURI, true).getContents(); } resourceImplModel = resourceSet.createResource(implModelURI); // create the model implModelEClass = ImplModelPackage.eINSTANCE.getimplModel(); implModelEObject = EcoreUtil.create(implModelEClass); } catch (Exception exception) { FsmlEditPlugin.INSTANCE.log(exception); } } public EObject getModel() { return implModelEObject; } public void finish() { if (JavaMappingInterpreter.analyzeImplVariants()) { if (skipFinish) { return; } try { skipFinish = true;//next time computeDefault(); // add the implementation model object to the content resourceImplModel.getContents().add(implModelEObject); // Save the contents of the resource to the file system. Map options = new HashMap(); // Michal: TODO: fix it //options.put(XMLResource.OPTION_ENCODING, "ASCII"); options.put("OPTION_ENCODING", "ASCII"); resourceImplModel.save(options); } catch (IOException e) { e.printStackTrace(); } } } public void computeDefault() { // adaptive/intelligent/predictive code generation goes here... // reset all the "overridden" defaults into the newly generated model if (existingResourceModel != null) { EList<EObject> implModelContents = existingResourceModel.get(0) .eContents(); EObject currentFeature = null; for (int i = 0; i < implModelContents.size(); i++) { currentFeature = implModelContents.get(i); if (currentFeature.eClass().getName().startsWith( JavaImplVariantManagerConstants.DEFAULT)) { EStructuralFeature overrided = currentFeature.eClass() .getEStructuralFeature( JavaImplVariantManagerConstants.OVERRIDDEN); if (currentFeature.eGet(overrided).equals(Boolean.TRUE)) { String eClass = currentFeature .eGet( currentFeature .eClass() .getEStructuralFeature( JavaImplVariantManagerConstants.ECLASS)) .toString(); String eStructuralFeature = currentFeature .eGet( currentFeature .eClass() .getEStructuralFeature( JavaImplVariantManagerConstants.ESTRUCTURALFEATURE)) .toString(); // mapping key is by the mapping excluding mapping type, // not mapping type String mappingKey = eClass + JavaImplVariantManagerConstants.DELIMITER + eStructuralFeature; overridedDefaultsMappingKeys.add(mappingKey); ((EList) implModelEObject.eGet(getDefaultsReference())) .add(currentFeature); } } } } EList<EObject> features = implModelEObject.eContents(); /*alternatively, the variant EObject can be used as the key for the *variant hashmap. However, since the equals in EObject is equivalent to == *instead of comparing the values in the EObjects, the containsKey *method in HashMap will fail. Although EcoreUtil has an equals method for this *purpose, using the method will require overriding the equals method in EObject (generated code that *is current unstable) * */ //anonymous class of the object stored in the variant hashmap class VariantHashMapObject { int occurrences = 1; EObject variant = null; VariantHashMapObject(EObject variant) { this.variant = variant; } public int getOccurrences() { return occurrences; } public EObject getVariant() { return variant; } public VariantHashMapObject incrementOccurrences() { occurrences++; return this; } } HashMap<String, HashMap<String, VariantHashMapObject>> mapping = new HashMap<String, HashMap<String, VariantHashMapObject>>(); // find the number of occurrences of each variant in each mapping for (EObject currentFeature : features) { if (!currentFeature.eClass().getName().startsWith( JavaImplVariantManagerConstants.DEFAULT)) { // make sure that it is a variant String eClass = currentFeature.eGet( currentFeature.eClass().getEStructuralFeature( JavaImplVariantManagerConstants.ECLASS)) .toString(); String eStructuralFeature = currentFeature .eGet( currentFeature .eClass() .getEStructuralFeature( JavaImplVariantManagerConstants.ESTRUCTURALFEATURE)) .toString(); // mapping key is by the mapping excluding mapping type, not // mapping type String mappingKey = eClass + JavaImplVariantManagerConstants.DELIMITER + eStructuralFeature; StringBuffer structuralFeatures = new StringBuffer(); for (EStructuralFeature feature:currentFeature.eClass().getEAllAttributes()) { structuralFeatures.append(currentFeature.eGet(feature)); } String variantKey = currentFeature.toString().replaceFirst( ".+\\(", currentFeature.eClass().getName() + "(")+structuralFeatures.toString(); if (overridedDefaultsMappingKeys.contains(mappingKey)) { System.err.println(mappingKey + " overrided by the user"); } else { if (!mapping.containsKey(mappingKey)) { HashMap<String, VariantHashMapObject> variants = new HashMap<String, VariantHashMapObject>(); variants.put(variantKey, new VariantHashMapObject( currentFeature)); mapping.put(mappingKey, variants); } else { HashMap<String, VariantHashMapObject> variants = mapping .get(mappingKey); if (mapping.get(mappingKey).containsKey(variantKey)) { // check if the variant already exists variants.put(variantKey, variants.get(variantKey) .incrementOccurrences()); } else { // variant is new variants.put(variantKey, new VariantHashMapObject( currentFeature)); } mapping.put(mappingKey, variants); } } } } // iterate over the hash maps and set the defaults (the most frequently occuring) in the model for (String mappingKey : mapping.keySet()) { Map<String, VariantHashMapObject> variants = mapping .get(mappingKey); String variant = ""; EObject defaultVariant = null; int maxOccurrences = 0; for (Map.Entry entry : variants.entrySet()) { int currentVariantOccurrences = ((VariantHashMapObject) entry .getValue()).getOccurrences(); if (currentVariantOccurrences >= maxOccurrences) { if (currentVariantOccurrences == maxOccurrences) { System.out .println("More than one default variant candidates found (" + currentVariantOccurrences + ")"); } defaultVariant = EcoreUtil .copy(((VariantHashMapObject) entry.getValue()) .getVariant()); variant = (String) entry.getKey(); maxOccurrences = currentVariantOccurrences; } } //set the defaults in the model try { String currentMappingType = variant.split(JavaImplVariantManagerConstants.DELIMITER)[1]; EObject defaultForCurrentMapping = EcoreUtil .create(getDefaultEClass(JavaImplVariantManagerConstants.DEFAULT + JavaImplVariantManagerConstants.DELIMITER + currentMappingType)); defaultForCurrentMapping .eSet( defaultForCurrentMapping .eClass() .getEStructuralFeature( JavaImplVariantManagerConstants.ECLASS), mappingKey .substring( 0, mappingKey .indexOf(JavaImplVariantManagerConstants.DELIMITER))); defaultForCurrentMapping .eSet( defaultForCurrentMapping .eClass() .getEStructuralFeature( JavaImplVariantManagerConstants.ESTRUCTURALFEATURE), mappingKey .substring( mappingKey .indexOf(JavaImplVariantManagerConstants.DELIMITER) + 1, mappingKey.length())); ((EList) implModelEObject.eGet(getDefaultsReference())) .add(defaultForCurrentMapping); ((EList) defaultForCurrentMapping .eGet(getVariantReferenceForDefault(defaultForCurrentMapping .eClass()))).add(defaultVariant); } catch (FSMLMappingException e) { e.printStackTrace(); } } //let's sort all the variants for easier readability if (features.size()==0) { return; } EObject[] result = features.toArray(new EObject[1]); { class NodeComparator implements Comparator { public int compare(Object o1, Object o2) { EObject e1 = (EObject) o1; EObject e2 = (EObject) o2; return (FSMLEcoreUtil.getFSMLId(e1,null).compareTo(FSMLEcoreUtil.getFSMLId(e2,null))); } } NodeComparator comparator = new NodeComparator(); Arrays.sort(result,comparator); } implModelEObject= EcoreUtil.create(implModelEClass); for (EObject sortedFeature: result){ if (sortedFeature.eClass().getName().startsWith(JavaImplVariantManagerConstants.DEFAULT)) { ((EList) implModelEObject.eGet(getDefaultsReference())) .add(sortedFeature); } else { ((EList) implModelEObject.eGet(getVariantsReference())) .add(sortedFeature); } } } public String getVariantName(EObject variant) { return variant.eClass().getName().replaceFirst( JavaImplVariantManagerConstants.VARIANT + JavaImplVariantManagerConstants.DELIMITER + ".+" + JavaImplVariantManagerConstants.DELIMITER, ""); } public EObject getDefault(String eClass, String eStructuralFeature, String annotationSource) { // reload the model in case the user override the default //TODO: need to be smarter and only refresh the model from the disk once in each forward IFile implModelFile = ResourcesPlugin.getWorkspace().getRoot().getFile( project.getPath().append(modelName)); URI implModelURI = URI.createPlatformResourceURI(implModelFile .getFullPath().toString(), true); if (implModelFile.exists()) { // it should exist resourceSet = new ResourceSetImpl(); existingResourceModel = resourceSet.getResource(implModelURI, true) .getContents(); implModelEObject = existingResourceModel.get(0); } // TODO: else - the implModel is gone ...need to use the default from the metamodel EList<Default> defaultMappingTypes = ((EList) implModelEObject .eGet(getDefaultsReference())); for (Default defaultMappingType : defaultMappingTypes) { if (defaultMappingType.eClass().getName().equals( JavaImplVariantManagerConstants.DEFAULT + JavaImplVariantManagerConstants.DELIMITER + annotationSource)) { // check if the mapping is the same (eClass and // eStructuralFeature) if (defaultMappingType.eGet( defaultMappingType.eClass().getEStructuralFeature( JavaImplVariantManagerConstants.ECLASS)) .equals(eClass) && defaultMappingType .eGet( defaultMappingType .eClass() .getEStructuralFeature( JavaImplVariantManagerConstants.ESTRUCTURALFEATURE)) .equals(eStructuralFeature)) { EList<Variant> defaultVariants = ((EList) defaultMappingType .eGet(getVariantReferenceForDefault(defaultMappingType .eClass()))); return defaultVariants.get(0); } } // if } // for return null; } public EReference getVariantsReference() { EList<EStructuralFeature> features = implModelEClass .getEAllStructuralFeatures(); for (EStructuralFeature feature : features) { if (feature.getName().equals( JavaImplVariantManagerConstants.VARIANTS)) return (EReference) feature; } return null; } public static EReference getVariantReferenceForDefault(EClass defaultEClass) { EList<EStructuralFeature> features = defaultEClass .getEAllStructuralFeatures(); for (EStructuralFeature feature : features) { if (feature.getName().equals( JavaImplVariantManagerConstants.VARIANT)) return (EReference) feature; } return null; } public EReference getDefaultsReference() { EList<EStructuralFeature> features = implModelEClass .getEAllStructuralFeatures(); for (EStructuralFeature feature : features) { if (feature.getName().equals( JavaImplVariantManagerConstants.DEFAULTS)) return (EReference) feature; } return null; } public EClass getVariantEClass(String name) throws FSMLMappingException { Collection<EClass> concreteClasses = FSMLEcoreUtil .getSubclassesOfEClass(getVariantsReference() .getEReferenceType(), true); EClass targetClass = null; for (EClass currentClass : concreteClasses) { if (currentClass.getName().equals(name)) { targetClass = currentClass; break; } } if (targetClass == null) { throw new FSMLMappingException(Cause.MISSING_CONCRETE_CHILD_TYPE, "in impl Model"); } return targetClass; } public EClass getDefaultEClass(String name) throws FSMLMappingException { Collection<EClass> concreteClasses = FSMLEcoreUtil .getSubclassesOfEClass(getDefaultsReference() .getEReferenceType(), true); EClass targetClass = null; for (EClass currentClass : concreteClasses) { if (currentClass.getName().equals(name)) { targetClass = currentClass; break; } } if (targetClass == null) { throw new FSMLMappingException(Cause.MISSING_CONCRETE_CHILD_TYPE, "in impl Model"); } return targetClass; } }