Discounter and the Wholesaler

You inherit from KnockOff to be able to parse your sources. If you're not doing fancy customization, you use the DefaultDiscounter.

val blocks = DefaultDiscounter.knockoff( "document" )

println( "Hello, Conversion!" )
println( blocks.toXML )

Otherwise...

// In com/tristanhunt/knockoff/Discounter.scala
package com.tristanhunt.knockoff
// See the Discounter imports

trait   Discounter
extends ChunkStreamFactory
with    SpanConverterFactory
with    HasElementFactory {

  def toXML( blocks : Seq[ Block ]  ) : Node =
    Group( blocks.map( _.xml ) )

  /**
    Parses and returns our best guess at the sequence of blocks. It will
    never fail, just log all suspicious things.
  */
  def knockoff( source : java.lang.CharSequence ) : Seq[ Block ] = {
      
    val chunks = createChunkStream( new CharSequenceReader( source, 0 ) )

    // These next lines are really ugly because I couldn't figure out a nice
    // way to match a tuple argument (thank you erasure!)
    val linkDefinitions = chunks.flatMap{ case ((chunk, pos)) =>
      if ( chunk.isLinkDefinition )
        List( chunk.asInstanceOf[ LinkDefinitionChunk ] )
      else Nil
    }
    
    val convert = spanConverter( linkDefinitions )
    
    val spanned = chunks.map { chunkAndPos =>
      ( chunkAndPos._1, convert( chunkAndPos._1 ), chunkAndPos._2 )
    }
    
    combine( spanned.toList, new ListBuffer )
  }
  
  /**
    Consume input and append the right thing to the output until empty. The
    Chunk itself determines the "right thing to do". All chunks only know what
    has come before itself, by peering into the output. (It shouldn't matter
    what comes next...)
  */
  private def combine( input : List[ (Chunk, SpanSeq, Position) ],
                       output  : ListBuffer[ Block ] )
                     : Seq[ Block ] = {
    if ( input.isEmpty ) return output
    input.head._1.appendNewBlock( output, input.tail, input.head._2,
                                  input.head._3 )( elementFactory, this )
    combine( input.tail, output )
  }
}

// The Discounter imports

import scala.collection.mutable.ListBuffer
import scala.util.parsing.input.Position
import scala.util.parsing.input.CharSequenceReader
import scala.xml.{ Group, Node }

DefaultDiscounter

For many applications the default is "good enough". Note that a major aim of this discounter is to mimic the usage of Markdown.pl.

Markdown.pl [ ??html4tags ] [ ??version ] [ ?shortversion ] [ file ... ]

The --html4tags argument will just do nothing, but not be processed as a file.

// In com/tristanhunt/knockoff/DefaultDiscounter.scala
package com.tristanhunt.knockoff

import scala.util.logging.ConsoleLogger

object DefaultDiscounter extends Discounter with ColoredLogger {
  def main( args : Array[ String ] ) : Unit = try {
    if ( args.contains("--version") ) {
      Console.err.print( "DefaultDiscounter " )
    }
    if ( args.contains("--version") || args.contains("-shortversion") ) {
      Console.err.println( "0.6.1-6" )
      return 0
    }
    
    if ( args.isEmpty ) {
      val sb = new StringBuilder
      var line : String = null
      do {
        line = Console.readLine
        if ( line != null ) sb.append( line )
      } while ( line != null )
      println( toXML( knockoff( sb.toString ) ).toString )
    } else {
      args.filter( _ != "--html4tags" ).foreach { fileName =>
        println( toXML( knockoff( readText( fileName ) ) ).toString )
      }
    }
  } catch {
    case th : Throwable => {
      th.printStackTrace( Console.err )
    }
  }
  
  private def readText( fileName : String ) : String =
    io.Source.fromFile( fileName ).mkString("")
}

Wholesaler

I've started to include ideas from the MultiMarkdown syntax system, but I didn't want to mix that up with a normal Markdown format. The Wholesaler is going to be the variation of the Discounter, plus so much more!

Some Rules May Apply

If there is a leading Paragraph, and it can be broken into Metadata, we treat it like a block.

// In com/tristanhunt/knockoff/extra/Wholesaler.scala
package com.tristanhunt.knockoff.extra

trait Wholesaler extends Discounter with MetaDataConverter {
  
  override def knockoff( source : java.lang.CharSequence ) : Seq[ Block ] = {
    var blocks = super.knockoff( source )
    
    if ( ! blocks.isEmpty ) {
      blocks.first match {
        case p : Paragraph =>
          toMetaData( p ).foreach { metaData =>
            blocks = Seq( metaData ) ++ blocks.drop(1) }
        case _ => {}
      }
    }
    
    return blocks
  }
}

DefaultWholesaler

Another console wrapping application. This one has to be called explicitly.

// In com/tristanhunt/knockoff/extra/DefaultWholesaler.scala
package com.tristanhunt.knockoff.extra

import scala.util.logging.ConsoleLogger

object DefaultWholesaler extends Wholesaler with ColoredLogger {
  def main( args : Array[ String ] ) : Unit = try {
    if ( args.contains("--version") ) {
      Console.err.print( "DefaultWholesaler " )
    }
    if ( args.contains("--version") || args.contains("-shortversion") ) {
      Console.err.println( "0.6.1-6" )
      return 0
    }
    
    if ( args.isEmpty ) {
      val sb = new StringBuilder
      var line : String = null
      do {
        line = Console.readLine
        if ( line != null ) sb.append( line )
      } while ( line != null )
      println( toXML( knockoff( sb.toString ) ).toString )
    } else {
      args.filter( _ != "--html4tags" ).foreach { fileName =>
        println( toXML( knockoff( readText( fileName ) ) ).toString )
      }
    }
  } catch {
    case th : Throwable => {
      th.printStackTrace( Console.err )
    }
  }
  
  private def readText( fileName : String ) : String =
    io.Source.fromFile( fileName ).mkString("")
}