<?php #-*-Mode: php; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/*
instruction_document_parser for PHP 4.
Copyright (C) 2004 John J Foerch
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Class instruction_document_parser parses a JJFMapper XML Instruction
* Document. This class was written based on xml_detail_parser, but has been
* specialized for handling JJFMapper Instruction Documents to minimize on
* memory requirements for the Instruction Document data structure.
*/
//It is here noted that the tagset for a jjfmapper config file
//is inclusive of all possible variations of alphabetic case.
if (!defined('LF')) define('LF',"\n");
//an enumeration of output formats
define ('JJFM_PNG',1);
define ('JJFM_JPEG',2);
define ('JJFM_RAW_OUTPUT',4);
class instruction_document_parser {
//
// Variables for parsing the XML
//
var $ok=true;
var $error=false;
var $content='';
var $owner;
var $supported_tags;
var $level=0;
var $ignore_level=false;
var $current_tag_is_complete=false;
var $open_element_for_current_end_element=false;
var $current_index=0;
var $tag_indices=array();
//
// Variables used by other parts of JJFMapper.
//
var $symbols;
//cache
var $cached_map;
var $cached_projection;
var $cache_dir=JJFM_CACHEDIR;
var $name='';
var $namecrc;
//output
var $output_file=false;
var $output_format=false;
var $output_flags=0;
//layer
var $layers=array();
var $layer_names=array();
//projection
var $projection=array('imwidth' => JJFM_DEFAULT_WIDTH,
'imheight' => JJFM_DEFAULT_HEIGHT);
var $fit=false;
var $fit_spec=false;
//debug
var $debug_file = false;
var $debug_output_mode = 'w';
function instruction_document_parser (&$xmldata,&$symbols) {
$this->content = $xmldata;
$this->symbols = $symbols;
$this->supported_tags = array('JJFMAPPER'=>0,'PROJECTION'=>0,
'OUTPUT'=>0,'DEBUG'=>0,'CACHE'=>0,
'LAYER'=>0);
$parser = xml_parser_create();
xml_set_object ($parser, $this);
xml_set_element_handler ($parser,'start_element','end_element');
$parse_success = xml_parse ($parser,$this->content,true);
if ($parse_success === false)
{
$this->ok = false;
$this->error = xml_error_string(xml_get_error_code($parser));
}
xml_parser_free ($parser);
$this->validate();
}
function start_element (&$parser, $name, &$attr) {
++$this->level;
$position = xml_get_current_byte_index ($parser);
$fulltag = substr ($this->content, $position);
$fulltag = substr ($fulltag, 0, strpos ($fulltag,'>')+1);
$type = $fulltag[strlen ($fulltag) - 2] == '/' ? 'complete' : 'open';
if ($type == 'complete') $this->current_tag_is_complete = true;
if ($this->ignore_level == false &&
isset ($this->supported_tags[$name]) == false)
{
$this->ignore_level = $this->level;
}
if ($this->ignore_level == false)
{
$method = 'start_element_'.$name;
if (method_exists($this,$method))
{
call_user_func(array(&$this,$method),$parser,$attr);
}
$this->tag_indices[] = array('index' => $this->current_index);
if (! $this->current_tag_is_complete)
{
$i = count($this->tag_indices) - 1;
$this->tag_indices[$i]['cdatastart'] =
$position + strlen($fulltag);
}
++$this->current_index;
}
}
function end_element (&$parser, $name) {
if ($this->ignore_level == false)
{
$this->open_element_for_current_end_element =
array_pop ($this->tag_indices);
$open_index = $this->open_element_for_current_end_element['index'];
}
if ($this->current_tag_is_complete)
{
$this->current_tag_is_complete = false;
--$this->level;
if ($this->level == $this->ignore_level)
$this->ignore_level = false;
return;
}
if ($this->ignore_level == false)
{
$position = xml_get_current_byte_index ($parser);
$method = 'end_element_'.$name;
if (method_exists($this,$method))
{
call_user_func(array(&$this,$method),$parser);
}
++$this->current_index;
}
if ($this->level == $this->ignore_level) $this->ignore_level = false;
--$this->level;
}
function start_element_JJFMAPPER (&$parser,&$attr) {
if (isset ($attr['NAME']))
{
$this->name = $attr['NAME'];
$this->namecrc = sprintf('%u',crc32 ($this->name));
}
}
function start_element_DEBUG (&$parser,&$attr) {
define ('DEBUG',true);
//file
if (isset ($attr['MODE']) && $attr['MODE'] == 'APPEND')
{
$this->debug_output_mode = 'a';
}
if (isset ($attr['FILE'])) $this->debug_file = $attr['FILE'];
if (isset ($attr['NOIMAGE'])) define ('JJFM_NO_IMAGE_OUTPUT',1);
}
function start_element_CACHE (&$parser,&$attr) {
define ('JJFM_CACHING',1);
if (isset ($attr['DIR'])) $this->cache_dir = $attr['DIR'];
$sep = strlen ($this->cache_dir) - 1;
if ($this->cache_dir{$sep} == '/')
$this->cache_dir = substr($this->cache_dir,0,
strlen ($jjfm_basedir) - 1);
$this->cache_base_name = $this->cache_dir . '/' .
$this->namecrc . sprintf('%u',crc32 ($this->content));
$this->cached_map = $this->cache_base_name .'.png';
$this->cached_projection = $this->cache_base_name .'.projection';
if (file_exists ($this->cached_map))
{
define ('JJFM_HAVE_CACHED_MAP',1);
if (file_exists ($this->cached_projection))
{
define ('HAVE_CACHED_PROJECTION',1);
}
}
}
function start_element_OUTPUT (&$parser,&$attr) {
if (isset($attr['FORMAT']))
$this->output_format = strtoupper($attr['FORMAT']);
if ($this->output_format === false || $this->output_format == 'RETURN')
define ('JJFM_RETURN',1);
if ($this->output_format == 'PNG')
{
$this->output_flags = JJFM_PNG;
} elseif ($this->output_format == 'JPG' ||
$this->output_format == 'JPEG') {
$this->output_flags = JJFM_JPEG;
}
if (isset ($attr['WIDTH']))
$this->projection['imwidth'] = $attr['WIDTH'];
if (isset ($attr['HEIGHT']))
$this->projection['imheight'] = $attr['HEIGHT'];
if (isset ($attr['FILE']))
{
define ('JJFM_FILE_OUTPUT',1);
$this->output_file = $attr('FILE');
$this->output_flags |= JJFM_RAW_OUTPUT;
}
}
function start_element_PROJECTION (&$parser,&$attr) {
$latmin = isset($attr['LATMIN']) ? $attr['LATMIN'] : false;
$latmax = isset($attr['LATMAX']) ? $attr['LATMAX'] : false;
$lonmin = isset($attr['LONMIN']) ? $attr['LONMIN'] : false;
$lonmax = isset($attr['LONMAX']) ? $attr['LONMAX'] : false;
$latpoint = isset($attr['LAT']) ? $attr['LAT'] : false;
$lonpoint = isset($attr['LON']) ? $attr['LON'] : false;
$padding = isset($attr['PADDING'])? $attr['PADDING'] : 0;
if ($latpoint !== false)
{
$latmin = $latmax = $latpoint;
$lonmin = $lonmax = $lonpoint;
if ($padding == 0) $padding += 10;
$padlastchar = substr($padding,strlen($padding)-1);
if ($padlastchar != '%' && $padlastchar != '°' &&
$padlastchar != '*')
{
$padding .= '°';
}
}
if (isset($attr['FIT']))
{
if (strpos($attr['FIT'],':') !== false)
{
list ($this->fit,$this->fit_spec) =
explode (':',$attr['FIT'],2);
} else {
$this->fit = $attr['FIT'];
}
}
$this->projection['xmin'] = $lonmin;
$this->projection['ymin'] = $latmin;
$this->projection['xmax'] = $lonmax;
$this->projection['ymax'] = $latmax;
$this->projection['padding'] = $padding;
}
function start_element_LAYER (&$parser,&$attr) {
$this->layers[] = $attr;
if (! isset ($attr['FORMAT']))
{
$this->ok = false;
$this->error = 'every layer element must have a format attribute.';
return;
}
if (isset ($attr['NAME']))
{
if (isset ($this->layer_names[$attr['NAME']]))
{
$this->ok = false;
$this->error = 'layer names must be unique.';
}
if (strrchr($attr['NAME'], ':') !== false)
{
$this->ok = false;
$this->error = 'colon (:) is not allowed in layer names.';
}
$this->layer_names[$attr['NAME']] = count ($this->layers) - 1;
}
}
function end_element_LAYER (&$parser) {
//is there cdata to save?
$clip_start=$this->open_element_for_current_end_element['cdatastart'];
$clip_end = xml_get_current_byte_index ($parser);
$clip_length = $clip_end - $clip_start;
if ($clip_length > 0) {
$i = count($this->layers) - 1;
$this->layers[$i]['cdata'] =
substr ($this->content, $clip_start, $clip_length);
}
}
/* validate does very simple validation on the data structure that has
* been created.
*/
function validate () {
// $this->fit must be a key in $this->layer_names
if ($this->fit !== false && ! isset ($this->layer_names[$this->fit]))
{
$this->ok = false;
$this->error = 'projection fit attribute '.
'does not match the name of any layer.';
}
// if caching there must be a name attribute in the root node
if (defined ('JJFM_CACHING') && $this->name == '')
{
$this->ok = false;
$this->error = 'the root node must have a name attribute '.
'when caching.';
}
}
}
?>