/** * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the Eclipse Public License (EPL). * Please see the license.txt included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ /* * Created on May 24, 2005 * * @author Fabio Zadrozny */ package org.python.pydev.editor.codecompletion.revisited; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.SortedMap; import org.eclipse.core.runtime.Assert; import org.eclipse.jface.text.IDocument; import org.python.pydev.core.DeltaSaver; import org.python.pydev.core.FileUtilsFileBuffer; import org.python.pydev.core.IGrammarVersionProvider; import org.python.pydev.core.IInterpreterInfo; import org.python.pydev.core.IInterpreterManager; import org.python.pydev.core.IModule; import org.python.pydev.core.IModulesManager; import org.python.pydev.core.IPythonNature; import org.python.pydev.core.ISystemModulesManager; import org.python.pydev.core.IToken; import org.python.pydev.core.MisconfigurationException; import org.python.pydev.core.ModulesKey; import org.python.pydev.core.log.Log; import org.python.pydev.editor.codecompletion.revisited.modules.AbstractModule; import org.python.pydev.editor.codecompletion.revisited.modules.CompiledModule; import org.python.pydev.editor.codecompletion.revisited.modules.EmptyModule; import org.python.pydev.editor.codecompletion.revisited.modules.PredefinedSourceModule; import org.python.pydev.editor.codecompletion.revisited.modules.SourceModule; import org.python.pydev.parser.PyParser; import org.python.pydev.parser.jython.SimpleNode; import org.python.pydev.plugin.PydevPlugin; import org.python.pydev.plugin.nature.SystemPythonNature; import org.python.pydev.ui.pythonpathconf.InterpreterInfo; import com.aptana.shared_core.cache.LRUCache; import com.aptana.shared_core.string.FastStringBuffer; import com.aptana.shared_core.structure.Tuple; /** * @author Fabio Zadrozny */ public final class SystemModulesManager extends ModulesManagerWithBuild implements ISystemModulesManager { /** * The system modules manager may have a nature if we create a SystemASTManager */ private transient IPythonNature nature; /** * This is the place where we store the info related to this manager */ private InterpreterInfo info; public SystemModulesManager(InterpreterInfo info) { this.info = info; } public void setInfo(InterpreterInfo info) { //Should only be used in tests (in general the info should be passed in the constructor and never changed again). this.info = info; } public void endProcessing() { save(); } /** * @see org.python.pydev.core.ISystemModulesManager#getBuiltins() */ public String[] getBuiltins() { return this.info.getBuiltins(); } public void setPythonNature(IPythonNature nature) { Assert.isTrue(nature instanceof SystemPythonNature); Assert.isTrue(((SystemPythonNature) nature).info == this.info); this.nature = nature; } public IPythonNature getNature() { if (nature == null) { IInterpreterManager manager = getInterpreterManager(); nature = new SystemPythonNature(manager, this.info); } return nature; } public IInterpreterManager getInterpreterManager() { int interpreterType = this.info.getInterpreterType(); switch (interpreterType) { case IInterpreterManager.INTERPRETER_TYPE_JYTHON: return PydevPlugin.getJythonInterpreterManager(); case IInterpreterManager.INTERPRETER_TYPE_PYTHON: return PydevPlugin.getPythonInterpreterManager(); case IInterpreterManager.INTERPRETER_TYPE_IRONPYTHON: return PydevPlugin.getIronpythonInterpreterManager(); default: throw new RuntimeException("Don't know how to handle: " + interpreterType); } } public ISystemModulesManager getSystemModulesManager() { return this; //itself } public IModule getModule(String name, IPythonNature nature, boolean checkSystemManager, boolean dontSearchInit) { return getModule(name, nature, dontSearchInit); } public String resolveModule(String full, boolean checkSystemManager) { return super.resolveModule(full); } public List<String> getCompletePythonPath(IInterpreterInfo interpreter, IInterpreterManager manager) { if (interpreter == null) { throw new RuntimeException("The interpreter must be specified (received null)"); } else { return interpreter.getPythonPath(); } } public IModule getRelativeModule(String name, IPythonNature nature) { return super.getModule(name, nature, true); } /** * Called after the pythonpath is changed. */ @Override protected void onChangePythonpath(SortedMap<ModulesKey, ModulesKey> keys) { //create the builtin modules String[] builtins = getBuiltins(); if (builtins != null) { for (int i = 0; i < builtins.length; i++) { String name = builtins[i]; final ModulesKey k = new ModulesKey(name, null); keys.put(k, k); } } } /** * This is a cache with the name of a builtin pointing to itself (so, it works basically as a set), it's used * so that when we find a builtin that does not have a __file__ token we do not try to recreate it again later. */ private final LRUCache<String, String> builtinsNotConsidered = new LRUCache<String, String>(500); /** * @return true if there is a token that has rep as its representation. */ private boolean contains(IToken[] tokens, String rep) { for (IToken token : tokens) { if (token.getRepresentation().equals(rep)) { return true; } } return false; } /** * Files only get here if we were unable to parse them. */ private transient Map<File, Long> predefinedFilesNotParsedToTimestamp; public AbstractModule getBuiltinModule(String name, boolean dontSearchInit) { AbstractModule n = null; //check for supported builtins these don't have files associated. //they are the first to be passed because the user can force a module to be builtin, because there //is some information that is only useful when you have builtins, such as os and wxPython (those can //be source modules, but they have so much runtime info that it is almost impossible to get useful information //from statically analyzing them). String[] builtins = getBuiltins(); if (builtins == null || this.info == null) { //still on startup return null; } //for temporary access (so that we don't generate many instances of it) ModulesKey keyForCacheAccess = new ModulesKey(null, null); //A different choice for users that want more complete information on the libraries they're dealing //with is using predefined modules. Those will File predefinedModule = this.info.getPredefinedModule(name); if (predefinedModule != null && predefinedModule.exists()) { keyForCacheAccess.name = name; keyForCacheAccess.file = predefinedModule; n = cache.getObj(keyForCacheAccess, this); if ((n instanceof PredefinedSourceModule)) { PredefinedSourceModule predefinedSourceModule = (PredefinedSourceModule) n; if (predefinedSourceModule.isSynched()) { return n; } //otherwise (not PredefinedSourceModule or not synched), just keep going to create //it as a predefined source module } boolean tryToParse = true; Long lastModified = null; if (predefinedFilesNotParsedToTimestamp == null) { predefinedFilesNotParsedToTimestamp = new HashMap<File, Long>(); } else { Long lastTimeChanged = predefinedFilesNotParsedToTimestamp.get(predefinedModule); if (lastTimeChanged != null) { lastModified = predefinedModule.lastModified(); if (lastTimeChanged.equals(lastModified)) { tryToParse = false; } else { predefinedFilesNotParsedToTimestamp.remove(predefinedModule); } } } if (tryToParse) { IDocument doc; try { doc = FileUtilsFileBuffer.getDocFromFile(predefinedModule); IGrammarVersionProvider provider = new IGrammarVersionProvider() { public int getGrammarVersion() throws MisconfigurationException { return IGrammarVersionProvider.GRAMMAR_PYTHON_VERSION_3_0; // Always Python 3.0 here } }; Tuple<SimpleNode, Throwable> obj = PyParser.reparseDocument(new PyParser.ParserInfo(doc, provider, name, predefinedModule)); if (obj.o2 != null) { if (lastModified == null) { lastModified = predefinedModule.lastModified(); } predefinedFilesNotParsedToTimestamp.put(predefinedModule, lastModified); Log.log("Unable to parse: " + predefinedModule, obj.o2); } else if (obj.o1 != null) { n = new PredefinedSourceModule(name, predefinedModule, obj.o1, obj.o2); doAddSingleModule(keyForCacheAccess, n); return n; } //keep on going } catch (Throwable e) { Log.log(e); } } } boolean foundStartingWithBuiltin = false; FastStringBuffer buffer = null; for (int i = 0; i < builtins.length; i++) { String forcedBuiltin = builtins[i]; if (name.startsWith(forcedBuiltin)) { if (name.length() > forcedBuiltin.length() && name.charAt(forcedBuiltin.length()) == '.') { foundStartingWithBuiltin = true; keyForCacheAccess.name = name; n = cache.getObj(keyForCacheAccess, this); if (n == null && dontSearchInit == false) { if (buffer == null) { buffer = new FastStringBuffer(); } else { buffer.clear(); } keyForCacheAccess.name = buffer.append(name).append(".__init__").toString(); n = cache.getObj(keyForCacheAccess, this); } if (n instanceof EmptyModule || n instanceof SourceModule) { //it is actually found as a source module, so, we have to 'coerce' it to a compiled module n = new CompiledModule(name, this); doAddSingleModule(new ModulesKey(n.getName(), null), n); return n; } } if (name.equals(forcedBuiltin)) { keyForCacheAccess.name = name; n = cache.getObj(keyForCacheAccess, this); if (n == null || n instanceof EmptyModule || n instanceof SourceModule) { //still not created or not defined as compiled module (as it should be) n = new CompiledModule(name, this); doAddSingleModule(new ModulesKey(n.getName(), null), n); return n; } } if (n instanceof CompiledModule) { return n; } } } if (foundStartingWithBuiltin) { if (builtinsNotConsidered.getObj(name) != null) { return null; } //ok, just add it if it is some module that actually exists n = new CompiledModule(name, this); IToken[] globalTokens = n.getGlobalTokens(); //if it does not contain the __file__, this means that it's not actually a module //(but may be a token from a compiled module, so, clients wanting it must get the module //first and only then go on to this token). //done: a cache with those tokens should be kept, so that we don't actually have to create //the module to see its return values (because that's slow) if (globalTokens.length > 0 && contains(globalTokens, "__file__")) { doAddSingleModule(new ModulesKey(name, null), n); return n; } else { builtinsNotConsidered.add(name, name); return null; } } return null; } /** * In the system modules manager, we also have to check for the builtins */ @Override public IModule getModule(String name, IPythonNature nature, boolean dontSearchInit) { AbstractModule n = getBuiltinModule(name, dontSearchInit); if (n != null) { return n; } return super.getModule(name, nature, dontSearchInit); } public IModule getModuleWithoutBuiltins(String name, IPythonNature nature, boolean dontSearchInit) { return super.getModule(name, nature, dontSearchInit); } public Tuple<IModule, IModulesManager> getModuleAndRelatedModulesManager(String name, IPythonNature nature, boolean checkSystemManager, boolean dontSearchInit) { IModule module = this.getModule(name, nature, checkSystemManager, dontSearchInit); if (module != null) { return new Tuple<IModule, IModulesManager>(module, this); } return null; } public void load() throws IOException { final File workspaceMetadataFile = getIoDirectory(); ModulesManager.loadFromFile(this, workspaceMetadataFile); try { this.deltaSaver = new DeltaSaver<ModulesKey>(this.getIoDirectory(), "v1_sys_astdelta", readFromFileMethod, toFileMethod); } catch (Exception e) { Log.log(e); } deltaSaver.processDeltas(this); //process the current deltas (clears current deltas automatically and saves it when the processing is concluded) } public void save() { final File workspaceMetadataFile = getIoDirectory(); if (deltaSaver != null) { deltaSaver.clearAll(); //When save is called, the deltas don't need to be used anymore. } this.saveToFile(workspaceMetadataFile); this.deltaSaver = new DeltaSaver<ModulesKey>(this.getIoDirectory(), "v1_sys_astdelta", readFromFileMethod, toFileMethod); } public File getIoDirectory() { final File workspaceMetadataFile = PydevPlugin.getWorkspaceMetadataFile(info.getExeAsFileSystemValidPath()); return workspaceMetadataFile; } /** * @param keysFound */ public void updateKeysAndSave(PyPublicTreeMap<ModulesKey, ModulesKey> keysFound) { synchronized (modulesKeysLock) { modulesKeys.clear(); modulesKeys.putAll(keysFound); } this.save(); } }