/** * Copyright (c) 2005-2013 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 Sep 13, 2005 * * @author Fabio Zadrozny */ package com.python.pydev.analysis.additionalinfo; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.python.pydev.core.FileUtilsFileBuffer; import org.python.pydev.core.IModulesManager; import org.python.pydev.core.IPythonNature; import org.python.pydev.core.IPythonPathNature; import org.python.pydev.core.MisconfigurationException; import org.python.pydev.core.ModulesKey; import org.python.pydev.core.PythonNatureWithoutProjectException; import org.python.pydev.core.log.Log; import org.python.pydev.editor.codecompletion.revisited.ProjectModulesManager; import org.python.pydev.plugin.PydevPlugin; import org.python.pydev.plugin.nature.PythonNature; import org.python.pydev.plugin.nature.SystemPythonNature; import org.python.pydev.shared_core.string.StringUtils; import org.python.pydev.shared_core.structure.OrderedMap; import org.python.pydev.shared_core.structure.Tuple; import com.python.pydev.analysis.AnalysisPlugin; import com.python.pydev.analysis.system_info_builder.InterpreterInfoBuilder; public class AdditionalProjectInterpreterInfo extends AbstractAdditionalInfoWithBuild { /** * This is the project that contains this info */ private final IProject project; private final File persistingFolder; private final File persistingLocation; private PythonNature nature; /** * holds nature info (project name points to info) */ private final static Map<String, AbstractAdditionalDependencyInfo> additionalNatureInfo = new HashMap<String, AbstractAdditionalDependencyInfo>(); private final static Object additionalNatureInfoLock = new Object(); public IProject getProject() { return project; } @Override protected IPythonNature getNature() { return nature; } /** * @return the path to the folder we want to keep things on */ @Override protected File getPersistingFolder() { return persistingFolder; } @Override protected File getPersistingLocation() { return persistingLocation; } public AdditionalProjectInterpreterInfo(IProject project) throws MisconfigurationException { super(false); Assert.isNotNull(project); this.project = project; this.nature = PythonNature.getPythonNature(project); File f; try { f = AnalysisPlugin.getStorageDirForProject(project); } catch (NullPointerException e) { //it may fail in tests... (save it in default folder in this cases) Log.logInfo("Error getting persisting folder", e); f = new File("."); } persistingFolder = f; persistingLocation = new File(persistingFolder, "AdditionalProjectInterpreterInfo.pydevinfo"); init(); } @Override protected String getUIRepresentation() { return project != null ? project.getName() : "Unknown project"; } @Override protected Set<String> getPythonPathFolders() { PythonNature pythonNature = PythonNature.getPythonNature(project); IPythonPathNature pythonPathNature = pythonNature.getPythonPathNature(); Set<String> ret = new HashSet<>(); try { ret.addAll(StringUtils.split(pythonPathNature.getOnlyProjectPythonPathStr(true), "|")); } catch (CoreException e) { Log.log(e); } return ret; } public static List<AbstractAdditionalTokensInfo> getAdditionalInfo(IPythonNature nature) throws MisconfigurationException { return getAdditionalInfo(nature, true, false); } /** * @param nature the nature we want to get info on * @return all the additional info that is bounded with some nature (including related projects) * @throws MisconfigurationException */ public static List<AbstractAdditionalTokensInfo> getAdditionalInfo(IPythonNature nature, boolean addSystemInfo, boolean addReferencingProjects) throws MisconfigurationException { List<Tuple<AbstractAdditionalTokensInfo, IPythonNature>> infoAndNature = getAdditionalInfoAndNature(nature, addSystemInfo, addReferencingProjects); ArrayList<AbstractAdditionalTokensInfo> ret = new ArrayList<AbstractAdditionalTokensInfo>(); for (Tuple<AbstractAdditionalTokensInfo, IPythonNature> tuple : infoAndNature) { ret.add(tuple.o1); } return ret; } public static List<Tuple<AbstractAdditionalTokensInfo, IPythonNature>> getAdditionalInfoAndNature( IPythonNature nature, boolean addSystemInfo, boolean addReferencingProjects) throws MisconfigurationException { return getAdditionalInfoAndNature(nature, addSystemInfo, addReferencingProjects, true); } public static List<Tuple<AbstractAdditionalTokensInfo, IPythonNature>> getAdditionalInfoAndNature( IPythonNature nature, boolean addSystemInfo, boolean addReferencingProjects, boolean addReferencedProjects) throws MisconfigurationException { List<Tuple<AbstractAdditionalTokensInfo, IPythonNature>> ret = new ArrayList<Tuple<AbstractAdditionalTokensInfo, IPythonNature>>(); IProject project = nature.getProject(); //get for the system info if (addSystemInfo) { AbstractAdditionalTokensInfo systemInfo; try { systemInfo = AdditionalSystemInterpreterInfo.getAdditionalSystemInfo( PydevPlugin.getInterpreterManager(nature), nature.getProjectInterpreter().getExecutableOrJar()); } catch (MisconfigurationException e) { throw e; } catch (PythonNatureWithoutProjectException e) { throw new RuntimeException(e); } ret.add(new Tuple<AbstractAdditionalTokensInfo, IPythonNature>(systemInfo, new SystemPythonNature(nature .getRelatedInterpreterManager()))); } //get for the current project if (project != null) { AbstractAdditionalTokensInfo additionalInfoForProject = getAdditionalInfoForProject(nature); if (additionalInfoForProject != null) { ret.add(new Tuple<AbstractAdditionalTokensInfo, IPythonNature>(additionalInfoForProject, nature)); } try { if (addReferencedProjects) { //get for the referenced projects Set<IProject> referencedProjects = ProjectModulesManager.getReferencedProjects(project); for (IProject refProject : referencedProjects) { additionalInfoForProject = getAdditionalInfoForProject( PythonNature.getPythonNature(refProject)); if (additionalInfoForProject != null) { ret.add(new Tuple<AbstractAdditionalTokensInfo, IPythonNature>(additionalInfoForProject, PythonNature.getPythonNature(refProject))); } } } if (addReferencingProjects) { Set<IProject> referencingProjects = ProjectModulesManager.getReferencingProjects(project); for (IProject refProject : referencingProjects) { additionalInfoForProject = getAdditionalInfoForProject( PythonNature.getPythonNature(refProject)); if (additionalInfoForProject != null) { ret.add(new Tuple<AbstractAdditionalTokensInfo, IPythonNature>(additionalInfoForProject, PythonNature.getPythonNature(refProject))); } } } } catch (Exception e) { Log.log(e); } } return ret; } /** * @param project the project we want to get info on * @return the additional info for a given project (gotten from the cache with its name) * @throws MisconfigurationException */ public static AbstractAdditionalDependencyInfo getAdditionalInfoForProject(final IPythonNature nature) throws MisconfigurationException { if (nature == null) { return null; } IProject project = nature.getProject(); if (project == null) { return null; } String name = FileUtilsFileBuffer.getValidProjectName(project); synchronized (additionalNatureInfoLock) { AbstractAdditionalDependencyInfo info = additionalNatureInfo.get(name); if (info == null) { info = new AdditionalProjectInterpreterInfo(project); additionalNatureInfo.put(name, info); if (!info.load()) { recreateAllInfo(nature, new NullProgressMonitor()); } else { final AbstractAdditionalDependencyInfo temp = info; temp.setWaitForIntegrityCheck(true); //Ok, after it's loaded the first time, check the index integrity! Job j = new Job("Check index integrity for: " + project.getName()) { @Override protected IStatus run(IProgressMonitor monitor) { try { new InterpreterInfoBuilder().syncInfoToPythonPath(monitor, nature); } finally { temp.setWaitForIntegrityCheck(false); } return Status.OK_STATUS; } }; j.setPriority(Job.INTERACTIVE); j.setSystem(true); j.schedule(); } } return info; } } //interfaces that iterate through all of them public static List<IInfo> getTokensEqualTo(String qualifier, IPythonNature nature, int getWhat) throws MisconfigurationException { ArrayList<IInfo> ret = new ArrayList<IInfo>(50); List<AbstractAdditionalTokensInfo> additionalInfo = getAdditionalInfo(nature); for (AbstractAdditionalTokensInfo info : additionalInfo) { info.getTokensEqualTo(qualifier, getWhat, ret); } return ret; } public static List<IInfo> getTokensStartingWith(String qualifier, IPythonNature nature, int getWhat) throws MisconfigurationException { ArrayList<IInfo> ret = new ArrayList<IInfo>(); List<AbstractAdditionalTokensInfo> additionalInfo = getAdditionalInfo(nature); for (AbstractAdditionalTokensInfo info : additionalInfo) { info.getTokensStartingWith(qualifier, getWhat, ret); } return ret; } /** * @param project the project we want to get info on * @return a list of the additional info for the project + referencing projects * @throws MisconfigurationException */ public static List<AbstractAdditionalDependencyInfo> getAdditionalInfoForProjectAndReferencing(IPythonNature nature) throws MisconfigurationException { List<AbstractAdditionalDependencyInfo> ret = new ArrayList<AbstractAdditionalDependencyInfo>(); IProject project = nature.getProject(); if (project == null) { return ret; } ret.add(getAdditionalInfoForProject(nature)); Set<IProject> referencingProjects = ProjectModulesManager.getReferencingProjects(project); for (IProject p : referencingProjects) { AbstractAdditionalDependencyInfo info2 = getAdditionalInfoForProject(PythonNature.getPythonNature(p)); if (info2 != null) { ret.add(info2); } } return ret; } public static void recreateAllInfo(IPythonNature nature, IProgressMonitor monitor) { try { synchronized (additionalNatureInfoLock) { //Note: at this point we're 100% certain that the ast manager is there. IModulesManager m = nature.getAstManager().getModulesManager(); IProject project = nature.getProject(); AbstractAdditionalDependencyInfo currInfo = AdditionalProjectInterpreterInfo .getAdditionalInfoForProject(nature); if (currInfo != null) { currInfo.clearAllInfo(); currInfo.dispose(); } String feedback = "(project:" + project.getName() + ")"; synchronized (m) { AbstractAdditionalDependencyInfo info = (AbstractAdditionalDependencyInfo) restoreInfoForModuleManager( monitor, m, feedback, new AdditionalProjectInterpreterInfo(project), nature, nature.getGrammarVersion()); if (info != null) { //ok, set it and save it additionalNatureInfo.put(FileUtilsFileBuffer.getValidProjectName(project), info); info.save(); } } } } catch (Exception e) { Log.log(e); throw new RuntimeException(e); } } //Make it available for being in a HashSet. @Override public int hashCode() { return getProject().hashCode(); } @Override public boolean equals(Object obj) { if (!(obj instanceof AdditionalProjectInterpreterInfo)) { return false; } AdditionalProjectInterpreterInfo additionalProjectInterpreterInfo = (AdditionalProjectInterpreterInfo) obj; return this.getProject().equals(additionalProjectInterpreterInfo.getProject()); } /** * @param token the token we want to search for (must be an exact match). Only tokens which are valid identifiers * may be searched (i.e.: no dots in it or anything alike). * * @return List<ModulesKey> a list with all the modules that contains the passed token. * * Note: if it's a name with dots, we'll split it and search for each one. */ public List<ModulesKey> getModulesWithToken(String token, IProgressMonitor monitor) throws OperationCanceledException { NullProgressMonitor nullMonitor = new NullProgressMonitor(); if (monitor == null) { monitor = nullMonitor; } int length = token.length(); if (token == null || length == 0) { return new ArrayList<>(); } for (int i = 0; i < length; i++) { char c = token.charAt(i); if (!Character.isJavaIdentifierPart(c) && c != '.') { throw new RuntimeException(StringUtils.format( "Token: %s is not a valid token to search for.", token)); } } StringUtils.checkTokensValidForWildcardQuery(token); OrderedMap<String, Set<String>> fieldNameToValues = new OrderedMap<>(); Set<String> split = new HashSet<>(); for (String s : StringUtils.splitForIndexMatching(token)) { // We need to search in lowercase (we only index case-insensitive). split.add(s.toLowerCase()); } fieldNameToValues.put(IReferenceSearches.FIELD_CONTENTS, split); List<ModulesKey> search = getReferenceSearches().search(project, fieldNameToValues, monitor); //Checking consistency with old version //List<ModulesKey> old = new ReferenceSearches(this).search(project, token, nullMonitor); //System.out.println("Searching for: " + token); //Collections.sort(search); //Collections.sort(old); //System.out.println("---- New ----"); //for (ModulesKey modulesKey : search) { // System.out.println(modulesKey); //} //System.out.println("---- Old ----"); //for (ModulesKey modulesKey : old) { // System.out.println(modulesKey); //} return search; } }