package OOXMLconvert;

import java.io.File;
import java.text.DecimalFormat;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.EntityRef;
import org.jdom.input.SAXBuilder;

/**
 *
 * @author ferou
 */
public class OOXMLparser {
	
	// pomocne premenne
	/** zoznam properties pre aktualny paragraph */
	private LinkedHashMap<String,String> pPr = new LinkedHashMap<String,String>();
	/** zoznam properties pre aktualny run */
	private LinkedHashMap<String,String> rPr = new LinkedHashMap<String,String>();
	
	/** obsah elementu z XHTML, ktory nahradza "run" element z XML */
	private Element runEl = null;
	/** obsah elementu z XHTML, ktory nahradza "paragraph" element z XML */
	private Element paragraphEl = null;
	/** obsah elementu z XHTML, ktory zodpoveda vytvaranemu listu */
	private Element numberingEl = null;
	
	/** pri vyplnani numberingu oznacuje level v predoslej iteracii */
	private int numLvl = -1;
	
	/** pomocna premenna na urcovanie nadpisu v elemente "r" */
	private String headingInR = "";
	
	/** pomocne premenne na footnotes a endnotes - pocitadlo a html vystup */
	private int notesCount = 0;
	private List<Element> notesEl = new LinkedList<Element>();
	
	// pomocne premenne na komentáre - override parametrov
	/** id aktuálneho komentára */
	private String commentId = "";
	/** pomocna premenná na určovanie toho, či bol spravený backup */
	private boolean isBackup = false;
	
	/**
	 * Konštruktor
	 */
	public OOXMLparser() {
		// noop
	}
	
	/**
	 * Funkcia zistí, či element obsahuje nejakú entityRef (napr. &nbsp;)
	 * @param el element, ktorý chceme prezrieť
	 * @return <i>true</i> ak element obsahuje entityRef, <i>false</i> inak
	 */
	private boolean containsEntityRef(Element el) {
		try {
			List s = el.getContent();
			Iterator i = s.iterator();
			while (i.hasNext()) {
				Object o = i.next();
				try {
					EntityRef er = (EntityRef)o;
					return true;
				}
				catch (ClassCastException e) {
					// noop
				}
			}
			return false;
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (isVisualEmpty): " + e.toString());
			return false;
		}
	}
	
	/**
	 * Funkcia, ktorá zisťuje, či XHTML element niečo vypíše
	 * @param el element, ktorý chceme otestovať
	 * @return <i>true</i> ak je element vizuálne prázdny <i>false</i> inak
	 */
	private boolean isVisualEmpty(Element el) {
		try {
			if (!el.getText().isEmpty()) {
				return false;
			}
			else {
				List elChildren = el.getChildren();
				Iterator elIterator = elChildren.iterator();
				while (elIterator.hasNext()) {
					Element elChild = (Element) elIterator.next();
					// ak je potomok obrazok, tak vyhodnot ako neprazdny
					if (elChild.getName().trim().equals("img")) {
						return false;
					}
					// rekurzivne skontroluj vsetkych potomkov
					if ((!isVisualEmpty(elChild))) {
						return false;
					}
				}
			}
			// este zisti, ci neobsahuje EntityRef
			if (containsEntityRef(el)) {
				return false;
			}
			return true;
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (isVisualEmpty): " + e.toString());
			return false;
		}
	}
	
	/**
	 * Funkcia pospája span elementy, ktoré sa spojiť môžu a tiež ak paragraphEl obsahuje iba jeden span, tak prenesie obsah
	 */
	private void tidyUpParagraph() {
		try {
			// spoj spany, ktore su rovnake
			XHTMLoutput.mergeSpanBlocks(paragraphEl);
			List spanEls = paragraphEl.getChildren("span");
			List allEls = paragraphEl.getChildren();
			String contentText = paragraphEl.getText();
			// ak sa jedna o paragraph, ktory obsahuje iba jeden span bez formatovania pozadia a neobsahuje TEXT!!!!!, tak obsah prenes do paragraphu a spoj styly
			if ((spanEls.size() == 1) && (allEls.size() == 1) && (contentText.isEmpty())) {
				Element spanEl = ((Element)spanEls.get(0));
				String spanClasses = spanEl.getAttributeValue("class", "");
				if (!spanClasses.contains("bkgColor")) {
					String classes = (spanClasses + " " + paragraphEl.getAttributeValue("class", "").trim()).trim();
					paragraphEl.removeContent();
					paragraphEl.addContent(spanEl.cloneContent());
					if (!classes.isEmpty()) {
						paragraphEl.setAttribute("class", classes);
					}
				}
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (tidyUpParagraph): " + e.toString());
		}
	}
	
	/**
	 * Funkcia pospája span elementy, ktoré sa spojiť môžu a tiež ak element el obsahuje iba jeden span, tak prenesie obsah. Ak má potomkov, tak s nimi spraví to isté.
	 */
	private void tidyUpElement(Element el) {
		try {
			// spoj spany, ktore su rovnake
			XHTMLoutput.mergeSpanBlocks(el);
			List spanEls = el.getChildren("span");
			List allEls = el.getChildren();
			String contentText = el.getText();
			// ak sa jedna o paragraph, ktory obsahuje iba jeden span bez formatovania pozadia a neobsahuje TEXT!!!!!, tak obsah prenes do paragraphu a spoj styly
			if ((spanEls.size() == 1) && (allEls.size() == 1) && (contentText.isEmpty())) {
				Element spanEl = ((Element)spanEls.get(0));
				String spanClasses = spanEl.getAttributeValue("class", "");
				if (!spanClasses.contains("bkgColor")) {
					String classes = (spanClasses + " " + el.getAttributeValue("class", "").trim()).trim();
					el.removeContent();
					el.addContent(spanEl.cloneContent());
					if (!classes.isEmpty()) {
						el.setAttribute("class", classes);
					}
				}
			}
			else {
				List lstElChild = el.getChildren();
				Iterator i = lstElChild.iterator();
				while (i.hasNext()) {
					Element elChild = (Element)i.next();
					tidyUpElement(elChild);
				}
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (tidyUpParagraph): " + e.toString());
		}
	}
	
	/**
	 * Funkcia spracuje numbering (list) a pridá zodpovedajúci obsah do numberingEl
	 */
	private void processNumbering() {
		int eee = 0;
		try {
			// level v ktorom sme vnoreny
			int numLevel = Functions.intValDef(pPr.get("numLevel"),-1);
			// typ odrazky
			String numType = pPr.containsKey("numType")&&pPr.get("numType")!=null?pPr.get("numType"):"bullet";
			// "text" odrazky
			String numText = pPr.containsKey("numText")&&pPr.get("numText")!=null?pPr.get("numText"):"o";
			// id obrazka
			String numPicId = pPr.containsKey("numPicId")&&pPr.get("numPicId")!=null?pPr.get("numPicId"):"no";
			// CSS styl
			String numStyle = "";
			// ak prenasame styly odrazok
			if (Global.ooxmlPreserve.bulletStyle) {
				// ak je odrazka obrazok
				if (!numPicId.equals("no")) {
					// obrazok ako odrazka
					numStyle = Global.ooxmlNumberings.getImgNumberingStyle(numPicId);
				}
				// ak je odrazka neciselna
				else if (numType.equals("bullet")) {
					char c = numText.charAt(0);
					int ord = c;
					numStyle = Global.ooxmlNumberings.getBulletStyle(ord);
				}
				// ak je odrazka ciselna
				else {
					numStyle = Global.ooxmlNumberings.getNumberingStyle(numType.trim());
				}
			}
			
			if (this.numLvl < numLevel) {
				// ak sme sa vnorili alebo vytvorili novy list
				Element listEl = null;
				if (numType.equals("bullet")) {
					listEl = new Element("ul");
				}
				else {
					listEl = new Element("ol");
				}
				//tidyUpParagraph();
				//numStyle = (paragraphEl.getAttributeValue("class", "") + " " + numStyle).trim();
				Element liEl = new Element("li");
				if (!numStyle.trim().isEmpty()) {
					liEl.setAttribute("class", numStyle);
				}
				listEl.addContent(liEl.addContent(paragraphEl.cloneContent()));
				if (numberingEl != null) {
					// ak sa vnarame do listu, tak pridaj obsah listEl do posledneho "li" elementu
					List lis = numberingEl.getChildren("li");
					((Element)lis.get(lis.size()-1)).addContent(listEl);
				}
				numberingEl = listEl;
			}
			else if (this.numLvl == numLevel) {
				// pridavame item na tej istej urovni
				//tidyUpParagraph();
				//numStyle = (paragraphEl.getAttributeValue("class", "") + " " + numStyle).trim();
				Element liEl = new Element("li");
				if (!numStyle.trim().isEmpty()) {
					liEl.setAttribute("class", numStyle);
				}
				numberingEl.addContent(liEl.addContent(paragraphEl.cloneContent()));
			}
			else {
				// pridavame item do vyssej urovne
				int c = (this.numLvl - numLevel);
				for (int i = 0; i < c; i++) {
					Element e1 = numberingEl.getParentElement();
					if (e1 != null) {
						numberingEl = e1.getParentElement();
					}
				}
				//tidyUpParagraph();
				//numStyle = (paragraphEl.getAttributeValue("class", "") + " " + numStyle).trim();
				Element liEl = new Element("li");
				if (!numStyle.trim().isEmpty()) {
					liEl.setAttribute("class", numStyle);
				}
				numberingEl.addContent(liEl.addContent(paragraphEl.cloneContent()));
			}
			this.numLvl = numLevel;
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (processNumbering): "+ eee + " " + e.toString());
		}
	}
	
	/**
	 * Funkcia pridá obsah numberingEl do elXHTML.
	 * @param elXHTML element výstupného XHTML
	 */
	private void finishNumbering(Element elXHTML) {
		try {
			while (numberingEl.getParent() != null) {
				numberingEl = numberingEl.getParentElement();
			}
			
			tidyUpElement(numberingEl);
			
			elXHTML.addContent((Element)numberingEl.clone());
			numberingEl.removeContent();
			numberingEl = null;
			this.numLvl = -1;
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (finishNumbering): " + e.toString());
		}
	}
	
	/**
	 * Funkcia ukončí "p" element a zapíše vygenerovaný obsah do výstupného XHTML
	 * @param elXHTML element výstupného XHTML
	 */
	private void finishP(Element elXHTML) {
		try {
			// kvoli "heuristike" na rozoznavanie nadpisov
			boolean isBold = false;
			int fontSize = 0;

			// css styly (nazvy tried)
			String stylesClasses = "";
			// strike
			if (Global.ooxmlPreserve.strike && pPr.containsKey("strike") && pPr.get("strike").equals("on")) {
				stylesClasses += " " + Global.ooxmlParams.cssS;
				Global.cssOutput.addClass("." + Global.ooxmlParams.cssS, "text-decoration: line-through;");
			}
			// sub
			if (Global.ooxmlPreserve.subscript && pPr.containsKey("sub") && pPr.get("sub").equals("on")) {
				stylesClasses += " " + Global.ooxmlParams.cssLI;
				Global.cssOutput.addClass("." + Global.ooxmlParams.cssLI, "vertical-align: sub; font-size: 70%;");
			}
			// super
			if (Global.ooxmlPreserve.superscript && pPr.containsKey("super") && pPr.get("super").equals("on")) {
				stylesClasses += " " + Global.ooxmlParams.cssUI;
				Global.cssOutput.addClass("." + Global.ooxmlParams.cssUI, "vertical-align: super; font-size: 70%;");
			}
			// underline
			if (Global.ooxmlPreserve.underline && pPr.containsKey("u") && pPr.get("u").equals("on")) {
				stylesClasses += " " + Global.ooxmlParams.cssU;
				Global.cssOutput.addClass("." + Global.ooxmlParams.cssU, "text-decoration: underline;");
			}
			// italic
			if (Global.ooxmlPreserve.italic && pPr.containsKey("i") && pPr.get("i").equals("on")) {
				stylesClasses += " " + Global.ooxmlParams.cssI;
				Global.cssOutput.addClass("." + Global.ooxmlParams.cssI, "font-style: italic;");
			}
			// bold
			if (Global.ooxmlPreserve.bold && pPr.containsKey("b") && pPr.get("b").equals("on")) {
				isBold = true;
				stylesClasses += " " + Global.ooxmlParams.cssB;
				Global.cssOutput.addClass("."+ Global.ooxmlParams.cssB, "font-weight: bold;");
			}
			// velkost fontu
			if (Global.ooxmlPreserve.fontSize && pPr.containsKey("fontSize") && !pPr.get("fontSize").trim().isEmpty()) {
				fontSize = Functions.intValDef(pPr.get("fontSize"), 0);
				fontSize = fontSize / 2;
				String className = Global.cssOutput.addClassN(".fontSize", "font-size: " + fontSize + "pt;");
				stylesClasses += " " + className;
			}
			// nazov fontu
			if (Global.ooxmlPreserve.fontName && pPr.containsKey("fontName") && !pPr.get("fontName").trim().isEmpty()) {
				String fontName = pPr.get("fontName");
				String className = Global.cssOutput.addClassN(".fontName", "font-family: \"" + fontName + "\";");
				stylesClasses += " " + className;
			}
			// farba textu
			if (Global.ooxmlPreserve.color && pPr.containsKey("color") && !pPr.get("color").trim().isEmpty()) {
				String color = pPr.get("color");
				String className = Global.cssOutput.addClassN(".color", "color: #" + color + ";");
				stylesClasses += " " + className;
			}
			// farba pozadia
			if (Global.ooxmlPreserve.background && pPr.containsKey("bkgColor") && !pPr.get("bkgColor").trim().isEmpty()) {
				String bkgColor = pPr.get("bkgColor");
				String className = Global.cssOutput.addClassN(".bkgColor", "background-color: #" + bkgColor + ";");
				stylesClasses += " " + className;
			}
			// zarovnanie textu
			if (Global.ooxmlPreserve.alignment && pPr.containsKey("align") && !pPr.get("align").trim().isEmpty()) {
				String align = pPr.get("align");
				String className = Global.cssOutput.addClassN(".align", "text-align: " + align + ";");
				stylesClasses += " " + className;
			}
			// borders
			if (Global.ooxmlPreserve.border) {
				String[] borders = {"border-top","border-left","border-bottom","border-right"};
				for (int i = 0; i < borders.length; i++) {
					if (pPr.containsKey(borders[i]) && !pPr.get(borders[i]).trim().isEmpty()) {
						String border = pPr.get(borders[i]);
						String className = Global.cssOutput.addClassN(".border", borders[i] + ": " + border + ";");
						stylesClasses += " " + className;
					}
				}
			}
			// odsadenie lave
			if (Global.ooxmlPreserve.indentation && pPr.containsKey("leftIndent") && !pPr.get("leftIndent").trim().isEmpty()) {
				int leftIndent = Functions.intValDef(pPr.get("leftIndent"),0);
				String className = Global.cssOutput.addClassN(".indent", "margin-left: " + leftIndent + "px;");
				stylesClasses += " " + className;
			}
			// odsadenie prave
			if (Global.ooxmlPreserve.indentation && pPr.containsKey("rightIndent") && !pPr.get("rightIndent").trim().isEmpty()) {
				int rightIndent = Functions.intValDef(pPr.get("rightIndent"),0);
				String className = Global.cssOutput.addClassN(".indent", "margin-right: " + rightIndent + "px;");
				stylesClasses += " " + className;
			}
			// vyska riadka
			if (Global.ooxmlPreserve.linespacing && pPr.containsKey("lineHeight") && !pPr.get("lineHeight").trim().isEmpty()) {
				String lineHeight = pPr.get("lineHeight");
				String className = Global.cssOutput.addClassN(".lineheight", "line-height: " + lineHeight + ";");
				stylesClasses += " " + className;
			}
			// odsadenie pred odstavcom
			if (Global.ooxmlPreserve.linespacing && pPr.containsKey("spacingBefore") && !pPr.get("spacingBefore").trim().isEmpty()) {
				int spacingBefore = Functions.intValDef(pPr.get("spacingBefore"),0);
				String className = Global.cssOutput.addClassN(".indent", "margin-top: " + spacingBefore + "px;");
				stylesClasses += " " + className;
			}
			// odsadenie za odstavcom
			if (Global.ooxmlPreserve.linespacing && pPr.containsKey("spacingAfter") && !pPr.get("spacingAfter").trim().isEmpty()) {
				int spacingAfter = Functions.intValDef(pPr.get("spacingAfter"),0);
				String className = Global.cssOutput.addClassN(".indent", "margin-bottom: " + spacingAfter + "px;");
				stylesClasses += " " + className;
			}
			
			// kvoli zistovaniu nadpisu zisti nazov stylu (id)
			String styleId = "";
			if (pPr.containsKey("styleId") && !pPr.get("styleId").trim().isEmpty()) {
				styleId = pPr.get("styleId");
			}
			
			// zisti ci je dany P element nadpis
			String heading = "";
			if (Global.ooxmlParams.useHHeuristic) {
				if ((fontSize > Global.ooxmlStyles.defFontSize && isBold == true)) {
					heading = Global.ooxmlParams.htmlTagForHeading;
				}
			}
			if ((styleId.toLowerCase().contains(Global.ooxmlParams.heading1StyleName))) {
				heading = "h1";
			}
			else if ((styleId.toLowerCase().contains(Global.ooxmlParams.heading2StyleName))) {
				heading = "h2";
			}
			else if ((styleId.toLowerCase().contains(Global.ooxmlParams.heading3StyleName))) {
				heading = "h3";
			}
			
			// ak je nastaveny heading v "r" elemente
			if (!this.headingInR.isEmpty()) {
				heading = this.headingInR;
				this.headingInR = "";
				paragraphEl = (Element)paragraphEl.getChild("span").clone();
			}

			if (!heading.isEmpty() && !isVisualEmpty(paragraphEl)) {
				if (!Global.ooxmlPreserve.headingStyle) {
					// ak nezachovavam styly pre nadpis, tak ich vymaz
					stylesClasses = "";
				}
				Element headingEl = new Element(heading);
				headingEl.addContent(paragraphEl.cloneContent());
				paragraphEl = headingEl;
			}
			if (isVisualEmpty(paragraphEl)) {
				paragraphEl = new Element("p");
				paragraphEl.addContent(new Element("br"));
			}
			stylesClasses = stylesClasses.trim();
			if (!stylesClasses.trim().isEmpty()) {
				paragraphEl.setAttribute("class", stylesClasses);
			}
			
			tidyUpParagraph();
			
			elXHTML.addContent((Element)paragraphEl.clone());
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (finishP): " + e.toString());
		}
	}
	
	/**
	 * Funkcia, ktorá prejde daným "tbl" XML elementom
	 * @param elTbl vstupný XML element
	 * @param elXHTML element výstupného XHTML
	 */
	private void parseElementTbl(Element elTbl, Element elXHTML) {
		try {
			ElementTable table = new ElementTable(elTbl);
			Element xTable = table.getXHTMLTable();
			if (xTable != null) {
				elXHTML.addContent(xTable);
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseElementTbl): " + e.toString());
		}
	}
	
	/**
	 * Funkcia, ktorá prejde daným "drawing" XML elementom
	 * @param elDrawing vstupný XML element
	 */
	private void parseElementDrawing(Element elDrawing) {
		try {
			if (Global.ooxmlParams.preserveImages) {
				ElementPicture pic = new ElementPicture(elDrawing);
				LinkedList<Element> xImgEls = pic.getXHTMLImage();
				if (xImgEls != null) {
					Iterator imgIterator = xImgEls.iterator();
					while (imgIterator.hasNext()) {
						Element xImgEl = (Element)imgIterator.next();
						if (xImgEl != null) {
							runEl.addContent(xImgEl);
						}
					}
				}
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseElementDrawing): " + e.toString());
		}
	}
	
	/**
	 * Funkcia, ktorá prejde daným "hyperlink" XML elementom
	 * @param elHyperlink vstupný XML element
	 */
	private void parseElementHyperlink(Element elHyperlink) {
		try {
			// zisti id hyperlinku
			String hyperlinkId = elHyperlink.getAttributeValue("id", Global.rns);
			// zisti kam hyperlink smeruje
			String destURL = Global.ooxmlRels.hyperlinks.get(hyperlinkId);
			// prejdi vsetkymi potomkami "hyperlink" elementu
			List hyperlinkChildren = elHyperlink.getChildren();
			Iterator hyperlinkIterator = hyperlinkChildren.iterator();
			while (hyperlinkIterator.hasNext()) {
				Element hyperlinkChild = (Element) hyperlinkIterator.next();
				// zisti co je pChild

				// "run" element
				if (hyperlinkChild.getName().trim().equals("r")) {
					parseHyperlinkContent(hyperlinkChild);
				}
			}
			Element elA = new Element("a");
			elA.setAttribute("href", destURL);
			elA.addContent(runEl.cloneContent());
			paragraphEl.addContent(elA);
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseElementHyperlink): " + e.toString());
		}
	}
	
	/**
	 * Funkcia dočasne prepíše parametre
	 * @param overrides zoznam prepísaných parametrov
	 */
	private void overrideParams(LinkedHashMap<String,String> overrides) {
		try {
			if (!this.isBackup) {
				Global.ooxmlParams.backup();
				Global.ooxmlParams.setParams(overrides);
				Global.ooxmlPreserve.setElementsFormatting();
				this.isBackup = true;
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (overrideParams): " + e.toString());
		}
	}
	
	/**
	 * Funkcia vráti pôvodné hodnoty parametrom
	 */
	private void setOriginalParams() {
		try {
			if (this.isBackup) {
				Global.ooxmlParams.restore();
				Global.ooxmlPreserve.setElementsFormatting();
				this.isBackup = false;
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (overrideParams): " + e.toString());
		}
	}
	
	/**
	 * Funkcia, ktorá prejde daným "p" XML elementom
	 * @param elP vstupný XML element
	 */
	private void parseElementP(Element elP) {
		try {
			// najprv nastav paragraphEl, do ktoreho sa pridava obsah "paragraph" elementu
			paragraphEl = new Element("p");

			// prejdi vsetkymi potomkami "paragraph" elementu
			List pChildren = elP.getChildren();
			Iterator pIterator = pChildren.iterator();
			while (pIterator.hasNext()) {
				Element pChild = (Element) pIterator.next();
				// zisti co je pChild

				// "paragraph properties" element
				if (pChild.getName().trim().equals("pPr")) {
					parseElementPPr(pChild);
				}
				// "run" element
				else if (pChild.getName().trim().equals("r")) {
					parseElementR(pChild);
				}
				// zaciatok komentara
				else if (pChild.getName().trim().equals("commentRangeStart")) {
					// ak je nastaveny nejaky comment id, tak tento koment ignoruj
					if (this.commentId.trim().isEmpty()) {
						String id = pChild.getAttributeValue("id", Global.wns);
						this.commentId = id;
						// zacni override elementov
						LinkedHashMap<String,String> overrides = Global.ooxmlComments.getComment(id);
						if (overrides != null) {
							overrideParams(overrides);
						}
					}
				}
				// koniec komentara
				else if (pChild.getName().trim().equals("commentRangeEnd")) {
					String id = pChild.getAttributeValue("id", Global.wns);
					// ak uzatvarame nie aktualny koment, tak ignoruj
					if (this.commentId.equals(id)) {
						// ukonci override elementov
						this.commentId = "";
						setOriginalParams();
					}
				}
				// "hyperlink" element
				else if (pChild.getName().trim().equals("hyperlink")) {
					parseElementHyperlink(pChild);
				}
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseElementP): " + e.toString());
		}
	}
	
	/**
	 * Funkcia, ktorá prejde daným "pPr" XML elementom
	 * @param elPPr vstupný XML element
	 */
	private void parseElementPPr(Element elPPr) {
		try {
			// prejdi vsetkymi potomkami "paragraph properties" elementu
			List pPrChildren = elPPr.getChildren();
			Iterator pPrIterator = pPrChildren.iterator();
			while (pPrIterator.hasNext()) {
				Element pPrChild = (Element) pPrIterator.next();
				// zisti co je pPrChild

				// "paragraph style" element
				if (pPrChild.getName().trim().equals("pStyle")) {
					parseElementPStyle(pPrChild);
				}
				// cislovanie
				else if (pPrChild.getName().trim().equals("numPr")) {
					parseElementNumPr(pPrChild);
				}
				// zarovnanie
				else if (Global.ooxmlPreserve.alignment && pPrChild.getName().trim().equals("jc")) {
					String align = pPrChild.getAttributeValue("val", Global.wns, "left");
					if (align.equals("left")) {
						pPr.put("align", "left");
					}
					else if (align.equals("right")) {
						pPr.put("align", "right");
					}
					else if (align.equals("center")) {
						pPr.put("align", "center");
					}
					else if (align.equals("both")) {
						pPr.put("align", "justify");
					}
				}
				// pozadie
				else if (Global.ooxmlPreserve.background && pPrChild.getName().trim().equals("shd")) {
					String bkgColor = pPrChild.getAttributeValue("fill", Global.wns, "");
					pPr.put("bkgColor", bkgColor);
				}
				// border
				else if (Global.ooxmlPreserve.border && pPrChild.getName().trim().equals("pBdr")) {
					parseElementPBdr(pPrChild);
				}
				// odsadenie
				else if (Global.ooxmlPreserve.indentation && pPrChild.getName().trim().equals("ind")) {
					int leftIndent = Functions.intValDef(pPrChild.getAttributeValue("left", Global.wns, "0"),0) / 15;
					int rightIndent = Functions.intValDef(pPrChild.getAttributeValue("right", Global.wns, "0"),0) / 15;
					if (leftIndent > 0) {
						pPr.put("leftIndent", leftIndent + "");
					}
					if (rightIndent > 0) {
						pPr.put("rightIndent", rightIndent + "");
					}
				}
				// vyska riadku
				else if (Global.ooxmlPreserve.linespacing && pPrChild.getName().trim().equals("spacing")) {
					DecimalFormat df = new DecimalFormat("0.00"); 
					double line = 1.0 * Functions.intValDef(pPrChild.getAttributeValue("line", Global.wns, "0"),0) / 240; // 240tina riadku
					String lineRule = pPrChild.getAttributeValue("lineRule", Global.wns, "auto").trim();
					int before = Functions.intValDef(pPrChild.getAttributeValue("before", Global.wns, "-100"),-100) / 15;
					int after = Functions.intValDef(pPrChild.getAttributeValue("after", Global.wns, "-100"),-100) / 15;
					
					if (lineRule.equals("auto")) {
						if (line <= 0) {
							line = Global.ooxmlStyles.lineHeight;
						}
						pPr.put("lineHeight", df.format(line).replace(",", "."));
					}
					
					if (before < 0) {
						before = Global.ooxmlStyles.spacingBefore;
					}
					pPr.put("spacingBefore", before + "");

					if (after < 0) {
						after = Global.ooxmlStyles.spacingAfter;
					}
					pPr.put("spacingAfter", after + "");
				}
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseElementPPr): " + e.toString());
		}
	}
	
	/**
	 * Funkcia, ktorá prejde daným "numPr" XML elementom
	 * @param elNumPr vstupný XML element
	 */
	private void parseElementNumPr(Element elNumPr) {
		try {
			Element ilvlEl = elNumPr.getChild("ilvl", Global.wns);
			Element numIdEl = elNumPr.getChild("numId", Global.wns);
			int ilvl = Functions.intValDef(ilvlEl.getAttributeValue("val", Global.wns), 0);
			String numId = numIdEl.getAttributeValue("val", Global.wns);
			LinkedHashMap<String,String> numb = Global.ooxmlNumberings.getNumbering(numId, ilvl);
			if (numb != null) {
				String numType = numb.get("numFmt");
				String numText = numb.get("lvlText");
				String numPicId = numb.get("lvlPicBulletId");
				pPr.put("numLevel", ilvl+"");
				pPr.put("numType", numType);
				pPr.put("numText", numText);
				pPr.put("numPicId", numPicId);
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseElementNumPr): " + e.toString());
		}
	}
	
	/**
	 * Funkcia, ktorá prejde daným "pStyle" XML elementom
	 * @param elPStyle vstupný XML element
	 */
	private void parseElementPStyle(Element elPStyle) {
		try {
			// skopiruj vlastnosti zo stylov
			String styleId = elPStyle.getAttributeValue("val", Global.wns);
			if (Global.ooxmlStyles.getStyle(styleId) != null) {
				pPr.putAll(Global.ooxmlStyles.getStyle(styleId));
				pPr.putAll(Global.ooxmlStyles.getPStyle(styleId));
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseElementPStyle): " + e.toString());
		}
	}
	
	/**
	 * Funkcia vráti CSS štýl zodpovedajúci borderu
	 * @param pBdrChild element, v ktorom je definovaný border
	 * @return String obsahujúci CSS štýl
	 */
	private String getBorderProperties(Element pBdrChild) {
		try {
			String retString = "";
			String color = pBdrChild.getAttributeValue("color", Global.wns);
			String type = pBdrChild.getAttributeValue("val", Global.wns);
			String sizeS = pBdrChild.getAttributeValue("sz", Global.wns);
			int sizeI = Functions.intValDef(sizeS, 0);
			// nacitaj defaulty
			
			// farba
			if (color == null) {
				color = "black";
			}
			else {
				if (color.equals("auto")) {
					color = "black";
				}
				else {
					color = "#" + color;
				}
			}
			
			// typ ciary (registruj iba dashed a ostatne je solid)
			if (type == null) {
				type = "solid";
			}
			else if (type.contains("dash")) {
				type = "dashed";
			}
			else {
				type = "solid";
			}
			
			// hrubka ciary
			// premen jednotky pouzivane vo Worde na hrubku borderu na pixely
			sizeI = sizeI / 3;
			if (sizeI <= 0) {
				sizeI = 1;
			}
			
			retString = sizeI + "px " + type + " " + color;
			return retString;
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (getBorderProperties): " + e.toString());
			return "";
		}
	}
	
	/**
	 * Funkcia, ktorá prejde daným "pBdr" XML elementom
	 * @param elPBdr vstupný XML element
	 */
	private void parseElementPBdr(Element elPBdr) {
		try {
			List pBdrChildren = elPBdr.getChildren();
			Iterator pBdrIterator = pBdrChildren.iterator();
			while (pBdrIterator.hasNext()) {
				Element pBdrChild = (Element) pBdrIterator.next();
				if (pBdrChild.getName().trim().equals("top")) {
					String props = getBorderProperties(pBdrChild);
					pPr.put("border-top", props);
				}
				if (pBdrChild.getName().trim().equals("left")) {
					String props = getBorderProperties(pBdrChild);
					pPr.put("border-left", props);
				}
				if (pBdrChild.getName().trim().equals("bottom")) {
					String props = getBorderProperties(pBdrChild);
					pPr.put("border-bottom", props);
				}
				if (pBdrChild.getName().trim().equals("right")) {
					String props = getBorderProperties(pBdrChild);
					pPr.put("border-right", props);
				}
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseElementPBdr): " + e.toString());
		}
	}
	
	/**
	 * Funkcia, ktorá prejde daným "rStyle" XML elementom
	 * @param elRStyle vstupný XML element
	 */
	private void parseElementRStyle(Element elRStyle) {
		try {
			// skopiruj vlastnosti zo stylov
			String styleId = elRStyle.getAttributeValue("val", Global.wns);
			if (Global.ooxmlStyles.getStyle(styleId) != null) {
				rPr.putAll(Global.ooxmlStyles.getStyle(styleId));
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseElementRStyle): " + e.toString());
		}
	}
	
	/**
	 * Funkcia, ktorá prejde daným "rPr" XML elementom
	 * @param elRPr vstupný XML element
	 */
	private void parseElementRPr(Element elRPr) {
		try {
			// prejdi vsetky elementy a nastav formatovanies
			List rPrChildren = elRPr.getChildren();
			Iterator rPrIterator = rPrChildren.iterator();
			while (rPrIterator.hasNext()) {
				Element rPrChild = (Element) rPrIterator.next();
				// strike
				if (rPrChild.getName().trim().equals("strike")) {
					rPr.put("strike", rPrChild.getAttributeValue("val", Global.wns, "on").equals("none")?"off":"on");
				}
				// sub & super
				if (rPrChild.getName().trim().equals("vertAlign")) {
					String val = rPrChild.getAttributeValue("val", Global.wns);
					if (val.trim().equals("subscript")) {
						rPr.put("sub", "on");
					}
					else if (val.trim().equals("superscript")) {
						rPr.put("super", "on");
					}
				}
				// underline
				if (rPrChild.getName().trim().equals("u")) {
					rPr.put("u", rPrChild.getAttributeValue("val", Global.wns, "on").equals("none")?"off":"on");
				}
				// italic
				else if (rPrChild.getName().trim().equals("i")) {
					rPr.put("i", rPrChild.getAttributeValue("val", Global.wns, "on"));
				}
				// bold
				else if (rPrChild.getName().trim().equals("b")) {
					rPr.put("b", rPrChild.getAttributeValue("val", Global.wns, "on"));
				}
				// velkost fontu
				else if (rPrChild.getName().trim().equals("sz")) {
					String fontSize = rPrChild.getAttributeValue("val", Global.wns, "0");
					rPr.put("fontSize", fontSize);
				}
				// nazov fontu
				else if (rPrChild.getName().trim().equals("rFonts")) {
					String fontName = rPrChild.getAttributeValue("ascii", Global.wns, "");
					rPr.put("fontName", fontName);
				}
				// farba pisma
				else if (rPrChild.getName().trim().equals("color")) {
					String color = rPrChild.getAttributeValue("val", Global.wns, "");
					rPr.put("color", color);
				}
				// farba pozadia (paragraph)
				else if (rPrChild.getName().trim().equals("shd")) {
					String bkgColor = rPrChild.getAttributeValue("fill", Global.wns, "");
					rPr.put("bkgColor", bkgColor);
				}
				// farba pozadia (run)
				else if (rPrChild.getName().trim().equals("highlight")) {
					String bkgColor2 = rPrChild.getAttributeValue("val", Global.wns, "");
					rPr.put("bkgColor2", bkgColor2);
				}
				// externe styly
				else if (rPrChild.getName().trim().equals("rStyle")) {
					parseElementRStyle(rPrChild);
				}
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseElementRPr): " + e.toString());
		}
	}
	
	/**
	 * Funkcia, ktorá prejde daným footnote XML elementom
	 * @param elFootnote vstupný XML element
	 */
	private void parseElementFootnote(Element elFootnote) {
		try {
			if (Global.ooxmlParams.preserveFootnotes) {
				String id = elFootnote.getAttributeValue("id", Global.wns, "-1");
				String idNote = "note" + ++this.notesCount;
				Element footnote = Global.ooxmlNotes.getFootnote(id.trim());
				String footnoteTxt = Functions.getTextFromElement(footnote);
				footnote.setAttribute("id", idNote);
				String className = Global.cssOutput.addClassN(".super", "vertical-align: super; font-size: 70%;");
				// vloz do elementu id poznamky (neda sa to dat skor, lebo nevieme kolka poznamka to bola)
				Element pEl = footnote.getChild("p");
				Element titleNoteEl = new Element("span");
				titleNoteEl.setAttribute("class", className);
				titleNoteEl.setText(this.notesCount + "");
				pEl.addContent(0, titleNoteEl);
				this.notesEl.add(footnote);
				// priprav odkaz
				Element refEl = new Element("a");
				refEl.setAttribute("href", "#" + idNote);
				refEl.setAttribute("class", className);
				refEl.setAttribute("title", footnoteTxt);
				refEl.setText(this.notesCount + "");
				runEl.addContent(refEl);
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseElementFootnote): " + e.toString());
		}
	}
	
	/**
	 * Funkcia, ktorá prejde daným endnote XML elementom
	 * @param elEndnote vstupný XML element
	 */
	private void parseElementEndnote(Element elEndnote) {
		try {
			if (Global.ooxmlParams.preserveEndnotes) {
				String id = elEndnote.getAttributeValue("id", Global.wns, "-1");
				String idNote = "note" + ++this.notesCount;
				Element endnote = Global.ooxmlNotes.getEndnote(id.trim());
				String endnoteTxt = Functions.getTextFromElement(endnote);
				endnote.setAttribute("id", idNote);
				String className = Global.cssOutput.addClassN(".super", "vertical-align: super; font-size: 70%;");
				// vloz do elementu id poznamky (neda sa to dat skor, lebo nevieme kolka poznamka to bola)
				Element pEl = endnote.getChild("p");
				Element titleNoteEl = new Element("span");
				titleNoteEl.setAttribute("class", className);
				titleNoteEl.setText(this.notesCount + "");
				pEl.addContent(0, titleNoteEl);
				this.notesEl.add(endnote);
				// priprav odkaz
				Element refEl = new Element("a");
				refEl.setAttribute("href", "#" + idNote);
				refEl.setAttribute("class", className);
				refEl.setAttribute("title", endnoteTxt);
				refEl.setText(this.notesCount + "");
				runEl.addContent(refEl);
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseElementEndnote): " + e.toString());
		}
	}
	
	/**
	 * Funkcia, ktorá prejde daným "r" XML elementom
	 * @param elR vstupný XML element
	 */
	private void parseElementR(Element elR) {
		try {
			// najprv nastav runEl, do ktoreho sa bude pridavat text
			runEl = new Element("span");

			// prejdi vsetkymi potomkami "run" elementu
			List rChildren = elR.getChildren();
			Iterator rIterator = rChildren.iterator();
			while (rIterator.hasNext()) {
				Element rChild = (Element) rIterator.next();
				// zisti co je rChild

				// "run properties" element
				if (rChild.getName().trim().equals("rPr")) {
					parseElementRPr(rChild);
				}
				// "text" element
				else if (rChild.getName().trim().equals("t")) {
					parseElementT(rChild);
				}
				// "br" element
				else if (rChild.getName().trim().equals("br")) {
					runEl.addContent(new Element("br"));
				}
				// footnote element
				else if (rChild.getName().trim().equals("footnoteReference")) {
					parseElementFootnote(rChild);
				}
				// endnote element
				else if (rChild.getName().trim().equals("endnoteReference")) {
					parseElementEndnote(rChild);
				}
				// "drawing" element
				else if (Global.ooxmlParams.preserveImages && rChild.getName().trim().equals("drawing")) {
					parseElementDrawing(rChild);
				}
			}
			if (!isVisualEmpty(runEl)) {
				// kvoli "heuristike" na rozoznavanie nadpisov
				boolean isBold = false;
				int fontSize = 0;
				
				// css styly (nazvy tried)
				String stylesClasses = "";
				// strike
				if (Global.ooxmlPreserve.strike && rPr.containsKey("strike") && rPr.get("strike").equals("on")) {
					stylesClasses += " " + Global.ooxmlParams.cssS;
					Global.cssOutput.addClass("." + Global.ooxmlParams.cssS, "text-decoration: line-through;");
				}
				// sub
				if (Global.ooxmlPreserve.subscript && rPr.containsKey("sub") && rPr.get("sub").equals("on")) {
					stylesClasses += " " + Global.ooxmlParams.cssLI;
					Global.cssOutput.addClass("." + Global.ooxmlParams.cssLI, "vertical-align: sub; font-size: 70%;");
				}
				// super
				if (Global.ooxmlPreserve.superscript && rPr.containsKey("super") && rPr.get("super").equals("on")) {
					stylesClasses += " " + Global.ooxmlParams.cssUI;
					Global.cssOutput.addClass("." + Global.ooxmlParams.cssUI, "vertical-align: super; font-size: 70%;");
				}
				// underline
				if (Global.ooxmlPreserve.underline && rPr.containsKey("u") && rPr.get("u").equals("on")) {
					stylesClasses += " " + Global.ooxmlParams.cssU;
					Global.cssOutput.addClass("." + Global.ooxmlParams.cssU, "text-decoration: underline;");
				}
				// italic
				if (Global.ooxmlPreserve.italic && rPr.containsKey("i") && rPr.get("i").equals("on")) {
					stylesClasses += " " + Global.ooxmlParams.cssI;
					Global.cssOutput.addClass("." + Global.ooxmlParams.cssI, "font-style: italic;");
				}
				// bold
				if (Global.ooxmlPreserve.bold && rPr.containsKey("b") && rPr.get("b").equals("on")) {
					stylesClasses += " " + Global.ooxmlParams.cssB;
					Global.cssOutput.addClass("." + Global.ooxmlParams.cssB, "font-weight: bold;");
					isBold = true;
				}
				// velkost fontu
				if (Global.ooxmlPreserve.fontSize && rPr.containsKey("fontSize") && !rPr.get("fontSize").trim().isEmpty()) {
					fontSize = Functions.intValDef(rPr.get("fontSize"), 0);
					fontSize = fontSize / 2;
					String className = Global.cssOutput.addClassN(".fontSize", "font-size: " + fontSize + "pt;");
					stylesClasses += " " + className;
				}
				// meno fontu
				if (Global.ooxmlPreserve.fontName && rPr.containsKey("fontName") && !rPr.get("fontName").trim().isEmpty()) {
					String fontName = rPr.get("fontName");
					String className = Global.cssOutput.addClassN(".fontName", "font-family: \"" + fontName + "\";");
					stylesClasses += " " + className;
				}
				// farba pisma
				if (Global.ooxmlPreserve.color && rPr.containsKey("color") && !rPr.get("color").trim().isEmpty()) {
					String color = rPr.get("color");
					String className = Global.cssOutput.addClassN(".color", "color: #" + color + ";");
					stylesClasses += " " + className;
				}
				// farba pozadia (run)
				if (Global.ooxmlPreserve.background) {
					if (rPr.containsKey("bkgColor2") && !rPr.get("bkgColor2").trim().isEmpty()) {
						String bkgColor2 = rPr.get("bkgColor2");
						String className = Global.cssOutput.addClassN(".bkgColor", "background-color: " + bkgColor2 + ";");
						stylesClasses += " " + className;
					}
					else {
						// farba pozadia (paragraph) - iba v pripade, ze nie je nastavena farba na "run"
						if (rPr.containsKey("bkgColor") && !rPr.get("bkgColor").trim().isEmpty()) {
							String bkgColor = rPr.get("bkgColor");
							String className = Global.cssOutput.addClassN(".bkgColor", "background-color: #" + bkgColor + ";");
							stylesClasses += " " + className;
						}
					}
				}

				// kvoli zistovaniu nadpisu zisti nazov stylu (id)
				String styleId = "";
				if (rPr.containsKey("styleId") && !rPr.get("styleId").trim().isEmpty()) {
					styleId = rPr.get("styleId");
				}

				// zisti ci je dany R element nadpis
				String heading = "";
				if (Global.ooxmlParams.useHHeuristic) {
					if ((fontSize > Global.ooxmlStyles.defFontSize && isBold == true)) {
						heading = Global.ooxmlParams.htmlTagForHeading;
					}
				}
				if ((styleId.toLowerCase().contains(Global.ooxmlParams.heading1StyleName))) {
					heading = "h1";
				}
				else if ((styleId.toLowerCase().contains(Global.ooxmlParams.heading2StyleName))) {
					heading = "h2";
				}
				else if ((styleId.toLowerCase().contains(Global.ooxmlParams.heading3StyleName))) {
					heading = "h3";
				}
				if (!heading.isEmpty() && !isVisualEmpty(runEl)) {
					this.headingInR = heading;
				}
				else {
					this.headingInR = "";
				}
				
				// pridaj triedy elementu, ktory nahradza run
				stylesClasses = stylesClasses.trim();
				if (!stylesClasses.trim().isEmpty()) {
					runEl.setAttribute("class", stylesClasses);
					paragraphEl.addContent((Element)runEl.clone());
				}
				else {
					// ak nepridavame ziadne formatovanie, tak pridaj iba obsah elementu span
					paragraphEl.addContent(runEl.cloneContent());								
				}
			}
			runEl.removeContent();
			rPr.clear();
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseElementR): " + e.toString());
		}
	}
	
	/**
	 * Funkcia, ktorá prejde obsahom hyperlinku a vráti ho
	 * @param elHC vstupný XML element
	 */
	private void parseHyperlinkContent(Element elHC) {
		try {
			// najprv nastav runEl, do ktoreho sa bude pridavat text
			runEl = new Element("span");

			// prejdi vsetkymi potomkami "run" elementu
			List rChildren = elHC.getChildren();
			Iterator rIterator = rChildren.iterator();
			while (rIterator.hasNext()) {
				Element rChild = (Element) rIterator.next();
				// zisti co je rChild

				// "run properties" element
				if (rChild.getName().trim().equals("rPr")) {
					// nateraz nechceme prenasat formatovanie
				}
				// "text" element
				else if (rChild.getName().trim().equals("t")) {
					parseElementT(rChild);
				}
				// "drawing" element
				else if (rChild.getName().trim().equals("drawing")) {
					parseElementDrawing(rChild);
				}
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseHyperlinkContent): " + e.toString());
		}
	}
	
	/**
	 * Funkcia, ktorá prejde daným "t" XML elementom
	 * @param elT vstupný XML element
	 */
	private void parseElementT(Element elT) {
		try {
			// ak je nastaveny atribut space na preserve, tak zachovaj medzery
			String preserveSpace = elT.getAttributeValue("space", Global.xmlns);
			if ((preserveSpace != null) && (preserveSpace.trim().equals("preserve"))) {
				String text = elT.getText();
				if (text.trim().isEmpty() && !text.isEmpty()) {
					// ak retazec obsahuje iba medzere, tak ich prepis na &nbsp;
					for (int i = 0; i < text.length(); i++) {
						runEl.addContent(new EntityRef("nbsp"));
					}
				}
				else {
					runEl.addContent(elT.getText());
				}
			}
			else {
				runEl.addContent(elT.getText().trim());
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseElementT): " + e.toString());
		}
	}
	
	/**
	 * Funkcia, ktorá prejde daným "document" XML elementom a vytvorí z neho XHTML výstupný element
	 * @param elDocument vstupný XML element
	 * @param elXHTML výstupný XHTML element
	 */
	private void parseElementDocument(Element elDocument, Element elXHTML) {
		try {
			// sme v elemente "document"
			if (Global.ooxmlPreserve.documentBackground) {
				// zisti, ci nema dokument nejaku farbu pozadia
				Element background = elDocument.getChild("background", Global.wns);
				if (background != null) {
					String color = background.getAttributeValue("color", Global.wns);
					if ((color != null) && (color.length() == 6)) {
						Global.cssOutput.addClass("body", "background-color: #" + color + ";");
					}
				}
			}
			// preparsuj body element (container na hlavnu cast)
			Element body = elDocument.getChild("body", Global.wns);
			if (body != null) {
				parseMain(body,elXHTML);
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseElementDocument): " + e.toString());
		}
	}
	
	/**
	 * Funkcia ktorá preparsuje zadaný - main - element, co je container na hlavnú časť.
	 * @param elContainer XML element na sparsovanie
	 * @param elXHTML výstupný XHTML element
	 */
	private void parseMain(Element elContainer, Element elXHTML) {
		try {
			List containerChildren = elContainer.getChildren();
			Iterator containerIterator = containerChildren.iterator();
			while (containerIterator.hasNext()) {
				Element containerChild = (Element) containerIterator.next();
				// zisti co je bodyChild

				// "paragraph" element
				if (containerChild.getName().trim().equals("p")) {
					// 
					parseElementP(containerChild);
					if (pPr.containsKey("numLevel") && !pPr.get("numLevel").isEmpty()) {
						// ak je v "p" elemente item z numbering
						processNumbering();
					}
					else {
						if (this.numLvl >= 0) {
							// ak skoncil numbering...
							finishNumbering(elXHTML);
						}
						// ukonci pracu s aktualnym "p" elementom
						finishP(elXHTML);
					}
				}

				// "table" element
				if (containerChild.getName().trim().equals("tbl")) {
					parseElementTbl(containerChild, elXHTML);
				}
				
				if (paragraphEl != null) paragraphEl.removeContent();
				pPr.clear();
			}
			
			if (this.numLvl >= 0) {
				// ak skoncil numbering...
				finishNumbering(elXHTML);
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseMain): " + e.toString());
		}
	}
	
	/**
	 * Funkcia ktorá preparsuje zadaný XML súbor
	 * @param fileToParse XML súbor na sparsovanie
	 * @return XHTML element - výstup
	 */
	public Element parse(File fileToParse) {
		try {
			// XML ktore sa parsuje
			SAXBuilder builder = new SAXBuilder();
			Document doc = builder.build(fileToParse);
			Element root = doc.getRootElement();
			// XHTML vystup
			Element bodyEl = new Element("body");
			parseElementDocument(root, bodyEl);
			// pridaj notes ak treba
			if (this.notesCount > 0) {
				Element delimiterEl = new Element("hr");
				bodyEl.addContent(delimiterEl);
				Iterator i = this.notesEl.iterator();
				while (i.hasNext()) {
					Element el = (Element)i.next();
					bodyEl.addContent(el);
				}
			}
			return bodyEl;
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parse): " + e.toString());
			return null;
		}
	}
	
	/**
	 * Funkcia ktorá preparsuje zadaný element
	 * @param main container na hlavnú časť
	 * @return XHTML element - výstup
	 */
	public Element parse(Element main) {
		try {
			// XHTML vystup
			Element container = new Element("div");
			parseMain(main, container);
			return container;
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parse): " + e.toString());
			return null;
		}
	}
	
}
