/** * Copyright (c) 2015 by Brainwy Software Ltda. 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.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; import java.util.AbstractCollection; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.zip.ZipFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.python.pydev.core.ModulesKey; import org.python.pydev.core.ModulesKeyForZip; import org.python.pydev.core.docutils.PySelection; import org.python.pydev.core.log.Log; import org.python.pydev.editor.codecompletion.revisited.ModulesFoundStructure; import org.python.pydev.editor.codecompletion.revisited.ModulesFoundStructure.ZipContents; import org.python.pydev.editor.codecompletion.revisited.ModulesManager; import org.python.pydev.editor.codecompletion.revisited.PyPublicTreeMap; import org.python.pydev.editor.codecompletion.revisited.PythonPathHelper; import org.python.pydev.shared_core.io.FileUtils; import org.python.pydev.shared_core.out_of_memory.OnExpectedOutOfMemory; import org.python.pydev.shared_core.string.FastStringBuffer; import org.python.pydev.shared_core.structure.OrderedMap; import com.python.pydev.analysis.additionalinfo.AbstractAdditionalDependencyInfo.IBufferFiller; /** * @deprecated */ @Deprecated public class ReferenceSearches implements IReferenceSearches { private class Command { public final boolean finish; public final IBufferFiller bufferFiller; public final ModulesKey modulesKey; public Command(ModulesKey modulesKey, IBufferFiller bufferFiller) { this.modulesKey = modulesKey; this.bufferFiller = bufferFiller; this.finish = false; } public Command() { this.modulesKey = null; this.bufferFiller = null; this.finish = true; } } private static class Searcher implements Runnable { private final BlockingQueue<Command> queue; private final Collection<String> searchTokens; private final List<ModulesKey> ret; private final FastStringBuffer temp = new FastStringBuffer(); private final Object retLock; public Searcher(BlockingQueue<Command> linkedBlockingQueue, Collection<String> token, List<ModulesKey> ret, Object retLock) { this.queue = linkedBlockingQueue; if (token.size() == 1) { final String searchfor = token.iterator().next(); this.searchTokens = new AbstractCollection<String>() { @Override public boolean contains(Object o) { return searchfor.equals(o); // implementation should be a bit faster than using a set (only for when we know there's a single entry) } @Override public Iterator<String> iterator() { throw new RuntimeException("not implemented"); } @Override public int size() { throw new RuntimeException("not implemented"); } }; } else { this.searchTokens = new HashSet<String>(token); } this.retLock = retLock; this.ret = ret; } @Override public void run() { FastStringBuffer buf = new FastStringBuffer(); while (true) { Command cmd; try { cmd = queue.take(); if (cmd.finish) { break; } cmd.bufferFiller.fillBuffer(buf.clear()); this.search(cmd.modulesKey, buf); } catch (InterruptedException e) { Log.log("Not expecting to be interrupted in searcher. Results may be wrong.", e); break; } } } private void search(ModulesKey modulesKey, FastStringBuffer bufFileContents) { temp.clear(); int length = bufFileContents.length(); char[] internalCharsArray = bufFileContents.getInternalCharsArray(); for (int i = 0; i < length; i++) { char c = internalCharsArray[i]; if (Character.isJavaIdentifierStart(c)) { temp.clear(); temp.append(c); i++; for (; i < length; i++) { c = internalCharsArray[i]; if (c == ' ' || c == '\t' || c == '\r' || c == '\n') { break; //Fast forward through the most common case... } if (Character.isJavaIdentifierPart(c)) { temp.append(c); } else { break; } } String str = temp.toString(); if (PySelection.ALL_KEYWORD_TOKENS.contains(str)) { continue; } if (searchTokens.contains(str)) { if (AbstractAdditionalDependencyInfo.DEBUG) { System.out.println("Found in: " + modulesKey); } synchronized (retLock) { ret.add(modulesKey); } break; } } } } } private WeakReference<AbstractAdditionalDependencyInfo> abstractAdditionalDependencyInfo; public ReferenceSearches(AbstractAdditionalDependencyInfo abstractAdditionalDependencyInfo) { this.abstractAdditionalDependencyInfo = new WeakReference<>(abstractAdditionalDependencyInfo); } @Override public void dispose() { } private void fill(FastStringBuffer bufFileContents, InputStream stream) throws IOException { for (int i = 0; i < 5; i++) { try { bufFileContents.clear(); FileUtils.fillBufferWithStream(stream, null, new NullProgressMonitor(), bufFileContents); return; //if it worked, return, otherwise go to the next iteration } catch (OutOfMemoryError e) { //We went too fast and have no more memory... (consumers are slow) retry again in a few moments... bufFileContents.clearMemory(); Object o = new Object(); synchronized (o) { try { o.wait(50); } catch (InterruptedException e1) { } } if (i == 3) { //Maybe we can't really load it because too much is cached? OnExpectedOutOfMemory.clearCacheOnOutOfMemory.call(null); } } } //If we haven't returned, try a last iteration which will make any error public. bufFileContents.clear(); FileUtils.fillBufferWithStream(stream, null, new NullProgressMonitor(), bufFileContents); } @Override public List<ModulesKey> search(IProject project, OrderedMap<String, Set<String>> fieldNameToValues, IProgressMonitor monitor) { final List<ModulesKey> ret = new ArrayList<ModulesKey>(); AbstractAdditionalDependencyInfo abstractAdditionalDependencyInfo = this.abstractAdditionalDependencyInfo.get(); if (abstractAdditionalDependencyInfo == null) { Log.log("AbstractAdditionalDependencyInfo alreeady collected!"); return ret; } final NullProgressMonitor nullMonitor = new NullProgressMonitor(); Set<String> pythonPathFolders = abstractAdditionalDependencyInfo.getPythonPathFolders(); LinkedBlockingQueue<Command> queue = new LinkedBlockingQueue<>(); int searchers = Runtime.getRuntime().availableProcessors(); //The 'ret' should be filled with the module keys where the tokens are found. final Object retLock = new Object(); // Create 2 consumers Thread[] threads = new Thread[searchers]; for (int i = 0; i < searchers; i++) { Searcher searcher = new Searcher(queue, fieldNameToValues.get(IReferenceSearches.FIELD_CONTENTS), ret, retLock); //Spawn a thread to do the search while we load the contents. Thread t = new Thread(searcher); threads[i] = t; t.start(); } try { PythonPathHelper pythonPathHelper = new PythonPathHelper(); pythonPathHelper.setPythonPath(new ArrayList<String>(pythonPathFolders)); ModulesFoundStructure modulesFound = pythonPathHelper.getModulesFoundStructure(project, nullMonitor); int totalSteps = modulesFound.regularModules.size() + modulesFound.zipContents.size(); monitor.beginTask("Get modules with token in: " + abstractAdditionalDependencyInfo.getUIRepresentation(), totalSteps); PyPublicTreeMap<ModulesKey, ModulesKey> keys = new PyPublicTreeMap<>(); boolean includeOnlySourceModules = true; //no point in searching dlls. ModulesManager.buildKeysForRegularEntries(nullMonitor, modulesFound, keys, includeOnlySourceModules); //Get from regular files found for (ModulesKey entry : keys.values()) { if (monitor.isCanceled()) { break; } if (AbstractAdditionalDependencyInfo.DEBUG) { System.out.println("Loading: " + entry); } final File file = entry.file; try { queue.put(new Command(entry, new IBufferFiller() { @Override public void fillBuffer(FastStringBuffer bufFileContents) { try (FileInputStream stream = new FileInputStream(file)) { fill(bufFileContents, stream); } catch (Exception e) { Log.log(e); } } })); } catch (InterruptedException e) { Log.log(e); } } //Get from zip files found List<ZipContents> allZipsZipContents = modulesFound.zipContents; for (ZipContents zipContents : allZipsZipContents) { keys.clear(); if (monitor.isCanceled()) { break; } ModulesManager.buildKeysForZipContents(keys, zipContents); try (ZipFile zipFile = new ZipFile(zipContents.zipFile)) { for (ModulesKey entry : keys.values()) { if (AbstractAdditionalDependencyInfo.DEBUG) { System.out.println("Loading: " + entry); } if (monitor.isCanceled()) { break; } final ModulesKeyForZip z = (ModulesKeyForZip) entry; if (!z.isFile) { continue; } queue.put(new Command(entry, new IBufferFiller() { @Override public void fillBuffer(FastStringBuffer bufFileContents) { try (InputStream stream = zipFile.getInputStream(zipFile.getEntry(z.zipModulePath))) { fill(bufFileContents, stream); } catch (Exception e) { Log.log(e); } } })); } } catch (Exception e) { Log.log(e); } } } finally { for (int i = 0; i < searchers; i++) { queue.add(new Command()); // add it to wait for the thread to finish. } } int j = 0; while (true) { j++; boolean liveFound = false; for (Thread t : threads) { if (t.isAlive()) { liveFound = true; break; } } if (liveFound) { if (j % 50 == 0) { monitor.setTaskName("Searching references..."); monitor.worked(1); } Thread.yield(); } else { break; } } return ret; } }