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();
}
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;
}
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;
}
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)));
}
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;
}
}
Aggregations