User:Jiangxin/Patch load mm file with mmx file

From FreeMind

(Difference between revisions)
Jump to: navigation, search
(initial)
m (fixed low performance xslt)
Line 1: Line 1:
-
==What is .mmx file?==
+
==Why this patch?==
-
.mmx file has the same file structure as FreeMind .mm file, but It only contains the unnecessary and frequently changed attributes. This file is '''NOT''' suitable for version control system.
+
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==
-
'''Warning: this patch is immature, and it can cause trouble if your FreeMind .mm file is as large as 200KB!'''
+
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.
 +
Last week when I feel it's time to upgrade to freemind 0.9, I overview and rewrite the xslt and the patch.
-
===the 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.
 +
 
 +
==the XSLT==
My implementation is using a XSLT file to join the .mmx file with .mm file in runtime. The XSLT is below:
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''
 
-
<pre><nowiki>
 
-
<xsl:stylesheet version="1.0"
 
-
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 
-
 
-
<xsl:output method="xml" version="1.0" encoding="utf-8"
 
-
indent="yes" />
 
-
 
-
<xsl:param name="mmx_file" />
 
-
 
-
<xsl:template match="map">
 
-
<map>
 
-
<xsl:copy-of select="@*" />
 
-
<xsl:apply-templates />
 
-
</map>
 
-
</xsl:template>
 
-
<xsl:template match="node">
+
file : ''freemind/modes/mindmapmode/freemind_join_mm_mmx.xslt''
-
<xsl:param name="mmx_node" select="document($mmx_file)" />
+
=== low performance version ===
 +
<s>
 +
<pre><nowiki>
 +
...
<xsl:copy>
<xsl:copy>
<xsl:choose>
<xsl:choose>
-
<xsl:when test="$mmx_node//node[@ID=current()/@ID]">
+
<xsl:when test="$mmx_nodes//node[@ID=current()/@ID]">
<xsl:for-each select="@*">
<xsl:for-each select="@*">
<xsl:choose>
<xsl:choose>
Line 42: Line 38:
</xsl:choose>
</xsl:choose>
</xsl:for-each>
</xsl:for-each>
-
<xsl:copy-of
+
<xsl:copy-of select="$mmx_nodes//node[@ID=current()/@ID]/@*" />
-
select="$mmx_node//node[@ID=current()/@ID]/@*" />
+
</xsl:when>
</xsl:when>
<xsl:otherwise>
<xsl:otherwise>
Line 51: Line 46:
<xsl:apply-templates />
<xsl:apply-templates />
</xsl:copy>
</xsl:copy>
-
</xsl:template>
+
...
 +
</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:template match="*">
-
  <xsl:copy-of select="."/>
+
    <xsl:copy-of select="."/>
-
</xsl:template>
+
  </xsl:template>
</xsl:stylesheet>
</xsl:stylesheet>
</nowiki></pre>
</nowiki></pre>
-
=== Problem 1: too slow ===
+
=== test the xslt ===
-
But is is very slow if .mm file and .mmx is as big as 200KB. I test my file using xsltproc like this.
+
*xslt
-
<pre><nowiki>
+
<pre>
-
$ time xsltproc --stringparam mmx_file subject-forum.mmx freemind_join_mm_mmx.xslt subject-forum.mm > jx.mm
+
$ xsltproc --stringparam mmx_file subject-forum.mmx freemind_join_mm_mmx.xslt subject-forum.mm > jx.mm
 +
</pre>
-
real    4m33.148s
+
*saxon
-
user    4m30.093s
+
<pre>
-
sys    0m0.015s
+
$ java -cp /usr/share/java/saxon.jar com.icl.saxon.StyleSheet test.mm freemind_join_mm_mmx.xslt  mmx_file=test.mmx
-
</nowiki></pre>
+
</pre>
-
=== Problem 2: memery killer ===
+
*xalan
-
load without .mmx file, only 40MB memery usage. But if load .mm file with .mmx file, 80MB maybe. It terrible, where is Java GC? Just a tale of sun. (Sorry, I am not familiar with Java.)
+
<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.
-
== My Immature Patch ==
 
<pre><nowiki>
<pre><nowiki>
-
Index: freemind/freemind/modes/mindmapmode/MindMapMapModel.java
+
System.setProperty("javax.xml.transform.TransformerFactory",
 +
    "com.icl.saxon.TransformerFactoryImpl");
 +
</nowiki></pre>
 +
 
 +
== Patch ==
 +
<pre><nowiki>
 +
Index: freemind/main/Tools.java
===================================================================
===================================================================
-
--- freemind/freemind/modes/mindmapmode/MindMapMapModel.java (.../tags/RELEASE-0-8-0) (revision 2)
+
--- a/freemind/freemind/main/Tools.java (修订版 2519)
-
+++ freemind/freemind/modes/mindmapmode/MindMapMapModel.java (.../branches/WHFM-0-8-0) (working copy)
+
+++ b/freemind/freemind/main/Tools.java (工作拷贝)
-
@@ -519,21 +562,37 @@
+
@@ -74,6 +74,12 @@
-
        }
+
import javax.xml.transform.stream.StreamResult;
-
        // the resulting file is accessed by the reader:
+
import javax.xml.transform.stream.StreamSource;
-
        Reader reader = null;
+
-
-        if (mapStart.equals(expectedStartString)
+
-
-                || mapStart.equals(expectedAlternativeStartString)) {
+
-
-            // actual version:
+
-
-            reader = getActualReader(file);
+
-
-        } else {
+
-
+        // join .mmx with .mm file
+
-
+        reader = getActualReader(file);
+
-
+
+
-
+        if (! mapStart.equals(expectedStartString)
+
-
+                && ! mapStart.equals(expectedAlternativeStartString)) {
+
-
            // older version:
+
-
-            reader = getUpdateReader(file);
+
-
+            reader = getUpdateReader(reader, file.getName());
+
-
        }
+
-
        try {
+
-
            mapElement.parseFromReader(reader);
+
-
@@ -573,14 +602,15 @@
+
   
   
-
    /** Creates a reader that pipes the input file through a XSLT-Script that
+
+//OSSXP.COM: classes for .mm and .mmx join.
-
      *  updates the version to the current.
+
+import javax.xml.parsers.DocumentBuilder;
-
-    * @param file
+
+import javax.xml.parsers.DocumentBuilderFactory;
-
+     * @param reader
+
+import javax.xml.transform.dom.DOMSource;
-
+    * @param filename
+
+import org.w3c.dom.Document;
-
      * @return
+
-
      * @throws IOException
+
-
      */
+
-
-    private Reader getUpdateReader(File file) throws IOException {
+
-
+    private Reader getUpdateReader(Reader reader, String filename) throws IOException {
+
-
        StringWriter writer = null;
+
-
        InputStream inputStream = null;
+
-
-        logger.info("Updating the file "+file.getName()+" to the current version.");
+
-
+        logger.info("Updating the file "+filename+" to the current version.");
+
-
        try{
+
-
            // try to convert map with xslt:
+
-
            URL updaterUrl=null;
+
-
@@ -598,12 +629,12 @@
+
-
            // create an instance of TransformerFactory
+
-
            TransformerFactory transFact = TransformerFactory.newInstance();
+
-
            Transformer trans = transFact.newTransformer(xsltSource);
+
-
-            trans.transform(new StreamSource(file), result);
+
-
-            logger.info("Updating the file "+file.getName()+" to the current version. Done.");
+
-
+           trans.transform(new StreamSource(reader), result);
+
-
+            logger.info("Updating the file "+filename+" to the current version. Done.");
+
-
        } catch(Exception ex) {
+
-
            ex.printStackTrace();
+
-
            // exception: we take the file itself:
+
-
-            return getActualReader(file);
+
-
+           return reader;
+
-
        } finally {
+
-
            if(inputStream!= null) {
+
-
                inputStream.close();
+
-
@@ -618,12 +649,99 @@
+
-
    /** Creates a default reader that just reads the given file.
+
-
      * @param file
+
-
      * @return
+
-
-    * @throws FileNotFoundException
+
-
+    * @throws IOException
+
-
      */
+
-
-    private Reader getActualReader(File file) throws FileNotFoundException {
+
-
-        return new BufferedReader(new FileReader(file));
+
-
+    private 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);
+
public class Tools {
-
+            Source src = new DOMSource(doc);
+
-
+           StringWriter buffwriter = new StringWriter();
+
    //public static final Set executableExtensions = new HashSet ({ "exe",
-
+            StreamResult result = new StreamResult(buffwriter);
+
@@ -845,7 +851,8 @@
-
+            transformer.transform(src, result);
+
    } catch(Exception ex) {
-
+
+
        freemind.main.Resources.getInstance().logException(ex);
-
+           return new StringReader(buffwriter.toString());
+
        // exception: we take the file itself:
-
+        }
+
-         return getActualReader(file);
-
+        catch(Exception exp)
+
+         // OSSXP.COM
-
+        {
+
+         return getActualReader(file, frame);
-
+        exp.printStackTrace();
+
    } finally {
-
+       }
+
        if(inputStream!= null) {
-
+
+
            inputStream.close();
-
+        return new BufferedReader(new FileReader(file));  
+
@@ -857,10 +864,112 @@
-
    }
+
    return new StringReader(writer.getBuffer().toString());
 +
}
   
   
-
+   private Reader getActualReader(File file) throws IOException {
+
- /** Creates a default reader that just reads the given file.
-
+       // load .mmx file...
+
- * @throws FileNotFoundException
-
+       String ext = Tools.getExtension(file.getName());
+
+ /*
-
+       String mmxFileName = "";
+
+ * 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 = "";
+
+
-
+       if(!ext.equals("mm"))  
+
+     // OSSXP.COM: can disable join .mm with .mmx here.
-
+       {
+
+     // if(true) return getActualReaderXml(file);
-
+       mmxFileName = file.getName()+".mmx";
+
+    
-
+       }
+
+     if(!ext.equals("mm"))  
-
+       else  
+
+     {
-
+       {
+
+     mmxFileName = "." + file.getName()+".mmx";
-
+       mmxFileName = Tools.removeExtension(file.getName()) + ".mmx";
+
+     }
-
+       }
+
+     else  
-
+       File mmxfile = new File(file.getParent(), mmxFileName);
+
+     {
-
+      
+
+     mmxFileName = "." + Tools.removeExtension(file.getName()) + ".mmx";
-
+       if (!mmxfile.exists())
+
+     }
-
+       {
+
+     File mmxfile = new File(file.getParent(), mmxFileName);
-
+       return getActualReaderXml(file);
+
+    
-
+       }
+
+     if (!mmxfile.exists())
 +
+     {
 +
+     return getActualReaderXml(file);
 +
+     }
+
+
-
+       URL updaterUrl = null;
+
+     URL updaterUrl = null;
-
+       InputStream inputStream = null;
+
+     InputStream inputStream = null;
-
+       Source xsltSource = null;
+
+     Source xsltSource = null;
-
+       StringWriter buffwriter = null;
+
+     StringWriter buffwriter = null;
-
+       Result result = null;
+
+     Result result = null;
-
+       TransformerFactory tf = null;
+
+     TransformerFactory tf = null;
-
+       Transformer transformer = null;
+
+     Transformer transformer = null;
-
+       String mmxFileFullName = file.getParent() + "/" + mmxFileName;
+
+     String mmxFileFullName = mmxfile.toURI().toString();
-
+       try {
+
+     try {
-
+           // try to convert map with xslt:
+
+         // try to convert map with xslt:
-
+           updaterUrl = getFrame().getResource(
+
+     updaterUrl = frame.getResource(
-
+                   "freemind/modes/mindmapmode/freemind_join_mm_mmx.xslt");
+
+                 "freemind/modes/mindmapmode/freemind_join_mm_mmx.xslt");
-
+           if (updaterUrl == null) {
+
+         if (updaterUrl == null) {
-
+               throw new IllegalArgumentException(
+
+             throw new IllegalArgumentException(
-
+                       "freemind_join_mm_mmx.xslt not found.");
+
+                     "freemind_join_mm_mmx.xslt not found.");
-
+           }
+
+         }
-
+           inputStream = updaterUrl.openStream();
+
+         inputStream = updaterUrl.openStream();
-
+           xsltSource = new StreamSource(inputStream);
+
+         xsltSource = new StreamSource(inputStream);
-
+           // get output:
+
+         // get output:
-
+           buffwriter = new StringWriter();
+
+         buffwriter = new StringWriter();
-
+           result = new StreamResult(buffwriter);
+
+         result = new StreamResult(buffwriter);
-
+           // create an instance of TransformerFactory
+
+         /* OSSXP.COM: create an instance of TransformerFactory.
-
+           tf = TransformerFactory.newInstance();
+
+         * the default xslt engine (com.sun.org.apache.xalan.internal.xsltc.trax...)
-
+           transformer = tf.newTransformer(xsltSource);
+
+         * may not support 'key()' in freemind_join_mm_mmx.xslt.
-
+           transformer.setParameter("mmx_file", mmxFileFullName);
+
+         * Use saxon implement. */
-
+           transformer.transform(new StreamSource(file), result);
+
+         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) {
+
+     } catch (Exception ex) {
-
+           ex.printStackTrace();
+
+         ex.printStackTrace();
-
+           // exception: we take the file itself:
+
+         // exception: we take the file itself:
-
+           return getActualReaderXml(file);
+
+         return getActualReaderXml(file);
-
+       } finally {
+
+     } finally {
-
+           if (inputStream != null) {
+
+         if (inputStream != null) {
-
+               inputStream.close();
+
+             inputStream.close();
-
+           }
+
+         }
-
+           if (buffwriter != null) {
+
+         if (buffwriter != null) {
-
+               buffwriter.close();
+
+             buffwriter.close();
-
+           }
+
+         }
-
+           inputStream = null;
+
+         inputStream = null;
-
+           xsltSource = null;
+
+         xsltSource = null;
-
+           updaterUrl = null;
+
+         updaterUrl = null;
-
+           result = null;
+
+         result = null;
-
+           transformer = null;
+
+         transformer = null;
-
+           tf = null;
+
+         tf = null;
-
+       }
+
+     }
-
+       return new StringReader(buffwriter.getBuffer().toString());   }
+
+     return new StringReader(buffwriter.getBuffer().toString());
 +
+ }
+
+
-
    //
+
+ /*
-
    // cut'n'paste
+
+ * OSSXP.COM: In this hacked version, .mm file is a true XML file.
-
    //
+
+ * load XML file using DOM.
-
Index: freemind/freemind/modes/mindmapmode/freemind_join_mm_mmx.xslt
+
+ */
-
===================================================================
+
+ private static Reader getActualReaderXml(File file) throws IOException {
-
--- freemind/freemind/modes/mindmapmode/freemind_join_mm_mmx.xslt (.../tags/RELEASE-0-8-0) (revision 0)
+
+     try
-
+++ freemind/freemind/modes/mindmapmode/freemind_join_mm_mmx.xslt (.../branches/WHFM-0-8-0) (revision 28)
+
+     {
-
@@ -0,0 +1,45 @@
+
+         TransformerFactory tf = TransformerFactory.newInstance();
-
+<xsl:stylesheet version="1.0"
+
+         Transformer transformer = tf.newTransformer();
-
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+         DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
 +
+         DocumentBuilder domBuilder = domFactory.newDocumentBuilder();
+
+
-
+ <xsl:output method="xml" version="1.0" encoding="utf-8"
+
+         Document doc = domBuilder.parse(file);
-
+ indent="yes" />
+
+         Source src = new DOMSource(doc);
 +
+         StringWriter buffwriter = new StringWriter();
 +
+         StreamResult result = new StreamResult(buffwriter);
 +
+         transformer.transform(src, result);
+
+
-
+ <xsl:param name="mmx_file" />
+
+         return new StringReader(buffwriter.toString());
 +
+     }
 +
+     catch(Exception exp)
 +
+     {
 +
+     exp.printStackTrace();
 +
+     }
+
+
-
+ <xsl:template match="map">
+
    return new BufferedReader(new FileReader(file));
-
+ <map>
+
}
-
+ <xsl:copy-of select="@*" />
+
-
+ <xsl:apply-templates />
+
Index: freemind/modes/mindmapmode/MindMapMapModel.java
-
+ </map>
+
===================================================================
-
+ </xsl:template>
+
--- a/freemind/freemind/modes/mindmapmode/MindMapMapModel.java (修订版 2519)
-
+
+
+++ b/freemind/freemind/modes/mindmapmode/MindMapMapModel.java (工作拷贝)
-
+ <xsl:template match="node">
+
@@ -440,7 +440,7 @@
-
+ <xsl:param name="mmx_node" select="document($mmx_file)" />
+
            }
-
+ <xsl:copy>
+
            if (mapStart.equals(EXPECTED_START_STRINGS[i])) {
-
+ <xsl:choose>
+
                // actual version:
-
+ <xsl:when test="$mmx_node//node[@ID=current()/@ID]">
+
-               reader = Tools.getActualReader(file);
-
+ <xsl:for-each select="@*">
+
+               reader = Tools.getActualReader(file, getFrame());
-
+ <xsl:choose>
+
                break;
-
+ <xsl:when test="local-name(.) = 'FOLDED'">
+
            }
-
+ </xsl:when>
+
        }
-
+ <xsl:otherwise>
+
Index: build.xml
-
+ <xsl:copy-of select="." />
+
-
+ </xsl:otherwise>
+
-
+ </xsl:choose>
+
-
+ </xsl:for-each>
+
-
+ <xsl:copy-of
+
-
+ select="$mmx_node//node[@ID=current()/@ID]/@*" />
+
-
+ </xsl:when>
+
-
+ <xsl:otherwise>
+
-
+ <xsl:copy-of select="@*" />
+
-
+ </xsl:otherwise>
+
-
+ </xsl:choose>
+
-
+ <xsl:apply-templates />
+
-
+ </xsl:copy>
+
-
+ </xsl:template>
+
-
+
+
-
+ <xsl:template match="*">
+
-
+   <xsl:copy-of select="."/>
+
-
+ </xsl:template>
+
-
+
+
-
+</xsl:stylesheet>
+
-
Index: freemind/build.xml
+
===================================================================
===================================================================
-
--- freemind/build.xml (.../tags/RELEASE-0-8-0) (revision 2)
+
--- a/freemind/build.xml (修订版 2519)
-
+++ freemind/build.xml (.../branches/WHFM-0-8-0) (working copy)
+
+++ b/freemind/build.xml (工作拷贝)
-
@@ -284,6 +284,7 @@
+
@@ -343,6 +343,7 @@
  <include name="Resources*"/>
  <include name="Resources*"/>
  <include name="mindmap_menus.xml"/>
  <include name="mindmap_menus.xml"/>

Revision as of 09:18, 15 March 2007

Contents

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. Last week when I feel it's time to upgrade to freemind 0.9, I overview and rewrite the xslt and the patch.

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.

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	(修订版 2519)
+++ b/freemind/freemind/main/Tools.java	(工作拷贝)
@@ -74,6 +74,12 @@
 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	(修订版 2519)
+++ 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	(修订版 2519)
+++ 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>
Personal tools