diff --git a/catalog/sgprops.py b/catalog/sgprops.py
index f523657..9901154 100644
--- a/catalog/sgprops.py
+++ b/catalog/sgprops.py
@@ -144,6 +144,22 @@ class Node(object):
return n;
+class ParseState:
+ def __init__(self):
+ self._counters = {}
+
+ def getNextIndex(self, name):
+ if name in self._counters:
+ self._counters[name] += 1
+ else:
+ self._counters[name] = 0
+ return self._counters[name]
+
+ def recordExplicitIndex(self, name, index):
+ if not name in self._counters:
+ self._counters[name] = index
+ else:
+ self._counters[name] = max(self._counters[name], index)
class PropsHandler(handler.ContentHandler):
def __init__(self, root = None, path = None, includePaths = []):
@@ -152,6 +168,7 @@ class PropsHandler(handler.ContentHandler):
self._basePath = os.path.dirname(path)
self._includes = includePaths
self._locator = None
+ self._stateStack = [ParseState()]
if root is None:
# make a nameless root node
@@ -163,25 +180,31 @@ class PropsHandler(handler.ContentHandler):
def startElement(self, name, attrs):
self._content = None
- if 'include' in attrs.keys():
- self.handleInclude(attrs['include'])
-
if (name == 'PropertyList'):
+ # still need to handle includes on the root element
+ if 'include' in attrs.keys():
+ self.handleInclude(attrs['include'])
return
-
+
+ currentState = self._stateStack[-1]
if 'n' in attrs.keys():
try:
index = int(attrs['n'])
except:
- print "Invalid index at line:", self._locator.getLineNumber(), "of", self._path
- self._current = self._current.addChild(name)
- return
+ raise IndexError("Invalid index at line:", self._locator.getLineNumber(), "of", self._path)
+ currentState.recordExplicitIndex(name, index)
self._current = self._current.getChild(name, index, create=True)
else:
+ index = currentState.getNextIndex(name)
# important we use getChild here, so that includes are resolved
# correctly
- self._current = self._current.getChild(name, create=True)
+ self._current = self._current.getChild(name, index, create=True)
+
+ self._stateStack.append(ParseState())
+
+ if 'include' in attrs.keys():
+ self.handleInclude(attrs['include'])
self._currentTy = None;
if 'type' in attrs.keys():
@@ -229,6 +252,7 @@ class PropsHandler(handler.ContentHandler):
self._current = self._current.parent
self._content = None
self._currentTy = None
+ self._stateStack.pop()
def parsePropsBool(self, content):
diff --git a/catalog/testData/bad-index.xml b/catalog/testData/bad-index.xml
new file mode 100644
index 0000000..1c7650d
--- /dev/null
+++ b/catalog/testData/bad-index.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+ 225000
+
+
+
+
diff --git a/catalog/testData/props1.xml b/catalog/testData/props1.xml
new file mode 100644
index 0000000..b9cda22
--- /dev/null
+++ b/catalog/testData/props1.xml
@@ -0,0 +1,25 @@
+
+
+ 42
+
+
+ apple
+
+
+
+ lemon
+
+
+
+ pear
+
+
+
+
+ a
+ b
+ c
+ d
+
+
+
diff --git a/catalog/testData/props2.xml b/catalog/testData/props2.xml
new file mode 100644
index 0000000..a7289e2
--- /dev/null
+++ b/catalog/testData/props2.xml
@@ -0,0 +1,20 @@
+
+
+ 33
+
+
+ apple
+
+
+
+ lemon
+
+
+
+
+
+ 99
+
+
+
+
diff --git a/catalog/testData/root-include.xml b/catalog/testData/root-include.xml
new file mode 100644
index 0000000..71ff5f2
--- /dev/null
+++ b/catalog/testData/root-include.xml
@@ -0,0 +1,22 @@
+
+
+ 42
+ 43
+ 44
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/testData/sub-include.xml b/catalog/testData/sub-include.xml
new file mode 100644
index 0000000..d330f67
--- /dev/null
+++ b/catalog/testData/sub-include.xml
@@ -0,0 +1,6 @@
+
+
+ 42
+ 43
+ 44
+
diff --git a/catalog/test_sgprops.py b/catalog/test_sgprops.py
new file mode 100755
index 0000000..f731717
--- /dev/null
+++ b/catalog/test_sgprops.py
@@ -0,0 +1,61 @@
+import unittest
+
+import types
+import sgprops
+
+class SGProps(unittest.TestCase):
+
+ def test_parse(self):
+ parsed = sgprops.readProps("testData/props1.xml")
+
+ self.assertEqual(parsed.getValue("value"), 42)
+ self.assertEqual(type(parsed.getValue("value")), types.IntType)
+
+ valNode = parsed.getChild("value")
+ self.assertEqual(valNode.parent, parsed)
+ self.assertEqual(valNode.name, "value")
+
+ self.assertEqual(valNode.value, 42)
+ self.assertEqual(type(valNode.value), types.IntType)
+
+ with self.assertRaises(IndexError):
+ missingNode = parsed.getChild("missing")
+
+ things = parsed.getChildren("thing")
+ self.assertEqual(len(things), 3)
+
+ self.assertEqual(things[0], parsed.getChild("thing", 0));
+ self.assertEqual(things[1], parsed.getChild("thing", 1));
+ self.assertEqual(things[2], parsed.getChild("thing", 2));
+
+ self.assertEqual(things[0].getValue("value"), "apple");
+ self.assertEqual(things[1].getValue("value"), "lemon");
+ self.assertEqual(things[2].getValue("value"), "pear");
+
+ def test_create(self):
+ pass
+
+
+ def test_invalidIndex(self):
+ with self.assertRaises(IndexError):
+ parsed = sgprops.readProps("testData/bad-index.xml")
+
+ def test_include(self):
+ parsed = sgprops.readProps("testData/props2.xml")
+
+ # test that value in main file over-rides the one in the include
+ self.assertEqual(parsed.getValue("value"), 33)
+
+ # but these come from the included file
+ self.assertEqual(parsed.getValue("value[1]"), 43)
+ self.assertEqual(parsed.getValue("value[2]"), 44)
+
+ subNode = parsed.getChild("sub")
+ widgets = subNode.getChildren("widget")
+ self.assertEqual(len(widgets), 4)
+
+ self.assertEqual(widgets[2].value, 44)
+ self.assertEqual(widgets[3].value, 99)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/catalog/update-catalog.py b/catalog/update-catalog.py
index 8b9ce22..bc1c52c 100755
--- a/catalog/update-catalog.py
+++ b/catalog/update-catalog.py
@@ -10,6 +10,7 @@ import shutil
import subprocess
import time
import sgprops
+import sys
CATALOG_VERSION = 4
@@ -125,7 +126,7 @@ def scan_aircraft_dir(aircraft_dir):
if d == None:
continue
except:
- print "Skipping set file since couldn't be parsed:", os.path.join(aircraft_dir, file)
+ print "Skipping set file since couldn't be parsed:", os.path.join(aircraft_dir, file), sys.exc_info()[0]
continue
setDicts.append(d)