Added Liferay export and import
diff --git a/conf/converter.liferayhierarchy.properties b/conf/converter.liferayhierarchy.properties
new file mode 100644
index 0000000..f1310ea
--- /dev/null
+++ b/conf/converter.liferayhierarchy.properties
@@ -0,0 +1,77 @@
+FilepathHierarchy.0001.switch.hierarchy-builder=UseBuilder
+FilepathHierarchy.0002.classname.hierarchy-builder=com.atlassian.uwc.hierarchies.FilepathHierarchy
+FilepathHierarchy.0003.doc-directory-attachments.property=C:\Temp\WikOut\attach
+FilepathHierarchy.0004.filepath-hierarchy-ext.property=
+#FilepathHierarchy.0005.filepath-hierarchy-matchpagename.property=true
+FilepathHierarchy.0006.filepath-hierarchy-ignorable-ancestors.property=C:\Temp\WikiOut\root
+#FilepathHierarchy.0007.doc-directory-template.property=/Users/laura/Code/Subversion/uwc-spac/sampleData/docdirectory/testtemplate.txt
+FilepathHierarchy.0008.doc-directory-exclude.property=\.svn
+FilepathHierarchy.0009.engine-saves-to-disk.property=true
+
+##### Tokenizer adds ######xxxyyy blocks that stop the regex's parsing the matched delimeters
+## This can be used to stop converters changing values in the source.
+
+## Disabled illegal names/links handling as this changes various links from [[ to [( for some reason?
+FilepathHierarchy..0010.illegal-handling=false
+
+FilepathHierarchy.0011-attachments.class=com.atlassian.uwc.converters.mediawiki.AttachmentConverter
+
+FilepathHierarchy.0012-html.java-regex-tokenizer=\{html\}((?s).*?)\{html\}{replace-with}{html}$1{html}
+                     
+
+## {{{ }}} is no format on LR
+FilepathHierarchy.0060-codeblock.java-regex-tokenizer=(?s)\{{3}(.*?)\}{3}{replace-with}{code}$1{code}
+
+## Code, Pre, and Leading Spaces
+FilepathHierarchy.0090-re_pre.java-regex-tokenizer=\<pre\>((?s).*?)\<\/pre\>{replace-with}{code}$1{code}
+FilepathHierarchy.0095-re_code.java-regex-tokenizer=\<code\>((?s).*?)\<\/code\>{replace-with}{code}$1{code}
+
+FilepathHierarchy.0356.span-color.java-regex-tokenizer=<span style=\"color:([^\"]+)\">(.*?)<\/span>{replace-with}{color:$1}$2{color}
+
+# TOC
+FilepathHierarchy.0063-format-TOC.java-regex-tokenizer=(<<TableOfContents>>){replace-with}{toc:style=decimal|maxLevel=2}
+
+## |= is heading in LR - in Confluence its ||
+FilepathHierarchy.0540-re_title.java-regex=(\|\=){replace-with}||
+
+## || is OK for table formattiog in LR, in Confluence we need spaces between
+FilepathHierarchy.0063-table-addspace.java-regex=(\|\|){replace-with} | | 
+
+## [page|displayed link] in LR - in Confluence its [page].
+FilepathHierarchy.0420-re_links_alias.java-regex=\[\[([^|]*)\| *([^\]]*)\]\]{replace-with}[$1]
+
+## set the images-all property to false if you only want the images and 
+## attachments that the page text refer to. Otherwise, all attached images will 
+## be used.
+FilepathHierarchy.0500.images-all.property=true
+
+## == this is legal in LR for a heading so convert to h1. or h2.
+FilepathHierarchy.0338-rex_h4.java-regex=(?m)^={5}([\s\w\d()-:/,.]+)${replace-with}h4. $1
+FilepathHierarchy.0339-rex_h3.java-regex=(?m)^={4}([\s\w\d()-:/,.]+)${replace-with}h3. $1
+FilepathHierarchy.0340-rex_h2.java-regex=(?m)^={3}([\s\w\d()-:/,.]+)${replace-with}h2. $1
+FilepathHierarchy.0341-rex_h1.java-regex=(?m)^={2}([\s\w\d()-:/,.]+)${replace-with}h1. $1
+
+FilepathHierarchy.0500-re_h4.java-regex=={5}\s*(.*?)\s*={5}{replace-with}h4. $1
+FilepathHierarchy.0510-re_h3.java-regex=={4}\s*(.*?)\s*={4}{replace-with}h3. $1
+FilepathHierarchy.0520-re_h2.java-regex=={3}\s*(.*?)\s*={3}{replace-with}h2. $1
+FilepathHierarchy.0530-re_h1.java-regex=={2}\s*(.*?)\s*={2}{replace-with}h1. $1
+
+## bold text in LR is two *, in Confluence it's one.
+FilepathHierarchy.0550-re_text_bold.java-regex=\*\*(.+)\*\*{replace-with}*$1*
+
+## Italics in LR is //, in Confluence it's _one_.
+FilepathHierarchy.0560-re_text_italic.java-regex=//(.+)//{replace-with}_$1_
+
+
+## NOTE: Images must come after tables or the whitespace gets screwed up if a table has images
+## Images must come before Links or Alias handling will make Image conversion more complicated
+FilepathHierarchy.0370-re_images.class=com.atlassian.uwc.converters.mediawiki.ImageConverter
+FilepathHierarchy.0710-images_ws2underscore.class=com.atlassian.uwc.converters.mediawiki.ImageWhitespaceConverter
+
+## { } is insert macro in Confluence and nothing in LR so escape it
+FilepathHierarchy.0862-escaping-curlybrace.java-regex=(\{){replace-with} \\{
+
+
+##### For any tokenizer regex above, strip out tokens #####
+FilepathHierarchy.2000-detokenize.class=com.atlassian.uwc.converters.DetokenizerConverter
+
diff --git a/conf/exporter.liferayhierarchy.properties b/conf/exporter.liferayhierarchy.properties
new file mode 100644
index 0000000..662b3bc
--- /dev/null
+++ b/conf/exporter.liferayhierarchy.properties
@@ -0,0 +1,10 @@
+# Exporter class
+exporter.class=com.atlassian.uwc.exporters.LiferayExporter
+
+# The Liferay export.lar file is a zip archive. 
+# Your directory where your unzipped liferay export is located
+src=C:\Temp\WikiIn\
+
+# Where your export dir should go
+out=C:\Temp\WikiOut\
+
diff --git a/lib/commons-cli-1.2.jar b/lib/commons-cli-1.2.jar
new file mode 100644
index 0000000..ce4b9ff
--- /dev/null
+++ b/lib/commons-cli-1.2.jar
Binary files differ
diff --git a/lib/commons-digester3-3.2-with-deps.jar b/lib/commons-digester3-3.2-with-deps.jar
new file mode 100644
index 0000000..ec79528
--- /dev/null
+++ b/lib/commons-digester3-3.2-with-deps.jar
Binary files differ
diff --git a/src/com/atlassian/uwc/exporters/LiferayExporter.java b/src/com/atlassian/uwc/exporters/LiferayExporter.java
new file mode 100644
index 0000000..db71214
--- /dev/null
+++ b/src/com/atlassian/uwc/exporters/LiferayExporter.java
@@ -0,0 +1,301 @@
+package com.atlassian.uwc.exporters;
+
+import java.io.File;
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.management.RuntimeErrorException;
+
+import org.apache.commons.cli.BasicParser;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Options;
+import org.apache.log4j.Logger;
+import org.xml.sax.SAXException;
+
+import com.atlassian.uwc.exporters.liferay.Params;
+import com.atlassian.uwc.exporters.liferay.Util;
+import com.atlassian.uwc.exporters.liferay.digester.Indigestion;
+import com.atlassian.uwc.exporters.liferay.digester.LrWikiTree;
+import com.atlassian.uwc.exporters.liferay.digester.Manifest;
+import com.atlassian.uwc.exporters.liferay.digester.Page;
+import com.atlassian.uwc.exporters.liferay.digester.Portlet;
+import com.atlassian.uwc.exporters.liferay.digester.PressCancelException;
+import com.atlassian.uwc.exporters.liferay.digester.WikiData;
+import com.atlassian.uwc.exporters.liferay.digester.WikiPage;
+import com.atlassian.uwc.ui.FileUtils;
+
+/**
+ * Liferay:
+ * Only Creole and HTML format pages are handled.
+ * Images attached to a Liferay page must be linked to in the page text i.e. [[filename.zip]].
+ * Takes the latest version only, no history.
+
+Simplified LAR structure. This ignores some unneeded directories and files.
+
+    manifest.xml - Export timestamp, path to portlet-data.xml (Same dir as portlet.xml)
+    groups
+        comments - Comments data
+        portlets
+            113518
+                portlet-data.xml - Primary index for all nodes, pages and attachments.
+            bin - Attachment files
+            nodes
+                1113469.xml - Wiki last change date?
+            pages - Page data (all versions, see portlet-data.xml to find current page versions)
+        comments.xml - Links comment and page data
+
+
+
+Confluence:
+The HTML Macro must be enabled if you have HTML pages to upload.
+
+ * @author SJC 
+ *
+ */
+public class LiferayExporter implements Exporter {
+	private static Logger log = Logger.getLogger(LiferayExporter.class);
+	static final String VERSION = "1.1";
+	static final String OPT_HELP = "help";
+	static final String OPT_LARDIR = "lar";
+	static final String OPT_OUTDIR = "out";
+	static final String OPT_CON_START = "start";
+	static final String OPT_ORIGINAL = "original";
+	static final String OPT_NOHTML = "nohtml";
+	static final String OPT_CON_ORPHANS = "orphans";
+	private boolean running = false;
+	private Map<String, String> properties;
+
+	@Override
+	public void cancel() {
+		log.info("Export Cancelled.");
+		this.running = false;
+	}
+
+	@Override
+	public void export(Map properties) {
+		this.running = true;
+		setProperties(properties);
+
+		try {
+			Params params = new Params();
+			
+			// TODO get other properties and set them in the params
+			// for instance opt.addOption(OPT_CON_ORPHANS, true, "Page name for orphan pages");
+			
+			params.setLarDirectory(getProp("src"));
+			params.setOutputDir(new File(getProp("out")));
+
+			convert(params);
+
+		} catch (PressCancelException e) {
+			// do nothing here as user has decided to abort
+			log.debug("Received PressCancelException");
+		} catch (Exception e) {
+			log.error("Problem while exporting. Exiting.", e);
+			e.printStackTrace();
+			
+			throw new RuntimeException(e);
+		}
+
+		if (this.running) {
+			log.info("Export Complete.");
+		}
+
+		this.running = false;
+	}
+
+	void checkCancel() {
+		if (!this.running) {
+			throw new PressCancelException();
+		}
+	}
+
+	public void setProperties(Map<String, String> props) {
+		this.properties = props;
+	}
+
+	public String getProp(String key) {
+		return getProperties().get(key);
+	}
+
+	public Map<String, String> getProperties() {
+		if (this.properties == null){
+			this.properties = new HashMap<String, String>();
+		}
+		
+		return this.properties;
+	}
+
+	public static void main(String[] args) {
+		CommandLine cl = null;
+		HelpFormatter hf = new HelpFormatter();
+		Options opt = new Options();
+
+		try {
+			Util.initLog4j();
+
+			opt.addOption(OPT_HELP, false, "Print help for this application.");
+			opt.addOption(OPT_LARDIR, true, "Unzipped Lifieray .lar file directory");
+			opt.addOption(OPT_OUTDIR, true, "Output directory");
+			opt.addOption(OPT_CON_START, true, "Confluence Start page");
+			opt.addOption(OPT_ORIGINAL, false, "Save original files from .lar");
+			opt.addOption(OPT_NOHTML, false, "Uploads HTML formatted pages as {code}");
+			opt.addOption(OPT_CON_ORPHANS, true, "Page name for orphan pages");
+
+			BasicParser parser = new BasicParser();
+			cl = parser.parse(opt, args);
+
+			if (cl.hasOption(OPT_HELP)) {
+				printUsage(hf, opt);
+			} else {
+
+				if (cl.hasOption(OPT_LARDIR) && cl.hasOption(OPT_OUTDIR)) {
+					LiferayExporter wtx = new LiferayExporter();
+					Params params = new Params();
+
+					// the unzipped .lar file directory
+					params.setLarDirectory(cl.getOptionValue(OPT_LARDIR));
+
+					params.setOutputDir(new File(cl.getOptionValue(OPT_OUTDIR)));
+					
+					// confluenceStartPage defaults to Home, this opt can overide this
+					String confluenceStartPage = cl.getOptionValue(OPT_CON_START);
+					if (confluenceStartPage != null) {
+						params.setConfluenceStartPage(confluenceStartPage);
+					}
+					
+					// confluence page off root to hold orphans.
+					String confluenceOrphanPage = cl.getOptionValue(OPT_CON_ORPHANS);
+					if (confluenceOrphanPage != null) {
+						params.setOrphanPages(confluenceOrphanPage);
+					}					
+
+					// copies the original pages into the destination. No conversion.
+					params.setOriginal(cl.hasOption(OPT_ORIGINAL));
+
+					// copies HTML pages as code
+					params.setNoHtml(cl.hasOption(OPT_NOHTML));
+
+					wtx.convert(params);
+					log.info("Completed Liferay to Confluence conversion");
+				} else {
+					printUsage(hf, opt);
+				}
+
+				System.exit(0);
+			}
+
+		} catch (Exception e) {
+			log.error("Main", e);
+		}
+	}
+
+	private static void printUsage(HelpFormatter hf, Options opt) {
+		hf.printHelp("WikiTransfer " + VERSION, "Use to pre process a Liferay Wiki export .lar file. "
+				+ "Only looks at the latest version - no history is transfered.", opt,
+				"\nE.G. WikiTransfer -lar C:\\WikiIn -out C:\\WikiOut\n", true);
+
+		System.exit(1);
+	}
+
+	public void convert(Params params) throws IOException, SAXException {
+		
+		// make the output directory if it doesn't exist, if it exists delete it.
+		createExportDir(params.getOutputDir());					
+		
+		LrWikiTree lrwt = new LrWikiTree();
+		log.info("Input directory (unzipped .lar): " + params.getLarDirectory());
+		readPagesInLarFile(params.getLarDirectory(), lrwt);
+
+		checkCancel();		
+		lrwt.linkChildren(false);
+
+		WikiPage frontPage = lrwt.findPage(LrWikiTree.FRONTPAGE);
+		frontPage.setOutDir(params.getOutputDir());
+		log.info("Output directory: " + frontPage.getOutDir());
+
+		lrwt.recursePages(frontPage);
+		
+		checkCancel();		
+		lrwt.reformatAttachments();
+		lrwt.saveAttachments(params.getLarDirectory(),
+				params.getOutputDir() + File.separator + params.getAttachmentDir());
+
+		checkCancel();
+		lrwt.reformatHtmlPages(params.isNoHtml());
+
+		ArrayList<WikiPage> oList = lrwt.findOrphans();
+		checkCancel();
+
+		if (!oList.isEmpty()) {
+			// remove orphans from the linked list and save them in an Uncategorizd page.
+			log.info("Placing " + oList.size() + " orphan pages in " + params.getOrphanPage() + " directory");
+			lrwt.unlinkChildren(oList);
+
+			String unCategorized = frontPage.getOutDir() + File.separator + frontPage.getPath() + File.separator
+					+ params.getConfluenceStartPage() + File.separator + params.getOrphanPage();
+
+			// create a page in the home directory that will hold all the orphans
+			lrwt.createUncategorizedChildPage(unCategorized, params.getOrphanPage());
+
+			lrwt.saveOrphanList(oList, unCategorized, params.isOriginal());
+			log.info("---End of orphan list---\r\n");
+		}
+
+		// Set Confluence home page by changing the page title. This title gets
+		// written as the filename directory on disk.
+		frontPage.set__title(params.getConfluenceStartPage());
+		checkCancel();
+
+		lrwt.saveRecursive(frontPage, params.isOriginal());
+	}
+
+	/**
+	 * The lar structure is defined by file paths inside XML files. This function parses the successive XML files and
+	 * finds the page content and attachments. The page history is discarded, we only want the current data.
+	 * 
+	 * @param larDirectory
+	 * @param lrwt
+	 *            this is filled with Java objects created from the files on disk.
+	 * @throws IOException
+	 * @throws SAXException
+	 */
+	private void readPagesInLarFile(String larDirectory, LrWikiTree lrwt) throws IOException, SAXException {
+		Indigestion xmlDigest = new Indigestion();
+		Manifest mf = xmlDigest.readManifestXml(larDirectory);
+
+		String path = mf.getPortlet().getPath();
+		File file = new File(larDirectory, path);
+		Portlet portlet = xmlDigest.readPortletXml(file);
+
+		path = portlet.getPortlet().getPath();
+		file = new File(larDirectory, path);
+
+		WikiData wd = xmlDigest.readPortletDataXml(file);
+		ArrayList<Page> pageList = wd.getPages().getPageList();
+
+		for (Page page : pageList) {
+			path = page.getPath();
+			file = new File(larDirectory, path);
+			WikiPage wp = xmlDigest.readWikiPageXml(file, page.getAttachments());
+			lrwt.addPage(wp);
+		}
+	}
+
+	private void createExportDir(File export) {
+		if (export.exists()) {
+			log.debug("Deleting existing export dir: " + export.getAbsolutePath());
+			FileUtils.deleteDir(export);
+		}
+		
+		if (!export.mkdirs()) {
+			log.error("Could not create export dir: " + export.getAbsolutePath());
+		}		
+	}
+	
+}
diff --git a/src/com/atlassian/uwc/exporters/liferay/Params.java b/src/com/atlassian/uwc/exporters/liferay/Params.java
new file mode 100644
index 0000000..1c023b2
--- /dev/null
+++ b/src/com/atlassian/uwc/exporters/liferay/Params.java
@@ -0,0 +1,79 @@
+package com.atlassian.uwc.exporters.liferay;
+
+import java.io.File;
+
+public class Params {
+	private String orphanPages = "Uncategorized";
+	private String confluenceStartPage = "Home";
+	private String attachmentDir = "attach";
+	private String larDirectory;
+	private File outputDir;
+	private boolean original = false;
+	private boolean noHtml = false;
+
+	/**
+	 * This is the name of the page in Confluence that will hold the orphans
+	 * 
+	 * @return
+	 */
+	public String getOrphanPage() {
+		return orphanPages;
+	}
+
+	public void setOrphanPages(String orphanPages) {
+		this.orphanPages = orphanPages;
+	}
+
+	public String getConfluenceStartPage() {
+		return confluenceStartPage;
+	}
+
+	public void setConfluenceStartPage(String confluenceStartPage) {
+		this.confluenceStartPage = confluenceStartPage;
+	}
+
+	public String getAttachmentDir() {
+		return attachmentDir;
+	}
+
+	public void setAttachmentDir(String attachmentDir) {
+		this.attachmentDir = attachmentDir;
+	}
+
+	public String getLarDirectory() {
+		return larDirectory;
+	}
+
+	public void setLarDirectory(String larDirectory) {
+		this.larDirectory = larDirectory;
+	}
+
+	public File getOutputDir() {
+		return outputDir;
+	}
+
+	public void setOutputDir(File outputDir) {
+		this.outputDir = outputDir;
+	}
+
+	public boolean isOriginal() {
+		return original;
+	}
+
+	public void setOriginal(boolean original) {
+		this.original = original;
+	}
+
+	public void setOriginal(String original) {
+		this.original = Boolean.parseBoolean(original);
+	}
+
+	public boolean isNoHtml() {
+		return noHtml;
+	}
+
+	public void setNoHtml(boolean noHtml) {
+		this.noHtml = noHtml;
+	}
+
+}
diff --git a/src/com/atlassian/uwc/exporters/liferay/Util.java b/src/com/atlassian/uwc/exporters/liferay/Util.java
new file mode 100644
index 0000000..b0c42c6
--- /dev/null
+++ b/src/com/atlassian/uwc/exporters/liferay/Util.java
@@ -0,0 +1,140 @@
+package com.atlassian.uwc.exporters.liferay;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Properties;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.apache.log4j.BasicConfigurator;
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+
+/** General utility methods */
+public class Util {
+
+	/** Load properties from resource. */
+	public static Properties load(Object context, String fileName) {
+		Properties properties = null;
+		InputStream inputStream = null;
+
+		String currentDir = new File(".").getAbsolutePath();
+
+		try {
+			properties = new Properties();
+			inputStream = new FileInputStream(fileName);
+			properties.load(inputStream);
+			inputStream.close();
+		} catch (Exception e) {
+			throw new IllegalArgumentException("Properties file: " + currentDir + " " + fileName, e);
+		} finally {
+			if (inputStream != null) {
+				try {
+					inputStream.close();
+				} catch (Exception e) {
+					/* Ignore */
+				}
+			}
+		}
+		return properties;
+	}
+
+	public static Reader loadResource(Object context, String fileName) {
+		Reader properties = null;
+		
+		String currentDir = new File(".").getAbsolutePath();
+
+		try {						
+			InputStream inputStream	= context.getClass().getResourceAsStream(fileName);						
+			properties = new InputStreamReader(inputStream);
+			
+//			inputStream.close();
+		} catch (Exception e) {
+			throw new IllegalArgumentException("Properties file: " + currentDir + " " + fileName, e);
+		}
+
+		return properties;
+	}
+
+	static void printClasspath() {
+		System.out.println("Classpath:-");
+
+		ClassLoader cl = ClassLoader.getSystemClassLoader();
+
+		URL[] urls = ((URLClassLoader) cl).getURLs();
+
+		for (URL url : urls) {
+			System.out.println(url.getFile());
+		}
+
+	}
+
+	/** Load xml file */
+	public static Document loadXml(String fileName) {
+		Document doc = null;
+		InputStream inputStream = null;
+		String currentDir = new File(".").getAbsolutePath();
+
+		try {
+			DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+			DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
+
+			inputStream = new FileInputStream(fileName);
+
+			doc = dBuilder.parse(inputStream);
+			doc.getDocumentElement().normalize();
+
+			inputStream.close();
+		} catch (Exception e) {
+			throw new IllegalArgumentException("XML file path: " + currentDir, e);
+		} finally {
+			if (inputStream != null) {
+				try {
+					inputStream.close();
+				} catch (Exception e) {
+					/* Ignore */
+				}
+			}
+		}
+
+		return doc;
+	}
+
+	public static String printDom(Document document) throws TransformerException {
+
+		String str = "";
+
+		Transformer transformer = TransformerFactory.newInstance().newTransformer();
+		ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+		Source source = new DOMSource(document);
+		Result output = new StreamResult(byteOut);
+		transformer.transform(source, output);
+		str = byteOut.toString();
+
+		return str;
+	}
+
+	/** Use default log4j config. */
+	public static void initLog4j() {
+		Logger root = Logger.getRootLogger();
+		if (!root.getAllAppenders().hasMoreElements()) {
+			BasicConfigurator.configure();
+			root.warn("log4j configuration file not found using defaults!!!");
+		}
+	}
+
+}
diff --git a/src/com/atlassian/uwc/exporters/liferay/digester/Attachment.java b/src/com/atlassian/uwc/exporters/liferay/digester/Attachment.java
new file mode 100644
index 0000000..c283069
--- /dev/null
+++ b/src/com/atlassian/uwc/exporters/liferay/digester/Attachment.java
@@ -0,0 +1,32 @@
+package com.atlassian.uwc.exporters.liferay.digester;
+
+
+
+public class Attachment {
+
+	private String binPath = "b-pathX";
+	private String name;	
+	
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getBinPath() {
+		return binPath;
+	}
+
+	public void setBinPath(String binPath) {
+		this.binPath = binPath;
+	}
+
+	@Override
+	public String toString() {
+		return "Attachment [name=" + name + "]";
+	}
+
+
+}
diff --git a/src/com/atlassian/uwc/exporters/liferay/digester/Indigestion.java b/src/com/atlassian/uwc/exporters/liferay/digester/Indigestion.java
new file mode 100644
index 0000000..6a6b80c
--- /dev/null
+++ b/src/com/atlassian/uwc/exporters/liferay/digester/Indigestion.java
@@ -0,0 +1,125 @@
+package com.atlassian.uwc.exporters.liferay.digester;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import org.apache.commons.digester3.Digester;
+import org.apache.log4j.Logger;
+import org.xml.sax.SAXException;
+
+public class Indigestion {
+	private static Logger _log = Logger.getLogger(Indigestion.class);
+
+	public Manifest readManifestXml(String directory) throws IOException, SAXException {
+		Manifest retval = null;
+		File file = new File(directory, "manifest.xml");
+
+		Digester digester = new Digester();
+		digester.setValidating(false);
+
+		digester.addObjectCreate("root", Manifest.class);
+		digester.addObjectCreate("root/portlet", ManifestPortlet.class);
+		digester.addSetProperties("root/portlet");
+		digester.addSetNext("root/portlet", "setPortlet");
+
+		retval = (Manifest) digester.parse(file);
+
+		_log.debug(retval.toString());
+
+		return retval;
+	}
+
+//	@formatter:off
+/*
+	<portlet portlet-id="36" root-portlet-id="36" old-plid="113518" scope-layout-type="" scope-layout-uuid="">
+		<portlet-data path="/groups/113333/portlets/36/113518/portlet-data.xml"/>
+*/
+//	@formatter:on
+	public Portlet readPortletXml(File file) throws IOException, SAXException {
+		Portlet retval = null;
+
+		Digester digester = new Digester();
+		digester.setValidating(false);
+
+		digester.addObjectCreate("portlet", Portlet.class);
+		digester.addObjectCreate("portlet/portlet-data", PortletData.class);
+		digester.addSetProperties("portlet/portlet-data");
+		digester.addSetNext("portlet/portlet-data", "setPortlet");
+
+		retval = (Portlet) digester.parse(file);
+
+		_log.debug(retval.toString());
+
+		return retval;
+	}
+
+//	@formatter:off
+/*
+<wiki-data group-id="113333">
+	<pages>
+		<page image-path="/groups/113333/portlets/36/page/028b08d8-1a74-4542-a3ec-9e429374f36c/1.0/" path="/groups/113333/portlets/36/pages/320552.xml"/>
+		<page image-path="/groups/113333/portlets/36/page/0f07a009-8107-49bb-8612-48fb7b52c37a/1.0/" path="/groups/113333/portlets/36/pages/347437.xml">
+			<attachment name="java-proxy-settings.jpg" bin-path="/groups/113333/portlets/36/bin/347437/java-proxy-settings.jpg"/>
+		</page>
+*/
+//	@formatter:on
+	public WikiData readPortletDataXml(File file) throws IOException, SAXException {
+		WikiData retval = null;
+
+		Digester digester = new Digester();
+		digester.setValidating(false);
+
+		digester.addObjectCreate("wiki-data", WikiData.class);
+		//element <pages>
+		digester.addObjectCreate("wiki-data/pages", Pages.class);
+		digester.addSetProperties("wiki-data/pages");
+		digester.addSetNext("wiki-data/pages", "setPages");
+
+		//element <page>
+		digester.addObjectCreate("wiki-data/pages/page", Page.class);
+		digester.addSetProperties("wiki-data/pages/page");
+		digester.addSetNext("wiki-data/pages/page", "setPage");
+
+		//element <attachment>
+		digester.addObjectCreate("wiki-data/pages/page/attachment", Attachment.class);
+		digester.addSetProperties("wiki-data/pages/page/attachment", "bin-path", "binPath");
+		digester.addSetNext("wiki-data/pages/page/attachment", "setAttachment");
+
+		retval = (WikiData) digester.parse(file);
+
+		_log.debug(retval.toString());
+
+		return retval;
+	}
+
+/* @formatter:off
+	<WikiPage>
+	<__new>false</__new>
+	...
+*/
+//	@formatter:on
+	public WikiPage readWikiPageXml(File file, ArrayList<Attachment> attachments) throws IOException, SAXException {
+		WikiPage retval = null;
+
+		Digester digester = new Digester();
+		digester.setValidating(false);
+
+		digester.addObjectCreate("WikiPage", WikiPage.class);
+		digester.addBeanPropertySetter("WikiPage/__title");
+		digester.addBeanPropertySetter("WikiPage/__parentTitle");
+		digester.addBeanPropertySetter("WikiPage/__redirectTitle");
+		digester.addBeanPropertySetter("WikiPage/__format");
+		digester.addBeanPropertySetter("WikiPage/__version");
+		digester.addBeanPropertySetter("WikiPage/__content");
+
+		retval = (WikiPage) digester.parse(file);		
+		retval.setAttachments(attachments);
+		retval.setFile(file);
+
+		// _log.debug(retval.toString());
+
+		return retval;
+	}
+
+}
diff --git a/src/com/atlassian/uwc/exporters/liferay/digester/LrWikiTree.java b/src/com/atlassian/uwc/exporters/liferay/digester/LrWikiTree.java
new file mode 100644
index 0000000..2672ac3
--- /dev/null
+++ b/src/com/atlassian/uwc/exporters/liferay/digester/LrWikiTree.java
@@ -0,0 +1,511 @@
+package com.atlassian.uwc.exporters.liferay.digester;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.Collections;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Liferay (LR) wiki page processor.
+ * 
+ * @author Developer
+ * 
+ */
+public class LrWikiTree {
+	public static final String FRONTPAGE = "FrontPage";
+
+	private ArrayList<WikiPage> pageList = new ArrayList<WikiPage>();
+	private static Logger _log = Logger.getLogger(LrWikiTree.class);
+
+	/**
+	 * Adds pages to the internal page list. We only keep the current page and discard historical pages.
+	 * 
+	 * @param page
+	 */
+	public void addPage(WikiPage page) {
+		// if we have an existing page check the version and discard ancient pages
+		WikiPage wp = findPage(page.get__title());
+
+		if (wp != null) {
+			Double version = wp.getVersion();
+
+			// only keep latest version of the page
+			if (page.getVersion() > version) {
+				pageList.remove(wp);
+				pageList.add(page);
+			}
+		} else {
+			// no previous version so add the page
+			// _log.debug("Adding Page: " + page.get__title());
+			pageList.add(page);
+		}
+	}
+
+	/**
+	 * Finds pages that don't have parents and are not named Frontpage.
+	 * 
+	 * @return
+	 */
+	public ArrayList<WikiPage> findOrphans() {
+		ArrayList<WikiPage> retval = new ArrayList<WikiPage>();
+
+		for (WikiPage page : pageList) {
+			String parentTitle = page.get__parentTitle();
+
+			if (parentTitle.isEmpty() && !page.get__title().equals(FRONTPAGE)) {
+				retval.add(page);
+			} else if (!page.get__redirectTitle().isEmpty()) {
+				retval.add(page);
+			}
+		}
+
+		return retval;
+	}
+
+	/**
+	 * Creates a page in the home directory that will hold all the orphans. Also creates the directory structure on disk
+	 * for the pages.
+	 * 
+	 * @param outputDir
+	 * @throws IOException
+	 */
+	public void createUncategorizedChildPage(String outputDir, String orphanPage) throws IOException {
+		String uncategorizedPage = "== Overview ==\r\n\r\nPages that don't have parents are placed here after transfering the Wiki into Confluence.";
+
+		File dir = new File(outputDir);
+		dir.mkdirs();
+
+		File unCat = new File(outputDir + File.separator + orphanPage);
+
+		write(unCat, uncategorizedPage);
+	}
+
+	/**
+	 * Copies the page content of the files in the list to the output directory.
+	 * 
+	 * @param list
+	 * @param outputDir
+	 * @throws IOException
+	 */
+	public void saveOrphanList(ArrayList<WikiPage> list, String outputDir, boolean original) throws IOException {
+
+		for (WikiPage page : list) {
+			page.setDepth(0); // page is an orphan so reset it's place to the root in the hierarchy
+			recursePages(page);
+
+			page.setPath("");
+			page.setOutDir(new File(outputDir));
+
+			saveOrphanRecursive(page, original);
+		}
+	}
+
+	/**
+	 * Writes the content of the orphaned pages onto the Uncategorized directory. Child pages are written maintaining
+	 * the hierarchy in the file system.
+	 * 
+	 * @param frontPage
+	 * @param copyOriginal
+	 *            true to copy the original xml file to the destination
+	 * @throws IOException
+	 */
+	public void saveOrphanRecursive(WikiPage frontPage, boolean copyOriginal) throws IOException {
+		String padding = "";
+		for (int i = 0; i < frontPage.getDepth(); i++) {
+			padding += "    ";
+		}
+
+		_log.info(padding + frontPage.get__title());
+
+		File dir = new File(frontPage.getOutDir() + File.separator + frontPage.getPath());
+		dir.mkdirs();
+
+		// _log.info("++" + dir.getCanonicalPath());
+
+		// _log.debug("Create Dir: " + dir);
+		if (copyOriginal) {
+			copyFile(frontPage.getFile(), new File(dir, frontPage.get__title() + ".xml"));
+		} else {
+			File filePage = new File(dir, frontPage.get__title());
+			write(filePage, frontPage.get__content());
+		}
+
+		if (!frontPage.getAttachments().isEmpty()) {
+			_log.info(padding + "  " + frontPage.getAttachments().size() + "-Attachments " + frontPage.getAttachments());
+		}
+
+		for (WikiPage page : frontPage.getChildren()) {
+			page.setPath(frontPage.getPath());
+			page.setOutDir(frontPage.getOutDir());
+
+			saveRecursive(page, copyOriginal);
+		}
+	}
+
+	/**
+	 * Removes the parent child relationship for the given pages.
+	 * 
+	 * @param pages
+	 */
+	public void unlinkChildren(ArrayList<WikiPage> pages) {
+		for (WikiPage page : pages) {
+			_log.debug("Removing page '" + page.get__title() + "'");
+			if (!pageList.remove(page)) {
+				_log.error("Can't remove: " + page.get__title());
+			}
+
+			// We also need to remove the child from the parent child relationship
+			WikiPage parent = findPage(page.get__parentTitle());
+			if (parent != null) {
+				if (!parent.removeChild(page)) {
+					_log.error("Can't remove child from parent: " + page.get__parentTitle());
+				}
+			}
+		}
+	}
+
+	public WikiPage findPage(String title) {
+		WikiPage retval = null;
+
+		for (WikiPage page : pageList) {
+			if (page.get__title().equals(title)) {
+				retval = page;
+				break;
+			}
+		}
+
+		return retval;
+	}
+
+	/**
+	 * Creates a parent child relationship with the internal pages.
+	 * 
+	 * @param sort
+	 */
+	public void linkChildren(boolean sort) {
+
+		if (sort) {
+			Collections.sort(pageList);
+		}
+
+		for (WikiPage page : pageList) {
+			String parentTitle = page.get__parentTitle();
+
+			if (!parentTitle.isEmpty()) {
+				WikiPage parent = findPage(parentTitle);
+
+				if (parent != null) {
+					parent.addChildPage(page);
+
+				} else {
+					_log.debug("!!!Error finding Parent!!! " + parentTitle);
+				}
+			}
+		}
+	}
+
+	/**
+	 * copies all the attachments for all the pages into the given directory.
+	 * 
+	 * @param larDirectory
+	 * @param destDirName
+	 * @throws IOException
+	 */
+	public void saveAttachments(String larDirectory, String destDirName) throws IOException {
+		File destDir = new File(destDirName);
+		destDir.mkdirs();
+		_log.info("Copying attachments to: " + destDirName);
+
+		for (WikiPage page : pageList) {
+			ArrayList<Attachment> files = page.getAttachments();
+
+			for (Attachment attachment : files) {
+				String name = attachment.getBinPath();
+
+				copyFile(new File(larDirectory, name), new File(destDir, attachment.getName()));
+			}
+		}
+	}
+
+	/**
+	 * Attachments in the LR export are indistinguishable from links. The function looks for [[xxx]], where xxx is an
+	 * attachment and changes the download link to [^xxx]. This makes the UWC
+	 * com.atlassian.uwc.hierarchies.FilepathHierarchy class upload the attachment.
+	 * 
+	 * Image conversion {{image.jpg}} -> !image.jpg! is also handled here for the same reason as above.
+	 * 
+	 */
+	public void reformatAttachments() {
+
+		for (WikiPage page : pageList) {
+			ArrayList<Attachment> files = page.getAttachments();
+
+			for (Attachment attachment : files) {
+				String name = attachment.getName();
+				String content = page.get__content();
+
+				if (content.indexOf(name) > 0) {
+					String replace = convertAttachLink(content, name);
+
+					replace = convertImageLink(replace, name);
+
+					page.set__content(replace);
+				}
+			}
+		}
+	}
+
+	/**
+	 * [[link]] converts to [^link] and [[link|link name]] converts to [^link]
+	 * 
+	 * Linking to an attachment in LR stopped working after the SP2 upgrade but we still use this mechanism to mark
+	 * attachments for inclusion in Confluence.
+	 * 
+	 * @param content
+	 * @param name
+	 * @return
+	 */
+	public String convertAttachLink(String content, String name) {
+		String retval = content;
+
+		// [[attach] -> [^attach]
+		retval = content.replaceAll("\\[\\[" + name + "\\]\\]", "[^" + name + "]");
+
+		// [[attach|attach name]] -> [^attach]
+		// // "\\[\\[" + // opening left brackets
+		// // name is captured by replaceAll
+		// // "\\|" + // until a pipe
+		// // "[^\\]|]+" + // anything but a right bracket or | until
+		// // "\\]\\]"; // right brackets
+		retval = retval.replaceAll("\\[\\[" + name + "\\|[^\\]|]*]]", "[^" + name + "]");
+
+		return retval;
+	}
+
+	/**
+	 * {{image.jpg}} is display image in LR - in Confluence its !image.jpg! We do this here as we can find out if it's
+	 * an image by looking at the attachments.
+	 * 
+	 * @param content
+	 * @param name
+	 * @return
+	 */
+	public String convertImageLink(String content, String name) {
+		return content.replaceFirst("\\{\\{" + name + "\\}\\}", "!" + name + "!");
+	}
+
+	/**
+	 * Html formatted pages need to be surrounded with {html}
+	 * 
+	 * @param noHtml
+	 *            if true encloses the html fragment inside a code block this disables HTML rendering in Confluence
+	 */
+	public void reformatHtmlPages(boolean noHtml) {
+
+		for (WikiPage page : pageList) {
+			String format = page.get__format();
+			if (format.equalsIgnoreCase("html")) {
+				if (noHtml) {
+					_log.debug("Disabling html formatting for page: " + page.get__title());
+					String content = page.get__content();
+					page.set__content("{code} " + content + "{code}");
+				} else {
+					_log.debug("Adding html formatting for page: " + page.get__title());
+					String content = page.get__content();
+					page.set__content("{html} " + content + "{html}");
+				}
+			} else if (format.equalsIgnoreCase("creole")) {
+				// This is the stuff we are expecting
+			} else {
+				_log.warn("Unsupported format detected: " + format);
+			}
+		}
+	}
+
+	/**
+	 * Sets the depth value for the pages in the hierarchy.
+	 * 
+	 * @param frontPage
+	 */
+	public void recursePages(WikiPage frontPage) {
+
+		for (WikiPage page : frontPage.getChildren()) {
+
+			// if(page.get__title().contains("Admin")){
+			// _log.debug("!break " );
+			// }
+
+			page.setDepth(frontPage.getDepth() + 1);
+			recursePages(page);
+		}
+	}
+
+	/**
+	 * Prints out the current tree structure.
+	 * 
+	 * @param frontPage
+	 */
+	public void showRecursive(WikiPage frontPage) {
+		String padding = "";
+		for (int i = 0; i < frontPage.getDepth(); i++) {
+			padding += "    ";
+		}
+
+		_log.info(padding + frontPage.get__title());
+
+		if (!frontPage.getAttachments().isEmpty()) {
+			_log.info(padding + "  " + frontPage.getAttachments().size() + "-Attachments " + frontPage.getAttachments());
+		}
+
+		for (WikiPage page : frontPage.getChildren()) {
+			showRecursive(page);
+		}
+	}
+
+	/**
+	 * Copy the current pagelist original files into the given directory.
+	 * 
+	 * @param dir
+	 * @throws IOException
+	 */
+	public void copyTo(String dir) throws IOException {
+		File destDir = new File(dir);
+		destDir.mkdir();
+		_log.info("Copying " + pageList.size() + " current pages to: " + dir);
+
+		for (WikiPage page : pageList) {
+			_log.info("XML Path: " + page.getFile().getAbsolutePath());
+			copyFile(page.getFile(), new File(destDir, page.get__title() + ".xml"));
+		}
+	}
+
+	/**
+	 * Copy the source file to the destination overwriting the file if it exists.
+	 * 
+	 * @param sourceFile
+	 * @param destFile
+	 * @throws IOException
+	 */
+	public static void copyFile(File sourceFile, File destFile) throws IOException {
+		copyFile(sourceFile, destFile, true);
+	}
+
+	/**
+	 * Copy the source file to the destination.
+	 * 
+	 * @param sourceFile
+	 * @param destFile
+	 * @param overwrite
+	 *            overwrite the file if true.
+	 * @throws IOException
+	 */
+	public static void copyFile(File sourceFile, File destFile, boolean overwrite) throws IOException {
+		if (!destFile.exists()) {
+			destFile.createNewFile();
+		} else {
+			if (!overwrite) {
+				throw new IOException("Destination file exists! " + destFile);
+			}
+
+			_log.trace("Destination file exists: " + destFile);
+		}
+
+		FileChannel source = null;
+		FileChannel destination = null;
+
+		try {
+			source = new FileInputStream(sourceFile).getChannel();
+			destination = new FileOutputStream(destFile).getChannel();
+			destination.transferFrom(source, 0, source.size());
+		} finally {
+			if (source != null) {
+				source.close();
+			}
+			if (destination != null) {
+				destination.close();
+			}
+		}
+	}
+
+	/**
+	 * Writes the content of the pages of the Wiki onto the directory, maintaining the hierarchy structure in the file
+	 * system.
+	 * 
+	 * @param frontPage
+	 * @param copyOriginal
+	 *            true to copy the original xml file to the destination
+	 * @throws IOException
+	 */
+	public void saveRecursive(WikiPage frontPage, boolean copyOriginal) throws IOException {
+		String padding = "";
+		for (int i = 0; i < frontPage.getDepth(); i++) {
+			padding += "    ";
+		}
+
+		_log.info(padding + frontPage.get__title());
+
+		File dir = new File(frontPage.getOutDir() + File.separator + frontPage.getPath(), frontPage.get__title());
+		dir.mkdirs();
+
+		// _log.debug("Create Dir: " + dir);
+		if (copyOriginal) {
+			copyFile(frontPage.getFile(), new File(dir, frontPage.get__title() + ".xml"));
+		} else {
+			File filePage = new File(dir, frontPage.get__title());
+			write(filePage, frontPage.get__content());
+		}
+
+		if (!frontPage.getAttachments().isEmpty()) {
+			_log.info(padding + "  " + frontPage.getAttachments().size() + "-Attachments " + frontPage.getAttachments());
+		}
+
+		for (WikiPage page : frontPage.getChildren()) {
+			page.setPath(frontPage.getPath() + File.separator + frontPage.get__title());
+			page.setOutDir(frontPage.getOutDir());
+
+			saveRecursive(page, copyOriginal);
+		}
+	}
+
+	protected void write(File file, String content) {
+		try {
+			FileOutputStream fw = new FileOutputStream(file);
+			OutputStreamWriter outstream = new OutputStreamWriter(fw, "utf-8");
+			BufferedWriter out = new BufferedWriter(outstream);
+			out.write(content);
+			out.close();
+		} catch (IOException e) {
+			_log.error("Problem writing to file: " + file.getAbsolutePath());
+			e.printStackTrace();
+		}
+	}
+
+	public void showPages() {
+		_log.debug("---------Pages-----------");
+		for (WikiPage page : pageList) {
+			_log.info(page);
+
+			if (!page.showChildren().isEmpty()) {
+				_log.info(page.showChildren());
+			}
+		}
+	}
+
+	public void showPagesNoParentTitle() {
+		_log.info("---------showPagesNoParentTitle-----------");
+		for (WikiPage page : pageList) {
+
+			if (page.get__parentTitle().isEmpty()) {
+				_log.debug(page);
+			}
+		}
+	}
+
+}
diff --git a/src/com/atlassian/uwc/exporters/liferay/digester/Manifest.java b/src/com/atlassian/uwc/exporters/liferay/digester/Manifest.java
new file mode 100644
index 0000000..dac5eb6
--- /dev/null
+++ b/src/com/atlassian/uwc/exporters/liferay/digester/Manifest.java
@@ -0,0 +1,34 @@
+package com.atlassian.uwc.exporters.liferay.digester;
+
+
+public class Manifest {
+
+	private String header = "head";	
+	private ManifestPortlet portlet;
+	
+	public Manifest() {
+	}
+	
+			
+	public ManifestPortlet getPortlet() {
+		return portlet;
+	}
+
+	public void setPortlet(ManifestPortlet portlet) {
+		this.portlet = portlet;
+	}
+
+	public String getHeader() {
+		return header;
+	}
+
+	public void setHeader(String header) {
+		this.header = header;
+	}
+
+	@Override
+	public String toString() {
+		return "Manifest [header=" + header + ", portlet=" + portlet + "]";
+	}
+
+}
diff --git a/src/com/atlassian/uwc/exporters/liferay/digester/ManifestPortlet.java b/src/com/atlassian/uwc/exporters/liferay/digester/ManifestPortlet.java
new file mode 100644
index 0000000..55c0441
--- /dev/null
+++ b/src/com/atlassian/uwc/exporters/liferay/digester/ManifestPortlet.java
@@ -0,0 +1,22 @@
+package com.atlassian.uwc.exporters.liferay.digester;
+
+
+
+public class ManifestPortlet {
+
+	private String path = "path1";
+
+	public String getPath() {
+		return path;
+	}
+
+	public void setPath(String path) {
+		this.path = path;
+	}
+
+	@Override
+	public String toString() {
+		return "Portlet [path=" + path + "]";
+	}
+
+}
diff --git a/src/com/atlassian/uwc/exporters/liferay/digester/Page.java b/src/com/atlassian/uwc/exporters/liferay/digester/Page.java
new file mode 100644
index 0000000..5f2c993
--- /dev/null
+++ b/src/com/atlassian/uwc/exporters/liferay/digester/Page.java
@@ -0,0 +1,37 @@
+package com.atlassian.uwc.exporters.liferay.digester;
+
+import java.util.ArrayList;
+
+
+
+public class Page {
+
+	private String path = "pathX";	
+	private ArrayList<Attachment> attachments = new ArrayList<Attachment>();;
+	
+	public Page() {
+	}	
+			
+	public ArrayList<Attachment> getAttachments() {
+		return attachments;
+	}
+
+	public void setAttachment(Attachment portlet) {
+		this.attachments.add(portlet);
+	}
+	
+
+	public String getPath() {
+		return path;
+	}
+
+	public void setPath(String path) {
+		this.path = path;
+	}
+
+	@Override
+	public String toString() {
+		return "Page [path=" + path + ", attachments=" + attachments + "]";
+	}
+
+}
diff --git a/src/com/atlassian/uwc/exporters/liferay/digester/Pages.java b/src/com/atlassian/uwc/exporters/liferay/digester/Pages.java
new file mode 100644
index 0000000..3e7b450
--- /dev/null
+++ b/src/com/atlassian/uwc/exporters/liferay/digester/Pages.java
@@ -0,0 +1,29 @@
+package com.atlassian.uwc.exporters.liferay.digester;
+
+import java.util.ArrayList;
+
+
+
+public class Pages {
+
+	private ArrayList<Page> pageList = new ArrayList<Page>();;
+	
+	public Pages() {
+	}	
+			
+	public ArrayList<Page> getPageList() {
+		return pageList;
+	}
+
+	public void setPage(Page portlet) {
+		this.pageList.add(portlet);
+	}
+
+
+	@Override
+	public String toString() {
+		return "Pages [pageList=" + pageList + "]";
+	}
+
+
+}
diff --git a/src/com/atlassian/uwc/exporters/liferay/digester/Portlet.java b/src/com/atlassian/uwc/exporters/liferay/digester/Portlet.java
new file mode 100644
index 0000000..6167ca3
--- /dev/null
+++ b/src/com/atlassian/uwc/exporters/liferay/digester/Portlet.java
@@ -0,0 +1,35 @@
+package com.atlassian.uwc.exporters.liferay.digester;
+
+
+
+public class Portlet {
+
+	private String header = "head";	
+	private PortletData portlet;
+	
+	public Portlet() {
+	}
+	
+			
+	public PortletData getPortlet() {
+		return portlet;
+	}
+
+	public void setPortlet(PortletData portlet) {
+		this.portlet = portlet;
+	}
+
+	public String getHeader() {
+		return header;
+	}
+
+	public void setHeader(String header) {
+		this.header = header;
+	}
+
+	@Override
+	public String toString() {
+		return "Portlet [header=" + header + ", portlet=" + portlet + "]";
+	}
+
+}
diff --git a/src/com/atlassian/uwc/exporters/liferay/digester/PortletData.java b/src/com/atlassian/uwc/exporters/liferay/digester/PortletData.java
new file mode 100644
index 0000000..0d64d94
--- /dev/null
+++ b/src/com/atlassian/uwc/exporters/liferay/digester/PortletData.java
@@ -0,0 +1,22 @@
+package com.atlassian.uwc.exporters.liferay.digester;
+
+
+
+public class PortletData {
+
+	private String path = "path1";
+
+	public String getPath() {
+		return path;
+	}
+
+	public void setPath(String path) {
+		this.path = path;
+	}
+
+	@Override
+	public String toString() {
+		return "Portlet [path=" + path + "]";
+	}
+
+}
diff --git a/src/com/atlassian/uwc/exporters/liferay/digester/PressCancelException.java b/src/com/atlassian/uwc/exporters/liferay/digester/PressCancelException.java
new file mode 100644
index 0000000..2dedff5
--- /dev/null
+++ b/src/com/atlassian/uwc/exporters/liferay/digester/PressCancelException.java
@@ -0,0 +1,26 @@
+package com.atlassian.uwc.exporters.liferay.digester;
+
+public class PressCancelException extends RuntimeException {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 1L;
+
+	public PressCancelException() {
+		// TODO Auto-generated constructor stub
+	}
+
+	public PressCancelException(String arg0) {
+		super(arg0);
+	}
+
+	public PressCancelException(Throwable arg0) {
+		super(arg0);
+	}
+
+	public PressCancelException(String arg0, Throwable arg1) {
+		super(arg0, arg1);
+	}
+
+}
diff --git a/src/com/atlassian/uwc/exporters/liferay/digester/WikiData.java b/src/com/atlassian/uwc/exporters/liferay/digester/WikiData.java
new file mode 100644
index 0000000..09d7a9c
--- /dev/null
+++ b/src/com/atlassian/uwc/exporters/liferay/digester/WikiData.java
@@ -0,0 +1,36 @@
+package com.atlassian.uwc.exporters.liferay.digester;
+
+
+
+public class WikiData {
+
+	private String header = "head";	
+	private Pages pages;
+	
+	public WikiData() {
+	}
+	
+			
+	public Pages getPages() {
+		return pages;
+	}
+
+	public void setPages(Pages portlet) {
+		this.pages = portlet;
+	}
+
+	public String getHeader() {
+		return header;
+	}
+
+	public void setHeader(String header) {
+		this.header = header;
+	}
+
+	@Override
+	public String toString() {
+		return "WikiData [header=" + header + ", pages=" + pages + "]";
+	}
+		
+
+}
diff --git a/src/com/atlassian/uwc/exporters/liferay/digester/WikiPage.java b/src/com/atlassian/uwc/exporters/liferay/digester/WikiPage.java
new file mode 100644
index 0000000..5e6263e
--- /dev/null
+++ b/src/com/atlassian/uwc/exporters/liferay/digester/WikiPage.java
@@ -0,0 +1,186 @@
+package com.atlassian.uwc.exporters.liferay.digester;
+
+import java.io.File;
+import java.util.ArrayList;
+
+/* @formatter:off
+<WikiPage>
+<__new>false</__new>
+<__cachedModel>false</__cachedModel>
+<__escapedModel>false</__escapedModel>
+<__uuid>f1819813-8bba-4fcf-8eb8-df63e73f3882</__uuid>
+<__originalUuid>f1819813-8bba-4fcf-8eb8-df63e73f3882</__originalUuid>
+<__pageId>113470</__pageId>
+<__resourcePrimKey>113471</__resourcePrimKey>
+<__originalResourcePrimKey>113471</__originalResourcePrimKey>
+<__setOriginalResourcePrimKey>false</__setOriginalResourcePrimKey>
+<__groupId>113333</__groupId>
+<__originalGroupId>113333</__originalGroupId>
+<__setOriginalGroupId>false</__setOriginalGroupId>
+<__companyId>10233</__companyId>
+<__userId>10272</__userId>
+<__userUuid>5f04387c-e071-43d8-b2a1-ece933d80c31</__userUuid>
+<__userName>Test Test</__userName>
+<__createDate class="sql-timestamp">2011-03-25 10:35:13.0</__createDate>
+<__modifiedDate class="sql-timestamp">2011-03-25 10:35:13.0</__modifiedDate>
+<__nodeId>113469</__nodeId>
+<__originalNodeId>113469</__originalNodeId>
+<__setOriginalNodeId>false</__setOriginalNodeId>
+<__title>FrontPage</__title>
+<__originalTitle>FrontPage</__originalTitle>
+<__version>1.0</__version>
+<__originalVersion>1.0</__originalVersion>
+<__setOriginalVersion>false</__setOriginalVersion>
+<__minorEdit>true</__minorEdit>
+<__content></__content>
+<__summary>New</__summary>
+<__format>creole</__format>
+<__head>false</__head>
+<__parentTitle></__parentTitle>
+<__redirectTitle></__redirectTitle>
+<__status>0</__status>
+<__statusByUserId>10272</__statusByUserId>
+<__statusByUserName>Test Test</__statusByUserName>
+<__statusDate class="sql-timestamp">2011-10-03 16:45:33.0</__statusDate>
+</WikiPage>
+*/ //@formatter:on
+
+public class WikiPage implements Comparable<WikiPage>{
+	private String __title;
+	private String __version;
+	private String __parentTitle = "";
+	private String __redirectTitle = "";
+	private String __format = "";
+	private String __content;
+	private ArrayList<WikiPage> childList = new ArrayList<WikiPage>();
+	private ArrayList<Attachment> attachments = new ArrayList<Attachment>();	
+	private int depth = 0;	
+	private String path = "root";
+	private File file; // the location on disk of the data that produced this object
+	private File outDir;
+		
+	public File getOutDir() {
+		return outDir;
+	}
+	public void setOutDir(File outDir) {
+		this.outDir = outDir;
+	}
+	public File getFile() {
+		return file;
+	}
+	public void setFile(File file) {
+		this.file = file;
+	}
+	public String getPath() {
+		return path;
+	}
+	public void setPath(String path) {
+		this.path = path;
+	}
+	public int getDepth() {
+		return depth;
+	}
+	public void setDepth(int depth) {
+		this.depth = depth;
+	}
+	
+	public String get__format() {
+		return __format;
+	}
+	
+	public void set__format(String __format) {
+		this.__format = __format;
+	}
+	
+	public String get__parentTitle() {
+		return __parentTitle;
+	}
+
+	public void set__parentTitle(String __parentTitle) {
+		this.__parentTitle = __parentTitle;
+	}
+		
+	public String get__redirectTitle() {
+		return __redirectTitle;
+	}
+	
+	public void set__redirectTitle(String __redirectTitle) {
+		this.__redirectTitle = __redirectTitle;
+	}
+	
+	public String get__title() {
+		return __title;
+	}
+
+	public void set__title(String __title) {
+		this.__title = __title;
+	}
+
+	public String get__version() {
+		return __version;
+	}
+
+	public Double getVersion() {
+		return Double.parseDouble(__version);
+	}
+
+	public void set__version(String __version) {
+		this.__version = __version;
+	}
+
+	public String get__content() {
+		return __content;
+	}
+
+	public void set__content(String __content) {
+		this.__content = __content;
+	}
+
+	public void addChildPage(WikiPage page) {
+		childList.add(page);
+	}
+	public ArrayList<WikiPage> getChildren() {
+		return childList;
+	}
+		
+	public ArrayList<Attachment> getAttachments() {
+		return attachments;
+	}
+	public void setAttachments(ArrayList<Attachment> attachments) {
+		this.attachments = attachments;
+	}
+	
+	boolean isFrontPage(){
+		boolean retval = false;
+		if( get__title().equals("FrontPage")){
+			retval = true;
+		}
+		
+		return retval;
+	}
+
+	@Override
+	public String toString() {
+		return depth + "-WikiPage [__title=" + __title + ", __version=" + __version + ", __parentTitle=" + __parentTitle + "]";
+	}
+
+	@Override
+	public int compareTo(WikiPage o) {		
+		return this.get__title().compareTo(o.get__title());
+	}
+	
+	public String showChildren() {
+		String retval = "";
+
+		if (!childList.isEmpty()) {
+			retval = "[children(" + childList.size() + ") " + childList + "]";
+		}
+
+		return retval;
+	}
+
+	public boolean removeChild(WikiPage child) {
+		return childList.remove(child);
+	}
+	
+}