/** * 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 18/09/2005 */ package com.python.pydev.analysis.builder; import java.io.File; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.text.IDocument; import org.python.pydev.builder.PyDevBuilderVisitor; import org.python.pydev.builder.VisitorMemo; import org.python.pydev.core.IMiscConstants; import org.python.pydev.core.IModule; import org.python.pydev.core.IPythonNature; import org.python.pydev.core.log.Log; import org.python.pydev.editor.codecompletion.revisited.modules.AbstractModule; import org.python.pydev.logging.DebugSettings; import org.python.pydev.parser.jython.SimpleNode; import org.python.pydev.plugin.nature.PythonNature; import org.python.pydev.shared_core.model.ISimpleNode; import org.python.pydev.shared_core.parsing.ChangedParserInfoForObservers; import org.python.pydev.shared_core.parsing.ErrorParserInfoForObservers; import org.python.pydev.shared_core.parsing.IParserObserver; import org.python.pydev.shared_core.parsing.IParserObserver3; import org.python.pydev.shared_core.structure.Tuple; /** * Observes changes to the parser and when OK, it'll ask for the analysis of the module reparsed. * * @author Fabio */ public class AnalysisParserObserver implements IParserObserver, IParserObserver3 { /** * @author fabioz * */ private final class AnalyzeLaterJob extends Job { private final IPythonNature nature; private ChangedParserInfoForObservers info; private SimpleNode root; private IFile fileAdapter; private boolean force; private int rescheduleTimes = 15; private AnalyzeLaterJob(String name, ChangedParserInfoForObservers info, SimpleNode root, IFile fileAdapter, boolean force, IPythonNature nature) { super(name); this.nature = nature; this.info = info; this.root = root; this.fileAdapter = fileAdapter; this.force = force; } @Override protected IStatus run(IProgressMonitor monitor) { rescheduleTimes--; try { if (!nature.isOkToUse()) { if (rescheduleTimes >= 0) { this.schedule(200); } } else { analyze(info, root, fileAdapter, force, nature, false); } } catch (Throwable e) { Log.log(e); } return Status.OK_STATUS; } } public static final String ANALYSIS_PARSER_OBSERVER_FORCE = IMiscConstants.ANALYSIS_PARSER_OBSERVER_FORCE; @Override public void parserChanged(final ChangedParserInfoForObservers info) { if (DebugSettings.DEBUG_ANALYSIS_REQUESTS) { System.out.println("AnalysisParserObserver: parserChanged"); } final SimpleNode root = (SimpleNode) info.root; if (info.file == null) { return; } IFile fileAdapter = null; if (info.file instanceof IFile) { fileAdapter = (IFile) info.file; } if (fileAdapter == null) { fileAdapter = info.file.getAdapter(IFile.class); if (fileAdapter == null) { return; } } boolean force = false; boolean forceAnalyzeInThisThread = false; if (info.argsToReparse != null && info.argsToReparse.length > 0) { if (info.argsToReparse[0] instanceof Tuple) { Tuple t = (Tuple) info.argsToReparse[0]; if (t.o1 instanceof String && t.o2 instanceof Boolean) { if (t.o1.equals(ANALYSIS_PARSER_OBSERVER_FORCE)) { //if this message is passed, it will decide whether we will force the analysis or not force = (Boolean) t.o2; } if (t.o1.equals(IMiscConstants.ANALYSIS_PARSER_OBSERVER_FORCE_IN_THIS_THREAD)) { //if this message is passed, it will decide whether we will force the analysis or not forceAnalyzeInThisThread = force = (Boolean) t.o2; } } } } // Note: whenever there's a successful reparse, analyze the files. // create the module final IPythonNature nature = PythonNature.getPythonNature(fileAdapter); if (nature == null) { return; } // don't analyze it if we're still not 'all set' if (!nature.isOkToUse()) { Job job = new AnalyzeLaterJob("Analyze later", info, root, fileAdapter, force, nature); job.schedule(100); return; } analyze(info, root, fileAdapter, force, nature, forceAnalyzeInThisThread); } private static void analyze(ChangedParserInfoForObservers info, SimpleNode root, IFile fileAdapter, boolean force, IPythonNature nature, boolean forceAnalyzeInThisThread) { if (!nature.startRequests()) { return; } IModule module; try { //we visit external because we must index them String moduleName = nature.resolveModuleOnlyInProjectSources(fileAdapter, true); if (moduleName == null) { AnalysisRunner.deleteMarkers(fileAdapter); return; // we only analyze resources that are in the pythonpath } String file = fileAdapter.getRawLocation().toOSString(); module = AbstractModule.createModule(root, new File(file), moduleName, nature); } catch (Exception e) { Log.log(e); //Not much we can do about it. return; } finally { nature.endRequests(); } //visit it AnalysisBuilderVisitor visitor = new AnalysisBuilderVisitor(); visitor.memo = new VisitorMemo(); visitor.memo.put(PyDevBuilderVisitor.IS_FULL_BUILD, false); visitor.memo.put(PyDevBuilderVisitor.DOCUMENT_TIME, info.documentMillisTime); visitor.visitingWillStart(new NullProgressMonitor(), false, null); try { visitor.doVisitChangedResource(nature, fileAdapter, info.doc, null, module, new NullProgressMonitor(), force, AnalysisBuilderRunnable.ANALYSIS_CAUSE_PARSER, info.documentMillisTime, forceAnalyzeInThisThread); } finally { visitor.visitingEnded(new NullProgressMonitor()); } } @Override public void parserChanged(ISimpleNode root, IAdaptable resource, IDocument doc, long docModificationStamp) { throw new RuntimeException("As it uses IParserObserver2, this interface should not be asked for."); } @Override public void parserError(Throwable error, IAdaptable file, IDocument doc) { //ignore errors... } @Override public void parserError(ErrorParserInfoForObservers info) { //ignore } }