/*
* Lilith - a log event viewer.
* Copyright (C) 2007-2011 Joern Huxhorn
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Copyright 2007-2011 Joern Huxhorn
*
* 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 de.huxhorn.lilith.idea;
import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.search.GlobalSearchScope;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;
import java.util.Set;
import javax.swing.SwingUtilities;
import org.jetbrains.annotations.NotNull;
/**
*
* http://www.jetbrains.net/confluence/display/IDEADEV/Diana+Plugin+Migration+Guide
*
*/
public class LilithPlugin
implements ApplicationComponent
{
private static final int DEFAULT_PORT = 11111;
private Method getInstanceMethod;
private Method findClassMethod;
private ServerSocket serverSocket;
private Set<Thread> receiverThreads;
@NotNull
public String getComponentName()
{
return "Lilith";
}
public void initComponent()
{
System.out.println("Starting initComponent...");
Class<?> clazz=null;
try
{
clazz = Class.forName("com.intellij.psi.JavaPsiFacade");
}
catch (ClassNotFoundException e)
{
try
{
clazz = Class.forName("com.intellij.psi.PsiManager");
}
catch (ClassNotFoundException e1)
{
e1.printStackTrace();
}
}
Method instanceMethod = null;
Method classMethod = null;
if(clazz!=null)
{
try
{
instanceMethod = clazz.getMethod("getInstance", Project.class);
classMethod = clazz.getMethod("findClass", String.class, GlobalSearchScope.class);
}
catch (NoSuchMethodException e)
{
e.printStackTrace();
}
}
getInstanceMethod = instanceMethod;
findClassMethod = classMethod;
receiverThreads=new HashSet<Thread>();
try
{
serverSocket=new ServerSocket(DEFAULT_PORT, 50, InetAddress.getByName("127.0.0.1"));
ServerSocketRunnable r = new ServerSocketRunnable();
Thread t=new Thread(r, "Lilith-ServerSocket");
t.setDaemon(true);
t.start();
}
catch (IOException e)
{
e.printStackTrace();
}
System.out.println("Finished initComponent...");
}
public void disposeComponent()
{
System.out.println("Starting disposeComponent...");
closeServerSocket();
HashSet<Thread> threads = new HashSet<Thread>(receiverThreads);
for(Thread t:threads)
{
t.interrupt();
}
System.out.println("Finished disposeComponent...");
}
class ServerSocketRunnable
implements Runnable
{
public void run()
{
System.out.println("Started Lilith-ServerSocket-Runnable");
ServerSocket ss = serverSocket;
if(ss!=null)
{
for(;;)
{
try
{
Socket socket = ss.accept();
Thread t=new Thread(new SocketRunnable(socket), "Lilith-Socket-"+socket);
t.setDaemon(true);
t.start();
receiverThreads.add(t);
}
catch (IOException e)
{
e.printStackTrace();
break;
}
}
closeServerSocket();
}
System.out.println("Finished Lilith-ServerSocket-Runnable");
}
}
private void closeServerSocket()
{
if(serverSocket!=null)
{
try
{
serverSocket.close();
}
catch (IOException e)
{
// ignore
}
serverSocket=null;
}
}
private class SocketRunnable
implements Runnable
{
private Socket socket;
public SocketRunnable(Socket socket)
{
this.socket=socket;
}
public void run()
{
System.out.println("Started Lilith-Socket-Runnable");
ObjectInputStream ois;
try
{
ois=new ObjectInputStream(new BufferedInputStream(socket.getInputStream()));
}
catch (IOException e1)
{
e1.printStackTrace();
return;
}
for(;;)
{
try
{
Object obj=ois.readObject();
if(obj instanceof StackTraceElement)
{
showInEditor((StackTraceElement) obj);
}
Thread.sleep(1);
}
catch (IOException e)
{
e.printStackTrace();
break;
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
break;
}
catch (InterruptedException e)
{
break;
}
}
try
{
ois.close();
}
catch (IOException e)
{
//ignore
}
receiverThreads.remove(Thread.currentThread());
System.out.println("Finished Lilith-Socket-Runnable");
}
}
public void showInEditor(StackTraceElement ste)
{
if(ste==null)
{
return;
}
SwingUtilities.invokeLater(new EditorRunnable(ste));
}
public class EditorRunnable
implements Runnable
{
private StackTraceElement stackTraceElement;
public EditorRunnable(StackTraceElement stackTraceElement)
{
this.stackTraceElement=stackTraceElement;
}
public void run()
{
System.out.println(stackTraceElement);
ProjectManager projectManager = ProjectManager.getInstance();
Project[] openProjects = projectManager.getOpenProjects();
String className=stackTraceElement.getClassName();
String parentClassName=null;
int dollarIndex = className.indexOf("$");
if(dollarIndex>=0)
{
parentClassName=className.substring(0, dollarIndex);
className=className.replace('$','.');
}
String methodName=stackTraceElement.getMethodName();
int lineNumber=stackTraceElement.getLineNumber();
Project project = null;
PsiClass psiClass = null;
PsiClass psiSourceClass = null;
PsiClass psiParentClass = null;
PsiClass psiParentSourceClass = null;
for(Project current:openProjects)
{
GlobalSearchScope scope = GlobalSearchScope.projectScope(current);
PsiClass found = findClass(current, className, scope);
if(found!=null)
{
project=current;
psiClass=found;
if(psiClass.canNavigateToSource())
{
// don't search anympore if source class was found.
psiSourceClass=psiClass;
break;
}
}
// we only care about parent class if class was not found.
if(parentClassName != null && psiClass==null && psiParentClass==null)
{
// search for parent as fallback...
found = findClass(current, parentClassName, scope);
if(found!=null)
{
project=current;
psiParentClass=found;
if(psiParentClass.canNavigateToSource())
{
psiParentSourceClass=psiParentClass;
// no break here because we might still find the direct class
}
}
}
}
if(psiSourceClass == null)
{
// class not found yet, search all instead of project
for(Project current:openProjects)
{
GlobalSearchScope scope = GlobalSearchScope.allScope(current);
PsiClass found = findClass(current, className, scope);
if(found!=null)
{
project=current;
psiClass=found;
if(psiClass.canNavigateToSource())
{
psiSourceClass=psiClass;
// don't search anympore if source class was found.
break;
}
}
// we only care about parent class if class was not found.
if(parentClassName != null && psiClass==null && psiParentClass==null)
{
// search for parent as fallback...
found = findClass(current, parentClassName, scope);
if(found!=null)
{
project=current;
psiParentClass=found;
if(psiParentClass.canNavigateToSource())
{
psiParentSourceClass=psiParentClass;
// no break here because we might still find the direct class
}
}
}
}
}
if(project==null)
{
System.out.println("Couldn't find project...");
return;
}
System.out.println("Project: "+project.getName());
System.out.println("PsiClass: "+psiClass);
System.out.println("PsiSourceClass: "+psiSourceClass);
System.out.println("PsiParentClass: "+psiParentClass);
System.out.println("PsiParentSourceClass: "+psiParentSourceClass);
if(psiClass==null)
{
psiClass=psiParentClass;
psiSourceClass=psiParentSourceClass;
}
PsiElement elem;
if(psiSourceClass!=null)
{
elem=psiSourceClass.getNavigationElement();
}
else
{
elem=psiClass.getNavigationElement();
}
if(elem instanceof PsiClass)
{
psiClass = (PsiClass) elem;
}
PsiFile psiFile = psiClass.getContainingFile();
System.out.println("PsiFile: "+psiFile);
if(psiFile!=null)
{
VirtualFile vfile = psiFile.getVirtualFile();
if(vfile!=null)
{
OpenFileDescriptor fileDescriptor=null;
PsiMethod theMethod=null;
PsiMethod[] methods = psiClass.getMethods();
for(PsiMethod method: methods)
{
if(method.getName().equals(methodName))
{
theMethod=method;
break;
}
}
FileType fileType = psiFile.getFileType();
if(lineNumber>=0 && !fileType.isBinary())
{
// go to the line.
lineNumber--; // I don't get it...
fileDescriptor=new OpenFileDescriptor(project, vfile, lineNumber, 0);
System.out.println("Using lineNumber!");
}
else if(theMethod==null)
{
// just go to the file
fileDescriptor=new OpenFileDescriptor(project, vfile);
System.out.println("Using file!");
}
if(fileDescriptor!=null)
{
FileEditorManager fileEditorManager=FileEditorManager.getInstance(project);
if(fileEditorManager == null)
{
System.out.println("fileEditorManager is null!");
return;
}
fileDescriptor.navigate(true);
}
else
{
theMethod.navigate(true);
}
WindowManager.getInstance().suggestParentWindow(project).toFront();
}
}
}
}
private PsiClass findClass(Project project, String className, GlobalSearchScope scope)
{
try
{
Object instance = getInstanceMethod.invoke(null, project);
if(instance != null)
{
return (PsiClass) findClassMethod.invoke(instance, className, scope);
}
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
catch (InvocationTargetException e)
{
e.printStackTrace();
}
// IDEA 7: PsiManager.getInstance(project).findClass(className, scope);
// IDEA 8: JavaPsiFacade.getInstance(project).findClass(className, scope);
System.err.println("Couldn't find class '"+className+"'!");
return null;
}
}