blob: 2706f15be2432b6923496cc7eb8629211fc13ab7 [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.wikitext.core.model;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.text.AbstractDocument;
import org.eclipse.statet.jcommons.string.InternStringCache;
import org.eclipse.statet.docmlet.tex.core.model.TexModel;
import org.eclipse.statet.docmlet.wikitext.core.IWikitextCoreAccess;
import org.eclipse.statet.docmlet.wikitext.core.MarkupSourceModelStamp;
import org.eclipse.statet.docmlet.wikitext.core.WikitextCore;
import org.eclipse.statet.docmlet.wikitext.core.ast.Embedded;
import org.eclipse.statet.docmlet.wikitext.core.ast.SourceComponent;
import org.eclipse.statet.docmlet.wikitext.core.ast.WikidocParser;
import org.eclipse.statet.docmlet.wikitext.core.ast.WikitextAstInfo;
import org.eclipse.statet.docmlet.wikitext.core.markup.IMarkupLanguage;
import org.eclipse.statet.docmlet.wikitext.core.markup.IMarkupLanguageManager1;
import org.eclipse.statet.docmlet.wikitext.core.model.EmbeddingReconcileItem;
import org.eclipse.statet.docmlet.wikitext.core.model.IWikidocModelInfo;
import org.eclipse.statet.docmlet.wikitext.core.model.IWikidocSuModelContainerEmbeddedExtension;
import org.eclipse.statet.docmlet.wikitext.core.model.IWikitextSourceUnit;
import org.eclipse.statet.docmlet.wikitext.core.model.WikidocSuModelContainer;
import org.eclipse.statet.docmlet.wikitext.core.source.MarkupLanguageDocumentSetupParticipant;
import org.eclipse.statet.docmlet.wikitext.core.source.extdoc.IExtdocMarkupLanguage;
import org.eclipse.statet.ltk.ast.core.util.AstPrinter;
import org.eclipse.statet.ltk.core.LTK;
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.IWorkspaceSourceUnit;
import org.eclipse.statet.yaml.core.model.YamlModel;
public class WikidocReconciler {
protected static class Data {
public final WikidocSuModelContainer<?> adapter;
public final IWikidocSuModelContainerEmbeddedExtension embedded;
public final SourceContent content;
private final IWikitextCoreAccess coreAccess;
public int parseOffset;
public WikitextAstInfo ast;
public IWikidocModelInfo oldModel;
public IWikidocModelInfo newModel;
public Data(final WikidocSuModelContainer<?> adapter, final IProgressMonitor monitor) {
this.adapter= adapter;
this.embedded= (adapter instanceof IWikidocSuModelContainerEmbeddedExtension) ?
(IWikidocSuModelContainerEmbeddedExtension) adapter : null;
this.content= adapter.getParseContent(monitor);
this.coreAccess= adapter.getSourceUnit().getWikitextCoreAccess();
}
}
private static final boolean DEBUG_LOG_AST= Boolean.parseBoolean(
Platform.getDebugOption("org.eclipse.statet.docmlet.wikitext/debug/Reconciler/logAst") ); //$NON-NLS-1$
private final Map<IMarkupLanguage, IMarkupLanguage> languages= new HashMap<>();
private final WikitextModelManager modelManager;
protected boolean stop= false;
private final IMarkupLanguageManager1 markupLanguageManager;
private final Object f1AstLock= new Object();
private final WikidocParser f1Parser= new WikidocParser(new InternStringCache(0x20));
private AstPrinter f1DebugAstPrinter;
private final Object f2ModelLock= new Object();
private final SourceAnalyzer f2SourceAnalyzer= new SourceAnalyzer();
private final Object f3ReportLock= new Object();
// private final WikidocProblemReporter f3ProblemReporter= new WikidocProblemReporter();
private IWikidocSuModelContainerEmbeddedExtension yamlExt;
private IWikidocSuModelContainerEmbeddedExtension ltxExt;
public WikidocReconciler(final WikitextModelManager manager) {
this.modelManager= manager;
this.markupLanguageManager= WikitextCore.getMarkupLanguageManager();
this.f1Parser.setCollectHeadingText(true);
}
public void reconcile(final WikidocSuModelContainer<?> adapter, final int flags,
final IProgressMonitor monitor) {
final IWikitextSourceUnit su= adapter.getSourceUnit();
final Data data= new Data(adapter, monitor);
if (data.content == null) {
return;
}
synchronized (this.f1AstLock) {
if (this.stop || monitor.isCanceled()) {
return;
}
updateAst(data, flags, monitor);
}
if (this.stop || monitor.isCanceled()
|| (flags & 0xf) < IModelManager.MODEL_FILE) {
return;
}
synchronized (this.f2ModelLock) {
if (this.stop || monitor.isCanceled()) {
return;
}
final boolean updated= updateModel(data, flags, monitor);
if (updated) {
this.modelManager.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.f3ReportLock) {
if (!this.stop && !monitor.isCanceled()
&& data.newModel == adapter.getCurrentModel() ) {
problemRequestor= adapter.createProblemRequestor();
if (problemRequestor != null) {
if (data.ast.getMarkupLanguage() instanceof IExtdocMarkupLanguage) {
((IExtdocMarkupLanguage) data.ast.getMarkupLanguage()).getProblemReporter()
.run(su, data.content, data.newModel,
problemRequestor, flags, monitor );
}
if (data.ast.getEmbeddedTypes().contains(YamlModel.YAML_TYPE_ID)) {
this.yamlExt.reportEmbeddedProblems(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 IMarkupLanguage getMarkupLanguage(final Data data, final IProgressMonitor monitor) {
final IWikitextSourceUnit su= data.adapter.getSourceUnit();
IMarkupLanguage markupLanguage= null;
if (su.getWorkingContext() == LTK.EDITOR_CONTEXT) {
final AbstractDocument document= su.getDocument(monitor);
markupLanguage= MarkupLanguageDocumentSetupParticipant.getMarkupLanguage(document, su.getDocumentContentInfo().getPartitioning());
}
if (markupLanguage == null && su instanceof IWorkspaceSourceUnit) {
markupLanguage= this.markupLanguageManager.getLanguage((IFile) su.getResource(),
null, true );
}
if (markupLanguage == null) {
return null;
}
synchronized (this.languages) {
IMarkupLanguage internal= this.languages.get(markupLanguage);
if (internal == null) {
internal= markupLanguage.clone("Reconciler", markupLanguage.getMode());
this.languages.put(internal, internal);
}
return internal;
}
}
protected final void updateAst(final Data data, final int flags,
final IProgressMonitor monitor) {
final IMarkupLanguage markupLanguage= getMarkupLanguage(data, monitor);
if (markupLanguage == null) {
throw new UnsupportedOperationException("Markup language is missing.");
}
final MarkupSourceModelStamp stamp= new MarkupSourceModelStamp(data.content.getStamp(), markupLanguage);
data.ast= (WikitextAstInfo) data.adapter.getCurrentAst();
if (data.ast != null && !stamp.equals(data.ast.getStamp())) {
data.ast= null;
}
if (data.ast == null) {
final SourceComponent sourceNode;
// final StringParseInput input= new StringParseInput(data.content.text);
//
this.f1Parser.setMarkupLanguage(markupLanguage);
this.f1Parser.setCollectEmebeddedNodes(true);
sourceNode= this.f1Parser.parse(data.content);
final List<Embedded> embeddedNodes= this.f1Parser.getEmbeddedNodes();
final Map<String, Boolean> embeddedTypes= new IdentityHashMap<>(4);
for (final Embedded node : embeddedNodes) {
embeddedTypes.put(node.getForeignTypeId(), Boolean.TRUE);
}
if (embeddedTypes.containsKey(YamlModel.YAML_TYPE_ID)) {
if (this.yamlExt == null) {
this.yamlExt= new YamlReconcilerExtension();
}
this.yamlExt.reconcileEmbeddedAst(data.content, embeddedNodes, markupLanguage,
flags, monitor );
}
if (embeddedTypes.containsKey(TexModel.LTX_TYPE_ID)) {
if (this.ltxExt == null) {
this.ltxExt= new LtxReconcilerExtension();
}
this.ltxExt.reconcileEmbeddedAst(data.content, embeddedNodes, markupLanguage,
flags, monitor );
}
if (data.embedded != null) {
data.embedded.reconcileEmbeddedAst(data.content, embeddedNodes, markupLanguage,
flags, monitor );
}
data.ast= new WikitextAstInfo(1, stamp, sourceNode, markupLanguage,
embeddedTypes.keySet() );
if (DEBUG_LOG_AST) {
if (this.f1DebugAstPrinter == null) {
this.f1DebugAstPrinter= new AstPrinter(new StringWriter());
}
final StringWriter out= (StringWriter) this.f1DebugAstPrinter.getWriter();
out.getBuffer().setLength(0);
try {
out.append("====\nWikidoc AST:\n"); //$NON-NLS-1$
this.f1DebugAstPrinter.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 WikidocSourceUnitModelInfo model= this.f2SourceAnalyzer.createModel(data.adapter.getSourceUnit(),
data.content.getText(), data.ast );
final boolean isOK= (model != null);
final List<EmbeddingReconcileItem> embeddedItems= this.f2SourceAnalyzer.getEmbeddedItems();
if (data.ast.getEmbeddedTypes().contains(YamlModel.YAML_TYPE_ID)) {
this.yamlExt.reconcileEmbeddedModel(data.content, model, embeddedItems,
flags, monitor );
}
if (data.ast.getEmbeddedTypes().contains(TexModel.LTX_TYPE_ID)) {
this.ltxExt.reconcileEmbeddedModel(data.content, model, embeddedItems,
flags, monitor );
}
if (data.embedded != null) {
data.embedded.reconcileEmbeddedModel(data.content, model, embeddedItems,
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;
}
}