User:Jiangxin/Patch load mm file with mmx file: Difference between revisions
(initial) |
(remove spam.) |
||
(3 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
== | ==Why this patch?== | ||
. | All my .mm files are version controled using CVS/SVN. What FreeMind has scratched my personal itch: | ||
*Some attributes of FreeMind's .mm file are not suitable for version control. | |||
*e.g., 'FOLDED' attribute saved in .mm file, makes documents changed frequently and unnecessarily. | |||
How the file is generated? Please see this link: [[User:Jiangxin/Patch_save_extra_attributes_outof_mmfile]]. | ==How .mmx is saved.== | ||
How the .mmx file is generated? Please see this link: | |||
* [[User:Jiangxin/Patch_save_extra_attributes_outof_mmfile]]. | |||
==Overview== | ==Overview== | ||
When load a mindmap file(*.mm), will check whether a .*.mmx file exist at the same directory. It it exist, | |||
a xslt will help to join the two files into one big XML. | |||
'''Java expert/FreeMind Funs, can you help me to make it usable.''' Thank you. | When I write the patch at 2005, I even don't know the basic knowledge of optimization of a XSLT. | ||
*<s>Warning: this patch is immature, and it can cause trouble if your FreeMind .mm file is as large as 200KB!</s> | |||
*<s>'''Java expert/FreeMind Funs, can you help me to make it usable.'''</s> Thank you. | |||
Last week when I feel it's time to upgrade to freemind 0.9, I overview and rewrite the xslt and the patch. | |||
-- [[User:Jiangxin|Jiangxin]] 02:26, 15 Mar 2007 (PDT) | |||
==the XSLT== | |||
My implementation is using a XSLT file to join the .mmx file with .mm file in runtime. The XSLT is below: | |||
file : ''freemind/modes/mindmapmode/freemind_join_mm_mmx.xslt'' | |||
=== low performance version === | |||
<s> | |||
<pre><nowiki> | |||
... | |||
<xsl:copy> | <xsl:copy> | ||
<xsl:choose> | <xsl:choose> | ||
<xsl:when test="$ | <xsl:when test="$mmx_nodes//node[@ID=current()/@ID]"> | ||
<xsl:for-each select="@*"> | <xsl:for-each select="@*"> | ||
<xsl:choose> | <xsl:choose> | ||
Line 42: | Line 40: | ||
</xsl:choose> | </xsl:choose> | ||
</xsl:for-each> | </xsl:for-each> | ||
<xsl:copy-of | <xsl:copy-of select="$mmx_nodes//node[@ID=current()/@ID]/@*" /> | ||
</xsl:when> | </xsl:when> | ||
<xsl:otherwise> | <xsl:otherwise> | ||
Line 51: | Line 48: | ||
<xsl:apply-templates /> | <xsl:apply-templates /> | ||
</xsl:copy> | </xsl:copy> | ||
... | |||
</nowiki></pre> | |||
</s> | |||
=== optimized version using key === | |||
<pre><nowiki> | |||
<xsl:stylesheet version="1.0" | |||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | |||
<!-- Usage: | |||
xsltproc -stringparam mmx_file mindmap.mmx <this_xslt> mindmap.mm | |||
--> | |||
<xsl:output method="xml" version="1.0" encoding="utf-8" | |||
indent="no" /> | |||
<xsl:param name="mmx_file" /> | |||
<xsl:variable name="indexfile" select="document($mmx_file)" /> | |||
<xsl:key name="node-by-id" match="node" use="@ID"/> | |||
<xsl:template match="map"> | |||
<map> | |||
<xsl:copy-of select="@*" /> | |||
<xsl:apply-templates /> | |||
</map> | |||
</xsl:template> | |||
<xsl:template match="node"> | |||
<xsl:variable name="id" select="@ID" /> | |||
<xsl:copy> | |||
<xsl:copy-of select="@*" /> | |||
<xsl:for-each select="$indexfile"> | |||
<xsl:copy-of select="key('node-by-id', $id)/@*" /> | |||
</xsl:for-each> | |||
<xsl:apply-templates /> | |||
</xsl:copy> | |||
</xsl:template> | |||
<xsl:template match="*"> | |||
<xsl:copy-of select="."/> | |||
</xsl:template> | |||
</xsl:stylesheet> | </xsl:stylesheet> | ||
</nowiki></pre> | </nowiki></pre> | ||
=== | === test the xslt === | ||
*xslt | |||
<pre | <pre> | ||
$ | $ xsltproc --stringparam mmx_file subject-forum.mmx freemind_join_mm_mmx.xslt subject-forum.mm > jx.mm | ||
</pre> | |||
*saxon | |||
<pre> | |||
$ java -cp /usr/share/java/saxon.jar com.icl.saxon.StyleSheet test.mm freemind_join_mm_mmx.xslt mmx_file=test.mmx | |||
</pre> | |||
*xalan | |||
<pre> | |||
$ java -classpath /usr/share/java/xalan2.jar org.apache.xalan.xslt.Process -IN test.mm -XSL freemind_join_mm_mmx.xslt -PARAM mmx_file test.mmx | |||
</pre> | |||
=== | === Using saxon xslt engine === | ||
The built-in xslt engine does not work with this xslt. I choose saxon xslt engine. | |||
<pre><nowiki> | |||
System.setProperty("javax.xml.transform.TransformerFactory", | |||
"com.icl.saxon.TransformerFactoryImpl"); | |||
</nowiki></pre> | |||
== | == Patch == | ||
<pre><nowiki> | <pre><nowiki> | ||
Index: freemind/ | Index: freemind/main/Tools.java | ||
=================================================================== | =================================================================== | ||
--- freemind/freemind/ | --- a/freemind/freemind/main/Tools.java | ||
+++ freemind/freemind/ | +++ b/freemind/freemind/main/Tools.java | ||
import javax.xml.transform.stream.StreamResult; | |||
import javax.xml.transform.stream.StreamSource; | |||
+//OSSXP.COM: classes for .mm and .mmx join. | |||
+import javax.xml.parsers.DocumentBuilder; | |||
+import javax.xml.parsers.DocumentBuilderFactory; | |||
+import javax.xml.transform.dom.DOMSource; | |||
+import org.w3c.dom.Document; | |||
+ | |||
+ | |||
+ | + | ||
+ | public class Tools { | ||
//public static final Set executableExtensions = new HashSet ({ "exe", | |||
- | @@ -845,7 +851,8 @@ | ||
+ | } catch(Exception ex) { | ||
freemind.main.Resources.getInstance().logException(ex); | |||
// exception: we take the file itself: | |||
- return getActualReader(file); | |||
@@ - | + // OSSXP.COM | ||
+ return getActualReader(file, frame); | |||
} finally { | |||
if(inputStream!= null) { | |||
inputStream.close(); | |||
@@ -857,10 +864,112 @@ | |||
return new StringReader(writer.getBuffer().toString()); | |||
} | |||
- /** Creates a default reader that just reads the given file. | |||
- * @throws FileNotFoundException | |||
- | + /* | ||
+ | + * OSSXP.COM: hacked FreeMind saved two seperate files, .mm and .mmx file. | ||
+ | + * Join them in runtime using XSLT TransformerFactory. | ||
+ * TODO: Improvement needed. the joining stage may very slow, so disable it. | |||
*/ | |||
- public static Reader getActualReader(File file) throws FileNotFoundException { | |||
+ public static Reader getActualReader(File file, FreeMindMain frame) throws IOException { | |||
+ // load .mmx file... | |||
+ String ext = Tools.getExtension(file.getName()); | |||
+ String mmxFileName = ""; | |||
+ | |||
+ | |||
- | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | + | ||
+ | + // OSSXP.COM: can disable join .mm with .mmx here. | ||
+ | + // if(true) return getActualReaderXml(file); | ||
+ | + | ||
+ | + if(!ext.equals("mm")) | ||
+ | + { | ||
+ mmxFileName = "." + file.getName()+".mmx"; | |||
+ } | |||
+ else | |||
+ { | |||
+ mmxFileName = "." + Tools.removeExtension(file.getName()) + ".mmx"; | |||
+ } | |||
+ File mmxfile = new File(file.getParent(), mmxFileName); | |||
+ | |||
+ if (!mmxfile.exists()) | |||
+ { | |||
+ return getActualReaderXml(file); | |||
+ } | |||
+ | + | ||
+ | + URL updaterUrl = null; | ||
+ | + InputStream inputStream = null; | ||
+ | + Source xsltSource = null; | ||
+ | + StringWriter buffwriter = null; | ||
+ | + Result result = null; | ||
+ | + TransformerFactory tf = null; | ||
+ Transformer transformer = null; | |||
+ String mmxFileFullName = mmxfile.toURI().toString(); | |||
+ try { | |||
+ // try to convert map with xslt: | |||
+ updaterUrl = frame.getResource( | |||
+ "freemind/modes/mindmapmode/freemind_join_mm_mmx.xslt"); | |||
+ if (updaterUrl == null) { | |||
+ throw new IllegalArgumentException( | |||
+ "freemind_join_mm_mmx.xslt not found."); | |||
+ } | |||
+ inputStream = updaterUrl.openStream(); | |||
+ xsltSource = new StreamSource(inputStream); | |||
+ // get output: | |||
+ buffwriter = new StringWriter(); | |||
+ result = new StreamResult(buffwriter); | |||
+ /* OSSXP.COM: create an instance of TransformerFactory. | |||
+ * the default xslt engine (com.sun.org.apache.xalan.internal.xsltc.trax...) | |||
+ * may not support 'key()' in freemind_join_mm_mmx.xslt. | |||
+ * Use saxon implement. */ | |||
+ System.setProperty("javax.xml.transform.TransformerFactory", | |||
+ "com.icl.saxon.TransformerFactoryImpl"); | |||
+ tf = TransformerFactory.newInstance(); | |||
+ transformer = tf.newTransformer(xsltSource); | |||
+ transformer.setParameter("mmx_file", mmxFileFullName); | |||
+ transformer.transform(new StreamSource(file), result); | |||
+ | + | ||
+ | + } catch (Exception ex) { | ||
+ ex.printStackTrace(); | |||
+ // exception: we take the file itself: | |||
+ | + return getActualReaderXml(file); | ||
+ | + } finally { | ||
+ | + if (inputStream != null) { | ||
+ | + inputStream.close(); | ||
+ } | |||
+ if (buffwriter != null) { | |||
+ buffwriter.close(); | |||
+ } | |||
+ inputStream = null; | |||
+ xsltSource = null; | |||
+ updaterUrl = null; | |||
+ result = null; | |||
+ transformer = null; | |||
+ tf = null; | |||
+ } | |||
+ return new StringReader(buffwriter.getBuffer().toString()); | |||
+ } | |||
+ | + | ||
+ | + /* | ||
+ * OSSXP.COM: In this hacked version, .mm file is a true XML file. | |||
+ | + * load XML file using DOM. | ||
+ | + */ | ||
+ | + private static Reader getActualReaderXml(File file) throws IOException { | ||
+ | + try | ||
+ | + { | ||
+ | + TransformerFactory tf = TransformerFactory.newInstance(); | ||
+ Transformer transformer = tf.newTransformer(); | |||
+ | + DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); | ||
+ DocumentBuilder domBuilder = domFactory.newDocumentBuilder(); | |||
+ | |||
+ | + | ||
+ | + Document doc = domBuilder.parse(file); | ||
+ Source src = new DOMSource(doc); | |||
+ StringWriter buffwriter = new StringWriter(); | |||
+ StreamResult result = new StreamResult(buffwriter); | |||
+ transformer.transform(src, result); | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | + | ||
+ | + return new StringReader(buffwriter.toString()); | ||
+ } | |||
+ catch(Exception exp) | |||
+ { | |||
+ | + exp.printStackTrace(); | ||
+ | + } | ||
+ | |||
+ | |||
+ | |||
+ | + | ||
return new BufferedReader(new FileReader(file)); | |||
} | |||
Index: | Index: freemind/modes/mindmapmode/MindMapMapModel.java | ||
=================================================================== | =================================================================== | ||
--- freemind/freemind/modes/mindmapmode/ | --- a/freemind/freemind/modes/mindmapmode/MindMapMapModel.java | ||
+++ freemind/freemind/modes/mindmapmode/ | +++ b/freemind/freemind/modes/mindmapmode/MindMapMapModel.java | ||
@@ - | @@ -440,7 +440,7 @@ | ||
} | |||
if (mapStart.equals(EXPECTED_START_STRINGS[i])) { | |||
// actual version: | |||
- reader = Tools.getActualReader(file); | |||
+ reader = Tools.getActualReader(file, getFrame()); | |||
break; | |||
} | |||
} | |||
Index: build.xml | |||
+ | |||
Index: | |||
=================================================================== | =================================================================== | ||
--- freemind/build.xml | --- a/freemind/build.xml | ||
+++ freemind/build.xml | +++ b/freemind/build.xml | ||
@@ - | @@ -343,6 +343,7 @@ | ||
<include name="Resources*"/> | <include name="Resources*"/> | ||
<include name="mindmap_menus.xml"/> | <include name="mindmap_menus.xml"/> |
Latest revision as of 06:02, 14 February 2009
Why this patch?
All my .mm files are version controled using CVS/SVN. What FreeMind has scratched my personal itch:
- Some attributes of FreeMind's .mm file are not suitable for version control.
- e.g., 'FOLDED' attribute saved in .mm file, makes documents changed frequently and unnecessarily.
How .mmx is saved.
How the .mmx file is generated? Please see this link:
Overview
When load a mindmap file(*.mm), will check whether a .*.mmx file exist at the same directory. It it exist, a xslt will help to join the two files into one big XML.
When I write the patch at 2005, I even don't know the basic knowledge of optimization of a XSLT.
Warning: this patch is immature, and it can cause trouble if your FreeMind .mm file is as large as 200KB!Java expert/FreeMind Funs, can you help me to make it usable.Thank you.
Last week when I feel it's time to upgrade to freemind 0.9, I overview and rewrite the xslt and the patch.
-- Jiangxin 02:26, 15 Mar 2007 (PDT)
the XSLT
My implementation is using a XSLT file to join the .mmx file with .mm file in runtime. The XSLT is below:
file : freemind/modes/mindmapmode/freemind_join_mm_mmx.xslt
low performance version
... <xsl:copy> <xsl:choose> <xsl:when test="$mmx_nodes//node[@ID=current()/@ID]"> <xsl:for-each select="@*"> <xsl:choose> <xsl:when test="local-name(.) = 'FOLDED'"> </xsl:when> <xsl:otherwise> <xsl:copy-of select="." /> </xsl:otherwise> </xsl:choose> </xsl:for-each> <xsl:copy-of select="$mmx_nodes//node[@ID=current()/@ID]/@*" /> </xsl:when> <xsl:otherwise> <xsl:copy-of select="@*" /> </xsl:otherwise> </xsl:choose> <xsl:apply-templates /> </xsl:copy> ...
optimized version using key
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- Usage: xsltproc -stringparam mmx_file mindmap.mmx <this_xslt> mindmap.mm --> <xsl:output method="xml" version="1.0" encoding="utf-8" indent="no" /> <xsl:param name="mmx_file" /> <xsl:variable name="indexfile" select="document($mmx_file)" /> <xsl:key name="node-by-id" match="node" use="@ID"/> <xsl:template match="map"> <map> <xsl:copy-of select="@*" /> <xsl:apply-templates /> </map> </xsl:template> <xsl:template match="node"> <xsl:variable name="id" select="@ID" /> <xsl:copy> <xsl:copy-of select="@*" /> <xsl:for-each select="$indexfile"> <xsl:copy-of select="key('node-by-id', $id)/@*" /> </xsl:for-each> <xsl:apply-templates /> </xsl:copy> </xsl:template> <xsl:template match="*"> <xsl:copy-of select="."/> </xsl:template> </xsl:stylesheet>
test the xslt
- xslt
$ xsltproc --stringparam mmx_file subject-forum.mmx freemind_join_mm_mmx.xslt subject-forum.mm > jx.mm
- saxon
$ java -cp /usr/share/java/saxon.jar com.icl.saxon.StyleSheet test.mm freemind_join_mm_mmx.xslt mmx_file=test.mmx
- xalan
$ java -classpath /usr/share/java/xalan2.jar org.apache.xalan.xslt.Process -IN test.mm -XSL freemind_join_mm_mmx.xslt -PARAM mmx_file test.mmx
Using saxon xslt engine
The built-in xslt engine does not work with this xslt. I choose saxon xslt engine.
System.setProperty("javax.xml.transform.TransformerFactory", "com.icl.saxon.TransformerFactoryImpl");
Patch
Index: freemind/main/Tools.java =================================================================== --- a/freemind/freemind/main/Tools.java +++ b/freemind/freemind/main/Tools.java import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +//OSSXP.COM: classes for .mm and .mmx join. +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.dom.DOMSource; +import org.w3c.dom.Document; + public class Tools { //public static final Set executableExtensions = new HashSet ({ "exe", @@ -845,7 +851,8 @@ } catch(Exception ex) { freemind.main.Resources.getInstance().logException(ex); // exception: we take the file itself: - return getActualReader(file); + // OSSXP.COM + return getActualReader(file, frame); } finally { if(inputStream!= null) { inputStream.close(); @@ -857,10 +864,112 @@ return new StringReader(writer.getBuffer().toString()); } - /** Creates a default reader that just reads the given file. - * @throws FileNotFoundException + /* + * OSSXP.COM: hacked FreeMind saved two seperate files, .mm and .mmx file. + * Join them in runtime using XSLT TransformerFactory. + * TODO: Improvement needed. the joining stage may very slow, so disable it. */ - public static Reader getActualReader(File file) throws FileNotFoundException { + public static Reader getActualReader(File file, FreeMindMain frame) throws IOException { + // load .mmx file... + String ext = Tools.getExtension(file.getName()); + String mmxFileName = ""; + + // OSSXP.COM: can disable join .mm with .mmx here. + // if(true) return getActualReaderXml(file); + + if(!ext.equals("mm")) + { + mmxFileName = "." + file.getName()+".mmx"; + } + else + { + mmxFileName = "." + Tools.removeExtension(file.getName()) + ".mmx"; + } + File mmxfile = new File(file.getParent(), mmxFileName); + + if (!mmxfile.exists()) + { + return getActualReaderXml(file); + } + + URL updaterUrl = null; + InputStream inputStream = null; + Source xsltSource = null; + StringWriter buffwriter = null; + Result result = null; + TransformerFactory tf = null; + Transformer transformer = null; + String mmxFileFullName = mmxfile.toURI().toString(); + try { + // try to convert map with xslt: + updaterUrl = frame.getResource( + "freemind/modes/mindmapmode/freemind_join_mm_mmx.xslt"); + if (updaterUrl == null) { + throw new IllegalArgumentException( + "freemind_join_mm_mmx.xslt not found."); + } + inputStream = updaterUrl.openStream(); + xsltSource = new StreamSource(inputStream); + // get output: + buffwriter = new StringWriter(); + result = new StreamResult(buffwriter); + /* OSSXP.COM: create an instance of TransformerFactory. + * the default xslt engine (com.sun.org.apache.xalan.internal.xsltc.trax...) + * may not support 'key()' in freemind_join_mm_mmx.xslt. + * Use saxon implement. */ + System.setProperty("javax.xml.transform.TransformerFactory", + "com.icl.saxon.TransformerFactoryImpl"); + tf = TransformerFactory.newInstance(); + transformer = tf.newTransformer(xsltSource); + transformer.setParameter("mmx_file", mmxFileFullName); + transformer.transform(new StreamSource(file), result); + + } catch (Exception ex) { + ex.printStackTrace(); + // exception: we take the file itself: + return getActualReaderXml(file); + } finally { + if (inputStream != null) { + inputStream.close(); + } + if (buffwriter != null) { + buffwriter.close(); + } + inputStream = null; + xsltSource = null; + updaterUrl = null; + result = null; + transformer = null; + tf = null; + } + return new StringReader(buffwriter.getBuffer().toString()); + } + + /* + * OSSXP.COM: In this hacked version, .mm file is a true XML file. + * load XML file using DOM. + */ + private static Reader getActualReaderXml(File file) throws IOException { + try + { + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer transformer = tf.newTransformer(); + DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder domBuilder = domFactory.newDocumentBuilder(); + + Document doc = domBuilder.parse(file); + Source src = new DOMSource(doc); + StringWriter buffwriter = new StringWriter(); + StreamResult result = new StreamResult(buffwriter); + transformer.transform(src, result); + + return new StringReader(buffwriter.toString()); + } + catch(Exception exp) + { + exp.printStackTrace(); + } + return new BufferedReader(new FileReader(file)); } Index: freemind/modes/mindmapmode/MindMapMapModel.java =================================================================== --- a/freemind/freemind/modes/mindmapmode/MindMapMapModel.java +++ b/freemind/freemind/modes/mindmapmode/MindMapMapModel.java @@ -440,7 +440,7 @@ } if (mapStart.equals(EXPECTED_START_STRINGS[i])) { // actual version: - reader = Tools.getActualReader(file); + reader = Tools.getActualReader(file, getFrame()); break; } } Index: build.xml =================================================================== --- a/freemind/build.xml +++ b/freemind/build.xml @@ -343,6 +343,7 @@ <include name="Resources*"/> <include name="mindmap_menus.xml"/> <include name="**/freemind_version_updater.xslt"/> + <include name="**/freemind_join_mm_mmx.xslt"/> </fileset> </jar> </target>