<?php
/**
* GeSHi - Generic Syntax Highlighter
* <pre>
* File: geshi/classes/class.geshicodecontext.php
* Author: Nigel McNie
* E-mail: hide@address.com
* </pre>
*
* For information on how to use GeSHi, please consult the documentation
* found in the docs/ directory, or online at http://geshi.org/docs/
*
* This program is part of GeSHi.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* @package geshi
* @subpackage core
* @author Nigel McNie <hide@address.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL
* @copyright (C) 2004 - 2006 Nigel McNie
* @version $Id: class.geshistyler.php 871 2006-12-10 09:53:17Z oracleshinoda $
*
*/
/**
* The GeSHiStyler class
*
* @package geshi
* @subpackage core
* @author Nigel McNie <hide@address.com>
* @since 1.1.0
* @version $Revision: 871 $
*/
class GeSHiStyler
{
// {{{ properties
/**
* @var string
*/
var $charset;
/**
* Array of themes to attempt to use for highlighting, in
* preference order
*
* @var array
*/
var $themes = array('default');
/**
* @var string
* Note: only set once language name is determined to be valid
*/
var $language = '';
/**
* @var boolean
*/
var $reloadThemeData = true;
/**#@+
* @access private
*/
/**
* @var array
*/
var $_styleData = array();
/**
* @var array
*/
var $_wildcardStyleData = array();
/**
* @var array
*/
var $_contextCacheData = array();
/**
* @var GeSHiCodeParser
*/
var $_codeParser = null;
/**
* @var GeSHiRenderer
*/
var $_renderer = null;
/**
* @var string
*/
var $_parsedCode = '';
/**#@-*/
// }}}
// {{{ setStyle()
/**
* Sets the style of a specific context. Language name is prefixed,
* to make theme files shorter and easier
*/
function setStyle ($context_name, $style, $start_name = 'start', $end_name = 'end')
{
geshi_dbg('GeSHiStyler::setStyle(' . $context_name . ', ' . $style . ')');
if ($context_name) {
$context_name = "/$context_name";
}
$this->setRawStyle($this->language . $context_name, $style);
}
// }}}
// {{{ setRawStyle()
/**
* Sets styles with explicit control over style name
*/
function setRawStyle ($context_name, $style)
{
if (substr($context_name, -1) != '*') {
$this->_styleData[$context_name] = $style;
} else {
$this->_wildcardStyleData[substr($context_name, 0, -2)] = $style;
}
}
// }}}
// {{{ removeStyleData()
/**
* Removes any style data for the related context, including
* data for the start and end of the context
*/
function removeStyleData ($context_name, $context_start_name = 'start', $context_end_name = 'end')
{
unset($this->_styleData[$context_name]);
unset($this->_styleData["$context_name/$context_start_name"]);
unset($this->_styleData["$context_name/$context_end_name"]);
geshi_dbg(' removed style data for ' . $context_name);
}
// }}}
// {{{ getStyle()
function getStyle ($context_name)
{
if (isset($this->_styleData[$context_name])) {
return $this->_styleData[$context_name];
}
// If style for starter/ender requested and we got here, use the default
if ('/end' == substr($context_name, -4)) {
$this->_styleData[$context_name] = $this->getStyle(substr($context_name, 0, -4));
return $this->_styleData[$context_name];
}
if ('/start' == substr($context_name, -6)) {
$this->_styleData[$context_name] = $this->getStyle(substr($context_name, 0, -6));
return $this->_styleData[$context_name];
}
// Check for a one-level wildcard match
$wildcard_idx = substr($context_name, 0, strrpos($context_name, '/'));
if (isset($this->_wildcardStyleData[$wildcard_idx])) {
$this->_styleData[$context_name] = $this->_wildcardStyleData[$wildcard_idx];
return $this->_wildcardStyleData[$wildcard_idx];
}
// Maybe a deeper match?
foreach ($this->_wildcardStyleData as $context => $style) {
if (substr($context_name, 0, strlen($context)) == $context) {
$this->_styleData[$context_name] = $style;
return $style;
}
}
//@todo [blocking 1.1.5] Make the default style for otherwise unstyled elements configurable
$this->_styleData[$context_name] = 'color:#000;';
return 'color:#000;';
}
// }}}
// {{{ loadStyles()
function loadStyles ($language = '', $load_theme = false)
{
if (!$language) {
$language = $this->language;
}
geshi_dbg('GeSHiStyler::loadStyles(' . $language . ')');
if ($this->reloadThemeData) {
geshi_dbg(' Loading theme data');
// Trash old data
if ($load_theme) {
geshi_dbg(' Old data trashed');
$this->_styleData = array();
}
// Lie for a short while, to get extra style names to behave
$tmp = $this->language;
$this->language = $language;
foreach ($this->themes as $theme) {
$theme_file = GESHI_THEMES_ROOT . $theme . GESHI_DIR_SEP . $language . '.php';
if (is_readable($theme_file)) {
require $theme_file;
break;
}
}
if ($load_theme) {
$this->reloadThemeData = false;
}
$this->language = $tmp;
}
}
// }}}
// {{{ resetParseData()
/**
* Sets up GeSHiStyler for assisting with parsing.
* Makes sure that GeSHiStyler has a code parser and
* renderer associated with it.
*/
function resetParseData ()
{
// Set result to empty
$this->_parsedCode = '';
// If the language we are using does not have a code
// parser associated with it, use the default one
if (is_null($this->_codeParser)) {
/** Get the GeSHiCodeParser class */
require_once GESHI_CLASSES_ROOT . 'class.geshicodeparser.php';
/** Get the default code parser class */
require_once GESHI_CLASSES_ROOT . 'class.geshidefaultcodeparser.php';
$this->_codeParser =& new GeSHiDefaultCodeParser($this, $this->language);
}
// It the user did not explicitly set a renderer with GeSHi::accept(), then
// use the default renderer (HTML)
if (is_null($this->_renderer)) {
/** Get the GeSHiRenderer class */
require_once GESHI_CLASSES_ROOT . 'class.geshirenderer.php';
/** Get the renderer class */
require_once GESHI_RENDERERS_ROOT . 'class.geshirendererhtml.php';
$this->_renderer =& new GeSHiRendererHTML;
}
// Load theme data now
$this->loadStyles('', true);
}
// }}}
// {{{ setCodeParser()
/**
* Sets the code parser that will be used. This is used by language
* files in the geshi/languages directory to set their code parser
*
* @param GeSHiCodeParser The code parser to use
*/
function setCodeParser (&$codeparser)
{
if (is_subclass_of($codeparser, 'GeSHiCodeParser')) {
$this->_codeParser =& $codeparser;
} else {
trigger_error('GeSHiStyler::setCodeParser(): code parser must be a '
. 'subclass of GeSHiCodeParser', E_USER_ERROR);
}
}
// }}}
// {{{ setRenderer()
/**
* Sets the renderer that will be used.
*
* @param GeSHiRenderer $renderer The renderer to use
*/
function setRenderer (&$renderer)
{
if (is_subclass_of($renderer, 'GeSHiRenderer')) {
$this->_renderer =& $renderer;
} else {
trigger_error('GeSHiStyler::setRenderer(): renderer must be a '
. 'subclass of GeSHiRenderer', E_USER_ERROR);
}
}
// }}}
// {{{ useThemes()
/**
* Sets the themes to use
*/
function useThemes ($themes)
{
$themes = (array) $themes;
$this->themes = array_merge($themes, $this->themes);
$this->themes = array_unique($this->themes);
// Could check here: get first element of orig. $this->themes, if different now then reload
$this->reloadThemeData = true;
}
// }}}
// {{{ addParseData()
/**
* Recieves parse data from the context tree. Sends the
* data on to the code parser, then to the renderer for
* building the result string
*/
function addParseData ($code, $context_name, $data = null, $complex = false)
{
// @todo [blocking 1.1.5] test this, esp. not passing back anything and passing back multiple
// can use PHP code parser for this
// @todo [blocking 1.1.9] since we are only looking for whitespace at start and end this can
// be optimised
if (GESHI_COMPLEX_NO == $complex) {
$this->_addToParsedCode(array($code, $context_name, $data));
} elseif (GESHI_COMPLEX_PASSALL == $complex) {
// Parse all at once
$this->_addToParsedCode($this->_codeParser->parseToken($code, $context_name, $data));
} elseif (GESHI_COMPLEX_TOKENISE == $complex) {
$matches = array();
preg_match_all('/^(\s*)(.*?)(\s*)$/si', $code, $matches);
//echo 'START<br />';
//print_r($matches);
if ($matches[1][0]) {
$this->_addToParsedCode($this->_codeParser->parseToken($matches[1][0],
$context_name, $data));
}
if ('' != $matches[2][0]) {
while ('' != $matches[2][0]) {
$pos = geshi_get_position($matches[2][0], 'REGEX#(\s+)#');
if (false !== $pos['pos']) {
// Parse the token up to the whitespace
//echo 'code: |' . substr($matches[2][0], 0, $pos['pos']) . '|<br />';
$this->_addToParsedCode(
$this->_codeParser->parseToken(substr($matches[2][0], 0, $pos['pos']),
$context_name, $data)
);
// Parse the whitespace
//echo 'ws: |' . substr($matches[2][0], $pos['pos'], $pos['len']) . '|<br />';
$this->_addToParsedCode(
$this->_codeParser->parseToken(substr($matches[2][0], $pos['pos'], $pos['len']),
$context_name, $data)
);
// Trim what we just parsed
$matches[2][0] = substr($matches[2][0], $pos['pos'] + $pos['len']);
} else {
// No more whitespace
//echo 'no more whitespace: |' . $matches[2][0] . '<br />';
$this->_addToParsedCode($this->_codeParser->parseToken($matches[2][0],
$context_name, $data));
break;
}
}
}
if ($matches[3][0]) {
$this->_addToParsedCode($this->_codeParser->parseToken($matches[3][0],
$context_name, $data));
}
} // else wtf???
}
// }}}
// {{{ _addToParsedCode()
/**
* Adds data from the renderer to the parsed code
*/
function _addToParsedCode ($data)
{
if ($data) {
if (!is_array($data[0])) {
$this->_parsedCode .= $this->_renderer->parseToken($data[0], $data[1], $data[2]);
} else {
foreach ($data as $dat) {
$this->_parsedCode .= $this->_renderer->parseToken($dat[0], $dat[1], $dat[2]);
}
}
}
}
// }}}
// {{{ addParseDataStart()
function addParseDataStart ($code, $context_name, $start_name = 'start', $complex = false)
{
$this->addParseData($code, "$context_name/$start_name", null, $complex);
}
// }}}
// {{{ addParseDataEnd()
function addParseDataEnd ($code, $context_name, $end_name = 'end', $complex = false)
{
$this->addParseData($code, "$context_name/$end_name", null, $complex);
}
// }}}
// {{{ getParsedCode()
function getParsedCode ()
{
// Flush the last of the code
$this->_addToParsedCode($this->_codeParser->flush());
$result = $this->_renderer->getHeader() . $this->_parsedCode . $this->_renderer->getFooter();
$this->_parsedCode = '';
return $result;
}
// }}}
}
?>