From 62d60253d50841b8628b1565ed8326b7d37fc1ce Mon Sep 17 00:00:00 2001
From: Georgy Litvinov <git@litvinovg.pro>
Date: Mon, 9 Mar 2020 11:36:28 +0100
Subject: [PATCH] Extracted list parser

---
 .../java/writer2latex/xhtml/Converter.java    |   7 +
 src/main/java/writer2latex/xhtml/Parser.java  |   4 +
 .../xhtml/content/ListParser.java             | 369 ++++++++++++++++
 .../xhtml/content/TextParser.java             | 412 +-----------------
 4 files changed, 391 insertions(+), 401 deletions(-)
 create mode 100644 src/main/java/writer2latex/xhtml/content/ListParser.java

diff --git a/src/main/java/writer2latex/xhtml/Converter.java b/src/main/java/writer2latex/xhtml/Converter.java
index b79fdb9..10c8397 100644
--- a/src/main/java/writer2latex/xhtml/Converter.java
+++ b/src/main/java/writer2latex/xhtml/Converter.java
@@ -57,6 +57,7 @@ import writer2latex.office.XMLString;
 import writer2latex.util.ExportNameCollection;
 import writer2latex.util.Misc;
 import writer2latex.xhtml.content.DrawParser;
+import writer2latex.xhtml.content.ListParser;
 import writer2latex.xhtml.content.MathParser;
 import writer2latex.xhtml.content.PageContainer;
 import writer2latex.xhtml.content.TableParser;
@@ -86,6 +87,7 @@ public class Converter extends BasicConverter {
     // The helpers
     private Styles styles;
     private TextParser textParser;
+    private ListParser listParser;
     private TableParser tableParser;
     private DrawParser drawParser;
     private MathParser mathParser;
@@ -192,6 +194,8 @@ public class Converter extends BasicConverter {
     public Styles getStylesParser() { return styles; }
 	
     public TextParser getTextParser() { return textParser; }
+    
+		public ListParser getListParser() {		return listParser;	}
 	
     public TableParser getTableParser() { return tableParser; }
 
@@ -276,6 +280,7 @@ public class Converter extends BasicConverter {
 
         styles = new Styles(ofr,config,this,nType);
         textParser = new TextParser(ofr,config,this);
+        listParser = new ListParser(ofr,config,this);
         tableParser = new TableParser(ofr,config,this);
         drawParser = new DrawParser(ofr,config,this);
         mathParser = new MathParser(ofr,config,this,nType!=XhtmlDocument.XHTML10 && nType!=XhtmlDocument.XHTML11);
@@ -837,6 +842,8 @@ public class Converter extends BasicConverter {
         meta.setAttribute("content",sValue);
         head.appendChild(meta);
     }
+
+
 	
 		
 }
\ No newline at end of file
diff --git a/src/main/java/writer2latex/xhtml/Parser.java b/src/main/java/writer2latex/xhtml/Parser.java
index 1d2482b..bdc8728 100644
--- a/src/main/java/writer2latex/xhtml/Parser.java
+++ b/src/main/java/writer2latex/xhtml/Parser.java
@@ -29,6 +29,7 @@ import org.w3c.dom.Element;
 
 import writer2latex.office.OfficeReader;
 import writer2latex.xhtml.content.DrawParser;
+import writer2latex.xhtml.content.ListParser;
 import writer2latex.xhtml.content.MathParser;
 import writer2latex.xhtml.content.TableParser;
 import writer2latex.xhtml.content.TextParser;
@@ -93,6 +94,9 @@ public class Parser {
     protected PageStyleParser getPageSc() { return converter.getStylesParser().getPageSc(); }
     
     protected TextParser getTextCv() { return converter.getTextParser(); }
+    
+    protected ListParser getListParser() { return converter.getListParser(); }
+
 	
     protected TableParser getTableCv() { return converter.getTableParser(); }
 
diff --git a/src/main/java/writer2latex/xhtml/content/ListParser.java b/src/main/java/writer2latex/xhtml/content/ListParser.java
new file mode 100644
index 0000000..d4c00a5
--- /dev/null
+++ b/src/main/java/writer2latex/xhtml/content/ListParser.java
@@ -0,0 +1,369 @@
+package writer2latex.xhtml.content;
+
+import static writer2latex.office.XMLString.TEXT_CONTINUE_NUMBERING;
+import static writer2latex.office.XMLString.TEXT_H;
+import static writer2latex.office.XMLString.TEXT_LIST;
+import static writer2latex.office.XMLString.TEXT_LIST_HEADER;
+import static writer2latex.office.XMLString.TEXT_LIST_ITEM;
+import static writer2latex.office.XMLString.TEXT_ORDERED_LIST;
+import static writer2latex.office.XMLString.TEXT_P;
+import static writer2latex.office.XMLString.TEXT_RESTART_NUMBERING;
+import static writer2latex.office.XMLString.TEXT_START_VALUE;
+import static writer2latex.office.XMLString.TEXT_STYLE_NAME;
+import static writer2latex.office.XMLString.TEXT_UNORDERED_LIST;
+
+import java.util.Hashtable;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import writer2latex.office.ListCounter;
+import writer2latex.office.ListStyle;
+import writer2latex.office.OfficeReader;
+import writer2latex.util.Misc;
+import writer2latex.xhtml.Converter;
+import writer2latex.xhtml.Parser;
+import writer2latex.xhtml.StyleInfo;
+import writer2latex.xhtml.XhtmlConfig;
+
+public class ListParser extends Parser {
+
+	private Hashtable<String, ListCounter> listCounters = new Hashtable<String, ListCounter>();
+	protected String sCurrentListLabel = null;
+	protected ListStyle currentListStyle = null;
+	protected int nCurrentListLevel = 0;
+  ListCounter outlineNumbering;
+
+
+	public ListParser(OfficeReader ofr, XhtmlConfig config, Converter converter) {
+		super(ofr, config, converter);
+    outlineNumbering = new ListCounter(ofr.getOutlineStyle());
+	}
+
+	protected ListCounter getListCounter(ListStyle style) {
+	    if (style==ofr.getOutlineStyle()) {
+	        // Outline numbering has a special counter
+	        return outlineNumbering;
+	    }
+	    else if (style!=null) {
+	        // Get existing or create new counter
+	        if (listCounters.containsKey(style.getName())) {
+	            return listCounters.get(style.getName());
+	        }
+	        else {
+	            ListCounter counter = new ListCounter(style);
+	            listCounters.put(style.getName(),counter);
+	            return counter;
+	        }
+	    }
+	    else {
+	        // No style, return a dummy
+	        return new ListCounter();
+	    }
+	}
+
+	protected void insertListLabel(ListStyle style, int nLevel, String sDefaultStyle, String sPrefix, String sLabel, Element hnode) {
+	    if (sLabel!=null && sLabel.length()>0) {
+	    	if (sPrefix!=null) {
+	    		Element prefix = converter.createElement("span");
+	    		prefix.setAttribute("class", "chapter-name");
+	    		hnode.appendChild(prefix);
+	    		prefix.appendChild( converter.createTextNode(sPrefix));
+	    	}
+	        StyleInfo info = new StyleInfo();
+	        if (style!=null) {
+	            String sTextStyleName = style.getLevelProperty(nLevel,TEXT_STYLE_NAME);
+	            getTextSP().readStyle(sTextStyleName, info);
+	        }
+	
+	        if (info.sTagName==null) { info.sTagName = "span"; }
+	        if (info.sClass==null) { info.sClass = sDefaultStyle; }
+	
+	        Element content = converter.createElement(info.sTagName);
+	        getTextSP().writeStyle(info, content);
+	        hnode.appendChild(content);
+	        content.appendChild( converter.createTextNode(sLabel) );
+	    }
+	}
+
+	private boolean hasItems(Node onode) {
+	    Node child = onode.getFirstChild();
+	    while (child!=null) {
+	        if (Misc.isElement(child,TEXT_LIST_ITEM) ||
+	            Misc.isElement(child,TEXT_LIST_HEADER)) {
+	            return true;
+	        }
+	        child = child.getNextSibling();
+	    }
+	    return false;
+	}
+
+	private void handleOL(Node onode, int nLevel, String sStyleName, Node hnode) {
+	    if (hasItems(onode)) {
+	        // add an OL element
+	        Element list = converter.createElement("ol");
+	        StyleInfo listInfo = new StyleInfo();
+	        getListSc().applyStyle(nLevel,sStyleName,listInfo);
+	        getListSc().readParentStyle(nLevel, sStyleName, listInfo);
+	        writeStyle(listInfo,list);
+	        hnode.appendChild(list);
+	        traverseList(onode,nLevel,sStyleName,list);
+	    }
+	}
+
+	private void handleUL(Node onode, int nLevel, String sStyleName, Node hnode) {
+	    if (hasItems(onode)) {
+	        // add an UL element
+	        Element list = converter.createElement("ul");
+	        StyleInfo listInfo = new StyleInfo();
+	        getListSc().applyStyle(nLevel,sStyleName,listInfo);
+	        writeStyle(listInfo,list);
+	        hnode.appendChild(list);
+	        traverseList(onode,nLevel,sStyleName,list);
+	    }
+	}
+
+	protected void handleList(Node onode, int nLevel, String sStyleName, Node hnode) {
+	    // In OpenDocument, we should use the style to determine the type of list
+	    String sStyleName1 = Misc.getAttribute(onode,TEXT_STYLE_NAME);
+	    if (sStyleName1!=null) { sStyleName = sStyleName1; }
+	    ListStyle style = ofr.getListStyle(sStyleName);
+	    if (style!=null && style.isNumber(nLevel)) {
+	        handleOL(onode,nLevel,sStyleName,hnode);
+	    }
+	    else {
+	        handleUL(onode,nLevel,sStyleName,hnode);
+	    }
+	}
+
+	private void traverseList(Node onode, int nLevel, String styleName, Element hnode) {
+	    ListCounter counter = getListCounter(ofr.getListStyle(styleName));
+	
+	    // Restart numbering, if required
+	    //if (counter!=null) {
+	    boolean bContinueNumbering = "true".equals(Misc.getAttribute(onode,TEXT_CONTINUE_NUMBERING));
+	    if (!bContinueNumbering && counter!=null) {
+	        counter.restart(nLevel);
+	    }
+	    if (config.listFormatting()==XhtmlConfig.CSS1_HACK && counter.getValue(nLevel)>0) {
+	        hnode.setAttribute("start",Integer.toString(counter.getValue(nLevel)+1));                	
+	    }
+	    //}
+	
+	    if (onode.hasChildNodes()) {
+	        NodeList nList = onode.getChildNodes();
+	        int len = nList.getLength();
+	        
+	        for (int i = 0; i < len; i++) {
+	            Node child = nList.item(i);
+	            
+	            if (child.getNodeType() == Node.ELEMENT_NODE) {
+	                String nodeName = child.getNodeName();
+	                
+	                if (nodeName.equals(TEXT_LIST_ITEM)) {
+	                    // Check to see if first child is a new list
+	                    boolean bIsImmediateNestedList = false;
+	                    Element child1 = Misc.getFirstChildElement(child);
+	                    if (child1.getTagName().equals(TEXT_ORDERED_LIST) || // old
+	                        child1.getTagName().equals(TEXT_UNORDERED_LIST) || // old
+	                        child1.getTagName().equals(TEXT_LIST)) { // oasis
+	                        bIsImmediateNestedList = true;
+	                    }
+	
+	                    if (config.listFormatting()==XhtmlConfig.CSS1_HACK && bIsImmediateNestedList) {
+	                        traverseListItem(child,nLevel,styleName,hnode);
+	                    }
+	                    else {
+	                        // add an li element
+	                    	//if (counter!=null) {
+	                    	sCurrentListLabel = counter.step(nLevel).getLabel();
+	                    	//}
+	                        currentListStyle = ofr.getListStyle(styleName);
+	                        nCurrentListLevel = nLevel;
+	                        Element item = converter.createElement("li");
+	                        StyleInfo info = new StyleInfo();
+	                        getPresentationSc().applyOutlineStyle(nLevel,info);
+	                        writeStyle(info,item);
+	                        hnode.appendChild(item);
+	                        if (config.listFormatting()==XhtmlConfig.CSS1_HACK) {
+	                            boolean bRestart = "true".equals(Misc.getAttribute(child,
+	                                TEXT_RESTART_NUMBERING));
+	                            int nStartValue = Misc.getPosInteger(Misc.getAttribute(child,
+	                                TEXT_START_VALUE),1);
+	                            if (bRestart) {
+	                                item.setAttribute("value",Integer.toString(nStartValue));
+	                                //if (counter!=null) {
+	                                sCurrentListLabel = counter.restart(nLevel,nStartValue).getLabel();
+	                                //}
+	                            }
+	                        }
+	                        traverseListItem(child,nLevel,styleName,item);
+	                    }
+	                }
+	                if (nodeName.equals(TEXT_LIST_HEADER)) {
+	                    // add an li element
+	                    Element item = converter.createElement("li");
+	                    hnode.appendChild(item);
+	                    item.setAttribute("style","list-style-type:none");
+	                    traverseListItem(child,nLevel,styleName,item);
+	                }
+	            }
+	        }
+	    }
+	}
+
+	private void traverseListItem(Node onode, int nLevel, String styleName, Node hnode) { 
+	    // First check if we have a single paragraph to be omitted
+	    // This should happen if we ignore styles and have no style-map
+	    // for the paragraph style used        
+	    if (config.xhtmlFormatting()!=XhtmlConfig.CONVERT_ALL && onode.hasChildNodes()) {
+	        NodeList list = onode.getChildNodes();
+	        int nLen = list.getLength();
+	        int nParCount = 0;
+	        boolean bNoPTag = true;
+	        for (int i=0; i<nLen; i++) {
+	            if (list.item(i).getNodeType()==Node.ELEMENT_NODE) {
+	                if (list.item(i).getNodeName().equals(TEXT_P)) {
+	                    nParCount++;
+	                    if (bNoPTag) {
+	                        String sDisplayName = ofr.getParStyles().getDisplayName(Misc.getAttribute(list.item(0),TEXT_STYLE_NAME));
+	                        if (config.getXParStyleMap().contains(sDisplayName)) {
+	                            bNoPTag = false;
+	                        }
+	                    }
+	                }
+	                else { // found non-text:p element
+	                    bNoPTag=false;
+	                }
+	            }
+	        }
+	        if (bNoPTag && nParCount<=1) {
+	            // traverse the list
+	            for (int i = 0; i < nLen; i++) {
+	                Node child = list.item(i);
+	      
+	                if (child.getNodeType() == Node.ELEMENT_NODE) {
+	                    String nodeName = child.getNodeName();
+	                
+	                    if (nodeName.equals(TEXT_P)) {
+	                    	getTextCv().traverseInlineText(child,hnode);
+	                    }
+	                    if (nodeName.equals(TEXT_LIST)) { // oasis
+	                        handleList(child,nLevel+1,styleName,hnode);
+	                    }
+	                    if (nodeName.equals(TEXT_ORDERED_LIST)) { // old
+	                        handleOL(child,nLevel+1,styleName,hnode);
+	                    }
+	                    if (nodeName.equals(TEXT_UNORDERED_LIST)) { // old
+	                        handleUL(child,nLevel+1,styleName,hnode);
+	                    }
+	                }
+	            }
+	            return;
+	        }
+	    }
+	    // Still here? - traverse block text as usual!
+	    getTextCv().parseText(onode,nLevel,styleName,hnode);
+	}
+
+	protected boolean listIsOnlyHeadings(Node node) {
+	    Node child = node.getFirstChild();
+	    while (child!=null) {
+	        if (child.getNodeType() == Node.ELEMENT_NODE) {
+	            String nodeName = child.getNodeName();
+	            if (nodeName.equals(TEXT_LIST_ITEM)) {
+	                if (!itemIsOnlyHeadings(child)) return false;
+	            }
+	            else if (nodeName.equals(TEXT_LIST_HEADER)) {
+	                if (!itemIsOnlyHeadings(child)) return false;
+	            }
+	        }
+	        child = child.getNextSibling();
+	    }
+	    return true;
+	}
+
+	private boolean itemIsOnlyHeadings(Node node) {
+	    Node child = node.getFirstChild();
+	    while (child!=null) {
+	        if (child.getNodeType() == Node.ELEMENT_NODE) {
+	            String nodeName = child.getNodeName();
+	            if (nodeName.equals(TEXT_LIST)) {
+	                if (!listIsOnlyHeadings(child)) return false;
+	            }
+	            else if (nodeName.equals(TEXT_ORDERED_LIST)) {
+	                if (!listIsOnlyHeadings(child)) return false;
+	            }
+	            else if (nodeName.equals(TEXT_UNORDERED_LIST)) {
+	                if (!listIsOnlyHeadings(child)) return false;
+	            }
+	            else if(!nodeName.equals(TEXT_H)) {
+	                return false;
+	            }
+	        }
+	        child = child.getNextSibling();
+	    }
+	    return true;
+	}
+
+	protected Node handleFakeList(Node onode, int nLevel, String sStyleName, Node hnode) {
+	    String sStyleName1 = Misc.getAttribute(onode,TEXT_STYLE_NAME);
+	    if (sStyleName1!=null) { sStyleName = sStyleName1; }
+	    return traverseFakeList(onode,hnode,nLevel,sStyleName);
+	}
+
+	private Node traverseFakeList(Node onode, Node hnode, int nLevel, String sStyleName) {
+	    // Restart numbering?
+	    boolean bContinueNumbering ="true".equals(
+	        Misc.getAttribute(onode,TEXT_CONTINUE_NUMBERING));
+	    if (!bContinueNumbering) {
+	        getListCounter(ofr.getListStyle(sStyleName)).restart(nLevel);
+	    }
+	
+	    Node child = onode.getFirstChild();
+	    while (child!=null) {
+	        if (child.getNodeType() == Node.ELEMENT_NODE) {
+	            String sNodeName = child.getNodeName();
+	            
+	            if (sNodeName.equals(TEXT_LIST_ITEM)) {
+	                boolean bRestart = "true".equals(Misc.getAttribute(child,
+	                    TEXT_RESTART_NUMBERING));
+	                int nStartValue = Misc.getPosInteger(Misc.getAttribute(child,
+	                    TEXT_START_VALUE),1);
+	                hnode = traverseFakeListItem(child, hnode, nLevel, sStyleName, false, bRestart, nStartValue);
+	            }
+	            else if (sNodeName.equals(TEXT_LIST_HEADER)) {
+	                hnode = traverseFakeListItem(child, hnode, nLevel, sStyleName, true, false, 0);
+	            }
+	        }
+	        child = child.getNextSibling();
+	    }
+	    return hnode;
+	}
+
+	private Node traverseFakeListItem(Node onode, Node hnode, int nLevel, String sStyleName, boolean bUnNumbered, boolean bRestart, int nStartValue) {
+		Node child = onode.getFirstChild();
+		while (child != null) {
+			if (child.getNodeType() == Node.ELEMENT_NODE) {
+				String sNodeName = child.getNodeName();
+	
+				if (sNodeName.equals(TEXT_H)) {
+					Node rememberNode = hnode;
+					getTextCv().handleHeading((Element) child, (Element) hnode, rememberNode != hnode, ofr.getListStyle(sStyleName), nLevel, bUnNumbered, bRestart, nStartValue);
+				} else if (sNodeName.equals(TEXT_P)) {
+					// Currently we only handle fakes lists containing headings
+				} else if (sNodeName.equals(TEXT_LIST)) { // oasis
+					return traverseFakeList(child, hnode, nLevel + 1, sStyleName);
+				} else if (sNodeName.equals(TEXT_ORDERED_LIST)) { // old
+					return traverseFakeList(child, hnode, nLevel + 1, sStyleName);
+				} else if (sNodeName.equals(TEXT_UNORDERED_LIST)) { // old
+					return traverseFakeList(child, hnode, nLevel + 1, sStyleName);
+				}
+			}
+			child = child.getNextSibling();
+		}
+		return hnode;
+	}
+
+}
\ No newline at end of file
diff --git a/src/main/java/writer2latex/xhtml/content/TextParser.java b/src/main/java/writer2latex/xhtml/content/TextParser.java
index bac31b2..f18cc96 100644
--- a/src/main/java/writer2latex/xhtml/content/TextParser.java
+++ b/src/main/java/writer2latex/xhtml/content/TextParser.java
@@ -69,8 +69,6 @@ public class TextParser extends Parser {
     private int splitHeadingLevel = 0;  // The outline level at which to split files (0=no split)
     private int nRepeatLevels = 5; // The number of levels to repeat when splitting (0=no repeat)
     private int nLastSplitLevel = 1; // The outline level at which the last split occurred
-    private int nDontSplitLevel = 0; // if > 0 splitting is forbidden
-    boolean bAfterHeading=false; // last element was a top level heading
     Element[] currentHeading = new Element[7]; // Last headings (repeated when splitting)
     private int nCharacterCount = 0; // The number of text characters in the current document
 
@@ -255,9 +253,8 @@ public class TextParser extends Parser {
         return parseText(onode,0,null,hnode);
     } 
 	
-    private Node parseText(Node onode, int nLevel, String styleName, Node hnode) {
+    public Node parseText(Node onode, int nLevel, String styleName, Node hnode) {
         if (!onode.hasChildNodes()) { return hnode; }
-        bAfterHeading = false;
         NodeList nList = onode.getChildNodes();
         int nLen = nList.getLength();
         int i = 0;
@@ -268,7 +265,6 @@ public class TextParser extends Parser {
             if (child.getNodeType() == Node.ELEMENT_NODE) {
                 String nodeName = child.getNodeName();
                 // Block splitting
-                nDontSplitLevel++;
                                                        
                 if (OfficeReader.isDrawElement(child)) {
                     getDrawCv().handleDrawElement((Element)child,(Element)hnode,null,nFloatMode);
@@ -366,13 +362,11 @@ public class TextParser extends Parser {
                 	}
                 	hnode = processPageBreaks(child, hnode,style);
                 	inList = true;
-                	if (listIsOnlyHeadings(child)) {
-                        nDontSplitLevel--;
-                        hnode = handleFakeList(child,nLevel+1,styleName,hnode);
-                        nDontSplitLevel++;
+                	if (getListParser().listIsOnlyHeadings(child)) {
+                        hnode = getListParser().handleFakeList(child,nLevel+1,styleName,hnode);
                     }
                     else {
-                        handleList(child,nLevel+1,styleName,hnode);
+                    	getListParser().handleList(child,nLevel+1,styleName,hnode);
                     }
                 	inList = false;
                 }
@@ -388,9 +382,7 @@ public class TextParser extends Parser {
                 }
                 else if (nodeName.equals(TEXT_SECTION)) {
                 	hnode = processPageBreaks(child, hnode,null);
-                    nDontSplitLevel--;
                     hnode = handleSection(child,hnode);
-                    nDontSplitLevel++;
                 }
                 else if (nodeName.equals(TEXT_TABLE_OF_CONTENT)) {
                     if (!ofr.getTocReader((Element)child).isByChapter()) {
@@ -424,13 +416,8 @@ public class TextParser extends Parser {
                 else if (nodeName.equals(TEXT_SEQUENCE_DECLS)) {
                     //handleSeqeuenceDecls(child);
                 }
-                // Reenable splitting
-                nDontSplitLevel--;
-                // Remember if this was a heading
-                if (nDontSplitLevel==0) {
-                    bAfterHeading = nodeName.equals(TEXT_H);
-                    hnode = getDrawCv().flushFullscreenFrames((Element)hnode);
-                }
+                //TODO:IS IT NEEDED?
+                hnode = getDrawCv().flushFullscreenFrames((Element)hnode);
             }
             i++;
         }
@@ -507,7 +494,7 @@ public class TextParser extends Parser {
     /*
      * Process a text:h tag
      */
-  	private void handleHeading(Element onode, Element hnode, boolean bAfterSplit, ListStyle listStyle, int nListLevel, boolean bUnNumbered, boolean bRestart, int nStartValue) {
+  	public void handleHeading(Element onode, Element hnode, boolean bAfterSplit, ListStyle listStyle, int nListLevel, boolean bUnNumbered, boolean bRestart, int nStartValue) {
   
   		// Note: nListLevel may in theory be different from the outline level,
   		// though the ui in OOo does not allow this
@@ -541,7 +528,7 @@ public class TextParser extends Parser {
   			ListCounter counter = null;
   			String sLabel = "";
   			if (!bUnNumbered) {
-  				counter = getListCounter(listStyle);
+  				counter = getListParser().getListCounter(listStyle);
   				if (bRestart) {
   					counter.restart(nListLevel, nStartValue);
   				}
@@ -598,7 +585,7 @@ public class TextParser extends Parser {
   
   				// Prepend numbering
   				if (!bUnNumbered) {
-  					insertListLabel(listStyle, nListLevel, "SectionNumber", null, sLabel, heading);
+  					getListParser().insertListLabel(listStyle, nListLevel, "SectionNumber", null, sLabel, heading);
   				}
   
   				// Add to toc
@@ -666,7 +653,7 @@ public class TextParser extends Parser {
         if (!bIsEmpty) {
             par = createTextBackground(par, styleName);
             if (config.listFormatting()==XhtmlConfig.HARD_LABELS) {
-            	insertListLabel(currentListStyle, nCurrentListLevel, "ItemNumber", null, sCurrentListLabel, par);
+            	getListParser().insertListLabel(currentListStyle, nCurrentListLevel, "ItemNumber", null, sCurrentListLabel, par);
             }
             sCurrentListLabel = null;
             traverseInlineText(onode,par);
@@ -699,384 +686,7 @@ public class TextParser extends Parser {
     }
 	
 	
-    ///////////////////////////////////////////////////////////////////////////
-    // LISTS
-    ///////////////////////////////////////////////////////////////////////////
-	
-    // Helper: Get a list counter for a list style
-    private ListCounter getListCounter(ListStyle style) {
-        if (style==ofr.getOutlineStyle()) {
-            // Outline numbering has a special counter
-            return outlineNumbering;
-        }
-        else if (style!=null) {
-            // Get existing or create new counter
-            if (listCounters.containsKey(style.getName())) {
-                return listCounters.get(style.getName());
-            }
-            else {
-                ListCounter counter = new ListCounter(style);
-                listCounters.put(style.getName(),counter);
-                return counter;
-            }
-        }
-        else {
-            // No style, return a dummy
-            return new ListCounter();
-        }
-    }
-    
-    // Helper: Insert a list label formatted with a list style
-    private void insertListLabel(ListStyle style, int nLevel, String sDefaultStyle, String sPrefix, String sLabel, Element hnode) {
-        if (sLabel!=null && sLabel.length()>0) {
-        	if (sPrefix!=null) {
-        		Element prefix = converter.createElement("span");
-        		prefix.setAttribute("class", "chapter-name");
-        		hnode.appendChild(prefix);
-        		prefix.appendChild( converter.createTextNode(sPrefix));
-        	}
-            StyleInfo info = new StyleInfo();
-            if (style!=null) {
-                String sTextStyleName = style.getLevelProperty(nLevel,TEXT_STYLE_NAME);
-                getTextSP().readStyle(sTextStyleName, info);
-            }
-
-            if (info.sTagName==null) { info.sTagName = "span"; }
-            if (info.sClass==null) { info.sClass = sDefaultStyle; }
-
-            Element content = converter.createElement(info.sTagName);
-            getTextSP().writeStyle(info, content);
-            hnode.appendChild(content);
-            content.appendChild( converter.createTextNode(sLabel) );
-        }
-    }
-	
-    // Helper: Check if a list contains any items
-    private boolean hasItems(Node onode) {
-        Node child = onode.getFirstChild();
-        while (child!=null) {
-            if (Misc.isElement(child,TEXT_LIST_ITEM) ||
-                Misc.isElement(child,TEXT_LIST_HEADER)) {
-                return true;
-            }
-            child = child.getNextSibling();
-        }
-        return false;
-    }
-
-    // TODO: Merge these three methods
-	
-    /*
-     * Process a text:ordered-list tag.
-     */
-    private void handleOL (Node onode, int nLevel, String sStyleName, Node hnode) {
-        if (hasItems(onode)) {
-            // add an OL element
-            Element list = converter.createElement("ol");
-            StyleInfo listInfo = new StyleInfo();
-            getListSc().applyStyle(nLevel,sStyleName,listInfo);
-            getListSc().readParentStyle(nLevel, sStyleName, listInfo);
-            writeStyle(listInfo,list);
-            hnode.appendChild(list);
-            traverseList(onode,nLevel,sStyleName,list);
-        }
-    }
-
-    /*
-     * Process a text:unordered-list tag.  
-     */
-    private void handleUL (Node onode, int nLevel, String sStyleName, Node hnode) {
-        if (hasItems(onode)) {
-            // add an UL element
-            Element list = converter.createElement("ul");
-            StyleInfo listInfo = new StyleInfo();
-            getListSc().applyStyle(nLevel,sStyleName,listInfo);
-            writeStyle(listInfo,list);
-            hnode.appendChild(list);
-            traverseList(onode,nLevel,sStyleName,list);
-        }
-    }
-	
-    private void handleList(Node onode, int nLevel, String sStyleName, Node hnode) {
-        // In OpenDocument, we should use the style to determine the type of list
-        String sStyleName1 = Misc.getAttribute(onode,TEXT_STYLE_NAME);
-        if (sStyleName1!=null) { sStyleName = sStyleName1; }
-        ListStyle style = ofr.getListStyle(sStyleName);
-        if (style!=null && style.isNumber(nLevel)) {
-            handleOL(onode,nLevel,sStyleName,hnode);
-        }
-        else {
-            handleUL(onode,nLevel,sStyleName,hnode);
-        }
-    }
-
-    /*
-     * Process the contents of a list (Changed as suggested by Nick Bower)
-     * The option xhtml_use_list_hack triggers some *invalid* code:
-     * - the attribute start on ol (is valid in html 4 transitional)
-     * - the attribute value on li (is valid in html 4 transitional)
-     * (these attributes are supposed to be replaced by css, but browsers
-     * generally don't support that)
-     * - generates <ol><ol><li>...</li></ol></ol> instead of
-     *   <ol><li><ol><li>...</li></ol></li></ol> in case the first child of
-     *   a list item is a new list. This occurs when a list is *continued* at
-     *   level 2 or higher. This hack seems to be the only solution that
-     *   actually produces correct results in browsers :-(
-     */
-    private void traverseList (Node onode, int nLevel, String styleName, Element hnode) {
-        ListCounter counter = getListCounter(ofr.getListStyle(styleName));
-
-        // Restart numbering, if required
-        //if (counter!=null) {
-        boolean bContinueNumbering = "true".equals(Misc.getAttribute(onode,TEXT_CONTINUE_NUMBERING));
-        if (!bContinueNumbering && counter!=null) {
-            counter.restart(nLevel);
-        }
-        if (config.listFormatting()==XhtmlConfig.CSS1_HACK && counter.getValue(nLevel)>0) {
-            hnode.setAttribute("start",Integer.toString(counter.getValue(nLevel)+1));                	
-        }
-        //}
-
-        if (onode.hasChildNodes()) {
-            NodeList nList = onode.getChildNodes();
-            int len = nList.getLength();
-            
-            for (int i = 0; i < len; i++) {
-                Node child = nList.item(i);
-                
-                if (child.getNodeType() == Node.ELEMENT_NODE) {
-                    String nodeName = child.getNodeName();
-                    
-                    if (nodeName.equals(TEXT_LIST_ITEM)) {
-                        // Check to see if first child is a new list
-                        boolean bIsImmediateNestedList = false;
-                        Element child1 = Misc.getFirstChildElement(child);
-                        if (child1.getTagName().equals(TEXT_ORDERED_LIST) || // old
-                            child1.getTagName().equals(TEXT_UNORDERED_LIST) || // old
-                            child1.getTagName().equals(TEXT_LIST)) { // oasis
-                            bIsImmediateNestedList = true;
-                        }
-
-                        if (config.listFormatting()==XhtmlConfig.CSS1_HACK && bIsImmediateNestedList) {
-                            traverseListItem(child,nLevel,styleName,hnode);
-                        }
-                        else {
-                            // add an li element
-                        	//if (counter!=null) {
-                        	sCurrentListLabel = counter.step(nLevel).getLabel();
-                        	//}
-                            currentListStyle = ofr.getListStyle(styleName);
-                            nCurrentListLevel = nLevel;
-                            Element item = converter.createElement("li");
-                            StyleInfo info = new StyleInfo();
-                            getPresentationSc().applyOutlineStyle(nLevel,info);
-                            writeStyle(info,item);
-                            hnode.appendChild(item);
-                            if (config.listFormatting()==XhtmlConfig.CSS1_HACK) {
-                                boolean bRestart = "true".equals(Misc.getAttribute(child,
-                                    TEXT_RESTART_NUMBERING));
-                                int nStartValue = Misc.getPosInteger(Misc.getAttribute(child,
-                                    TEXT_START_VALUE),1);
-                                if (bRestart) {
-                                    item.setAttribute("value",Integer.toString(nStartValue));
-                                    //if (counter!=null) {
-                                    sCurrentListLabel = counter.restart(nLevel,nStartValue).getLabel();
-                                    //}
-                                }
-                            }
-                            traverseListItem(child,nLevel,styleName,item);
-                        }
-                    }
-                    if (nodeName.equals(TEXT_LIST_HEADER)) {
-                        // add an li element
-                        Element item = converter.createElement("li");
-                        hnode.appendChild(item);
-                        item.setAttribute("style","list-style-type:none");
-                        traverseListItem(child,nLevel,styleName,item);
-                    }
-                }
-            }
-        }
-    }
-    
-    
-    /*
-     * Process the contents of a list item
-     * (a list header should only contain paragraphs, but we don't care)
-     */
-    private void traverseListItem (Node onode, int nLevel, String styleName, Node hnode) { 
-        // First check if we have a single paragraph to be omitted
-        // This should happen if we ignore styles and have no style-map
-        // for the paragraph style used        
-        if (config.xhtmlFormatting()!=XhtmlConfig.CONVERT_ALL && onode.hasChildNodes()) {
-            NodeList list = onode.getChildNodes();
-            int nLen = list.getLength();
-            int nParCount = 0;
-            boolean bNoPTag = true;
-            for (int i=0; i<nLen; i++) {
-                if (list.item(i).getNodeType()==Node.ELEMENT_NODE) {
-                    if (list.item(i).getNodeName().equals(TEXT_P)) {
-                        nParCount++;
-                        if (bNoPTag) {
-                            String sDisplayName = ofr.getParStyles().getDisplayName(Misc.getAttribute(list.item(0),TEXT_STYLE_NAME));
-                            if (config.getXParStyleMap().contains(sDisplayName)) {
-                                bNoPTag = false;
-                            }
-                        }
-                    }
-                    else { // found non-text:p element
-                        bNoPTag=false;
-                    }
-                }
-            }
-            if (bNoPTag && nParCount<=1) {
-                // traverse the list
-                for (int i = 0; i < nLen; i++) {
-                    Node child = list.item(i);
-          
-                    if (child.getNodeType() == Node.ELEMENT_NODE) {
-                        String nodeName = child.getNodeName();
-                    
-                        if (nodeName.equals(TEXT_P)) {
-                            traverseInlineText(child,hnode);
-                        }
-                        if (nodeName.equals(TEXT_LIST)) { // oasis
-                            handleList(child,nLevel+1,styleName,hnode);
-                        }
-                        if (nodeName.equals(TEXT_ORDERED_LIST)) { // old
-                            handleOL(child,nLevel+1,styleName,hnode);
-                        }
-                        if (nodeName.equals(TEXT_UNORDERED_LIST)) { // old
-                            handleUL(child,nLevel+1,styleName,hnode);
-                        }
-                    }
-                }
-                return;
-            }
-        }
-        // Still here? - traverse block text as usual!
-        parseText(onode,nLevel,styleName,hnode);
-    }
-	
-    ///////////////////////////////////////////////////////////////////////////
-    // FAKE LISTS
-    ///////////////////////////////////////////////////////////////////////////
-	
-    // A fake list is a list which is converted into a sequence of numbered
-    // paragraphs rather than into a list.
-    // Currently this is done for list which only contains headings
-	
-    // Helper: Check to see, if this list contains only headings
-    // (If so, we will ignore the list and apply the numbering to the headings)   
-    private boolean listIsOnlyHeadings(Node node) {
-        Node child = node.getFirstChild();
-        while (child!=null) {
-            if (child.getNodeType() == Node.ELEMENT_NODE) {
-                String nodeName = child.getNodeName();
-                if (nodeName.equals(TEXT_LIST_ITEM)) {
-                    if (!itemIsOnlyHeadings(child)) return false;
-                }
-                else if (nodeName.equals(TEXT_LIST_HEADER)) {
-                    if (!itemIsOnlyHeadings(child)) return false;
-                }
-            }
-            child = child.getNextSibling();
-        }
-        return true;
-    }
-    
-    private boolean itemIsOnlyHeadings(Node node) {
-        Node child = node.getFirstChild();
-        while (child!=null) {
-            if (child.getNodeType() == Node.ELEMENT_NODE) {
-                String nodeName = child.getNodeName();
-                if (nodeName.equals(TEXT_LIST)) {
-                    if (!listIsOnlyHeadings(child)) return false;
-                }
-                else if (nodeName.equals(TEXT_ORDERED_LIST)) {
-                    if (!listIsOnlyHeadings(child)) return false;
-                }
-                else if (nodeName.equals(TEXT_UNORDERED_LIST)) {
-                    if (!listIsOnlyHeadings(child)) return false;
-                }
-                else if(!nodeName.equals(TEXT_H)) {
-                    return false;
-                }
-            }
-            child = child.getNextSibling();
-        }
-        return true;
-    }
-	
-    // Splitting may occur inside a fake list, so we return the (new) hnode 
-    private Node handleFakeList(Node onode, int nLevel, String sStyleName, Node hnode) {
-        String sStyleName1 = Misc.getAttribute(onode,TEXT_STYLE_NAME);
-        if (sStyleName1!=null) { sStyleName = sStyleName1; }
-        return traverseFakeList(onode,hnode,nLevel,sStyleName);
-    }
-
-    // Traverse a list which is not exported as a list but as a sequence of
-    // numbered headings/paragraphs
-    private Node traverseFakeList (Node onode, Node hnode, int nLevel, String sStyleName) {
-        // Restart numbering?
-        boolean bContinueNumbering ="true".equals(
-            Misc.getAttribute(onode,TEXT_CONTINUE_NUMBERING));
-        if (!bContinueNumbering) {
-            getListCounter(ofr.getListStyle(sStyleName)).restart(nLevel);
-        }
-
-        Node child = onode.getFirstChild();
-        while (child!=null) {
-            if (child.getNodeType() == Node.ELEMENT_NODE) {
-                String sNodeName = child.getNodeName();
-                
-                if (sNodeName.equals(TEXT_LIST_ITEM)) {
-                    boolean bRestart = "true".equals(Misc.getAttribute(child,
-                        TEXT_RESTART_NUMBERING));
-                    int nStartValue = Misc.getPosInteger(Misc.getAttribute(child,
-                        TEXT_START_VALUE),1);
-                    hnode = traverseFakeListItem(child, hnode, nLevel, sStyleName, false, bRestart, nStartValue);
-                }
-                else if (sNodeName.equals(TEXT_LIST_HEADER)) {
-                    hnode = traverseFakeListItem(child, hnode, nLevel, sStyleName, true, false, 0);
-                }
-            }
-            child = child.getNextSibling();
-        }
-        return hnode;
-    }
-    
-    
-    // Process the contents of a fake list item
-	private Node traverseFakeListItem(Node onode, Node hnode, int nLevel, String sStyleName, boolean bUnNumbered, boolean bRestart, int nStartValue) {
-		Node child = onode.getFirstChild();
-		while (child != null) {
-			if (child.getNodeType() == Node.ELEMENT_NODE) {
-				String sNodeName = child.getNodeName();
-
-				if (sNodeName.equals(TEXT_H)) {
-					nDontSplitLevel++;
-					Node rememberNode = hnode;
-					handleHeading((Element) child, (Element) hnode, rememberNode != hnode, ofr.getListStyle(sStyleName), nLevel, bUnNumbered, bRestart, nStartValue);
-					nDontSplitLevel--;
-					if (nDontSplitLevel == 0) {
-						bAfterHeading = true;
-					}
-				} else if (sNodeName.equals(TEXT_P)) {
-					// Currently we only handle fakes lists containing headings
-				} else if (sNodeName.equals(TEXT_LIST)) { // oasis
-					return traverseFakeList(child, hnode, nLevel + 1, sStyleName);
-				} else if (sNodeName.equals(TEXT_ORDERED_LIST)) { // old
-					return traverseFakeList(child, hnode, nLevel + 1, sStyleName);
-				} else if (sNodeName.equals(TEXT_UNORDERED_LIST)) { // old
-					return traverseFakeList(child, hnode, nLevel + 1, sStyleName);
-				}
-			}
-			child = child.getNextSibling();
-		}
-		return hnode;
-	}
-	
+   	
     ////////////////////////////////////////////////////////////////////////
     // INLINE TEXT
     ////////////////////////////////////////////////////////////////////////