Search in sources :

Example 56 with BasedSequence

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

the class TableParagraphPreProcessor method preProcessBlock.

@Override
public int preProcessBlock(Paragraph block, ParserState state) {
    InlineParser inlineParser = state.getInlineParser();
    ArrayList<BasedSequence> tableLines = new ArrayList<BasedSequence>();
    int separatorLineNumber = -1;
    BasedSequence separatorLine = null;
    int blockIndent = block.getLineIndent(0);
    BasedSequence captionLine = null;
    int i = 0;
    for (BasedSequence rowLine : block.getContentLines()) {
        int rowNumber = tableLines.size();
        // too many header rows
        if (separatorLineNumber == -1 && rowNumber > options.maxHeaderRows)
            return 0;
        if (rowLine.indexOf('|') < 0) {
            if (separatorLineNumber == -1)
                return 0;
            if (options.withCaption) {
                BasedSequence trimmed = rowLine.trim();
                if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
                    captionLine = trimmed;
                }
            }
            break;
        }
        BasedSequence fullRowLine = block.getLineIndent(rowNumber) <= blockIndent ? rowLine.trimEOL() : rowLine.baseSubSequence(rowLine.getStartOffset() - (block.getLineIndent(rowNumber) - blockIndent), rowLine.getEndOffset() - rowLine.eolLength());
        if (separatorLineNumber == -1) {
            if (rowNumber >= options.minHeaderRows && TABLE_HEADER_SEPARATOR.matcher(rowLine).matches()) {
                // must start with | or cell, whitespace means its not a separator line
                if (fullRowLine.charAt(0) != ' ' && fullRowLine.charAt(0) != '\t' || rowLine.charAt(0) != '|') {
                    separatorLineNumber = rowNumber;
                    separatorLine = rowLine;
                } else if (fullRowLine.charAt(0) == ' ' || fullRowLine.charAt(0) == '\t') {
                    block.setHasTableSeparator(true);
                }
            }
        } else if (options.multiLineRows) {
        // TODO: need to do inline parsing here to determine whether we have open element constructs which need to include the next line
        }
        tableLines.add(rowLine);
        i++;
    }
    if (separatorLineNumber == -1)
        return 0;
    ArrayList<TableRow> tableRows = new ArrayList<TableRow>();
    for (BasedSequence rowLine : tableLines) {
        int rowNumber = tableRows.size();
        BasedSequence fullRowLine = block.getLineIndent(rowNumber) <= blockIndent ? rowLine.trimEOL() : rowLine.baseSubSequence(rowLine.getStartOffset() - (block.getLineIndent(rowNumber) - blockIndent), rowLine.getEndOffset() - rowLine.eolLength());
        TableRow tableRow = new TableRow(fullRowLine);
        int tableRowNumber;
        List<Node> sepList;
        if (rowNumber == separatorLineNumber) {
            sepList = inlineParser.parseCustom(fullRowLine, tableRow, separatorCharacters, pipeNodeMap);
            tableRowNumber = 0;
        } else {
            sepList = inlineParser.parseCustom(fullRowLine, tableRow, pipeCharacters, pipeNodeMap);
            if (rowNumber < separatorLineNumber)
                tableRowNumber = rowNumber + 1;
            else
                tableRowNumber = rowNumber - separatorLineNumber;
        }
        if (sepList == null) {
            if (rowNumber <= separatorLineNumber)
                return 0;
            break;
        }
        tableRow.setRowNumber(tableRowNumber);
        tableRows.add(tableRow);
    }
    // table is done, could be earlier than the lines tested earlier, may need to truncate lines
    Block tableBlock = new TableBlock(tableLines.subList(0, tableRows.size()));
    Node section = new TableHead(tableLines.get(0).subSequence(0, 0));
    tableBlock.appendChild(section);
    List<TableCell.Alignment> alignments = parseAlignment(separatorLine);
    int rowNumber = 0;
    int separatorColumns = alignments.size();
    for (TableRow tableRow : tableRows) {
        if (rowNumber == separatorLineNumber) {
            section.setCharsFromContent();
            section = new TableSeparator();
            tableBlock.appendChild(section);
        } else if (rowNumber == separatorLineNumber + 1) {
            section.setCharsFromContent();
            section = new TableBody();
            tableBlock.appendChild(section);
        }
        boolean firstCell = true;
        int cellCount = 0;
        NodeIterator nodes = new NodeIterator(tableRow.getFirstChild());
        TableRow newTableRow = new TableRow(tableRow.getChars());
        newTableRow.setRowNumber(tableRow.getRowNumber());
        int accumulatedSpanOffset = 0;
        while (nodes.hasNext()) {
            if (cellCount >= separatorColumns && options.discardExtraColumns) {
                if (options.headerSeparatorColumnMatch && rowNumber < separatorLineNumber) {
                    // header/separator mismatch
                    return 0;
                }
                break;
            }
            TableCell tableCell = new TableCell();
            if (firstCell && nodes.peek() instanceof TableColumnSeparator) {
                Node columnSep = nodes.next();
                tableCell.setOpeningMarker(columnSep.getChars());
                columnSep.unlink();
                firstCell = false;
            }
            TableCell.Alignment alignment = cellCount + accumulatedSpanOffset < separatorColumns ? alignments.get(cellCount + accumulatedSpanOffset) : null;
            tableCell.setHeader(rowNumber < separatorLineNumber);
            tableCell.setAlignment(alignment);
            // take all until separator or end of iterator
            while (nodes.hasNext()) {
                if (nodes.peek() instanceof TableColumnSeparator)
                    break;
                tableCell.appendChild(nodes.next());
            }
            // accumulate closers, and optional spans
            BasedSequence closingMarker = null;
            int span = 1;
            while (nodes.hasNext()) {
                if (!(nodes.peek() instanceof TableColumnSeparator))
                    break;
                if (closingMarker == null) {
                    closingMarker = nodes.next().getChars();
                    if (!options.columnSpans)
                        break;
                } else {
                    BasedSequence nextSep = nodes.peek().getChars();
                    if (!closingMarker.isContinuedBy(nextSep))
                        break;
                    closingMarker = closingMarker.spliceAtEnd(nextSep);
                    nodes.next().unlink();
                    span++;
                }
            }
            accumulatedSpanOffset += span - 1;
            if (closingMarker != null)
                tableCell.setClosingMarker(closingMarker);
            tableCell.setChars(tableCell.getChildChars());
            // TODO: Add option to keep cell whitespace, if yes, then convert it to text and merge adjacent text nodes
            if (options.trimCellWhitespace)
                tableCell.trimWhiteSpace();
            else
                tableCell.mergeWhiteSpace();
            tableCell.setText(tableCell.getChildChars());
            tableCell.setCharsFromContent();
            tableCell.setSpan(span);
            newTableRow.appendChild(tableCell);
            cellCount++;
        }
        if (options.headerSeparatorColumnMatch && rowNumber < separatorLineNumber && cellCount < separatorColumns) {
            // no match
            return 0;
        }
        while (options.appendMissingColumns && cellCount < separatorColumns) {
            TableCell tableCell = new TableCell();
            tableCell.setHeader(rowNumber < separatorLineNumber);
            tableCell.setAlignment(alignments.get(cellCount));
            newTableRow.appendChild(tableCell);
            cellCount++;
        }
        newTableRow.setCharsFromContent();
        section.appendChild(newTableRow);
        rowNumber++;
    }
    section.setCharsFromContent();
    if (section instanceof TableSeparator) {
        TableBody tableBody = new TableBody(section.getChars().subSequence(section.getChars().length()));
        tableBlock.appendChild(tableBody);
    }
    // Add caption if the option is enabled
    if (captionLine != null) {
        TableCaption caption = new TableCaption(captionLine.subSequence(0, 1), captionLine.subSequence(1, captionLine.length() - 1), captionLine.subSequence(captionLine.length() - 1));
        inlineParser.parse(caption.getText(), caption);
        caption.setCharsFromContent();
        tableBlock.appendChild(caption);
    }
    tableBlock.setCharsFromContent();
    block.insertBefore(tableBlock);
    state.blockAdded(tableBlock);
    return tableBlock.getChars().length();
}
Also used : NodeIterator(com.vladsch.flexmark.ast.NodeIterator) BasedSequence(com.vladsch.flexmark.util.sequence.BasedSequence) Node(com.vladsch.flexmark.ast.Node) InlineParser(com.vladsch.flexmark.parser.InlineParser) Block(com.vladsch.flexmark.ast.Block)

Example 57 with BasedSequence

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

the class TableParagraphPreProcessor method parseAlignment.

private static List<TableCell.Alignment> parseAlignment(BasedSequence separatorLine) {
    List<BasedSequence> parts = split(separatorLine, false, false);
    List<TableCell.Alignment> alignments = new ArrayList<TableCell.Alignment>();
    for (BasedSequence part : parts) {
        BasedSequence trimmed = part.trim();
        boolean left = trimmed.startsWith(":");
        boolean right = trimmed.endsWith(":");
        TableCell.Alignment alignment = getAlignment(left, right);
        alignments.add(alignment);
    }
    return alignments;
}
Also used : BasedSequence(com.vladsch.flexmark.util.sequence.BasedSequence)

Example 58 with BasedSequence

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

the class TableParagraphPreProcessor method split.

private static List<BasedSequence> split(BasedSequence input, boolean columnSpans, boolean wantPipes) {
    BasedSequence line = input.trim();
    int lineLength = line.length();
    List<BasedSequence> segments = new ArrayList<BasedSequence>();
    if (line.startsWith("|")) {
        if (wantPipes)
            segments.add(line.subSequence(0, 1));
        line = line.subSequence(1, lineLength);
        lineLength--;
    }
    boolean escape = false;
    int lastPos = 0;
    int cellChars = 0;
    for (int i = 0; i < lineLength; i++) {
        char c = line.charAt(i);
        if (escape) {
            escape = false;
            cellChars++;
        } else {
            switch(c) {
                case '\\':
                    escape = true;
                    // Removing the escaping '\' is handled by the inline parser later, so add it to cell
                    cellChars++;
                    break;
                case '|':
                    if (!columnSpans || lastPos < i)
                        segments.add(line.subSequence(lastPos, i));
                    if (wantPipes)
                        segments.add(line.subSequence(i, i + 1));
                    lastPos = i + 1;
                    cellChars = 0;
                    break;
                default:
                    cellChars++;
            }
        }
    }
    if (cellChars > 0) {
        segments.add(line.subSequence(lastPos, lineLength));
    }
    return segments;
}
Also used : BasedSequence(com.vladsch.flexmark.util.sequence.BasedSequence)

Example 59 with BasedSequence

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

the class TocLevelsOptionParser method parseOption.

@SuppressWarnings("unchecked")
@Override
public Pair<TocOptions, List<ParsedOption<TocOptions>>> parseOption(BasedSequence optionText, TocOptions options, MessageProvider provider) {
    // may have levels
    TocOptions result = options;
    BasedSequence[] levelsOptionValue = optionText.split(',');
    final ParserParams parserParams = new ParserParams();
    if (provider == null)
        provider = MessageProvider.DEFAULT;
    int newLevels = 0;
    int i = 0;
    final MessageProvider finalProvider = provider;
    Computable<Integer, BasedSequence> convertWithMessage = new Computable<Integer, BasedSequence>() {

        @Override
        public Integer compute(BasedSequence option) {
            try {
                return option.isEmpty() ? null : Integer.parseInt(option.toString());
            } catch (Exception ignored) {
                parserParams.add(new ParserMessage(option, ParsedOptionStatus.ERROR, finalProvider.message(KEY_OPTION_0_VALUE_1_NOT_INTEGER, OPTION_0_VALUE_1_NOT_INTEGER, myOptionName, option)));
                parserParams.skip = true;
                return null;
            }
        }
    };
    for (BasedSequence option : levelsOptionValue) {
        BasedSequence[] optionRange = option.split('-', 2, BasedSequence.SPLIT_TRIM_PARTS);
        Integer rangeStart;
        Integer rangeEnd;
        parserParams.skip = false;
        if (optionRange.length == 2) {
            rangeStart = convertWithMessage.compute(optionRange[0]);
            rangeEnd = convertWithMessage.compute(optionRange[1]);
            if (rangeStart == null)
                rangeStart = 1;
            if (rangeEnd == null)
                rangeEnd = 6;
        } else {
            rangeStart = convertWithMessage.compute(optionRange[0]);
            rangeEnd = rangeStart;
        }
        if (!parserParams.skip) {
            if (rangeStart == null) {
                parserParams.add(new ParserMessage(option, ParsedOptionStatus.IGNORED, finalProvider.message(KEY_OPTION_0_VALUE_1_TRUNCATED_TO_EMPTY_RANGE, OPTION_0_VALUE_1_TRUNCATED_TO_EMPTY_RANGE, myOptionName, option)));
            } else {
                if (rangeEnd < rangeStart) {
                    int tmp = rangeStart;
                    rangeStart = rangeEnd;
                    rangeEnd = tmp;
                }
                if (rangeEnd < 1 || rangeStart > 6) {
                    if (rangeStart == (int) rangeEnd) {
                        parserParams.add(new ParserMessage(option, ParsedOptionStatus.IGNORED, provider.message(KEY_OPTION_0_VALUE_1_NOT_IN_RANGE, OPTION_0_VALUE_1_NOT_IN_RANGE, myOptionName, option)));
                    } else {
                        parserParams.add(new ParserMessage(option, ParsedOptionStatus.WARNING, finalProvider.message(KEY_OPTION_0_VALUE_1_TRUNCATED_TO_EMPTY_RANGE, OPTION_0_VALUE_1_TRUNCATED_TO_EMPTY_RANGE, myOptionName, option)));
                    }
                } else {
                    int wasStart = rangeStart;
                    int wasEnd = rangeEnd;
                    rangeStart = Utils.minLimit(rangeStart, 1);
                    rangeEnd = Utils.maxLimit(rangeEnd, 6);
                    if (wasStart != rangeStart || wasEnd != rangeEnd) {
                        parserParams.add(new ParserMessage(option, ParsedOptionStatus.WEAK_WARNING, finalProvider.message(KEY_OPTION_0_VALUE_1_TRUNCATED_TO_RANGE_2, OPTION_0_VALUE_1_TRUNCATED_TO_RANGE_2, myOptionName, option, rangeStart + ", " + rangeEnd)));
                    }
                    for (int b = rangeStart; b <= rangeEnd; b++) newLevels = newLevels | (1 << b);
                }
            }
        }
        i++;
    }
    if (newLevels != 0)
        result = result.withLevels(newLevels);
    return new Pair<TocOptions, List<ParsedOption<TocOptions>>>(result, (List<ParsedOption<TocOptions>>) Collections.<ParsedOption<TocOptions>>singletonList(new ParsedOption(optionText, this, parserParams.status, parserParams.messages)));
}
Also used : BasedSequence(com.vladsch.flexmark.util.sequence.BasedSequence) Computable(com.vladsch.flexmark.util.Computable) Pair(com.vladsch.flexmark.util.Pair)

Example 60 with BasedSequence

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

the class SpecExampleNodeRenderer method render.

private void render(SpecExampleBlock node, NodeRendererContext context, HtmlWriter html) {
    // here we should probably prettify and display section, number and options
    switch(options.renderAs) {
        case DEFINITION_LIST:
            html.tagVoidLine("hr");
            html.tag("dl").indent();
            html.tag("dt").text(SpecReader.EXAMPLE_KEYWORD);
            if (node.getSection().isNotNull() || node.getNumberSeparator().isNotNull() || node.getNumber().isNotNull()) {
                html.text(" ").text(node.getSection().toString()).text(": ").text(node.getNumber().toString());
            }
            if (node.getOptionsKeyword().isNotNull() || node.getOptionsOpeningMarker().isNotNull() || node.getOptions().isNotNull() || node.getOptionsClosingMarker().isNotNull()) {
                String optionsText = "";
                BasedSequence trimmed = node.getOptions().trim(BasedSequence.WHITESPACE_NBSP_CHARS);
                if (!trimmed.isEmpty()) {
                    BasedSequence[] optionsList = trimmed.split(',', 0, BasedSequence.SPLIT_TRIM_SKIP_EMPTY);
                    DelimitedBuilder out = new DelimitedBuilder(", ");
                    optionsText = out.appendAll(optionsList).getAndClear();
                }
                html.text(" options(").text(optionsText).text(")");
            }
            html.tag("/dt").line();
            context.renderChildren(node);
            html.unIndent().tag("/dl").line();
            break;
        case SECTIONS:
            html.tagVoidLine("hr");
            if (node.getSection().isNotNull() || node.getNumberSeparator().isNotNull() || node.getNumber().isNotNull()) {
                html.tag("h5").text(node.getSection().toString()).text(": ").text(node.getNumber().toString()).tag("/h5").line();
            }
            context.renderChildren(node);
            break;
        case FENCED_CODE:
        default:
            render(node.getContentChars(), "text", context, html);
            break;
    }
}
Also used : BasedSequence(com.vladsch.flexmark.util.sequence.BasedSequence) DelimitedBuilder(com.vladsch.flexmark.util.options.DelimitedBuilder)

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