Universal Wiki Converter
-- set version to 3.13.1_SNAPSHOT 
-- improved trac link handling: (double brackets links, better alias handling, improved escape linking, added link handling to work with new default hierarchy FilenameHierarchy)


git-svn-id: https://studio.plugins.atlassian.com/svn/UWC/devel@165785 2c54a935-e501-0410-bc05-97a93f6bca70
diff --git a/conf/converter.trac.properties b/conf/converter.trac.properties
index 6d4198c..c97e3b5 100644
--- a/conf/converter.trac.properties
+++ b/conf/converter.trac.properties
@@ -2,14 +2,21 @@
 #Trac.0000-duplicate_lines.java-regex=\r\n\r\n{replace-with}NEWLINE
 
 # Page Hierarchy
+## If your trac-admin dump creates a flat directory of pages 
+## in which the hierarchy is maintained as url encoded filenames, then 
+## use the FilenameHierarchy hierarchy-builder (default)
+## If your trac-admin dump creates a system hierarchy of files that matches 
+## your page hierarchy, use the FilepathHierarchy hierarchy-builder. Do also
+## comment 1210 converter
 Trac.0001.switch.hierarchy-builder=UseBuilder
-Trac.0002.classname.hierarchy-builder=com.atlassian.uwc.hierarchies.FilepathHierarchy
+Trac.0002.classname.hierarchy-builder=com.atlassian.uwc.hierarchies.FilenameHierarchy
+#Trac.0002.classname.hierarchy-builder=com.atlassian.uwc.hierarchies.FilepathHierarchy
 #Trac.0003.filepath-hierarchy-ignorable-ancestors.property=/SET/THIS/To/the/location/of/your/pages
-Trac.0004.filepath-hierarchy-ext.property=
+#Trac.0004.filepath-hierarchy-ext.property=
 
 # Preformatted text and wiki names
 Trac.0050-preformatted.java-regex-tokenizer=(?ms)^\{{3}(.*?)^\}{3}{replace-multiline-with}{noformat}$1{noformat}
-Trac.0015-escaped_wikiname.java-regex-tokenizer= !([A-Z][a-z/]+[A-Z][a-z][\w/]+){replace-with} $1
+Trac.0015-escaped_wikiname.java-regex-tokenizer=(?<=^|\s)!([A-Z][a-z/]+[A-Z][a-z][\w/]+){replace-with}$1
 
 # Escape characters that have a special meaning in Confluence
 Trac.0060-escape_exclamation_marks.java-regex=!{replace-with}\\!
@@ -49,8 +56,10 @@
 # Macros: Images, line breaks and generic
 Trac.0600-macro_image_on_other_page.java-regex=\[\[Image\(wiki:([^:]+):([^\)]+)\)\]\]{replace-with}[[Image($1^$2)]]
 Trac.0601-macro_image_attachment.java-regex=\[\[Image\(([^\)]+)\)\]\]{replace-with}!$1!
-Trac.0650-macro_br.java-regex=\[\[BR\]\]{replace-with}NEWLINE
-Trac.0699-macros.java-regex=\[{2}([^\]]+)\]{2}{replace-with}{$1}
+Trac.0650-macro_br.java-regex=\[\[BR\]\] ?{replace-with}NEWLINE
+Trac.0660-doublebracket-links.java-regex=\[{2}([^\]]+)\]{2}{replace-with}[$1]
+## This macros converter won't get called with 0660 enabled, so I've commented it for now. 
+#Trac.0699-macros.java-regex=\[{2}([^\]]+)\]{2}{replace-with}{$1}
 
 # Tables - copied from the MoinMoin converter
 # BUG: || gets replaced in mormal text
@@ -70,10 +79,16 @@
 Trac.1200-wikilink_prefix.java-regex=\[wiki:{replace-with}[
 Trac.1201-wikilink_with_quotes.java-regex-tokenizer=\[\"([^\"]+)\"\]{replace-with}[$1]
 Trac.1202-attachment_link.java-regex-tokenizer=\[attachment:{replace-with}[^
-Trac.1203-link_with_title.java-regex=\[([^ \]]+) ([^\]]+)\]{replace-with}[$2|$1]
+Trac.1203-link_with_title_pipe.java-regex-tokenizer=\[([^|\]]+)\|([^\]]+)\]{replace-with}[$2|$1]
+Trac.1204-link_with_title_space.java-regex=\[([^ \]]+) ([^\]]+)\]{replace-with}[$2|$1]
 
 ## For any tokenizer regex above, strip out tokens
 Trac.2000-detokenize.class=com.atlassian.uwc.converters.DetokenizerConverter
 
+## Remove everything but leaf node part of link from link target. Works with
+## FilenameHierarchy. If you are not using the FilenameHierarchy, comment this
+## converter.
+Trac.2100-hierarchylink.class=com.atlassian.uwc.converters.trac.FilenameHierarchyLinkConverter
+
 # Attachments
 Trac.3000-attachments.class=com.atlassian.uwc.converters.trac.AttachmentConverter
diff --git a/sampleData/trac/SampleTrac-ExpectedBase.txt b/sampleData/trac/SampleTrac-ExpectedBase.txt
index 2440026..148051c 100644
--- a/sampleData/trac/SampleTrac-ExpectedBase.txt
+++ b/sampleData/trac/SampleTrac-ExpectedBase.txt
@@ -7,7 +7,7 @@
 
 The second paragraph has some links: First we have internal wiki links like [TracGuide], [TracWiki] and to an [^Attached_File.pdf]. Links can be given a more descriptive title by writing the link followed by a space and a title and all this inside square brackets, like in [Trac Overview|TracOverview]. Links can be avoided by EscapingWikiNames.
 
-Of course, there can also be external links. URLs like http://www.google.com/search?q=Confluence are automatically linked but they can also be put inside square brackets: [http://www.google.de/search?hl=de&q=Confluence+Trac+importer&btnG=Suche&meta=], even with titles like [Google search for "trac export"|http://www.google.de/search?hl=de&q=trac+export&btnG=Suche&meta=]. We add some [relative links|#Relativelinks] and [links to|MainPage-SubWikiPage] [WikiPage-SubPages] just for fun.
+Of course, there can also be external links. URLs like http://www.google.com/search?q=Confluence are automatically linked but they can also be put inside square brackets: [http://www.google.de/search?hl=de&q=Confluence+Trac+importer&btnG=Suche&meta=], even with titles like [Google search for "trac export"|http://www.google.de/search?hl=de&q=trac+export&btnG=Suche&meta=]. We add some [relative links|#Relativelinks] and [links to|SubWikiPage] [SubPages] just for fun.
 
 bq. This text is a quote from someone else, rendered as blockquote.
   
diff --git a/sampleData/trac/SampleTrac-ExpectedLinks.txt b/sampleData/trac/SampleTrac-ExpectedLinks.txt
new file mode 100644
index 0000000..305d34e
--- /dev/null
+++ b/sampleData/trac/SampleTrac-ExpectedLinks.txt
@@ -0,0 +1,33 @@
+
+internal wiki links 
+[TracGuide]
+[TracWiki] 
+[Somepage]
+[Somepage]
+
+attachment links
+[^Attached_File.pdf]. 
+
+links with aliases
+[Trac Overview|TracOverview] 
+[Trac Guide with some German umlauts: ä ö ü|TracGuide]. 
+[This is another link to ticket number one|ticket].
+[the sandbox|SandBox]
+
+anchors
+[relative links|#Relativelinks] 
+[relative links|#Relativelinks]
+
+escape links
+EscapingWikiNames.
+
+hierarchy
+[SubWikiPage] 
+[SubWikiPage] 
+[alias|SubWikiPage] 
+[see next sibling|Sibling] 
+[see next sibling|Sibling]
+
+breaks 
+(included here because very much like link syntax)
+
diff --git a/sampleData/trac/SampleTrac-InputLinks.txt b/sampleData/trac/SampleTrac-InputLinks.txt
new file mode 100644
index 0000000..447bf9c
--- /dev/null
+++ b/sampleData/trac/SampleTrac-InputLinks.txt
@@ -0,0 +1,32 @@
+
+internal wiki links 
+[wiki:TracGuide]
+["TracWiki"] 
+[Somepage]
+[[Somepage]]
+
+attachment links
+[attachment:Attached_File.pdf]. 
+
+links with aliases
+[TracOverview Trac Overview] 
+[wiki:TracGuide Trac Guide with some German umlauts: ä ö ü]. 
+[[ticket|This is another link to ticket number one]].
+[[SandBox|the sandbox]]
+
+anchors
+[#Relativelinks relative links] 
+[[#Relativelinks|relative links]]
+
+escape links
+!EscapingWikiNames.
+
+hierarchy
+[WikiPage/SubWikiPage] 
+[wiki:WikiPage/SubWikiPage] 
+[wiki:WikiPage/SubWikiPage alias] 
+[../Sibling see next sibling] 
+[[../Sibling|see next sibling]]
+
+breaks [[BR]] (included here because very much like link syntax)
+
diff --git a/sampleData/trac/testall.sh b/sampleData/trac/testall.sh
index 00450b2..7f300cb 100755
--- a/sampleData/trac/testall.sh
+++ b/sampleData/trac/testall.sh
@@ -6,3 +6,6 @@
 echo "Stack"
 ./compare.sh Stack
 
+echo "Links"
+./compare.sh Links 
+
diff --git a/src/com/atlassian/uwc/converters/trac/FilenameHierarchyLinkConverter.java b/src/com/atlassian/uwc/converters/trac/FilenameHierarchyLinkConverter.java
new file mode 100644
index 0000000..421ad93
--- /dev/null
+++ b/src/com/atlassian/uwc/converters/trac/FilenameHierarchyLinkConverter.java
@@ -0,0 +1,55 @@
+package com.atlassian.uwc.converters.trac;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.atlassian.uwc.converters.BaseConverter;
+import com.atlassian.uwc.converters.tikiwiki.RegexUtil;
+import com.atlassian.uwc.ui.Page;
+
+public class FilenameHierarchyLinkConverter extends BaseConverter {
+
+	@Override
+	public void convert(Page page) {
+		String input = page.getOriginalText();
+		String converted = convertLink(input);
+		page.setConvertedText(converted);
+	}
+
+	Pattern link = Pattern.compile("\\[([^\\]]+)\\]");
+	protected String convertLink(String input) {
+		Matcher linkFinder = link.matcher(input);
+		StringBuffer sb = new StringBuffer();
+		boolean found = false;
+		while (linkFinder.find()) {
+			found = true;
+			String link = linkFinder.group(1);
+			String alias = "";
+			if (link.contains("|")) {
+				String[] parts = link.split("\\|");
+				alias = parts[0];
+				link = parts[1];
+			}
+			if (link.startsWith("#")) continue; //anchor, skip
+			if (link.startsWith("^")) continue; //attachment, skip
+			if (!link.contains("/")) continue; //link will be fine with no changes
+			if (link.startsWith("http") ||
+	                  link.startsWith("mailto:") ||
+	                  link.startsWith("file:")) continue; //external link: skip
+
+			
+			String[] nodes = link.split("\\/");
+			link = nodes[nodes.length-1];
+			if (!"".equals(alias)) alias += "|";
+			String replacement = "[" + alias + link + "]";
+			replacement = RegexUtil.handleEscapesInReplacement(replacement);
+			linkFinder.appendReplacement(sb, replacement);
+		}
+		if (found) {
+			linkFinder.appendTail(sb);
+			return sb.toString();
+		}
+		return input;
+	}
+
+}
diff --git a/src/com/atlassian/uwc/converters/trac/FilenameHierarchyLinkConverterTest.java b/src/com/atlassian/uwc/converters/trac/FilenameHierarchyLinkConverterTest.java
new file mode 100644
index 0000000..0b3c67e
--- /dev/null
+++ b/src/com/atlassian/uwc/converters/trac/FilenameHierarchyLinkConverterTest.java
@@ -0,0 +1,77 @@
+package com.atlassian.uwc.converters.trac;
+
+import junit.framework.TestCase;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+
+//at this stage we're done alot of link handling already, so this is really confluence link handling
+public class FilenameHierarchyLinkConverterTest extends TestCase {
+
+	FilenameHierarchyLinkConverter tester = null;
+	Logger log = Logger.getLogger(this.getClass());
+	protected void setUp() throws Exception {
+		super.setUp();
+		tester =  new FilenameHierarchyLinkConverter();
+		PropertyConfigurator.configure("log4j.properties");
+	}
+	
+	public void testConvertLinks_simple() {
+		String input, expected, actual;
+		input = "[WikiPage/SubWikiPage]"; 
+		expected = "[SubWikiPage]";
+		actual = tester.convertLink(input);
+		assertNotNull(actual);
+		assertEquals(expected, actual);
+	}
+	
+	public void testConvertLinks_alias() {
+		String input, expected, actual;
+		input = "[alias|WikiPage/SubWikiPage]";
+		expected = "[alias|SubWikiPage]";
+		actual = tester.convertLink(input);
+		assertNotNull(actual);
+		assertEquals(expected, actual);
+	}
+	
+	public void testConvertLinks_sibling() {
+		String input, expected, actual;
+		input = "[../Sibling]\n" +
+				"[alias|../Sibling]";
+		expected = "[Sibling]\n" +
+				"[alias|Sibling]";
+		actual = tester.convertLink(input);
+		assertNotNull(actual);
+		assertEquals(expected, actual);
+	}
+	
+	public void testConvertLinks_notattachment() {
+		String input, expected, actual;
+		input = "[alias/with dangerous chars|^file/wouldthisevenhappen.pdf]";
+		expected = input;
+		actual = tester.convertLink(input);
+		assertNotNull(actual);
+		assertEquals(expected, actual);
+	}
+	
+	public void testConvertLinks_notanchor() {
+		String input, expected, actual;
+		input = "[alias/foobar|#foo/bar]";
+		expected = input;
+		actual = tester.convertLink(input);
+		assertNotNull(actual);
+		assertEquals(expected, actual);
+	}
+	
+	
+	public void testConvertLinks_notexternal() {
+		String input, expected, actual;
+		input = "[alias/foobar|http://lalala.com]";
+		expected = input;
+		actual = tester.convertLink(input);
+		assertNotNull(actual);
+		assertEquals(expected, actual);
+	}
+
+
+}
diff --git a/src/com/atlassian/uwc/ui/UWCForm3.java b/src/com/atlassian/uwc/ui/UWCForm3.java
index cfaed61..259a570 100644
--- a/src/com/atlassian/uwc/ui/UWCForm3.java
+++ b/src/com/atlassian/uwc/ui/UWCForm3.java
@@ -92,7 +92,7 @@
 	private static final String LOGIN_TOOLTIP = "This is the username of an account on the Confluence server with write privileges to the space where you\'re adding content.";
 	private static final String ADDRESS_TOOLTIP = "This is the url to Confluence. Example: 'localhost:8080'";
 	private static final String VERSION_INDICATOR = ""; 
-	public static final String VERSION_NUMBER = "3.13.0";
+	public static final String VERSION_NUMBER = "3.13.1_Snapshot";
 	private static final Dimension TABBEDPANE_SIZE = new Dimension(420, 475);
 	public static final String APP_NAME = "Universal Wiki Converter";
 	private static final int BASIC_RIGHT_MARGIN = 35;