/******************************************************************************* * 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.contentassist; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Vector; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.URI; 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.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.jdt.ui.text.java.ContentAssistInvocationContext; import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal; import org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer; import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext; import org.eclipse.swt.graphics.Image; import org.eclipse.ui.IEditorDescriptor; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.PlatformUI; import ca.uwaterloo.gsd.fsml.codeAssist.FSMLCodeAssistUtil; import ca.uwaterloo.gsd.fsml.core.MarkerDescriptor; 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.javaMappingInterpreter.JavaMappingInterpreter; import ca.uwaterloo.gsd.fsml.javaMappingInterpreter.JavaMarkers; import ca.uwaterloo.gsd.fsml.javaMappingInterpreter.analysis.JavaModelUtils; import ca.uwaterloo.gsd.fsml.xmlMappingInterpreter.XMLMappingInterpreter; public class FSMLJavaContentCompleteProcessor implements IJavaCompletionProposalComputer { public static final Image IMG_TOOL_FORWARD = PlatformUI.getWorkbench().getSharedImages() .getImage(ISharedImages.IMG_TOOL_FORWARD); //Eclipse callback method @Override public List<IJavaCompletionProposal> computeCompletionProposals( ContentAssistInvocationContext context, IProgressMonitor monitor) { JavaContentAssistInvocationContext jc = (JavaContentAssistInvocationContext) context; char[] token = jc.getCoreContext().getToken(); String keywordToken; if (token==null){ keywordToken=""; }else { keywordToken= new String(token); } String currentFramework = null; //check whether we should enable keyword programming if (keywordToken!=null && keywordToken.length()!=0) for (int i=0;i<FSMLCodeAssistUtil.supportedFrameworks.length;i++){ if (keywordToken.toLowerCase().startsWith(FSMLCodeAssistUtil.supportedFrameworks[i])){ currentFramework = FSMLCodeAssistUtil.supportedFrameworks[i]; break; } } // assume at least a FSML model exist // get the metamodel from the FSML model, start querying it // TODO: if no FSML model exists, do reverse? //find the model file String[] frameworksToCheck; if (currentFramework==null){ frameworksToCheck = FSMLCodeAssistUtil.supportedFrameworks; }else { //search for the framework-specific models for all possible frameworks if the //keyword doesn't tell us which framework are we currently using frameworksToCheck = new String[]{currentFramework}; } List<IJavaCompletionProposal> proposals= new Vector<IJavaCompletionProposal>(); List<IFile> fsmlModelFiles = FSMLCodeAssistUtil.findModelFile(jc .getCompilationUnit() .getJavaProject().getProject(),frameworksToCheck); int score = 9999; for (IFile fsmlModelFile : fsmlModelFiles) { int counter = 0; if (fsmlModelFile ==null||!fsmlModelFile.exists()){ System.err.println ("no valid project found"); return Collections.EMPTY_LIST; } EList<EObject> fsmlModel = null; ResourceSet resourceSet = new ResourceSetImpl(); URI fsmlModelURI = URI.createPlatformResourceURI( fsmlModelFile.getFullPath().toString(), true); Resource resource = resourceSet.getResource( fsmlModelURI, true); fsmlModel = resource.getContents(); Model assertedModel = ((ModelContainer) fsmlModel.get(0)) .getAssertedModel(); Queries.INSTANCE.reset(); Queries.INSTANCE.setProject(jc.getCompilationUnit().getJavaProject().getProject()); //register Java mapping interpreter JavaMappingInterpreter javaMappingInterpreter = new JavaMappingInterpreter(); Queries.INSTANCE.registerCustomInterpreter(javaMappingInterpreter); //register XML mapping interpreter XMLMappingInterpreter xmlMappingInterpreter = new XMLMappingInterpreter(); Queries.INSTANCE.registerCustomInterpreter(xmlMappingInterpreter); Queries.INSTANCE.initialize(jc .getCompilationUnit() .getJavaProject().getProject(),assertedModel); //main method call proposals.addAll(getProposals(assertedModel, jc,resource,javaMappingInterpreter,score+counter*(score/10))); } return proposals; } @Override public List<IJavaCompletionProposal> computeContextInformation( ContentAssistInvocationContext context, IProgressMonitor monitor) { //TODO: compute context information System.out.println ("compute context information from FSMLJavaContentCompleteProcessor"); return Collections.EMPTY_LIST; } @Override public String getErrorMessage() { return "getErrorMessage() from FSMLJavaContentCompleteProcessor"; } @Override public void sessionEnded() { //System.out.println ("Session ended from FSMLJavaContentCompleteProcessor"); } @Override public void sessionStarted() { //System.out.println ("Session started from FSMLJavaContentCompleteProcessor"); } protected Vector<IJavaCompletionProposal> getProposals(Model model, JavaContentAssistInvocationContext jc, Resource resource, JavaMappingInterpreter javaMappingInterpreter,int score) { // iterate through all the supported mappings - deprecated mechanism Vector<IJavaCompletionProposal> proposals = new Vector<IJavaCompletionProposal>(); ASTParser parser = ASTParser.newParser(AST.JLS3); parser.setSource(jc.getCompilationUnit()); parser.setStatementsRecovery(true); parser.setResolveBindings(true); //compute actual code transformation proposals ASTNode coveringNode = FSMLCodeAssistUtil.getCoveringNode(jc.getCompilationUnit(), jc.getInvocationOffset(), 0); IJavaElement javaElement = ((CompilationUnit)coveringNode.getRoot()).getJavaElement(); javaMappingInterpreter.getAnalysisManagers().getJavaASTManager().setCompilationUnit( JavaModelUtils.getTypeRoot(javaElement),(CompilationUnit)coveringNode.getRoot()); HashMap<String, EObject> fsmlId2ModelEObject = new HashMap<String,EObject>(); FSMLEcoreUtil.createFsmlId2EObjectMap(model, fsmlId2ModelEObject); //map current context to markers Vector<IMarker> markers = JavaMarkers.getMarkersCoveredByNode(coveringNode); //set the context FSMLEcoreUtil.parameterValuesFromRecommenderSystem.clear(); FSMLEcoreUtil.parameterValuesFromRecommenderSystem.put(JavaMappingInterpreter.DETAIL_POSITION, ""+jc.getInvocationOffset()); for (IMarker marker : markers) { try { EObject eObject = fsmlId2ModelEObject.get(marker.getAttribute(MarkerDescriptor.ATTRIBUTE_ID)); if (eObject!=null){ ASTNode astNode = coveringNode; Queries.INSTANCE.removeContext(eObject); while (astNode!=null){ Queries.INSTANCE.associateContext(eObject, astNode); if (astNode instanceof MethodDeclaration){ MethodDeclaration methodDeclaration = (MethodDeclaration)astNode; FSMLEcoreUtil.parameterValuesFromRecommenderSystem.put(JavaMappingInterpreter.DETAIL_LOCATION_NAME, methodDeclaration.getName().getIdentifier()); IMethod iMethod = (IMethod) methodDeclaration.resolveBinding().getJavaElement(); String key = iMethod.getKey(); String auxSignature = key.substring(key.indexOf('(')); auxSignature = auxSignature.replace('/', '.'); FSMLEcoreUtil.parameterValuesFromRecommenderSystem.put(JavaMappingInterpreter.DETAIL_LOCATION_SIG, auxSignature); } astNode = astNode.getParent(); } } } catch (CoreException e) { e.printStackTrace(); } } if (markers.size()>0) { for (IMarker marker : markers) { try { EObject eObject = fsmlId2ModelEObject.get(marker.getAttribute(MarkerDescriptor.ATTRIBUTE_ID)); //eObject corresponding to the marker if (eObject!=null) { proposals.addAll(createProposals(model, jc, resource, coveringNode, score, eObject)); } } catch (CoreException e) { e.printStackTrace(); } score--; } } return proposals; } private Vector<IJavaCompletionProposal> createProposals(Model model, JavaContentAssistInvocationContext jc, Resource resource, ASTNode coveringNode,int score, EObject eObject) { //content assist Vector<IJavaCompletionProposal> proposals = new Vector<IJavaCompletionProposal>(); EList<EStructuralFeature> allStructuralFeatures = eObject.eClass().getEAllStructuralFeatures(); Image proposalImage=null; if (resource.getURI().path()!=null){ IEditorDescriptor modelEditor = PlatformUI.getWorkbench().getEditorRegistry().getDefaultEditor( resource.getURI().path()); if (modelEditor!=null){ proposalImage= modelEditor.getImageDescriptor().createImage(); } } if (proposalImage ==null){ proposalImage = FSMLJavaContentCompleteProcessor.IMG_TOOL_FORWARD; } for (EStructuralFeature structuralFeature:allStructuralFeatures){ String type=""; if (structuralFeature instanceof EAttribute){ type = "EAttribute"; if (!FSMLEcoreUtil.isFeaturePresent(eObject, structuralFeature)){ String projectName = resource.getURI().fileExtension(); projectName = projectName.substring(0,1).toUpperCase()+projectName.substring(1); String proposalName = structuralFeature.getName(); if (proposalName.length()>0){ proposalName = proposalName.toUpperCase().charAt(0)+proposalName.substring(1); //if we have more than one concrete class, we should use the concreteClass name instead //going with the reference name for now to be consistent with the metamodel in DSPD //proposalName = concreteClass.getName(); } ASTRewrite rewrite = ASTRewrite.create(coveringNode.getAST()); FSMLJavaProposal linkedCorrectionProposal = new FSMLJavaProposal( "FSML: " + proposalName+"("+eObject.eClass().getName()+", "+projectName+")", jc.getCompilationUnit(), rewrite, score, proposalImage,eObject,null,structuralFeature,coveringNode,jc.getInvocationOffset(),resource,jc.getCoreContext()); proposals.add(linkedCorrectionProposal); } continue; } else if (structuralFeature instanceof EReference){ type = "EReference"; Collection<EClass> concreteClasses = FSMLEcoreUtil .getSubclassesOfEClass(((EReference)structuralFeature).getEReferenceType(), true); for (EClass concreteClass : concreteClasses) { //calculate the number of instances so cardinality check can be done later on EList<EObject> contents = eObject.eContents(); int numInstances = 0; for (EObject object : contents) { if (object.eClass().getName().equals(concreteClass.getName())){ numInstances++; } } if (structuralFeature.getUpperBound()!=-1 && numInstances+1>structuralFeature.getUpperBound()){ //skip this proposal if cardinaltiy constraint is not met //TODO: problem with feature group continue; } EAnnotation featureGroup= structuralFeature.getEAnnotation(FSMLEcoreUtil.FEATURE_GROUP); if (featureGroup!=null){ //feature group if (false){ continue; } } ASTRewrite rewrite = ASTRewrite.create(coveringNode.getAST()); String projectName = resource.getURI().fileExtension(); projectName = projectName.substring(0,1).toUpperCase()+projectName.substring(1); String proposalName = structuralFeature.getName(); if (proposalName.length()>0){ proposalName = proposalName.toUpperCase().charAt(0)+proposalName.substring(1); //if we have more than one concrete class, we should use the concreteClass name instead //going with the reference name for now to be consistent with the metamodel in DSPD //proposalName = concreteClass.getName(); } FSMLJavaProposal linkedCorrectionProposal = new FSMLJavaProposal( "FSML: " + proposalName+"("+eObject.eClass().getName()+", "+projectName+")", jc.getCompilationUnit(), rewrite, score, proposalImage,eObject,concreteClass,structuralFeature,coveringNode,jc.getInvocationOffset(),resource,jc.getCoreContext()); /* * KEYWORD * PROGRAMMING * !!! */ char[] token = jc.getCoreContext().getToken(); String keywordToken; if (token==null){ keywordToken=""; }else { keywordToken= new String(token); } if (keywordToken!=null && keywordToken.length()!=0){ linkedCorrectionProposal.setDeleteKeyword(true); } if (keywordToken!=null && keywordToken.length()!=0 && keywordToken.matches(".*[A-Z].*")){ String conceptName = concreteClass.getName(); //split camel case concept name StringBuffer buffer = new StringBuffer(); Collection<String> conceptNameTokens = new Vector<String>(); for (int i=0;i<conceptName.length()-1;i++){ char currentChar = conceptName.charAt(i); if (Character.isUpperCase(currentChar) && buffer.toString().length()!=0){ conceptNameTokens.add(buffer.toString()); buffer = new StringBuffer(); } buffer.append(currentChar); } buffer.append(conceptName.charAt(conceptName.length()-1)); conceptNameTokens.add(buffer.toString()); boolean conceptNameContainsKeyword = false; for (Iterator iterator = conceptNameTokens.iterator(); iterator .hasNext();) { String currentToken = (String) iterator.next(); if (keywordToken.toUpperCase().contains(currentToken.toUpperCase())){ conceptNameContainsKeyword=true; } } //filter the proposals so that only proposals for concept names containing more than one word in the keyword appears //TODO: better ranking algorithm if (conceptNameContainsKeyword){ proposals.add(linkedCorrectionProposal); //score++; } } //end keyword programming else { proposals.add(linkedCorrectionProposal); //score++; } } } } // if (proposals.size()==1){ // FSMLProposal onlyProposal = (FSMLProposal)proposals.get(0); // try { // onlyProposal.performChange(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor(), jc.getDocument()); // } catch (CoreException e) { // e.printStackTrace(); // } // proposals.clear(); // } return proposals; } }