sysmodellibs/sysmodelgen/src/SysModelGen.pm
changeset 7 3c36c452f013
parent 6 5b32dc297d05
equal deleted inserted replaced
6:5b32dc297d05 7:3c36c452f013
       
     1 # Copyright (c) 2007-2010 Nokia Corporation and/or its subsidiary(-ies).
       
     2 # All rights reserved.
       
     3 # This component and the accompanying materials are made available
       
     4 # under the terms of "Eclipse Public License v1.0"
       
     5 # which accompanies this distribution, and is available
       
     6 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 #
       
     8 # Initial Contributors:
       
     9 # Nokia Corporation - initial contribution.
       
    10 #
       
    11 # Contributors:
       
    12 #
       
    13 # Description:
       
    14 # Package:      SysModelGen
       
    15 # Build an SVG System Model diagram
       
    16 # 
       
    17 #
       
    18 
       
    19 package SysModelGen;
       
    20 
       
    21 use Cwd;
       
    22 use Cwd 'abs_path';
       
    23 use File::Copy;
       
    24 use File::Path;
       
    25 use FindBin;
       
    26 use lib $FindBin::Bin."/../common";
       
    27 use Getopt::Long qw(:config no_ignore_case);
       
    28 use File::Basename;
       
    29 use File::Spec;
       
    30 use Logger;
       
    31 use Env qw(@PATH);
       
    32 use Env qw(@PATHEXT);
       
    33 use Env qw(@CLASSPATH);
       
    34 use strict;
       
    35 
       
    36 my @Filters;
       
    37 
       
    38 
       
    39 use constant KSystemModelGenerator							=> 201;
       
    40 
       
    41 #-------------------------------------------------------------------------------------------------
       
    42 # Subroutine:   new
       
    43 # Purpose:      
       
    44 # Input:        None (extracted from command line args)
       
    45 # Output:       A reference to itself
       
    46 #-------------------------------------------------------------------------------------------------
       
    47 sub new
       
    48 	{
       
    49     my $package = shift;
       
    50     my $self = {};              # Create reference to object
       
    51     bless $self,  $package;    # Associate a reference with class name
       
    52     
       
    53 	my $dataroot =&SystemModelXmlDataDir();
       
    54 	my @yr = gmtime();
       
    55 	my %Args = (
       
    56 	'iHelp'						=> {'param' => "h",
       
    57 		 'desc' => 'Help on usage'}, 
       
    58 	'iIniFile'					=> { 'param' => "i=s", 'type'=>'file',
       
    59 		'desc' => 'An INI file listing one argument per line, with the syntax: <argument> = <value>. Command line arguments will override ini file settings.' }, 
       
    60 	'iDiagram'				=> { 'param' => "output=s" , default=>"sysmodel.svg",'type'=>'outfile',
       
    61 		 'class' => 'Build Control', 'desc' => 'The name of the file to save the built System Model SVG. If in the format filename.svgz, it will attempt to compress the file. If compression is not supported, it will rename the output to filename.svg. Defaults to sysmodel.svg or sysmodel.svgz if -compress is set.'},
       
    62 	'iOutputCsv'				=> { 'param' => "csv_output=s" , 'type'=>'outfile',
       
    63 		 'class' => 'Build Control', 'desc' => 	'The name of the file to save a CSV description of the built System Model. Only items shown on the system model will be included.'},
       
    64 	'iCsvColumns'				=> { 'param' => "csv_columns=s" , 'type'=>'outfile',
       
    65 		 'class' => 'Build Control', 'desc' => 	'Comma-separated list of columns to include in the output CSV.  This does nothing if -csv_output is not present. By default (if -csv_columns is not present), the columns will be a sorted list of all attributes on all items. '},
       
    66 	'iCsvLabels'				=> { 'param' => "csv_labels=s" , 'type'=>'outfile',
       
    67 		 'class' => 'Build Control', 'desc' => 	'Comma-separated list of columns labels include in the output CSV.  Do not use quotes or commas in label names. This does nothing if -csv_output is not present. If this list is shorter than -csv_columns, the remaining columns will use the attribute name as the label. '},
       
    68 	'iOutputXml'				=> { 'param' => 'xml_output=s' ,'type'=>'outfile',
       
    69 		 'class' => 'Build Control', 'desc' => 'The name of the file to save a combined system definition XML. Only items shown in the built system model will be included.'},
       
    70 	'iWarningLevel'					=> { 'param' => "w=s", 'type' => 'number',
       
    71 		'class' =>'Build Control', 'desc' =>  'Warning level. 1: errors only (default), 2: warnings as well as errors, 3: info messages, warnings and errors, 4: all plus deep syntax validation and reporting -- note that this can take a long time to compute so do not use this warning level by default'},
       
    72 	'iLowMem'					=> { 'param' => "lowmem",
       
    73 		'class' =>'Build Control', 'desc' =>  'Build the model storing more data in the temp directory and using less runtime memory. If building fails due to an out of memory condition, try running again with the -lowmem option.'},
       
    74 	"iClean"				=> { 'param' => 'clean' ,
       
    75 		'class' => 'Build Control',  'desc' =>'Caution: if set,  it will delete the contents of the temporary directory.'},
       
    76 	'iCompress'				=> { 'param' => "compress", 
       
    77 		 'class' => 'Build Control', 'desc' => 	'If set, it will attempt to compress the output as an SVGZ file. In order to success gzip must be installed and in the PATH. This will also rename the output file from filename.svg to filename.svgz.'},
       
    78 	'iTemporaryDirectory'				=> { 'param' => "tempdir=s", 'default' => 'drawsvg_temp','type'=>'dir',
       
    79 		 'class' => 'Build Control', 'desc' =>  'Temporary directory for build files.'},
       
    80 	'iLogFile'				=> { 'param' => "log=s", 'type'=>'outfile',
       
    81 		'class' => 'Build Control', 'desc' =>  'File in which to store output. Defaults to stdout'},
       
    82 	'iModel'				=> { 'param' => "model=s" ,'type'=>'file/uri', 'default' => "$dataroot/ModelTemplate.xml",
       
    83 		'class' => 'Files or URIs', 'desc' => 'The location of the Model XML file to use to build the file. Content of this file will be overridden by anything set on the command line on in an ini file'},
       
    84 	'iSysDefFile'				=> { 'param' => "sysdef=s",  'multi' => 1,'type'=>'file/uri', 'xpath' => '/model/sysdef',
       
    85 		 'class' => 'Model Control', 'desc' =>  'The System Definition XML file(s) used to build the model.'},
       
    86 	"iSourceRoot"			=> {'param'=>'srcvar=s' ,'multi' => 1,
       
    87 		 'class' =>  'Model Control'},
       
    88 	'iPathPrefix'				=> {'param' => 'sysdef-prefix=s',   'multi' => 1, 'class' =>  'Model Control','type'=>'file/uri',},
       
    89 	"iSysDefPath"			=> {'param' => "sysdef-path=s",  'multi' => 1,'type'=>'path',
       
    90 		 'class' => 'Model Control', 'desc' =>  'The directory which the system definition file should be considered to be in when turning unit\'s relative links into absolute paths. This is only necessary to provide if the result requires the absolute paths to be meaningful. The order of this parameter must match the order of the -sysdef parameter'},
       
    91 	'iShapes'				=> { 'param' => "shapes=s", 'xpath' => '/model/@shapes','type'=>'file/uri',
       
    92 		 'class' => 'Files or URIs', 'desc' => 'The location of the Shapes XML file used to provide rules to control  the display of the system model items. If not present, default behaviour  (in Shapes.xml) is used. This and the default bahaviours are overrriden by  using the -color, -border, -pattern, and -style options. '},
       
    93 	'iLink'				=> { 'param' => "link=s",  'xpath' => '/model/@link',
       
    94 		 'class' => 'Files or URIs', 'desc' => 'The base URL to use for all hyperlinks in the model. A base URL will be appended by the type and name (e.g. Blocks/Comms%20Services.html) of the items to create the full URL of the linked file. Window directories will be converted into file URIs.'},
       
    95 	'iLinkExpr'				=> { 'param' => "link-expr=s",   'multi' => 1, 'xpath' => 'model/link',
       
    96 		 'class' => 'Model Control', 'desc' => 'The link used on any system model item. Any values within {...} are evaluled as an expression on the item. All xpath locations in the expresion must be set otherwise the link will not be created for the item'},
       
    97 	'iName'			=> { 'param' => "system_name=s" ,  'xpath' => '/model/@name',
       
    98 		  'class' => "Labels", 'desc' =>'The name of the product described in the model. It appears at  the bottom right.'},
       
    99 	'iRelease'		=> { 'param' => "system_version=s" , 'xpath' => '/model/@ver',
       
   100 		  'class' =>"Labels", 'desc' =>'The version of the product described in the model. It appears  at the bottom right after the name.'},
       
   101 	'iLabel'			=> { 'param' => "model_name=s" ,  'xpath' => '/model/@label',
       
   102 		  'class' =>"Labels", 'desc' =>'The label for the model. It appears at the bottom right,  under the name.'},
       
   103 	'iRevision'		=> { 'param' => "model_version=s",  'xpath' => '/model/@revision',
       
   104 		'class' =>"Labels", 'desc' =>'A number which appears before the model-revision-type.   If specified this overrides the build number used by depmodel.  If not building depmodel, this defaults to "1"'},
       
   105 	'iRevisionType'	=> { 'param' => "model_version_type=s",   'xpath' => '/model/@revision-type',
       
   106 		 'class' =>"Labels", 'desc' =>'One of "draft", "issued", "build", "date" or free-text value. Appears below the model label. If specified this overrides the build number used by DepToolkit.If not building depmodel, this defaults to "draft"'},
       
   107 	'iCopyright'			=> { 'param' => "copyright=s", 'default' => (1900+$yr[5])." Nokia Corporation",  'xpath' => '/model/@copyright',
       
   108 		 'class' =>"Labels", 'desc' =>'The copyright to appear in the lower left. Set to empty string to leave out.'},
       
   109 	'iDistribution'		=> { 'param' => "distribution=s",   'xpath' => '/model/@distribution',
       
   110 		 'class' => "Labels", 'desc' =>'Text to appear on the bottom centre to indicate to whom the  model can be shown. Informational only. Suggested values are "internal", "secret" or "unrestrictred". Not shown if not set.'},
       
   111 	'iLgdTitle'		=> { 'param' => "legend_title=s",  'xpath' => '/model/layout/legend/@label',
       
   112 		'class' =>"Labels", 'desc' =>'The title to appear in the leftmost part of the legend.'},
       
   113 	'iLgdFloat'		=> { 'param' => "legend_float=s",  'xpath' => '/model/layout/legend/@float', 'type' => 'boolean',
       
   114 		'class' =>"Model Control", 'desc' =>'If set, the legend will appear when the mouse hovers over the bottom of the window. The floating legend will span the full width of the window. This may not be readable, depending on the amonent of content in the legend.'},
       
   115 	'iCoreOs'				=> { 'param' => "coreos=s", 'type' => 'on/off/new',
       
   116 		 'class' =>'Model Control', 'desc' => 'Turn on or off Core OS colouring for 9.4 and later models -- For backwards compatibility only! Use "on" for Symbian OS 9.4 models and "new" for Symbian OS 9.5 and later models (non-Foundation)'},
       
   117 
       
   118 	'iExtra'				=> { 'multi' => 1, 'param' => "sysinfo=s",'type'=>'file/uri',  info=>'extra',  'xpath' => '/model/sysdef',
       
   119 		 'class' => 'Files or URIs', 'desc' =>  'The location of extra component information used to provided additional  properies for components.  By default,  the provided "SystemInfo.xml" is used.'},
       
   120 	'iLocalize'			=> {  'multi' => 1, 'param' => "localize=s", 'xpath' => '/model/layout','type'=>'file/uri',  info=>'abbrev',
       
   121 		 'class' => 'Files or URIs', 'desc' => 'The location of the Localization file used to provide displayable names for the model entities.'},
       
   122 	'iDict'			=> { 'param' => "dictionary=s", 'type'=>'file/uri', 
       
   123 		 'class' => 'Build Control', 'desc' => 'A term dictionary file used to semi-intelligently generate the abbreviations for the names of system model entries. Anything mentioned in the Localization files overrides generated abbreviations.'},
       
   124 	'iS12'				=>  { 'multi' => 1, 'param' => "s12=s",'type'=>'file/uri' , info=>'s12', 'xpath' => '/model/sysdef',
       
   125 		'depr' => "Only works on 2.0 syntax and older models",
       
   126 		 'class' => 'Files or URIs', 'desc' =>  'The location of the Schedule 12 XML file used to provide the border shapres of the components. If this a directory, the S12 XML file is found by appending "Symbian_OS_v[system_version]_Schedule12.xml" to the directory.'},
       
   127 	'iLevels'				=> {  'multi' => 1, 'param' => "levels=s",'type'=>'file/uri' , info=>'levels', 'xpath' => '/model/sysdef',
       
   128 		'depr' => "Only works on 2.0 syntax and older models. Use info file instead",
       
   129 		'class' =>'Files or URIs', 'desc' => 'The location of the Levels XML file used to override the  stacking of collections. '},
       
   130 	'iDepsFile'				=> { 'multi' => 1, 'param' => "deps=s",  'xpath' => '/model/sysdef','type'=>'file/uri',  info=>'extra', 
       
   131 		'class' => 'Files or URIs', 'desc' =>  'The location of a sysinfo file containing Dependencies.  If not present, dependencies will not be drawn'},
       
   132 
       
   133 	'iColor'				=> {  'multi' => 1, 'param' => "color=s", 'xpath' => '/model/layout','type'=>'file/uri', 'info'=>'color',
       
   134 		 'class' =>'Files or URIs', 'desc' =>  'The location of a Values XML file used to specify per-component colours. If not present, the default colours are used.'},
       
   135 	'iBorder'		=> {  'multi' => 1, 'param' => "border-shape=s", 'xpath' => '/model/layout','type'=>'file/uri','info'=>'border',
       
   136 		 'class' =>'Files or URIs', 'desc' => 'The location of a Values XML file used to specify the shape (border)  of each component. If not present, the default borders are used.'},
       
   137 	'iOverlay'				=> { 'multi' => 1, 'param' => "pattern=s", 'xpath' => '/model/layout','type'=>'file/uri','info'=>'overlay',
       
   138 		 'class' => 'Files or URIs', 'desc' =>  'The location of a Values XML file used to specify per-component overlay patterns. If not present, the default patterns (for new  and reference components) are used.'},
       
   139 	'iStyle'		=> { 'multi' => 1, 'param' => "border-style=s", 'xpath' => '/model/layout','type'=>'file/uri','info'=>'style',
       
   140 		 'class' => 'Files or URIs', 'desc' =>  'The location of a Values XML file used to specify per-component border  styles. If not present, the default border styles are used. '},
       
   141 
       
   142 	'iFilter'				=>{ 'type' => 'filter-name', 'multi' => 1, 'param' => "filter=s",
       
   143 		'depr' => "Only works on 2.0 syntax and older models",
       
   144 		 'class' =>'Model Control', 'desc' => 'The name of a filter to turn on when building the model.  All filters on an item must be present in this list in order for that item to appear. Can have any number of these Defaults to "java" and "gt"'},
       
   145 	'iFilterHas'			=>{ 'type' => 'filter-name', 'ordered' => 1,'param' => "filter-has=s",
       
   146 		'class' => 'Model Control', 'desc' =>'Like -filter, except any filter on an item must be present in this list in order for that item to appear. Include "*" in the list in order to show items with no filters. Equivalent to "-show-attr filter xxx"'},
       
   147 	'iShow'			=> {  'type' =>  'attr[=val]',  'ordered' => 1,'param' => "show-attr=s",
       
   148 		'class' =>'Model Control', 'desc' => 'A mechanism of filtering which allows filtering based on component attribute values. If a value is set for that attribute, the component will be shown. Use in conjunction with -hide-attr for fine contol of what is shown. "class" and "filter" attribtues are handled specially -- see the documentation for details'},
       
   149 	'iHide'			=> { 'type' =>  'attr[=val]', 'ordered' => 1,'param' => "hide-attr=s",
       
   150 		 'class' =>'Model Control', 'desc' => 'A mechanism of filtering which allows filtering based on component attribute values. If a value is set for that attribute, the component will not be shown on the model. Use in conjunction with -show-attr for fine contol of what is shown. "class" and "filter" attribtues are handled specially -- see the documentation for details'},
       
   151 	'iIgnore'				=> { 'type' => 'item', 'multi' => 1, 'param' => "ignore=s", 'xpath' => '/model/ignore',
       
   152 		 'class' =>'Model Control', 'desc' => 'The ID of a model entity to not draw. Any number of these can be used'},
       
   153 	'iIgnoreMeta'				=> { 'type' => 'item', 'multi' => 1, 'param' => "ignore-meta=s", 'xpath' => '/model/ignore',
       
   154 		 'class' =>'Model Control', 'desc' => 'The "rel" meta value to ignore. Takes the form of [relvalue] or [relvalue]:[type]. Any number of these can be used'},
       
   155 
       
   156 	'iNavCtrl'				=>{'param' => "navctrl=s" , 'type'=>'boolean' , 'xpath' => '/model/layout/@navctrl',
       
   157 		'class' =>'Model Control', 'desc' => 'If set, a navigation control widget will appear in the upper left corner of the model. The control might not work on some SVG viewers.'},
       
   158 	'iDetail'				=>{'param' => "detail=s", 'type' =>  'item-type' ,  'xpath' => '/model/layout/@detail',
       
   159 		'class' =>'Model Control', 'desc' => 'The type of the smallest System Model entity to draw. One of "layer", "package", "collection" or "component".  Defaults to "component"'},
       
   160 	'iLevelDetail'				=>{'param' => "level-detail=s", 'type' =>  'show/hide' ,  'xpath' => '/model/layout/@levels',
       
   161 		'class' =>'Model Control', 'desc' => 'Toggles display of level names on packages or layers. A value of "show" will display level names inside either layers (at "layer" level of detail only) or packages (at "package" level of detail only). A value of "hide" (default) will not show any level names.'},
       
   162 	'iDetailType'				=> { 'param' => "detail-type=s", 'type' => 'type' , 'xpath' => '/model/layout/@detail-type',
       
   163 		'class' =>'Model Control', 'desc' => 'If set to "fixed", the smallest System Model entity drawn will have a fixed with (rather then sized by their invisible components). This can be used to reduce the size and complexity of the overall model.'},
       
   164 	'iPlaceholderDetail'				=> { 'param' => "placeholder=s", 'type' => 'item-type' , 'xpath' => '/model/layout/@placeholder-detail',
       
   165 		'class' =>'Model Control', 'desc' => 'The type of the smallest *empty* System Model entity to draw. One of "layer", "package", "collection" or "component".  For example, if set to "package" empty layers and packages will be drawn, but empty collections will be ignored. If not set, no empty items will be drawn.'},
       
   166 	'iPageWidth'			=>{'param' => "page-width=s", 'type' =>  'length', 'xpath' => '/model/layout/@page-width', 
       
   167 		'depr' => "Only works on 2.0 syntax and older models",
       
   168 		 'class' =>'Model Control', 'desc' => 'The width of the drawn image (with units). If not specified it will fit the viewer window. Valid units: "in", "mm", "cm", "px", "pt"'},
       
   169 	'iStatic'				=> { 'param' => "static=s", 'type' => 'boolean', 'xpath' => '/model/layout/@static',
       
   170 		'class' =>'Model Control', 'desc' => 'If present, the model will not have any mouseover effects (this is  overriden by builing the depmodel).'},
       
   171 	'iPrintResolution'				=>{ 'param' => "dpi=s", 'type' =>  'number', 'xpath' => '/model/layout/@resolution',
       
   172 		'class' =>'Model Control', 'desc' => 'The DPI to use when printing from the Adobe SVG Viewer. If not present, it will print well at A4 size. A value of 300 will look good on A3 size paper'},
       
   173 	'iModelFont'				=>{'param' => "model_font=s", 'type' =>'font', 'xpath' => '/model/layout/@font',
       
   174 		'class' =>'Model Control', 'desc' => 'The name of the base font to use to draw the model. This will be overriden by any custom CSS in the Shapes XML'},
       
   175 	'iVersions'			=>  { 'param' => "version-list=s", 
       
   176 		 'class' =>'Model Control'},
       
   177 	'iLogoSrc'				=>{ 'param' => "logo=s",  'type'=>'file/uri', 'xpath' => '/model/layout/logo/@src',
       
   178 		 'class' => 'Model Control', 'desc' => 'If present, the logo will be drawn in the lower-left corner of the model. If the logo is an SVG file, -logo-width and -logo-height are optional, otherwise the must both be specified'},
       
   179 	'iLogoEmbed'				=>{ 'xpath' => '/model/layout/logo/@embed',	 'class' => 'Model Control' },
       
   180 	'iLogoHeight'				=> { 'param' => "logo-height=s", 'type' =>   'length', 'xpath' => '/model/layout/logo/@height',
       
   181 		 'class' =>'Model Control', 'desc' => 'Specifies the height of the logo (if any) in mm. Width is scaled along with height unless otherwise specified. Both width and height MUST be specified if a bitmap image is used'},
       
   182 	'iLogoWidth'				=> { 'param' => "logo-width=s", 'type' =>  'length', 'xpath' => '/model/layout/logo/@width',
       
   183 		 'class' =>'Model Control', 'desc' => 'Specifies the width of the logo (if any) in mm. Height is scaled along with width unless otherwise specified. Both width and height MUST be specified if a bitmap image is used'},
       
   184 	'iLegendWidth'			=>{ 'param' => "legend-width=s", 'type' =>  '%', 'xpath' => '/model/layout/legend/@width',
       
   185 		 'class' => 'Model Control', 'desc' =>'The percent width of the model the legend takes up. This will scale the size of the legend and model title, but not the logo, to fill the specified space. If a logo is included, but no width specified, the legend cannot be scaled since it will not be able to determine the available space. Note that that -max-legend-scale will further limit the potential width.'},
       
   186 	'iLegendMaxScale'			=>{ 'param' => "legend-max-scale=s", 'type' =>   'scale',   'xpath' => '/model/layout/legend/@maxscale',
       
   187 		'class' => 'Model Control', 'desc' =>'Specifies the maximum scale factor for resizing the legend. If this is present and -legend-width is not, the legend and title will scale to 100% of the available width. If both are present the scale factor will take precedent. If neither is present, the legend will not resize. Note that when this is used, the legend can shrink if it would normally be wider than the model.'},
       
   188 	'iTitleScale'			=> { 'param' => "title-scale=s", 'type' =>  'scale',  'xpath' => '/model/layout/legend/@title-scale',
       
   189 		 'class' => 'Model Control', 'desc' =>'Specifies the scale factor for the size of the title font (the text in the lower right). Use this instead of CSS to control the size, since the model generator needs to explicitly know how much space to allocate for the title.'},
       
   190 	'iXsltParam'			=>{  'multi' => 2, 'param' => "xslt-param=s",
       
   191 		 'class' =>'Build Control', 'desc' => 'Advanced: Parameters to feed directly to the XSLT transforms'},
       
   192 	'iLegendNote'			=>{  'multi' => 1, 'param' => "note=s", 'xpath' => '/model/layout/legend/note',
       
   193 		 'class' => 'Labels', 'desc' => 'Free text to appear inside the legend box, on the rightmost side. If multiple ones are provided, they will appear as separate boxes from left to right. Newlines and other special characters can be entity-encoded (e.g. &#xa;). When using entities in an INI file, you *must* quote the value, otherwise the # will be treated as a comment delimiter.',}
       
   194 				);
       
   195 
       
   196 	$self->{iArgs} = \%Args;
       
   197 
       
   198     
       
   199     # basic test of command line:
       
   200     if (scalar(@ARGV) == 0)
       
   201     	{
       
   202 		$self->Help();
       
   203 		exit Logger::KErrorNone;	# nothing to do. Leave
       
   204     	}
       
   205     
       
   206     # process the input:
       
   207     $self->ParseCommandLineOptions();
       
   208     
       
   209     $self->{iReturnCode} = Logger::KErrorNone;
       
   210     return $self;
       
   211 	}
       
   212 
       
   213 
       
   214 sub ParseCommandLineOptions()
       
   215 	{
       
   216 	my $self = shift;
       
   217 
       
   218 	my %opt;
       
   219 	while(my ($n,$b) = each %{$self->{iArgs}} )
       
   220 		{
       
   221 		if(!$b->{'param'}) {next}  # not a command line arg
       
   222 		my @ps = ($b->{'param'});
       
   223 		($ps[1]=$ps[0]) =~ tr/_-/-_/;
       
   224 		if($ps[1] eq $ps[0]) {shift(@ps)}
       
   225 		foreach my $p (@ps)
       
   226 			{ 
       
   227 			if($b->{'multi'}==1)
       
   228 				{
       
   229 				$opt{$p} = \@{$self->{$n}};
       
   230 				} 
       
   231 			elsif($b->{'multi'}==2)
       
   232 				{
       
   233 				$opt{$p} = \%{$self->{$n}};
       
   234 				} 
       
   235 			elsif($b->{'ordered'})
       
   236 				{
       
   237 				$opt{$p} = \&OrderedOption;
       
   238 				} 
       
   239 			else
       
   240 				{
       
   241 				$opt{$p} = \$self->{$n};
       
   242 				}
       
   243 			}
       
   244 		}
       
   245 
       
   246 	foreach (@ARGV) {
       
   247 		# some MS products replace "-" with en-dash in an effort to be "intelligent". 
       
   248 		# This replaces all leading en-dashes in the command line with "-" 
       
   249 		# There is a small risk that the en-dash is intentional and this will clobber it. 
       
   250 		s/^\x96/-/; 
       
   251 	}
       
   252 
       
   253 	GetOptions(%opt);
       
   254 
       
   255 	if ($self->{'iHelp'})
       
   256 	    {
       
   257 	   	$self->Help();
       
   258 	   	exit Logger::KErrorNone;
       
   259 	   	}
       
   260 
       
   261 	# set read files to absolute paths
       
   262 	my $dir  = cwd;
       
   263 	
       
   264 	while(my ($n,$b) = each %{$self->{iArgs}} )
       
   265 		{
       
   266 		my $type =$b->{'type'};
       
   267 		if( $type eq 'file'  or $type eq 'dir' or $type eq 'file/uri')
       
   268 			{
       
   269 			if($self->{$n} eq '') {next} # no value, so do nothing
       
   270 			if ($b->{'multi'} == 1)
       
   271 				{
       
   272 				foreach my $v (@{$self->{$n}})
       
   273 					{
       
   274 				 	$v =&fixFile($type,$dir,$v);
       
   275 					}
       
   276 				} 
       
   277 			elsif ($b->{'multi'} == 2)
       
   278 				{
       
   279 				while(my ($var,$val)=each (%{$self->{$n}}))
       
   280 					{
       
   281 				 	$self->{$n}->{$var}=&fixFile($type,$dir,$val);
       
   282 					}
       
   283 				}
       
   284 			else
       
   285 				{
       
   286 			 	$self->{$n}  = &fixFile($type,$dir,$self->{$n} );
       
   287 				}
       
   288 			}
       
   289 		}	
       
   290 
       
   291 
       
   292 	@{$self->{'iFiltering'}} = @Filters;
       
   293 	@Filters=();
       
   294 	my $i=0;
       
   295 	for($i=0;$i<=$#ARGV;$i++)
       
   296 		{ # check remaining args to ensure they are valid
       
   297 			if($ARGV[$i]=~/^(http|file):\/\//) { # assume URLs are correct
       
   298 				next;			
       
   299 				}
       
   300 			if($ARGV[$i] eq "-" || $ARGV[$i] eq "") 
       
   301 				{ #special values to use nothing or use the tmp file, but only valid for odd numbered args
       
   302 				if($i%2==1) {next}
       
   303 				$self->HelpBase();
       
   304 				&Logger::LogFatal("Invalid syntax", KSystemModelGenerator, 0,Logger::KIncorrectSyntax);
       
   305 				}
       
   306 			if(!(-e $ARGV[$i])) {
       
   307 				$self->HelpBase();
       
   308 				&Logger::LogFatal("file $ARGV[$i] does not exist", KSystemModelGenerator, 0,Logger::KFileDoesNotExist);
       
   309 			}
       
   310 		}
       
   311 	$self->ReadIniFile();
       
   312 	
       
   313 	if($self->{'iDetail'}) 
       
   314 		{	# for ease of BC with ini files
       
   315 		$self->{'iDetail'} =~  s/^(block|subblock|logical(sub)?set)$/package/ ||
       
   316 		$self->{'iDetail'} =~  s/^(module)$/collection/;
       
   317 		}
       
   318 	
       
   319 
       
   320 	while(my ($n,$b) = each %{$self->{iArgs}} ) # set defaults
       
   321 		{
       
   322 		if($b->{'default'} and !defined $self->{$n})
       
   323 			{
       
   324 			$self->{$n} =$b->{'default'};
       
   325 			}
       
   326 		if($b->{'type'} eq 'boolean' and (defined $self->{$n}))
       
   327 			{ #set booleans to true/false
       
   328 			if($self->{$n} == 1 or $self->{$n} =~/^(yes|on|true|y)$/i)
       
   329 				{
       
   330 				$self->{$n} = 'true';
       
   331 				} 
       
   332 			else
       
   333 				{
       
   334 				$self->{$n} = 'false';
       
   335 				}
       
   336 			}	
       
   337 		}
       
   338 
       
   339 	# computed defaults:
       
   340 
       
   341 	# if saving to .svgz, try to compress
       
   342 	$self->{iCompress} = $self->{iCompress} || ( $self->{iDiagram} =~ /\.svgz$/i );
       
   343 
       
   344 	if($self->{'iLogoSrc'} =~ /\.svg$/i)	# embed SVG logos only
       
   345 		{
       
   346 		$self->{'iLogoEmbed'}= "yes";
       
   347 		}
       
   348 
       
   349 	if(defined $self->{iCoreOs} and  ($self->{'iModel'} eq $self->{'iArgs'}->{'iModel'}->{'default'}))
       
   350 		{
       
   351 		my $dataroot =&SystemModelXmlDataDir();
       
   352 		if($self->{iCoreOs}=~/(on|yes|true)$/i )
       
   353 			{
       
   354 			$self->{'iModel'} = "$dataroot/ModelTemplate.94.xml",
       
   355 			}
       
   356 		elsif($self->{iCoreOs}=~/(off|no|false)$/i )
       
   357 			{
       
   358 			$self->{'iModel'} = "$dataroot/ModelTemplate.xml",
       
   359 			}
       
   360 		elsif(! ($self->{iCoreOs}=~/^[0-9]+$/ ))	# any other non-number
       
   361 			{
       
   362 			$self->{'iModel'} = "$dataroot/ModelTemplate.95.xml",
       
   363 			}
       
   364 		}
       
   365 
       
   366 
       
   367 	$self->{iTemporaryDirectory} =  &fixFile('dir', cwd,$self->{iTemporaryDirectory} ); # now gives the full path name $self->{iTemporaryDirectory}
       
   368 
       
   369 	mkpath $self->{iTemporaryDirectory} if ! -d $self->{iTemporaryDirectory};
       
   370 
       
   371 	# set the log file if needed:
       
   372 	$Logger::LOGFILE = $self->{iLogFile} if $self->{iLogFile};
       
   373 
       
   374 	# set the correct warning level:
       
   375 	#  -w=1: errors only (default)
       
   376 	#  -w=2: warnings as well as errors
       
   377 	#  -w=3: info messages, warnings and errors.
       
   378 	if (defined $self->{iWarningLevel} and $self->{iWarningLevel} > 1)
       
   379 		{
       
   380 		if ($self->{iWarningLevel} == 2)
       
   381 			{
       
   382 			$self->{iWarningLevel} = LogItem::WARNING;
       
   383 			}
       
   384 		elsif ($self->{iWarningLevel} == 3)
       
   385 			{
       
   386 			$self->{iWarningLevel} = LogItem::INFO;
       
   387 			}
       
   388 		else # for anything higher than set it to LogItem::VERBOSE
       
   389 			{
       
   390 			$self->{iWarningLevel} = LogItem::VERBOSE;
       
   391 			}
       
   392 		} 
       
   393 	else
       
   394 		{
       
   395 		$self->{iWarningLevel} = LogItem::ERROR;
       
   396 		}
       
   397 	# set the logger up:
       
   398 	$Logger::SEVERITY = $self->{iWarningLevel};
       
   399 
       
   400 	#determine the XSLT Processor we need to use
       
   401 	$self->{'iXslt'} = XsltProcessor();
       
   402 
       
   403 	}
       
   404 
       
   405 sub OrderedOption() {
       
   406 	my $var = shift;
       
   407 	my $val = shift;
       
   408 	if($var=~/^(show|hide)-attr$/) {
       
   409 		my $f = "<filter xmlns='' display='$1' ";
       
   410 		if($val=~s/^([^=]+)=//) {$f.="select='$1' value='$val'/>"}
       
   411 		else {$f.="select='$val'/>"}
       
   412 		push(@Filters,$f);
       
   413 	} elsif($var eq 'filter-has' && $val eq '*') {
       
   414 		push(@Filters,"<filter xmlns='' display='show' select='*'/>");
       
   415 	}elsif($var eq 'filter-has') {
       
   416 		if(!scalar(@Filters)) { # if the 1st is showing a filter than that implies everythig without a filter is turned off 
       
   417 			push(@Filters,'<filter xmlns="" select="*" display="hide"/>');
       
   418 		}
       
   419 		foreach my $v (split(/,/,$val)) {
       
   420 			push(@Filters,"<filter xmlns='' display='show' select='filter' value='$v'/>");
       
   421 		}
       
   422 	}
       
   423 }
       
   424 
       
   425 
       
   426 sub fixFile {
       
   427 	my $type = shift;
       
   428 	my $dir = shift;
       
   429 	my $val = shift;
       
   430 	if($val eq '') {return}
       
   431  	$val  = &FullPath("$dir/",	$val );
       
   432 	if($type eq 'file/uri') { $val =&FileAsUrl($val)}
       
   433 	return $val;
       
   434 }
       
   435 
       
   436 
       
   437 sub FullPath {
       
   438 	my $root = shift;
       
   439 	my $file = shift;
       
   440 	
       
   441 	# If the file is not specified then return null
       
   442 	if (!$file) {
       
   443 		return;
       
   444 	}
       
   445 	
       
   446 	
       
   447 	# If the file is a URL or Windows path then return it as is
       
   448 	if ($file =~ /:/) {
       
   449 		return $file;
       
   450 	}
       
   451 
       
   452 	# If the file is a windows remote path then return it as is
       
   453 	if ($file =~ /\\\\/ || $file =~ /\/\//) {
       
   454 		return $file;
       
   455 	}
       
   456 	
       
   457 	if ($root && !-e $root) {
       
   458 		&Logger::LogFatal("root$root does not exist");
       
   459 	}
       
   460 	
       
   461 	if (-f $root) {
       
   462 		$root = File::Basename::dirname($root)
       
   463 	}
       
   464 
       
   465 	# if root is empty or the same dir, then file is relative
       
   466 	if($root eq '' or $root eq '.') {
       
   467 		return $file;
       
   468 	}	
       
   469 	
       
   470 	# If the file is relative from the root then we want to add the drive letter to the file (if one exists)
       
   471 	if ($file =~ s/^[\\\/]// ) {
       
   472 		if ($root =~ /^([a-z]:)/i) {
       
   473 			return File::Spec->catdir($1, $file);
       
   474 		} else {	# it's a unix path, put the / back on
       
   475 			return "/$file";
       
   476 		}
       
   477 	}
       
   478 	
       
   479 	# Return the concatenated root and filename
       
   480 	return File::Spec->catdir($root, $file);
       
   481 }
       
   482 
       
   483 
       
   484 sub ReadIniFile()
       
   485 	{
       
   486 	my $self = shift;
       
   487 	my %setHere;
       
   488 	return if ! defined $self->{iIniFile};
       
   489 	
       
   490 	# Log a fatal error if the ini file is defined but doesn't exist:
       
   491 	&Logger::LogFatal("ini file does not exist\"$self->{iIniFile}\": $!", KSystemModelGenerator) if ! -e $self->{iIniFile};
       
   492 	
       
   493 	open(INI, $self->{iIniFile}) or 
       
   494 		&Logger::LogFatal("Could not open the ini file \"$self->{iIniFile}\": $!", KSystemModelGenerator);
       
   495 	
       
   496 	&Logger::LogInfo("Reading ini file \"$self->{iIniFile}...", KSystemModelGenerator);
       
   497 	
       
   498 
       
   499 	my %IniMap;		# map from ini var to internal var
       
   500 	foreach my $a (keys %{$self->{'iArgs'}})  {
       
   501 		my $v = $self->{'iArgs'}->{$a}->{'param'};
       
   502 		$v=~s/=.*//;
       
   503 		$IniMap{$v}=$a;
       
   504 		$v=~tr/-_/_-/;		# allow both model_name and model-name
       
   505 		$IniMap{$v}=$a;
       
   506 	}
       
   507 
       
   508 	my $iniDir = $self->{iIniFile};
       
   509 	$iniDir =~ s,[^\\//]+$,,;
       
   510 	#$iniDir .= '\\';
       
   511 
       
   512 	foreach my $line (<INI>)
       
   513 		{
       
   514 		$line =~ s/^\s*//; 		# remove spaces
       
   515 		$line =~ s/\s*$//;		# a/a
       
   516 		$line =~ s/\n$//; 		# remove new line
       
   517 		if($line =~/"/) {
       
   518 			$line =~ s/^(([^"#]*"[^"]*")+)#.*$/$1/; 		# remove comments indicated by # (to the end of the line)
       
   519 		}  else {
       
   520 			$line =~ s/#.*$//; 		# remove comments indicated by # (to the end of the line)
       
   521 		}
       
   522 		next if $line eq ""; 	# ignore blank lines
       
   523 		if ($line =~ m/([^=]+)\s*=\s*(.*)/)
       
   524 			{
       
   525 			my $argType = lc $1; 	# case-insensitive
       
   526 			my $argValue = $2; 		# case-sensitive as it can have strings intended for html output
       
   527 						
       
   528 			$argType =~ s/^\s*//; # remove spaces on either end (Cannot use s/\s+// as this will not be suitable for html text)
       
   529 			$argType =~ s/\s*$//;
       
   530 			$argValue =~ s/^\s*//;
       
   531 			$argValue =~ s/\s*$//;
       
   532 			
       
   533 			$argValue =~ s/^'//; # no need for quotes around the values
       
   534 			$argValue =~ s/'$//;
       
   535 			$argValue =~ s/^"//;
       
   536 			$argValue =~ s/"$//;
       
   537 
       
   538 			if(!defined $IniMap{$argType}) {next}
       
   539 			my $param = $IniMap{$argType};
       
   540 			if($self->{'iArgs'}->{$param}->{'ordered'}) {
       
   541 				&OrderedOption($argType, $argValue);
       
   542 			} else {
       
   543 				my $type = $self->{'iArgs'}->{$param}->{'type'};
       
   544 				# make sure all files mentioned are taken relative to the ini file
       
   545 				if($type eq 'file'  or $type eq 'outfile' or $type eq 'dir' or $type eq 'file/uri')
       
   546 					{
       
   547 					$argValue =&fixFile($type,$iniDir,$argValue);
       
   548 					}
       
   549 
       
   550 				# do not override! Only set values that have not been set on command line already
       
   551 
       
   552 				if ($self->{'iArgs'}->{$param}->{'multi'} == 1) {
       
   553 					if(scalar(@{$self->{$param}})==0 || $setHere{$param}) {
       
   554 				 		push(@{$self->{$param}}, $argValue) ;
       
   555 						$setHere{$param}=1;
       
   556 					}
       
   557 				} elsif ($self->{'iArgs'}->{$param}->{'multi'} == 2) {
       
   558 					$argValue=~s/^([^=]+)=//;
       
   559 					if(scalar(%{$self->{$param}})==0 || $setHere{$param}) {
       
   560 				 		$self->{$param}->{$1}=$argValue;
       
   561 						$setHere{$param} = 1;
       
   562 					}
       
   563 				} else 
       
   564 					{
       
   565 				 	$self->{$param} = $argValue if ! $self->{$param}; 
       
   566 					}
       
   567 				}
       
   568 			}
       
   569 		}
       
   570 
       
   571 	close(INI);
       
   572 	@{$self->{'iFiltering'}} = @Filters if ! @{$self->{'iFiltering'}}; 
       
   573 	@Filters=();
       
   574 	}
       
   575 
       
   576 sub getModel()
       
   577 	{
       
   578 	my $self = shift;
       
   579 
       
   580 	my $tempDirectoryPathname = $self->{iTemporaryDirectory};
       
   581 	my $modelXml = "$tempDirectoryPathname/Model.xml";
       
   582 
       
   583 	if(defined $self->{'iModelCreated'}) {return $modelXml}
       
   584 
       
   585 	my $needsMod=0;
       
   586 	# the following needs a bit of work
       
   587 	foreach my $param (keys %{$self->{'iArgs'}})  {
       
   588 		if (! ($self->{'iArgs'}->{$param}->{'class'} =~ /^(Build Control|)$/ ))
       
   589 			{
       
   590 			if ($self->IsSet($param)) {$needsMod=1; last}
       
   591 			}
       
   592 	}
       
   593 	# if no parameters are set that would impact the model, just use the raw Model XML provided
       
   594 	if(!$needsMod) {return $self->{iModel}}
       
   595 
       
   596 	my $dir = $self->{iModel};
       
   597 	$dir=~s,[^/\\]+$,,;
       
   598 
       
   599 	my $command = $self->XsltTransformCmd("-",$self->{'iModel'},$modelXml,1);  # does not take any params
       
   600 
       
   601 	open XSLT, "|$command" 
       
   602 	#open XSLT, ">$tempDirectoryPathname/xslt.xsl"
       
   603 		||	&Logger::LogFatal("error in running $command", KSystemModelGenerator);
       
   604 	my $basedir = &FileAsUrl($dir);
       
   605 	$basedir=~s,/$,,; # make sure no //
       
   606 print XSLT '<stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform">
       
   607 <variable name="fullpath">',$basedir,'/</variable>
       
   608 <template match="@*" priority="-2"><copy-of select="."/></template>
       
   609 <template match="@href|model/@shapes|logo/@src|legend/@use[not(starts-with(.,&apos;@&apos;) or starts-with(.,&apos;#&apos;))]" priority="-1">
       
   610 	<choose>
       
   611 		<when test="$fullpath!=&apos;&apos; and not(contains(.,&apos;:&apos;) or starts-with(.,&apos;/&apos;))">
       
   612 			<attribute name="{name()}"><value-of select="concat($fullpath,.)"/></attribute>
       
   613 		</when>
       
   614 		<otherwise><copy-of select="."/></otherwise>
       
   615 	</choose>
       
   616 </template>
       
   617 <template match="*" mode="attr"><apply-templates select="@*"/></template>
       
   618 <template match="*" mode="content" priority="-1"><apply-templates select="*"/></template>
       
   619 <template match="*" mode="legend"><copy-of select="."/></template>
       
   620 <template match="note" mode="legend"><copy><copy-of select="@*"/><apply-templates select="." mode="content"/></copy></template>
       
   621 <template match="model" mode="content">
       
   622 	<call-template name="sysdef"/>
       
   623 	<call-template name="filter"/>
       
   624 	<apply-templates select="*"/>
       
   625 </template>
       
   626 <template match="/shapes/*"><param name="m"/>
       
   627 	<if test="not(preceding-sibling::*[name()=name(current())] or $m//legend[@use=concat(\'@shapes#\',name(current()))])">
       
   628 	<element name="legend" namespace="">
       
   629 		<attribute name="use">@shapes#<value-of select="name()"/></attribute>
       
   630 	</element></if>
       
   631 </template>
       
   632 <template match="/shapes/*[namespace-uri(.)!=',"''",']"/>
       
   633 <template match="note" mode="content" priority="-1"><apply-templates select="node()"/></template>
       
   634 <template match="*">
       
   635 <copy><apply-templates select="." mode="attr"/><apply-templates select="." mode="content"/></copy>
       
   636 </template>
       
   637 ';
       
   638 
       
   639 my %at;
       
   640 my %info='';
       
   641 	foreach my $param (keys %{$self->{'iArgs'}})  {
       
   642 		my $cur = $self->{'iArgs'}->{$param};
       
   643 		if (! ($cur->{'class'} =~ /^(Build Control|)$/) && $self->IsSet($param) ) {
       
   644 			if($cur->{'xpath'}) {
       
   645 				my $x = $cur->{'xpath'};
       
   646 				my $match = $x;
       
   647 				$x=~s,/([^/]+)$,,;
       
   648 				my $item = $1;
       
   649 				if($item=~s/^@//) {
       
   650 					if($self->{$param} ne '') {
       
   651 						if($self->{'iArgs'}->{$param}->{'type'} ne 'boolean' || $self->{$param} eq 'true') { # if boolean and false, don't set attribute in XML
       
   652 							$at{$x}.="\t<attribute name=\"$item\">".&SafeXml($self->{$param})."</attribute>\n";
       
   653 						}
       
   654 					}
       
   655 					print XSLT "<template match=\"$match\"/>\n";
       
   656 				}  elsif($item eq 'note') { # just in case there's other things that can take plan text later, can add them here
       
   657 					if($cur->{'multi'}==1) {
       
   658 						my $count =0;
       
   659 						foreach my $n (@{$self->{$param}}) {
       
   660 							print XSLT "<template match=\"$item\[count(preceding::$item)=$count\]\" mode=\"content\">",&SafeXml($n),"</template>\n";
       
   661 							$count++;
       
   662 						}
       
   663 					}  elsif(!$cur->{'multi'}) {
       
   664 						my $n =$self->{$param};
       
   665 						print XSLT "<template match=\"$match\" mode=\"content\">&SafeXml($n)</template>\n";
       
   666 					}
       
   667 				}  elsif($cur->{'info'}) {
       
   668 					my $t = $cur->{'info'};
       
   669 					if( ! ($self->{'iArgs'}->{$param}->{'dontclear'} ))	{ # can force it to be included anyway if necessary
       
   670 						print XSLT "<template match=\"$match/info\[\@type='$t'\]\"/>\n"; # remove from doc, add explicitly
       
   671 					}
       
   672 					if($cur->{'multi'}==1){
       
   673 						foreach my $n (@{$self->{$param}}) {
       
   674 							if($n ne '') {
       
   675 								$info{$match}.="\t<info xmlns='' type='$t' href='$n'/>\n";
       
   676 							}
       
   677 						}
       
   678 					} elsif(!$cur->{'multi'}){
       
   679 						$info{$match}.="\t<info xmlns='' type='$t'  href='",$self->{$param},"'/>\n";
       
   680 					}
       
   681 				} elsif($param eq 'iIgnore' or $param eq 'iIgnoreMeta' or $param eq 'iLinkExpr' or $param eq 'iSysDefFile') {
       
   682 					print XSLT "<template match='",$cur->{'xpath'},"'/>\n"	# ignore any already present if set
       
   683 				}		
       
   684 			} elsif($param eq 'iModel' || $param eq 'iSysDefPath' || $param eq 'iPathPrefix' || $param eq 'iSourceRoot') {
       
   685 			} else {
       
   686 				print STDERR "$param   ",$self->{$param},"\n";  # should not get here
       
   687 			}
       
   688 		}
       
   689 	}
       
   690 	if(scalar @{$self->{'iFiltering'}}) {
       
   691 		print XSLT "<template match='filter'/>\n"	# ignore all already present if set
       
   692 	}
       
   693 	while (my ($a,$b) = each(%at) ){
       
   694 		print XSLT "<template match=\"$a\" mode=\"attr\">\n\t<apply-templates select=\"@*\"/>\n$b</template>\n";
       
   695 	}
       
   696 	print XSLT '<template name="sysdef">';
       
   697 	my $count=0;
       
   698 	foreach my $sys (@{$self->{'iSysDefFile'}}) {
       
   699 		$count++;
       
   700 		print XSLT "\n\t<element name='sysdef' namespace=''>\n";
       
   701 		print XSLT "\t\t<attribute name='href'>$sys</attribute>\n";
       
   702 		my $src=$count;
       
   703 		if(scalar(@{$self->{'iSourceRoot'}}) == 1) {$src=0}
       
   704 		if($self->{'iSourceRoot'}->[$src]) {
       
   705 			print XSLT "\t\t<attribute name='root'>",$self->{'iSourceRoot'}->[$src],"</attribute>\n";
       
   706 		}
       
   707 		$src=$count;
       
   708 		if(scalar(@{$self->{'iPathPrefix'}}) == 1) {$src=0}
       
   709 		if($self->{'iPathPrefix'}->[$src]) {
       
   710 			print XSLT "\t\t<attribute name='path-prefix'>",$self->{'iPathPrefix'}->[$src],"</attribute>\n";
       
   711 		}
       
   712 		$src=$count;
       
   713 		if(scalar(@{$self->{'iSysDefPath'}}) == 1) {$src=0}
       
   714 		if($self->{'iSysDefPath'}->[$src]) {
       
   715 			print XSLT "\t\t<attribute name='path'>",$self->{'iSysDefPath'}->[$src],"</attribute>\n";
       
   716 		}
       
   717 		print XSLT $info{'/model/sysdef'}, 
       
   718 			"<apply-templates select=\"/model/sysdef/info\"/>\n\t</element>\n";
       
   719 		$count++;
       
   720 	}
       
   721 	foreach my $link (@{$self->{'iLinkExpr'}}) {
       
   722 		print XSLT "\t<element name='link' namespace=''><attribute name='expr'>",&SafeXml($link),"</attribute></element>\n";
       
   723 	}
       
   724 	print XSLT "</template>\n",
       
   725 		"<template name=\"filter\">\n";
       
   726 	foreach my $ig (@{$self->{'iIgnore'}}) {
       
   727 		print XSLT "\t<ignore xmlns='' ";
       
   728 		if($ig=~/^(layer|package|block|logicalset|logicalsubset|subblock|collection|module|component):(.*)$/) {print XSLT "type='$1' name='$2'/>\n"}
       
   729 		elsif($ig=~/:.*\//) {print XSLT "namespace='$ig'/>\n"} # assume it's a namespace if it has a colon and a slash
       
   730 		else {print XSLT "ref='$ig'/>\n"}
       
   731 	}
       
   732 	foreach my $ig (@{$self->{'iIgnoreMeta'}}) {
       
   733 		print XSLT "\t<ignore xmlns='' ";
       
   734 		if($ig=~/^(.*):(.*)$/) {print XSLT "meta-type='$2' meta='$1'/>\n"}
       
   735 		else {print XSLT "meta='$ig'/>\n"}
       
   736 	}
       
   737 	print XSLT join("\n\t",@{$self->{'iFiltering'}}),
       
   738 		"</template>\n";
       
   739 
       
   740 	print XSLT '<template match="layout/legend" mode="content">',"\n";
       
   741 	if($self->{'iShapes'}) {print XSLT 	' <apply-templates select="document(\'',$self->{'iShapes'},'\',.)/shapes/*|*"><with-param name="m" select="current()"/></apply-templates>',"\n";}
       
   742 	foreach my $link ('iColor','iBorder','iOverlay'	,	'iStyle') {
       
   743 		my $type=$self->{'iArgs'}->{$link}->{'info'};
       
   744 		if(scalar @{$self->{$link}}) {
       
   745 			my $use ="#$type";
       
   746 			print XSLT "\t<if test=\"not(//legend[\@use='$use'])\">";
       
   747 		} else {
       
   748 			print XSLT "\t<if test=\"../info[\@type='$type']\">";
       
   749 		}
       
   750 		print XSLT "<legend xmlns=\"\" use=\"#$type\"/></if>\n";
       
   751 	}
       
   752 	$count =1;
       
   753 	my $notes='';
       
   754 	foreach my $n (@{$self->{'iLegendNote'}}) {
       
   755 		$notes.="\t<if test='count(//note) &lt; $count'><note xmlns='' width='auto'>".&SafeXml($n)."</note></if>\n";
       
   756 	}
       
   757 	print XSLT "$notes</template>\n",
       
   758 		 '<template match="layout/legend[legend]" mode="content">',
       
   759 		"\n\t<apply-templates select='*' mode='legend'/>\n$notes</template>\n",
       
   760 		'<template match="layout" mode="content">',"\n",$info{'/model/layout'};
       
   761 	if($self->{'iLogoSrc'}) {
       
   762 		print XSLT "\t<if test='not(//logo)'><element name='logo' namespace=''>\n",$at{'/model/layout/logo'},"\t</element></if>\n";
       
   763 	}
       
   764 	print XSLT "\t<apply-templates select='*'/></template>\n";
       
   765 	print XSLT "</stylesheet>\n";
       
   766 	close XSLT;
       
   767 	$self->{'iModelCreated'}=1;
       
   768 	return $modelXml;
       
   769 	}
       
   770 
       
   771 
       
   772 sub GetXsltDir()
       
   773 	{
       
   774 	my $self = shift;
       
   775 	my $xsltDir = $FindBin::Bin."/core";  # calcluated w.r.t root of Dep directory
       
   776 	return $xsltDir;
       
   777 	}
       
   778 
       
   779 
       
   780 sub GetExtrasDir()
       
   781 	{
       
   782 	my $self = shift;
       
   783 	my $dir = $FindBin::Bin."/extra";  # calcluated w.r.t root of Dep directory
       
   784 	return $dir;
       
   785 	}
       
   786 
       
   787 
       
   788 
       
   789 sub FileAsUrl() 
       
   790 	{
       
   791 	my $file = $_[0];
       
   792 	if($file=~/^..+:/){ return $file}	# already a URL
       
   793 	if(-f $file)
       
   794 		{ # abs_path only works on dirs, so strip off file name and put it back when done
       
   795 		if(! ($file=~/^[a-z]:[\\\/][^\\\/]+$/i))
       
   796 			{ # if it's in the root dir, do nothing
       
   797 			my $tail = "/$file";
       
   798 			# if it's just a file name, need to find cwd;
       
   799 			if($file =~ s,([\\/][^\\/]+)$,,)
       
   800 				{
       
   801 				$tail = $1;
       
   802 				}
       
   803 			else {$file = "."}
       
   804 			$file = abs_path($file)."$tail";
       
   805 			}
       
   806 		} 
       
   807 	elsif (-d $file)
       
   808 		{
       
   809 		$file = abs_path($file);
       
   810 		}  # else does not exist, so just convert to unix-style path
       
   811 	$file=~tr/\\/\//;	
       
   812 	return "file:///$file";
       
   813 	}
       
   814 
       
   815 
       
   816 sub RunCmd() {
       
   817 	my $command = shift;
       
   818 	open(EXE,"$command 2>&1|");
       
   819 	while(<EXE>){
       
   820 		chomp;
       
   821 		s/^XSLT Message: //;
       
   822 		s/\.Source tree node:.*$//;
       
   823 		if($_ ne '') {
       
   824 			if(s/^note: //i) {
       
   825 				&Logger::LogInfo($_, KSystemModelGenerator,2, 100);
       
   826 			} elsif(s/^Warning: //) {
       
   827 				&Logger::LogWarning($_,  KSystemModelGenerator,2, 600);
       
   828 			} elsif(s/^Error: //i) {
       
   829 				&Logger::LogError($_,  KSystemModelGenerator,2, 400);
       
   830 			} else {
       
   831 				print STDERR "$_\n";
       
   832 			}
       
   833 		}
       
   834 	}
       
   835 	close(EXE);
       
   836 	return $?;
       
   837 }
       
   838 
       
   839 sub ShouldCreateDepmodel()
       
   840 	{
       
   841 	my $self = shift;
       
   842 	if (@{$self->{iDepsFile}})
       
   843 		{
       
   844 		return 1;
       
   845 		}
       
   846 	my $should = 0;
       
   847 	my $model = $self->getModel();
       
   848 	my $t = $/;
       
   849 	$/='>';
       
   850 	open(M,$model);
       
   851 	while(<M>)
       
   852 		{
       
   853 		if(/<model\s.*deps=/){$should=1;last;}
       
   854 		if(/<info\s.*data-type="Dependencies"/){$should=1;last;}
       
   855 		}
       
   856 	close M;
       
   857 	$/ = $t;
       
   858 	return $should
       
   859 	}
       
   860 
       
   861 
       
   862 sub XsltTransformCmd()
       
   863 	{
       
   864 	my $self = shift;
       
   865 	my $command = $self->{'iXslt'} . ' ' ;
       
   866 
       
   867 	if(join('',@PATH)=~/\\/) {
       
   868 		# use windows path
       
   869 		$command =~ s#\/#\\#g;
       
   870 	}
       
   871 
       
   872 	if($command =~ /xalan\.jar/i) {
       
   873 		return $command.	XalanJTransformCmd(@_);
       
   874 	}
       
   875 	if($command =~ /xsltproc/i) {
       
   876 		return $command.	XsltprocTransformCmd(@_);
       
   877 	}
       
   878 
       
   879 	return $command.	XalanTransformCmd(@_);
       
   880 	}
       
   881 
       
   882 sub XalanTransformCmd()
       
   883 	{
       
   884 	my $xslt = shift;
       
   885 	my $from = shift;
       
   886 	my $to = shift;
       
   887 	my $indent = shift;
       
   888 	my %params = (scalar @_) ? %{$_[0]} : ();
       
   889 
       
   890 	if($from=~/ /) {$from= "\"$from\""}
       
   891 	if($to=~/ /) {$to= "\"$to\""}
       
   892 	if($xslt=~/ /) {$xslt= "\"$xslt\""}
       
   893 
       
   894 	my $command;
       
   895 	while (my($p,$v) = each(%{$_[0]}))
       
   896 		{
       
   897 		$v =~ s/"/&quot;/g;	# replace quotes with entities
       
   898 		$command.= " -p $p \"$v\"";
       
   899 		}
       
   900 
       
   901 	if($indent >=0) {
       
   902 		$command .= " -i $indent";
       
   903 	}
       
   904 	if($to ne '') {
       
   905 		$command .= " -o $to";
       
   906 	}
       
   907 	return "$command $from $xslt";
       
   908 	}
       
   909 
       
   910 
       
   911 sub XalanJTransformCmd()
       
   912 	{
       
   913 	my $xslt = shift;
       
   914 	my $from = shift;
       
   915 	my $to = shift;
       
   916 	my $indent = shift;		# not used in versions of xalan.jar we expect to see
       
   917 	my %params = (scalar @_) ? %{$_[0]} : ();
       
   918 	
       
   919 	if($from=~/ /) {$from= "\"$from\""}
       
   920 	if($to=~/ /) {$to= "\"$to\""}
       
   921 	if($xslt=~/ /) {$xslt= "\"$xslt\""}
       
   922 
       
   923 	my $command;
       
   924 	while (my($p,$v) = each(%{$_[0]}))
       
   925 		{
       
   926 		$v =~ s/"/&quot;/g;	# replace quotes with entities
       
   927 		$command.= " -param $p \"$v\"";
       
   928 		}
       
   929 
       
   930 	if($to ne '') {
       
   931 		$command .= " -out $to";
       
   932 	}
       
   933 
       
   934 	die &Logger::LogError("Model transforms are not supported using Xalan-J", KSystemModelGenerator, 1) if ($xslt eq '-');
       
   935 
       
   936 	$command .= " -xsl $xslt";
       
   937 	$command .= " -in $from";
       
   938 	return $command;
       
   939 	}
       
   940 
       
   941 sub XsltTransform()
       
   942 	{
       
   943 	my $self = shift;
       
   944 	my $to=$_[2];
       
   945 	my $command = $self->XsltTransformCmd(@_);
       
   946 	&Logger::LogInfo("System Call: $command", KSystemModelGenerator,3,800);
       
   947 	if($to eq '') {return `$command`}
       
   948 	return &RunCmd($command);
       
   949 	}
       
   950 
       
   951 sub XsltprocTransformCmd()
       
   952 	{
       
   953 	my $xslt = shift;
       
   954 	my $from = shift;
       
   955 	my $to = shift;
       
   956 	my $indent = shift;
       
   957 	my %params = (scalar @_) ? %{$_[0]} : ();
       
   958 	my $command;
       
   959 
       
   960 	if($from=~/ /) {$from= "\"$from\""}
       
   961 	if($to=~/ /) {$to= "\"$to\""}
       
   962 	if($xslt=~/ /) {$xslt= "\"$xslt\""}
       
   963 
       
   964 	while (my($p,$v) = each(%{$_[0]}))
       
   965 		{
       
   966 		$v =~ s/"/&quot;/g;
       
   967 		$command.= " --param $p \"$v\"";
       
   968 		}
       
   969 
       
   970 	if($to ne '') {
       
   971 		$command .= " -o $to";
       
   972 	}
       
   973 	return "$command $xslt $from";
       
   974 	}
       
   975 
       
   976 
       
   977 sub makeAbbrev 
       
   978 	{
       
   979 	my $self = shift;
       
   980 	my $xsltDir = $self->GetExtrasDir();
       
   981 	my %params = (
       
   982 		'dict' => "'".$self->{'iDict'}."'"
       
   983 	);
       
   984 	if(scalar @{$self->{'iSysDefFile'}}) 
       
   985 		{ # if sysdefs provided, create one abbrev file for each
       
   986 		my $count = 1;
       
   987 		if(! (scalar @{$self->{'iLocalize'}}))
       
   988 			{ # do not fitler out any abbrev already in the model template
       
   989 			$self->{'iArgs'}->{'iLocalize'}->{'dontclear'}=1;
       
   990 			}
       
   991 		foreach my $sysdef (@{$self->{'iSysDefFile'}}) 
       
   992 			{
       
   993 			my $afile =  $self->{'iTemporaryDirectory'} . "/abbrev$count.xml";
       
   994 			my $error = $self->XsltTransform("$xsltDir/makeabbrev.xsl",$sysdef,$afile,1,\%params);
       
   995 			&Logger::LogError("Xalan error ($error) occured in creating abbrev file", KSystemModelGenerator, 1) if $error;
       
   996 			# prepend generated file to the list (order does not matter since the same name appearing twice will have the same abbreviation)
       
   997 			unshift(@{$self->{'iLocalize'}}, &FileAsUrl($afile));
       
   998 			$count++;
       
   999 			}
       
  1000 		}
       
  1001 	else
       
  1002 		{ # no sysdefs provided, run against template model xml
       
  1003 		if(scalar @{$self->{'iLocalize'}})
       
  1004 			{# if localize files provided, include = 0, which means it will ignore any localize files in the model template
       
  1005 				# if there are none provided, then any in the model template will be appended to the generated file
       
  1006 				# this is only needed for the case where it's run on the model. When run on the sysdef, inclusion has no meaning
       
  1007 			$params{'include'} = 0; 
       
  1008 			}
       
  1009 		my $afile =  $self->{'iTemporaryDirectory'} . "/abbrev.xml";
       
  1010 		my $error = $self->XsltTransform("$xsltDir/makeabbrev.xsl",$self->{'iModel'},$afile,1,\%params);
       
  1011 		&Logger::LogError("Xalan error ($error) occured in creating abbrev file", KSystemModelGenerator, 1) if $error;
       
  1012 		# prepend generated file to the list
       
  1013 		unshift(@{$self->{'iLocalize'}}, &FileAsUrl($afile));	
       
  1014 		}
       
  1015 	}
       
  1016 
       
  1017 sub Draw()
       
  1018 	{
       
  1019 	my $self = shift;
       
  1020 	my $genSvg = $self->{'iDiagram'} ne '';
       
  1021 	my $genCsv = $self->{'iOutputCsv'} ne '';
       
  1022 	my $genXml = $self->{'iOutputXml'} ne '';
       
  1023 	my $error; 
       
  1024 
       
  1025 	if(!$genSvg && !$genCsv && !$genXml)  
       
  1026 		{
       
  1027         &Logger::LogFatal("Must specify at least one type of output file. Cannot continue...", KSystemModelGenerator, 0,Logger::KNothingToDo);		
       
  1028 		}
       
  1029 	
       
  1030 	# Step 0:
       
  1031 	# Prepare some file names and create output directory:
       
  1032 
       
  1033 	# construct full path name:
       
  1034 	
       
  1035 	my $xsltDir = $self->GetXsltDir();	
       
  1036 	my $extraDir = $self->GetExtrasDir();	
       
  1037 	my $tempDirectoryPathname = $self->{'iTemporaryDirectory'};
       
  1038 	my $tempStuctureFile = "$tempDirectoryPathname/system_definition_tmp.xml";
       
  1039 	my $tempXslFile = "$tempDirectoryPathname/styling_tmp.xsl";
       
  1040 	my $tempModelFile = "$tempDirectoryPathname/model_tmp.svg";
       
  1041 	my $tempModelFile2 = "$tempDirectoryPathname/model_tmp2.svg";
       
  1042 	my $modelXsl = $xsltDir."/layoutsysdef.xsl";
       
  1043 
       
  1044 	if($self->{'iDict'})	
       
  1045 		{
       
  1046 		&Logger::LogInfo("Create an abbreviation file...", KSystemModelGenerator, 0);
       
  1047 		$self->makeAbbrev();
       
  1048 		}
       
  1049 
       
  1050 	&Logger::LogInfo("Generating Model XML...", KSystemModelGenerator, 0);
       
  1051 	my $modelXml = $self->getModel();
       
  1052 
       
  1053 	# Step 2 - 
       
  1054 	&Logger::LogInfo("Generating merged sysdef XML...", KSystemModelGenerator, 0);
       
  1055 	$error = $self->XsltTransform($modelXsl,$modelXml,$tempStuctureFile,1);  # does not take any params
       
  1056 				
       
  1057 	&Logger::LogError("Xalan error ($error) occured in combining sysdefs", KSystemModelGenerator, 1) if $error;
       
  1058 
       
  1059 	# Step 3 - validation
       
  1060 	if($self->{iWarningLevel} == LogItem::VERBOSE )
       
  1061 		{
       
  1062 		&Logger::LogInfo("Validating merged XML...", KSystemModelGenerator, 0);
       
  1063 		my $errors = $self->XsltTransform($extraDir."/validate.xsl",$tempStuctureFile,'',-1);
       
  1064 		&Logger::LogList(split(/\n/,$errors));
       
  1065 		}
       
  1066 
       
  1067 	if($genSvg)
       
  1068 		{ # only needed for model building 
       
  1069 		&Logger::LogInfo("Generating Model Diagram...", KSystemModelGenerator, 0);
       
  1070 		
       
  1071 		# Step 4
       
  1072 		&Logger::LogInfo("Creating styling XSLT...", KSystemModelGenerator, 1);
       
  1073 		$error = $self->XsltTransform("$xsltDir/shapes.xsl",$modelXml,$tempXslFile,1,
       
  1074 				{%{$self->{'iXsltParam'}},'Model-Transform' =>  "'".&FileAsUrl("$xsltDir/draw.xsl")."'" });
       
  1075 		&Logger::LogError("Xalan error ($error) occured generating Styling transform", KSystemModelGenerator, 2) if $error;
       
  1076 	
       
  1077 		if($self->{iLowMem})
       
  1078 			{ # split step 5 into parts so we don't use as much runtime memory
       
  1079 			# Step 5a
       
  1080 			my $tempDetailsFile = "$tempDirectoryPathname/system_definition_draw.xml";			
       
  1081 			&Logger::LogInfo("Generating temp details XML", KSystemModelGenerator, 1);
       
  1082 			$error = $self->XsltTransform($tempXslFile,$tempStuctureFile,$tempDetailsFile,1,
       
  1083 				{%{$self->{'iXsltParam'}},'Run' => "'calc'" }  );
       
  1084 			&Logger::LogError("Xalan error ($error) occured in building temp datafile", KSystemModelGenerator, 2) if $error;
       
  1085 			# Step 5b
       
  1086 			&Logger::LogInfo("Generating SVG model...", KSystemModelGenerator, 1);
       
  1087 			$error = $self->XsltTransform($tempXslFile,$tempDetailsFile,$tempModelFile,1,
       
  1088 				{%{$self->{'iXsltParam'}},'Run' => "'draw'" }  );
       
  1089 			&Logger::LogError("Xalan error ($error) occured in building SVG", KSystemModelGenerator, 2) if $error;
       
  1090 			}
       
  1091 		else
       
  1092 			{
       
  1093 			# Step 5
       
  1094 			&Logger::LogInfo("Generating SVG model...", KSystemModelGenerator, 1);
       
  1095 			$error = $self->XsltTransform($tempXslFile,$tempStuctureFile,$tempModelFile,1,$self->{'iXsltParam'});
       
  1096 			&Logger::LogError("Xalan error ($error) occured in building SVG", KSystemModelGenerator, 2) if $error;
       
  1097 			}
       
  1098 		if ($self->ShouldCreateDepmodel()) {	# insert as 1st transform
       
  1099 			@ARGV=( $extraDir."/dependencies.xsl",'-',@ARGV)
       
  1100 		}
       
  1101 
       
  1102 		my $tmpsvg = $tempModelFile;
       
  1103 		while(scalar(@ARGV)) {
       
  1104 			my $transform = shift(@ARGV);
       
  1105 			my $datafile = shift(@ARGV);
       
  1106 			if($datafile eq '-') {$datafile = &FileAsUrl($tempStuctureFile)}
       
  1107 			elsif($datafile ne '') {$datafile = &FileAsUrl($datafile)}
       
  1108 			# save to the output if this is the last transform
       
  1109 			# otherwise save to tempModelFile2 if reading from tempModelFile, and vis versa
       
  1110 			my $saveto = $self->{'iDiagram'};
       
  1111 			if(scalar(@ARGV))	 {
       
  1112 				$saveto = ($tmpsvg eq $tempModelFile) ? $tempModelFile2 : $tempModelFile;
       
  1113 			}
       
  1114 			# Step 6
       
  1115 			&Logger::LogInfo("Applying post-processing transformation...", KSystemModelGenerator,1);
       
  1116 			my %p = %{$self->{'iXsltParam'}};  
       
  1117 			if($datafile ne '') {
       
  1118 				$p{'Data'}="'$datafile'";	# optional -- only if needed for transform
       
  1119 			}
       
  1120 			$error = $self->XsltTransform($transform,$tmpsvg,$saveto,1,\%p);
       
  1121 			&Logger::LogError("Xalan error ($error) occured in post-processing SVG file", KSystemModelGenerator, 2) if $error;
       
  1122 			$tmpsvg = $saveto; # read from this next time.
       
  1123 		}
       
  1124 		if ($tmpsvg ne $self->{'iDiagram'}) {
       
  1125 			open(OUT,">".$self->{iDiagram});
       
  1126 			open(IN,$tmpsvg);
       
  1127 			print OUT <IN>;
       
  1128 			close OUT;
       
  1129 			close IN;
       
  1130 		}
       
  1131 	
       
  1132 		my $zipname = $self->{iDiagram};
       
  1133 		my $unzipname = $zipname;
       
  1134 		$zipname =~ s/\.svg$/.svgz/i;
       
  1135 		$unzipname =~ s/\.svgz$/.svg/i;
       
  1136 		my $compressed = 0;
       
  1137 		if($self->{iCompress})
       
  1138 			{
       
  1139 			my $gzip = &GzipCommand();
       
  1140 			if($gzip)
       
  1141 				{
       
  1142 				&Logger::LogInfo("Compressing output model", KSystemModelGenerator,1);
       
  1143 				my $command = "$gzip ".$self->{iDiagram};
       
  1144 				&Logger::LogInfo("System Call: $command", KSystemModelGenerator,2);
       
  1145 				$error = &RunCmd($command);# this should generate the sysmodel.svg in the output directory
       
  1146 				&Logger::LogError("Gzip error ($error) occured when comrpessing SVG", KSystemModelGenerator, 2) if $error;
       
  1147 				&Logger::LogInfo("Renaming output to : $zipname", KSystemModelGenerator,2);		
       
  1148 				rename $self->{iDiagram}.".gz", $zipname;
       
  1149 				$compressed = 1;
       
  1150 				}
       
  1151 			}
       
  1152 		if(!$compressed && $unzipname ne $self->{iDiagram}) 
       
  1153 			{
       
  1154 			&Logger::LogInfo("Renaming output to : $unzipname", KSystemModelGenerator,1);		
       
  1155 			rename $self->{iDiagram}, $unzipname;	
       
  1156 			}
       
  1157 		}
       
  1158 	# create CSV if desired
       
  1159 	if($genCsv)
       
  1160 		{
       
  1161 		&Logger::LogInfo("Generating CSV output", KSystemModelGenerator, 0);
       
  1162 		my %p;
       
  1163 		if($self->{iCsvColumns})
       
  1164 			{
       
  1165 			$p{'atts'}="'".$self->{iCsvColumns}."'";
       
  1166 			}
       
  1167 		if($self->{iCsvLabels})
       
  1168 			{
       
  1169 			$p{'labels'}="'".$self->{iCsvLabels}."'";
       
  1170 			}
       
  1171 		$error = $self->XsltTransform($extraDir."/output-csv.xsl",$tempStuctureFile,$self->{iOutputCsv},-1,\%p);
       
  1172 		&Logger::LogError("Xalan error ($error) occured in CSV output...", KSystemModelGenerator, 1) if $error;
       
  1173 		}
       
  1174 		
       
  1175 	# create sysdef XML if desired
       
  1176 	
       
  1177 	if($genXml)
       
  1178 		{
       
  1179 		&Logger::LogInfo("Generating XML output", KSystemModelGenerator, 0);
       
  1180 		$error = $self->XsltTransform($extraDir."/output-sysdef.xsl",$tempStuctureFile,$self->{iOutputXml},1,$self->{'iXsltParam'});
       
  1181 		&Logger::LogError("Xalan error ($error) occured in Sysdef output...", KSystemModelGenerator, 1) if $error;
       
  1182 		}
       
  1183 
       
  1184 	# delete the contents of the temp directory if -clean is specified by the user:
       
  1185 	if ($self->{iClean})
       
  1186 		{
       
  1187 		&Logger::LogInfo("Deleting contents of the temp directory $self->{iTemporaryDirectory}...", KSystemModelGenerator,0);
       
  1188 		$self->DeleteTempDirectory();
       
  1189 		}	
       
  1190 	}
       
  1191 
       
  1192 
       
  1193 
       
  1194 sub SafeXml {
       
  1195 	my $txt = shift;
       
  1196 	if(!($txt=~/&#?[0-9a-z]+;/i))
       
  1197 		{	# if not entity-encoded, entity encode the stuff
       
  1198 		$txt=~ s/([&<>\x7f-\xff])/"&#".ord($1).";"/eg;
       
  1199 		}
       
  1200 	return $txt;	
       
  1201 }
       
  1202 
       
  1203 sub IsSet()
       
  1204 	{
       
  1205 	my $self = shift;
       
  1206 	my $param = shift;
       
  1207 	if ($self->{'iArgs'}->{$param}->{'multi'} == 1)
       
  1208 		{
       
  1209 		 if (scalar @{$self->{$param}} ) {return 1}
       
  1210 		}
       
  1211 	elsif ($self->{'iArgs'}->{$param}->{'multi'} == 2)
       
  1212 		{
       
  1213 		if (scalar %{$self->{$param}} ) {return 1}
       
  1214 		}
       
  1215 	elsif (defined $self->{$param}){return 1}
       
  1216 	return 0;
       
  1217 	}
       
  1218 
       
  1219 
       
  1220 sub SystemModelXmlDataDir()
       
  1221 	{
       
  1222 	my $file =$FindBin::Bin."/rsc";
       
  1223 	return $file;
       
  1224 	}
       
  1225 
       
  1226 
       
  1227 #-----------------------------------------------------------------------------
       
  1228 # Xalan
       
  1229 #-----------------------------------------------------------------------------
       
  1230 my $KXalanDirectory = $FindBin::Bin."/rsc/installed/Xalan";
       
  1231 my $KXalan = $KXalanDirectory."/xalan.exe";
       
  1232 sub Xalan()
       
  1233 	{
       
  1234 	my $xalan = &FindInPath("xalan");	
       
  1235 	if($xalan ne '') {
       
  1236 		if($xalan=~/ /) {return "\"$xalan\""}
       
  1237 		return $xalan
       
  1238 	}
       
  1239 	# not found, use windows built-in version
       
  1240 	$xalan = $KXalan;
       
  1241 	return $xalan;
       
  1242 	}
       
  1243 
       
  1244 sub XsltProcessor()
       
  1245 	{
       
  1246 	#first try xalan-c
       
  1247 	my $proc = &FindInPath("xalan");	
       
  1248 	if($proc ne '') {
       
  1249 		if($proc=~/ /) {return "\"$proc\""}
       
  1250 		return $proc
       
  1251 	}
       
  1252 	# now try xsltproc
       
  1253 	my $proc = &FindInPath("xsltproc");	
       
  1254 	if($proc ne '')
       
  1255 		{
       
  1256 		if($proc=~/ /) {return "\"$proc\""}
       
  1257 		return $proc
       
  1258 		}
       
  1259 	# now try xalan-j
       
  1260 	foreach my $dir(@CLASSPATH) 
       
  1261 		{
       
  1262 		my $file = "$dir/xalan.jar";
       
  1263 		if(-e $file)
       
  1264 			{
       
  1265 			if($file=~/ /) {$file="\"$file\""}
       
  1266 			return "java -jar $file";	# assume java is installed. Why would ever have a CLASSPATH and not have java?
       
  1267 			}
       
  1268 		}
       
  1269 	# not found, use windows built-in version. If we're not in windows this will fail, but since we have no other options we may as well try it.
       
  1270 	$proc = $KXalan;
       
  1271 	$proc = $FindBin::Bin."/../../resources/installed/Xalan/xalan.exe" if ! -e $proc;
       
  1272 	if($proc=~/ /) {return "\"$proc\""}
       
  1273 	return $proc;
       
  1274 	}
       
  1275 
       
  1276 
       
  1277 #-----------------------------------------------------------------------------
       
  1278 # Gzip
       
  1279 #-----------------------------------------------------------------------------
       
  1280 sub GzipCommand
       
  1281 	{ # returns empty if gzip not in path
       
  1282 	my $dir = &FindInPath("gzip");	
       
  1283 	if($dir ne '')  {return "gzip -9"}
       
  1284 	return "";
       
  1285 	}
       
  1286 
       
  1287 sub FindInPath
       
  1288 	{
       
  1289 	my $exe = shift;
       
  1290 	foreach my $d (@PATH)
       
  1291 		{
       
  1292 		my $dir = $d;
       
  1293 		$dir=~s,[\\\/]$,,;	# remove trailing slash
       
  1294 		$dir.="/$exe";	# try w/o extension
       
  1295 		foreach my $ext ('',@PATHEXT) {
       
  1296 			if(-f "$dir$ext" && -x "$dir$ext") {return "$dir$ext"}		# must be a file and must be executable
       
  1297 			}
       
  1298 		}
       
  1299 	return "";
       
  1300 	}
       
  1301 
       
  1302 
       
  1303 #-------------------
       
  1304 
       
  1305 
       
  1306 sub DeleteTempDirectory()
       
  1307 	{
       
  1308 	my $self = shift;
       
  1309 	# This will delete all files in the $self->{iTemporaryDirectory}
       
  1310 	rmtree $self->{iTemporaryDirectory};
       
  1311 	}
       
  1312 
       
  1313 sub Help()
       
  1314 	{
       
  1315 	my $self = shift;
       
  1316 	my ($param,$text);
       
  1317 
       
  1318 	my @helporder = ('','', 'Build Control', '','Files or URIs',"All of these take a file name (relative or absolute path) or URI of a data source",
       
  1319 		"Labels","All of these take a plain text value which is displayed on the model",'Model Control','');
       
  1320 
       
  1321 format STDERR =
       
  1322  @<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
       
  1323 $param,                               $text,
       
  1324                        ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~
       
  1325                        $text
       
  1326 .
       
  1327 
       
  1328 
       
  1329 my @list =(
       
  1330  'Switch',               'Explanation' );
       
  1331 
       
  1332 for(my $i=0;$i<=$#helporder;$i+=2) {
       
  1333 	my $item=$helporder[$i];
       
  1334 	my $next = $helporder[$i+1];
       
  1335 	if($next ne '') {$next="\n$next"}
       
  1336 	if($item ne '') {
       
  1337 		push (@list,"==== $item ====$next");
       
  1338 	}
       
  1339 	foreach my $b (sort values %{$self->{'iArgs'}})  {
       
  1340 		if(!$b->{'param'}) {next} # not an arg
       
  1341 		my $a = $b->{'param'};
       
  1342 		if ($b->{'class'} eq $item) {
       
  1343 			if ($a=~s/=.*// && $b->{'type'} ne '') {
       
  1344 				$a .= ' ['.$b->{'type'}.']';
       
  1345 			}
       
  1346 			my $ex = $b->{'desc'};
       
  1347 			if($b->{'default'}) { $ex.=  ($ex=~/./ ? '. ' : ''  ). 'Defaults to "'.$b->{'default'}.'"'}
       
  1348 			if($b->{'multi'}) { $ex.=  ($ex=~/./ ? '. ' : ''  ). "Can specify multiple times."}
       
  1349 			if($b->{'depr'}) { $ex.=  ($ex=~/./ ? '. ' : ''  ). "DEPRECATED: ".$b->{'depr'}}
       
  1350 			push(@list,'-'.$a,$ex);
       
  1351 		}
       
  1352 	}
       
  1353 }
       
  1354 
       
  1355 $self->HelpBase();
       
  1356 print STDERR "\nArguments:\n";
       
  1357   my $head=2;
       
  1358 while(@list) {
       
  1359 	$param = shift(@list);
       
  1360 	if($head<=0 and !($param=~/^-/)){print STDERR "\n$param\n";next;}
       
  1361 	$text = shift(@list);
       
  1362 	write STDERR ;
       
  1363 	$head--;
       
  1364 }
       
  1365 	return;
       
  1366 	}
       
  1367 
       
  1368 sub HelpBase()
       
  1369 	{
       
  1370 	print STDERR "Usage: $0 [Arguments] [Transform Data-file] ...\n";
       
  1371 	}
       
  1372 1;