<?xml version="1.0" encoding="iso-8859-1"?>

<xsl:stylesheet version="1.0"
	xmlns="http://www.w3.org/1999/xhtml"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
	xmlns:xrc="http://www.wxwidgets.org/wxxrc"
	xmlns:xrv="http://cking.be/wx/xrv/ns"
	>

	<xsl:param name="gCheckResourceVersion" select="true()"/>
	<xsl:param name="gCheckObjectNames" select="false()"/>
	<xsl:param name="gFileHref" select="''"/>

	<xsl:output method="xml" version="1.0" encoding="iso-8859-1" indent="yes"
		doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
		doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
		/>

	<xsl:strip-space elements="*"/>

	<xsl:variable name="grammar" select="document('../xml/grammar.xml')/xrv:grammar"/>
	<xsl:variable name="strings" select="document('../xml/strings.xml')/xrv:strings"/>

	<xsl:variable name="classes" select="$grammar//xrv:class"/>
	<xsl:variable name="types" select="$grammar/xrv:types/xrv:type"/>

	<xsl:key name="objects" match="xrc:object" use="@name"/>

	<xsl:template match="/xrv:validate" priority="1">
		<xsl:variable name="file-href" select="xrv:file/@href"/>
		<!-- added in v.0.5.2 -->
		<xsl:apply-templates select="document(xrv:file/@href)/xrc:*">
			<xsl:with-param name="file-href" select="$file-href"/>
		</xsl:apply-templates>
	</xsl:template>

	<xsl:template match="/*">
		<xsl:param name="file-href" select="$gFileHref"/>
		<!-- root element: generate HTML -->
		<html>
			<head>
				<title>xrv XRCvalidator report</title>
				<meta name="generator" content="xrv XRCvalidator {$grammar/@version} [{system-property('xsl:vendor')}]"/>
				<link rel="stylesheet" type="text/css" href="../css/global.css"/>
				<link rel="stylesheet" type="text/css" href="../css/validator.css"/>
			</head>
			<body>
				<div class="title"><a href="http://www.cking.be/wx/xrv/">xrv XRCvalidator <xsl:value-of select="$grammar/@version"/></a> report</div>
				<div class="content indent">
					<hr/>
					<!-- check root element name and attributes -->
					<xsl:choose>
						<xsl:when test="local-name()!='resource'">
							<xsl:apply-templates select="." mode="error">
								<xsl:with-param name="id" select="'errRootElemName'"/>
								<xsl:with-param name="s1" select="local-name()"/>
							</xsl:apply-templates>
						</xsl:when>
						<xsl:when test="namespace-uri()!='http://www.wxwidgets.org/wxxrc'">
							<xsl:apply-templates select="." mode="error">
								<xsl:with-param name="id" select="'errNamespace'"/>
							</xsl:apply-templates>
						</xsl:when>
						<xsl:otherwise>
							<!-- no error found on root element: proceed -->
							<xsl:if test="string($file-href)">
								<div class="href">
									<xsl:call-template name="strip-leading-dir">
										<xsl:with-param name="path" select="$file-href"/>
									</xsl:call-template>
								</div>
							</xsl:if>
							<div>XRC resource file, version <xsl:value-of select="@version"/></div>
							<div><xsl:apply-templates select="." mode="object-count"/></div>
							<div>&#0160;</div>
							<!-- check for warnings -->
							<xsl:choose>
								<xsl:when test="not(string(@version))">
									<xsl:if test="$gCheckResourceVersion">
										<!-- tn0014.txt says version attribute is optional -->
										<xsl:apply-templates select="." mode="error">
											<xsl:with-param name="id" select="'errMissingAttr'"/>
											<xsl:with-param name="s1" select="'resource'"/>
											<xsl:with-param name="s2" select="'version'"/>
											<xsl:with-param name="type" select="'warning'"/>
										</xsl:apply-templates>
									</xsl:if>
								</xsl:when>
								<xsl:when test="translate(@version,'0123456789','') != '...'">
									<xsl:apply-templates select="." mode="error">
										<xsl:with-param name="id" select="'errInvalidVersion'"/>
										<xsl:with-param name="s1" select="@version"/>
										<xsl:with-param name="type" select="'warning'"/>
									</xsl:apply-templates>
								</xsl:when>
								<!-- TODO... compare @version with $grammar/xrv:meta/xrv:xrc/@min and @max
								<xsl:when test="$grammar/xrv:meta/xrv:xrc/@min > @version or @version > $grammar/xrv:meta/xrv:xrc/@max">
									<xsl:apply-templates select="." mode="error">
										<xsl:with-param name="id" select="'errUnknownVersion'"/>
										<xsl:with-param name="s1" select="@version"/>
										<xsl:with-param name="s2" select="$grammar/xrv:meta/xrv:xrc/@min"/>
										<xsl:with-param name="s3" select="$grammar/xrv:meta/xrv:xrc/@max"/>
										<xsl:with-param name="type" select="'warning'"/>
									</xsl:apply-templates>
								</xsl:when>
								-->
							</xsl:choose>
							<!-- proceed with the children and attributes -->
							<xsl:apply-templates select="@*"/>
							<xsl:apply-templates select="*"/>
						</xsl:otherwise>
					</xsl:choose>
					<xsl:apply-templates select="." mode="report"/>	<!-- added in v.0.5.3 -->
				</div>
			</body>
		</html>
	</xsl:template>

	<xsl:template name="strip-leading-dir">
		<xsl:param name="path" select="''"/>
		<xsl:choose>
			<xsl:when test="starts-with($path,'../')">
				<xsl:call-template name="strip-leading-dir">
					<xsl:with-param name="path" select="substring($path,4,9999)"/>
				</xsl:call-template>
			</xsl:when>
			<xsl:otherwise>
				<xsl:value-of select="$path"/>
			</xsl:otherwise>
		</xsl:choose>		
	</xsl:template>

	<xsl:template match="/xrc:resource" mode="report">
		<!-- added in v.0.5.3 -->
		<div class="report">
			<xsl:apply-templates select="xrc:object | xrc:object_ref" mode="report"/>
		</div>
	</xsl:template>

	<xsl:variable name="indent1" select="'&#0160;&#0160;&#0160;'"/>

	<xsl:template match="xrc:object" mode="report">
		<xsl:param name="name" select="@name"/>
		<xsl:param name="indent" select="''"/>
		<xsl:param name="is-ref" select="false()"/>
		<xsl:variable name="add-this" select="@class=$classes/@name and starts-with(@class,'wx') and not(contains(@class,'Sizer'))"/>
		<xsl:variable name="refclass"><xsl:if test="$is-ref"> ref</xsl:if></xsl:variable>
		<xsl:if test="$add-this">
			<div class="col1{$refclass}">
				<xsl:value-of select="concat($indent,@class)"/>
			</div>
			<div class="col2{$refclass}">
				<xsl:value-of select="$name"/>
				<xsl:if test="not(string($name))">&#0160;</xsl:if>
			</div>
			<div class="col3{$refclass}">
				<xsl:value-of select="xrc:size"/>
				<xsl:if test="not(string(xrc:size))">&#0160;</xsl:if>
			</div>
			<div class="col4{$refclass}">
				<xsl:choose>
					<xsl:when test="@class='wxBitmap' or @class='wxIcon'">
						<img src="../{.}" width="10" height="10" alt=""/>	<!-- TODO parameterize path -->
						<xsl:text>&#0160;&#0160;</xsl:text>
						<xsl:value-of select="."/>
					</xsl:when>
					<xsl:otherwise>
						<xsl:value-of select="xrc:style"/>
						<xsl:if test="not(string(xrc:style))">&#0160;</xsl:if>
					</xsl:otherwise>
				</xsl:choose>
			</div>
		</xsl:if>
		<xsl:if test="not($is-ref)">
			<xsl:variable name="new-indent">
				<xsl:value-of select="$indent"/>
				<xsl:if test="$add-this">
					<xsl:value-of select="$indent1"/>
				</xsl:if>
			</xsl:variable>
			<xsl:apply-templates select="xrc:object | xrc:object_ref" mode="report">
				<xsl:with-param name="indent" select="$new-indent"/>
			</xsl:apply-templates>
		</xsl:if>
	</xsl:template>

	<xsl:template match="xrc:object_ref" mode="report">
		<xsl:param name="indent" select="''"/>
		<xsl:if test="string(@ref)">
			<!-- TODO: choose/otherwise: alert tr -->
			<xsl:apply-templates select="key('objects',@ref)" mode="report">
				<xsl:with-param name="name" select="@name"/>
				<xsl:with-param name="indent" select="$indent"/>
				<xsl:with-param name="is-ref" select="true()"/>
			</xsl:apply-templates>
			<xsl:apply-templates select="xrc:object | xrc:object_ref" mode="report">
				<xsl:with-param name="indent" select="concat($indent,$indent1)"/>
			</xsl:apply-templates>
		</xsl:if>
	</xsl:template>

	<xsl:template match="/xrc:resource" mode="object-count">
		<xsl:variable name="count" select="count(//xrc:object)+count(//xrc:object_ref)"/>
		<xsl:value-of select="$count"/>
		<xsl:text> object</xsl:text>
		<xsl:if test="$count!=1">s</xsl:if>
	</xsl:template>

	<xsl:template match="xrc:object">
		<xsl:choose>
			<xsl:when test="not(string(@class))">
				<xsl:apply-templates select="." mode="error">
					<xsl:with-param name="id" select="'errMissingAttr'"/>
					<xsl:with-param name="s1" select="'object'"/>
					<xsl:with-param name="s2" select="'class'"/>
				</xsl:apply-templates>
			</xsl:when>
			<xsl:when test="@class='unknown'">
				<!-- 'unknown' is an identifier for custom classes, so we'll skip this for now -->
			</xsl:when>
			<xsl:when test="not(@class=$classes/@name)">
				<xsl:apply-templates select="." mode="error">
					<xsl:with-param name="id" select="'errUnknownClassName'"/>
					<xsl:with-param name="s1" select="@class"/>
				</xsl:apply-templates>
			</xsl:when>
			<xsl:otherwise>
				<xsl:variable name="class" select="$classes[@name=current()/@class]"/>
				<xsl:if test="$class/xrv:deprecated">
					<xsl:apply-templates select="." mode="error">
						<xsl:with-param name="id" select="'errDeprecatedClass'"/>
						<xsl:with-param name="s1" select="@class"/>
						<xsl:with-param name="s2" select="$class/xrv:deprecated/@use"/>
						<xsl:with-param name="type" select="'warning'"/>
					</xsl:apply-templates>
				</xsl:if>
				<xsl:if test="$gCheckObjectNames">
					<xsl:if test="substring(@class,1,2)='wx' and not(string(@name)) and not($class/ancestor::xrv:sizers)">
						<xsl:apply-templates select="." mode="error">
							<xsl:with-param name="id" select="'errMissingAttr'"/>
							<xsl:with-param name="s1" select="'object'"/>
							<xsl:with-param name="s2" select="'name'"/>
							<xsl:with-param name="type" select="'warning'"/>
						</xsl:apply-templates>
					</xsl:if>
				</xsl:if>
				<xsl:if test="parent::xrc:object or parent::xrc:object_ref">
					<xsl:apply-templates select="." mode="check-class-within-class"/>
				</xsl:if>
				<xsl:apply-templates select="*|@*"/>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>

	<xsl:template match="xrc:object_ref">
		<xsl:variable name="object" select="key('objects',@ref)"/>
		<xsl:choose>
			<xsl:when test="not(string(@ref))">
				<xsl:apply-templates select="." mode="error">
					<xsl:with-param name="id" select="'errMissingAttr'"/>
					<xsl:with-param name="s1" select="'object_ref'"/>
					<xsl:with-param name="s2" select="'ref'"/>
				</xsl:apply-templates>
			</xsl:when>
			<xsl:when test="not($object)">
				<xsl:apply-templates select="." mode="error">
					<xsl:with-param name="id" select="'errRefUnknownObj'"/>
					<xsl:with-param name="s1" select="@ref"/>
				</xsl:apply-templates>
			</xsl:when>
			<xsl:otherwise>
				<xsl:variable name="class" select="$classes[@name=key('objects',current()/@ref)/@class]"/>
				<xsl:if test="$gCheckObjectNames">
					<xsl:if test="substring($object/@class,1,2)='wx' and not(string(@name)) and not($class/ancestor::xrv:sizers)">
						<xsl:apply-templates select="." mode="error">
							<xsl:with-param name="id" select="'errMissingAttr'"/>
							<xsl:with-param name="s1" select="'object_ref'"/>
							<xsl:with-param name="s2" select="'name'"/>
							<xsl:with-param name="type" select="'warning'"/>
						</xsl:apply-templates>
					</xsl:if>
				</xsl:if>
				<xsl:if test="parent::xrc:object or parent::xrc:object_ref">
					<xsl:apply-templates select="." mode="check-class-within-class"/>
				</xsl:if>
				<xsl:apply-templates select="*|@*"/>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>

	<xsl:template match="xrc:object | xrc:object_ref" mode="check-class-within-class">
		<xsl:variable name="parentclassname" select="concat(parent::xrc:object/@class,key('objects',parent::xrc:object_ref/@ref)/@class)"/>
		<xsl:variable name="parentclass" select="$classes[@name=$parentclassname]"/>
		<xsl:variable name="classname" select="concat(self::xrc:object/@class,key('objects',self::xrc:object_ref/@ref)/@class)"/>
		<xsl:variable name="class" select="$classes[@name=$classname]"/>
		<xsl:choose>
			<xsl:when test="$parentclass/xrv:class[@name=$classname]"/>
			<xsl:when test="$class/ancestor::xrv:bars and $parentclass/xrv:child-bars"/>
			<xsl:when test="$class/ancestor::xrv:controls and $parentclass/xrv:child-controls"/>
			<xsl:when test="$class/ancestor::xrv:sizers and $parentclass/xrv:child-sizers"/>
			<xsl:when test="$class/ancestor::xrv:windows and $parentclass/xrv:child-windows"/>
			<xsl:otherwise>
				<xsl:apply-templates select="." mode="error">
					<xsl:with-param name="id" select="'errClassWithinClass'"/>
					<xsl:with-param name="s1" select="$parentclassname"/>
					<xsl:with-param name="s2" select="$classname"/>
				</xsl:apply-templates>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>

	<xsl:template match="xrc:*">
		<xsl:choose>
			<xsl:when test="parent::xrc:resource">
				<xsl:apply-templates select="." mode="error">
					<xsl:with-param name="id" select="'errOnlyObjects'"/>
					<xsl:with-param name="s1" select="local-name()"/>
					<xsl:with-param name="type" select="'warning'"/>
				</xsl:apply-templates>
			</xsl:when>
			<xsl:otherwise>
				<xsl:variable name="parentclassname" select="concat(parent::xrc:object/@class,key('objects',parent::xrc:object_ref/@ref)/@class)"/>
				<xsl:variable name="parentclass" select="$classes[@name=$parentclassname]"/>
				<xsl:choose>
					<xsl:when test="local-name()=$parentclass/xrv:prop/@name">
						<!-- OK: parent class does have a property of this name -->
						<xsl:apply-templates select="." mode="check-datatype">
							<xsl:with-param name="datatype" select="$parentclass/xrv:prop[@name=local-name(current())]/@type"/>
						</xsl:apply-templates>
					</xsl:when>
					<xsl:when test="local-name()=$grammar/xrv:common/xrv:prop/@name and $parentclass/xrv:commonprops">
						<!-- OK: it's a common property -->
						<xsl:apply-templates select="." mode="check-datatype">
							<xsl:with-param name="datatype" select="$grammar/xrv:common/xrv:prop[@name=local-name(current())]/@type"/>
						</xsl:apply-templates>
					</xsl:when>
					<xsl:otherwise>
						<xsl:apply-templates select="." mode="error">
							<xsl:with-param name="id" select="'errUnknownClassProp'"/>
							<xsl:with-param name="s1" select="$parentclassname"/>
							<xsl:with-param name="s2" select="local-name()"/>
						</xsl:apply-templates>
					</xsl:otherwise>
				</xsl:choose>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>

	<xsl:template match="xrc:*" mode="check-datatype">
		<xsl:param name="datatype"/>
		<xsl:choose>
			<xsl:when test="$datatype='tFont'">
				<xsl:apply-templates select="*" mode="check-tFont"/>
			</xsl:when>
			<xsl:when test="$datatype='tList'">
				<xsl:apply-templates select="*" mode="check-tList"/>
			</xsl:when>
			<!-- TODO: check other datatypes (eg. tBoolean must be '0' or '1') -->
			<xsl:otherwise>
				<!-- it's not a font or list property so no more child elements are allowed -->
				<xsl:if test="*">
					<xsl:apply-templates select="." mode="error">
						<xsl:with-param name="id" select="'errUnknownPropChild'"/>
						<xsl:with-param name="s1" select="local-name()"/>
						<xsl:with-param name="s2" select="local-name(*[1])"/>
					</xsl:apply-templates>
				</xsl:if>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>

	<xsl:template match="xrc:*" mode="check-tFont">
		<xsl:if test="not(local-name()=$types[@id='tFont']/xrv:prop/@name)">
			<xsl:apply-templates select="." mode="error">
				<xsl:with-param name="id" select="'errUnknownPropChild'"/>
				<xsl:with-param name="s1" select="'font'"/>
				<xsl:with-param name="s2" select="local-name()"/>
			</xsl:apply-templates>
		</xsl:if>
	</xsl:template>

	<xsl:template match="xrc:*" mode="check-tList">
		<xsl:if test="not(local-name()=$types[@id='tList']/xrv:items/@name)">
			<xsl:apply-templates select="." mode="error">
				<xsl:with-param name="id" select="'errUnknownPropChild'"/>
				<xsl:with-param name="s1" select="'list'"/>
				<xsl:with-param name="s2" select="local-name()"/>
			</xsl:apply-templates>
		</xsl:if>
	</xsl:template>

	<!-- check attributes -->

	<!--<xsl:template match="xrc:resource/@xmlns"/>-->
	<xsl:template match="xrc:resource/@version"/>

	<xsl:template match="xrc:resource/@*" priority="0.1">
		<xsl:apply-templates select="parent::xrc:resource" mode="error">
			<xsl:with-param name="id" select="'errUnknownAttr'"/>
			<xsl:with-param name="s1" select="'resource'"/>
			<xsl:with-param name="s2" select="name()"/>
		</xsl:apply-templates>
	</xsl:template>

	<xsl:template match="xrc:object/@name"/>
	<xsl:template match="xrc:object/@class"/>

	<xsl:template match="xrc:object/@subclass"/>
	<xsl:template match="xrc:object/@platform"/>
	<xsl:template match="xrc:object/@*" priority="0.1">
		<xsl:apply-templates select="parent::xrc:object" mode="error">
			<xsl:with-param name="id" select="'errUnknownAttr'"/>
			<xsl:with-param name="s1" select="'object'"/>
			<xsl:with-param name="s2" select="name()"/>
		</xsl:apply-templates>
	</xsl:template>

	<xsl:template match="xrc:object_ref/@name"/>
	<xsl:template match="xrc:object_ref/@ref"/>
	<!-- TODO... can object_ref also have 'platform' attribute ?? -->

	<xsl:template match="xrc:object_ref/@*" priority="0.1">
		<xsl:apply-templates select="parent::xrc:object_ref" mode="error">
			<xsl:with-param name="id" select="'errUnknownAttr'"/>
			<xsl:with-param name="s1" select="'object_ref'"/>
			<xsl:with-param name="s2" select="name()"/>
		</xsl:apply-templates>
	</xsl:template>

	<!-- general error handling templates -->

	<xsl:template match="*" mode="error">
		<xsl:param name="id" select="''"/>
		<xsl:param name="s1" select="''"/>
		<xsl:param name="s2" select="''"/>
		<xsl:param name="s3" select="''"/>
		<xsl:param name="type" select="'fatal'"/>
		<div class="error {$type}">
			<xsl:apply-templates select="$strings/xrv:errors/xrv:error[@id=$id]">
				<xsl:with-param name="s1" select="$s1"/>
				<xsl:with-param name="s2" select="$s2"/>
				<xsl:with-param name="s3" select="$s3"/>
			</xsl:apply-templates>
			<xsl:apply-templates select="." mode="path"/>
		</div>
	</xsl:template>

	<xsl:template match="xrv:error">
		<xsl:param name="s1" select="''"/>
		<xsl:param name="s2" select="''"/>
		<xsl:param name="s3" select="''"/>
		<xsl:apply-templates select="xrv:dscr">
			<xsl:with-param name="s1" select="$s1"/>
			<xsl:with-param name="s2" select="$s2"/>
			<xsl:with-param name="s3" select="$s3"/>
		</xsl:apply-templates>
		<xsl:apply-templates select="xrv:info">
			<xsl:with-param name="s1" select="$s1"/>
			<xsl:with-param name="s2" select="$s2"/>
			<xsl:with-param name="s3" select="$s3"/>
		</xsl:apply-templates>
	</xsl:template>

	<xsl:template match="xrv:error/xrv:dscr | xrv:error/xrv:info">
		<xsl:param name="class" select="''"/>
		<xsl:param name="s1" select="''"/>
		<xsl:param name="s2" select="''"/>
		<xsl:param name="s3" select="''"/>
		<div class="{local-name()}">
			<xsl:apply-templates select="*|node()">
				<xsl:with-param name="s1" select="$s1"/>
				<xsl:with-param name="s2" select="$s2"/>
				<xsl:with-param name="s3" select="$s3"/>
			</xsl:apply-templates>
		</div>
	</xsl:template>

	<xsl:template match="xrv:e">
		<xsl:param name="s1"/>
		<xsl:param name="s2"/>
		<xsl:param name="s3"/>
		<span class="elem">
			<xsl:apply-templates select="node()"/>
		</span>
	</xsl:template>

	<xsl:template match="xrv:s1">
		<xsl:param name="s1" select="''"/>
		<xsl:param name="s2"/>
		<xsl:param name="s3"/>
		<span class="s">
			<xsl:value-of select="$s1"/>
		</span>
	</xsl:template>

	<xsl:template match="xrv:s2">
		<xsl:param name="s1"/>
		<xsl:param name="s2" select="''"/>
		<xsl:param name="s3"/>
		<span class="s">
			<xsl:value-of select="$s2"/>
		</span>
	</xsl:template>

	<xsl:template match="xrv:s3">
		<xsl:param name="s1"/>
		<xsl:param name="s2"/>
		<xsl:param name="s3" select="''"/>
		<span class="s">
			<xsl:value-of select="$s3"/>
		</span>
	</xsl:template>

	<!-- generate path of a given node -->

	<xsl:template match="*" mode="path">
		<xsl:if test="not(self::xrc:resource or (not(self::xrc:object) and parent::xrc:resource))">
			<div class="path">
				<xsl:apply-templates select="." mode="path-recursive"/>
			</div>
		</xsl:if>
	</xsl:template>

	<xsl:template match="*" mode="path-recursive">
		<xsl:apply-templates select="parent::*" mode="path-recursive"/>
		<xsl:if test="self::xrc:object or self::xrc:object_ref">
			<xsl:if test="parent::*/parent::*">
				<xsl:text>/</xsl:text>
			</xsl:if>
			<xsl:if test="self::xrc:object">
				<xsl:value-of select="@class"/>
			</xsl:if>
			<xsl:if test="self::xrc:object_ref">
				<xsl:text>{</xsl:text>
				<xsl:value-of select="@ref"/>
				<xsl:text>}</xsl:text>
			</xsl:if>
			<xsl:choose>
				<xsl:when test="string(@name)">
					<xsl:text>(</xsl:text>
					<xsl:value-of select="@name"/>
					<xsl:text>)</xsl:text>
				</xsl:when>
				<xsl:when test="self::xrc:object">
					<xsl:variable name="count-preceding" select="count(preceding-sibling::xrc:object[@class=current()/@class])"/>
					<xsl:variable name="count-following" select="count(following-sibling::xrc:object[@class=current()/@class])"/>
					<xsl:if test="$count-preceding + $count-following > 1">
						<xsl:text>[</xsl:text>
						<xsl:value-of select="$count-preceding + 1"/>
						<xsl:text>]</xsl:text>
					</xsl:if>
				</xsl:when>
			</xsl:choose>
		</xsl:if>
	</xsl:template>

</xsl:stylesheet>



