#!/usr/local/bin/php
<?php
# Salty XML Transformer
# Version 1.2.1
#
# A crude but effective-enough way to use (or try to use) any XSLT sheet to
# transform any XML file or VUE concept map file.
#
# Named "Salty" because the name XSLT reminds me of salt, and because I didn't
# have the energy or patience to try to make something more perfect and less
# crude.
#
# 
# Can be run either from the command line, or on a web server, though this was
# not designed with security in mind, so I don't recommend using it on any
# web server connected to the internet.
#
#
# Works with PHP 5.6.13 and hopefully other versions.
#
#
# Requires the XSLT processor Saxon/C:
# http://www.saxonica.com/saxon-c/index.xml
#
#
# Here's my blog post about how to make Saxon/C into a PHP extension when
# using Lucid Puppy Linux 5.2.8 version 004.  (I eventually also added some
# notes related to Lighthouse 64 Puppy Linux 6.02 Beta 2.)
#
# http://astroblahhh.com/blog/puppy-linux/how-to-make-saxon-c-into-a-php-extension-for-php-5-6-13-in-lucid-puppy-linux-5-2-360.shtml#blogtoc
#
#
# Also uses the standard GNU/Linux tool "sed".
#
#
# Some of the Saxon-related code is slightly modified code from
# the PHP demo scripts included with Saxon/C.
#
#------------------------------------------------------------------------------
#
# Command line arguments:
#
# Arg 1: A path to an XML file or VUE map to be transformed.
#
# Arg 2: A path to an XSLT sheet which will be used on the file pointed out by
# Arg 1.
#
#
# Optional Arg 3: Set to "y" or "yes" or "revuize" (all equivalent) to set
# "should_convert_output_back_to_vue_format" to true.
#
# Makes Salty run the included Salty-related Perl script "unrelocvuecomments"
# (whose longer name is "Undo Moving a VUE Map's Top Comment Lines Into a CDATA 
# Section") on the XSLT output. 
#
#
# Optional Arg 4: A path to the file where output should go.
#
# Must be inside whatever path is in the Dest_Folder constant.
#
# By default, that path is: "/root/Web-Server/Salty-XML-Transformer-Output/"
#
# Relative paths are assumed to be in there.  (Except if the path contains
# dots that lead out of that folder.)
#
#
# Optional Arg 5: Set to "y" or "yes" or "mojifix" (all equivalent) to set
# "should_try_to_convert_trashed_unicode_to_entities" to true.
#
# Makes Salty run the included Salty-related Perl script
# "somehow-fix-unicode-garbage-in-Salty-XML-Transformer-output" on the
# XSLT output.
#
# (Moji is short for mojibake, "the name for incorrect, unreadable characters
# shown when computer software fails to show text correctly.")
#
#
# All args beyond Arg 5 are assumed to be parameters that should be passed
# into the XSLT sheet being used.
#
# If a parameter's value should be interpreted as an Xpath statement,
# put ":xpath" after the parameter's name.
#
# Example:
#
# time:xpath=current-dateTime()
#
#
# Everything else is assumed to be something other than an Xpath.
#
# By default, Salty converts non-Xpath parameters to XdmValues using Saxon's
# createAtomicValue function.
#
#
#------------------------------------------------------------------------------
#
# Getvars (when run in web mode)
#
# "xml" = Arg 1 above
# "xslt" = Arg 2 above
# "revuize" = Arg 3 above
# "dest" = Arg 4 above
# "mojifix" = Arg 5 above
#
# Any other name = Assumed to be a parameter for XSLT.
#
#
#------------------------------------------------------------------------------
#
#
# Under the GNU Affero General Public License v3.0.
#
# Copyright (C) 2017  Apollia
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#
# Contact info:     http://astroblahhh.com/contact-apollia.shtml
#
#-----



$App_Root=dirname(__FILE__);

$Procedure_Root="$App_Root/Procedures/";

$orig_working_dir=getcwd();

define( "Dest_Folder", "/root/Web-Server/Salty-XML-Transformer-Output/");
	# 15:39:34 03/26/2017.  It's probably best to put a slash on the end of that path.

if (! file_exists(Dest_Folder) )
{
	mkdir(Dest_Folder);
}

define ("Default_dest_path", Dest_Folder . "Default");

chdir("$App_Root");

require ("Procedures/Startup.php");


define ( "REQUIRED", 1);
define ( "OPTIONAL", 2);



class Prototype___Mr_Raw_Input
{
	protected $count_of_named_args;
	
	protected $args_to_get=array
	(
		"xml_path" => REQUIRED, 
		"xslt_path" => REQUIRED,
		"should_convert_output_back_to_vue_format" => OPTIONAL,
		"dest_path" => OPTIONAL,
		"should_try_to_convert_trashed_unicode_to_entities" => OPTIONAL
	);
	
	protected $args_and_getvars = array
	(
		"xml_path" => "xml",
		"xslt_path" => "xslt",
		"should_convert_output_back_to_vue_format" => "revuize",
		"dest_path" => "dest",
		"should_try_to_convert_trashed_unicode_to_entities" => "mojifix"
	);
	
			# 22:13:02 03/28/2017.  Moji is short for mojibake, "the name for incorrect, unreadable characters shown
			# when computer software fails to show text correctly."
			#
			# This setting only affects the result of transforming plain XML, not vuemaps (which Salty tries to mojifix
			# automatically when revuizing).
			
			
	
	protected $raw_input=array();
	
	protected $unnamed_raw_input=array();
			# 23:03:52 04/02/2017.  All unnamed args are assumed to be params for the XSLT sheet.
	
	
	use Magic_Getter;
	
	
	public function __construct()
	{
		$this->count_of_named_args=count($this->args_to_get);
	}
	
	public function Fetch_Raw_Input()
	{
		
		$args_to_get=$this->args_to_get;
		
		if ( In_PHP_CLI_Mode )
		{
			
			global $Mr_Cliargs;
		
			$this->unnamed_raw_input=$Mr_Cliargs->Maybe_Get_Args_After_Num($this->count_of_named_args);
			
			
			$argnum=0;
			foreach ($args_to_get as $arg => $necessity )
			{
				if ( $necessity==REQUIRED)
				{
					$this->raw_input[$arg]= $Mr_Cliargs->Get_Arg($argnum);
					
				}
				else
				{	
					$this->raw_input[$arg]= $Mr_Cliargs->Maybe_Get_Arg($argnum);
				}
				$argnum++;
			}
			
		}
		else if ( In_PHP_CGI_Mode )
		{
			global $Mr_Getvars;
			
			$getvars_to_remove=array_values($this->args_and_getvars);
			
			
			$this->unnamed_raw_input=$Mr_Getvars->Provide_Getvars_With_These_Items_Filtered_Out( $getvars_to_remove );
		
			
			foreach ($args_to_get as $arg => $necessity )
			{
				$getvar = $this->args_and_getvars["$arg"];
				
				if ( $necessity==REQUIRED)
				{
					$this->raw_input[$arg]= $Mr_Getvars->Get_Getvar( $getvar );
					
				}
				else
				{	
					$this->raw_input[$arg]= $Mr_Getvars->Maybe_Get_Getvar($getvar);
				}
				
		
			}		

		}
		
		foreach ($args_to_get as $arg => $necessity )
		{
			$this->$arg=$this->raw_input[$arg];
		}

	}
}



# 00:56:30 04/03/2017.  A slightly modified version of the "is_asso" function from this page
# http://thinkofdev.com/check-if-an-array-is-associative-or-sequentialindexed-in-php

function is_associative_array ($array)
{
	foreach (array_keys($array) as $key)
	{
		if (!is_int($key))
		{
			return true;
		}
	}
	return false;
}



class Prototype___Mr_Standardized_Input
{
	
	protected $raw_input;
	protected $input;
	
	protected $unnamed_raw_input;
	protected $unnamed_input;
	protected $types_of_unnamed_input;
	
	protected $raw_input_to_standardize=array("xml_path", "xslt_path", "should_convert_output_back_to_vue_format", "should_try_to_convert_trashed_unicode_to_entities", "dest_path");
	
	
	use Magic_Getter;



	public function __construct()
	{
		global $Mr_Raw_Input;
		
		$this->raw_input=$Mr_Raw_Input->raw_input;
		$this->unnamed_raw_input=$Mr_Raw_Input->unnamed_raw_input;
		
	}



	public function Please_Provide_the_( $arg)
	{
		return $this->input["$arg"];
	}



	public function says( $arg )
	{
		return $this->input["$arg"];
	}



	public function Standardize_Unnamed_Raw_Input()
	{
		$unnamed_raw_input=$this->unnamed_raw_input;
		
		
		
		if (! is_associative_array($unnamed_raw_input) )
		{
			foreach ($unnamed_raw_input as $arg)
			{
				$parts=explode("=", $arg);
				$key=array_shift($parts);
				$key=trim($key);
				
				$value=implode("=", $parts);
				
				$value=trim($value);
				
				$partly_standardized[$key]=$value;
			}
		}
		else
		{
			$partly_standardized=$unnamed_raw_input;
			#$this->unnamed_input=$unnamed_raw_input;
			#return true;	
		}
		
		
		foreach ($partly_standardized as $key => $value)
		{
			if (strpos ("$key", ":" ) === false)
			{
				$type="string";
				$types_of_unnamed_input["$key"]=$type;
				$unnamed_input[$key]=$value;
				continue;
			}
			
			$parts=explode(":", $key);
			
			$type=array_pop($parts);
			$key=implode("|", $parts);
			$types_of_unnamed_input["$key"]=$type;
			$unnamed_input[$key]=$value;
		}
		
		$this->unnamed_input=$unnamed_input;
		$this->types_of_unnamed_input=$types_of_unnamed_input;
	}
	
	
	
	public function Standardize_Raw_Input()
	{
		
		if ( $this->unnamed_raw_input)
		{
			$this->Standardize_Unnamed_Raw_Input();
		}
		else
		{
			$this->unnamed_input=null;
		}
		
		
		$raw_input=$this->raw_input;
		
		$raw_input_to_standardize=$this->raw_input_to_standardize;
		
		foreach ($raw_input_to_standardize as $arg)
		{
			${$arg}=$raw_input["$arg"];
		}


		chdir ( Orig_Working_Dir);

		
		$xml_path=Resolve_Path_Dots($xml_path);
		$xslt_path=Resolve_Path_Dots($xslt_path);
		

		if ( $should_try_to_convert_trashed_unicode_to_entities=="yes" || $should_try_to_convert_trashed_unicode_to_entities == "y" || $should_try_to_convert_trashed_unicode_to_entities=="mojifix" )
		{
			$should_try_to_convert_trashed_unicode_to_entities="y";
		}
		else
		{
			$should_try_to_convert_trashed_unicode_to_entities=null;
		}
		
		
		if ( $should_convert_output_back_to_vue_format=="yes" || $should_convert_output_back_to_vue_format=="y" || $should_convert_output_back_to_vue_format=="revuize" )
		{
			$should_convert_output_back_to_vue_format="y";
		}
		else
		{
			$should_convert_output_back_to_vue_format=null;
		}

		
		if (! $dest_path )
		{
			$dest_path=Default_dest_path;
		}
		else
		{
			chdir ( Dest_Folder );
			$dest_path=Resolve_Path_Dots($dest_path);
		}
		
		
		

		foreach ($raw_input_to_standardize as $arg)
		{
			$this->input[$arg]=${$arg};
		}

	}
}




class Prototype___Mr_Valichecker
{
	
	protected $args_to_valicheck=array("xml_path", "xslt_path", "should_convert_output_back_to_vue_format", "should_try_to_convert_trashed_unicode_to_entities", "dest_path" );
	
	protected $xml_path;
	protected $xslt_path;
	protected $should_convert_output_back_to_vue_format;
	protected $dest_path;
	protected $should_try_to_convert_trashed_unicode_to_entities;
	
	
	
	public function Do_Valicheck_of_Input()
	{
		global $Mr_Standardized_Input;
		
		$input=$Mr_Standardized_Input->input;
		
		
		$this->Abort_If_File_Not_Found( $input["xml_path"] , "XML file");
		$this->Abort_If_File_Not_Found($input["xslt_path"], "XSLT file");
		$this->Abort_If_File_Not_In_Dest_Folder($input["dest_path"] );
		
		$this->Valicheck_Arg___should_convert_output_back_to_vue_format( $input["should_convert_output_back_to_vue_format"] );

		$this->Valicheck_Arg___should_try_to_convert_trashed_unicode_to_entities( $input["should_try_to_convert_trashed_unicode_to_entities"] );	
		
		return true;
	}
	
		
	
	
	protected function Abort_if_File_Not_Found( $path, $what_it_is )
	{
		if (! file_exists( "$path" ) )
		{
			echo "No $what_it_is found at path $path!  Aborting.";
			exit;
		}
		
		if (! is_file( "$path" ) )
		{	echo "Something other than a file is at $what_it_is path $path!  Aborting.";
			exit;
		}
	}
	
	
	
	protected function Abort_If_File_Not_In_Dest_Folder( $path )
	{
		if ( 0 === strpos ( $path, Dest_Folder ) )
		{
			return false;
		}
		
		echo "The output file path $path wasn't inside the mandatory destination folder " . Dest_Folder . "!  Aborting.";
		exit;
	}
	
	
	protected function Valicheck_Arg___should_convert_output_back_to_vue_format( $arg )
	{
		$should_convert_output_back_to_vue_format=$arg;
		
		#$should_convert_output_back_to_vue_format=false;
		if ($should_convert_output_back_to_vue_format == "y" || $should_convert_output_back_to_vue_format === null )
		{
			return true;
		}
		p();
		echo "Aborting: The setting 'should_convert_output_back_to_vue_format' had an invalid value: '";
		print_r($should_convert_output_back_to_vue_format);
		echo "'";
		exit;
		
	}
	
	
	
	protected function Valicheck_Arg___should_try_to_convert_trashed_unicode_to_entities( $arg)
	{
		$should_try_to_convert_trashed_unicode_to_entities=$arg;
		
		#$should_convert_output_back_to_vue_format=false;
		if ($should_try_to_convert_trashed_unicode_to_entities == "y" || $should_try_to_convert_trashed_unicode_to_entities === null )
		{
			return true;
		}
		p();
		echo "Aborting: The setting 'should_try_to_convert_trashed_unicode_to_entities' had an invalid value: '";
		print_r($should_try_to_convert_trashed_unicode_to_entities);
		echo "'";
		exit;
		
	}	

}



$Mr_Raw_Input=new Prototype___Mr_Raw_Input;
$Mr_Raw_Input->Fetch_Raw_Input();

$Mr_Standardized_Input=new Prototype___Mr_Standardized_Input;
$Mr_Standardized_Input_says=$Mr_Standardized_Input;

$Mr_Standardized_Input->Standardize_Raw_Input();


$Mr_Valichecker=new Prototype___Mr_Valichecker;
$Mr_Valichecker->Do_Valicheck_of_Input();




class Prototype___Mr_Vuemap
{
	protected $xml_path_is_a_vuemap=false;
	protected $xml_path;
	protected $esc_xml_path;
	
	protected $dest_path;
	protected $esc_dest_path;
	
	protected $Fullpath_to_relocvuecomments;
	protected $Fullpath_to_unrelocvuecomments;
	
	protected $esc_Fullpath_to_relocvuecomments;
	protected $esc_Fullpath_to_unrelocvuecomments;
	
	protected $to_xml_retcode;
	protected $to_xml_output;
	
	protected $to_vue_retcode;
	protected $to_vue_output;
	
	protected $xml_to_convert_to_vuemap;

	
	use Magic_Getter;
	


	public function Check_if_xml_path_is_a_Vuemap()
	{
		
		$xml_path=$this->xml_path;
		
		$pathinfo = pathinfo("$xml_path");
		
		$filext=$pathinfo['extension'];
		$filext=strtolower($filext);
		if ($filext == "vue")
		{
			$this->xml_path_is_a_vuemap=true;
		}
	}
	
	
	
	public function __construct()
	{
		global $Perl_Tools_Root;
		global $Mr_Standardized_Input;
		
		$this->xml_path = $Mr_Standardized_Input->Please_Provide_the_("xml_path");
		$this->esc_xml_path = escapeshellarg( $this->xml_path );
		
		$this->dest_path = $Mr_Standardized_Input->Please_Provide_the_("dest_path");
		$this->esc_dest_path = escapeshellarg( $this->dest_path );
		
		$this->Fullpath_to_relocvuecomments = "$Perl_Tools_Root" . "relocvuecomments";
		$this->Fullpath_to_unrelocvuecomments = "$Perl_Tools_Root" . "unrelocvuecomments";
		
		$this->esc_Fullpath_to_relocvuecomments = escapeshellarg( $this->Fullpath_to_relocvuecomments );
		$this->esc_Fullpath_to_unrelocvuecomments = escapeshellarg( $this->Fullpath_to_unrelocvuecomments );
	}
		
	
	
	public function Convert_Output_to_Vuemap()
	{
		global $Mr_Output;
		
		$dest_path = $this->dest_path;
		
		$esc_dest_path = $this->esc_dest_path;
		
		chdir(Dest_Folder);
		
		$command_line="sed -i -e 's/&lt;!--/<!--/' $esc_dest_path";
		$result = exec( $command_line, $output, $retcode );
		
		
		$command_line="sed -i -e 's/--&gt;/-->/' $esc_dest_path";
		$result = exec( $command_line, $output, $retcode );
		

		$esc_Fullpath_to_unrelocvuecomments=$this->esc_Fullpath_to_unrelocvuecomments;
		
		$command_line="perl $esc_Fullpath_to_unrelocvuecomments $esc_dest_path  2>&1";
		
		$result = exec( $command_line, $output, $retcode );
		
		if ($retcode != 0 )
		{
			$vuemap_before_unrelocvuecomments=file_get_contents("$dest_path");
			
			if (In_PHP_CLI_Mode)
			{
				Shellonly_Echo ("$vuemap_before_unrelocvuecomments");
				p();
			}
			
			$output_string=implode("\n", $output);
			
			$error_message="Aborting because unrelocvuecomments failed!  Error $retcode from unrelocvuecomments:
		$output_string";
			Web_and_Shell_Echoes( $error_message, $error_message, "replace less thans in web echo");
			
			Webonly_Echo($vuemap_before_unrelocvuecomments, "replace less thans", "<pre>");
			exit($retcode);
		}
		
		$count_of_lines=count($output);
		
		# 19:59:34 03/28/2017.  This part gets rid of errors possibly printed at the top by unrelocvuecomments.
		# And shows those errors to the user.

		$arrin=0;
		foreach ($output as $line)
		{
			if ( strpos($line, "<!-- Tufts " ) === 0)
			{
				break;
			}
			$arrin++;
		}
		if ($arrin!=0)
		{
			$error_lines=array_splice($output, 0, $arrin);
			
			$error_lines_string=implode("\n", $error_lines);
			
			$message="The output from unrelocvuecomments contained these errors at the top.  They were removed from the output file:";
			
			Web_and_Shell_Echoes($message, $message);
			
			$message="

$error_lines_string";
			Web_and_Shell_Echoes( $message, $message, "replace less thans in web echo", "pre");
			
			Webonly_Echo("<p><br><hr><p><br>");			
			
		}

		$output_string=implode("\n", $output);
		
		#Webecho("Arrin: $arrin  $output_string", "replace less thans", "pre");
		
		
		$Mr_Output->Set_Output_to($output_string);
		$Mr_Output->Convert_Unicode_Surrogate_Pairs_to_HTML_Entities();
		
		
		$Mr_Output->Create_Output_File();
		
		$dest_filename=basename($dest_path);
		
		$Mr_Output->Rename_Output_File("$dest_filename.vue");
	}
	
	
	
	public function Convert_Vuemap_to_XML()
	{
		$esc_xml_path=$this->esc_xml_path;
		
		$esc_Fullpath_to_relocvuecomments=$this->esc_Fullpath_to_relocvuecomments;
		
		$command_line="perl $esc_Fullpath_to_relocvuecomments $esc_xml_path  2>&1";

		$result = exec( $command_line, $output, $retcode );
		
		if ($retcode != 0 )
		{
			$output_string=implode("\n", $output);
			
			$error_message="Aborting because relocvuecomments failed!  Error $retcode from relocvuecomments:
		$output_string";
			Web_and_Shell_Echoes( $error_message, $error_message, "replace less thans in web echo");
			
			exit($retcode);
		}
		
		$output_string=implode("\n", $output);
		
		$this->to_xml_retcode=$retcode;
		$this->to_xml_output=$output_string;

	}
}


$Mr_Vuemap=new Prototype___Mr_Vuemap;

$Mr_Vuemap->Check_if_xml_path_is_a_Vuemap();
$Mr_Vuemap_says=$Mr_Vuemap;



class Prototype___Mr_Xml
{
	protected $xml_file;
	protected $xml_path;
	
	protected $xml_xdm;
	
	use Magic_Getter;
	
	
	
	public function Get_Xml_from_Mr_Vuemap()
	{
		global $Mr_Vuemap;
		
		$this->xml_file=$Mr_Vuemap->to_xml_output;

	}
	
	
	
	public function Get_Xml_from_Xml_Filepath()
	{		
		$xml_path = $this->xml_path;
		
		$this->xml_file=file_get_contents($xml_path);
		
	}
	
	
	
	public function __construct()
	{
		global $Mr_Standardized_Input;
		
		$this->xml_path = $Mr_Standardized_Input->Please_Provide_the_("xml_path");
	}
	
	
	
	public function Set_Xml_Xdm_to($xml_xdm)
	{
		$this->xml_xdm=$xml_xdm;
	}
}

$Mr_Xml=new Prototype___Mr_Xml;



if ( $Mr_Vuemap->xml_path_is_a_vuemap )
{
	#p();
	#echo "Mr Vuemap says: XML file is a vue map!";
	
	$Mr_Vuemap->Convert_Vuemap_to_XML();
	
	$Mr_Xml->Get_Xml_from_Mr_Vuemap();
}
else
{
	$Mr_Xml->Get_Xml_from_Xml_Filepath();
}


Echo_XHTML_Header("Salty XML Transformer");

$Mr_Saxon = new Saxon\SaxonProcessor();

#$version = $Mr_Saxon->version();

#p();

#Webecho($version);


$xml_xdm = $Mr_Saxon->parseXmlFromString($Mr_Xml->xml_file);


$Mr_Xml->Set_Xml_Xdm_to($xml_xdm);




class Prototype___Mr_Sheet
{
	protected $xslt_path;
	
	use Magic_Getter;
	


	public function __construct()
	{
		global $Mr_Standardized_Input;
		
		$this->xslt_path = $Mr_Standardized_Input->Please_Provide_the_("xslt_path");
	}
	
}




class Prototype___Mr_Params
{
	
	protected $params;
	protected $param_types;
	
	#use Magic_Getter;
	
	
	
	public function __construct()
	{
		global $Mr_Standardized_Input;
		
		$this->params=$Mr_Standardized_Input->unnamed_input;
		$this->param_types=$Mr_Standardized_Input->types_of_unnamed_input;
	}
	
	
	
	public function Give_Params_to_Mr_Transformer()
	{
		if (! $this->params)
		{
			return false;
		}
		
		global $Mr_Saxon;
		global $Mr_Transformer;
		
		$Mr_Xpath=$Mr_Saxon->newXPathProcessor();
		
		$params=$this->params;
		$param_types=$this->param_types;
		
		foreach ($params as $key => $value )
		{
			$type =$param_types[$key];
			
			#echo "Value: $value  Type: $type";
			if ($type=="xpath")
			{
				$xdmValue = $Mr_Xpath->evaluate($value);
			}
			else
			{
				$xdmValue = $Mr_Saxon->createAtomicValue( $value );
			}
			#$xdmNode = $Mr_Saxon->parselXmlFromString( "
			#print_r($xdmValue);

			
			if ($xdmValue != null)
			{
				#echo "Name of Class ", get_class($xdmValue) , "\n";
				$Mr_Transformer->setParameter("$key", $xdmValue);
				
			}
		}
	}
	
}



$Mr_Sheet=new Prototype___Mr_Sheet;

$Mr_Params=new Prototype___Mr_Params;



$Mr_Transformer = $Mr_Saxon->newXsltProcessor();

$Mr_Transformer->setSourceFromXdmValue($Mr_Xml->xml_xdm);

$Mr_Params->Give_Params_to_Mr_Transformer();

$Mr_Transformer->compileFromFile($Mr_Sheet->xslt_path);



class Prototype___Mr_Output
{
	protected $output;
	protected $dest_path;
	
	use Magic_Getter;
	
	
	
	public function Set_Output_to($string)
	{
		$this->output=$string;
	}
	


	public function Somehow_Fix_Unicode_Garbage_with_Perl()
	{
		global $Mr_Standardized_Input;
		global $Perl_Tools_Root;
			
		$Fullpath_to_Somehow_Fix_Unicode_Garbage = "$Perl_Tools_Root/somehow-fix-unicode-garbage-in-Salty-XML-Transformer-output";
		
		$Fullpath_to_Somehow_Fix_Unicode_Garbage = escapeshellarg( $Fullpath_to_Somehow_Fix_Unicode_Garbage );


		$dest_path = $Mr_Standardized_Input->Please_Provide_the_("dest_path");
		
		$esc_dest_path = escapeshellarg( $dest_path );
		
		$command_line="perl $Fullpath_to_Somehow_Fix_Unicode_Garbage $esc_dest_path  2>&1";

		
		$result = exec( $command_line, $output, $retcode );
		
		
		
				$count_of_lines=count($output);
		
		# 19:59:34 03/28/2017.  This part gets rid of errors possibly printed at the top by unrelocvuecomments.
		# And shows those errors to the user.

		$arrin=0;
		foreach ($output as $line)
		{
			$arrin++;
			if ( strpos($line, "Hopefully there's no more error text below this line." ) === 0)
			{
				break;
			}
			
		}
		if ($arrin!=0)
		{
			$arrin++;
			
			$error_lines=array_splice($output, 0, $arrin-1);
			
			$error_lines_string=implode("\n", $error_lines);
			
			$message="The output of 'somehow-fix-unicode-garbage' contained these errors at the top.  They were removed from the output file:";
			
			Web_and_Shell_Echoes($message, $message);
			
			$message="

$error_lines_string";
			Web_and_Shell_Echoes( $message, $message, "replace less thans in web echo", "pre");
			
			Webonly_Echo("<p><br><hr><p><br>");			
			
		}

		$output_string=implode("\n", $output);
		
		
		if ($retcode != 0 )
		{
			$output_string=implode("\n", $output);
			
			$error_message="Aborting because Perl script somehow-fix-unicode-garbage failed!  Error $retcode:
		$output_string";
			Web_and_Shell_Echoes( $error_message, $error_message, "replace less thans in web echo");
			
			exit($retcode);
		}
		
		
		
		$output_string=implode("\n", $output);
		
		$this->Set_Output_to($output_string);

		$this->Create_Output_File();
		
	}
	
	
	
	public function Convert_Unicode_Surrogate_Pairs_to_HTML_Entities()
	{		
		$text=$this->output;
		
		# 19:49:21 03/28/2017.  Slightly modified code from:
		# http://stackoverflow.com/questions/39226593/how-to-convert-utf61-surrogate-pairs-to-equivalent-hex-codepoint-in-php
		
		$result= 
			preg_replace_callback
			(
				'/\\\\x\{(D[89ab][0-9a-f]{2})\}\\\\x\{(D[c-f][0-9a-f]{2})\}/i', 
				
				function ($matches) {
					$first = $matches[1];
					$second = $matches[2];
					$value = ((eval("return 0x$first;") & 0x3ff) << 10) | (eval("return 0x$second;") & 0x3ff);
					$value += 0x10000;
					return "&#$value;";
				},
				
				$text
			);

		$this->output=$result;
	}
	
	
	
	public function Rename_Output_File( $filename )
	{
		$dest_path=$this->dest_path;
		
		$dest_parentdirs=dirname($dest_path);
		
		$new_dest_path="$dest_parentdirs/$filename";
		
		rename($dest_path, $new_dest_path);
	}
	
	
	
	public function Create_Output_File()
	{
		$dest_path=$this->dest_path;
		$output=$this->output;
		
		file_put_contents( $dest_path, $output);
	}
	
	
	
	public function __construct()
	{
		global $Mr_Standardized_Input;
		
		$this->dest_path = $Mr_Standardized_Input->input["dest_path"];
	}
}


$result = $Mr_Transformer->transformToString(); 



if ($result===null)
{
	echo "<p>Result is null";
	
	$errCount = $Mr_Transformer->getExceptionCount();
	
	if ($errCount > 0 )
	{
			for ($i =0; $i < $errCount; $i++  )
			{
				$errCode = $Mr_Transformer->getErrorCode(intval($i));
				$errMessage = $Mr_Transformer->getErrorMessage(intval($i));
				p();
				echo 'Expected error: Code='.$errCode.' Message='.$errMessage;
			}
			$Mr_Transformer->exceptionClear();
	}
}

#$Mr_Transformer->clearParameters();
#$Mr_Transformer->clearProperties();



$Mr_Output=new Prototype___Mr_Output;

$Mr_Output->Set_Output_to($result);
$Mr_Output->Create_Output_File();


if ( $Mr_Standardized_Input->says("should_convert_output_back_to_vue_format" ) )
{
	$Mr_Vuemap->Convert_Output_to_Vuemap();	
}
else
{
	if ( $Mr_Standardized_Input->says("should_try_to_convert_trashed_unicode_to_entities" ) )
	{
		$Mr_Output->Create_Output_File();
		$Mr_Output->Somehow_Fix_Unicode_Garbage_with_Perl();
		$Mr_Output->Convert_Unicode_Surrogate_Pairs_to_HTML_Entities();
		$Mr_Output->Create_Output_File();
	}
}



Webonly_Echo("<p><br>");
Webonly_Echo($Mr_Output->output, "replace less thans", "pre");

Webonly_Echo("<p><br><hr><p><br>");

echo $Mr_Output->output;

exit;