Bläddra i källkod

Fixes XML Map Export (#2093)

* export issue fixed

* tentative version checking

* commented versioning code

* implemented fix

* tested xml import

* tested binary import

* rollback changes

* implemented prefix in DOM input and output capsules

* moved helper methods to utility class

* added license for utility class

* incremented format version and made javadoc clearer

* fixed backwards compatibility issues

* removed unnecessary imports

* fixed incorrect prefix in output capsule

* Add unit test for jme exporter / jme importer

---------

Co-authored-by: Riccardo Balbo <[email protected]>
codex 1 år sedan
förälder
incheckning
641f1cee59

+ 9 - 2
jme3-core/src/main/java/com/jme3/export/FormatVersion.java

@@ -39,9 +39,16 @@ package com.jme3.export;
 public final class FormatVersion {
     
     /**
-     * Version number of the format
+     * Version number of the format.
+     * <p>
+     * Changes for each version:
+     * <ol>
+     *   <li>Undocumented
+     *   <li>Undocumented
+     *   <li>XML prefixes "jme-" to all key names
+     * </ol>
      */
-    public static final int VERSION = 2;
+    public static final int VERSION = 3;
 
     /**
      * Signature of the format: currently, "JME3" as ASCII.

+ 1 - 1
jme3-core/src/main/java/com/jme3/scene/UserData.java

@@ -138,7 +138,7 @@ public final class UserData implements Savable {
     public void write(JmeExporter ex) throws IOException {
         OutputCapsule oc = ex.getCapsule(this);
         oc.write(type, "type", (byte) 0);
-
+        
         switch (type) {
             case TYPE_INTEGER:
                 int i = (Integer) value;

+ 34 - 6
jme3-examples/src/main/java/jme3test/export/TestIssue2068.java

@@ -32,10 +32,14 @@
 package jme3test.export;
 
 import com.jme3.app.SimpleApplication;
+import com.jme3.asset.plugins.FileLocator;
 import com.jme3.export.JmeExporter;
 import com.jme3.export.xml.XMLExporter;
+import com.jme3.export.xml.XMLImporter;
+import com.jme3.scene.Spatial;
 import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.logging.Level;
@@ -55,7 +59,7 @@ import java.util.logging.Logger;
 public class TestIssue2068 extends SimpleApplication {
     // *************************************************************************
     // constants and loggers
-
+    
     /**
      * message logger for this class
      */
@@ -79,18 +83,42 @@ public class TestIssue2068 extends SimpleApplication {
      */
     @Override
     public void simpleInitApp() {
+        
+        ArrayList<String> list = new ArrayList<>();
+        list.add("list-value");
+        rootNode.setUserData("list", list);
+        
         Map<String, String> map = new HashMap<>();
-        map.put("key", "value");
+        map.put("map-key", "map-value");
         rootNode.setUserData("map", map);
-
-        String outputFilename = "TestIssue2068.xml";
-        File xmlFile = new File(outputFilename);
+        
+        String[] array = new String[1];
+        array[0] = "array-value";
+        rootNode.setUserData("array", array);
+        
+        // export xml
+        String filename = "TestIssue2068.xml";
+        File xmlFile = new File(filename);
         JmeExporter exporter = XMLExporter.getInstance();
         try {
             exporter.save(rootNode, xmlFile);
         } catch (IOException exception) {
-            logger.log(Level.SEVERE, exception.getMessage(), exception);
+            throw new IllegalStateException(exception);
         }
+        
+        // import binary/xml
+        assetManager.registerLocator("", FileLocator.class);
+        assetManager.registerLoader(XMLImporter.class, "xml");
+        Spatial model = assetManager.loadModel(filename);
+        //Spatial model = assetManager.loadModel("Models/Jaime/Jaime.j3o");
+        model.depthFirstTraversal((Spatial spatial) -> {
+            System.out.println("UserData for "+spatial);
+            for (String key : spatial.getUserDataKeys()) {
+                System.out.println("  "+key+": "+spatial.getUserData(key));
+            }
+        });
+        
         stop();
+        
     }
 }

+ 146 - 4
jme3-plugins/src/test/java/com/jme3/export/JmeExporterTest.java

@@ -31,11 +31,18 @@
  */
 package com.jme3.export;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
 import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Rule;
@@ -43,12 +50,18 @@ import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
+
+import com.jme3.asset.AssetInfo;
 import com.jme3.asset.AssetManager;
 import com.jme3.asset.DesktopAssetManager;
+import com.jme3.asset.ModelKey;
 import com.jme3.export.binary.BinaryExporter;
+import com.jme3.export.binary.BinaryImporter;
 import com.jme3.export.xml.XMLExporter;
+import com.jme3.export.xml.XMLImporter;
 import com.jme3.material.Material;
 import com.jme3.material.plugin.export.material.J3MExporter;
+import com.jme3.scene.Node;
 
 /**
  * Tests the methods on classes that implements the JmeExporter interface.
@@ -64,7 +77,6 @@ public class JmeExporterTest {
     @Rule
     public TemporaryFolder folder = new TemporaryFolder();
 
-
     @BeforeClass
     public static void beforeClass() {
         AssetManager assetManager = new DesktopAssetManager(true);
@@ -116,10 +128,140 @@ public class JmeExporterTest {
     public void testSaveWithNullParent() throws IOException {
         File file = new File("someFile.txt");
         try {
-        	exporter.save(material, file);
-        	Assert.assertTrue(file.exists());
+            exporter.save(material, file);
+            Assert.assertTrue(file.exists());
         } finally {
-        	file.delete();
+            file.delete();
+        }
+    }
+
+    @Test
+    public void testExporterConsistency() {
+        //
+        final boolean testXML = true;
+        final boolean testLists = false;
+        final boolean testMaps = true;
+        final boolean printXML = false;
+
+        // initialize data
+        AssetManager assetManager = new DesktopAssetManager(true);
+        ArrayList<JmeExporter> exporters = new ArrayList<JmeExporter>();
+        ArrayList<JmeImporter> importers = new ArrayList<JmeImporter>();
+
+        BinaryExporter be = new BinaryExporter();
+        BinaryImporter bi = new BinaryImporter();
+        exporters.add(be);
+        importers.add(bi);
+
+        if (testXML) {
+            XMLExporter xe = new XMLExporter();
+            XMLImporter xi = new XMLImporter();
+            exporters.add(xe);
+            importers.add(xi);
+        }
+
+        Node origin = new Node("origin");
+
+        origin.setUserData("testInt", 10);
+        origin.setUserData("testString", "ABC");
+        origin.setUserData("testBoolean", true);
+        origin.setUserData("testFloat", 1.5f);
+        origin.setUserData("1", "test");
+        if (testLists) {
+            origin.setUserData("string-list", Arrays.asList("abc"));
+            origin.setUserData("int-list", Arrays.asList(1, 2, 3));
+            origin.setUserData("float-list", Arrays.asList(1f, 2f, 3f));
         }
+
+        if (testMaps) {
+            Map<String, Object> map = new HashMap<>();
+            map.put("int", 1);
+            map.put("string", "abc");
+            map.put("float", 1f);
+            origin.setUserData("map", map);
+        }
+
+        // export
+        ByteArrayOutputStream outs[] = new ByteArrayOutputStream[exporters.size()];
+        for (int i = 0; i < exporters.size(); i++) {
+            JmeExporter exporter = exporters.get(i);
+            outs[i] = new ByteArrayOutputStream();
+            try {
+                exporter.save(origin, outs[i]);
+            } catch (IOException ex) {
+                Assert.fail(ex.getMessage());
+            }
+        }
+
+        // print
+        if (printXML) {
+            for (int i = 0; i < exporters.size(); i++) {
+                ByteArrayOutputStream out = outs[i];
+                if (exporters.get(i) instanceof XMLExporter) {
+                    System.out.println("XML: \n" + new String(out.toByteArray()) + "\n\n");
+                } else if (exporters.get(i) instanceof BinaryExporter) {
+                    System.out.println("Binary: " + out.size() + " bytes");
+                } else {
+                    System.out.println("Unknown exporter: " + exporters.get(i).getClass().getName());
+                }
+            }
+        }
+
+        // import
+        Node nodes[] = new Node[importers.size() + 1];
+        nodes[0] = origin;
+        for (int i = 0; i < importers.size(); i++) {
+            JmeImporter importer = importers.get(i);
+            ByteArrayOutputStream out = outs[i];
+            try {
+                AssetInfo info = new AssetInfo(assetManager, new ModelKey("origin")) {
+                    @Override
+                    public InputStream openStream() {
+                        return new ByteArrayInputStream(out.toByteArray());
+                    }
+                };
+                nodes[i + 1] = (Node) importer.load(info);
+            } catch (IOException ex) {
+                Assert.fail(ex.getMessage());
+            }
+        }
+
+        // compare
+        Map<String, Object> userData[] = new Map[nodes.length];
+        for (int i = 0; i < nodes.length; i++) {
+            Node n = nodes[i];
+            userData[i] = new HashMap<String, Object>();
+            for (String k : n.getUserDataKeys()) {
+                userData[i].put(k, n.getUserData(k));
+            }
+        }
+        compareMaps(userData);
+    }
+
+    private static final void compareMaps(Map<String, Object>[] maps) {
+        String[] keys = maps[0].keySet().toArray(new String[0]);
+        // check if all maps have the same keys and values for those keys
+        for (int i = 1; i < maps.length; i++) {
+            Map<String, Object> map = maps[i];
+            Assert.assertEquals("Map " + i + " keys do not match", keys.length, map.size());
+            for (String key : keys) {
+                Assert.assertTrue("Missing key " + key + " in map " + i, map.containsKey(key));
+                Object v1 = maps[0].get(key);
+                Object v2 = map.get(key);
+                if (v1.getClass().isArray()) {
+                    boolean c = Arrays.equals((Object[]) v1, (Object[]) v2);
+                    if (c) System.out.println(key + " match");
+                    Assert.assertTrue("Value does not match in map " + i + " for key " + key + " expected "
+                            + Arrays.deepToString((Object[]) v1) + " but got "
+                            + Arrays.deepToString((Object[]) v2), c);
+                } else {
+                    boolean c = v1.equals(v2);
+                    if (c) System.out.println(key + " match");
+                    Assert.assertTrue("Value does not match in map " + i + " for key " + key + " expected "
+                            + v1 + " but got " + v2, c);
+                }
+            }
+        }
+
     }
 }

+ 61 - 59
jme3-plugins/src/xml/java/com/jme3/export/xml/DOMInputCapsule.java

@@ -72,6 +72,7 @@ public class DOMInputCapsule implements InputCapsule {
         this.importer = importer;
         currentElem = doc.getDocumentElement();
         
+        // file version is always unprefixed for backwards compatibility
         String version = currentElem.getAttribute("format_version");
         importer.formatVersion = version.equals("") ? 0 : Integer.parseInt(version);
     }
@@ -126,7 +127,7 @@ public class DOMInputCapsule implements InputCapsule {
 
     @Override
     public byte readByte(String name, byte defVal) throws IOException {
-        String tmpString = currentElem.getAttribute(name);
+        String tmpString = XMLUtils.getAttribute(importer.getFormatVersion(), currentElem, name);
         if (tmpString == null || tmpString.length() < 1) return defVal;
         try {
             return Byte.parseByte(tmpString);
@@ -149,8 +150,8 @@ public class DOMInputCapsule implements InputCapsule {
             if (tmpEl == null) {
                 return defVal;
             }
-            String sizeString = tmpEl.getAttribute("size");
-            String[] strings = parseTokens(tmpEl.getAttribute("data"));
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
+            String[] strings = parseTokens(XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "data"));
             if (sizeString.length() > 0) {
                 int requiredSize = Integer.parseInt(sizeString);
                 if (strings.length != requiredSize)
@@ -186,7 +187,7 @@ public class DOMInputCapsule implements InputCapsule {
                 return defVal;
             }
 
-            String sizeString = tmpEl.getAttribute("size");
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
             NodeList nodes = currentElem.getChildNodes();
             List<byte[]> byteArrays = new ArrayList<>();
 
@@ -218,7 +219,7 @@ public class DOMInputCapsule implements InputCapsule {
 
     @Override
     public int readInt(String name, int defVal) throws IOException {
-        String tmpString = currentElem.getAttribute(name);
+        String tmpString = XMLUtils.getAttribute(importer.getFormatVersion(), currentElem, name);
         if (tmpString == null || tmpString.length() < 1) return defVal;
         try {
             return Integer.parseInt(tmpString);
@@ -241,8 +242,8 @@ public class DOMInputCapsule implements InputCapsule {
             if (tmpEl == null) {
                 return defVal;
             }
-            String sizeString = tmpEl.getAttribute("size");
-            String[] strings = parseTokens(tmpEl.getAttribute("data"));
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
+            String[] strings = parseTokens(XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "data"));
             if (sizeString.length() > 0) {
                 int requiredSize = Integer.parseInt(sizeString);
                 if (strings.length != requiredSize)
@@ -276,7 +277,7 @@ public class DOMInputCapsule implements InputCapsule {
             if (tmpEl == null) {
                 return defVal;
             }
-            String sizeString = tmpEl.getAttribute("size");
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
 
 
 
@@ -312,7 +313,7 @@ public class DOMInputCapsule implements InputCapsule {
 
     @Override
     public float readFloat(String name, float defVal) throws IOException {
-        String tmpString = currentElem.getAttribute(name);
+        String tmpString = XMLUtils.getAttribute(importer.getFormatVersion(), currentElem, name);
         if (tmpString == null || tmpString.length() < 1) return defVal;
         try {
             return Float.parseFloat(tmpString);
@@ -335,8 +336,8 @@ public class DOMInputCapsule implements InputCapsule {
             if (tmpEl == null) {
                 return defVal;
             }
-            String sizeString = tmpEl.getAttribute("size");
-            String[] strings = parseTokens(tmpEl.getAttribute("data"));
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
+            String[] strings = parseTokens(XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "data"));
             if (sizeString.length() > 0) {
                 int requiredSize = Integer.parseInt(sizeString);
                 if (strings.length != requiredSize)
@@ -371,12 +372,12 @@ public class DOMInputCapsule implements InputCapsule {
             if (tmpEl == null) {
                 return defVal;
             }
-            int size_outer = Integer.parseInt(tmpEl.getAttribute("size_outer"));
-            int size_inner = Integer.parseInt(tmpEl.getAttribute("size_outer"));
+            int size_outer = Integer.parseInt(XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size_outer"));
+            int size_inner = Integer.parseInt(XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size_outer"));
 
             float[][] tmp = new float[size_outer][size_inner];
 
-            String[] strings = parseTokens(tmpEl.getAttribute("data"));
+            String[] strings = parseTokens(XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "data"));
             for (int i = 0; i < size_outer; i++) {
                 tmp[i] = new float[size_inner];
                 for (int k = 0; k < size_inner; k++) {
@@ -393,7 +394,7 @@ public class DOMInputCapsule implements InputCapsule {
 
     @Override
     public double readDouble(String name, double defVal) throws IOException {
-        String tmpString = currentElem.getAttribute(name);
+        String tmpString = XMLUtils.getAttribute(importer.getFormatVersion(), currentElem, name);
         if (tmpString == null || tmpString.length() < 1) return defVal;
         try {
             return Double.parseDouble(tmpString);
@@ -416,8 +417,8 @@ public class DOMInputCapsule implements InputCapsule {
             if (tmpEl == null) {
                 return defVal;
             }
-            String sizeString = tmpEl.getAttribute("size");
-            String[] strings = parseTokens(tmpEl.getAttribute("data"));
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
+            String[] strings = parseTokens(XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "data"));
             if (sizeString.length() > 0) {
                 int requiredSize = Integer.parseInt(sizeString);
                 if (strings.length != requiredSize)
@@ -451,7 +452,7 @@ public class DOMInputCapsule implements InputCapsule {
             if (tmpEl == null) {
                 return defVal;
             }
-            String sizeString = tmpEl.getAttribute("size");
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
             NodeList nodes = currentElem.getChildNodes();
             List<double[]> doubleArrays = new ArrayList<>();
 
@@ -483,7 +484,7 @@ public class DOMInputCapsule implements InputCapsule {
 
     @Override
     public long readLong(String name, long defVal) throws IOException {
-        String tmpString = currentElem.getAttribute(name);
+        String tmpString = XMLUtils.getAttribute(importer.getFormatVersion(), currentElem, name);
         if (tmpString == null || tmpString.length() < 1) return defVal;
         try {
             return Long.parseLong(tmpString);
@@ -506,8 +507,8 @@ public class DOMInputCapsule implements InputCapsule {
             if (tmpEl == null) {
                 return defVal;
             }
-            String sizeString = tmpEl.getAttribute("size");
-            String[] strings = parseTokens(tmpEl.getAttribute("data"));
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
+            String[] strings = parseTokens(XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "data"));
             if (sizeString.length() > 0) {
                 int requiredSize = Integer.parseInt(sizeString);
                 if (strings.length != requiredSize)
@@ -541,7 +542,7 @@ public class DOMInputCapsule implements InputCapsule {
             if (tmpEl == null) {
                 return defVal;
             }
-            String sizeString = tmpEl.getAttribute("size");
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
             NodeList nodes = currentElem.getChildNodes();
             List<long[]> longArrays = new ArrayList<>();
 
@@ -573,7 +574,7 @@ public class DOMInputCapsule implements InputCapsule {
 
     @Override
     public short readShort(String name, short defVal) throws IOException {
-        String tmpString = currentElem.getAttribute(name);
+        String tmpString = XMLUtils.getAttribute(importer.getFormatVersion(), currentElem, name);
         if (tmpString == null || tmpString.length() < 1) return defVal;
         try {
             return Short.parseShort(tmpString);
@@ -596,8 +597,8 @@ public class DOMInputCapsule implements InputCapsule {
              if (tmpEl == null) {
                  return defVal;
              }
-            String sizeString = tmpEl.getAttribute("size");
-            String[] strings = parseTokens(tmpEl.getAttribute("data"));
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
+            String[] strings = parseTokens(XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "data"));
             if (sizeString.length() > 0) {
                 int requiredSize = Integer.parseInt(sizeString);
                 if (strings.length != requiredSize)
@@ -632,7 +633,7 @@ public class DOMInputCapsule implements InputCapsule {
                 return defVal;
             }
 
-            String sizeString = tmpEl.getAttribute("size");
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
             NodeList nodes = currentElem.getChildNodes();
             List<short[]> shortArrays = new ArrayList<>();
 
@@ -664,7 +665,7 @@ public class DOMInputCapsule implements InputCapsule {
 
     @Override
     public boolean readBoolean(String name, boolean defVal) throws IOException {
-        String tmpString = currentElem.getAttribute(name);
+        String tmpString = XMLUtils.getAttribute(importer.getFormatVersion(), currentElem, name);
         if (tmpString == null || tmpString.length() < 1) return defVal;
         try {
             return Boolean.parseBoolean(tmpString);
@@ -687,8 +688,8 @@ public class DOMInputCapsule implements InputCapsule {
             if (tmpEl == null) {
                 return defVal;
             }
-            String sizeString = tmpEl.getAttribute("size");
-            String[] strings = parseTokens(tmpEl.getAttribute("data"));
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
+            String[] strings = parseTokens(XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "data"));
             if (sizeString.length() > 0) {
                 int requiredSize = Integer.parseInt(sizeString);
                 if (strings.length != requiredSize)
@@ -722,7 +723,7 @@ public class DOMInputCapsule implements InputCapsule {
             if (tmpEl == null) {
                 return defVal;
             }
-            String sizeString = tmpEl.getAttribute("size");
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
             NodeList nodes = currentElem.getChildNodes();
             List<boolean[]> booleanArrays = new ArrayList<>();
 
@@ -754,7 +755,7 @@ public class DOMInputCapsule implements InputCapsule {
 
     @Override
     public String readString(String name, String defVal) throws IOException {
-        String tmpString = currentElem.getAttribute(name);
+        String tmpString = XMLUtils.getAttribute(importer.getFormatVersion(), currentElem, name);
         if (tmpString == null || tmpString.length() < 1) return defVal;
         try {
             return decodeString(tmpString);
@@ -777,7 +778,7 @@ public class DOMInputCapsule implements InputCapsule {
              if (tmpEl == null) {
                  return defVal;
              }
-            String sizeString = tmpEl.getAttribute("size");
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
             NodeList nodes = tmpEl.getChildNodes();
             List<String> strings = new ArrayList<>();
 
@@ -818,7 +819,7 @@ public class DOMInputCapsule implements InputCapsule {
             if (tmpEl == null) {
                 return defVal;
             }
-            String sizeString = tmpEl.getAttribute("size");
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
             NodeList nodes = currentElem.getChildNodes();
             List<String[]> stringArrays = new ArrayList<>();
 
@@ -850,7 +851,7 @@ public class DOMInputCapsule implements InputCapsule {
 
     @Override
     public BitSet readBitSet(String name, BitSet defVal) throws IOException {
-        String tmpString = currentElem.getAttribute(name);
+        String tmpString = XMLUtils.getAttribute(importer.getFormatVersion(), currentElem, name);
         if (tmpString == null || tmpString.length() < 1) return defVal;
         try {
             BitSet set = new BitSet();
@@ -914,19 +915,19 @@ public class DOMInputCapsule implements InputCapsule {
         if (currentElem == null || currentElem.getNodeName().equals("null")) {
             return null;
         }
-        String reference = currentElem.getAttribute("ref");
+        String reference = XMLUtils.getAttribute(importer.getFormatVersion(), currentElem, "ref");
         if (reference.length() > 0) {
             ret = referencedSavables.get(reference);
         } else {
             String className = currentElem.getNodeName();
-            if (currentElem.hasAttribute("class")) {
-                className = currentElem.getAttribute("class");
+            if (XMLUtils.hasAttribute(importer.getFormatVersion(), currentElem, "class")) {
+                className = XMLUtils.getAttribute(importer.getFormatVersion(), currentElem, "class");
             } else if (defVal != null) {
                 className = defVal.getClass().getName();
             }
             tmp = SavableClassUtil.fromName(className);
             
-            String versionsStr = currentElem.getAttribute("savable_versions");
+            String versionsStr = XMLUtils.getAttribute(importer.getFormatVersion(), currentElem, "savable_versions");
             if (versionsStr != null && !versionsStr.equals("")){
                 String[] versionStr = versionsStr.split(",");
                 classHierarchyVersions = new int[versionStr.length];
@@ -937,8 +938,8 @@ public class DOMInputCapsule implements InputCapsule {
                 classHierarchyVersions = null;
             }
             
-            String refID = currentElem.getAttribute("reference_ID");
-            if (refID.length() < 1) refID = currentElem.getAttribute("id");
+            String refID = XMLUtils.getAttribute(importer.getFormatVersion(), currentElem, "reference_ID");
+            if (refID.length() < 1) refID = XMLUtils.getAttribute(importer.getFormatVersion(), currentElem, "id");
             if (refID.length() > 0) referencedSavables.put(refID, tmp);
             if (tmp != null) {
                 // Allows reading versions from this savable
@@ -959,7 +960,7 @@ public class DOMInputCapsule implements InputCapsule {
                 return defVal;
             }
 
-            String sizeString = tmpEl.getAttribute("size");
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
             List<Savable> savables = new ArrayList<>();
             for (currentElem = findFirstChildElement(tmpEl);
                     currentElem != null;
@@ -994,8 +995,8 @@ public class DOMInputCapsule implements InputCapsule {
                 return defVal;
             }
 
-            int size_outer = Integer.parseInt(tmpEl.getAttribute("size_outer"));
-            int size_inner = Integer.parseInt(tmpEl.getAttribute("size_outer"));
+            int size_outer = Integer.parseInt(XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size_outer"));
+            int size_inner = Integer.parseInt(XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size_outer"));
 
             Savable[][] tmp = new Savable[size_outer][size_inner];
             currentElem = findFirstChildElement(tmpEl);
@@ -1029,7 +1030,7 @@ public class DOMInputCapsule implements InputCapsule {
                 return defVal;
             }
 
-            String sizeString = tmpEl.getAttribute("size");
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
             ArrayList<Savable> savables = new ArrayList<>();
             for (currentElem = findFirstChildElement(tmpEl);
                     currentElem != null;
@@ -1066,7 +1067,7 @@ public class DOMInputCapsule implements InputCapsule {
             }
             currentElem = tmpEl;
 
-            String sizeString = tmpEl.getAttribute("size");
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
             int requiredSize = (sizeString.length() > 0)
                              ? Integer.parseInt(sizeString)
                              : -1;
@@ -1107,7 +1108,7 @@ public class DOMInputCapsule implements InputCapsule {
                 return defVal;
             }
             currentElem = tmpEl;
-            String sizeString = tmpEl.getAttribute("size");
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
 
             ArrayList<Savable>[] arr;
             List<ArrayList<Savable>[]> sall = new ArrayList<>();
@@ -1142,7 +1143,7 @@ public class DOMInputCapsule implements InputCapsule {
                 return defVal;
             }
 
-            String sizeString = tmpEl.getAttribute("size");
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
             ArrayList<FloatBuffer> tmp = new ArrayList<>();
             for (currentElem = findFirstChildElement(tmpEl);
                     currentElem != null;
@@ -1214,7 +1215,7 @@ public class DOMInputCapsule implements InputCapsule {
                                 if (n instanceof Element && n.getNodeName().equals("MapEntry")) {
                                         Element elem = (Element) n;
                                         currentElem = elem;
-                                        String key = currentElem.getAttribute("key");
+                                        String key = XMLUtils.getAttribute(importer.getFormatVersion(), currentElem, "key");
                                         Savable val = readSavable("Savable", null);
                                         ret.put(key, val);
                                 }
@@ -1245,7 +1246,7 @@ public class DOMInputCapsule implements InputCapsule {
                                 if (n instanceof Element && n.getNodeName().equals("MapEntry")) {
                                         Element elem = (Element) n;
                                         currentElem = elem;
-                                        int key = Integer.parseInt(currentElem.getAttribute("key"));
+                                        int key = Integer.parseInt(XMLUtils.getAttribute(importer.getFormatVersion(), currentElem, "key"));
                                         Savable val = readSavable("Savable", null);
                                         ret.put(key, val);
                                 }
@@ -1272,8 +1273,8 @@ public class DOMInputCapsule implements InputCapsule {
             if (tmpEl == null) {
                 return defVal;
             }
-            String sizeString = tmpEl.getAttribute("size");
-            String[] strings = parseTokens(tmpEl.getAttribute("data"));
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
+            String[] strings = parseTokens(XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "data"));
             if (sizeString.length() > 0) {
                 int requiredSize = Integer.parseInt(sizeString);
                 if (strings.length != requiredSize)
@@ -1302,8 +1303,8 @@ public class DOMInputCapsule implements InputCapsule {
                 return defVal;
             }
 
-            String sizeString = tmpEl.getAttribute("size");
-            String[] strings = parseTokens(tmpEl.getAttribute("data"));
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
+            String[] strings = parseTokens(XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "data"));
             if (sizeString.length() > 0) {
                 int requiredSize = Integer.parseInt(sizeString);
                 if (strings.length != requiredSize)
@@ -1332,8 +1333,8 @@ public class DOMInputCapsule implements InputCapsule {
                 return defVal;
             }
 
-            String sizeString = tmpEl.getAttribute("size");
-            String[] strings = parseTokens(tmpEl.getAttribute("data"));
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
+            String[] strings = parseTokens(XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "data"));
             if (sizeString.length() > 0) {
                 int requiredSize = Integer.parseInt(sizeString);
                 if (strings.length != requiredSize)
@@ -1362,8 +1363,8 @@ public class DOMInputCapsule implements InputCapsule {
                 return defVal;
             }
 
-            String sizeString = tmpEl.getAttribute("size");
-            String[] strings = parseTokens(tmpEl.getAttribute("data"));
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
+            String[] strings = parseTokens(XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "data"));
             if (sizeString.length() > 0) {
                 int requiredSize = Integer.parseInt(sizeString);
                 if (strings.length != requiredSize)
@@ -1392,7 +1393,7 @@ public class DOMInputCapsule implements InputCapsule {
                 return defVal;
             }
 
-            String sizeString = tmpEl.getAttribute("size");
+            String sizeString = XMLUtils.getAttribute(importer.getFormatVersion(), tmpEl, "size");
             ArrayList<ByteBuffer> tmp = new ArrayList<>();
             for (currentElem = findFirstChildElement(tmpEl);
                     currentElem != null;
@@ -1422,7 +1423,7 @@ public class DOMInputCapsule implements InputCapsule {
                         T defVal) throws IOException {
         T ret = defVal;
         try {
-            String eVal = currentElem.getAttribute(name);
+            String eVal = XMLUtils.getAttribute(importer.getFormatVersion(), currentElem, name);
             if (eVal != null && eVal.length() > 0) {
                 ret = Enum.valueOf(enumType, eVal);
             }
@@ -1442,4 +1443,5 @@ public class DOMInputCapsule implements InputCapsule {
                ? zeroStrings
                : outStrings;
     }
+    
 }

+ 63 - 61
jme3-plugins/src/xml/java/com/jme3/export/xml/DOMOutputCapsule.java

@@ -56,7 +56,7 @@ import org.w3c.dom.Element;
  * @author Doug Daniels (dougnukem) - adjustments for jME 2.0 and Java 1.5
  */
 public class DOMOutputCapsule implements OutputCapsule {
-
+    
     private static final String dataAttributeName = "data";
     private Document doc;
     private Element currentElement;
@@ -80,6 +80,7 @@ public class DOMOutputCapsule implements OutputCapsule {
     private Element appendElement(String name) {
         Element ret = doc.createElement(name);
         if (currentElement == null) {
+            // file version is always unprefixed for backwards compatibility
             ret.setAttribute("format_version", Integer.toString(FormatVersion.VERSION));
             doc.appendChild(ret);
         } else {
@@ -104,7 +105,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         if (value == defVal) {
             return;
         }
-        currentElement.setAttribute(name, String.valueOf(value));
+        XMLUtils.setAttribute(currentElement, name, String.valueOf(value));
     }
 
     @Override
@@ -124,8 +125,8 @@ public class DOMOutputCapsule implements OutputCapsule {
         }
 
         Element el = appendElement(name);
-        el.setAttribute("size", String.valueOf(value.length));
-        el.setAttribute(dataAttributeName, buf.toString());
+        XMLUtils.setAttribute(el, "size", String.valueOf(value.length));
+        XMLUtils.setAttribute(el, dataAttributeName, buf.toString());
         currentElement = (Element) currentElement.getParentNode();
     }
 
@@ -151,9 +152,9 @@ public class DOMOutputCapsule implements OutputCapsule {
         }
 
         Element el = appendElement(name);
-        el.setAttribute("size_outer", String.valueOf(value.length));
-        el.setAttribute("size_inner", String.valueOf(value[0].length));
-        el.setAttribute(dataAttributeName, buf.toString());
+        XMLUtils.setAttribute(el, "size_outer", String.valueOf(value.length));
+        XMLUtils.setAttribute(el, "size_inner", String.valueOf(value[0].length));
+        XMLUtils.setAttribute(el, dataAttributeName, buf.toString());
         currentElement = (Element) currentElement.getParentNode();
     }
 
@@ -162,7 +163,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         if (value == defVal) {
             return;
         }
-        currentElement.setAttribute(name, String.valueOf(value));
+        XMLUtils.setAttribute(currentElement, name, String.valueOf(value));
     }
 
     @Override
@@ -182,8 +183,8 @@ public class DOMOutputCapsule implements OutputCapsule {
         }
 
         Element el = appendElement(name);
-        el.setAttribute("size", String.valueOf(value.length));
-        el.setAttribute(dataAttributeName, buf.toString());
+        XMLUtils.setAttribute(el, "size", String.valueOf(value.length));
+        XMLUtils.setAttribute(el, dataAttributeName, buf.toString());
         currentElement = (Element) currentElement.getParentNode();
     }
 
@@ -193,7 +194,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         if(Arrays.deepEquals(value, defVal)) return;
 
         Element el = appendElement(name);
-        el.setAttribute("size", String.valueOf(value.length));
+        XMLUtils.setAttribute(el, "size", String.valueOf(value.length));
 
         for (int i=0; i<value.length; i++) {
                 int[] array = value[i];
@@ -207,7 +208,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         if (value == defVal) {
             return;
         }
-        currentElement.setAttribute(name, String.valueOf(value));
+        XMLUtils.setAttribute(currentElement, name, String.valueOf(value));
     }
 
     @Override
@@ -229,8 +230,8 @@ public class DOMOutputCapsule implements OutputCapsule {
         }
 
         Element el = appendElement(name);
-        el.setAttribute("size", value == null ? "0" : String.valueOf(value.length));
-        el.setAttribute(dataAttributeName, buf.toString());
+        XMLUtils.setAttribute(el, "size", value == null ? "0" : String.valueOf(value.length));
+        XMLUtils.setAttribute(el, dataAttributeName, buf.toString());
         currentElement = (Element) currentElement.getParentNode();
     }
 
@@ -253,9 +254,9 @@ public class DOMOutputCapsule implements OutputCapsule {
         }
 
         Element el = appendElement(name);
-        el.setAttribute("size_outer", String.valueOf(value.length));
-        el.setAttribute("size_inner", String.valueOf(value[0].length));
-        el.setAttribute(dataAttributeName, buf.toString());
+        XMLUtils.setAttribute(el, "size_outer", String.valueOf(value.length));
+        XMLUtils.setAttribute(el, "size_inner", String.valueOf(value[0].length));
+        XMLUtils.setAttribute(el, dataAttributeName, buf.toString());
         currentElement = (Element) currentElement.getParentNode();
     }
 
@@ -264,7 +265,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         if (value == defVal) {
             return;
         }
-        currentElement.setAttribute(name, String.valueOf(value));
+        XMLUtils.setAttribute(currentElement, name, String.valueOf(value));
     }
 
     @Override
@@ -284,8 +285,8 @@ public class DOMOutputCapsule implements OutputCapsule {
         }
 
         Element el = appendElement(name);
-        el.setAttribute("size", String.valueOf(value.length));
-        el.setAttribute(dataAttributeName, buf.toString());
+        XMLUtils.setAttribute(el, "size", String.valueOf(value.length));
+        XMLUtils.setAttribute(el, dataAttributeName, buf.toString());
         currentElement = (Element) currentElement.getParentNode();
     }
 
@@ -295,7 +296,7 @@ public class DOMOutputCapsule implements OutputCapsule {
             if(Arrays.deepEquals(value, defVal)) return;
 
             Element el = appendElement(name);
-            el.setAttribute("size", String.valueOf(value.length));
+            XMLUtils.setAttribute(el, "size", String.valueOf(value.length));
 
             for (int i=0; i<value.length; i++) {
                 double[] array = value[i];
@@ -309,7 +310,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         if (value == defVal) {
             return;
         }
-        currentElement.setAttribute(name, String.valueOf(value));
+        XMLUtils.setAttribute(currentElement, name, String.valueOf(value));
     }
 
     @Override
@@ -329,8 +330,8 @@ public class DOMOutputCapsule implements OutputCapsule {
         }
 
         Element el = appendElement(name);
-        el.setAttribute("size", String.valueOf(value.length));
-        el.setAttribute(dataAttributeName, buf.toString());
+        XMLUtils.setAttribute(el, "size", String.valueOf(value.length));
+        XMLUtils.setAttribute(el, dataAttributeName, buf.toString());
         currentElement = (Element) currentElement.getParentNode();
     }
 
@@ -340,7 +341,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         if(Arrays.deepEquals(value, defVal)) return;
 
         Element el = appendElement(name);
-        el.setAttribute("size", String.valueOf(value.length));
+        XMLUtils.setAttribute(el, "size", String.valueOf(value.length));
 
         for (int i=0; i<value.length; i++) {
                 long[] array = value[i];
@@ -354,7 +355,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         if (value == defVal) {
             return;
         }
-        currentElement.setAttribute(name, String.valueOf(value));
+        XMLUtils.setAttribute(currentElement, name, String.valueOf(value));
     }
 
     @Override
@@ -374,8 +375,8 @@ public class DOMOutputCapsule implements OutputCapsule {
         }
 
         Element el = appendElement(name);
-        el.setAttribute("size", String.valueOf(value.length));
-        el.setAttribute(dataAttributeName, buf.toString());
+        XMLUtils.setAttribute(el, "size", String.valueOf(value.length));
+        XMLUtils.setAttribute(el, dataAttributeName, buf.toString());
         currentElement = (Element) currentElement.getParentNode();
     }
 
@@ -385,7 +386,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         if(Arrays.deepEquals(value, defVal)) return;
 
         Element el = appendElement(name);
-        el.setAttribute("size", String.valueOf(value.length));
+        XMLUtils.setAttribute(el, "size", String.valueOf(value.length));
 
         for (int i=0; i<value.length; i++) {
                 short[] array = value[i];
@@ -399,7 +400,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         if (value == defVal) {
             return;
         }
-        currentElement.setAttribute(name, String.valueOf(value));
+        XMLUtils.setAttribute(currentElement, name, String.valueOf(value));
     }
 
     @Override
@@ -419,8 +420,8 @@ public class DOMOutputCapsule implements OutputCapsule {
         }
 
         Element el = appendElement(name);
-        el.setAttribute("size", String.valueOf(value.length));
-        el.setAttribute(dataAttributeName, buf.toString());
+        XMLUtils.setAttribute(el, "size", String.valueOf(value.length));
+        XMLUtils.setAttribute(el, dataAttributeName, buf.toString());
         currentElement = (Element) currentElement.getParentNode();
     }
 
@@ -430,7 +431,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         if(Arrays.deepEquals(value, defVal)) return;
 
         Element el = appendElement(name);
-        el.setAttribute("size", String.valueOf(value.length));
+        XMLUtils.setAttribute(el, "size", String.valueOf(value.length));
 
         for (int i=0; i<value.length; i++) {
                 boolean[] array = value[i];
@@ -444,7 +445,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         if (value == null || value.equals(defVal)) {
             return;
         }
-        currentElement.setAttribute(name, encodeString(value));
+        XMLUtils.setAttribute(currentElement, name, encodeString(value));
     }
 
     @Override
@@ -455,13 +456,13 @@ public class DOMOutputCapsule implements OutputCapsule {
             value = defVal;
         }
 
-        el.setAttribute("size", String.valueOf(value.length));
+        XMLUtils.setAttribute(el, "size", String.valueOf(value.length));
 
         for (int i=0; i<value.length; i++) {
                 String b = value[i];
                 appendElement("String_"+i);
             String val = encodeString(b);
-            currentElement.setAttribute("value", val);
+            XMLUtils.setAttribute(currentElement, "value", val);
             currentElement = el;
         }
         currentElement = (Element) currentElement.getParentNode();
@@ -473,7 +474,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         if(Arrays.deepEquals(value, defVal)) return;
 
         Element el = appendElement(name);
-        el.setAttribute("size", String.valueOf(value.length));
+        XMLUtils.setAttribute(el, "size", String.valueOf(value.length));
 
         for (int i=0; i<value.length; i++) {
                 String[] array = value[i];
@@ -498,7 +499,7 @@ public class DOMOutputCapsule implements OutputCapsule {
             buf.setLength(buf.length() - 1);
         }
         
-        currentElement.setAttribute(name, buf.toString());
+        XMLUtils.setAttribute(currentElement, name, buf.toString());
 
     }
 
@@ -533,10 +534,10 @@ public class DOMOutputCapsule implements OutputCapsule {
             String refID = el.getAttribute("reference_ID");
             if (refID.length() == 0) {
                 refID = object.getClass().getName() + "@" + object.hashCode();
-                el.setAttribute("reference_ID", refID);
+                XMLUtils.setAttribute(el, "reference_ID", refID);
             }
             el = appendElement(name);
-            el.setAttribute("ref", refID);
+            XMLUtils.setAttribute(el, "ref", refID);
         } else {
             el = appendElement(name);
             
@@ -549,13 +550,13 @@ public class DOMOutputCapsule implements OutputCapsule {
                     sb.append(", ");
                 }
             }
-            el.setAttribute("savable_versions", sb.toString());
+            XMLUtils.setAttribute(el, "savable_versions", sb.toString());
             
             writtenSavables.put(object, el);
             object.write(exporter);
         }
         if(className != null){
-            el.setAttribute("class", className);
+            XMLUtils.setAttribute(el, "class", className);
         }
 
         currentElement = old;
@@ -572,7 +573,7 @@ public class DOMOutputCapsule implements OutputCapsule {
 
         Element old = currentElement;
         Element el = appendElement(name);
-        el.setAttribute("size", String.valueOf(objects.length));
+        XMLUtils.setAttribute(el, "size", String.valueOf(objects.length));
         for (int i = 0; i < objects.length; i++) {
             Savable o = objects[i];
             if(o == null){
@@ -595,8 +596,8 @@ public class DOMOutputCapsule implements OutputCapsule {
         if(Arrays.deepEquals(value, defVal)) return;
 
         Element el = appendElement(name);
-        el.setAttribute("size_outer", String.valueOf(value.length));
-        el.setAttribute("size_inner", String.valueOf(value[0].length));
+        XMLUtils.setAttribute(el, "size_outer", String.valueOf(value.length));
+        XMLUtils.setAttribute(el, "size_inner", String.valueOf(value[0].length));
         for (Savable[] bs : value) {
             for(Savable b : bs){
                 write(b, b.getClass().getSimpleName(), null);
@@ -616,7 +617,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         Element old = currentElement;
         Element el = appendElement(name);
         currentElement = el;
-        el.setAttribute(XMLExporter.ATTRIBUTE_SIZE, String.valueOf(array.size()));
+        XMLUtils.setAttribute(el, XMLExporter.ATTRIBUTE_SIZE, String.valueOf(array.size()));
         for (Object o : array) {
                 if(o == null) {
                         continue;
@@ -638,7 +639,7 @@ public class DOMOutputCapsule implements OutputCapsule {
 
         Element old = currentElement;
         Element el = appendElement(name);
-        el.setAttribute(XMLExporter.ATTRIBUTE_SIZE, String.valueOf(objects.length));
+        XMLUtils.setAttribute(el, XMLExporter.ATTRIBUTE_SIZE, String.valueOf(objects.length));
         for (int i = 0; i < objects.length; i++) {
             ArrayList o = objects[i];
             if(o == null){
@@ -661,7 +662,7 @@ public class DOMOutputCapsule implements OutputCapsule {
 
         Element el = appendElement(name);
         int size = value.length;
-        el.setAttribute(XMLExporter.ATTRIBUTE_SIZE, String.valueOf(size));
+        XMLUtils.setAttribute(el, XMLExporter.ATTRIBUTE_SIZE, String.valueOf(size));
 
         for (int i=0; i< size; i++) {
             ArrayList[] vi = value[i];
@@ -679,7 +680,7 @@ public class DOMOutputCapsule implements OutputCapsule {
             return;
         }
         Element el = appendElement(name);
-        el.setAttribute(XMLExporter.ATTRIBUTE_SIZE, String.valueOf(array.size()));
+        XMLUtils.setAttribute(el, XMLExporter.ATTRIBUTE_SIZE, String.valueOf(array.size()));
         for (FloatBuffer o : array) {
             write(o, XMLExporter.ELEMENT_FLOATBUFFER, null);
         }
@@ -723,7 +724,7 @@ public class DOMOutputCapsule implements OutputCapsule {
                 while(keyIterator.hasNext()) {
                         String key = keyIterator.next();
                         Element mapEntry = appendElement("MapEntry");
-                        mapEntry.setAttribute("key", key);
+                        XMLUtils.setAttribute(mapEntry, "key", key);
                         Savable s = map.get(key);
                         write(s, "Savable", null);
                         currentElement = stringMap;
@@ -745,7 +746,7 @@ public class DOMOutputCapsule implements OutputCapsule {
                 for(Entry<? extends Savable> entry : map) {
                         int key = entry.getKey();
                         Element mapEntry = appendElement("MapEntry");
-                        mapEntry.setAttribute("key", Integer.toString(key));
+                        XMLUtils.setAttribute(mapEntry, "key", Integer.toString(key));
                         Savable s = entry.getValue();
                         write(s, "Savable", null);
                         currentElement = stringMap;
@@ -761,7 +762,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         }
 
         Element el = appendElement(name);
-        el.setAttribute("size", String.valueOf(value.limit()));
+        XMLUtils.setAttribute(el, "size", String.valueOf(value.limit()));
         StringBuilder buf = new StringBuilder();
         int pos = value.position();
         value.rewind();
@@ -784,7 +785,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         }
         
         value.position(pos);
-        el.setAttribute(dataAttributeName, buf.toString());
+        XMLUtils.setAttribute(el, dataAttributeName, buf.toString());
         currentElement = (Element) el.getParentNode();
     }
 
@@ -798,7 +799,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         }
 
         Element el = appendElement(name);
-        el.setAttribute("size", String.valueOf(value.limit()));
+        XMLUtils.setAttribute(el, "size", String.valueOf(value.limit()));
         StringBuilder buf = new StringBuilder();
         int pos = value.position();
         value.rewind();
@@ -820,7 +821,7 @@ public class DOMOutputCapsule implements OutputCapsule {
             buf.setLength(buf.length() - 1);
         }
         value.position(pos);
-        el.setAttribute(dataAttributeName, buf.toString());
+        XMLUtils.setAttribute(el, dataAttributeName, buf.toString());
         currentElement = (Element) el.getParentNode();
     }
 
@@ -830,7 +831,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         if (value.equals(defVal)) return;
 
         Element el = appendElement(name);
-        el.setAttribute("size", String.valueOf(value.limit()));
+        XMLUtils.setAttribute(el, "size", String.valueOf(value.limit()));
         StringBuilder buf = new StringBuilder();
         int pos = value.position();
         value.rewind();
@@ -853,7 +854,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         }
         
         value.position(pos);
-        el.setAttribute(dataAttributeName, buf.toString());
+        XMLUtils.setAttribute(el, dataAttributeName, buf.toString());
         currentElement = (Element) el.getParentNode();
     }
 
@@ -867,7 +868,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         }
 
         Element el = appendElement(name);
-        el.setAttribute("size", String.valueOf(value.limit()));
+        XMLUtils.setAttribute(el, "size", String.valueOf(value.limit()));
         StringBuilder buf = new StringBuilder();
         int pos = value.position();
         value.rewind();
@@ -890,7 +891,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         }
         
         value.position(pos);
-        el.setAttribute(dataAttributeName, buf.toString());
+        XMLUtils.setAttribute(el, dataAttributeName, buf.toString());
         currentElement = (Element) el.getParentNode();
     }
 
@@ -899,7 +900,7 @@ public class DOMOutputCapsule implements OutputCapsule {
         if (value == defVal) {
             return;
         }
-        currentElement.setAttribute(name, String.valueOf(value));
+        XMLUtils.setAttribute(currentElement, name, String.valueOf(value));
 
         }
 
@@ -913,11 +914,12 @@ public class DOMOutputCapsule implements OutputCapsule {
             return;
         }
         Element el = appendElement(name);
-        el.setAttribute("size", String.valueOf(array.size()));
+        XMLUtils.setAttribute(el, "size", String.valueOf(array.size()));
         for (ByteBuffer o : array) {
             write(o, "ByteBuffer", null);
         }
         currentElement = (Element) el.getParentNode();
 
         }
+    
 }

+ 110 - 0
jme3-plugins/src/xml/java/com/jme3/export/xml/XMLUtils.java

@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2009-2021 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.export.xml;
+
+import org.w3c.dom.Element;
+
+/**
+ * Utilities for reading and writing XML files.
+ * 
+ * @author codex
+ */
+public class XMLUtils {
+    
+    /**
+     * Prefix for every jme xml attribute for format versions 3 and up.
+     * <p>
+     * This prefix should be appended at the beginning of every xml
+     * attribute name. For format versions 3 and up, every name to
+     * access an attribute must append this prefix first.
+     */
+    public static final String PREFIX = "jme-";
+    
+    /**
+     * Sets the attribute in the element under the name.
+     * <p>
+     * Automatically appends {@link #PREFIX} to the beginning of the name
+     * before assigning the attribute to the element.
+     * 
+     * @param element element to set the attribute in
+     * @param name name of the attribute (without prefix)
+     * @param attribute attribute to save
+     */
+    public static void setAttribute(Element element, String name, String attribute) {
+        element.setAttribute(PREFIX+name, attribute);
+    }
+    
+    /**
+     * Fetches the named attribute from the element.
+     * <p>
+     * Automatically appends {@link #PREFIX} to the beginning
+     * of the name before looking up the attribute for format versions 3 and up.
+     * 
+     * @param version format version of the xml
+     * @param element XML element to get the attribute from
+     * @param name name of the attribute (without prefix)
+     * @return named attribute
+     */
+    public static String getAttribute(int version, Element element, String name) {
+        if (version >= 3) {
+            return element.getAttribute(PREFIX+name);
+        } else {
+            return element.getAttribute(name);
+        }
+    }
+    
+    /**
+     * Tests if the element contains the named attribute.
+     * <p>
+     * Automatically appends {@link #PREFIX} to the beginning
+     * of the name before looking up the attribute for format versions 3 and up.
+     * 
+     * @param version format version of the xml
+     * @param element element to test
+     * @param name name of the attribute (without prefix)
+     * @return true if the element has the named attribute
+     */
+    public static boolean hasAttribute(int version, Element element, String name) {
+        if (version >= 3) {
+            return element.hasAttribute(PREFIX+name);
+        } else {
+            return element.hasAttribute(name);
+        }
+    }
+    
+    /**
+     * Denies instantiation of this class.
+     */
+    private XMLUtils() {
+    }
+    
+}