com.vladsch.flexmark.ext.wikilink.WikiNode Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of flexmark-ext-wikilink Show documentation
Show all versions of flexmark-ext-wikilink Show documentation
flexmark-java extension parsing and rendering wiki links
package com.vladsch.flexmark.ext.wikilink;
import com.vladsch.flexmark.ast.LinkRefDerived;
import com.vladsch.flexmark.util.ast.DoNotDecorate;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.ast.NodeVisitor;
import com.vladsch.flexmark.util.ast.TextContainer;
import com.vladsch.flexmark.util.misc.CharPredicate;
import com.vladsch.flexmark.util.sequence.BasedSequence;
import com.vladsch.flexmark.util.sequence.Escaping;
import com.vladsch.flexmark.util.sequence.ReplacedTextMapper;
import com.vladsch.flexmark.util.sequence.builder.ISequenceBuilder;
import org.jetbrains.annotations.NotNull;
public class WikiNode extends Node implements DoNotDecorate, TextContainer, LinkRefDerived {
final public static char SEPARATOR_CHAR = '|';
protected BasedSequence openingMarker = BasedSequence.NULL;
protected BasedSequence link = BasedSequence.NULL;
protected BasedSequence pageRef = BasedSequence.NULL;
protected BasedSequence anchorMarker = BasedSequence.NULL;
protected BasedSequence anchorRef = BasedSequence.NULL;
protected BasedSequence textSeparatorMarker = BasedSequence.NULL;
protected BasedSequence text = BasedSequence.NULL;
protected BasedSequence closingMarker = BasedSequence.NULL;
protected final boolean linkIsFirst;
@NotNull
@Override
public BasedSequence[] getSegments() {
if (linkIsFirst) {
return new BasedSequence[] {
openingMarker,
link,
pageRef,
anchorMarker,
anchorRef,
textSeparatorMarker,
text,
closingMarker
};
} else {
return new BasedSequence[] {
openingMarker,
text,
textSeparatorMarker,
link,
pageRef,
anchorMarker,
anchorRef,
closingMarker
};
}
}
@Override
public void getAstExtra(@NotNull StringBuilder out) {
if (linkIsFirst) {
segmentSpanChars(out, openingMarker, "linkOpen");
segmentSpanChars(out, text, "text");
segmentSpanChars(out, textSeparatorMarker, "textSep");
segmentSpanChars(out, link, "link");
if (pageRef.isNotNull()) segmentSpanChars(out, pageRef, "pageRef");
if (anchorMarker.isNotNull()) segmentSpanChars(out, anchorMarker, "anchorMarker");
if (anchorRef.isNotNull()) segmentSpanChars(out, anchorRef, "anchorRef");
segmentSpanChars(out, closingMarker, "linkClose");
} else {
segmentSpanChars(out, openingMarker, "linkOpen");
segmentSpanChars(out, link, "link");
if (pageRef.isNotNull()) segmentSpanChars(out, pageRef, "pageRef");
if (anchorMarker.isNotNull()) segmentSpanChars(out, anchorMarker, "anchorMarker");
if (anchorRef.isNotNull()) segmentSpanChars(out, anchorRef, "anchorRef");
segmentSpanChars(out, textSeparatorMarker, "textSep");
segmentSpanChars(out, text, "text");
segmentSpanChars(out, closingMarker, "linkClose");
}
}
public boolean isLinkIsFirst() {
return linkIsFirst;
}
public WikiNode(boolean linkIsFirst) {
this.linkIsFirst = linkIsFirst;
}
/**
* @return true if this node will be rendered as text because it depends on a reference which is not defined.
*/
@Override
public boolean isTentative() {
return false;
}
public WikiNode(BasedSequence chars, boolean linkIsFirst, boolean allowAnchors, boolean canEscapePipe, boolean canEscapeAnchor) {
super(chars);
this.linkIsFirst = linkIsFirst;
setLinkChars(chars, allowAnchors, canEscapePipe, canEscapeAnchor);
}
public BasedSequence getOpeningMarker() {
return openingMarker;
}
public void setOpeningMarker(BasedSequence openingMarker) {
this.openingMarker = openingMarker;
}
public BasedSequence getPageRef() {
return pageRef;
}
public void setPageRef(BasedSequence pageRef) {
this.pageRef = pageRef;
}
public BasedSequence getTextSeparatorMarker() {
return textSeparatorMarker;
}
public void setTextSeparatorMarker(BasedSequence textSeparatorMarker) {
this.textSeparatorMarker = textSeparatorMarker;
}
public BasedSequence getText() {
return text;
}
public void setText(BasedSequence text) {
this.text = text;
}
public BasedSequence getClosingMarker() {
return closingMarker;
}
public void setClosingMarker(BasedSequence closingMarker) {
this.closingMarker = closingMarker;
}
public BasedSequence getAnchorMarker() {
return anchorMarker;
}
public void setAnchorMarker(BasedSequence anchorMarker) {
this.anchorMarker = anchorMarker;
}
public BasedSequence getAnchorRef() {
return anchorRef;
}
public void setAnchorRef(BasedSequence anchorRef) {
this.anchorRef = anchorRef;
}
public BasedSequence getLink() {
return link;
}
public void setLink(BasedSequence linkChars, boolean allowAnchors, boolean canEscapeAnchor) {
// now parse out the # from the link
this.link = linkChars;
if (!allowAnchors) {
this.pageRef = linkChars;
} else {
int pos = -1;
do {
pos = linkChars.indexOf('#', pos + 1);
} while (pos != -1 && canEscapeAnchor && (pos > 0 && linkChars.charAt(pos - 1) == '\\' && (linkChars.subSequence(0, pos).countTrailing(CharPredicate.BACKSLASH) & 1) == 1));
if (pos < 0) {
this.pageRef = linkChars;
} else {
this.pageRef = linkChars.subSequence(0, pos);
this.anchorMarker = linkChars.subSequence(pos, pos + 1);
this.anchorRef = linkChars.subSequence(pos + 1);
}
}
}
public void setLinkChars(BasedSequence linkChars, boolean allowAnchors, boolean canEscapePipe, boolean canEscapeAnchor) {
int length = linkChars.length();
final int start = this instanceof WikiImage ? 3 : 2;
openingMarker = linkChars.subSequence(0, start);
closingMarker = linkChars.subSequence(length - 2, length);
int pos;
if (linkIsFirst) {
pos = linkChars.length() - 2;
do {
pos = linkChars.lastIndexOf(SEPARATOR_CHAR, pos - 1);
} while (pos != -1 && canEscapePipe && (pos > 0 && linkChars.charAt(pos - 1) == '\\' && (linkChars.subSequence(0, pos).countTrailing(CharPredicate.BACKSLASH) & 1) == 1));
} else {
pos = -1;
do {
pos = linkChars.indexOf(SEPARATOR_CHAR, pos + 1);
} while (pos != -1 && canEscapePipe && (pos > 0 && linkChars.charAt(pos - 1) == '\\' && (linkChars.subSequence(0, pos).countTrailing(CharPredicate.BACKSLASH) & 1) == 1));
}
BasedSequence link;
if (pos < 0) {
link = linkChars.subSequence(start, length - 2);
} else {
textSeparatorMarker = linkChars.subSequence(pos, pos + 1);
if (linkIsFirst) {
link = linkChars.subSequence(start, pos);
text = linkChars.subSequence(pos + 1, length - 2);
} else {
text = linkChars.subSequence(start, pos);
link = linkChars.subSequence(pos + 1, length - 2);
}
}
setLink(link, allowAnchors, canEscapeAnchor);
if (text.isNull() && allowAnchors && !anchorMarker.isNull()) {
// have anchor ref, remove it from text
text = pageRef;
}
}
@Override
public boolean collectText(ISequenceBuilder extends ISequenceBuilder, BasedSequence>, BasedSequence> out, int flags, NodeVisitor nodeVisitor) {
int urlType = flags & F_LINK_TEXT_TYPE;
BasedSequence text;
switch (urlType) {
case F_LINK_PAGE_REF:
text = getPageRef();
break;
case F_LINK_ANCHOR:
text = getAnchorRef();
break;
case F_LINK_URL:
text = getLink();
break;
case F_LINK_NODE_TEXT:
text = BasedSequence.NULL; // not used
break;
default:
case F_LINK_TEXT:
return true;
}
if (urlType == F_LINK_NODE_TEXT) {
out.append(getChars());
} else {
ReplacedTextMapper textMapper = new ReplacedTextMapper(text);
BasedSequence unescaped = Escaping.unescape(text, textMapper);
out.append(unescaped);
}
return false;
}
}