pyparsermanager.java

来自「Python Development Environment (Python I」· Java 代码 · 共 250 行

JAVA
250
字号
package org.python.pydev.parser;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.Preferences;
import org.eclipse.core.runtime.Preferences.IPropertyChangeListener;
import org.eclipse.core.runtime.Preferences.PropertyChangeEvent;
import org.python.pydev.core.IPyEdit;
import org.python.pydev.core.parser.IPyParser;

/**
 * This is the class that manager the PyParser and its interaction with the PyEdit
 * 
 * It's available from 1.3.2 onwards because a single parser may be bounded to multiple editors, so, when the input
 * of a given editor changes, its parser may become the same parser that another editor already contained.
 * 
 * This class needs to know about:
 * 1. When an editor has its input set
 * 2. When an editor is disposed (so, its input no longer exists)
 * 
 * The idea is that a PyParser only exists if it has some input binded, and if there's no input bounded, it is disposed.
 * 
 * It's a singleton because it needs to manage multiple editors and their inputs. It is responsible for setting the
 * parser in each PyEdit.
 * 
 * @author Fabio
 */
public class PyParserManager {
    
    private static final boolean DEBUG = false;
    
    private Object lock = new Object();
    
    private static final String KEY_IN_PYEDIT_CACHE = "PyParserManager_PyParser";
    
    // -------------------------------------------------------------------------------------------- preferences stuff...
    public static final String USE_PYDEV_ANALYSIS_ONLY_ON_DOC_SAVE = "USE_PYDEV_ONLY_ON_DOC_SAVE";
    public static final String PYDEV_ELAPSE_BEFORE_ANALYSIS = "PYDEV_ELAPSE_BEFORE_ANALYSIS";
    
    private Preferences prefs;
    private int millisBeforeAnalysis;
    private boolean useOnlyOnSave;
    
    public int getElapseMillisBeforeAnalysis() {
        return millisBeforeAnalysis;
    }
    
    public boolean useAnalysisOnlyOnDocSave() {
        return useOnlyOnSave;
    }

    
    // ---------------------------------------------------------------------------------------------- singleton stuff...
    private static PyParserManager pyParserManager;

    public static synchronized PyParserManager getPyParserManager(Preferences prefs){
        if(pyParserManager == null){
            pyParserManager = new PyParserManager(prefs);
        }
        return pyParserManager;
    }
    
    public static synchronized void setPyParserManager(PyParserManager pyParserManager) {
        PyParserManager.pyParserManager = pyParserManager; 
    }

    
    /**
     * Constructor
     * 
     * @param prefs the prefs to get to the parser
     */
    private PyParserManager(Preferences prefs) {
        Assert.isNotNull(prefs); //in this constructor the prefs may never be null!
        
        this.prefs = prefs;
        this.millisBeforeAnalysis = prefs.getInt(PYDEV_ELAPSE_BEFORE_ANALYSIS);
        this.useOnlyOnSave = prefs.getBoolean(USE_PYDEV_ANALYSIS_ONLY_ON_DOC_SAVE);
        
        //singleton: private constructor
        IPropertyChangeListener prefListener = new Preferences.IPropertyChangeListener() {
            
            public void propertyChange(PropertyChangeEvent event) {
                String property = event.getProperty();
                if(property.equals(USE_PYDEV_ANALYSIS_ONLY_ON_DOC_SAVE) || property.equals(PYDEV_ELAPSE_BEFORE_ANALYSIS)){
                	//reset the caches
                	millisBeforeAnalysis = PyParserManager.this.prefs.getInt(PYDEV_ELAPSE_BEFORE_ANALYSIS);
                	useOnlyOnSave = PyParserManager.this.prefs.getBoolean(USE_PYDEV_ANALYSIS_ONLY_ON_DOC_SAVE);
                	
                	
                	//and set the needed parsers
                    boolean useAnalysisOnlyOnDocSave = useAnalysisOnlyOnDocSave();
                    
                    synchronized(lock){
                        for(IPyParser parser:parsers.keySet()){
                            parser.resetTimeoutPreferences(useAnalysisOnlyOnDocSave);
                        }
                    }
                }
            }
        };
        this.prefs.addPropertyChangeListener(prefListener);
    }


    
    // ---------------------------------------------------------------------------------------------- parser control....
    private volatile Map<IPyParser, List<IPyEdit>> parsers = new HashMap<IPyParser, List<IPyEdit>>();
    
    public synchronized List<IPyParser> getParsers() {
        synchronized(lock){
            ArrayList<IPyParser> ret = new ArrayList<IPyParser>(parsers.keySet());
            return ret;
        }
    }


    /**
     * This method attaches a parser to an editor.
     * 
     * It should:
     * 1. Set the parser attribute in the IPyEdit
     * 2. Add the IPyEdit as a listener to the new parser
     * 
     * @param edit this is the editor to which a parser should be attached.
     */
    public synchronized void attachParserTo(IPyEdit edit) {
        synchronized(lock){
            //remove previous...
            IPyParser existingParser = getParser(edit);
            if(existingParser != null){
                //it was already bonded to a parser, so, we have to remove that one before
                //attaching a new one
                notifyEditorDisposed(edit);
            }
            
            for(Map.Entry<IPyParser, List<IPyEdit>> entry:parsers.entrySet()){
                for(IPyEdit curr:entry.getValue()){
                    if(curr.hasSameInput(edit)){
                        //do nothing, as it is already binded to a similar document (just force a reparse
                        //and add it to the list of edits for that parser)
                        IPyParser p = getParser(curr);
                        
                        makeParserAssociations(edit, p);
                        
                        p.forceReparse();
                        return;
                    }
                }
            }
            if(DEBUG){
                System.out.println("Creating new parser.");
            }
            
            
            IPyParser pyParser = new PyParser(edit);
            
            boolean useAnalysisOnlyOnDocSave = useAnalysisOnlyOnDocSave();
            pyParser.resetTimeoutPreferences(useAnalysisOnlyOnDocSave);
            
            makeParserAssociations(edit, pyParser);
            pyParser.setDocument(edit.getDocument(), edit.getEditorInput());
            
            if(DEBUG){
                System.out.println("Available parsers:"+this.parsers.size());
            }
        }
    }

    /**
     * Makes the needed associations between the editor and a parser.
     * 
     * Meaning: 
     * the edit is put in the map (parser > edits)
     * the edit is added as a listener for parser events
     * the parser is set as the parser to be used in the editor
     */
    private synchronized void makeParserAssociations(IPyEdit edit, IPyParser pyParser) {
        synchronized(lock){
            List<IPyEdit> lst = this.parsers.get(pyParser);
            if(lst == null){
                lst = new ArrayList<IPyEdit>();
                this.parsers.put(pyParser, lst);
            }
            lst.add(edit);
            
            pyParser.addParseListener(edit);
            edit.getCache().put(KEY_IN_PYEDIT_CACHE, pyParser);
        }
    }

    public synchronized void notifySaved(IPyEdit edit) {
        synchronized(lock){
            if(DEBUG){
                System.out.println("Notifying save.");
            }
            getParser(edit).notifySaved();
        }
    }

    public synchronized void notifyEditorDisposed(IPyEdit edit) {
        synchronized(lock){
            //remove the listener from the parser
            IPyParser parser = getParser(edit);
            
            parser.removeParseListener(edit);
            
            //from the internal list from the parsers to the editors
            List<IPyEdit> lst = parsers.get(parser);
            //we always have the list here (because we must have created it before disposing it)
            lst.remove(edit);
            
            //and from the edit itself
            edit.getCache().remove(KEY_IN_PYEDIT_CACHE);
            
            //now, if there's no one in that parsers list anymore, lets dispose the parser
            //and remove it from our references
            boolean dispose = lst.size() == 0;
            
            if(dispose){
                if(DEBUG){
                    System.out.println("Disposing parser.");
                }
                parser.dispose();
                this.parsers.remove(parser);
                if(DEBUG){
                    System.out.println("Available parsers:"+this.parsers.size());
                }
            }else{
                //otherwise, just set its new input
                IPyEdit pyEdit = lst.get(0);
                parser.setDocument(pyEdit.getDocument(), pyEdit.getEditorInput());
            }
        }
    }

    public synchronized IPyParser getParser(IPyEdit edit) {
        synchronized(lock){
            return (IPyParser) edit.getCache().get(KEY_IN_PYEDIT_CACHE);
        }
    }



}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?