/* * Copyright 2003-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.mps.idea.java.convert; import com.intellij.facet.FacetManager; import com.intellij.facet.FacetTypeRegistry; import com.intellij.ide.projectView.ProjectView; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiField; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiJavaFile; import com.intellij.psi.PsiManager; import com.intellij.psi.PsiMethod; import jetbrains.mps.extapi.model.SModelBase; import jetbrains.mps.ide.java.newparser.JavaParseException; import jetbrains.mps.ide.java.newparser.JavaToMpsConverter; import jetbrains.mps.ide.messages.MessagesViewTool; import jetbrains.mps.ide.platform.watching.ReloadManager; import jetbrains.mps.ide.project.ProjectHelper; import jetbrains.mps.ide.vfs.IdeaFileSystem; import jetbrains.mps.idea.core.facet.MPSFacet; import jetbrains.mps.idea.core.facet.MPSFacetType; import jetbrains.mps.idea.core.psi.impl.MPSPsiNode; import jetbrains.mps.idea.core.refactoring.NodePtr; import jetbrains.mps.idea.java.psiStubs.JavaForeignIdBuilder; import jetbrains.mps.progress.ProgressMonitorAdapter; import jetbrains.mps.project.MPSProject; import jetbrains.mps.smodel.DynamicReference; import jetbrains.mps.smodel.StaticReference; import jetbrains.mps.util.SNodeOperations; import jetbrains.mps.vfs.FileSystem; import jetbrains.mps.vfs.IFile; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.mps.openapi.model.SModel; import org.jetbrains.mps.openapi.model.SModelReference; import org.jetbrains.mps.openapi.model.SNode; import org.jetbrains.mps.openapi.model.SReference; import org.jetbrains.mps.openapi.module.FindUsagesFacade; import org.jetbrains.mps.openapi.module.SModule; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; /** * danilla 6/5/13 */ public class ConvertPackageToModel extends AnAction { private static Logger LOG = Logger.getLogger("Convert java to mps"); public ConvertPackageToModel() { super("Convert Java to MPS", "", null); } @Override public void update(AnActionEvent e) { PsiElement[] elements = e.getData(LangDataKeys.PSI_ELEMENT_ARRAY); Module module = e.getData(LangDataKeys.MODULE); if (elements == null || module == null || FacetManager.getInstance(module).getFacetByType(MPSFacetType.ID) == null || !containsJavaThings(elements)) { e.getPresentation().setVisible(false); e.getPresentation().setEnabled(false); } } @Override public void actionPerformed(final AnActionEvent e) { final PsiElement[] elements = e.getData(LangDataKeys.PSI_ELEMENT_ARRAY); final Module module = e.getData(LangDataKeys.MODULE); final Project project = e.getData(PlatformDataKeys.PROJECT); MPSFacet facet = FacetManager.getInstance(module).getFacetByType(MPSFacetType.ID); SModule mpsModule = facet.getSolution(); final MPSProject mpsProject = e.getProject().getComponent(MPSProject.class); List<PsiJavaFile> psiJavaFiles = JavaConverterHelper.getFilesFromSelection(JavaConverterHelper.liftToFiles(Arrays.asList(elements))); Collection<Module> modulesWithoutFacet = JavaConverterHelper.getModulesThatNeedMPSFacet(psiJavaFiles); if (!modulesWithoutFacet.isEmpty()) { final AddFacetToModulesDialog dialog = new AddFacetToModulesDialog(project, module.getName(), modulesWithoutFacet); dialog.show(); if (dialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { for(Module moduleToAddFacet : dialog.getResult()) { FacetManager.getInstance(moduleToAddFacet).addFacet( FacetTypeRegistry.getInstance().findFacetType(MPSFacetType.ID), "", null); } } }); } } final JavaToMpsConverter parser = new JavaToMpsConverter(mpsModule, mpsProject.getRepository(), true, true, project.getComponent(MessagesViewTool.class).newHandler()); final List<IFile> javaFiles = toIFiles(psiJavaFiles); ApplicationManager.getApplication().saveAll(); ProgressManager.getInstance().run(new Task.Modal(null, "Convert to MPS", false) { @Override public void run(@NotNull final ProgressIndicator progressIndicator) { try { parser.convertToMps(javaFiles, new ProgressMonitorAdapter(progressIndicator)); } catch (JavaParseException exc) { throw new RuntimeException(exc); } catch (IOException exc) { throw new RuntimeException(exc); } } }); // it was successful, so let's hack references that pointed to psi stubs and delete java files just converted mpsProject.getRepository().getModelAccess().executeCommand(new Runnable() { // ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { Set<PsiClass> psiClasses = getPsiClasses(parser.getSuccessfulFiles(), PsiManager.getInstance(e.getProject())); Set<SNode> stubNodes = getStubNodes(psiClasses); Set<SNode> roots = parser.getRootsBuilt(); List<SReference> referencesToFix = new ArrayList<SReference>(); boolean wasUnresolved = false; Set<SReference> references = FindUsagesFacade.getInstance().findUsages(mpsProject.getScope(), stubNodes, null); for (SReference ref : references) { if (roots.contains(ref.getSourceNode().getContainingRoot())) { continue; } if (!(ref instanceof StaticReference)) { referencesToFix.add(ref); continue; } SNode source = ref.getSourceNode(); String role = ref.getRole(); SModelReference targetModelRef = ref.getTargetSModelReference(); SNode targetNode = ref.getTargetNode(); // TODO need to make it more efficient (maintain this data in DirParser) SModelReference newModelRef = null; String modelName = targetModelRef.getModelName(); modelName = modelName.substring(0, modelName.indexOf('@')); for (SModel model : parser.getModels()) { if (modelName.equals(model.getModelName())) { newModelRef = model.getReference(); } } if (newModelRef == null) { wasUnresolved = true; continue; } String resolveInfo = SNodeOperations.getResolveInfo(targetNode); SReference tempDynamicRef = new DynamicReference(role, source, newModelRef, resolveInfo); referencesToFix.add(tempDynamicRef); source.setReference(role, tempDynamicRef); SModel sourceModel = source.getModel(); ((SModelBase) sourceModel).deleteModelImport(targetModelRef); if (!newModelRef.equals(sourceModel.getReference())) { // avoiding self-import ((SModelBase) sourceModel).addModelImport(newModelRef, false); } // better create static references right away // changing reference to a static reference pointing to mps node // SNode newTarget = stubToMpsNodes.get(stub); // SReference newRef = StaticReference.create(role, source, newTarget.getModel().getReference(), newTarget.getNodeId()); // source.setReference(role, newRef); } for (SReference ref : referencesToFix) { SNode target = ref.getTargetNode(); if (target == null) continue; SNode source = ref.getSourceNode(); String role = ref.getRole(); SReference finalStaticRef = StaticReference.create(role, source, target, ((DynamicReference) ref).getResolveInfo()); source.setReference(role, finalStaticRef); } // here more complicated logic can be written // e.g. not delete, but rather unmark as source dir -- in case if // the resulting model(s) don't fall into the same directory where java was for (IFile file : parser.getSuccessfulFiles()) { file.delete(); } if (wasUnresolved) { LOG.error("could not resolve some references"); } // we want psi stub models to be up-to-date with regard to those deletions ReloadManager.getInstance().flush(); } }); // if the package wasn't a model before then we want our MPSTreeStructureProvider to do its work with // the directory node for the package ProjectView projectView = ProjectView.getInstance(project); projectView.refresh(); } private boolean containsJavaThings(PsiElement[] elements) { for (PsiElement e : elements) { if (e instanceof PsiJavaFile) return true; if (e instanceof PsiClass && !(e instanceof MPSPsiNode)) return true; } for (PsiElement e : elements) { if (!(e instanceof PsiDirectory)) continue; if (containsJavaFiles((PsiDirectory) e)) return true; } return false; } private boolean containsJavaFiles(PsiDirectory dir) { for (PsiFile f : dir.getFiles()) { if (f instanceof PsiJavaFile) return true; } for (PsiDirectory d : dir.getSubdirectories()) { if (containsJavaFiles(d)) return true; } return false; } private List<IFile> toIFiles(List<? extends PsiFile> psiFiles) { List<IFile> result = new ArrayList<IFile>(psiFiles.size()); for (PsiFile file : psiFiles) { VirtualFile vfile = file.getVirtualFile(); IFile ifile = new IdeaFileSystem().getFile(vfile.getPath()); result.add(ifile); } return result; } private Set<PsiClass> getPsiClasses(List<IFile> javaFiles, PsiManager psiManager) { Set<PsiClass> result = new HashSet<PsiClass>(); for (IFile javaIFile : javaFiles) { VirtualFile vfile = VirtualFileManager.getInstance().findFileByUrl("file://" + javaIFile.getPath()); PsiFile psiFile = psiManager.findFile(vfile); assert psiFile instanceof PsiJavaFile; result.addAll(Arrays.asList(((PsiJavaFile) psiFile).getClasses())); } return result; } private Set<SNode> getStubNodes(Set<PsiClass> psiClasses) { Set<SNode> result = new HashSet<SNode>(); for (PsiClass cl : psiClasses) { putStubNodes(cl, result); } return result; } private void putStubNodes(PsiClass clas, Set<SNode> result) { putOneStubNode(clas, result); for (PsiClass cl : clas.getInnerClasses()) { putStubNodes(cl, result); } for (PsiMethod m : clas.getMethods()) { putOneStubNode(m, result); } for (PsiField f : clas.getFields()) { putOneStubNode(f, result); } } private void putOneStubNode(PsiElement element, Set<SNode> result) { NodePtr nodePtr = JavaForeignIdBuilder.computeNodePtr(element); if (nodePtr == null) return; // TODO change to project SRepository SNode node = nodePtr.toSNodeReference().resolve(ProjectHelper.getProjectRepository(element.getProject())); if (node == null) return; result.add(node); } }