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

laika.ast.containers.scala Maven / Gradle / Ivy

/*
 * Copyright 2012-2020 the original author or authors.
 *
 * 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 laika.ast

import laika.api.Renderer
import laika.format.AST

/** A generic container.
  *  Usually not mixed in directly, instead one of the sub-traits
  *  `TextContainer`, `ListContainer`, `SpanContainer` or `BlockContainer` should be used.
  */
trait Container[+T] extends Element {
  def content: T
}

/** A container for plain text.
  */
trait TextContainer extends Container[String]

/** A generic container of other elements.
  *  Provides means to traverse, select and rewrite children of
  *  this container.
  *
  *  Usually not mixed in directly, instead one of the sub-traits
  *  `ListContainer`, `SpanContainer` or `BlockContainer` should be used.
  */
trait ElementContainer[+E <: Element] extends Container[Seq[E]] with ElementTraversal {
  override def toString: String = FormattedElementString.render(this)
}

/** A generic container of child elements which can have
  * rewrite rules applied to them in recursive tree rewriting.
  */
trait RewritableContainer extends Element {

  type Self <: RewritableContainer

  /** Rewrites all children of this container based on the specified rules.
    *
    * Concrete types are expected to support rewriting at least for all standard block, span and template span
    * elements they contain, plus optionally for any other elements that have custom support for rewriting.
    */
  def rewriteChildren(rules: RewriteRules): Self

  /** Rewrites all span children of this container based on the specified rules.
    *
    * Concrete types are expected to support rewriting at least for all standard block, span and template span
    * elements they contain, plus optionally for any other elements that have custom support for rewriting.
    */
  def rewriteSpans(rule: RewriteRule[Span]): Self = rewriteChildren(
    RewriteRules.forSpans(rule)
  )

}

/** A container of other Block elements. Such a container is usually
  *  also a Block itself.
  */
trait BlockContainer extends ElementContainer[Block] with RewritableContainer {

  type Self <: BlockContainer

  /** Rewrites all block children of this container based on the specified rules.
    *
    * Concrete types are expected to support rewriting at least for all standard block, span and template span
    * elements they contain, plus optionally for any other elements that have custom support for rewriting.
    */
  def rewriteBlocks(rules: RewriteRule[Block]): Self = rewriteChildren(
    RewriteRules.forBlocks(rules)
  )

  def rewriteChildren(rules: RewriteRules): Self = withContent(rules.rewriteBlocks(content))

  /** Creates a copy of this instance with the specified new content.
    *
    * Implementation note: This method exists to deal with the fact that there is no polymorphic copy method
    * and trades a small bit of boilerplate for avoiding the compile time hit of using shapeless for this.
    */
  def withContent(newContent: Seq[Block]): Self

}

/** A container of other Span elements. Such a container may be a Block
  *  or a Span itself.
  */
trait SpanContainer extends ElementContainer[Span] with RewritableContainer {

  type Self <: SpanContainer

  def rewriteChildren(rules: RewriteRules): Self = withContent(rules.rewriteSpans(content))

  /** Creates a copy of this instance with the specified new content.
    *
    * Implementation note: This method exists to deal with the fact that there is no polymorphic copy method
    * and trades a small bit of boilerplate for avoiding the compile time hit of using shapeless for this.
    */
  def withContent(newContent: Seq[Span]): Self

  /**  Extracts the text from the spans of this container, removing
    *  any formatting or links.
    */
  def extractText: String = content.map {
    case tc: TextContainer => tc.content
    case sc: SpanContainer => sc.extractText
    case _                 => ""
  }.mkString

}

/** A container of list items. Such a container is usually a Block itself.
  */
trait ListContainer extends ElementContainer[ListItem]

/** Common methods for simple span containers (without additional parameters). */
trait SpanContainerCompanion {

  type ContainerType

  /** Creates an empty instance */
  def empty: ContainerType = createSpanContainer(Nil)

  /** Create an instance only containing only one or more Text spans */
  def apply(text: String, texts: String*): ContainerType = createSpanContainer(
    (text +: texts).map(Text(_))
  )

  /** Create an instance containing one or more spans */
  def apply(span: Span, spans: Span*): ContainerType = createSpanContainer(span +: spans.toList)

  protected def createSpanContainer(spans: Seq[Span]): ContainerType
}

/** Common methods for simple block containers (without additional parameters). */
trait BlockContainerCompanion extends SpanContainerCompanion {

  override def empty: ContainerType = createBlockContainer(Nil)

  protected def createSpanContainer(spans: Seq[Span]): ContainerType = createBlockContainer(
    spans.map(Paragraph(_))
  )

  /** Create an instance containing one or more blocks */
  def apply(block: Block, blocks: Block*): ContainerType = createBlockContainer(
    block +: blocks.toList
  )

  protected def createBlockContainer(blocks: Seq[Block]): ContainerType
}

private[ast] object FormattedElementString {
  private lazy val renderer: Renderer = Renderer.of(AST).build.skipRewritePhase
  def render(elem: Element): String   = "\n" + renderer.render(elem) + "\n"
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy