All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.helger.html.markdown.Block Maven / Gradle / Ivy

/**
 * Copyright (C) 2014-2016 Philip Helger (www.helger.com)
 * philip[at]helger[dot]com
 *
 * 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 com.helger.html.markdown;

import javax.annotation.Nonnull;

import com.helger.commons.annotation.CodingStyleguideUnaware;

/**
 * This class represents a block of lines.
 *
 * @author René Jeschke <[email protected]>
 */
@CodingStyleguideUnaware
final class Block
{
  /** This block's type. */
  public EBlockType m_eType = EBlockType.NONE;
  /** Head of linked lines. */
  public Line m_aLines = null;
  /** Tail of linked lines. */
  public Line m_aLineTail = null;
  /** Head of child blocks. */
  public Block m_aBlocks = null;
  /** Tail of child blocks. */
  public Block m_aBlockTail = null;
  /** Next block. */
  public Block m_aNext = null;
  /** Depth of headline BlockType. */
  public int m_nHeadlineDepth = 0;
  /** ID for headlines and list items */
  public String m_sId = null;
  /** Block meta information */
  public String m_sMeta = "";

  /** Constructor. */
  public Block ()
  {}

  /**
   * @return true if this block contains lines.
   */
  public boolean hasLines ()
  {
    return m_aLines != null;
  }

  /**
   * Removes leading and trailing empty lines.
   */
  public void removeSurroundingEmptyLines ()
  {
    if (m_aLines != null)
    {
      removeTrailingEmptyLines ();
      removeLeadingEmptyLines ();
    }
  }

  /**
   * Sets hlDepth and takes care of '#' chars.
   */
  public void transfromHeadline ()
  {
    if (m_nHeadlineDepth > 0)
      return;
    int nLevel = 0;
    final Line aLine = m_aLines;
    if (aLine.m_bIsEmpty)
      return;
    int nStart = aLine.m_nLeading;
    while (nStart < aLine.m_sValue.length () && aLine.m_sValue.charAt (nStart) == '#')
    {
      nLevel++;
      nStart++;
    }
    while (nStart < aLine.m_sValue.length () && aLine.m_sValue.charAt (nStart) == ' ')
      nStart++;
    if (nStart >= aLine.m_sValue.length ())
    {
      aLine.setEmpty ();
    }
    else
    {
      int nEnd = aLine.m_sValue.length () - aLine.m_nTrailing - 1;
      while (aLine.m_sValue.charAt (nEnd) == '#')
        nEnd--;
      while (aLine.m_sValue.charAt (nEnd) == ' ')
        nEnd--;
      aLine.m_sValue = aLine.m_sValue.substring (nStart, nEnd + 1);
      aLine.m_nLeading = aLine.m_nTrailing = 0;
    }
    m_nHeadlineDepth = Math.min (nLevel, 6);
  }

  /**
   * Used for nested lists. Removes list markers and up to 4 leading spaces.
   *
   * @param bExtendedMode
   *        Whether extended profile is activated or not
   */
  public void removeListIndent (final boolean bExtendedMode)
  {
    Line line = m_aLines;
    while (line != null)
    {
      if (!line.m_bIsEmpty)
      {
        switch (line.getLineType (bExtendedMode))
        {
          case ULIST:
            line.m_sValue = line.m_sValue.substring (line.m_nLeading + 2);
            break;
          case OLIST:
            line.m_sValue = line.m_sValue.substring (line.m_sValue.indexOf ('.') + 2);
            break;
          default:
            line.m_sValue = line.m_sValue.substring (Math.min (line.m_nLeading, 4));
            break;
        }
        line.initLeading ();
      }
      line = line.m_aNext;
    }
  }

  /**
   * Used for nested block quotes. Removes '>' char.
   */
  public void removeBlockQuotePrefix ()
  {
    Line aLine = m_aLines;
    while (aLine != null)
    {
      if (!aLine.m_bIsEmpty)
      {
        if (aLine.m_sValue.charAt (aLine.m_nLeading) == '>')
        {
          int rem = aLine.m_nLeading + 1;
          if (aLine.m_nLeading + 1 < aLine.m_sValue.length () && aLine.m_sValue.charAt (aLine.m_nLeading + 1) == ' ')
            rem++;
          aLine.m_sValue = aLine.m_sValue.substring (rem);
          aLine.initLeading ();
        }
      }
      aLine = aLine.m_aNext;
    }
  }

  /**
   * Removes leading empty lines.
   *
   * @return true if an empty line was removed.
   */
  public boolean removeLeadingEmptyLines ()
  {
    boolean wasEmpty = false;
    Line line = m_aLines;
    while (line != null && line.m_bIsEmpty)
    {
      removeLine (line);
      line = m_aLines;
      wasEmpty = true;
    }
    return wasEmpty;
  }

  /**
   * Removes trailing empty lines.
   */
  public void removeTrailingEmptyLines ()
  {
    Line line = m_aLineTail;
    while (line != null && line.m_bIsEmpty)
    {
      removeLine (line);
      line = m_aLineTail;
    }
  }

  /**
   * 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.m_aLines = m_aLines;
    block.m_aLineTail = line;
    m_aLines = line.m_aNext;
    line.m_aNext = null;
    if (m_aLines == null)
      m_aLineTail = null;
    else
      m_aLines.m_aPrevious = null;

    if (m_aBlocks == null)
      m_aBlocks = m_aBlockTail = block;
    else
    {
      m_aBlockTail.m_aNext = block;
      m_aBlockTail = block;
    }

    return block;
  }

  /**
   * Removes the given line from this block.
   *
   * @param line
   *        Line to remove.
   */
  public void removeLine (final Line line)
  {
    if (line.m_aPrevious == null)
      m_aLines = line.m_aNext;
    else
      line.m_aPrevious.m_aNext = line.m_aNext;
    if (line.m_aNext == null)
      m_aLineTail = line.m_aPrevious;
    else
      line.m_aNext.m_aPrevious = line.m_aPrevious;
    line.m_aPrevious = line.m_aNext = null;
  }

  /**
   * Appends the given line to this block.
   *
   * @param aLine
   *        Line to append.
   */
  public void appendLine (@Nonnull final Line aLine)
  {
    if (m_aLineTail == null)
      m_aLines = m_aLineTail = aLine;
    else
    {
      aLine.m_bPrevEmpty = m_aLineTail.m_bIsEmpty;
      aLine.m_aPrevious = m_aLineTail;
      m_aLineTail.m_aNext = aLine;
      m_aLineTail = aLine;
    }
  }

  /**
   * Changes all Blocks of type NONE to PARAGRAPH if
   * this Block is a List and any of the ListItems contains a paragraph.
   */
  public void expandListParagraphs ()
  {
    if (m_eType != EBlockType.ORDERED_LIST && m_eType != EBlockType.UNORDERED_LIST)
    {
      return;
    }
    Block outer = m_aBlocks, inner;
    boolean hasParagraph = false;
    while (outer != null && !hasParagraph)
    {
      if (outer.m_eType == EBlockType.LIST_ITEM)
      {
        inner = outer.m_aBlocks;
        while (inner != null && !hasParagraph)
        {
          if (inner.m_eType == EBlockType.PARAGRAPH)
            hasParagraph = true;
          inner = inner.m_aNext;
        }
      }
      outer = outer.m_aNext;
    }
    if (hasParagraph)
    {
      outer = m_aBlocks;
      while (outer != null)
      {
        if (outer.m_eType == EBlockType.LIST_ITEM)
        {
          inner = outer.m_aBlocks;
          while (inner != null)
          {
            if (inner.m_eType == EBlockType.NONE)
              inner.m_eType = EBlockType.PARAGRAPH;
            inner = inner.m_aNext;
          }
        }
        outer = outer.m_aNext;
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy