📄 abstractshell.java
字号:
/*
* Created on 13/08/2005
*/
package org.python.pydev.editor.codecompletion.shell;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.python.copiedfromeclipsesrc.JDTNotAvailableException;
import org.python.pydev.core.IPythonNature;
import org.python.pydev.core.IToken;
import org.python.pydev.core.Tuple;
import org.python.pydev.core.log.Log;
import org.python.pydev.editor.codecompletion.PyCodeCompletion;
import org.python.pydev.editor.codecompletion.PyCodeCompletionPreferencesPage;
import org.python.pydev.plugin.PydevPlugin;
import org.python.pydev.plugin.SocketUtil;
import org.python.pydev.runners.ThreadStreamReader;
/**
* This is the shell that 'talks' to the python / jython process (it is intended to be subclassed so that
* we know how to deal with each).
*
* Its methods are synched to prevent concurrent access.
*
* @author fabioz
*
*/
public abstract class AbstractShell {
public static final int BUFFER_SIZE = 1024 ;
public static final int OTHERS_SHELL = 2;
public static final int COMPLETION_SHELL = 1;
protected static final int DEFAULT_SLEEP_BETWEEN_ATTEMPTS = 1000; //1sec, so we can make the number of attempts be shown as elapsed in secs
protected static final int DEBUG_SHELL = -1;
/**
* Determines if we are already in a method that starts the shell
*/
private boolean inStart = false;
/**
* Determines if we are (theoretically) already connected (meaning that trying to start the shell
* again will not do anything)
*
* Ending the shell sets this to false and starting it sets it to true (if successful)
*/
private boolean isConnected = false;
private boolean isInRead = false;
private boolean isInWrite = false;
private boolean isInRestart = false;
/**
* Lock to know if there is someone already using this shell for some operation
*/
private boolean isInOperation = false;
private static void dbg(String string, int priority) {
if(priority <= DEBUG_SHELL){
System.out.println(string);
}
if(PyCodeCompletion.DEBUG_CODE_COMPLETION){
Log.toLogFile(string, AbstractShell.class);
}
}
/**
* the encoding used to encode messages
*/
private static final String ENCODING_UTF_8 = "UTF-8";
/**
* Reference to 'global python shells'
*
* this works as follows:
* we have a 'related to' id as the first step, according to the IPythonNature constants
*
* @see org.python.pydev.core.IPythonNature#PYTHON_RELATED
* @see org.python.pydev.core.IPythonNature#JYTHON_RELATED
*
* and then we have the id with the shell type that points to the actual shell
*
* @see #COMPLETION_SHELL
* @see #OTHERS_SHELL
*/
protected static Map<Integer,Map<Integer,AbstractShell>> shells = new HashMap<Integer,Map<Integer,AbstractShell>>();
/**
* if we are already finished for good, we may not start new shells (this is a static, because this
* should be set only at shutdown).
*/
private static boolean finishedForGood = false;
/**
* simple stop of a shell (it may be later restarted)
*/
public synchronized static void stopServerShell(int relatedId, int id) {
Map<Integer, AbstractShell> typeToShell = getTypeToShellFromId(relatedId);
AbstractShell pythonShell = (AbstractShell) typeToShell.get(new Integer(id));
if(pythonShell != null){
try {
pythonShell.endIt();
} catch (Exception e) {
// ignore... we are ending it anyway...
}
}
}
/**
* stops all registered shells
*
*/
public synchronized static void shutdownAllShells(){
synchronized(shells){
for (Iterator<Map<Integer, AbstractShell>> iter = shells.values().iterator(); iter.hasNext();) {
finishedForGood = true; //we may no longer restart shells
Map<Integer,AbstractShell> rel = (Map<Integer, AbstractShell>) iter.next();
if(rel != null){
for (Iterator iter2 = rel.values().iterator(); iter2.hasNext();) {
AbstractShell element = (AbstractShell) iter2.next();
if(element != null){
try {
element.shutdown(); //shutdown
} catch (Exception e) {
PydevPlugin.log(e); //let's log it... this should not happen
}
}
}
}
}
shells.clear();
}
}
/**
* @param relatedId the id that is related to the structure we want to get
* @return a map with the type of the shell mapping to the shell itself
*/
private synchronized static Map<Integer, AbstractShell> getTypeToShellFromId(int relatedId) {
synchronized(shells){
Map<Integer, AbstractShell> typeToShell = shells.get(relatedId);
if (typeToShell == null) {
typeToShell = new HashMap<Integer, AbstractShell>();
shells.put(relatedId, typeToShell);
}
return typeToShell;
}
}
/**
* register a shell and give it an id
* @param id the shell id
* @param shell the shell to register
*
* @see org.python.pydev.core.IPythonNature#PYTHON_RELATED
* @see org.python.pydev.core.IPythonNature#JYTHON_RELATED
*
* @see #COMPLETION_SHELL
* @see #OTHERS_SHELL
*/
public synchronized static void putServerShell(IPythonNature nature, int id, AbstractShell shell) {
try {
Map<Integer, AbstractShell> typeToShell = getTypeToShellFromId(nature.getRelatedId());
typeToShell.put(new Integer(id), shell);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public synchronized static AbstractShell getServerShell(IPythonNature nature, int id) throws IOException, JDTNotAvailableException, CoreException {
return getServerShell(nature.getRelatedId(), id);
}
/**
* @return the shell with the given id related to some nature
*
* @see org.python.pydev.core.IPythonNature#PYTHON_RELATED
* @see org.python.pydev.core.IPythonNature#JYTHON_RELATED
*
* @see #COMPLETION_SHELL
* @see #OTHERS_SHELL
*
* @throws CoreException
* @throws IOException
*/
public synchronized static AbstractShell getServerShell(int relatedId, int id) throws IOException, JDTNotAvailableException, CoreException {
AbstractShell pythonShell = null;
synchronized(shells){
if(PyCodeCompletion.DEBUG_CODE_COMPLETION){
Log.toLogFile("Synchronizing on shells...", AbstractShell.class);
}
if(PyCodeCompletion.DEBUG_CODE_COMPLETION){
Log.toLogFile( "Getting shell relatedId:"+relatedId+" id:"+id, AbstractShell.class);
}
Map<Integer, AbstractShell> typeToShell = getTypeToShellFromId(relatedId);
pythonShell = (AbstractShell) typeToShell.get(new Integer(id));
if(pythonShell == null){
if(PyCodeCompletion.DEBUG_CODE_COMPLETION){
Log.toLogFile("pythonShell == null", AbstractShell.class);
}
if(relatedId == IPythonNature.PYTHON_RELATED){
pythonShell = new PythonShell();
}else if(relatedId == IPythonNature.JYTHON_RELATED){
pythonShell = new JythonShell();
}else{
throw new RuntimeException("unknown related id");
}
if(PyCodeCompletion.DEBUG_CODE_COMPLETION){
Log.toLogFile("pythonShell.startIt()", AbstractShell.class);
Log.addLogLevel();
}
pythonShell.startIt(); //first start it
if(PyCodeCompletion.DEBUG_CODE_COMPLETION){
Log.remLogLevel();
Log.toLogFile("Finished pythonShell.startIt()", AbstractShell.class);
}
//then make it accessible
typeToShell.put(new Integer(id), pythonShell);
}
}
return pythonShell;
}
/**
* Python server process.
*/
protected Process process;
/**
* We should write in this socket.
*/
protected Socket socketToWrite;
/**
* We should read this socket.
*/
protected Socket socketToRead;
/**
* Python file that works as the server.
*/
protected File serverFile;
/**
* Server socket (accept connections).
*/
protected ServerSocket serverSocket;
private ThreadStreamReader stdReader;
private ThreadStreamReader errReader;
/**
* Initialize given the file that points to the python server (execute it
* with python).
*
* @param f file pointing to the python server
*
* @throws IOException
* @throws CoreException
*/
protected AbstractShell(File f) throws IOException, CoreException {
if(finishedForGood){
throw new RuntimeException("Shells are already finished for good, so, it is an invalid state to try to create a new shell.");
}
serverFile = f;
if(!serverFile.exists()){
throw new RuntimeException("Can't find python server file");
}
}
/**
* Just wait a little...
*/
protected synchronized void sleepALittle(int t) {
try {
synchronized(this){
wait(t); //millis
}
} catch (InterruptedException e) {
}
}
/**
* This method creates the python server process and starts the sockets, so that we
* can talk with the server.
* @throws IOException
* @throws CoreException
*/
public synchronized void startIt() throws IOException, JDTNotAvailableException, CoreException {
synchronized(this){
this.startIt(AbstractShell.DEFAULT_SLEEP_BETWEEN_ATTEMPTS);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -