Search in sources :

Example 16 with BasedSequence

use of com.vladsch.flexmark.util.sequence.BasedSequence in project flexmark-java by vsch.

the class AttributeImpl method indexOfValue.

@SuppressWarnings("WeakerAccess")
public static int indexOfValue(final CharSequence value, final CharSequence valueName, final char valueListDelimiter, final char valueNameDelimiter) {
    if (valueName.length() == 0 || value.length() == 0)
        return -1;
    if (valueListDelimiter == NUL) {
        return value.equals(valueName) ? 0 : -1;
    } else {
        int lastPos = 0;
        final BasedSequence subSeq = SubSequence.of(value);
        while (lastPos < value.length()) {
            int pos = subSeq.indexOf(valueName, lastPos);
            if (pos == -1)
                break;
            // see if it is 0 or preceded by a space, or at the end or followed by a space
            int endPos = pos + valueName.length();
            if (pos == 0 || value.charAt(pos - 1) == valueListDelimiter || valueNameDelimiter != NUL && value.charAt(pos - 1) == valueNameDelimiter) {
                if (endPos >= value.length() || value.charAt(endPos) == valueListDelimiter || valueNameDelimiter != NUL && value.charAt(endPos) == valueNameDelimiter) {
                    return pos;
                }
            }
            lastPos = endPos + 1;
        }
    }
    return -1;
}
Also used : BasedSequence(com.vladsch.flexmark.util.sequence.BasedSequence)

Example 17 with BasedSequence

use of com.vladsch.flexmark.util.sequence.BasedSequence in project flexmark-java by vsch.

the class IndentedCodeBlockParser method closeBlock.

@Override
public void closeBlock(ParserState state) {
    // trim trailing blank lines out of the block
    if (trimTrailingBlankLines) {
        int trailingBlankLines = 0;
        List<BasedSequence> lines = content.getLines();
        for (BasedSequence line : new Reverse<BasedSequence>(lines)) {
            if (!line.isBlank())
                break;
            trailingBlankLines++;
        }
        if (trailingBlankLines > 0)
            block.setContent(lines.subList(0, lines.size() - trailingBlankLines));
        else
            block.setContent(content);
    } else {
        block.setContent(content);
    }
    if (codeContentBlock) {
        CodeBlock codeBlock = new CodeBlock(block.getChars(), block.getContentLines());
        block.appendChild(codeBlock);
    }
    content = null;
}
Also used : Reverse(com.vladsch.flexmark.util.collection.iteration.Reverse) BasedSequence(com.vladsch.flexmark.util.sequence.BasedSequence)

Example 18 with BasedSequence

use of com.vladsch.flexmark.util.sequence.BasedSequence in project flexmark-java by vsch.

the class InlineParserImpl method matchLinkRef.

private ReferenceProcessorMatch matchLinkRef(Bracket opener, int startIndex, int lookAhead, int nesting) {
    if (linkRefProcessorsData.nestingIndex.length == 0)
        return null;
    ReferenceProcessorMatch match = null;
    BasedSequence textNoBang = null;
    BasedSequence textWithBang = null;
    boolean wantBang;
    int iMax = linkRefProcessorsData.processors.size();
    int startProc = linkRefProcessorsData.nestingIndex[lookAhead + nesting];
    for (int i = startProc; i < iMax; i++) {
        LinkRefProcessor linkProcessor = linkRefProcessors.get(i);
        BasedSequence nodeChars;
        if (lookAhead + nesting < linkProcessor.getBracketNestingLevel())
            break;
        wantBang = linkProcessor.getWantExclamationPrefix();
        // preview the link ref
        if (opener.image && wantBang) {
            // this one has index off by one for the leading !
            if (textWithBang == null)
                textWithBang = input.subSequence(opener.index - 1 - lookAhead, startIndex + lookAhead);
            nodeChars = textWithBang;
        } else {
            if (wantBang && opener.index >= lookAhead + 1 && input.charAt(opener.index - 1 - lookAhead) == '!') {
                if (textWithBang == null)
                    textWithBang = input.subSequence(opener.index - 1 - lookAhead, startIndex + lookAhead);
                nodeChars = textWithBang;
            } else {
                if (textNoBang == null)
                    textNoBang = input.subSequence(opener.index - lookAhead, startIndex + lookAhead);
                nodeChars = textNoBang;
            }
        }
        if (linkProcessor.isMatch(nodeChars)) {
            match = new ReferenceProcessorMatch(linkProcessor, wantBang, nodeChars);
            break;
        }
    }
    return match;
}
Also used : BasedSequence(com.vladsch.flexmark.util.sequence.BasedSequence)

Example 19 with BasedSequence

use of com.vladsch.flexmark.util.sequence.BasedSequence in project flexmark-java by vsch.

the class InlineParserImpl method parseAutolink.

/**
 * Attempt to parse an autolink (URL or email in pointy brackets).
 *
 * @return true if processed characters false otherwise
 */
@Override
public boolean parseAutolink() {
    BasedSequence m;
    if ((m = match(myParsing.EMAIL_AUTOLINK)) != null) {
        MailLink node = new MailLink(m.subSequence(0, 1), m.subSequence(1, m.length() - 1), m.subSequence(m.length() - 1, m.length()));
        appendNode(node);
        return true;
    } else if ((m = match(myParsing.AUTOLINK)) != null) {
        AutoLink node = new AutoLink(m.subSequence(0, 1), m.subSequence(1, m.length() - 1), m.subSequence(m.length() - 1, m.length()));
        appendNode(node);
        return true;
    } else {
        return false;
    }
}
Also used : BasedSequence(com.vladsch.flexmark.util.sequence.BasedSequence)

Example 20 with BasedSequence

use of com.vladsch.flexmark.util.sequence.BasedSequence in project flexmark-java by vsch.

the class InlineParserImpl method parseCloseBracket.

/**
 * Try to match close bracket against an opening in the delimiter stack. Add either a link or image, or a
 * plain [ character, to block's children. If there is a matching delimiter, removeIndex it from the delimiter stack.
 * <p>
 * Also handles custom link ref processing
 *
 * @return true
 */
protected boolean parseCloseBracket() {
    index++;
    int startIndex = index;
    int nestedBrackets;
    boolean hadBang = false;
    // look through stack of delimiters for a [ or ![
    Bracket opener = this.lastBracket;
    if (opener == null) {
        // No matching opener, just return a literal.
        appendText(input.subSequence(index - 1, index));
        return true;
    }
    if (!opener.allowed) {
        // Matching opener but it's not allowed, just return a literal.
        appendText(input.subSequence(index - 1, index));
        removeLastBracket();
        return true;
    }
    nestedBrackets = 0;
    // Check to see if we have a link/image
    BasedSequence dest = null;
    BasedSequence title = null;
    BasedSequence ref = null;
    boolean isLinkOrImage = false;
    boolean refIsBare = false;
    ReferenceProcessorMatch linkRefProcessorMatch = null;
    boolean refIsDefined = false;
    BasedSequence linkOpener = BasedSequence.NULL;
    BasedSequence linkCloser = BasedSequence.NULL;
    BasedSequence bareRef = BasedSequence.NULL;
    BasedSequence imageUrlContent = null;
    // Inline link?
    int preSpaceIndex = index;
    // May need to skip spaces
    if (options.spaceInLinkElements && peek() == ' ') {
        sp();
    }
    if (peek() == '(') {
        int savedIndex = index;
        linkOpener = input.subSequence(index, index + 1);
        index++;
        spnl();
        if ((dest = parseLinkDestination()) != null) {
            if (options.parseMultiLineImageUrls && opener.image && !dest.startsWith("<") && dest.endsWith("?") && spnlUrl()) {
                // possible multi-line image url
                int contentStart = index;
                int contentEnd = contentStart;
                BasedSequence multiLineTitle;
                while (true) {
                    sp();
                    multiLineTitle = parseLinkTitle();
                    if (multiLineTitle != null)
                        sp();
                    if (peek() == ')') {
                        linkCloser = input.subSequence(index, index + 1);
                        index++;
                        imageUrlContent = input.subSequence(contentStart, contentEnd);
                        title = multiLineTitle;
                        isLinkOrImage = true;
                        break;
                    }
                    BasedSequence restOfLine = toEOL();
                    if (restOfLine == null)
                        break;
                    contentEnd = index;
                }
            } else {
                spnl();
                // title needs a whitespace before
                if (myParsing.WHITESPACE.matcher(input.subSequence(index - 1, index)).matches()) {
                    title = parseLinkTitle();
                    spnl();
                }
                // test for spaces in url making it invalid, otherwise anything else goes
                if (peek() == ')') {
                    linkCloser = input.subSequence(index, index + 1);
                    index++;
                    isLinkOrImage = true;
                } else {
                    // back out, no match
                    index = savedIndex;
                }
            }
        } else {
            index = savedIndex;
        }
    } else {
        index = preSpaceIndex;
    }
    if (!isLinkOrImage) {
        // as something else, like a wiki link
        if (!options.matchLookaheadFirst) {
            linkRefProcessorMatch = matchLinkRef(opener, startIndex, 0, nestedBrackets);
        }
        if (linkRefProcessorMatch != null) {
        // have a match, then no look ahead for next matches
        } else {
            // need to figure out max nesting we should test based on what is max processor desire and max available
            // nested inner ones are always only []
            int maxWanted = linkRefProcessorsData.maxNesting;
            int maxAvail = 0;
            if (maxWanted > nestedBrackets) {
                // need to see what is available
                Bracket nested = opener;
                while (nested.previous != null && nested.index == nested.previous.index + 1 && peek(maxAvail) == ']') {
                    nested = nested.previous;
                    maxAvail++;
                    if (maxAvail + nestedBrackets == maxWanted || nested.image)
                        break;
                }
            }
            for (int nesting = maxAvail + 1; nesting-- > 0; ) {
                linkRefProcessorMatch = matchLinkRef(opener, startIndex, nesting, nestedBrackets);
                if (linkRefProcessorMatch != null) {
                    if (nesting > 0) {
                        while (nesting-- > 0) {
                            index++;
                            lastBracket.node.unlink();
                            removeLastBracket();
                        }
                        opener = lastBracket;
                    }
                    break;
                }
            }
        }
        if (linkRefProcessorMatch == null) {
            // See if there's a link label
            int beforeLabel = index;
            int labelLength = parseLinkLabel();
            if (labelLength > 2) {
                ref = input.subSequence(beforeLabel, beforeLabel + labelLength);
            } else if (!opener.bracketAfter) {
                // Empty or missing second label can only be a reference if there's no unescaped bracket in it.
                bareRef = input.subSequence(beforeLabel, beforeLabel + labelLength);
                if (opener.image) {
                    // this one has index off by one for the leading !
                    ref = input.subSequence(opener.index - 1, startIndex);
                } else {
                    ref = input.subSequence(opener.index, startIndex);
                }
                refIsBare = true;
            }
            if (ref != null) {
                String normalizedLabel = Escaping.normalizeReferenceChars(ref, true);
                if (referenceRepository.containsKey(normalizedLabel)) {
                    BasedSequence sequence = input.subSequence(opener.index, startIndex);
                    boolean containsLinks = containsLinkRefs(refIsBare ? ref : sequence, opener.node.getNext(), true);
                    isLinkOrImage = !containsLinks;
                    refIsDefined = true;
                } else {
                    // need to test if we are cutting in the middle of some other delimiters matching, if we are not then we will make this into a tentative
                    if (!opener.isStraddling(ref)) {
                        if (!refIsBare && peek() == '[') {
                            int beforeNext = index;
                            int nextLength = parseLinkLabel();
                            if (nextLength > 0) {
                                // not bare and not defined and followed by another [], roll back to before the label and make it just text
                                index = beforeLabel;
                            } else {
                                // undefined ref, create a tentative one but only if does not contain any other link refs
                                boolean containsLinks = containsLinkRefs(ref, opener.node.getNext(), null);
                                if (!containsLinks) {
                                    refIsBare = true;
                                    isLinkOrImage = true;
                                }
                            }
                        } else {
                            // undefined ref, bare or followed by empty [], create a tentative link ref but only if does not contain any other link refs
                            boolean containsLinks = containsLinkRefs(ref, opener.node.getNext(), null);
                            if (!containsLinks) {
                                isLinkOrImage = true;
                            }
                        }
                    }
                }
            }
        }
    }
    if (isLinkOrImage || linkRefProcessorMatch != null) {
        // If we got here, open is a potential opener
        // Flush text now. We don't need to worry about combining it with adjacent text nodes, as we'll wrap it in a
        // link or image node.
        flushTextNode();
        Node insertNode;
        boolean isImage = opener.image;
        if (linkRefProcessorMatch != null) {
            if (!linkRefProcessorMatch.wantExclamation && isImage) {
                appendText(input.subSequence(opener.index - 1, opener.index));
                opener.node.setChars(opener.node.getChars().subSequence(1));
                // opener.image = false;
                isImage = false;
            }
            insertNode = linkRefProcessorMatch.processor.createNode(linkRefProcessorMatch.nodeChars);
        } else {
            insertNode = ref != null ? isImage ? new ImageRef() : new LinkRef() : isImage ? new Image() : new Link();
        }
        {
            Node node = opener.node.getNext();
            while (node != null) {
                Node next = node.getNext();
                insertNode.appendChild(node);
                node = next;
            }
        }
        if (linkRefProcessorMatch != null) {
            // may need to adjust children's text because some characters were part of the processor's opener/closer
            if (insertNode.hasChildren()) {
                final BasedSequence original = insertNode.getChildChars();
                final BasedSequence text = linkRefProcessorMatch.processor.adjustInlineText(document, insertNode);
                // may need to remove some delimiters if they span across original and changed text boundary or if now they are outside text boundary
                Delimiter delimiter = lastDelimiter;
                while (delimiter != null) {
                    Delimiter prevDelimiter = delimiter.previous;
                    final BasedSequence delimiterChars = delimiter.getInput().subSequence(delimiter.getStartIndex(), delimiter.getEndIndex());
                    if (original.containsAllOf(delimiterChars)) {
                        if (!text.containsAllOf(delimiterChars) || !linkRefProcessorMatch.processor.allowDelimiters(delimiterChars, document, insertNode)) {
                            // remove it
                            removeDelimiterKeepNode(delimiter);
                        }
                    }
                    delimiter = prevDelimiter;
                }
                if (!text.containsAllOf(original)) {
                    // now need to truncate child text
                    for (Node node : insertNode.getChildren()) {
                        final BasedSequence nodeChars = node.getChars();
                        if (text.containsSomeOf(nodeChars)) {
                            if (!text.containsAllOf(nodeChars)) {
                                // truncate the contents to intersection of node's chars and adjusted chars
                                BasedSequence chars = text.intersect(nodeChars);
                                node.setChars(chars);
                            }
                        } else {
                            // remove the node
                            node.unlink();
                        }
                    }
                }
            }
        }
        appendNode(insertNode);
        if (insertNode instanceof RefNode) {
            // set up the parts
            RefNode refNode = (RefNode) insertNode;
            refNode.setReferenceChars(ref);
            if (refIsDefined)
                refNode.setDefined(true);
            if (!refIsBare) {
                refNode.setTextChars(input.subSequence(opener.index, startIndex));
            } else if (!bareRef.isEmpty()) {
                refNode.setTextOpeningMarker(bareRef.subSequence(0, 1));
                refNode.setTextClosingMarker(bareRef.endSequence(1));
            }
            insertNode.setCharsFromContent();
        } else if (insertNode instanceof InlineLinkNode) {
            // set dest and title
            InlineLinkNode inlineLinkNode = (InlineLinkNode) insertNode;
            inlineLinkNode.setUrlChars(dest);
            inlineLinkNode.setTitleChars(title);
            inlineLinkNode.setLinkOpeningMarker(linkOpener);
            inlineLinkNode.setLinkClosingMarker(linkCloser);
            inlineLinkNode.setTextChars(isImage ? input.subSequence(opener.index - 1, startIndex) : input.subSequence(opener.index, startIndex));
            if (imageUrlContent != null) {
                ((Image) insertNode).setUrlContent(imageUrlContent);
            }
            insertNode.setCharsFromContent();
        }
        // Process delimiters such as emphasis inside link/image
        processDelimiters(opener.previousDelimiter);
        Node toRemove = opener.node;
        removeLastBracket();
        if (linkRefProcessorMatch != null) {
            linkRefProcessorMatch.processor.updateNodeElements(document, insertNode);
        }
        // Links within links are not allowed. We found this link, so there can be no other link around it.
        if (insertNode instanceof Link) {
            Bracket bracket = this.lastBracket;
            while (bracket != null) {
                if (!bracket.image) {
                    // Disallow link opener. It will still get matched, but will not result in a link.
                    bracket.allowed = false;
                }
                bracket = bracket.previous;
            }
            // collapse any link refs contained in this link, they are duds, link takes precedence
            // TODO: add a test to see if all link refs should be collapsed or just undefined ones
            collapseLinkRefChildren(insertNode, null);
        } else if (insertNode instanceof RefNode) {
            // have a link ref, collapse to text any tentative ones contained in it, they are duds
            collapseLinkRefChildren(insertNode, true);
        }
        toRemove.unlink();
        return true;
    } else {
        // no link or image
        index = startIndex;
        appendText(input.subSequence(index - 1, index));
        removeLastBracket();
        return true;
    }
}
Also used : BasedSequence(com.vladsch.flexmark.util.sequence.BasedSequence)

Aggregations

BasedSequence (com.vladsch.flexmark.util.sequence.BasedSequence)91 Matcher (java.util.regex.Matcher)13 Node (com.vladsch.flexmark.ast.Node)6 ArrayList (java.util.ArrayList)5 MacroClose (com.vladsch.flexmark.ext.xwiki.macros.MacroClose)3 ReplacedTextMapper (com.vladsch.flexmark.util.sequence.ReplacedTextMapper)3 Text (com.vladsch.flexmark.ast.Text)2 AttributesNode (com.vladsch.flexmark.ext.attributes.AttributesNode)2 FootnoteBlock (com.vladsch.flexmark.ext.footnotes.FootnoteBlock)2 Macro (com.vladsch.flexmark.ext.xwiki.macros.Macro)2 Pair (com.vladsch.flexmark.util.Pair)2 RepeatedCharSequence (com.vladsch.flexmark.util.sequence.RepeatedCharSequence)2 Block (com.vladsch.flexmark.ast.Block)1 BulletListItem (com.vladsch.flexmark.ast.BulletListItem)1 Link (com.vladsch.flexmark.ast.Link)1 ListItem (com.vladsch.flexmark.ast.ListItem)1 NodeIterator (com.vladsch.flexmark.ast.NodeIterator)1 OrderedListItem (com.vladsch.flexmark.ast.OrderedListItem)1 Parsing (com.vladsch.flexmark.ast.util.Parsing)1 TextCollectingVisitor (com.vladsch.flexmark.ast.util.TextCollectingVisitor)1