2011-07-05 3 views
4

<xsl:number>에 대한 사용자 지정 형식을 정의 할 수 있습니까?<xsl:number>의 사용자 지정 형식

표준 알파 기반 형식이 필요한 경우가 있지만 알파벳의 특정 문자는 금지되어 있습니다 (이상한 요구 사항이지만 클라이언트가 요구하는 것입니다). 예를 들어 i 문자는 사용할 수 없으므로 <xsl:number>을 사용할 때 a, b, c, d, e, f, g, h, j, k, ..., aa, ab ... 시퀀스를 가져야합니다. ., ah, aj, ...

프로젝트에서 XSLT 2.0과 Saxon을 사용하고 있기 때문에 Saxon에서만 사용할 수있는 솔루션이 있다면 괜찮습니다.

XSLT 2.0은 사용자 지정 형식 시퀀스를 정의하는 기능을 제공합니까? Saxon은 <xsl:number>과 함께 사용할 사용자 지정 시퀀스를 등록 할 수있는 기능을 제공합니까?

+0

흥미로운 질문 +1. –

답변

1

문제에 대한 원래 해결책을 게시 한 후 좀 더 일반화 된 해결책을 찾았습니다. 해결책은 순수한 XSLT이고 기본에서는 여전히 <xsl:number>을 사용하므로 모든 형식 유형에 적용 할 수 있어야합니다.

<xsl:stylesheet version="2.0" 
       xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
       xmlns:xs="http://www.w3.org/2001/XMLSchema" 
       xmlns:ewh="http://www.earlhood.com/XSL/Transform" 
       exclude-result-prefixes="#all"> 

<!-- Description: XSLT to generate a alpha formatted sequence 
    label (via <xsl:number>), but disallowing specific characters 
    from being used. 
    --> 

<!-- Algorithm: Given the index value of the item to generate 
    a label for via <xsl:number>, we adjust the value so the resulting 
    label avoids the use of the forbidden characters. 

    This is achieved by converting the index value into a baseX 
    number, with X the number of allowed characters. 

    The baseX number will be converted into a reverse sequence 
    of numbers for each ^E place. For example, the number 12167 
    converted to base23 will generate the following reverse sequence: 

     Place: (23^0, 23^1, 23^2, 23^3) 
     Sequence: ( 0, 0, 0, 1) // 1000 in base23 

    Having it in right-to-left order makes processing easier. 

    Each item in the sequence will be a number from 0 to baseX-1. 

    With the sequence, we can then just call <xsl:number> on 
    each item and reverse concatenate the result. 

    NOTE: Since <xsl:number> does not like 0 as a given value, 
    the sequence must be processed so each item is within the 
    range of 1-to-baseX. For example, the above base23 example 
    will be translated to the following: 

     (23, 22, 22) 
    --> 

<xsl:output method="xml" indent="yes"/> 

<!-- Number of allowed characters: This should be total number of chars of 
    format-type desired minus the chars that should be skipped. --> 
<xsl:variable name="lbase" select="23"/> 
<!-- Sequence of character positions not allowed, with 1=>a to 26=>z --> 
<xsl:variable name="lexcs" select="(9,12,15)"/> <!-- i,l,o --> 

<!-- Helper Function: 
    Convert integer to sequence of number of given base. 
    The sequence of numbers is in reverse order: ^0,^1,^2,...^N. 
    --> 
<xsl:function name="ewh:get_base_digits" as="item()*"> 
    <xsl:param name="number" as="xs:integer"/> 
    <xsl:param name="to"  as="xs:integer"/> 
    <xsl:variable name="Q" select="$number idiv $to"/> 
    <xsl:variable name="R" select="$number mod $to"/> 
    <xsl:sequence select="$R"/> 
    <xsl:if test="$Q gt 0"> 
    <xsl:sequence select="ewh:get_base_digits($Q,$to)"/> 
    </xsl:if> 
</xsl:function> 

<!-- Helper Function: 
    Compute carry-overs in reverse-base digit sequence. XSLT starts 
    numbering at 1, so we cannot have any 0s. 
    --> 
<xsl:function name="ewh:compute_carry_overs" as="item()*"> 
    <xsl:param name="digits" as="item()*"/> 
    <xsl:variable name="d" select="subsequence($digits, 1, 1)"/> 
    <xsl:choose> 
    <xsl:when test="($d le 0) and (count($digits) = 1)"> 
     <!-- 0 at end of list, nothing to do --> 
    </xsl:when> 
    <xsl:when test="$d le 0"> 
     <!-- If digit <=0, need to perform carry-over operation --> 
     <xsl:variable name="next" select="subsequence($digits, 2, 1)"/> 
     <xsl:choose> 
     <xsl:when test="count($digits) le 2"> 
      <xsl:sequence select="$lbase + $d"/> 
      <xsl:sequence select="ewh:compute_carry_overs($next - 1)"/> 
     </xsl:when> 
     <xsl:otherwise> 
      <xsl:sequence select="$lbase + $d"/> 
      <xsl:sequence select="ewh:compute_carry_overs(($next - 1, 
       subsequence($digits, 3)))"/> 
     </xsl:otherwise> 
     </xsl:choose> 
    </xsl:when> 
    <xsl:when test="count($digits) le 1"> 
     <xsl:sequence select="$d"/> 
    </xsl:when> 
    <xsl:otherwise> 
     <xsl:sequence select="$d"/> 
     <xsl:sequence select="ewh:compute_carry_overs(subsequence($digits, 2))"/> 
    </xsl:otherwise> 
    </xsl:choose> 
</xsl:function> 

<!-- Helper Function: 
    Given a number in the base range, determine number for 
    purposes of <xsl:number>. We loop thru the exclusion 
    list and add 1 for each exclusion letter that has 
    been passed. The $digit parameter should be a number 
    in the range [1..$lbase]. 
    --> 
<xsl:function name="ewh:compute_digit_offset" as="xs:integer"> 
    <xsl:param name="digit"  as="xs:integer"/> 
    <xsl:param name="excludes" as="item()*"/> 
    <xsl:variable name="l" select="subsequence($excludes, 1, 1)"/> 
    <xsl:variable name="result"> 
    <xsl:choose> 
     <xsl:when test="$digit lt $l"> 
     <xsl:value-of select="0"/> 
     </xsl:when> 
     <xsl:when test="count($excludes) = 1"> 
     <xsl:value-of select="1"/> 
     </xsl:when> 
     <xsl:otherwise> 
     <xsl:variable name="rest"> 
      <xsl:value-of select="ewh:compute_digit_offset($digit+1, 
       subsequence($excludes,2))"/> 
     </xsl:variable> 
     <xsl:value-of select="1 + $rest"/> 
     </xsl:otherwise> 
    </xsl:choose> 
    </xsl:variable> 
    <xsl:value-of select="$result"/> 
</xsl:function> 

<!-- Retrieve alpha sequence label. 
    This is the main function to call. 
    --> 
<xsl:function name="ewh:get-alpha-label" as="xs:string"> 
    <xsl:param name="number" as="xs:integer"/> 
    <xsl:variable name="basedigits" 
       select="ewh:get_base_digits($number,$lbase)"/> 
    <xsl:variable name="digits" 
       select="ewh:compute_carry_overs($basedigits)"/> 
    <xsl:variable name="result" as="item()*"> 
    <xsl:for-each select="$digits"> 
     <xsl:variable name="digit" select="."/> 
     <!-- Should not have any 0 values. If some reason we do, 
      we ignore assuming they are trailing items. --> 
     <xsl:if test="$digit != 0"> 
     <xsl:variable name="value"> 
      <xsl:value-of select="$digit + 
       ewh:compute_digit_offset($digit,$lexcs)"/> 
     </xsl:variable> 
     <xsl:variable name="number"> 
      <xsl:number value="$value" format="a"/> 
     </xsl:variable> 
     <xsl:sequence select="$number"/> 
     </xsl:if> 
    </xsl:for-each> 
    </xsl:variable> 
    <xsl:value-of select="string-join(reverse($result),'')"/> 
</xsl:function> 

<!-- For testing --> 
<xsl:template match="/"> 
    <result> 
    <xsl:for-each select="(1 to 1000,12166,12167,12168,279840,279841,279842)"> 
     <value n="{.}"><xsl:value-of select="ewh:get-alpha-label(.)"/></value> 
    </xsl:for-each> 
    </result> 
</xsl:template> 

</xsl:stylesheet> 
+0

+1 (테스트와 같이). –

+0

나는 일반화 된 수학적 해법을 생각해 냈지만 실패했다. 잠시 동안 일할 수도있는 일이 생길 수도 있지만 더 큰 숫자의 경우 결과가 잘못되었습니다. 각 23^N 장소에서 여분의 시프 팅을 다루는 방법을 알아 내려고 시도하는 것은이 시간에 나를 빠져 나간다. 그러므로 현재의 해결책이다. – ewh

+0

내가 찾은 가장 가까운 수학적 해결책은 반복적이었고, 큰 숫자는 넘쳐서 실패했습니다. 그것이 맞지 않을 수도 있고 재귀가 될 수도 있습니다. –

3

XSLT 2.0은 xsl:numberformat 속성을 제공하므로 토큰 형식은 aa입니다. 계산 된 수는 value 특성에서 계산 된 식에 따라 달라지며 format에 따라 서식이 지정됩니다.

주어진 경우 은 먼저 특정 문자와 일치하는 숫자를 제외하고 숫자의 올바른 순서를 평가한다고 생각할 수 있습니다. 예컨대

다음 명령어 :

<xsl:number value="$sequence" format="aa"/> 

인쇄한다 (제외 통지 i)

a.b.c.d.e.f.g.h.j.k.l.m 

$sequence가 평가되는 경우 (통지 9 스킵)에 :

1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13 

12 개의 원소가 있다면 expre 시온은 불필요한 숫자를 건너 뛰고 (i의 경우 9) 다음 중 하나를 증가시킬 수 있어야합니다. 12 번 위치의 마지막 요소는 해당하는 숫자 13을 가져야합니다.

원하는 항목은 인 알고리즘입니다. 이는 입력 문서에 따라 다릅니다.

참고 : XSLT 2.0 Rec.

+0

나는 기본적인 접근법을 좋아하지만 시퀀스를 올바르게 생성하는 방법에 대해 생각해야 할 것입니다. 제외 할 글자는 입력과 관계가 없습니다. 예를 들어 숫자가 이중 문자로 바뀌는 경우를 포함하여 결과 인 ''에는 'i', 'l'및 'o'문자를 사용할 수 없습니다. 알고리즘이 얼마나 원시 시퀀스 번호를 유효한 시퀀스 번호에 매핑하는지 간단하지 않습니다. – ewh

+0

예를 들어, 이중 문자로'i'도 제외하려면 숫자 34 (9 + 26)를 건너 뛰어야합니다. 처리 할 XML 샘플을 게시 할 수 있으며 여기에 전문가가 도움을 드리고자합니다. –

+0

순서는 일반적입니다 : 순서가 지정된 목록처럼 레이블이 지정되어야하지만 특정 알파 문자를 제외하는 레이블이있는 x 항목 수. "건너 뛰기"는 생각만큼 간단하지 않습니다. 'z'는 항목 23을 나타냅니다. 항목 34는 'am'이라는 라벨을 갖습니다. 누락 된 문자는 23 개 항목마다 이동을 증가 시키지만 제외해야하는 글자가 3 개이므로 실제 이동이 사이클마다 다양합니다. 내 일반적인 문제에 대한 내 대답을 참조하십시오, 그러나 그것은 ''을 사용하지 않습니다. – ewh

1

편집 : 대체는,보다 일반적인 솔루션이 존재하고 별도의 답변으로 게시됩니다. 나는 아직도이 가치가있을 수 있기 때문에이 대답을 떠날 것이다.

나는 @ empo의 생각이 마음에 들지만 (나는 그것을 수정했다), 나는 그것이 작동하는 해결책을 얻는 것이 어려울 것이라고 생각한다. 금지 된 문자가 포함되지 않은 레이블을 얻지 못하게하려면 원시 시퀀스를 기반으로 올바른 시퀀스 번호를 찾는 데 영리한 알고리즘/수식이 필요합니다. 이 때, 그런 알고리즘은 나를 벗어난다.

내 생각에 떠오르는 한 가지 방법은 내 기능을 만들고, <xsl:number>을 사용하지 않는 것입니다. 본질적으로 우리는 , lo 문자를 제외한 기본 23 세트 (a ~ z)를 처리합니다.필자가 생각해 낸 기능은 zz까지만 가능하지만 필요한만큼 충분해야합니다 (최대 552 개까지 레이블 지정 가능).

<result> 
    <value n="9">j</value> 
    <value n="12">n</value> 
    <value n="15">r</value> 
    <value n="23">z</value> 
    <value n="26">ac</value> 
    <value n="33">ak</value> 
    <value n="46">az</value> 
    <value n="69">bz</value> 
    <value n="70">ca</value> 
    <value n="200">hs</value> 
    <value n="552">zz</value> 
</result> 

그것은 <xsl:number>와 함께 사용하기위한 사용자 정의 문자 순서를 정의 할 수있는 기능을 제공 XSLT의 좋은 것 : 나는 위를 실행하면

<xsl:stylesheet version="2.0" 
       xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
       xmlns:xs="http://www.w3.org/2001/XMLSchema" 
       xmlns:ewh="http://www.earlhood.com/XSL/Transform" 
       exclude-result-prefixes="#all"> 

<xsl:output method="xml" indent="yes"/> 

<xsl:variable name="letters" select="'abcdefghjkmnpqrstuvwxyz'"/> 
<xsl:variable name="lbase" select="23"/> 

<xsl:function name="ewh:get-alpha-label" as="xs:string"> 
    <xsl:param name="number" as="xs:integer"/> 
    <xsl:variable name="quotient" select="$number idiv $lbase"/> 
    <xsl:variable name="remainder" select="$number mod $lbase"/> 
    <xsl:variable name="p1"> 
    <xsl:choose> 
     <xsl:when test="($quotient gt 0) and ($remainder = 0)"> 
     <xsl:value-of select="substring($letters,($quotient - 1),1)"/> 
     </xsl:when> 
     <xsl:when test="($quotient gt 0) and ($remainder gt 0)"> 
     <xsl:value-of select="substring($letters,$quotient,1)"/> 
     </xsl:when> 
     <xsl:otherwise/> 
    </xsl:choose> 
    </xsl:variable> 
    <xsl:variable name="p0"> 
    <xsl:choose> 
     <xsl:when test="$remainder = 0"> 
     <xsl:value-of select="substring($letters,$lbase,1)"/> 
     </xsl:when> 
     <xsl:otherwise> 
     <xsl:value-of select="substring($letters,$remainder,1)"/> 
     </xsl:otherwise> 
    </xsl:choose> 
    </xsl:variable> 
    <xsl:value-of select="concat($p1,$p0)"/> 
</xsl:function> 

<xsl:template match="/"> 
    <result> 
    <value n="9"><xsl:value-of select="ewh:get-alpha-label(9)"/></value> 
    <value n="12"><xsl:value-of select="ewh:get-alpha-label(12)"/></value> 
    <value n="15"><xsl:value-of select="ewh:get-alpha-label(15)"/></value> 
    <value n="23"><xsl:value-of select="ewh:get-alpha-label(23)"/></value> 
    <value n="26"><xsl:value-of select="ewh:get-alpha-label(26)"/></value> 
    <value n="33"><xsl:value-of select="ewh:get-alpha-label(33)"/></value> 
    <value n="46"><xsl:value-of select="ewh:get-alpha-label(46)"/></value> 
    <value n="69"><xsl:value-of select="ewh:get-alpha-label(69)"/></value> 
    <value n="70"><xsl:value-of select="ewh:get-alpha-label(70)"/></value> 
    <value n="200"><xsl:value-of select="ewh:get-alpha-label(200)"/></value> 
    <value n="552"><xsl:value-of select="ewh:get-alpha-label(552)"/></value> 
    </result> 
</xsl:template> 

</xsl:stylesheet> 

, 나는 다음과 같은 출력을 얻을. XSLT 엔진이 <xsl:number>을 제공하는지는 알 수없는 사용자 지정 확장에 의존하지 않고 이러한 기능을 일반화하는 것은 <xsl:number> 일 것입니다.

+0

나는 많은 수의 항목에 적용 할 수있는 수치 솔루션을 발견했으며 xsl : number와 쉽게 통합되었다. 아직도 이런 종류의 솔루션에 관심이 있습니까? –

+0

예 관심 있어요. 실제로 숫자 제한이없는 다른 솔루션을 제안했습니다. 특히 큰 숫자의 경우에는 경계 사례를 올바르게 처리하는 데 약간의 시간이 걸렸습니다. 비슷한 일반적인 해결책을 찾았을 때를 대비하여 게시 할 때까지 게시 할 수 있습니다. – ewh

+0

좋아요, 5 분 동안 무료로 답변을 드리겠습니다. –

1

인터페이스 net.sf.saxon.lib.Numberer의 구현을 작성하여 Saxon에서 xsl : number의 출력을 사용자 정의 할 수 있습니다. 아마 net.sf.saxon.expr의 서브 클래스로 만들고 싶을 것입니다. 번호 .Numberer_en. 소스 코드를 연구하고 재정의가 필요한 항목을 찾아야합니다.

Saxon PE/EE에서는 Saxon 구성 파일에서 주어진 언어에 사용할 Numberer을 등록 할 수 있습니다. Saxon HE의 경우 좀 더 많은 작업이 필요합니다. LocalizerFactory 인터페이스를 구현하고 LocalizerFactory를 Configuration에 등록해야합니다.

관련 문제