blob: b71361acc7ab00019870f21deccc15bd03ff09c9 [file] [log] [blame]
/*=============================================================================#
# 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;
}
}