<xsl:template match="tutorial" mode="generate-zip-file">
<xsl:choose>
<xsl:when test="function-available('zip:buildZipFile')">
<xsl:variable name="referencedGraphics"
select="./@img|//image-column/@img|//img/@src"/>
<xsl:value-of
select="zip:buildZipFile($fn, $curDir,
$fileSep, ., $referencedGraphics)"/>
</xsl:when>
<xsl:otherwise>
<xsl:message terminate="yes">
Error! Zip file library not available!
</xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
In the extension function code itself, we start by creating the ZipOutputStream itself:
ZipOutputStream zipOut =
new ZipOutputStream(new FileOutputStream(currentDirectory +
fileSeparator +
baseFilename + ".zip"));
Once we've created our ZipOutputStream, we'll see if there's a comment for the zip file in the zip-file-comment attribute of the <tutorial> element:
Node currentNode = tutorialElement.nextNode();
while (currentNode != null)
{
if (currentNode.getLocalName().equals("tutorial"))
{
ElementImpl currentElement = (ElementImpl)currentNode;
String zipFileComment = currentElement.getAttribute("zip-file-comment");
if (zipFileComment != null)
zipOut.setComment(zipFileComment);
else
{
zipFileComment = currentElement.getAttribute("alt");
if (zipFileComment != null)
zipOut.setComment(zipFileComment);
}
With everything we do with the DOM nodes, we'll need to make sure we actually work with the appropriate nodes; that's why we use the function call getLocalName().equals("tutorial"). Once we've found the <tutorial> element, we can work with its children to figure out the names of all the HTML and JPEG files we need to add to the zip file. If the <tutorial> element has five <section> children, and the first <section> contains eleven <panel>s, then we'll need to write the files tootomatic-1-1.html through tootomatic-1-11.html to the zip file. (This assumes that the base filename we use is tootomatic.) Here's an excerpt from the code:
int numKids = currentElement.getChildCount();
int numSections = 0;
for (int i = 0; i < numKids; i++)
{
Node currentChild = currentElement.getChild(i);
if (currentChild.getLocalName().equals("section"))
{
ElementImpl currentChildElement = (ElementImpl)currentChild
fileToZip = new File(currentDirectory + fileSeparator + "index" +
++numSections + ".html");
fis = new FileInputStream(fileToZip);
entry = new ZipEntry(currentDirectory + fileSeparator +
fileToZip.getName());
if (zipOut != null)
{
zipOut.putNextEntry(entry);
while((bytes_read = fis.read(buffer)) != -1)
zipOut.write(buffer, 0, bytes_read);
}
fis.close();
int numGrandkids = currentChildElement.getChildCount();
int numPanels = 0;
for (int j = 0; j < numGrandkids; j++)
{
Node currentGrandchildElement = currentChildElement.getChild(j);
if (currentGrandchildElement.getLocalName().equals("panel"))
{
fileToZip = new File(currentDirectory + fileSeparator +
baseFilename + "-" + numSections + "-" +
++numPanels + ".html");
fis = new FileInputStream(fileToZip);
entry = new ZipEntry(currentDirectory + fileSeparator +
fileToZip.getName());
if (zipOut != null)
{
zipOut.putNextEntry(entry);
while((bytes_read = fis.read(buffer)) != -1)
zipOut.write(buffer, 0, bytes_read);
}
fis.close();
}
}
}
}
Now that we know how many <section> elements are in our <tutorial>, we can write all the generated JPEG graphics to the zip file. Our extension function also contains a static array of the filenames of all standard files used by every tutorial:
static String standardFiles[] = {"c.gif", "sw-gold.gif",
"main.gif", "xmain.gif",
"section.gif", "xsection.gif",
"feedback.gif", "xfeedback.gif",
"previous.gif", "xprevious.gif",
"next.gif", "xnext.gif",
"icon-discuss.gif", "icon-email.gif",
"icon-pdf-ltr.gif", "icon-zip.gif",
"icon-pdf-a4.gif",
"mast_logo.gif", "shopibm.gif",
"support.gif", "downloads.gif",
"mast_lnav_sp.gif", "about.gif",
"h-menu.gif", "h-main.gif",
"h-section.gif", "h-feedback.gif",
"h-previous.gif", "h-next.gif",
"nextsection.gif", "h-nextsection.gif",
"arrow.gif", "mgradient.gif",
"email.gif", "dw-logo2.gif",
"btn-send.gif", "btn-close.gif",
"emailfriend.js"};
We store each of these standard files in the zip file for each tutorial. Storing the names of the files in an array makes it easy to add or delete new files from the list. If this list of files changed frequently, we would consider writing an XML-based configuration file that listed all standard files. We could then parse that file, extract the filenames from it, and add those files to the zip file.
Our next task is to use our node-set of graphics elements to add all referenced graphics to the zip file:
currentNode = graphicsElements.nextNode();
HashMap zipEntries = new HashMap();
while (currentNode != null)
{
String nextGraphicsFile = currentNode.getNodeValue();
if (!zipEntries.containsKey(nextGraphicsFile))
{
fileToZip = new File(currentNode.getNodeValue());
fis = new FileInputStream(fileToZip);
entry = new ZipEntry(currentDirectory + fileSeparator +
currentNode.getNodeValue());
zipOut.putNextEntry(entry);
while ((bytes_read = fis.read(buffer)) != -1)
zipOut.write(buffer, 0, bytes_read);
zipEntries.put(nextGraphicsFile, nextGraphicsFile);
}
currentNode = graphicsElements.nextNode();
}
As we add a referenced graphics file to the zip file, we put the name of the file into a HashMap. If we attempt to add a file to the zip archive and that file is already in the archive, we'll get an exception. To avoid that problem, we check each filename before we add it to the zip file.