// $codepro.audit.disable useEquals /** * Aptana Studio * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions). * Please see the license.html included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ package com.aptana.editor.php.indexer; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; 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.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtension; import org.eclipse.core.runtime.IExtensionPoint; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org2.eclipse.php.internal.core.ast.nodes.Program; import com.aptana.build.util.BuildHelper; import com.aptana.core.logging.IdeLog; import com.aptana.core.util.EclipseUtil; import com.aptana.editor.php.PHPEditorPlugin; import com.aptana.editor.php.core.CorePreferenceConstants.Keys; import com.aptana.editor.php.internal.builder.BuildPathManager; import com.aptana.editor.php.internal.builder.FileSystemBuildPath; import com.aptana.editor.php.internal.core.builder.IBuildPath; import com.aptana.editor.php.internal.core.builder.IBuildPathChangeListener; import com.aptana.editor.php.internal.core.builder.IBuildPathsListener; import com.aptana.editor.php.internal.core.builder.IDirectory; import com.aptana.editor.php.internal.core.builder.IModule; import com.aptana.editor.php.internal.indexer.ComplexIndex; import com.aptana.editor.php.internal.indexer.IndexPersistence; import com.aptana.editor.php.internal.indexer.UnpackedElementIndex; import com.aptana.editor.php.internal.indexer.language.PHPBuiltins; /** * PHP global indexer. * * @author Denis Denisenko */ // $codepro.audit.disable useEquals public final class PHPGlobalIndexer { public static final Object FAMILY_PHP_BUILD = new Object(); private static Object mutex = new Object(); private static final class WrapIndexer implements IModuleIndexer, IProgramIndexer { private final IConfigurationElement element; private IModuleIndexer indexer; private WrapIndexer(IConfigurationElement element) { this.element = element; } public void indexModule(IModule module, IIndexReporter reporter) { try { initIfNeeded(); indexer.indexModule(module, reporter); } catch (CoreException e) { IdeLog.logError(PHPEditorPlugin.getDefault(), "Error indexing a PHP module", e, PHPEditorPlugin.INDEXER_SCOPE); //$NON-NLS-1$ } } /* * (non-Javadoc) * @see * com.aptana.editor.php.indexer.IProgramIndexer#indexModule(org2.eclipse.php.internal.core.ast.nodes.Program, * com.aptana.editor.php.internal.core.builder.IModule, java.lang.String, * com.aptana.editor.php.indexer.IIndexReporter) */ public void indexModule(Program program, IModule module, String source, IIndexReporter reporter) { try { initIfNeeded(); if (indexer instanceof IProgramIndexer) { IProgramIndexer pi = (IProgramIndexer) indexer; pi.indexModule(program, module, source, reporter); } } catch (CoreException e) { IdeLog.logError(PHPEditorPlugin.getDefault(), "Error indexing a PHP module", e, PHPEditorPlugin.INDEXER_SCOPE); //$NON-NLS-1$ } } private void initIfNeeded() throws CoreException { if (indexer == null) { indexer = (IModuleIndexer) element.createExecutableExtension(CLASS_ATTRIBUTE_NAME); } } public void indexModule(Program program, IModule module, IIndexReporter reporter) { try { initIfNeeded(); if (indexer instanceof IProgramIndexer) { IProgramIndexer pi = (IProgramIndexer) indexer; pi.indexModule(program, module, reporter); } } catch (CoreException e) { IdeLog.logError(PHPEditorPlugin.getDefault(), "Error indexing a PHP module", e, PHPEditorPlugin.INDEXER_SCOPE); //$NON-NLS-1$ } } } private static final int SAVING_INTERVAL = 10000; Thread saverThread = new Thread("PHPGlobalIndexer saver") //$NON-NLS-1$ { public void run() { while (true) { try { Thread.sleep(SAVING_INTERVAL); // $codepro.audit.disable disallowSleepInsideWhile try { doSave(); } catch (Exception e) { IdeLog.logError(PHPEditorPlugin.getDefault(), "Error saving the PHP index", e, PHPEditorPlugin.INDEXER_SCOPE); //$NON-NLS-1$ } } catch (InterruptedException e) { IdeLog.logWarning(PHPEditorPlugin.getDefault(), "Saving the PHP index was interrupted", e, PHPEditorPlugin.INDEXER_SCOPE); //$NON-NLS-1$ return; } } } }; /** * Extension point name. */ private static final String EXTENSION_POINT_NAME = "com.aptana.editor.php.indexer"; //$NON-NLS-1$ /** * Indexer element name. */ private static final String INDEXER_ELEMENT_NAME = "indexer"; //$NON-NLS-1$ /** * Class attribute name. */ private static final String CLASS_ATTRIBUTE_NAME = "class"; //$NON-NLS-1$ /** * Indexer instance. */ private static PHPGlobalIndexer instance; /** * Main index. */ private ComplexIndex mainIndex; /** * Module indexers. */ private List<IModuleIndexer> moduleIndexers; /** * Build path change listener. */ private IBuildPathChangeListener buildPathChangeListener; private Set<IIndexChangeListener> listeners = new HashSet<IIndexChangeListener>(); /** * Module index listeners. */ private Set<IModuleIndexListener> moduleIndexListeners = new HashSet<IModuleIndexListener>(); /** * Gets indexer instance. * * @return indexer instance. */ public static PHPGlobalIndexer getInstance() { synchronized (mutex) { if (instance == null) { instance = new PHPGlobalIndexer(); } return instance; } } /** * Gets index. * * @return index. */ public IElementsIndex getIndex() { return mainIndex; } /** * PHPElementsIndexer private constructor. */ private PHPGlobalIndexer() { createMainIndex(); // FIXME: Shalom - Don't think it will work for a variety of php versions in the workspace. PHPEditorPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(new IPropertyChangeListener() { public void propertyChange(PropertyChangeEvent event) { if (event.getProperty().equals(Keys.PHP_VERSION)) PHPGlobalIndexer.getInstance().cleanLibraries(new NullProgressMonitor()); } }); collectModuleIndexersInfo(); initPersistence(); buildPathChangeListener = new IBuildPathChangeListener() { public void changedBefore(List<IModule> changed, List<IModule> removed, List<IDirectory> removedDirectories) { processChangedBefore(changed, removed, removedDirectories); } public void changedAfter(List<IModule> added, List<IModule> changed, List<IModule> removed, List<IDirectory> addedDirectories, List<IDirectory> removedDirectories) { processChangedAfter(added, changed, removed, addedDirectories, removedDirectories); } }; indexLocalModules(); BuildPathManager.getInstance().addBuildPathChangeListener(new IBuildPathsListener() { public void changed(List<IBuildPath> added, List<IBuildPath> removed) { for (IBuildPath path : removed) { path.removeBuildPathChangeListener(buildPathChangeListener); mainIndex.removeIndex(path); // if build path is passive, we need to initiate // modules indexing manually if (path.isPassive()) { Job job = handleModulesRemoved(path.getModules()); job.setPriority(Job.BUILD); job.schedule(); } } for (IBuildPath path : added) { mainIndex.addIndex(path, new UnpackedElementIndex()); path.addBuildPathChangeListener(buildPathChangeListener); // if build path is passive, we need to initiate // modules indexing manually if (path.isPassive()) { Job job = handleModulesAdded(path.getModules()); job.setPriority(Job.BUILD); job.schedule(); } } } }); // Listen to the workspace and remove the index files for deleted projects IWorkspace workspace = ResourcesPlugin.getWorkspace(); workspace.addResourceChangeListener(new IResourceChangeListener() { public void resourceChanged(IResourceChangeEvent event) { IResource resource = event.getResource(); if (resource instanceof IProject) { IBuildPath buildPath = BuildPathManager.getInstance().getBuildPathByResource(resource); if (buildPath != null) { File indexFile = getIndexFile(buildPath); if (indexFile != null && indexFile.exists()) { // We only delete the index file. // The content of the indexMapping file will be updated only on the next loading of the // Studio. indexFile.delete(); } } } } }, IResourceChangeEvent.PRE_DELETE); } private void initPersistence() { saverThread.setDaemon(true); saverThread.start(); } /** * Creates main index. */ private void createMainIndex() { mainIndex = new ComplexIndex(); } /** * Handles removed modules. * * @param modules * - modules. * @return job that is able to handle the removed modules. */ private Job handleModulesRemoved(final List<IModule> modules) { Job job = new Job(Messages.PHPGlobalIndexer_RemovingModuleIndex2) { public IStatus run(IProgressMonitor monitor) { long start = System.currentTimeMillis(); monitor.beginTask(Messages.PHPGlobalIndexer_RemovingModuleIndex2, modules.size()); fireChanged(modules.size()); for (int i = 0; i < modules.size(); i++) { if (monitor.isCanceled()) { break; } IModule module = modules.get(i); mainIndex.removeModuleEntries(module, module.getBuildPath()); monitor.worked(1); UnpackedElementIndex elementIndex = (UnpackedElementIndex) mainIndex.getElementIndex(module .getBuildPath()); if (elementIndex != null) { elementIndex.removeTimeStamp(module); } } monitor.done(); markDirtyPathes(modules); fireChanged(0); IdeLog.logInfo(PHPEditorPlugin.getDefault(), "Indexer handleModulesRemoved [took " + (System.currentTimeMillis() - start) + "ms)", null, //$NON-NLS-1$ //$NON-NLS-2$ PHPEditorPlugin.INDEXER_SCOPE); return Status.OK_STATUS; } /* * (non-Javadoc) * @see org.eclipse.core.runtime.jobs.Job#belongsTo(java.lang.Object) */ @Override public boolean belongsTo(Object family) { if (family == FAMILY_PHP_BUILD || family == ResourcesPlugin.FAMILY_AUTO_BUILD || family == ResourcesPlugin.FAMILY_MANUAL_BUILD) { return true; } return super.belongsTo(family); } }; return job; } private void markDirtyPathes(final List<IModule> modules) { synchronized (needSaving) { for (IModule m : modules) { needSaving.add(m.getBuildPath()); } } } /** * Handles changed modules. * * @param modules * - modules. * @param resource * - resource. * @return job that is able to handle the changed modules. */ private Job handleModulesChanged(final List<IModule> modules) { Job job = new Job(Messages.PHPGlobalIndexer_IndexChanged) { public IStatus run(IProgressMonitor monitor) { long start = System.currentTimeMillis(); monitor.beginTask(Messages.PHPGlobalIndexer_IndexChanged2, modules.size()); fireChanged(modules.size()); for (int i = 0; i < modules.size(); i++) { IModule module = modules.get(i); long l = module.getTimeStamp(); mainIndex.removeModuleEntries(module, module.getBuildPath()); for (IModuleIndexer indexer : moduleIndexers) { if (monitor.isCanceled()) { break; } indexer.indexModule(module, new IIndexReporter() { public IElementEntry reportEntry(int category, String entryPath, IReportable value, IModule module) { return mainIndex.addEntry(category, entryPath, value, module, module.getBuildPath()); } }); } UnpackedElementIndex elementIndex = (UnpackedElementIndex) mainIndex.getElementIndex(module .getBuildPath()); if (elementIndex != null) { elementIndex.recordTimeStamp(module, l); } if (monitor.isCanceled()) { break; } monitor.worked(1); } fireChanged(0); markDirtyPathes(modules); monitor.done(); IdeLog.logInfo(PHPEditorPlugin.getDefault(), "Indexer handleModulesChanged [took " + (System.currentTimeMillis() - start) + "ms)", null, //$NON-NLS-1$ //$NON-NLS-2$ PHPEditorPlugin.INDEXER_SCOPE); return Status.OK_STATUS; } /* * (non-Javadoc) * @see org.eclipse.core.runtime.jobs.Job#belongsTo(java.lang.Object) */ @Override public boolean belongsTo(Object family) { if (family == FAMILY_PHP_BUILD || family == ResourcesPlugin.FAMILY_AUTO_BUILD || family == ResourcesPlugin.FAMILY_MANUAL_BUILD) { return true; } return super.belongsTo(family); } }; return job; } /** * Handles added modules. * * @param modules * - modules. * @param monitor * @param resource * - resource. * @return job that is able to handle the added modules. */ private Job handleModulesAdded(final List<IModule> modules) { Job job = new Job(Messages.PHPGlobalIndexer_IndexNew) { public IStatus run(IProgressMonitor monitor) { long start = System.currentTimeMillis(); fireChanged(modules.size()); monitor.beginTask(Messages.PHPGlobalIndexer_IndexNew, modules.size()); for (int i = 0; i < modules.size(); i++) { if (monitor.isCanceled()) { break; } IModule module = modules.get(i); monitor.setTaskName(Messages.PHPGlobalIndexer_IndexNew + " - ../" + module.getShortName()); //$NON-NLS-1$ long l = module.getTimeStamp(); for (IModuleIndexer indexer : moduleIndexers) { final int[] counts = new int[1]; counts[0]=0; indexer.indexModule(module, new IIndexReporter() { public IElementEntry reportEntry(int category, String entryPath, IReportable value, IModule module) { counts[0] +=1; BuildHelper.sleepLoop(BuildHelper.PHP_INDEX_TYPE, counts[0]); return mainIndex.addEntry(category, entryPath, value, module, module.getBuildPath()); } }); } UnpackedElementIndex elementIndex = (UnpackedElementIndex) mainIndex.getElementIndex(module .getBuildPath()); if (elementIndex != null) { elementIndex.recordTimeStamp(module, l); } BuildHelper.sleep(BuildHelper.BUILD_FILE_TYPE); monitor.worked(1); } monitor.done(); markDirtyPathes(modules); fireChanged(0); IdeLog.logInfo(PHPEditorPlugin.getDefault(), "Indexer handleModulesAdded [took " + (System.currentTimeMillis() - start) + "ms)", null, //$NON-NLS-1$ //$NON-NLS-2$ PHPEditorPlugin.INDEXER_SCOPE); return Status.OK_STATUS; } /* * (non-Javadoc) * @see org.eclipse.core.runtime.jobs.Job#belongsTo(java.lang.Object) */ @Override public boolean belongsTo(Object family) { if (family == FAMILY_PHP_BUILD || family == ResourcesPlugin.FAMILY_AUTO_BUILD || family == ResourcesPlugin.FAMILY_MANUAL_BUILD) { return true; } return super.belongsTo(family); } }; return job; } /** * Collects module indexers from extensions. */ private void collectModuleIndexersInfo() { moduleIndexers = new ArrayList<IModuleIndexer>(); IExtensionRegistry registry = Platform.getExtensionRegistry(); IExtensionPoint ep = registry.getExtensionPoint(EXTENSION_POINT_NAME); if (ep != null) { IExtension[] extensions = ep.getExtensions(); for (int i = 0; i < extensions.length; i++) { IExtension extension = extensions[i]; IConfigurationElement[] elements = extension.getConfigurationElements(); for (int j = 0; j < elements.length; j++) { final IConfigurationElement element = elements[j]; String elementName = element.getName(); if (elementName.equals(INDEXER_ELEMENT_NAME)) { try { moduleIndexers.add(new WrapIndexer(element)); } catch (Throwable th) { IdeLog.logError(PHPEditorPlugin.getDefault(), Messages.PHPGlobalIndexer_UnableLoad + elementName + Messages.PHPGlobalIndexer_ProviderDecl, th, PHPEditorPlugin.INDEXER_SCOPE); } } } } } } static Set<IBuildPath> needSaving = new HashSet<IBuildPath>(); /** * Save the index, if needed. */ public void save() { doSave(); } private void doSave() { if (EclipseUtil.isTesting()) { return; } Set<IBuildPath> bp = new HashSet<IBuildPath>(); synchronized (needSaving) { bp.addAll(needSaving); needSaving.clear(); } for (IBuildPath p : bp) { File indexFile = getIndexFile(p); if (indexFile != null) { // long l0 = System.currentTimeMillis(); BufferedOutputStream stream = null; try { stream = new BufferedOutputStream(new FileOutputStream(indexFile)); UnpackedElementIndex elementIndex = (UnpackedElementIndex) mainIndex.getElementIndex(p); if (elementIndex != null) { IndexPersistence.store(elementIndex, new DataOutputStream(stream), p); // $codepro.audit.disable // closeWhereCreated } } catch (IOException e) { IdeLog.logError(PHPEditorPlugin.getDefault(), "Error saving the PHP index", e, PHPEditorPlugin.INDEXER_SCOPE); //$NON-NLS-1$ } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { IdeLog.logWarning(PHPEditorPlugin.getDefault(), "Error closing a stream", e, PHPEditorPlugin.INDEXER_SCOPE); //$NON-NLS-1$ } } } } } } private File getIndexFile(IBuildPath p) { if (pathes == null) { loadPathMappings(); } String handleIdentifier = p.getHandleIdentifier(); String string = pathes.get(handleIdentifier); if (string != null) { // In case the indexMappings got deleted during the session, recreate it. File parent = PHPEditorPlugin.getDefault().getStateLocation().toFile(); File pathesFile = new File(parent, "indexMappings"); //$NON-NLS-1$ if (!pathesFile.exists()) { savePathMappings(); } return new File(string); } else { int code = handleIdentifier.hashCode(); File parent = PHPEditorPlugin.getDefault().getStateLocation().toFile(); String sm = "" + code; //$NON-NLS-1$ while (pathes.get(new File(parent, sm).getAbsolutePath()) != null) { sm += 'l'; } File result = new File(parent, sm); pathes.put(handleIdentifier, result.getAbsolutePath()); savePathMappings(); return result; } } private synchronized void loadPathMappings() { pathes = new HashMap<String, String>(); File parent = PHPEditorPlugin.getDefault().getStateLocation().toFile(); File pathesFile = new File(parent, "indexMappings"); //$NON-NLS-1$ if (pathesFile.exists()) { DataInputStream dataInputStream = null; try { dataInputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(pathesFile))); int readInt = dataInputStream.readInt(); for (int a = 0; a < readInt; a++) { String handle = dataInputStream.readUTF(); String path = dataInputStream.readUTF(); // Test the path and remove any non-existing index files from the indexMappings file if (path != null && new File(path).exists()) { pathes.put(handle, path); } } } catch (IOException e) { IdeLog.logError(PHPEditorPlugin.getDefault(), "Error loading PHP index-mapping", e, PHPEditorPlugin.INDEXER_SCOPE); //$NON-NLS-1$ } finally { if (dataInputStream != null) { try { dataInputStream.close(); } catch (IOException e) { IdeLog.logWarning(PHPEditorPlugin.getDefault(), "Error closing a DataInputStream in the PHPGlobalIndexer", e, //$NON-NLS-1$ PHPEditorPlugin.INDEXER_SCOPE); } } } } } private synchronized void savePathMappings() { File parent = PHPEditorPlugin.getDefault().getStateLocation().toFile(); File pathesFile = new File(parent, "indexMappings"); //$NON-NLS-1$ DataOutputStream dataOutputStream = null; try { dataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(pathesFile))); dataOutputStream.writeInt(pathes.size()); for (String s : pathes.keySet()) { dataOutputStream.writeUTF(s); dataOutputStream.writeUTF(pathes.get(s)); } } catch (IOException e) { IdeLog.logError(PHPEditorPlugin.getDefault(), "Error saving PHP index-mapping", e, PHPEditorPlugin.INDEXER_SCOPE); //$NON-NLS-1$ } finally { if (dataOutputStream != null) { try { dataOutputStream.close(); } catch (IOException e) { IdeLog.logWarning(PHPEditorPlugin.getDefault(), "Error closing a DataInputStream in the PHPGlobalIndexer", e, PHPEditorPlugin.INDEXER_SCOPE);//$NON-NLS-1$ } } } } private Map<String, String> pathes; int modulesNum = 0; /** * Indexes local modules. */ public void indexLocalModules() { Job initializator = new Job(Messages.PHPGlobalIndexer_initializinIndex) { protected IStatus run(IProgressMonitor monitor) { List<IBuildPath> paths = BuildPathManager.getInstance().getBuildPaths(); for (IBuildPath path : paths) { if (monitor.isCanceled()) { break; } final List<IModule> modules = new ArrayList<IModule>(); UnpackedElementIndex index = new UnpackedElementIndex(); boolean loaded = false; File indexFile = getIndexFile(path); if (indexFile.exists()) { DataInputStream di = null; try { di = new DataInputStream(new BufferedInputStream(new FileInputStream(indexFile))); try { IndexPersistence.load(index, di, path); loaded = true; } catch (Exception e) { indexFile.delete(); if (!(e instanceof IOException)) { IdeLog.logError(PHPEditorPlugin.getDefault(), "Error loading the PHP index", e); //$NON-NLS-1$ } } } catch (Exception e1) { IdeLog.logError(PHPEditorPlugin.getDefault(), "Error indexing local PHP modules", e1, PHPEditorPlugin.INDEXER_SCOPE); //$NON-NLS-1$ } finally { if (di != null) { try { di.close(); } catch (IOException e) { IdeLog.logWarning(PHPEditorPlugin.getDefault(), "Error closing a DataInputStream in the PHPGlobalIndexer", e,//$NON-NLS-1$ PHPEditorPlugin.INDEXER_SCOPE); } } } } if (monitor.isCanceled()) { break; } path.addBuildPathChangeListener(buildPathChangeListener); modules.addAll(path.getModules()); if (!loaded) { mainIndex.addIndex(path, new UnpackedElementIndex()); Job addedJob = handleModulesAdded(modules); addedJob.schedule(); } else { mainIndex.addIndex(path, index); List<IModule> changed = new ArrayList<IModule>(); List<IModule> asList = Arrays.asList(index.getAllModules()); Set<IModule> all = new HashSet<IModule>(asList); for (IModule m : modules) { if (monitor.isCanceled()) { break; } if (all.contains(m)) { long timeStamp = index.getTimeStamp(m); long timeStamp2 = m.getTimeStamp(); if (timeStamp != timeStamp2) { changed.add(m); } } } if (!changed.isEmpty()) { Job changedJob = handleModulesChanged(changed); changedJob.setPriority(Job.BUILD); changedJob.schedule(); } all.removeAll(modules); if (!all.isEmpty()) { Job removedJob = handleModulesRemoved(new ArrayList<IModule>(all)); removedJob.setPriority(Job.BUILD); removedJob.schedule(); } all = new HashSet<IModule>(modules); all.removeAll(asList); if (!all.isEmpty()) { Job addedJob = handleModulesAdded(new ArrayList<IModule>(all)); addedJob.setPriority(Job.BUILD); addedJob.schedule(); } } } return Status.OK_STATUS; } }; initializator.setPriority(Job.BUILD); initializator.setRule(ResourcesPlugin.getWorkspace().getRoot()); initializator.schedule(); } /** * @param added * @param changed * @param removed */ public void processChangedBefore(final List<IModule> changed, final List<IModule> removed, final List<IDirectory> removedDirectories) { fireBeforeIndexing(changed, removed, removedDirectories); } /** * @param added * @param changed * @param removed */ public void processChangedAfter(final List<IModule> added, final List<IModule> changed, List<IModule> removed, final List<IDirectory> addedDirectories, final List<IDirectory> removedDirectories) { final List<Job> jobs = new ArrayList<Job>(); final List<Integer> sizes = new ArrayList<Integer>(); if (changed.size() != 0) { jobs.add(handleModulesChanged(changed)); sizes.add(changed.size()); } if (added.size() != 0) { jobs.add(handleModulesAdded(added)); sizes.add(added.size()); } if (removed.size() != 0) { jobs.add(handleModulesRemoved(removed)); sizes.add(removed.size()); } int size = 0; for (int i = 0; i < sizes.size(); i++) { size += sizes.get(i); } final int summSize = size; if (size != 0) { Job complexJob = new Job(Messages.PHPGlobalIndexer_PHP_Index) { @Override protected IStatus run(IProgressMonitor monitor) { monitor.beginTask(Messages.PHPGlobalIndexer_PHP_Index2, summSize); IProgressMonitor pgMonitor = getJobManager().createProgressGroup(); pgMonitor.setTaskName(Messages.PHPGlobalIndexer_PHP_Index); for (int i = 0; i < jobs.size(); i++) { if (monitor.isCanceled() || pgMonitor.isCanceled()) { break; } Job job = jobs.get(i); job.setProgressGroup(pgMonitor, sizes.get(i)); job.schedule(); try { job.join(); } catch (InterruptedException e) // $codepro.audit.disable emptyCatchClause { } } monitor.done(); pgMonitor.done(); fireChangeProcessed(); fireAfterIndexing(added, changed, addedDirectories); return Status.OK_STATUS; } }; complexJob.setPriority(Job.BUILD); complexJob.schedule(); } else { // notifying listeners about added directories fireAfterIndexing(added, changed, addedDirectories); } } public void processUnsavedModuleUpdate(Program program, IModule module, String source) { mainIndex.removeModuleEntries(module, module.getBuildPath()); UnpackedElementIndex elementIndex = (UnpackedElementIndex) mainIndex.getElementIndex(module.getBuildPath()); for (IModuleIndexer indexer : moduleIndexers) { if (indexer instanceof IProgramIndexer) { ((IProgramIndexer) indexer).indexModule(program, module, source, new IIndexReporter() { public IElementEntry reportEntry(int category, String entryPath, IReportable value, IModule module) { return mainIndex.addEntry(category, entryPath, value, module, module.getBuildPath()); } }); } } if (elementIndex != null) { // reindex it later elementIndex.recordTimeStamp(module, -1); } fireChanged(0); fireChangeProcessed(); } /** * @param modulesLeft */ private synchronized void fireChanged(int modulesLeft) { for (IIndexChangeListener l : listeners) { l.stateChanged(modulesLeft == 0, MessageFormat.format(Messages.PHPGlobalIndexer_ModulesLeft, modulesLeft)); } } /** * @param modulesLeft */ private synchronized void fireChangeProcessed() { for (IIndexChangeListener l : listeners) { l.changeProcessed(); } } /** * Fires changes. * * @param changed * - changed modules. * @param removed * - removed modules. */ private void fireBeforeIndexing(List<IModule> changed, List<IModule> removed, List<IDirectory> removedDirectories) { List<IModuleIndexListener> listeners = new ArrayList<IModuleIndexListener>(); synchronized (moduleIndexListeners) { listeners.addAll(moduleIndexListeners); } for (IModuleIndexListener listener : listeners) { listener.beforeIndexChange(changed, removed, removedDirectories); } } /** * Fires changes. * * @param added * - added. * @param changed * - changed. */ private void fireAfterIndexing(List<IModule> added, List<IModule> changed, List<IDirectory> addedDirectories) { List<IModuleIndexListener> toNotify = new ArrayList<IModuleIndexListener>(); synchronized (moduleIndexListeners) { toNotify.addAll(moduleIndexListeners); } for (IModuleIndexListener listener : toNotify) { listener.afterIndexChange(added, changed, addedDirectories); } } /** * @param listener */ public synchronized void addListener(IIndexChangeListener listener) { listeners.add(listener); } /** * @param listener */ public synchronized void removeListener(IIndexChangeListener listener) { listeners.remove(listener); } /** * @param listener */ public void addListener(IModuleIndexListener listener) { synchronized (moduleIndexListeners) { moduleIndexListeners.add(listener); } } /** * @param listener */ public synchronized void removeListener(IModuleIndexListener listener) { synchronized (moduleIndexListeners) { moduleIndexListeners.remove(listener); } } /** * Clean the index for the contained project in the given builder. * * @param project * The project to clean * @param monitor */ public void clean(IProject project, IProgressMonitor monitor) { if (project != null) { try { project.refreshLocal(IResource.DEPTH_INFINITE, monitor); } catch (CoreException e) { IdeLog.logError(PHPEditorPlugin.getDefault(), "Error cleaning the PHP index", e, PHPEditorPlugin.INDEXER_SCOPE); //$NON-NLS-1$ } BuildPathManager buildPathManager = BuildPathManager.getInstance(); IBuildPath buildPath = buildPathManager.getBuildPathByResource(project); if (buildPath != null) { File indexFile = getIndexFile(buildPath); if (indexFile != null && indexFile.exists()) { // We only delete the index file. // The content of the indexMapping file will be updated only on the next loading of the Studio. indexFile.delete(); } Set<IProject> targetProject = new HashSet<IProject>(1); Set<IProject> empty = new HashSet<IProject>(0); // TODO - SG Check the threading issue that might happen with the global indexer in the buildpathmanager // call mainIndex.removeIndex(buildPath); buildPathManager.handleChanged(empty, targetProject); buildPathManager.handleChanged(targetProject, empty); } } } /** * Build the index for the given project. Usually, this call should arrive after a clean is requested. * * @param project * @param monitor */ public void build(IProject project, IProgressMonitor monitor) { if (project != null) { BuildPathManager buildPathManager = BuildPathManager.getInstance(); final IBuildPath newBuildPath = buildPathManager.getBuildPathByResource(project); mainIndex.addIndex(newBuildPath, new UnpackedElementIndex()); Job job = handleModulesAdded(newBuildPath.getModules()); job.setPriority(Job.BUILD); job.schedule(); try { job.join(); } catch (InterruptedException e) // $codepro.audit.disable emptyCatchClause { } Job savingJob = new Job(Messages.PHPGlobalIndexer_savingIndex) { protected IStatus run(IProgressMonitor monitor) { needSaving.add(newBuildPath); doSave(); return Status.OK_STATUS; } }; EclipseUtil.setSystemForJob(savingJob); savingJob.setPriority(Job.BUILD); } } /** * Clean all the projects in the workspace. */ public void cleanAllProjects() { IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects(); for (IProject p : projects) { if (p.isAccessible()) { clean(p, new NullProgressMonitor()); } } } /** * Perform a clean on the external libraries attached to the system. * * @param monitor */ public void cleanLibraries(IProgressMonitor monitor) { if (monitor == null) { throw new IllegalArgumentException("The progress monitor should not be null"); //$NON-NLS-1$ } if (monitor.isCanceled()) return; monitor.beginTask(Messages.PHPGlobalIndexer_rebuildingLibraries, 3); BuildPathManager buildPathManager = BuildPathManager.getInstance(); List<IBuildPath> buildPaths = buildPathManager.getBuildPaths(); for (IBuildPath buildPath : buildPaths) { if (buildPath instanceof FileSystemBuildPath) { File indexFile = getIndexFile(buildPath); if (indexFile != null && indexFile.exists()) { // We only delete the index file. // The content of the indexMapping file will be updated only on the next loading of the Studio. indexFile.delete(); if (monitor.isCanceled()) return; } if (monitor.isCanceled()) return; buildPathManager.removeBuildPath(((FileSystemBuildPath) buildPath).getFile()); } if (monitor.isCanceled()) return; } monitor.worked(1); if (monitor.isCanceled()) return; buildPathManager.indexExternalLibraries(); monitor.worked(1); if (monitor.isCanceled()) return; // Clean the built-ins PHPBuiltins.getInstance().clean(monitor); monitor.done(); } }