User:Jiangxin/Patch load mm file with mmx file: Difference between revisions

From FreeMind
Jump to navigationJump to search
(initial)
 
(remove spam.)
 
(3 intermediate revisions by 2 users not shown)
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.
*<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===
Last week when I feel it's time to upgrade to freemind 0.9, I overview and rewrite the xslt and the patch.
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"
-- [[User:Jiangxin|Jiangxin]] 02:26, 15 Mar 2007 (PDT)
indent="yes" />


<xsl:param name="mmx_file" />
==the XSLT==
My implementation is using a XSLT file to join the .mmx file with .mm file in runtime. The XSLT is below:


<xsl:template match="map">
file : ''freemind/modes/mindmapmode/freemind_join_mm_mmx.xslt''
<map>
=== low performance version ===
<xsl:copy-of select="@*" />
<s>
<xsl:apply-templates />
<pre><nowiki>
</map>
...
</xsl:template>
 
<xsl:template match="node">
<xsl:param name="mmx_node" select="document($mmx_file)" />
<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 40:
</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 48:
<xsl:apply-templates />
<xsl:apply-templates />
</xsl:copy>
</xsl:copy>
</xsl:template>
...
</nowiki></pre>
</s>


<xsl:template match="*">
=== optimized version using key ===
  <xsl:copy-of select="."/>
<pre><nowiki>
</xsl:template>
<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>


=== 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>
 
*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>


real    4m33.148s
*xalan
user    4m30.093s
<pre>
sys    0m0.015s
$ 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
</nowiki></pre>
</pre>


=== Problem 2: memery killer ===
=== Using saxon xslt engine ===
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.)
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>


== My Immature Patch ==
== Patch ==
<pre><nowiki>
<pre><nowiki>
Index: freemind/freemind/modes/mindmapmode/MindMapMapModel.java
Index: freemind/main/Tools.java
===================================================================
===================================================================
--- freemind/freemind/modes/mindmapmode/MindMapMapModel.java (.../tags/RELEASE-0-8-0) (revision 2)
--- a/freemind/freemind/main/Tools.java  
+++ freemind/freemind/modes/mindmapmode/MindMapMapModel.java (.../branches/WHFM-0-8-0) (working copy)
+++ b/freemind/freemind/main/Tools.java  
@@ -519,21 +562,37 @@
import javax.xml.transform.stream.StreamResult;
        }
import javax.xml.transform.stream.StreamSource;
        // the resulting file is accessed by the reader:
        Reader reader = null;
+//OSSXP.COM: classes for .mm and .mmx join.
-        if (mapStart.equals(expectedStartString)
+import javax.xml.parsers.DocumentBuilder;
-                || mapStart.equals(expectedAlternativeStartString)) {
+import javax.xml.parsers.DocumentBuilderFactory;
-            // actual version:
+import javax.xml.transform.dom.DOMSource;
-            reader = getActualReader(file);
+import org.w3c.dom.Document;
-        } else {
+       // join .mmx with .mm file
+       reader = getActualReader(file);
+
+
+       if (! mapStart.equals(expectedStartString)
public class Tools {
+                && ! mapStart.equals(expectedAlternativeStartString)) {
            // older version:
    //public static final Set executableExtensions = new HashSet ({ "exe",
-           reader = getUpdateReader(file);
@@ -845,7 +851,8 @@
+           reader = getUpdateReader(reader, file.getName());
    } catch(Exception ex) {
        }
        freemind.main.Resources.getInstance().logException(ex);
        try {
        // exception: we take the file itself:
            mapElement.parseFromReader(reader);
-         return getActualReader(file);
@@ -573,14 +602,15 @@
+         // OSSXP.COM
+         return getActualReader(file, frame);
    } finally {
        if(inputStream!= null) {
            inputStream.close();
@@ -857,10 +864,112 @@
    return new StringReader(writer.getBuffer().toString());
}
   
   
    /** Creates a reader that pipes the input file through a XSLT-Script that
- /** Creates a default reader that just reads the given file.
      *  updates the version to the current.
- * @throws FileNotFoundException
-     * @param file
+ /*
+     * @param reader
+ * OSSXP.COM: hacked FreeMind saved two seperate files, .mm and .mmx file.
+     * @param filename
+ * Join them in runtime using XSLT TransformerFactory.
      * @return
+ * TODO: Improvement needed. the joining stage may very slow, so disable it.
      * @throws IOException
*/
      */
- public static Reader getActualReader(File file) throws FileNotFoundException {
-    private Reader getUpdateReader(File file) throws IOException {
+ public static Reader getActualReader(File file, FreeMindMain frame) throws IOException {
+    private Reader getUpdateReader(Reader reader, String filename) throws IOException {
+     // load .mmx file...
        StringWriter writer = null;
+     String ext = Tools.getExtension(file.getName());
        InputStream inputStream = null;
+     String mmxFileName = "";
-        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);
+     // OSSXP.COM: can disable join .mm with .mmx here.
+           Source src = new DOMSource(doc);
+     // if(true) return getActualReaderXml(file);
+           StringWriter buffwriter = new StringWriter();
+    
+           StreamResult result = new StreamResult(buffwriter);
+     if(!ext.equals("mm"))
+           transformer.transform(src, result);
+     {
+     mmxFileName = "." + file.getName()+".mmx";
+     }
+     else
+     {
+     mmxFileName = "." + Tools.removeExtension(file.getName()) + ".mmx";
+     }
+     File mmxfile = new File(file.getParent(), mmxFileName);
+    
+     if (!mmxfile.exists())
+     {
+     return getActualReaderXml(file);
+     }
+
+
+           return new StringReader(buffwriter.toString());
+     URL updaterUrl = null;
+       }
+     InputStream inputStream = null;
+       catch(Exception exp)
+     Source xsltSource = null;
+       {
+     StringWriter buffwriter = null;
+       exp.printStackTrace();
+     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);
+
+
+       return new BufferedReader(new FileReader(file));  
+     } catch (Exception ex) {
    }
+         ex.printStackTrace();
+         // exception: we take the file itself:
+   private Reader getActualReader(File file) throws IOException {
+         return getActualReaderXml(file);
+       // load .mmx file...
+     } finally {
+       String ext = Tools.getExtension(file.getName());
+         if (inputStream != null) {
+       String mmxFileName = "";
+             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());
+ }
+
+
+       if(!ext.equals("mm"))
+ /*
+        {
+ * OSSXP.COM: In this hacked version, .mm file is a true XML file.
+       mmxFileName = file.getName()+".mmx";
+ * load XML file using DOM.
+       }
+ */
+       else
+ private static Reader getActualReaderXml(File file) throws IOException {
+       {
+     try
+       mmxFileName = Tools.removeExtension(file.getName()) + ".mmx";
+     {
+       }
+         TransformerFactory tf = TransformerFactory.newInstance();
+        File mmxfile = new File(file.getParent(), mmxFileName);
+         Transformer transformer = tf.newTransformer();
+      
+         DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
+        if (!mmxfile.exists())
+         DocumentBuilder domBuilder = domFactory.newDocumentBuilder();
+       {
+        return getActualReaderXml(file);
+        }
+
+
+       URL updaterUrl = null;
+         Document doc = domBuilder.parse(file);
+        InputStream inputStream = null;
+         Source src = new DOMSource(doc);
+        Source xsltSource = null;
+         StringWriter buffwriter = new StringWriter();
+        StringWriter buffwriter = null;
+         StreamResult result = new StreamResult(buffwriter);
+        Result result = null;
+         transformer.transform(src, result);
+        TransformerFactory tf = null;
+        Transformer transformer = null;
+        String mmxFileFullName = file.getParent() + "/" + mmxFileName;
+       try {
+            // try to convert map with xslt:
+            updaterUrl = getFrame().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);
+           // create an instance of TransformerFactory
+            tf = TransformerFactory.newInstance();
+            transformer = tf.newTransformer(xsltSource);
+            transformer.setParameter("mmx_file", mmxFileFullName);
+            transformer.transform(new StreamSource(file), result);
+
+
+       } catch (Exception ex) {
+         return new StringReader(buffwriter.toString());
+            ex.printStackTrace();
+     }
+            // exception: we take the file itself:
+     catch(Exception exp)
+            return getActualReaderXml(file);
+     {
+       } finally {
+     exp.printStackTrace();
+           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());    }
+
+
    //
    return new BufferedReader(new FileReader(file));
    // cut'n'paste
}
    //
Index: freemind/freemind/modes/mindmapmode/freemind_join_mm_mmx.xslt
Index: freemind/modes/mindmapmode/MindMapMapModel.java
===================================================================
===================================================================
--- freemind/freemind/modes/mindmapmode/freemind_join_mm_mmx.xslt (.../tags/RELEASE-0-8-0) (revision 0)
--- a/freemind/freemind/modes/mindmapmode/MindMapMapModel.java
+++ freemind/freemind/modes/mindmapmode/freemind_join_mm_mmx.xslt (.../branches/WHFM-0-8-0) (revision 28)
+++ b/freemind/freemind/modes/mindmapmode/MindMapMapModel.java
@@ -0,0 +1,45 @@
@@ -440,7 +440,7 @@
+<xsl:stylesheet version="1.0"
            }
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
            if (mapStart.equals(EXPECTED_START_STRINGS[i])) {
+
                // actual version:
+ <xsl:output method="xml" version="1.0" encoding="utf-8"
-               reader = Tools.getActualReader(file);
+ indent="yes" />
+               reader = Tools.getActualReader(file, getFrame());
+
                break;
+ <xsl:param name="mmx_file" />
            }
+
        }
+ <xsl:template match="map">
Index: build.xml
+ <map>
+ <xsl:copy-of select="@*" />
+ <xsl:apply-templates />
+ </map>
+ </xsl:template>
+
+ <xsl:template match="node">
+ <xsl:param name="mmx_node" select="document($mmx_file)" />
+ <xsl:copy>
+ <xsl:choose>
+ <xsl:when test="$mmx_node//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_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  
+++ 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"/>

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>