|
<?xml version="1.0" encoding="utf-8"?>
|
|
<!--
|
|
This stylesheet provides functions to generate valid C# identifiers.
|
|
-->
|
|
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
|
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
|
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
|
|
xmlns:t="http://www.bphx.com/csharp"
|
|
xmlns:p="http://www.bphx.com/csharp/private/csharp-names"
|
|
xmlns="http://www.bphx.com/csharp-3.0/2009-05-23"
|
|
xpath-default-namespace="http://www.bphx.com/csharp-3.0/2009-05-23"
|
|
exclude-result-prefixes="xs map t p">
|
|
|
|
<!-- C# reserved keywords. -->
|
|
<xsl:variable name="t:csharp-reserved-words" as="xs:string+" select="
|
|
'abstract', 'as', 'base', 'bool', 'break',
|
|
'byte', 'case', 'catch', 'char', 'checked',
|
|
'class', 'const', 'continue', 'decimal', 'default',
|
|
'delegate', 'do', 'double', 'else', 'enum',
|
|
'event', 'explicit', 'extern', 'false', 'finally',
|
|
'fixed', 'float', 'for', 'foreach', 'goto',
|
|
'if', 'implicit', 'in', 'int', 'interface',
|
|
'internal', 'is', 'lock', 'long', 'namespace',
|
|
'new', 'null', 'object', 'operator', 'out',
|
|
'override', 'params', 'private', 'protected', 'public',
|
|
'readonly', 'ref', 'return', 'sbyte', 'sealed',
|
|
'short', 'sizeof', 'stackalloc', 'static', 'string',
|
|
'struct', 'switch', 'this', 'throw', 'true',
|
|
'try', 'typeof', 'uint', 'ulong', 'unchecked',
|
|
'unsafe', 'ushort', 'using', 'virtual', 'void',
|
|
'volatile', 'while'"/>
|
|
|
|
<!-- Extended reserved names. -->
|
|
<xsl:variable name="t:reserved-names" as="xs:string*">
|
|
<xsl:apply-templates mode="t:call" select="$t:reserved-names-handler"/>
|
|
</xsl:variable>
|
|
|
|
<!-- A total set of reserved words. -->
|
|
<xsl:variable name="t:reserved-words" as="xs:string*"
|
|
select="$t:csharp-reserved-words, $t:reserved-names"/>
|
|
|
|
<!-- A map of reserved words. -->
|
|
<xsl:variable name="t:reserved-words-map" as="map(xs:string, xs:string)"
|
|
select="map:merge($t:reserved-words!map{.: .})"/>
|
|
|
|
<!-- A handler to collect reserved names. -->
|
|
<xsl:variable name="t:reserved-names-handler" as="element()">
|
|
<t:reserved-names/>
|
|
</xsl:variable>
|
|
|
|
<!-- A default handler for the reserved names. -->
|
|
<xsl:template mode="t:call" match="t:reserved-names"/>
|
|
|
|
<!--
|
|
Capitalizes a name.
|
|
@value - a string to be capitalized.
|
|
Returns a capitalized version of the string.
|
|
-->
|
|
<xsl:function name="t:capitalize" as="xs:string">
|
|
<xsl:param name="value" as="xs:string?"/>
|
|
|
|
<xsl:sequence select="
|
|
concat
|
|
(
|
|
upper-case(substring($value, 1, 1)),
|
|
substring($value, 2)
|
|
)"/>
|
|
</xsl:function>
|
|
|
|
<!--
|
|
Note: though this function is borrowed from java, nevertheless
|
|
it works good in C# world.
|
|
|
|
Utility function to take a string and convert it to normal Java variable
|
|
name capitalization. This normally means converting the first
|
|
character from upper case to lower case, but in the (unusual) special
|
|
case when there is more than one character and both the first and
|
|
second characters are upper case, we leave it alone.
|
|
|
|
Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays
|
|
as "URL".
|
|
|
|
@value - a string to be decapitalized.
|
|
Returns a decapitalized version of the string.
|
|
-->
|
|
<xsl:function name="t:decapitalize" as="xs:string">
|
|
<xsl:param name="value" as="xs:string?"/>
|
|
|
|
<xsl:variable name="c" as="xs:string"
|
|
select="substring($value, 2, 1)"/>
|
|
|
|
<xsl:sequence select="
|
|
if ($c != lower-case($c)) then
|
|
$value
|
|
else
|
|
concat
|
|
(
|
|
lower-case(substring($value, 1, 1)),
|
|
substring($value, 2)
|
|
)"/>
|
|
</xsl:function>
|
|
|
|
<!--
|
|
Gets a C# name for a specified name components.
|
|
$name - a name to generate C# name for.
|
|
$default-name - a default name in case a name cannot be built.
|
|
Returns C# name (upper case first).
|
|
-->
|
|
<xsl:function name="t:create-name" as="xs:string?">
|
|
<xsl:param name="name" as="xs:string*"/>
|
|
<xsl:param name="default-name" as="xs:string?"/>
|
|
|
|
<xsl:variable name="components" as="xs:string*">
|
|
<xsl:for-each select="$name">
|
|
<xsl:analyze-string
|
|
regex="[\p{{L}}]+|\d+"
|
|
flags="imx"
|
|
select=".">
|
|
<xsl:matching-substring>
|
|
<xsl:sequence select="upper-case(substring(., 1, 1))"/>
|
|
<xsl:sequence select="lower-case(substring(., 2))"/>
|
|
</xsl:matching-substring>
|
|
</xsl:analyze-string>
|
|
</xsl:for-each>
|
|
</xsl:variable>
|
|
|
|
<xsl:sequence select="
|
|
if (empty($components)) then
|
|
$default-name
|
|
else
|
|
string-join
|
|
(
|
|
(
|
|
if ($components[1][(. le '9') and (. ge '0')]) then
|
|
(($default-name, 'N')[1], $components)
|
|
else
|
|
$components
|
|
),
|
|
''
|
|
)"/>
|
|
</xsl:function>
|
|
|
|
<!--
|
|
Allocates unique names in the form $prefix{number}?.
|
|
Note: that prefixes may coincide.
|
|
Note: that result names shall be different not only using case.
|
|
$prefixes - a name prefixes.
|
|
$names - allocated names pool.
|
|
Returns unique names.
|
|
-->
|
|
<xsl:function name="t:allocate-names" as="xs:string*">
|
|
<xsl:param name="prefixes" as="xs:string*"/>
|
|
<xsl:param name="names" as="xs:string*"/>
|
|
|
|
<xsl:if test="exists($prefixes)">
|
|
<xsl:variable name="reserved" as="map(xs:string, xs:string)"
|
|
select="map:merge($names!map{upper-case(.): .})"/>
|
|
<xsl:variable name="count" as="xs:integer" select="count($prefixes)"/>
|
|
|
|
<xsl:iterate select="$prefixes">
|
|
<xsl:param name="allocated" as="map(xs:string, xs:integer+)" select="
|
|
map:merge
|
|
(
|
|
$prefixes!map{upper-case(.): 1},
|
|
map{'duplicates': 'combine'}
|
|
)"/>
|
|
|
|
<xsl:param name="result" as="map(xs:integer, xs:string)" select="
|
|
map:merge((1 to $count)!map{.: $prefixes=>subsequence(., 1)})"/>
|
|
|
|
<xsl:on-completion select="(1 to $count)!$result(.)"/>
|
|
|
|
<xsl:variable name="position" as="xs:integer" select="position()"/>
|
|
<xsl:variable name="key" as="xs:string" select="upper-case(.)"/>
|
|
|
|
<xsl:if test="
|
|
$reserved=>map:contains($key) or
|
|
(count($allocated($key)) > 1) or
|
|
map:contains($t:reserved-words-map, .)">
|
|
|
|
<xsl:variable name="new-prefix" as="xs:string"
|
|
select="replace(., '\d+$', '')"/>
|
|
<xsl:variable name="new-key-prefix" as="xs:string"
|
|
select="upper-case($new-prefix)"/>
|
|
|
|
<xsl:variable name="name" as="xs:string">
|
|
<xsl:iterate select="1 to $count + map:size($reserved) + 1">
|
|
<xsl:variable name="new-key" as="xs:string"
|
|
select="$new-key-prefix || ."/>
|
|
|
|
<xsl:if test="
|
|
not($reserved=>map:contains($new-key)) and
|
|
not($allocated=>map:contains($new-key))">
|
|
<xsl:break select="$new-prefix || ."/>
|
|
</xsl:if>
|
|
</xsl:iterate>
|
|
</xsl:variable>
|
|
|
|
<xsl:next-iteration>
|
|
<xsl:with-param name="allocated"
|
|
select="map:put($allocated, upper-case($name), 1)"/>
|
|
<xsl:with-param name="result"
|
|
select="map:put($result, $position, $name)"/>
|
|
</xsl:next-iteration>
|
|
</xsl:if>
|
|
</xsl:iterate>
|
|
</xsl:if>
|
|
</xsl:function>
|
|
|
|
</xsl:stylesheet>
|