[ooo-build-commit] 2 commits - scratch/mso-dumper

Kohei Yoshida kohei at kemper.freedesktop.org
Mon Jan 4 10:27:36 PST 2010


 scratch/mso-dumper/src/formula.py   |   72 +++++++++++++---------
 scratch/mso-dumper/src/xlsmodel.py  |  113 ++++++++++++++++++++++++++++++++----
 scratch/mso-dumper/src/xlsrecord.py |   30 ++++++++-
 3 files changed, 171 insertions(+), 44 deletions(-)

New commits:
commit eb0b331416df0d9e27f600d2e6f312f9bbd53f22
Author: Kohei Yoshida <kyoshida at novell.com>
Date:   Mon Jan 4 13:25:19 2010 -0500

    [xls-dump] Display active autofilter info in cXML mode.
    
    The actual autofilter state can be complex, but let's just handle
    simple string equality cases for now.
    
    * scratch/mso-dumper/src/xlsmodel.py:
    * scratch/mso-dumper/src/xlsrecord.py:

diff --git a/scratch/mso-dumper/src/xlsmodel.py b/scratch/mso-dumper/src/xlsmodel.py
index 51e2356..17fe776 100644
--- a/scratch/mso-dumper/src/xlsmodel.py
+++ b/scratch/mso-dumper/src/xlsmodel.py
@@ -176,6 +176,9 @@ class Worksheet(SheetBase):
         # Swap with the new and empty list.
         self.__autoFilterArrows = arrows
 
+    def setAutoFilterArrow (self, filterID, obj):
+        self.__autoFilterArrows[filterID] = obj
+
     def setCell (self, col, row, cell):
         if not self.__rows.has_key(row):
             self.__rows[row] = {}
@@ -206,6 +209,7 @@ class Worksheet(SheetBase):
 
     def __appendAutoFilterNode (self, wb, baseNode):
         if len(self.__autoFilterArrows) <= 0:
+            # No autofilter in this sheet.
             return
 
         wbg = wb.getWorkbookGlobal()
@@ -214,19 +218,24 @@ class Worksheet(SheetBase):
         parser.parse()
         tokens = parser.getTokens()
         if len(tokens) != 1 or tokens[0].tokenType != formula.TokenType.Area3d:
+            # We assume that the database range only has one range token, otherwise
+            # we bail out.
             return
 
-
         tk = tokens[0]
         cellRange = tk.cellRange
 
         elem = baseNode.appendElement('autofilter')
         elem.setAttr('range', "(col=%d,row=%d)-(col=%d,row=%d)"%(cellRange.firstCol, cellRange.firstRow, cellRange.lastCol, cellRange.lastRow))
 
-        for col in xrange(cellRange.firstCol, cellRange.lastCol+1):
-            arrow = elem.appendElement('arrow')
-            arrow.setAttr('col', col)
-            arrow.setAttr('row', cellRange.firstRow)
+        for i in xrange(0, len(self.__autoFilterArrows)):
+            arrowObj = self.__autoFilterArrows[i]
+            if arrowObj == None:
+                arrow = elem.appendElement('arrow')
+                arrow.setAttr('pos', "(col=%d,row=%d)"%(cellRange.firstCol+i,cellRange.firstRow))
+            else:
+                elem.appendChild(arrowObj.createDOM(wb, cellRange))
+
 
 class CellBase(object):
 
@@ -276,3 +285,26 @@ class FormulaCell(CellBase):
             s = globals.getRawBytes(self.tokens, True, False)
             nd.setAttr('token-bytes', s)
         return nd
+
+
+class AutoFilterArrow(object):
+
+    def __init__ (self, filterID):
+        self.filterID = filterID
+        self.isActive = False
+        self.equalString1 = None
+        self.equalString2 = None
+
+    def createDOM (self, wb, filterRange):
+        nd = node.Element('arrow')
+        col = self.filterID + filterRange.firstCol
+        row = filterRange.firstRow
+        nd.setAttr('pos', "(col=%d,row=%d)"%(col,row))
+        nd.setAttr('active', self.isActive)
+        eqStr = ''
+        if self.equalString1 != None:
+            eqStr = self.equalString1
+        if self.equalString2 != None:
+            eqStr += ',' + self.equalString2
+        nd.setAttr('equals', eqStr)
+        return nd
diff --git a/scratch/mso-dumper/src/xlsrecord.py b/scratch/mso-dumper/src/xlsrecord.py
index dc79c76..9dc3a25 100644
--- a/scratch/mso-dumper/src/xlsrecord.py
+++ b/scratch/mso-dumper/src/xlsrecord.py
@@ -316,6 +316,15 @@ class Autofilter(BaseRecordHandler):
 
     def fillModel (self, model):
         self.__parseBytes()
+        sh = model.getCurrentSheet()
+        obj = xlsmodel.AutoFilterArrow(self.filterIndex)
+        obj.isActive = True
+        if self.simple1:
+            obj.equalString1 = self.string1
+
+        if self.simple2:
+            obj.equalString1 = self.string2
+        sh.setAutoFilterArrow(self.filterIndex, obj)
 
 
 class BOF(BaseRecordHandler):
commit b665c298b05e1839420358027d96e98ff16df1d3
Author: Kohei Yoshida <kyoshida at novell.com>
Date:   Mon Jan 4 12:54:34 2010 -0500

    [xls-dump] More work on handling autofilter in cXML (canonical XML) mode.
    
    Pick up necessary bits from the BIFF parser, and display autofilter range
    and arrow positions in cXML structure.
    
    * scratch/mso-dumper/src/formula.py:
    * scratch/mso-dumper/src/xlsmodel.py:
    * scratch/mso-dumper/src/xlsrecord.py:

diff --git a/scratch/mso-dumper/src/formula.py b/scratch/mso-dumper/src/formula.py
index 42fd82c..7f422d3 100644
--- a/scratch/mso-dumper/src/formula.py
+++ b/scratch/mso-dumper/src/formula.py
@@ -345,42 +345,51 @@ which is usually the first 2 bytes.
     def getText (self):
         return self.text
 
-class FormulaParser2(object):
-    """This is a new formula parser that will eventually replace the old one.
+# ============================================================================
 
-Once replaced, I'll change the name to FormulaParser and the names of the 
-nested classes will be without the leading underscore (_)."""
+class TokenType:
+    Area3d = 0
+    Unknown = 9999
 
-    class _TokenBase(object):
-        def __init__ (self, strm, opcode1, opcode2=None):
-            self.opcode1 = opcode1
-            self.opcode2 = opcode2
-            self.strm = strm
+class _TokenBase(object):
+    def __init__ (self, strm, opcode1, opcode2=None):
+        self.opcode1 = opcode1
+        self.opcode2 = opcode2
+        self.strm = strm
+        self.tokenType = TokenType.Unknown
 
-        def parse (self):
-            self.parseBytes()
-            self.strm = None # no need to hold reference to the stream.
+    def parse (self):
+        self.parseBytes()
+        self.strm = None # no need to hold reference to the stream.
 
-        def parseBytes (self):
-            # derived class should overwrite this method.
-            pass
+    def parseBytes (self):
+        # derived class should overwrite this method.
+        pass
 
-        def getText (self):
-            return ''
+    def getText (self):
+        return ''
 
-    class _Area3d(_TokenBase):
-        def parseBytes (self):
-            self.xti = self.strm.readUnsignedInt(2)
-            self.cellRange = parseCellRangeAddress(self.strm.readBytes(8))
+class _Area3d(_TokenBase):
+    def parseBytes (self):
+        self.xti = self.strm.readUnsignedInt(2)
+        self.cellRange = parseCellRangeAddress(self.strm.readBytes(8))
+        self.tokenType = TokenType.Area3d
 
-        def getText (self):
-            return "(xti=%d,"%self.xti + self.cellRange.getName() + ")"
+    def getText (self):
+        return "(xti=%d,"%self.xti + self.cellRange.getName() + ")"
+
+_tokenMap = {
+    0x3B: _Area3d,
+    0x5B: _Area3d,
+    0x7B: _Area3d
+}
+
+class FormulaParser2(object):
+    """This is a new formula parser that will eventually replace the old one.
+
+Once replaced, I'll change the name to FormulaParser and the names of the 
+associated token classes will be without the leading underscore (_)."""
 
-    tokenMap = {
-        0x3B: _Area3d,
-        0x5B: _Area3d,
-        0x7B: _Area3d
-    }
 
     def __init__ (self, header, bytes, sizeField=True):
         self.header = header
@@ -395,10 +404,10 @@ nested classes will be without the leading underscore (_)."""
     def parse (self):
         while not self.strm.isEndOfRecord():
             b = self.strm.readUnsignedInt(1)
-            if not FormulaParser2.tokenMap.has_key(b):
+            if not _tokenMap.has_key(b):
                 return
 
-            token = FormulaParser2.tokenMap[b](self.strm, b)
+            token = _tokenMap[b](self.strm, b)
             token.parse()
             self.tokens.append(token)
 
@@ -407,3 +416,6 @@ nested classes will be without the leading underscore (_)."""
         for tk in self.tokens:
             s += tk.getText()
         return s
+
+    def getTokens (self):
+        return self.tokens
diff --git a/scratch/mso-dumper/src/xlsmodel.py b/scratch/mso-dumper/src/xlsmodel.py
index d1b8a78..51e2356 100644
--- a/scratch/mso-dumper/src/xlsmodel.py
+++ b/scratch/mso-dumper/src/xlsmodel.py
@@ -1,5 +1,5 @@
 
-import globals, node
+import globals, node, formula
 
 
 class ModelType:
@@ -24,10 +24,11 @@ class Workbook(ModelBase):
         self.__sheets = []
 
     def appendSheet (self):
-        if len(self.__sheets) == 0:
+        n = len(self.__sheets)
+        if n == 0:
             self.__sheets.append(WorkbookGlobal())
         else:
-            self.__sheets.append(Worksheet())
+            self.__sheets.append(Worksheet(n-1))
 
         return self.getCurrentSheet()
 
@@ -112,6 +113,8 @@ class WorkbookGlobal(SheetBase):
         self.__sheetData = []
         self.__sharedStrings = []
         self.__supbooks = []
+        self.__externSheets = []  # tuple (book ID, sheet begin ID, sheet end ID)
+        self.__dbRanges = {}      # key: sheet ID (0-based), value: range tokens
 
     def createDOM (self, wb):
         nd = node.Element('workbook-global')
@@ -139,35 +142,91 @@ class WorkbookGlobal(SheetBase):
             return None
         return self.__supbooks[sbID]
 
+    def appendExternSheet (self, bookID, sheetBeginID, sheetEndID):
+        self.__externSheets.append((bookID, sheetBeginID, sheetEndID))
+
+    def getExternSheet (self, xtiID):
+        if len(self.__externSheets) <= xtiID:
+            return None
+        return self.__externSheets[xtiID]
+
+    def setFilterRange (self, sheetID, tokens):
+        self.__dbRanges[sheetID] = tokens
+
+    def getFilterRange (self, sheetID):
+        if not self.__dbRanges.has_key(sheetID):
+            return None
+
+        return self.__dbRanges[sheetID]
+
 
 class Worksheet(SheetBase):
 
-    def __init__ (self):
+    def __init__ (self, sheetID):
         SheetBase.__init__(self, SheetBase.Type.Worksheet)
-        self.rows = {}
+        self.__rows = {}
+        self.__autoFilterArrows = []
+        self.sheetID = sheetID
+
+    def setAutoFilterArrowSize (self, arrowSize):
+        arrows = []
+        for i in xrange(0, arrowSize):
+            arrows.append(None)
+
+        # Swap with the new and empty list.
+        self.__autoFilterArrows = arrows
 
     def setCell (self, col, row, cell):
-        if not self.rows.has_key(row):
-            self.rows[row] = {}
+        if not self.__rows.has_key(row):
+            self.__rows[row] = {}
 
-        self.rows[row][col] = cell
+        self.__rows[row][col] = cell
 
     def createDOM (self, wb):
         nd = node.Element('worksheet')
         nd.setAttr('version', self.version)
-        rows = self.rows.keys()
+
+        # cells
+        rows = self.__rows.keys()
         rows.sort()
         for row in rows:
             rowNode = nd.appendElement('row')
             rowNode.setAttr('id', row)
-            cols = self.rows[row].keys()
+            cols = self.__rows[row].keys()
             for col in cols:
-                cell = self.rows[row][col]
+                cell = self.__rows[row][col]
                 cellNode = cell.createDOM(wb)
                 rowNode.appendChild(cellNode)
                 cellNode.setAttr('col', col)
+
+        # autofilter (if exists)
+        self.__appendAutoFilterNode(wb, nd)
+
         return nd
 
+    def __appendAutoFilterNode (self, wb, baseNode):
+        if len(self.__autoFilterArrows) <= 0:
+            return
+
+        wbg = wb.getWorkbookGlobal()
+        tokens = wbg.getFilterRange(self.sheetID)
+        parser = formula.FormulaParser2(None, tokens, False)
+        parser.parse()
+        tokens = parser.getTokens()
+        if len(tokens) != 1 or tokens[0].tokenType != formula.TokenType.Area3d:
+            return
+
+
+        tk = tokens[0]
+        cellRange = tk.cellRange
+
+        elem = baseNode.appendElement('autofilter')
+        elem.setAttr('range', "(col=%d,row=%d)-(col=%d,row=%d)"%(cellRange.firstCol, cellRange.firstRow, cellRange.lastCol, cellRange.lastRow))
+
+        for col in xrange(cellRange.firstCol, cellRange.lastCol+1):
+            arrow = elem.appendElement('arrow')
+            arrow.setAttr('col', col)
+            arrow.setAttr('row', cellRange.firstRow)
 
 class CellBase(object):
 
diff --git a/scratch/mso-dumper/src/xlsrecord.py b/scratch/mso-dumper/src/xlsrecord.py
index c07aaba..dc79c76 100644
--- a/scratch/mso-dumper/src/xlsrecord.py
+++ b/scratch/mso-dumper/src/xlsrecord.py
@@ -122,9 +122,18 @@ Like parseBytes(), the derived classes must overwrite this method."""
 
 class AutofilterInfo(BaseRecordHandler):
 
+    def __parseBytes (self):
+        self.arrowCount = self.readUnsignedInt(2)
+
     def parseBytes (self):
-        arrowCount = self.readUnsignedInt(2)
-        self.appendLine("number of autofilter arrows: %d"%arrowCount)
+        self.__parseBytes()
+        self.appendLine("number of autofilter arrows: %d"%self.arrowCount)
+
+    def fillModel (self, model):
+        self.__parseBytes()
+        sh = model.getCurrentSheet()
+        sh.setAutoFilterArrowSize(self.arrowCount)
+
 
 
 class Autofilter(BaseRecordHandler):
@@ -1063,8 +1072,11 @@ class Name(BaseRecordHandler):
 
     def fillModel (self, model):
         self.__parseBytes()
-        
 
+        wbg = model.getWorkbookGlobal()
+        if self.isBuiltinName and len(self.name) == 1 and ord(self.name[0]) == 0x0D:
+            # Pick up a database range for autofilter.
+            wbg.setFilterRange(self.sheetId-1, self.tokenBytes)
 
 
 class SupBook(BaseRecordHandler):
@@ -1144,6 +1156,9 @@ class ExternSheet(BaseRecordHandler):
 
     def fillModel (self, model):
         self.__parseBytes()
+        wbg = model.getWorkbookGlobal()
+        for sh in self.sheets:
+            wbg.appendExternSheet(sh[0], sh[1], sh[2])
 
 
 class ExternName(BaseRecordHandler):


More information about the ooo-build-commit mailing list