| /*=============================================================================# |
| # 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; |
| } |
| |
| } |