#20 Skriptování: Makra ve skriptech a Skripty v makrech + výpis verzí z Mercurialu.
authorFrantišek Kučera <franta-hg@frantovo.cz>
Thu, 05 Jul 2012 19:10:42 +0200
changeset 107 379a2a893fd1
parent 106 ae484a54d7fb
child 108 8d9cab64c335
#20 Skriptování: Makra ve skriptech a Skripty v makrech + výpis verzí z Mercurialu.
vstup/makra/hg-verze.xsl
vstup/skriptování.xml
šablona/funkce/src/cz/frantovo/xmlWebGenerator/Xmlns.java
šablona/funkce/src/cz/frantovo/xmlWebGenerator/makra/Skriptování.java
šablona/makra/skriptování.xsl
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vstup/makra/hg-verze.xsl	Thu Jul 05 19:10:42 2012 +0200
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="2.0"
+	xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://www.w3.org/1999/xhtml"
+	xmlns:s="https://trac.frantovo.cz/xml-web-generator/wiki/xmlns/strana"
+	xmlns:k="https://trac.frantovo.cz/xml-web-generator/wiki/xmlns/konfigurace"
+	xmlns:m="https://trac.frantovo.cz/xml-web-generator/wiki/xmlns/makro"
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+	xmlns:fn="http://www.w3.org/2005/xpath-functions"
+	xmlns:svg="http://www.w3.org/2000/svg"
+	xmlns:xs="http://www.w3.org/2001/XMLSchema"
+	exclude-result-prefixes="fn h s k m xs">
+
+	<!-- Vypíše verze z verzovacího systému: -->
+	<xsl:template match="m:hg-verze">
+		<xsl:variable name="zadáníSkriptu">
+			<!--
+				TODO:
+				Zahazovat XML deklaraci bychom mohli v Javě u všech skriptů… 
+				Nicméně současná dohoda je taková, že skripty vracejí fragment, ne celý dokument,
+				což má výhodu v tom, že můžou vrátit kus textu a nějakou tu značku
+				a nemusí to být zabalené v kořenovém elementu.
+				
+			-->
+			<m:skript jazyk="bash" výstup="xml">hg log --style xml | awk '{if(NR&gt;1)print}';</m:skript>
+			<!--
+				Také bychom filtrování mohli provés ve skriptu…
+				hg log … | xpath -e "//logentry[tag[starts-with(., 'v')]]" 2>/dev/null
+				 …ale to by bylo trochu zbytečně pracné.
+			-->
+		</xsl:variable>
+
+		<xsl:variable name="výstupSkriptu">
+			<xsl:apply-templates select="$zadáníSkriptu/*"/>
+		</xsl:variable>
+
+		<table>
+			<thead>
+				<tr>
+					<td>Číslo verze</td>
+					<td>Datum vydání</td>
+				</tr>
+			</thead>
+			<tbody style="text-align: right;">
+				<!--
+					Výstup skriptu se bude nacházet v XHTML jmenném prostoru, což je obvykle v pořádku,
+					ale pro mezivýsledky to není úplně vhodné.
+				-->
+				<xsl:for-each select="$výstupSkriptu/h:log/h:logentry[h:tag[starts-with(text(), 'v')]]">
+					<tr>
+						<td><xsl:value-of select="substring(h:tag/text(), 2)"/></td>
+						<td><xsl:value-of select="format-dateTime(h:date, '[D]. [M]. [Y0001]')"/></td>
+					</tr>
+				</xsl:for-each>
+			</tbody>
+		</table>
+	</xsl:template>
+
+</xsl:stylesheet>
+
--- a/vstup/skriptování.xml	Thu Jul 05 14:27:01 2012 +0200
+++ b/vstup/skriptování.xml	Thu Jul 05 19:10:42 2012 +0200
@@ -22,9 +22,11 @@
 
 		<p>
 			Díky skriptování můžeme stránky obohatit o prakticky libovolný obsah – 
-			jak prostý text, tak i XHTML fragmenty.<m:podČarou>zapíná se pomocí atributu 
-			<code>výstup="xml"</code> a generátor pak kontroluje správné formátování – 
-			nestane se vám, že byste omylem vygenerovali stránky s překříženými nebo neuzavřenými značkami.</m:podČarou>
+			jak prostý text, tak i XHTML fragmenty.<m:podČarou>
+				Zapíná se pomocí atributu <code>výstup="xml"</code> a generátor pak kontroluje správné formátování – 
+				nestane se vám, že byste omylem vygenerovali stránky s překříženými nebo neuzavřenými značkami.
+				Výchozím jmenným prostorem je XHTML a je dostupný i jmenný prostor pro makra (<code>m</code>).
+			</m:podČarou>
 		</p>
 		<p>
 			Skriptování ale může být nebezpečné, pokud byste spustili generátor na stránkách,
@@ -62,7 +64,7 @@
 			</tbody>
 		</table>
 
-		<h2>Perl</h2>
+		<h2>Perl – ukázka</h2>
 		<p>Jazyky použité nebo citované na této stránce:</p>
 		<!--
 			Lepšího výsledku bychom samozřejmě dosáhli pomocí XPath dotazu,
@@ -86,7 +88,7 @@
 }
 			]]></m:skript></pre>
 
-			<h2>BASH</h2>
+			<h2>BASH – ukázka</h2>
 		<pre><m:skript jazyk="bash"><![CDATA[
 echo -n "Právě je: ";
 date;
@@ -143,6 +145,90 @@
 echo "Perex:  $XWG_STRANKA_PEREX";
 		]]></m:skript></pre>
 
+		<h2>Makra ve skriptech</h2>
+		<p>
+			XML generované skriptem může také obsahovat makra, která se následně interptetují.
+			<m:skript jazyk="bash" výstup="xml"><![CDATA[
+echo '<m:skript jazyk="bash">'; # Ty zrůdo! :-)
+echo 'echo "Takže můžeš skriptovat, když skriptuješ,";';
+echo '</m:skript>';
+			]]></m:skript>
+			nebo dělat něco užitečnějšího.
+		</p>
+		
+		<m:skript jazyk="perl" výstup="xml"><![CDATA[
+use strict;
+use warnings;
+
+my $adresar = "vstup/makra";
+
+print "<m:diagram nadpis='Uživatelská makra v adresáři $adresar'>\n";
+print "	node		[shape=\"box\"];\n";
+print "	koren	[label=\"Uživatelská makra\"];\n";
+
+opendir(DIR, $adresar) or die $!;
+my $i = 0;
+while (readdir(DIR)) {
+	next if (/^\./);
+	# Měli bychom ošetřit zvláštní znaky v názvech souborů,
+	# abychom nezpůsobili chybu GraphVizu.
+	print "n$i	[label=\"$_\"];\n";
+	print "koren -> n$i;\n";
+	$i++;
+}
+print "</m:diagram>";
+closedir(DIR);
+		]]></m:skript>
+		
+		<p>…třeba vygenerovat tento diagram následujícím perlovským skriptem:</p>
+		
+		<m:pre jazyk="perl"><![CDATA[
+use strict;
+use warnings;
+
+my $adresar = "vstup/makra";
+
+print "<m:diagram nadpis='Uživatelská makra v adresáři $adresar'>\n";
+print "	node	[shape=\"box\"];\n";
+print "	koren	[label=\"Uživatelská makra\"];\n";
+
+opendir(DIR, $adresar) or die $!;
+my $i = 0;
+while (readdir(DIR)) {
+	next if (/^\./);
+	# Měli bychom ošetřit zvláštní znaky v názvech souborů,
+	# abychom nezpůsobili chybu GraphVizu.
+	print "n$i	[label=\"$_\"];\n";
+	print "koren -> n$i;\n";
+	$i++;
+}
+print "</m:diagram>";
+closedir(DIR);]]></m:pre>
+
+		<p>
+			Který vložíme zabalený v <code><![CDATA[<m:skript jazyk="perl" výstup="xml"> … </m:skript>]]></code> do stránky.
+		</p>
+		<p>
+			Známá chyba: ve skriptech zatím nefungují poznámky pod čarou (a není jisté, jestli kdy fungovat budou – pravděpodobně by to vyžadovalo vícefázové zpracování).
+		</p>
+
+		<h2>Skripty v makrech</h2>
+		<p>
+			Uvnitř maker můžeme volat<m:podČarou>
+				Ovšem trochu jiným způsobem, než ve stránkách –
+				nacházíme se totiž v <em>programu</em> (XSL šablona definující makro)
+				nikoli v <em>datovém souboru</em> (XML stránka).
+			</m:podČarou>
+			jiná makra – mj. skripty.
+			Toho jsme využili v makru, které generuje tabulku verzí z mercurialu:
+		</p>
+
+		<m:hg-verze/>
+
+		<p>
+			Toto makro naleznete v souboru <code>vstup/makra/hg-verze.xsl</code>.
+		</p>
+
 	</text>
 
 </stránka>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/šablona/funkce/src/cz/frantovo/xmlWebGenerator/Xmlns.java	Thu Jul 05 19:10:42 2012 +0200
@@ -0,0 +1,15 @@
+package cz.frantovo.xmlWebGenerator;
+
+/**
+ * XML jmenné prostory používané v generátoru
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class Xmlns {
+
+	public static final String XHTML = "http://www.w3.org/1999/xhtml";
+	public static final String MAKRO = "https://trac.frantovo.cz/xml-web-generator/wiki/xmlns/makro";
+
+	private Xmlns() {
+	}
+}
--- a/šablona/funkce/src/cz/frantovo/xmlWebGenerator/makra/Skriptování.java	Thu Jul 05 14:27:01 2012 +0200
+++ b/šablona/funkce/src/cz/frantovo/xmlWebGenerator/makra/Skriptování.java	Thu Jul 05 19:10:42 2012 +0200
@@ -18,6 +18,7 @@
 package cz.frantovo.xmlWebGenerator.makra;
 
 import static cz.frantovo.xmlWebGenerator.NástrojeCLI.načtiProud;
+import static cz.frantovo.xmlWebGenerator.Xmlns.*;
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.PrintStream;
@@ -27,7 +28,10 @@
 import java.util.Map;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMSource;
 import org.w3c.dom.Document;
+import org.w3c.dom.Node;
 
 /**
  * Provedeme skript a do stránky vložíme jeho výstup.
@@ -62,9 +66,15 @@
 	 * @param uriStránky URI aktuálně generované stránky → proměnná prostředí
 	 * @param nadpisStránky nadpis stránky → proměnná prostředí
 	 * @param perexStránky perex stránky → proměnná prostředí
-	 * @return výstup příkazu
+	 * @return výstup příkazu buď jako textový řetězec nebo jako XML (DOMSource)
 	 */
-	public static String interpretuj(String skriptText, String skriptSoubor, String jazyk, String výstupníFormát, String uriStránky, String nadpisStránky, String perexStránky) {
+	public static Source interpretuj(String skriptText, String skriptSoubor, String jazyk, String výstupníFormát, String uriStránky, String nadpisStránky, String perexStránky) throws Exception {
+		String výstupSkriptu = získejVýstupSkriptu(skriptText, skriptSoubor, jazyk, uriStránky, nadpisStránky, perexStránky);
+		return vyrobXml(výstupSkriptu, "xml".equals(výstupníFormát));
+	}
+
+	private static String získejVýstupSkriptu(String skriptText, String skriptSoubor, String jazyk, String uriStránky, String nadpisStránky, String perexStránky) throws Exception {
+
 		try {
 			if (isNeprázdný(skriptSoubor)) {
 				System.err.println("\tInterpretuji skript ze souboru: " + skriptSoubor);
@@ -136,7 +146,7 @@
 					System.err.println("Nicméně skript skončil úspěšně, takže pokračujeme dál.");
 				}
 
-				return připravVýstup(výsledek, výstupníFormát);
+				return výsledek.trim();
 			} else {
 				System.err.println("--- Standardní výstup skriptu: -----");
 				System.err.println(výsledek);
@@ -146,26 +156,12 @@
 				throw new Exception("Návratová hodnota: " + p.exitValue());
 			}
 		} catch (Exception e) {
-			System.err.println("Došlo k chybě při vykonávání skriptu v jazyce: " + jazyk);
+			System.err.println("Došlo k chybě při vykonávání skriptu.");
 			System.err.println("--------");
 			System.err.println(skriptText);
 			System.err.println("--------");
 			e.printStackTrace(System.err);
-			return null;
-		}
-	}
-
-	private static String připravVýstup(String výsledek, String formát) {
-		if ("xml".equals(formát)) {
-			if (zkontrolujXml(výsledek)) {
-				return výsledek.trim();
-			} else {
-				System.err.println("Chyba v XML generovaném skriptem:");
-				System.err.println(výsledek);
-				return null;
-			}
-		} else {
-			return výsledek.trim();
+			throw e;
 		}
 	}
 
@@ -174,19 +170,34 @@
 	}
 
 	/**
-	 * @param xml fragment XML vygenerovaný skriptem
-	 * @return true v případě, že výstup je validním fragmentem XML
+	 * @param zadání výstup vygenerovaný skriptem
+	 * @param xmlFormát formát zadání: true = xml fragment | false = prostý text
+	 * @return xml fragment nebo prostý text zabalený do html/body
+	 * @throws Exception
 	 */
-	private static boolean zkontrolujXml(String xml) {
-		try {
-			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
-			DocumentBuilder db = dbf.newDocumentBuilder();
-			xml = "<xml>" + xml + "</xml>";
-			Document d = db.parse(new ByteArrayInputStream(xml.getBytes()));
-			return true;
-		} catch (Exception e) {
-			e.printStackTrace(System.err);
-			return false;
+	private static Source vyrobXml(String zadání, boolean xmlFormát) throws Exception {
+		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+		DocumentBuilder db = dbf.newDocumentBuilder();
+		Document d;
+
+		if (xmlFormát) {
+			try {
+				zadání = "<html xmlns='" + XHTML + "' xmlns:m='" + MAKRO + "'><body>" + zadání + "</body></html>";
+				d = db.parse(new ByteArrayInputStream(zadání.getBytes()));
+			} catch (Exception e) {
+				System.err.println("Chyba: Skript vrátil neplatné XML.");
+				throw e;
+			}
+		} else {
+			d = db.newDocument();
+			Node html = d.createElementNS(XHTML, "html");
+			Node body = d.createElementNS(XHTML, "body");
+			Node text = d.createTextNode(zadání);
+			body.appendChild(text);
+			html.appendChild(body);
+			d.appendChild(html);
 		}
+
+		return new DOMSource(d);
 	}
 }
--- a/šablona/makra/skriptování.xsl	Thu Jul 05 14:27:01 2012 +0200
+++ b/šablona/makra/skriptování.xsl	Thu Jul 05 19:10:42 2012 +0200
@@ -18,6 +18,7 @@
 -->
 <xsl:stylesheet version="2.0"
 	xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://www.w3.org/1999/xhtml"
 	xmlns:m="https://trac.frantovo.cz/xml-web-generator/wiki/xmlns/makro"
 	xmlns:j="java:cz.frantovo.xmlWebGenerator.makra.Skriptování"
 	xmlns:k="https://trac.frantovo.cz/xml-web-generator/wiki/xmlns/konfigurace"
@@ -49,17 +50,7 @@
 																	//s:stránka/s:nadpis/text(),
 																	//s:stránka/s:perex/text()
 																)"/>
-				<xsl:choose>
-					<xsl:when test="$výstupSkriptu">
-						<xsl:choose>
-							<xsl:when test="@výstup = 'xml'"><xsl:value-of select="$výstupSkriptu" disable-output-escaping="yes"/></xsl:when>
-							<xsl:otherwise><xsl:value-of select="$výstupSkriptu"/></xsl:otherwise>
-						</xsl:choose>
-					</xsl:when>
-					<xsl:otherwise>
-						<xsl:message terminate="yes">Při interpretaci skriptu došlo k chybě.</xsl:message>
-					</xsl:otherwise>
-				</xsl:choose>
+				<xsl:apply-templates select="$výstupSkriptu/h:html/h:body/node()"/>
 			</xsl:when>
 
 			<xsl:when test="$režim = 'zakázat'">