| /*=============================================================================# |
| # Copyright (c) 2007, 2020 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.internal.r.core.sourcemodel; |
| |
| import java.util.List; |
| |
| import com.ibm.icu.text.DecimalFormat; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| |
| import org.eclipse.statet.jcommons.collections.ImCollections; |
| import org.eclipse.statet.jcommons.string.InternStringCache; |
| import org.eclipse.statet.jcommons.string.StringFactory; |
| import org.eclipse.statet.jcommons.text.core.input.OffsetStringParserInput; |
| import org.eclipse.statet.jcommons.text.core.input.StringParserInput; |
| import org.eclipse.statet.jcommons.text.core.input.TextParserInput; |
| |
| import org.eclipse.statet.ltk.ast.core.AstInfo; |
| import org.eclipse.statet.ltk.core.SourceContent; |
| import org.eclipse.statet.ltk.issues.core.ProblemRequestor; |
| import org.eclipse.statet.ltk.model.core.IModelManager; |
| import org.eclipse.statet.ltk.model.core.elements.ISourceUnitModelInfo; |
| import org.eclipse.statet.ltk.model.core.impl.SourceModelStamp; |
| import org.eclipse.statet.r.core.model.IRModelInfo; |
| import org.eclipse.statet.r.core.model.IRSourceUnit; |
| import org.eclipse.statet.r.core.model.RChunkElement; |
| import org.eclipse.statet.r.core.model.RModel; |
| import org.eclipse.statet.r.core.model.RProblemReporter; |
| import org.eclipse.statet.r.core.model.RSuModelContainer; |
| import org.eclipse.statet.r.core.rsource.ast.RAstNode; |
| import org.eclipse.statet.r.core.rsource.ast.RScanner; |
| import org.eclipse.statet.r.core.rsource.ast.RoxygenScanner; |
| import org.eclipse.statet.r.core.rsource.ast.SourceComponent; |
| |
| |
| /** |
| * Worker for r model manager |
| */ |
| public class RReconciler { |
| |
| |
| private static final boolean LOG_TIME= false; |
| |
| protected static class Data { |
| |
| public final RSuModelContainer adapter; |
| public final SourceContent content; |
| |
| public AstInfo ast; |
| |
| public IRModelInfo oldModel; |
| public IRModelInfo newModel; |
| |
| public Data(final RSuModelContainer adapter, final IProgressMonitor monitor) { |
| this.adapter= adapter; |
| this.content= adapter.getParseContent(monitor); |
| } |
| |
| } |
| |
| |
| private final RModelManager rManager; |
| protected boolean stop= false; |
| |
| private final Object raLock= new Object(); |
| private final StringFactory raAstStringCache; |
| private final StringParserInput raInput= new StringParserInput(0x1000); |
| private final RoxygenScanner raRoxygenScanner; |
| |
| private final Object rmLock= new Object(); |
| private final SourceAnalyzer rmScopeAnalyzer; |
| |
| private final Object rpLock= new Object(); |
| private final RProblemReporter rpReporter; |
| |
| |
| public RReconciler(final RModelManager manager) { |
| this.rManager= manager; |
| this.raAstStringCache= new InternStringCache(0x20); |
| this.raRoxygenScanner= new RoxygenScanner(this.raAstStringCache); |
| this.rmScopeAnalyzer= new SourceAnalyzer(); |
| this.rpReporter= new RProblemReporter(); |
| } |
| |
| |
| /** for editor reconciling */ |
| public void reconcile(final RSuModelContainer adapter, final int flags, |
| final IProgressMonitor monitor) { |
| final IRSourceUnit su= adapter.getSourceUnit(); |
| final int type= (su.getModelTypeId().equals(RModel.R_TYPE_ID) ? su.getElementType() : 0); |
| if (type == 0) { |
| return; |
| } |
| |
| final Data data= new Data(adapter, monitor); |
| if (data.content == null) { |
| return; |
| } |
| |
| synchronized (this.raLock) { |
| if (this.stop || monitor.isCanceled()) { |
| return; |
| } |
| updateAst(data, monitor); |
| } |
| |
| if (this.stop || monitor.isCanceled() |
| || (flags & 0xf) < IModelManager.MODEL_FILE) { |
| return; |
| } |
| |
| synchronized (this.rmLock) { |
| if (this.stop || monitor.isCanceled()) { |
| return; |
| } |
| final boolean updated= updateModel(data); |
| |
| if (this.stop) { |
| return; |
| } |
| |
| if (updated) { |
| this.rManager.getEventJob().addUpdate(su, data.oldModel, data.newModel); |
| } |
| } |
| |
| if ((flags & IModelManager.RECONCILE) != 0 && data.newModel != null) { |
| if (this.stop || monitor.isCanceled()) { |
| return; |
| } |
| |
| ProblemRequestor problemRequestor= null; |
| synchronized (this.rpLock) { |
| if (!this.stop && !monitor.isCanceled() |
| && data.newModel == adapter.getCurrentModel() ) { |
| problemRequestor= adapter.createProblemRequestor(); |
| if (problemRequestor != null) { |
| this.rpReporter.run(su, data.content, |
| (RAstNode) data.ast.getRoot(), problemRequestor ); |
| } |
| } |
| } |
| if (problemRequestor != null) { |
| problemRequestor.finish(); |
| } |
| } |
| } |
| |
| public IRModelInfo reconcile(final IRSourceUnit su, final ISourceUnitModelInfo modelInfo, |
| final List<? extends RChunkElement> chunkElements, final List<? extends SourceComponent> inlineNodes, |
| final int level, final IProgressMonitor monitor) { |
| synchronized (this.rmLock) { |
| return updateModel(su, modelInfo, chunkElements, inlineNodes); |
| } |
| } |
| |
| protected final void updateAst(final Data data, final IProgressMonitor monitor) { |
| final SourceModelStamp stamp= new SourceModelStamp(data.content.getStamp()); |
| |
| data.ast= data.adapter.getCurrentAst(); |
| if (data.ast != null && !stamp.equals(data.ast.getStamp())) { |
| data.ast= null; |
| } |
| |
| if (data.ast == null) { |
| final long startAst; |
| final long stopAst; |
| startAst= System.nanoTime(); |
| |
| final TextParserInput input; |
| if (data.content.getStartOffset() != 0) { |
| input= new OffsetStringParserInput(data.content.getText(), data.content.getStartOffset()); |
| } |
| else { |
| input= this.raInput.reset(data.content.getText()); |
| } |
| |
| final RScanner scanner= new RScanner(AstInfo.LEVEL_MODEL_DEFAULT, |
| this.raAstStringCache ); |
| scanner.setCommentLevel(100); |
| final SourceComponent sourceComponent= scanner.scanSourceRange( |
| input.init(data.content.getStartOffset(), data.content.getEndOffset()), |
| null ); |
| data.ast= new AstInfo(scanner.getAstLevel(), stamp, sourceComponent); |
| |
| stopAst= System.nanoTime(); |
| |
| this.raRoxygenScanner.init( |
| input.init(data.content.getStartOffset(), data.content.getEndOffset())); |
| this.raRoxygenScanner.update(sourceComponent); |
| |
| if (LOG_TIME) { |
| System.out.println(this.raAstStringCache.toString()); |
| System.out.println("RReconciler/createAST : " + DecimalFormat.getInstance().format(stopAst - startAst)); //$NON-NLS-1$ |
| } |
| |
| synchronized (data.adapter) { |
| data.adapter.setAst(data.ast); |
| } |
| } |
| } |
| |
| protected final boolean updateModel(final Data data) { |
| data.newModel= data.adapter.getCurrentModel(); |
| if (data.newModel != null && !data.ast.getStamp().equals(data.newModel.getStamp())) { |
| data.newModel= null; |
| } |
| |
| if (data.newModel == null) { |
| final long startModel; |
| final long stopModel; |
| startModel= System.nanoTime(); |
| |
| final IRModelInfo model= this.rmScopeAnalyzer.createModel(data.adapter.getSourceUnit(), data.ast); |
| final boolean isOK= (model != null); |
| |
| stopModel= System.nanoTime(); |
| |
| if (LOG_TIME) { |
| System.out.println("RReconciler/createMODEL : " + DecimalFormat.getInstance().format(stopModel - startModel)); |
| } |
| |
| if (isOK) { |
| synchronized (data.adapter) { |
| data.oldModel= data.adapter.getCurrentModel(); |
| data.adapter.setModel(model); |
| } |
| data.newModel= model; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private IRModelInfo updateModel(final IRSourceUnit su, final ISourceUnitModelInfo modelInfo, |
| final List<? extends RChunkElement> chunkElements, |
| final List<? extends SourceComponent> inlineNodes) { |
| IRModelInfo model; |
| try { |
| final AstInfo ast= modelInfo.getAst(); |
| this.rmScopeAnalyzer.beginChunkSession(su, ast); |
| for (final RChunkElement chunkElement : chunkElements) { |
| final List<SourceComponent> rootNodes; |
| { final Object source= chunkElement.getAdapter(SourceComponent.class); |
| if (source instanceof SourceComponent) { |
| rootNodes= ImCollections.newList((SourceComponent) source); |
| } |
| else if (source instanceof List<?>) { |
| rootNodes= (List<SourceComponent>) source; |
| } |
| else { |
| continue; |
| } |
| } |
| this.rmScopeAnalyzer.processChunk(chunkElement, rootNodes); |
| } |
| for (final SourceComponent inlineNode : inlineNodes) { |
| this.rmScopeAnalyzer.processInlineNode(inlineNode); |
| } |
| } |
| finally { |
| model= this.rmScopeAnalyzer.stopChunkSession(); |
| } |
| return model; |
| } |
| |
| void stop() { |
| this.stop= true; |
| } |
| |
| } |