/* * Copyright (C) 2011 René Jeschke <rene_jeschke@yahoo.de> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.kaif.kmark; /** * This class represents a block of lines. * * @author René Jeschke <rene_jeschke@yahoo.de> */ class Block { /** * This block's type. */ public BlockType type = BlockType.NONE; /** * Head and tail of linked lines. */ public Line lines = null, lineTail = null; /** * Head and tail of child blocks. */ public Block blocks = null, blockTail = null; /** * Next block. */ public Block next = null; /** * ID for headlines and list items */ public String id = null; /** * Block meta information */ public String meta = ""; /** * Constructor. */ public Block() { // } /** * @return <code>true</code> if this block contains lines. */ public boolean hasLines() { return this.lines != null; } /** * Removes leading and trailing empty lines. */ public void removeSurroundingEmptyLines() { if (this.lines != null) { this.removeTrailingEmptyLines(); this.removeLeadingEmptyLines(); } } /** * Used for nested block quotes. Removes '>' char. */ public void removeBlockQuotePrefix() { Line line = this.lines; while (line != null) { if (!line.isEmpty) { if (line.value.charAt(line.leading) == '>') { int rem = line.leading + 1; if (line.leading + 1 < line.value.length() && line.value.charAt(line.leading + 1) == ' ') { rem++; } line.value = line.value.substring(rem); line.initLeading(); } } line = line.next; } } /** * Used for nested lists. Removes list markers and up to 4 leading spaces. */ public void removeListIndent() { Line line = this.lines; while (line != null) { if (!line.isEmpty) { switch (line.getLineType()) { case ULIST: line.value = line.value.substring(line.leading + 2); break; case OLIST: line.value = line.value.substring(line.value.indexOf('.') + 2); break; default: line.value = line.value.substring(Math.min(line.leading, 4)); break; } line.initLeading(); } line = line.next; } } /** * Removes leading empty lines. * * @return <code>true</code> if an empty line was removed. */ public boolean removeLeadingEmptyLines() { boolean wasEmpty = false; Line line = this.lines; while (line != null && line.isEmpty) { this.removeLine(line); line = this.lines; wasEmpty = true; } return wasEmpty; } /** * Removes trailing empty lines. */ public void removeTrailingEmptyLines() { Line line = this.lineTail; while (line != null && line.isEmpty) { this.removeLine(line); line = this.lineTail; } } /** * Splits this block's lines, creating a new child block having 'line' as * it's lineTail. * * @param line * The line to split from. * @return The newly created Block. */ public Block split(final Line line) { final Block block = new Block(); block.lines = this.lines; block.lineTail = line; this.lines = line.next; line.next = null; if (this.lines == null) { this.lineTail = null; } else { this.lines.previous = null; } if (this.blocks == null) { this.blocks = this.blockTail = block; } else { this.blockTail.next = block; this.blockTail = block; } return block; } /** * Removes the given line from this block. * * @param line * Line to remove. */ public void removeLine(final Line line) { if (line.previous == null) { this.lines = line.next; } else { line.previous.next = line.next; } if (line.next == null) { this.lineTail = line.previous; } else { line.next.previous = line.previous; } line.previous = line.next = null; } /** * Appends the given line to this block. * * @param line * Line to append. */ public void appendLine(final Line line) { if (this.lineTail == null) { this.lines = this.lineTail = line; } else { this.lineTail.nextEmpty = line.isEmpty; line.prevEmpty = this.lineTail.isEmpty; line.previous = this.lineTail; this.lineTail.next = line; this.lineTail = line; } } /** * Changes all Blocks of type <code>NONE</code> to <code>PARAGRAPH</code> if * this Block is a List and any of the ListItems contains a paragraph. */ public void expandListParagraphs() { if (this.type != BlockType.ORDERED_LIST && this.type != BlockType.UNORDERED_LIST) { return; } Block outer = this.blocks, inner; boolean hasParagraph = false; while (outer != null && !hasParagraph) { if (outer.type == BlockType.LIST_ITEM) { inner = outer.blocks; while (inner != null && !hasParagraph) { if (inner.type == BlockType.PARAGRAPH) { hasParagraph = true; } inner = inner.next; } } outer = outer.next; } if (hasParagraph) { outer = this.blocks; while (outer != null) { if (outer.type == BlockType.LIST_ITEM) { inner = outer.blocks; while (inner != null) { if (inner.type == BlockType.NONE) { inner.type = BlockType.PARAGRAPH; } inner = inner.next; } } outer = outer.next; } } } }