/* sxr -- Scala X-Ray
 * Copyright 2009 Mark Harrah
 */

package sxr

import java.io.{File, Reader, Writer}
import scala.collection.jcl.TreeSet

/** The entry point for annotating an input file.*/
private object object sxr.AnnotateAnnotate
{
	import FileUtil.{withReader, withWriter}
	/** Annotates an input source file with highlighting and type information provided by 'tokens' and applied by 'styler'.
	* The result is written to 'target'.*/
	def (java.io.File,java.io.File,scala.collection.jcl.TreeSet[sxr.Token],sxr.Styler)Unitapply(java.io.Filesource: java.io.FileFile, java.io.Filetarget: java.io.FileFile, scala.collection.jcl.TreeSet[sxr.Token]tokens: scala.collection.jcl.TreeSet[sxr.Token]TreeSet[Token], sxr.Stylerstyler: sxr.StylerStyler)
	{
		(java.io.File)((java.io.BufferedReader) => Unit)UnitwithReader(java.io.Filesource) { java.io.BufferedReaderinput =>
			(java.io.File)((java.io.BufferedWriter) => Unit)UnitwithWriter(java.io.Filetarget) { java.io.BufferedWriteroutput =>
				sxr.Annotatenew sxr.AnnotateAnnotate(java.io.BufferedReaderinput, java.io.BufferedWriteroutput, scala.collection.jcl.TreeSet[sxr.Token]tokens, sxr.Stylerstyler).()Unitannotate()
			}
		}
	}
}
/** Annotates a source file.  This class is one-time use and should only be used through the Annotate module.
*
*  'input' is the raw source file.
* The annotated file will be written to 'output'
* The information associated with a file is defined by 'tokens'
* 'styler' generates the final annotations for a token 
*
* Note that the 'write' method in this class is specific to HTML and would
* need to be generalized for another format */
private class class Annotate extends java.lang.Object with NotNull with ScalaObjectAnnotate(java.io.Readerinput: java.io.ReaderReader, java.io.Writeroutput: java.io.WriterWriter, scala.collection.jcl.TreeSet[sxr.Token]tokens: scala.collection.jcl.TreeSet[sxr.Token]TreeSet[Token], sxr.Stylerstyler: sxr.StylerStyler) extends NotNullNotNull
{
	/** Applies the annotations.*/
	def ()Unitannotate()
	{
		java.io.Writeroutput.(java.lang.String)Unitwrite(sxr.Stylerstyler.=> Stringhead)
		(Int)Unitannotate(Int(0)0)
		java.io.Writeroutput.(java.lang.String)Unitwrite(sxr.Stylerstyler.=> Stringtail)
	}
	/** Applies annotations.  index is the current position in the source file. */
	private def (Int)Unitannotate(Intindex: IntInt)
	{
		Unitif(scala.collection.jcl.TreeSet[sxr.Token]tokens.=> BooleanisEmpty) // no more tokens, copy the remaining characters over
			(Int)Unittransfer(java.lang.Integer.Int(2147483647)MAX_VALUE)
		else
		{
			//look at the next token
			val sxr.Tokentoken = scala.collection.jcl.TreeSet[sxr.Token]tokens.=> sxr.TokenfirstKey
			scala.collection.jcl.TreeSet[sxr.Token]tokens.(sxr.Token)Booleanremove(sxr.Tokentoken)
			Unitif(sxr.Tokentoken.=> Intstart (Int)Boolean< Intindex)
			{
				(Any)Unitprintln(java.lang.String("Overlapping span detected at index ")"Overlapping span detected at index " (Any)java.lang.String+ Intindex (Any)java.lang.String+ java.lang.String(": ")": " (Any)java.lang.String+ sxr.Tokentoken)
				(Int)Unitannotate(Intindex)
			}
			else
			{
				// copy over characters not to be annotated 
				(Int)Unittransfer(sxr.Tokentoken.=> Intstart (Int)Int- Intindex)
				// get the annotations for the token from the styler
				val List[sxr.Annotation]styledList = (sxr.Token)List[sxr.Annotation]styler(sxr.Tokentoken)
				// write all opening tags
				for(((sxr.Annotation) => Unit)Unitstyled <- List[sxr.Annotation]styledList)
					java.io.Writeroutput.(java.lang.String)Unitwrite(sxr.Annotationstyled.=> Stringopen)
				// copy the annotated content
				(Int)Unittransfer(sxr.Tokentoken.=> Intlength)
				// close the tags
				for(((sxr.Annotation) => Unit)Unitstyled <- List[sxr.Annotation]styledList.=> List[sxr.Annotation]reverse)
					java.io.Writeroutput.(java.lang.String)Unitwrite(sxr.Annotationstyled.=> Stringclose)
				// continue
				(Int)Unitannotate(sxr.Tokentoken.=> Intstart (Int)Int+ sxr.Tokentoken.=> Intlength)
			}
			
		}
	}
	
	/** Transfers the given number of characters from the input to the output unless the input does not have enough
	* characters, in which case all remaining characters are transferred.*/
	private def (Int)Unittransfer(Intchars: IntInt)
	{
		Unitif(Intchars (Int)Boolean> Int(0)0)
		{
			val Intc = java.io.Readerinput.()Intread()
			Unitif(Intc (Int)Boolean>= Int(0)0)
			{
				(Char)Unitwrite(Intc.CharasInstanceOf[CharChar])
				(Int)Unittransfer(Intchars (Int)Int- Int(1)1)
			}
		}
	}
	/** Writes the given character to the output, escaping to HTML it if necessary.*/
	private def (Char)Unitwrite(Charc: CharChar)
	{
		Charc Unitmatch
		{
			Unitcase Char('>')'>' => java.io.Writeroutput.(java.lang.String)Unitwrite(java.lang.String("&gt;")"&gt;")
			Unitcase Char('&')'&' => java.io.Writeroutput.(java.lang.String)Unitwrite(java.lang.String("&amp;")"&amp;")
			Unitcase Char('<')'<' => java.io.Writeroutput.(java.lang.String)Unitwrite(java.lang.String("&lt;")"&lt;")
			Unitcase Char('"')'"' => java.io.Writeroutput.(java.lang.String)Unitwrite(java.lang.String("&quot;")"&quot;")
			Unitcase _ => java.io.Writeroutput.(Int)Unitwrite(implicit scala.Predef.char2int : (Char)Intc)
		}
	}
	
}