blob: 1e432250d407e7fbfd2d39fa3c65cb354e336430 [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.docmlet.wikitext.core.ast;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.mylyn.wikitext.parser.Attributes;
import org.eclipse.mylyn.wikitext.parser.DocumentBuilder;
import org.eclipse.mylyn.wikitext.parser.LinkAttributes;
import org.eclipse.mylyn.wikitext.parser.Locator;
import org.eclipse.statet.jcommons.string.InternStringFactory;
import org.eclipse.statet.jcommons.string.StringFactory;
import org.eclipse.statet.jcommons.text.core.TextRegion;
import org.eclipse.statet.jcommons.text.core.util.HtmlUtils;
import org.eclipse.statet.docmlet.wikitext.core.markup.IMarkupLanguage;
import org.eclipse.statet.docmlet.wikitext.core.markup.IWikitextLocator;
import org.eclipse.statet.docmlet.wikitext.core.markup.MarkupParser2;
import org.eclipse.statet.docmlet.wikitext.core.source.EmbeddingAttributes;
import org.eclipse.statet.docmlet.wikitext.core.source.ImageByRefAttributes;
import org.eclipse.statet.docmlet.wikitext.core.source.LabelInfo;
import org.eclipse.statet.docmlet.wikitext.core.source.LinkByRefAttributes;
import org.eclipse.statet.docmlet.wikitext.core.source.LinkRefDefinitionAttributes;
import org.eclipse.statet.docmlet.wikitext.core.source.SourceTextBlockAttributes;
import org.eclipse.statet.ltk.core.SourceContent;
public class WikidocParser extends DocumentBuilder {
private final StringFactory labelFactory;
private IMarkupLanguage markupLanguage;
private SourceContent content;
private IWikitextLocator locator2;
private WikitextAstNode currentNode;
private boolean headingText;
private int collectText;
private Text currentText;
private final StringBuilder textBuilder= new StringBuilder();
private int depth;
private final List<List<WikitextAstNode>> childrenStack= new ArrayList<>();
private List<Embedded> embeddedList;
public WikidocParser(final StringFactory labelFactory) {
this.labelFactory= (labelFactory != null) ? labelFactory : InternStringFactory.INSTANCE;
}
public void setMarkupLanguage(final IMarkupLanguage markupLanguage) {
this.markupLanguage= markupLanguage;
}
@Override
public void setLocator(final Locator locator) {
super.setLocator(locator);
this.locator2= (IWikitextLocator) locator;
}
public void setCollectEmebeddedNodes(final boolean enable) {
this.embeddedList= (enable) ? new ArrayList<>(32) : null;
}
public List<Embedded> getEmbeddedNodes() {
return this.embeddedList;
}
public void setCollectHeadingText(final boolean enable) {
this.headingText= enable;
}
public SourceComponent parse(final SourceContent content) {
try {
if (this.embeddedList != null) {
this.embeddedList.clear();
}
this.content= content;
this.depth= -1;
final MarkupParser2 markupParser= new MarkupParser2(this.markupLanguage, this);
markupParser.disable(MarkupParser2.GENERATIVE_CONTENT);
markupParser.enable(MarkupParser2.SOURCE_STRUCT);
markupParser.parse(content, true);
return (SourceComponent) this.currentNode;
}
finally {
while (this.depth >= 0) {
final List<WikitextAstNode> list= this.childrenStack.get(this.depth);
list.clear();
this.depth--;
}
}
}
private void addChildNode(final WikitextAstNode node) {
final List<WikitextAstNode> children= this.childrenStack.get(this.depth);
children.add(node);
}
private void finishNode() {
final List<WikitextAstNode> children= this.childrenStack.get(this.depth);
if (!children.isEmpty()) {
((ContainerNode) this.currentNode).children= children.toArray(new WikitextAstNode[children.size()]);
children.clear();
}
}
private void enterNode(final WikitextAstNode node) {
if (this.depth >= 0) {
addChildNode(node);
}
this.depth++;
this.currentNode= node;
if (this.depth == this.childrenStack.size()) {
this.childrenStack.add(new ArrayList<WikitextAstNode>());
}
}
private void exitNode(final int offset) {
finishNode();
// if (offset < this.currentNode.endOffset) {
// System.out.println("endOffset: " + offset);
// }
// if (offset > this.currentNode.endOffset) {
this.currentNode.endOffset= offset;
// }
// else {
// offset= this.currentNode.endOffset;
// }
this.currentNode= this.currentNode.parent;
// if (offset > this.currentNode.endOffset) {
// this.currentNode.endOffset= offset;
// }
this.depth--;
}
private void finishText() {
if (this.currentText == null) {
return;
}
this.currentText.text= this.textBuilder.toString();
this.currentText= null;
}
@Override
public void beginDocument() {
enterNode(new SourceComponent(this.content.getText().length()));
}
@Override
public void endDocument() {
assert (this.currentNode instanceof SourceComponent);
finishText();
finishNode();
this.depth--;
}
@Override
public void beginBlock(final BlockType type, final Attributes attributes) {
finishText();
final WikitextAstNode node;
if (attributes instanceof EmbeddingAttributes) {
final EmbeddingAttributes embeddingAttributes= (EmbeddingAttributes) attributes;
final Embedded embedded;
node= embedded= new Embedded(this.currentNode,
this.locator2.getBeginOffset(), this.locator2.getEndOffset(),
embeddingAttributes.getForeignType(), embeddingAttributes.getEmbedDescr() );
if (this.embeddedList != null) {
this.embeddedList.add(embedded);
}
}
else if (attributes instanceof SourceTextBlockAttributes) {
node= new Block.TextBlock(this.currentNode, this.locator2.getBeginOffset(),
type, attributes.getId(), ((SourceTextBlockAttributes) attributes).getTextRegions() );
}
else {
node= new Block.Common(this.currentNode, this.locator2.getBeginOffset(),
type, attributes.getId() );
}
enterNode(node);
}
@Override
public void endBlock() {
finishText();
exitNode(this.locator2.getEndOffset());
}
@Override
public void beginSpan(final SpanType type, final Attributes attributes) {
finishText();
final WikitextAstNode node;
if (attributes instanceof EmbeddingAttributes) {
final EmbeddingAttributes embeddingAttributes= (EmbeddingAttributes) attributes;
final Embedded embedded;
final TextRegion contentRegion= embeddingAttributes.getContentRegion();
if (contentRegion != null
&& contentRegion.getStartOffset() > this.locator2.getBeginOffset() ) {
final Span span= new Span(this.currentNode,
this.locator2.getBeginOffset(), this.locator2.getEndOffset(),
type, attributes.getId());
node= span;
embedded= new Embedded(span,
contentRegion.getStartOffset(), contentRegion.getEndOffset(),
embeddingAttributes.getForeignType(), embeddingAttributes.getEmbedDescr() );
span.children= new WikitextAstNode[] { embedded };
}
else {
embedded= new Embedded(this.currentNode,
this.locator2.getBeginOffset(), this.locator2.getEndOffset(),
embeddingAttributes.getForeignType(), embeddingAttributes.getEmbedDescr() );
node= embedded;
}
if (this.embeddedList != null) {
this.embeddedList.add(embedded);
}
}
else if (type == SpanType.LINK && attributes instanceof LinkAttributes) {
node= createLink(attributes, null);
}
else {
node= new Span(this.currentNode,
this.locator2.getBeginOffset(), this.locator2.getEndOffset(),
type, attributes.getId());
}
enterNode(node);
}
@Override
public void endSpan() {
finishText();
exitNode(this.locator2.getEndOffset());
}
@Override
public void beginHeading(final int level, final Attributes attributes) {
finishText();
enterNode(new Heading(this.currentNode, this.locator2.getBeginOffset(), level,
attributes.getId() ));
if (this.headingText) {
this.collectText++;
}
}
@Override
public void endHeading() {
finishText();
if (this.headingText) {
this.collectText--;
}
exitNode(this.locator2.getEndOffset());
}
@Override
public void characters(final String text) {
if (this.currentText == null) {
this.currentText= new Text(this.currentNode,
this.locator2.getBeginOffset(), this.locator2.getEndOffset() );
addChildNode(this.currentText);
this.textBuilder.setLength(0);
}
else if (this.locator2.getEndOffset() > this.currentText.endOffset) {
this.currentText.endOffset= this.locator2.getEndOffset();
}
if (this.collectText > 0) {
this.textBuilder.append(text);
}
}
@Override
public void entityReference(final String entity) {
if (this.collectText > 0) {
try {
final String resovled= HtmlUtils.resolveEntity(HtmlUtils.getEntityReference(entity));
if (resovled != null) {
characters(resovled);
return;
}
}
catch (final IllegalArgumentException e) {}
}
// characters(null);
}
@Override
public void image(final Attributes attributes, final String url) {
finishText();
addChildNode(createImage(attributes, url));
}
private Image createImage(final Attributes attributes, final String src) {
final byte linkType;
final LabelInfo referenceInfo;
if (attributes instanceof ImageByRefAttributes) {
linkType= Image.SRC_BY_REF;
referenceInfo= ((ImageByRefAttributes) attributes).getReferenceLabel();
}
else {
linkType= 0;
referenceInfo= null;
}
if (linkType != 0 && referenceInfo != null) {
final Label referenceNode= createLabel(referenceInfo);
return new Image.Ref(this.currentNode,
this.locator2.getBeginOffset(), this.locator2.getEndOffset(),
linkType, referenceNode );
}
return new Image.Common(this.currentNode,
this.locator2.getBeginOffset(), this.locator2.getEndOffset(),
Image.COMMON, src );
}
@Override
public void link(final Attributes attributes, final String hrefOrHashName, final String text) {
finishText();
addChildNode(createLink(attributes, null));
}
private Link createLink(final Attributes attributes, String href) {
if (href == null && attributes instanceof LinkAttributes) {
href= ((LinkAttributes) attributes).getHref();
}
final byte linkType;
final LabelInfo referenceInfo;
if (attributes instanceof LinkRefDefinitionAttributes) {
linkType= Link.LINK_REF_DEFINITION;
referenceInfo= ((LinkRefDefinitionAttributes) attributes).getReferenceLabel();
}
else if (attributes instanceof LinkByRefAttributes) {
linkType= Link.LINK_BY_REF;
referenceInfo= ((LinkByRefAttributes) attributes).getReferenceLabel();
}
else {
linkType= 0;
referenceInfo= null;
}
if (linkType != 0 && referenceInfo != null) {
final Label referenceNode= createLabel(referenceInfo);
return new Link.Ref(this.currentNode,
this.locator2.getBeginOffset(), this.locator2.getEndOffset(),
linkType, referenceNode );
}
return new Link.Common(this.currentNode,
this.locator2.getBeginOffset(), this.locator2.getEndOffset(),
Link.COMMON, href );
}
private Label createLabel(final LabelInfo labelInfo) {
return new Label(null, labelInfo.getStartOffset(), labelInfo.getEndOffset(), labelInfo.getLabel());
}
@Override
public void imageLink(final Attributes linkAttributes, final Attributes imageAttributes, final String href,
final String imageUrl) {
finishText();
}
@Override
public void acronym(final String text, final String definition) {
finishText();
}
@Override
public void lineBreak() {
finishText();
addChildNode(new Control(this.currentNode,
this.locator2.getBeginOffset(), this.locator2.getEndOffset(),
Control.LINE_BREAK ));
}
@Override
public void charactersUnescaped(final String literal) {
finishText();
}
}