/** * 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. */ package com.python.pydev.analysis.additionalinfo; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.ListResourceBundle; import java.util.Set; import java.util.TreeSet; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jface.action.Action; import org.eclipse.jface.text.IDocument; import org.python.pydev.core.IModulesManager; import org.python.pydev.core.IPythonNature; import org.python.pydev.core.MisconfigurationException; import org.python.pydev.core.ModulesKey; import org.python.pydev.core.docutils.StringUtils; import org.python.pydev.editor.IPyEditListener; import org.python.pydev.editor.PyEdit; import org.python.pydev.editor.codecompletion.revisited.PythonPathHelper; import org.python.pydev.editor.codecompletion.revisited.modules.AbstractModule; import org.python.pydev.editor.codecompletion.revisited.modules.SourceModule; import org.python.pydev.parser.visitors.scope.ASTEntry; import org.python.pydev.plugin.nature.PythonNature; import org.python.pydev.utils.PyFileListing.PyFileInfo; import com.aptana.shared_core.io.FileUtils; import com.python.pydev.util.UIUtils; /** * Checks the integrity of the internal pydev caches. * * @author Fabio */ public class AdditionalInfoIntegrityChecker implements IPyEditListener { public static class IntegrityInfo { public boolean allOk = true; public StringBuffer desc = new StringBuffer(); public IPythonNature nature; public IModulesManager modulesManager; public List<ModulesKey> modulesNotInDisk = new ArrayList<ModulesKey>(); public List<ModulesKey> modulesNotInMemory = new ArrayList<ModulesKey>(); public AdditionalProjectInterpreterInfo additionalProjectInfo; public List<SourceModule> moduleNotInAdditionalInfo = new ArrayList<SourceModule>(); public List<String> additionalModulesNotInDisk = new ArrayList<String>(); public String toString() { return desc.toString(); } } public static IntegrityInfo checkIntegrity(IPythonNature nature, IProgressMonitor monitor, boolean fix) throws MisconfigurationException { IntegrityInfo info = new IntegrityInfo(); StringBuffer buffer = info.desc; info.nature = nature; info.modulesManager = nature.getAstManager().getModulesManager(); info.additionalProjectInfo = (AdditionalProjectInterpreterInfo) AdditionalProjectInterpreterInfo .getAdditionalInfoForProject(nature); if (info.additionalProjectInfo == null) { buffer.append(com.aptana.shared_core.string.StringUtils.format("Unable to get additional project info for: %s (gotten null)", nature.getProject())); info.allOk = false; } PythonPathHelper pythonPathHelper = (PythonPathHelper) info.modulesManager.getPythonPathHelper(); List<String> pythonpath = pythonPathHelper.getPythonpath(); buffer.append(com.aptana.shared_core.string.StringUtils .format("Checking the integrity of the project: %s\n\n", nature.getProject().getName())); buffer.append("Pythonpath:\n"); for (String string : pythonpath) { buffer.append(string); buffer.append("\n"); } buffer.append("\n"); HashSet<ModulesKey> expectedModuleNames = new HashSet<ModulesKey>(); for (String string : pythonpath) { File file = new File(string); if (file.exists() && file.isDirectory()) { //TODO: Handle zip file modules! Collection<PyFileInfo> modulesBelow = pythonPathHelper.getModulesBelow(file, monitor) .getFoundPyFileInfos(); for (PyFileInfo fileInfo : modulesBelow) { File moduleFile = fileInfo.getFile(); String modName = pythonPathHelper.resolveModule(FileUtils.getFileAbsolutePath(moduleFile), true); if (modName != null) { expectedModuleNames.add(new ModulesKey(modName, moduleFile)); buffer.append(com.aptana.shared_core.string.StringUtils.format("Found module: %s - %s\n", modName, moduleFile)); } else { if (PythonPathHelper.isValidModuleLastPart(StringUtils.stripExtension((moduleFile.getName())))) { info.allOk = false; buffer.append(com.aptana.shared_core.string.StringUtils.format( "Unable to resolve module: %s (gotten null module name)\n", moduleFile)); } } } } else { info.allOk = false; buffer.append(com.aptana.shared_core.string.StringUtils.format("File %s is referenced in the pythonpath but does not exist.", file)); } } check(expectedModuleNames, info, fix); return info; } /** * @param expectedModuleNames the modules that exist in the disk (an actual file is found and checked for the module it resolves to) * @throws MisconfigurationException */ private static void check(HashSet<ModulesKey> expectedModuleNames, IntegrityInfo info, boolean fix) throws MisconfigurationException { StringBuffer buffer = info.desc; ModulesKey[] onlyDirectModules = info.modulesManager.getOnlyDirectModules(); TreeSet<ModulesKey> inModulesManager = new TreeSet<ModulesKey>(Arrays.asList(onlyDirectModules)); Set<String> allAdditionalInfoTrackedModules = info.additionalProjectInfo.getAllModulesWithTokens(); for (ModulesKey key : inModulesManager) { if (!expectedModuleNames.contains(key)) { info.allOk = false; info.modulesNotInDisk.add(key); buffer.append(com.aptana.shared_core.string.StringUtils.format("ModulesKey %s exists in memory but not in the disk.\n", key)); } } for (String s : allAdditionalInfoTrackedModules) { if (!expectedModuleNames.contains(new ModulesKey(s, null))) { info.allOk = false; info.additionalModulesNotInDisk.add(s); buffer.append(com.aptana.shared_core.string.StringUtils.format( "The module %s exists in the additional info memory but not in the disk.\n", s)); } } for (ModulesKey key : expectedModuleNames) { if (!inModulesManager.contains(key)) { info.allOk = false; info.modulesNotInMemory.add(key); buffer.append(com.aptana.shared_core.string.StringUtils.format("ModulesKey %s exists in the disk but not in memory.\n", key)); } if (!allAdditionalInfoTrackedModules.contains(key.name)) { try { AbstractModule mod = AbstractModule.createModule(key.name, key.file, info.nature, true); if (!(mod instanceof SourceModule)) { continue; } SourceModule module = (SourceModule) mod; if (module == null || module.getAst() == null) { buffer.append(com.aptana.shared_core.string.StringUtils.format( "Warning: cannot parse: %s - %s (so, it's ok not having additional info on it)\n", key.name, key.file)); } else { try { Iterator<ASTEntry> innerEntriesForAST = AbstractAdditionalDependencyInfo .getInnerEntriesForAST(module.getAst()).o2; if (innerEntriesForAST.hasNext()) { info.allOk = false; info.moduleNotInAdditionalInfo.add(module); buffer.append(com.aptana.shared_core.string.StringUtils.format( "The additional info index of the module: %s is not updated.\n", key.name)); } } catch (Exception e) { buffer.append(com.aptana.shared_core.string.StringUtils.format("Unexpected error happened on: %s - %s: %s\n", key.name, key.file, e.getMessage())); } } } catch (IOException e) { //OK, it cannot be parsed, so, we cannot generate its info buffer.append(com.aptana.shared_core.string.StringUtils.format( "Warning: cannot parse: %s - %s (so, it's ok not having additional info on it)\n", key.name, key.file)); } } } if (info.allOk) { buffer.append("All checks OK!\n"); } else { if (fix) { buffer.append("Fixing:\n"); //modules manager buffer.append(com.aptana.shared_core.string.StringUtils.format("Removing modules from memory: %s\n", info.modulesNotInDisk)); info.modulesManager.removeModules(info.modulesNotInDisk); buffer.append(com.aptana.shared_core.string.StringUtils.format("Adding to memory modules: %s\n", info.modulesNotInMemory)); for (ModulesKey key : info.modulesNotInMemory) { buffer.append("Adding modules ...\n"); info.modulesManager.addModule(key); } //additional info buffer.append(com.aptana.shared_core.string.StringUtils .format("Removing from additional info: %s\n", info.additionalModulesNotInDisk)); for (String s : info.additionalModulesNotInDisk) { info.additionalProjectInfo.removeInfoFromModule(s, true); } buffer.append(com.aptana.shared_core.string.StringUtils.format("Adding to additional info modules found in disk: %s\n", info.moduleNotInAdditionalInfo)); for (SourceModule mod : info.moduleNotInAdditionalInfo) { info.additionalProjectInfo.addAstInfo(mod.getAst(), mod.getModulesKey(), true); } } } } public void onCreateActions(ListResourceBundle resources, final PyEdit edit, IProgressMonitor monitor) { edit.addOfflineActionListener("--internal-test-modules", new Action() { @Override public void run() { List<IPythonNature> allPythonNatures = PythonNature.getAllPythonNatures(); StringBuffer buf = new StringBuffer(); try { for (IPythonNature nature : allPythonNatures) { buf.append(checkIntegrity(nature, new NullProgressMonitor(), true)); } } catch (MisconfigurationException e) { buf.append(e.getMessage()); } UIUtils.showString(buf.toString()); } }, "Used just for testing (do not use).", true); } public void onDispose(PyEdit edit, IProgressMonitor monitor) { } public void onSave(PyEdit edit, IProgressMonitor monitor) { } public void onSetDocument(IDocument document, PyEdit edit, IProgressMonitor monitor) { } }