automatically apply XSLT to convert CSL 0.8 styles to CSL 1.0 styles on the fly

do we want to re-save the converted files over the old ones?
This commit is contained in:
Simon Kornblith 2010-05-30 12:39:48 +00:00
parent 67fb2f6fc2
commit dcdef746e5
2 changed files with 466 additions and 14 deletions

View file

@ -346,6 +346,7 @@ Zotero.Style = function(file) {
this.styleID = Zotero.Styles.ios.newFileURI(this.file).spec;
this.title = file.leafName.substr(0, file.leafName.length-4);
this.updated = Zotero.Date.dateToSQL(new Date(file.lastModifiedTime));
this._versino = "0.8";
} else if(extension == ".csl") {
// "with ({});" needed to fix default namespace scope issue
// See https://bugzilla.mozilla.org/show_bug.cgi?id=330572
@ -360,6 +361,8 @@ Zotero.Style = function(file) {
this.updated = xml.info.updated.toString().replace(/(.+)T([^\+]+)\+?.*/, "$1 $2");
this._class = xml.@class.toString();
this._hasBibliography = !!xml.bibliography.length();
this._version = xml.@version.toString();
if(this._version == "") this._version = "0.8";
this.source = null;
for each(var link in xml.info.link) {
@ -389,7 +392,40 @@ function() {
}
}
return new Zotero.CiteProc.CSL.Engine(Zotero.Cite.System, this.getXML(), Zotero.locale);
if(this._version == "0.8") {
// get XSLT file
let updateXSLTFile = Zotero.getInstallDirectory();
updateXSLTFile.append("updateCSL.xsl");
// read XSLT file as DOM XML
let xmlFiles = [];
let fis = Components.classes["@mozilla.org/network/file-input-stream;1"].
createInstance(Components.interfaces.nsIFileInputStream);
fis.init(updateXSLTFile, 0x01, 0664, 0);
let updateXSLT = Components.classes["@mozilla.org/xmlextras/domparser;1"]
.createInstance(Components.interfaces.nsIDOMParser)
.parseFromStream(fis, "UTF-8", updateXSLTFile.fileSize, "text/xml");
fis.close();
// load XSLT file into XSLTProcessor
let xsltProcessor = Components.classes["@mozilla.org/document-transformer;1?type=xslt"]
.createInstance(Components.interfaces.nsIXSLTProcessor);
xsltProcessor.importStylesheet(updateXSLT);
// read style file as DOM XML
let styleDOMXML = Components.classes["@mozilla.org/xmlextras/domparser;1"]
.createInstance(Components.interfaces.nsIDOMParser)
.parseFromString(this.getXML(), "text/xml");
// apply XSLT and serialize output
let newDOMXML = xsltProcessor.transformToDocument(styleDOMXML);
var xml = Components.classes["@mozilla.org/xmlextras/xmlserializer;1"]
.createInstance(Components.interfaces.nsIDOMSerializer).serializeToString(newDOMXML);
} else {
var xml = this.getXML();
}
return new Zotero.CiteProc.CSL.Engine(Zotero.Cite.System, xml, Zotero.locale);
});
Zotero.Style.prototype.__defineGetter__("class",
@ -413,6 +449,24 @@ function() {
return this._hasBibliography;
});
Zotero.Style.prototype.__defineGetter__("independentFile",
/**
* Retrieves the file corresponding to the independent CSL
* (the parent if this style is dependent, or this style if it is not)
*/
function() {
if(this.source) {
// parent/child
var formatCSL = Zotero.Styles.get(this.source);
if(!formatCSL) {
throw(new Error('Style references '+this.source+', but this style is not installed',
Zotero.Styles.ios.newFileURI(this.file).spec, null));
}
return formatCSL.file;
}
return this.file;
});
/**
* Retrieves the XML corresponding to this style
* @type String
@ -432,19 +486,7 @@ Zotero.Style.prototype.getXML = function() {
return XML.toXMLString();
} else {
if(this.source) {
// parent/child
var formatCSL = Zotero.Styles.get(this.source);
if(!formatCSL) {
throw(new Error('Style references '+this.source+', but this style is not installed',
Zotero.Styles.ios.newFileURI(this.file).spec, null));
}
var file = formatCSL.file;
} else {
var file = this.file;
}
return Zotero.File.getContents(file);
return Zotero.File.getContents(this.independentFile);
}
};

410
updateCSL.xsl Normal file
View file

@ -0,0 +1,410 @@
<xsl:stylesheet xmlns:cs="http://purl.org/net/xbiblio/csl" xmlns="http://purl.org/net/xbiblio/csl"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" exclude-result-prefixes="cs">
<xsl:output indent="yes" method="xml" encoding="utf-8"/>
<xsl:strip-space elements="*"/>
<!-- * xml:lang is no longer allowed on cs:style to eliminate confusion with
the default-locale attribute. If xml:lang was set, its value is
transferred to the default-locale attribute.
* cs:style now indicates CSL version compatibility via the version
attribute. -->
<xsl:template match="/cs:style">
<xsl:copy>
<xsl:copy-of select="@*[not(name()='xml:lang')]"/>
<xsl:choose>
<xsl:when test="@xml:lang and not(@xml:lang='en' or @xml:lang='en-US' or @xml:lang='en-us')">
<xsl:attribute name="default-locale">
<xsl:value-of select="@xml:lang"/>
</xsl:attribute>
</xsl:when>
</xsl:choose>
<xsl:attribute name="version">1.0</xsl:attribute>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<!-- Elements that themselves can be copied verbatim but whose child nodes
might require modification. -->
<xsl:template
match="cs:choose|cs:else|cs:info|cs:names|cs:substitute|cs:macro|cs:layout">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<!-- Elements that can be copied verbatim together with any child nodes. -->
<xsl:template match="cs:sort|cs:number|comment()">
<xsl:copy-of select="."/>
</xsl:template>
<!-- Child elements of cs:info that can be copied verbatim. -->
<xsl:template
match="cs:author|cs:contributor|cs:id|cs:issn|cs:published|cs:rights|cs:source|cs:summary|cs:title|cs:updated">
<xsl:copy-of select="."/>
</xsl:template>
<!-- For the rel attribute on cs:link, "documentation" will be used instead of
"homepage", and "source" has been renamed to "independent-parent". The
URL of a dependent style is now accompanied with a rel value of "self". -->
<xsl:template match="cs:link">
<xsl:variable name="rel-value">
<xsl:choose>
<xsl:when test="@rel='documentation' or @rel='homepage'">documentation</xsl:when>
<xsl:when test="@rel='template'">template</xsl:when>
<xsl:when test="@rel='source'">independent-parent</xsl:when>
<xsl:otherwise>self</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:copy>
<xsl:attribute name="href">
<xsl:value-of select="@href"/>
</xsl:attribute>
<xsl:attribute name="rel">
<xsl:value-of select="$rel-value"/>
</xsl:attribute>
</xsl:copy>
</xsl:template>
<!-- The citation-format and field attributes have replaced the term attribute
on cs:category, and the "in-text" category has been removed. Styles with
the "in-text" category are assigned the "numeric" citation-format if the
"citation-number" variable is used in citations, and the "author-date"
format in all other cases. -->
<xsl:template match="cs:category">
<xsl:choose>
<xsl:when test="@term='in-text'">
<xsl:choose>
<xsl:when test="/cs:style/cs:citation//cs:text[@variable='citation-number']">
<xsl:copy>
<xsl:attribute name="citation-format">numeric</xsl:attribute>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:attribute name="citation-format">author-date</xsl:attribute>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="@term='author-date' or @term='numeric' or @term='label' or @term='note'">
<xsl:copy>
<xsl:attribute name="citation-format">
<xsl:value-of select="@term"/>
</xsl:attribute>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:attribute name="field">
<xsl:value-of select="@term"/>
</xsl:attribute>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- The hierarchy cs:terms/cs:locale/cs:term has been replaced by
cs:locale/cs:terms/cs:term. -->
<xsl:template match="cs:terms/cs:locale">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:element name="terms">
<xsl:copy-of select="cs:term"/>
</xsl:element>
</xsl:copy>
</xsl:template>
<!-- Citation-specific CSL options are now set as attributes on cs:citation,
instead of via cs:option elements. -->
<xsl:template match="cs:citation">
<xsl:copy>
<xsl:for-each select="cs:option">
<xsl:attribute name="{@name}">
<xsl:value-of select="@value"/>
</xsl:attribute>
</xsl:for-each>
<xsl:apply-templates select="cs:layout|cs:sort"/>
</xsl:copy>
</xsl:template>
<!-- * Bibliography-specific CSL options are now set as attributes on
cs:bibliography, instead of via cs:option elements.
* second-field-align now uses the value "flush" instead of "true". -->
<xsl:template match="cs:bibliography">
<xsl:copy>
<xsl:for-each select="cs:option">
<xsl:choose>
<xsl:when test="@name='second-field-align' and @value='true'">
<xsl:attribute name="second-field-align">flush</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="{@name}">
<xsl:value-of select="@value"/>
</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:apply-templates select="cs:layout|cs:sort"/>
</xsl:copy>
</xsl:template>
<!-- CSL 1.0 eliminates item type fallback behavior in conditionals
("article", "book" and "chapter" used to be type archetypes, e.g.
"article-journal" would be a subtype of "article", see
http://docs.google.com/View?id=dg6h9k72_64hzmsmqgb). For the conversion,
each archetype is replaced by all subtypes of that archetype (while
making sure that "song" is only present once in the attribute string, as
"song" is a subtype of both "article" and "book"). If an archetype is
replaced by its subtypes, the match attribute is set to "any", unless
match was set to "none". This is done as "type='chapter' match='all'" can
test true, but "type='chapter paper-conference' match='all' can not (each
item is of a single type). cs:if and cs:if-else elements that, in
addition to the type conditional, test with match="all" (the default
attribute value) and carry additional conditionals are split into a
nested conditional (an example of this case is given at
http://forums.zotero.org/discussion/11960/item-type-testing-in-csl-10-and-fallbacks/#Comment_58392
). -->
<xsl:template match="cs:if|cs:else-if">
<xsl:copy>
<xsl:choose>
<xsl:when test="contains(@type,'book') or (contains(@type,'article') and not(contains(@type,'article-'))) or contains(@type,'chapter')">
<xsl:choose>
<xsl:when test="(not(@match='any') and not(@match='none')) and (@variable or @is-numeric or @position or @disambiguate)">
<xsl:call-template name="fallback-replacement"/>
<xsl:attribute name="match">any</xsl:attribute>
<xsl:element name="choose">
<xsl:element name="if">
<xsl:copy-of select="@*[not(name()='type')]"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="@*"/>
<xsl:call-template name="fallback-replacement"/>
<xsl:if test="not(@match='none')">
<xsl:attribute name="match">any</xsl:attribute>
</xsl:if>
<xsl:apply-templates/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
<xsl:template name="fallback-replacement">
<xsl:choose>
<xsl:when test="contains(@type,'book')">
<xsl:attribute name="type">
<xsl:call-template name="book-sans-space"/>
<xsl:call-template name="article"/>
<xsl:call-template name="chapter"/>
</xsl:attribute>
</xsl:when>
<xsl:when test="contains(@type,'article') and not(contains(@type,'article-'))">
<xsl:attribute name="type">
<xsl:call-template name="article-sans-space"/>
<xsl:call-template name="book"/>
<xsl:call-template name="chapter"/>
</xsl:attribute>
</xsl:when>
<xsl:when test="contains(@type,'chapter')">
<xsl:attribute name="type">
<xsl:call-template name="chapter-sans-space"/>
<xsl:call-template name="article"/>
<xsl:call-template name="book"/>
</xsl:attribute>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="book-sans-space">
<xsl:text>bill book graphic legal_case motion_picture report song</xsl:text>
</xsl:template>
<xsl:template name="article-sans-space">
<xsl:choose>
<xsl:when test="contains(@type,'article') and not(contains(@type,'article-')) and contains(@type,'book')">article-journal article-magazine article-newspaper broadcast interview manuscript map patent personal_communication speech thesis webpage</xsl:when>
<xsl:when test="contains(@type,'article') and not(contains(@type,'article-'))">article-journal article-magazine article-newspaper broadcast interview manuscript map patent personal_communication song speech thesis webpage</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="chapter-sans-space">
<xsl:text>chapter paper-conference</xsl:text>
</xsl:template>
<xsl:template name="book">
<xsl:choose>
<xsl:when test="contains(@type,'book')"> bill book graphic legal_case motion_picture report song</xsl:when>
<xsl:otherwise>
<xsl:if test="contains(@type,'bill')"> bill</xsl:if>
<xsl:if test="contains(@type,'graphic')"> graphic</xsl:if>
<xsl:if test="contains(@type,'legal_case')"> legal_case</xsl:if>
<xsl:if test="contains(@type,'motion_picture')"> motion_picture</xsl:if>
<xsl:if test="contains(@type,'report')"> report</xsl:if>
<xsl:if test="contains(@type,'song')"> song</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="article">
<xsl:choose>
<xsl:when test="contains(@type,'article') and not(contains(@type,'article-')) and contains(@type,'book')"> article-journal article-magazine article-newspaper broadcast interview manuscript map patent personal_communication speech thesis webpage</xsl:when>
<xsl:when test="contains(@type,'article') and not(contains(@type,'article-'))"> article-journal article-magazine article-newspaper broadcast interview manuscript map patent personal_communication song speech thesis webpage</xsl:when>
<xsl:otherwise>
<xsl:if test="contains(@type,'article-journal')"> article-journal</xsl:if>
<xsl:if test="contains(@type,'article-magazine')"> article-magazine</xsl:if>
<xsl:if test="contains(@type,'article-newspaper')"> article-newspaper</xsl:if>
<xsl:if test="contains(@type,'broadcast')"> broadcast</xsl:if>
<xsl:if test="contains(@type,'interview')"> interview</xsl:if>
<xsl:if test="contains(@type,'manuscript')"> manuscript</xsl:if>
<xsl:if test="contains(@type,'map')"> map</xsl:if>
<xsl:if test="contains(@type,'patent')"> patent</xsl:if>
<xsl:if test="contains(@type,'personal_communication')"> personal_communication</xsl:if>
<xsl:if test="contains(@type,'speech')"> speech</xsl:if>
<xsl:if test="contains(@type,'thesis')"> thesis</xsl:if>
<xsl:if test="contains(@type,'map')"> map</xsl:if>
<xsl:if test="contains(@type,'webpage')"> webpage</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="chapter">
<xsl:choose>
<xsl:when test="contains(@type,'chapter')"> chapter paper-conference</xsl:when>
<xsl:otherwise>
<xsl:if test="contains(@type,'paper-conference')"> paper-conference</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- The class attribute on cs:group has been removed in favor of the display
attribute. -->
<xsl:template match="cs:group">
<xsl:copy>
<xsl:copy-of select="@*[not(name()='class')]"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<!-- The text-case attribute can no longer be used on cs:name. In cases
where text-case was used on cs:name, the attribute and its value are
transferred to the "family" and "given" cs:name-part children. -->
<xsl:template match="cs:name">
<xsl:copy>
<xsl:copy-of select="@*[not(name()='text-case')]"/>
<xsl:choose>
<xsl:when test="@text-case">
<xsl:element name="name-part">
<xsl:attribute name="name">family</xsl:attribute>
<xsl:attribute name="text-case">
<xsl:value-of select="@text-case"/>
</xsl:attribute>
</xsl:element>
<xsl:element name="name-part">
<xsl:attribute name="name">given</xsl:attribute>
<xsl:attribute name="text-case">
<xsl:value-of select="@text-case"/>
</xsl:attribute>
</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:copy>
</xsl:template>
<!-- * In CSL 1.0, abbreviated terms are defined with periods, if applicable,
and the include-period attribute has replaced the strip-periods
attribute. For the conversion, strip-periods is set to "true" for any
cs:label element with form="short" or "verb-short", except when
include-period was set to "true".
* plural on cs:label now uses "always"/"never" instead of "true"/"false". -->
<xsl:template match="cs:label">
<xsl:copy>
<xsl:copy-of select="@*[not(name()='include-period')]"/>
<xsl:choose>
<xsl:when test="(@form='short' or @form='verb-short') and not(@include-period='true')">
<xsl:attribute name="strip-periods">true</xsl:attribute>
</xsl:when>
</xsl:choose>
<xsl:choose>
<xsl:when test="@plural='false'">
<xsl:attribute name="plural">never</xsl:attribute>
</xsl:when>
<xsl:when test="@plural='true'">
<xsl:attribute name="plural">always</xsl:attribute>
</xsl:when>
</xsl:choose>
</xsl:copy>
</xsl:template>
<!-- The "event" date variable has been renamed to "event-date" to eliminate
the name conflict with the 'standard' "event" variable. -->
<xsl:template match="cs:date">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:choose>
<xsl:when test="@variable='event'">
<xsl:attribute name="variable">event-date</xsl:attribute>
</xsl:when>
</xsl:choose>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<!-- In CSL 1.0, abbreviated terms are defined with periods, if applicable,
and the include-period attribute has replaced the strip-periods
attribute. For the conversion, strip-periods is set to "true" for any
"month" cs:date-part element with form="short", except when
include-period was set to "true". -->
<xsl:template match="cs:date-part">
<xsl:choose>
<xsl:when test="@name='year' or @name='month' or @name='day'">
<xsl:copy>
<xsl:copy-of select="@*[not(name()='include-period')]"/>
<xsl:choose>
<xsl:when
test="@form='short' and @name='month' and not(@include-period='true')">
<xsl:attribute name="strip-periods">true</xsl:attribute>
</xsl:when>
</xsl:choose>
</xsl:copy>
</xsl:when>
</xsl:choose>
</xsl:template>
<!-- * In CSL 1.0, abbreviated terms are defined with periods, if applicable,
and the include-period attribute has replaced the strip-periods
attribute. For the conversion, strip-periods is set to "true" for any
cs:text element with form="short" or "verb-short", except when
include-period was set to "true".
* The CSL 0.8 en-US locale file only included the "long" form of the
"no date" term, with a value of "n.d.". In the CSL 1.0 locale file, the
value has been changed to "no date", and a "short" form ("n.d.") has
been introduced. For the conversion, any cs:text element that called
the "long" form of the "no date" term will now call the "short" form,
unless the "long" form had been redefined in the style. -->
<xsl:template match="cs:text">
<xsl:copy>
<xsl:copy-of select="@*[not(name()='include-period')]"/>
<xsl:choose>
<xsl:when
test="(@form='short' or @form='verb-short') and not(@include-period='true') and @term">
<xsl:attribute name="strip-periods">true</xsl:attribute>
</xsl:when>
</xsl:choose>
<xsl:choose>
<xsl:when test="@term='no date' and not(/cs:style/cs:terms/cs:locale/cs:term/@name='no date')">
<xsl:attribute name="form">short</xsl:attribute>
</xsl:when>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>