| /*=============================================================================# |
| # Copyright (c) 2009, 2018 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.docmlet.tex.core.model; |
| |
| import java.io.StringWriter; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.Platform; |
| |
| import org.eclipse.statet.jcommons.string.InternStringCache; |
| import org.eclipse.statet.jcommons.text.core.input.StringParserInput; |
| |
| import org.eclipse.statet.docmlet.tex.core.ITexCoreAccess; |
| import org.eclipse.statet.docmlet.tex.core.ast.Embedded; |
| import org.eclipse.statet.docmlet.tex.core.ast.LtxParser; |
| import org.eclipse.statet.docmlet.tex.core.ast.SourceComponent; |
| import org.eclipse.statet.docmlet.tex.core.commands.TexCommand; |
| import org.eclipse.statet.docmlet.tex.core.model.ILtxModelInfo; |
| import org.eclipse.statet.docmlet.tex.core.model.ILtxSuModelContainerEmbeddedExtension; |
| import org.eclipse.statet.docmlet.tex.core.model.ITexSourceUnit; |
| import org.eclipse.statet.docmlet.tex.core.model.LtxProblemReporter; |
| import org.eclipse.statet.docmlet.tex.core.model.LtxSuModelContainer; |
| import org.eclipse.statet.docmlet.tex.core.parser.NowebLtxLexer; |
| import org.eclipse.statet.ltk.ast.core.AstInfo; |
| import org.eclipse.statet.ltk.ast.core.util.AstPrinter; |
| 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.impl.SourceModelStamp; |
| |
| |
| public class LtxReconciler { |
| |
| |
| protected static class Data { |
| |
| public final LtxSuModelContainer<?> adapter; |
| public final ILtxSuModelContainerEmbeddedExtension embedded; |
| public final SourceContent content; |
| |
| private final ITexCoreAccess texCoreAccess; |
| |
| public int parseOffset; |
| |
| public AstInfo ast; |
| |
| public Map<String, TexCommand> customCommands; |
| public Map<String, TexCommand> customEnvs; |
| |
| public ILtxModelInfo oldModel; |
| public ILtxModelInfo newModel; |
| |
| public Data(final LtxSuModelContainer<?> adapter, final IProgressMonitor monitor) { |
| this.adapter= adapter; |
| this.embedded= (adapter instanceof ILtxSuModelContainerEmbeddedExtension) ? |
| (ILtxSuModelContainerEmbeddedExtension) adapter : null; |
| this.content= adapter.getParseContent(monitor); |
| this.texCoreAccess= adapter.getSourceUnit().getTexCoreAccess(); |
| } |
| |
| } |
| |
| |
| private static final boolean DEBUG_LOG_AST= Boolean.parseBoolean( |
| Platform.getDebugOption("org.eclipse.statet.docmlet.tex/debug/Reconciler/logAst") ); //$NON-NLS-1$ |
| |
| |
| private final LtxModelManager texManager; |
| protected boolean stop= false; |
| |
| private final Object raLock= new Object(); |
| private final StringParserInput raInput= new StringParserInput(0x1000); |
| private final NowebLtxLexer raLexer= new NowebLtxLexer(); |
| private final LtxParser raParser= new LtxParser(this.raLexer, new InternStringCache(0x20)); |
| private AstPrinter raDebugAstPrinter; |
| |
| private final Object rmLock= new Object(); |
| private final SourceAnalyzer rmSourceAnalyzer= new SourceAnalyzer(); |
| |
| private final Object rpLock= new Object(); |
| private final LtxProblemReporter rpReporter= new LtxProblemReporter(); |
| |
| |
| public LtxReconciler(final LtxModelManager manager) { |
| this.texManager= manager; |
| } |
| |
| |
| public void reconcile(final LtxSuModelContainer<?> adapter, final int flags, |
| final IProgressMonitor monitor) { |
| final ITexSourceUnit su= adapter.getSourceUnit(); |
| final Data data= new Data(adapter, monitor); |
| if (data.content == null) { |
| return; |
| } |
| |
| synchronized (this.raLock) { |
| if (this.stop || monitor.isCanceled()) { |
| return; |
| } |
| updateAst(data, flags, 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, flags, monitor); |
| |
| if (updated) { |
| this.texManager.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, data.newModel, |
| problemRequestor, flags, monitor); |
| |
| if (data.embedded != null) { |
| data.embedded.reportEmbeddedProblems(data.content, data.newModel, |
| problemRequestor, flags, monitor); |
| } |
| } |
| } |
| if (problemRequestor != null) { |
| problemRequestor.finish(); |
| } |
| } |
| } |
| } |
| |
| protected final void updateAst(final Data data, final int flags, |
| 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 SourceComponent sourceNode; |
| this.raInput.reset(data.content.getText()); |
| |
| if (data.embedded != null) { |
| this.raLexer.setNowebType(data.embedded.getNowebType()); |
| this.raParser.setCollectEmebeddedNodes(true); |
| |
| sourceNode= this.raParser.parse(this.raInput.init(), data.texCoreAccess.getTexCommandSet()); |
| data.customCommands= this.raParser.getCustomCommandMap(); |
| data.customEnvs= this.raParser.getCustomEnvMap(); |
| |
| final List<Embedded> embeddedNodes= this.raParser.getEmbeddedNodes(); |
| |
| data.embedded.reconcileEmbeddedAst(data.content, embeddedNodes, flags, monitor); |
| } |
| else { |
| this.raLexer.setNowebType(null); |
| this.raParser.setCollectEmebeddedNodes(false); |
| |
| sourceNode= this.raParser.parse(this.raInput.init(), data.texCoreAccess.getTexCommandSet()); |
| } |
| data.ast= new AstInfo(1, stamp, sourceNode); |
| |
| if (DEBUG_LOG_AST) { |
| if (this.raDebugAstPrinter == null) { |
| this.raDebugAstPrinter= new AstPrinter(new StringWriter()); |
| } |
| final StringWriter out= (StringWriter) this.raDebugAstPrinter.getWriter(); |
| out.getBuffer().setLength(0); |
| try { |
| out.append("====\nLtx AST:\n"); //$NON-NLS-1$ |
| this.raDebugAstPrinter.print(data.ast.getRoot(), data.content.getText()); |
| out.append("====\n"); //$NON-NLS-1$ |
| System.out.println(out.toString()); |
| } |
| catch (final Exception e) { |
| System.out.println(out.toString()); |
| e.printStackTrace(); |
| } |
| } |
| |
| synchronized (data.adapter) { |
| data.adapter.setAst(data.ast); |
| } |
| } |
| } |
| |
| protected final boolean updateModel(final Data data, final int flags, |
| final IProgressMonitor monitor) { |
| data.newModel= data.adapter.getCurrentModel(); |
| if (data.newModel != null && !data.ast.getStamp().equals(data.newModel.getStamp())) { |
| data.newModel= null; |
| } |
| |
| if (data.newModel == null) { |
| final LtxSourceUnitModelInfo model= this.rmSourceAnalyzer.createModel(data.adapter.getSourceUnit(), |
| data.content.getText(), data.ast, data.customCommands, data.customEnvs ); |
| final boolean isOK= (model != null); |
| |
| if (data.embedded != null) { |
| data.embedded.reconcileEmbeddedModel(data.content, model, |
| this.rmSourceAnalyzer.getEmbeddedItems(), flags, monitor ); |
| } |
| |
| if (isOK) { |
| synchronized (data.adapter) { |
| data.oldModel= data.adapter.getCurrentModel(); |
| data.adapter.setModel(model); |
| } |
| data.newModel= model; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void stop() { |
| this.stop= true; |
| } |
| |
| } |