/*
* Copyright 2003-2011 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.plugin;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.compiler.CompileStatusNotification;
import com.intellij.openapi.compiler.CompilerManager;
import com.intellij.openapi.components.ProjectComponent;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
* @author Kostik
*/
public class ProjectHandler extends UnicastRemoteObject implements ProjectComponent, IProjectHandler {
private Project myProject;
private List<IMPSIDEHandler> myIDEHandlers = new ArrayList<IMPSIDEHandler>();
public ProjectHandler(Project project) throws RemoteException {
super();
myProject = project;
}
public void projectOpened() {
}
public void projectClosed() {
}
@NotNull
public String getComponentName() {
return "MPSSupport Handler";
}
public void initComponent() {
}
public void disposeComponent() {
}
private void refreshFSInternal() {
VirtualFile[] contentRoots = ProjectRootManager.getInstance(myProject).getContentRoots();
for (VirtualFile contentRoot : contentRoots) {
contentRoot.refresh(false, true);
}
}
public void refreshFS() {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
refreshFSInternal();
}
});
}
});
} catch (InterruptedException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public IdeaCompilationResult buildModules(final String[] paths) {
final CountDownLatch latch = new CountDownLatch(1);
final IdeaCompilationResult[] result = new IdeaCompilationResult[1];
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
refreshFSInternal();
List<Module> modules = new ArrayList<Module>();
for (String path : paths) {
Module module = findModule(path);
if (module != null) {
modules.add(module);
}
}
if (modules.isEmpty()) {
latch.countDown();
return;
}
CompilerManager compilerManager = CompilerManager.getInstance(myProject);
compilerManager.make(myProject, modules.toArray(new Module[modules.size()]), new CompileStatusNotification() {
public void finished(boolean aborted, int errors, int warnings, CompileContext compileContext) {
compilationFinished(aborted, errors, warnings);
}
private void compilationFinished(boolean aborted, int errorsNumber, int warningsNumber) {
result[0] = new IdeaCompilationResult(errorsNumber, warningsNumber, aborted);
latch.countDown();
}
});
}
});
}
});
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
return result[0];
}
public List<String> findInheritors(final String fqName) {
final List<String> list = new ArrayList<String>();
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
final JavaPsiFacade javaPsi = JavaPsiFacade.getInstance(myProject);
PsiClass cls = javaPsi.findClass(fqName, GlobalSearchScope.allScope(myProject));
if (cls == null) return;
PsiClass[] result = ClassInheritorsSearch.search(cls, GlobalSearchScope.allScope(myProject), true).toArray(new PsiClass[0]);
for (PsiClass aResult : result) {
if (aResult.getQualifiedName() != null) { //i.e anonymous class
list.add(aResult.getQualifiedName());
}
}
}
});
return list;
}
public void openClass(final String fqName) {
executeWriteAction(new Runnable() {
public void run() {
final JavaPsiFacade javaPsi = JavaPsiFacade.getInstance(myProject);
PsiClass cls = javaPsi.findClass(fqName, GlobalSearchScope.allScope(myProject));
if (cls == null) return;
cls.navigate(true);
activateProjectWindow();
}
});
}
public void openMethod(final String className, final String name, final int parameterCount) {
if (findClass(className) == null) return;
executeWriteAction(new Runnable() {
public void run() {
PsiClass cls = findClass(className);
PsiMethod[] methods = cls.getAllMethods();
for (PsiMethod method : methods) {
if (method.getName().equals(name)) {
if (parameterCount != -1 && method.getParameterList().getParameters().length != parameterCount) {
continue;
}
method.navigate(true);
activateProjectWindow();
return;
}
}
}
});
}
public void openField(final String className, final String name) {
if (findClass(className) == null) return;
executeWriteAction(new Runnable() {
public void run() {
PsiClass cls = findClass(className);
PsiField[] fields = cls.getAllFields();
for (PsiField field : fields) {
if (name.equals(field.getName())) {
field.navigate(true);
activateProjectWindow();
return;
}
}
}
});
}
public void openConstructor(final String className, final int parameterCount) throws RemoteException {
if (findClass(className) == null) return;
executeWriteAction(new Runnable() {
public void run() {
PsiClass cls = findClass(className);
PsiMethod[] methods = cls.getConstructors();
for (PsiMethod method : methods) {
if (parameterCount != -1 && method.getParameterList().getParameters().length != parameterCount) {
continue;
}
method.navigate(true);
activateProjectWindow();
return;
}
}
});
}
private void activateProjectWindow() {
if (SystemInfo.isLinux) return;
Frame window = (Frame) WindowManager.getInstance().suggestParentWindow(myProject);
if (window == null) return;
window.toFront();
window.setExtendedState(JFrame.ICONIFIED);
window.setExtendedState(JFrame.MAXIMIZED_BOTH);
}
public void addIdeHandler(IMPSIDEHandler handler) throws RemoteException {
myIDEHandlers.add(handler);
}
public void removeIdeHandler(IMPSIDEHandler handler) throws RemoteException {
myIDEHandlers.remove(handler);
//we need it because of RMI's distributed gc
System.gc();
}
private PsiClass findClass(final String className) {
final PsiClass[] cls = new PsiClass[1];
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
final JavaPsiFacade javaPsi = JavaPsiFacade.getInstance(myProject);
cls[0] = javaPsi.findClass(className, GlobalSearchScope.allScope(myProject));
}
});
return cls[0];
}
private void executeWriteAction(final Runnable runnable) {
ApplicationManager.getApplication().invokeAndWait(new Runnable() {
public void run() {
CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
public void run() {
ApplicationManager.getApplication().runWriteAction(runnable);
}
}, "command", "MPSPlugin");
}
}, ModalityState.NON_MODAL);
}
public Module findModule(final String path) {
VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(path));
if (file == null) return null;
int bestDistance = Integer.MAX_VALUE;
Module bestModule = null;
Module[] modules = ApplicationManager.getApplication().runReadAction(new Computable<Module[]>() {
public Module[] compute() {
return ModuleManager.getInstance(myProject).getModules();
}
});
for (Module module : modules) {
ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
for (VirtualFile contentRoot : rootManager.getContentRoots()) {
int distance = getDistance(contentRoot, file);
if (distance != -1 && distance < bestDistance) {
bestDistance = distance;
bestModule = module;
}
}
}
return bestModule;
}
void showAspectMethodUsages(String namepace, String name) {
for (IMPSIDEHandler h : myIDEHandlers) {
try {
h.showAspectMethodUsages(namepace, name);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
void showClassUsages(String fqName) {
for (IMPSIDEHandler h : myIDEHandlers) {
try {
h.showClassUsages(fqName);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
void showNode(String namespace, String id) {
for (IMPSIDEHandler h : myIDEHandlers) {
try {
h.showNode(namespace, id);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
void showMethodUsages(String classFqName, String methodName, int parameterCount) {
for (IMPSIDEHandler h : myIDEHandlers) {
try {
h.showMethodUsages(classFqName, methodName, parameterCount);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
public static int getDistance(VirtualFile ancestor, VirtualFile descendant) {
if (ancestor.equals(descendant)) return 0;
if (descendant.getParent() == null) return -1;
int distance = getDistance(ancestor, descendant.getParent());
if (distance == -1) return -1;
return distance + 1;
}
}