Location: PHPKode > projects > Network Configuration Store > NCS-3.0.2/wwwroot/inc/CSFileDiff.class.php
<?php

// CSFileDiff
//
// FIXME Proper copyright for Horde here!
//

include('Text/Diff.php');
include('Text/Diff/Renderer.php');
include('Text/Diff/Renderer/unified.php');

class CSFileDiff
{
	private $differences;
	public function &getDifferences() { return $this->differences; }

	public function __construct(CSConfig &$oldFile, CSConfig &$newFile)
	{
		//
		// Build a new native Text_Diff object, feeding in contents from
		// the old configuration and the new configuration record.
		//
		$differ = new Text_Diff('native', array(
							explode("\n", $oldFile->GetData()),
							explode("\n", $newFile->GetData())
						));

		//
		// Now we need to render this diff output into an unified diff, so
		// that our diff parser can read it!
		//
		$renderer = new Text_Diff_Renderer_unified();
		$unidiff = explode("\n", $renderer->render($differ));

		//
		// Now parse the unified diff.
		//
		$this->ParseUnifiedDiff($unidiff);
	}
	public function __destruct() { }

	private function ParseUnifiedDiff(Array $unidiff)
	{
		$this->differences = array();

		$cols = array( array(), array() ); // Hold the left and right columns of lines for change blocks
		$state = 'empty';

		foreach($unidiff as $line)
		{
			// Look for a header which indicates the start of a diff chunk

			if (preg_match('/^@@ \-([0-9]+).*\+([0-9]+).*@@(.*)/', $line, $regs))
			{
				// Push any previous header info to the return stack
				if (isset($data))
					$this->differences[] = $data;

				$data = array(
					'type' => 'header',
					'oldline' => $regs[1],
					'newline' => $regs[2],
					'contents' > array()
				);

				$data['function'] = isset($regs[3]) ? $regs[3] : '';
				$state = 'dump';
			}
			elseif ($state != 'empty')
			{
				// We are in a chunk, so split out the action (+/-) and the line
				preg_match('/^([\+\- ])(.*)/', $line, $regs);

				if (count($regs) > 2)
				{
					$action = $regs[1];
					$content = $regs[2];
				}
				else
				{
					$action = ' ';
					$content = '';
				}

				if ($action == '+')
				{
					// This is just an additional line
					if ($state == 'dump' || $state == 'add')
					{
						// Start adding to the addition stack
						$cols[0][] = $content;
						$state = 'add';
					}
					else
					{
						// This is inside a change block, so
						// start to accumulate lines
						$cols[1][] = $content;
						$state = 'change';
					}
				}
				elseif ($action == '-')
				{
					// This is a removal line
					$cols[0][] = $content;
					$state = 'remove';
				}
				else
				{
					// An empty block with no action...
					switch ($state)
					{
						case 'add':
							$data['contents'][] = array(
								'type' => 'add',
								'lines' => $cols[0]
							);
							break;
						case 'remove':
							// We have some removal lines pending
							// in our stack, so flush them
							$data['contents'][] = array(
								'type' => 'remove',
								'lines' => $cols[0]
							);
							break;
						case 'change':
							// We have both remove and addition lines,
							// so this is a change block!
							$data['contents'][] = array(
								'type' => 'change',
								'old' => $cols[0],
								'new' => $cols[1]
							);
							break;
					}

					$cols = array( array(), array() );
					$data['contents'][] = array(
						'type' => 'empty',
						'line' => $content
					);
					$state = 'dump';
				}
			}
		}

		// Just flush any remaining entries in the columns stack...

		switch ($state)
		{
			case 'add':
				$data['contents'][] = array(
					'type' => 'add',
					'lines' => $cols[0]
				);
				break;
			case 'remove':
				// We have some removal lines pending
				// in our stack, so flush them
				$data['contents'][] = array(
					'type' => 'remove',
					'lines' => $cols[0]
				);
				break;
			case 'change':
				// We have both remove and addition lines,
				// so this is a change block!
				$data['contents'][] = array(
					'type' => 'change',
					'old' => $cols[0],
					'new' => $cols[1]
				);
				break;
		}

		if (isset($data))
			$this->differences[] = $data;
	}

	public function FilesEqual()
	{
		return (count($this->differences) == 0);
	}

	private function code($s)
	{
		if (strlen($s) == 0)
			return '&nbsp;';

		return '<pre>'.$s.'</pre>';
	}

	public function InsertDifference(Array $h)
	{
		$currContext = '';
		$line = '';

		foreach ($h['contents'] as $change)
		{
			if (!empty($currContext) && $change['type'] != 'empty')
			{
				$line = $currContext;
				$currContext = '';

				echo '<tr class="Unchg"><td>'.$this->code($line).'</td>';
				echo '<td>'.$this->code($line).'</td></tr>';
			}

			switch ($change['type'])
			{
				case 'add':
					$line = '';
					foreach($change['lines'] as $l)
						$line .= htmlspecialchars($l).'<br />';

					echo '<tr><td class="AddEmpty">&nbsp;</td><td class="Add">';
					echo $this->code($line).'</td></tr>';
					break;
				case 'remove':
					$line = '';
					foreach($change['lines'] as $l)
						$line .= htmlspecialchars($l).'<br />';

					echo '<tr><td class="Rem">'.$this->code($line).'</td>';
					echo '<td class="RemEmpty">&nbsp;</td></tr>';
					break;
				case 'empty':
					$currContext .= htmlspecialchars($change['line']).'<br />';
					break;
				case 'change':
					// Pop the old/new stacks one by one until empty
					$oldSize = count($change['old']);
					$newSize = count($change['new']);
					$rowMax = max($oldSize, $newSize);

					$l = $r = '';
					for ($row = 0; $row < $rowMax; $row++)
					{
						$l .= isset($change['old'][$row]) ? htmlspecialchars($change['old'][$row]) : '';
						$l .= '<br />';
						$r .= isset($change['new'][$row]) ? htmlspecialchars($change['new'][$row]) : '';
						$r .= '<br />';
					}

					if (!empty($l))
						echo '<tr><td class="Chg">'.$this->code($l).'</td>';
					elseif ($row < $oldSize)
						echo '<tr><td class="Chg">&nbsp;</td>';
					else
						echo '<tr><td class="Unchg">&nbsp;</td>';

					if (!empty($r))
						echo '<td class="Chg">'.$this->code($r).'</td></tr>';
					elseif ($row < $newSize)
						echo '<td class="Chg">&nbsp;</td></tr>';
					else
						echo '<td class="Unchg">&nbsp;</td></tr>';

					break;
			}
		}

		if (!empty($currContext))
		{
			$line = $currContext;
			$currContext = '';

			echo '<tr class="Unchg"><td>'.$this->code($line).'</td>';
			echo '<td>'.$this->code($line).'</td></tr>';
		}
	}
}

?>
Return current item: Network Configuration Store