388657: [MediaWiki] Anchor to internal links with special chars.

update handling of mediawiki page names, links and page-internal anchors
to use MediaWiki encoding rules.

Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=388657

Change-Id: Ic75e3a8320e9d90f9fb4e11be7363a2570fa63ea
Signed-off-by: Jeremie Bresson <dev@jmini.fr>
diff --git a/org.eclipse.mylyn.wikitext.mediawiki.core/src/org/eclipse/mylyn/internal/wikitext/mediawiki/core/AbstractMediaWikiLanguage.java b/org.eclipse.mylyn.wikitext.mediawiki.core/src/org/eclipse/mylyn/internal/wikitext/mediawiki/core/AbstractMediaWikiLanguage.java
index 671a291..b15ad4d 100644
--- a/org.eclipse.mylyn.wikitext.mediawiki.core/src/org/eclipse/mylyn/internal/wikitext/mediawiki/core/AbstractMediaWikiLanguage.java
+++ b/org.eclipse.mylyn.wikitext.mediawiki.core/src/org/eclipse/mylyn/internal/wikitext/mediawiki/core/AbstractMediaWikiLanguage.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010, 2012 David Green and others.
+ * Copyright (c) 2010, 2012, 2015 David Green and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -50,8 +50,15 @@
 				return mapping;
 			}
 		}
-		String pageId = pageName.replace(' ', '_');
-		// FIXME: other character encodings occur here, not just ' '
+
+		String pageId;
+		int anchorIndex = pageName.indexOf("#"); //$NON-NLS-1$
+		if (anchorIndex < 0) {
+			pageId = pageName;
+		} else {
+			String encodedAnchor = MediaWikiIdGenerationStrategy.headingTextToId(pageName.substring(anchorIndex + 1));
+			pageId = pageName.substring(0, anchorIndex) + "#" + encodedAnchor; //$NON-NLS-1$
+		}
 
 		if (pageId.startsWith(CATEGORY_PREFIX) && pageId.length() > CATEGORY_PREFIX.length()) { // category
 			return pageId.substring(CATEGORY_PREFIX.length());
diff --git a/org.eclipse.mylyn.wikitext.mediawiki.core/src/org/eclipse/mylyn/internal/wikitext/mediawiki/core/MediaWikiIdGenerationStrategy.java b/org.eclipse.mylyn.wikitext.mediawiki.core/src/org/eclipse/mylyn/internal/wikitext/mediawiki/core/MediaWikiIdGenerationStrategy.java
index 1693bd3..5f0d41a 100644
--- a/org.eclipse.mylyn.wikitext.mediawiki.core/src/org/eclipse/mylyn/internal/wikitext/mediawiki/core/MediaWikiIdGenerationStrategy.java
+++ b/org.eclipse.mylyn.wikitext.mediawiki.core/src/org/eclipse/mylyn/internal/wikitext/mediawiki/core/MediaWikiIdGenerationStrategy.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007, 2011 David Green and others.
+ * Copyright (c) 2007, 2011, 2015 David Green and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -36,7 +36,7 @@
 		//   anchor = anchor + '_' + refCount(anchor) 
 		// }
 
-		String anchor = escape(headingText);
+		String anchor = headingTextToId(headingText);
 		Integer previousRefCount = anchorReferenceCount.put(anchor, 1);
 		if (previousRefCount != null) {
 			int refCount = previousRefCount + 1;
@@ -47,7 +47,14 @@
 		return anchor;
 	}
 
-	private String escape(String headingText) {
+	/**
+	 * encode a page name or anchor following MediaWiki encoding behaviour
+	 * 
+	 * @param headingText
+	 *            the heading text, page name or anchor text
+	 * @return an encoded id
+	 */
+	static String headingTextToId(String headingText) {
 		// implementation based on Sanitizer.php line 629
 		String escaped = headingText.replaceAll("\\s", "_"); //$NON-NLS-1$ //$NON-NLS-2$
 		// TODO: decode entity and char references
diff --git a/org.eclipse.mylyn.wikitext.mediawiki.core/src/org/eclipse/mylyn/internal/wikitext/mediawiki/core/PageMapping.java b/org.eclipse.mylyn.wikitext.mediawiki.core/src/org/eclipse/mylyn/internal/wikitext/mediawiki/core/PageMapping.java
index d2634f1..a427c63 100644
--- a/org.eclipse.mylyn.wikitext.mediawiki.core/src/org/eclipse/mylyn/internal/wikitext/mediawiki/core/PageMapping.java
+++ b/org.eclipse.mylyn.wikitext.mediawiki.core/src/org/eclipse/mylyn/internal/wikitext/mediawiki/core/PageMapping.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010 David Green and others.
+ * Copyright (c) 2010, 2015 David Green and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -21,7 +21,8 @@
 	 * provide a relative mapping for the given page name.
 	 * 
 	 * @param pageName
-	 *            the name of the page, for example "Mylyn/User_Guide"
+	 *            the name of the page, for example "Mylyn/User_Guide", ":Category:Mylyn", "#Some_link?" or
+	 *            "Main Page#help".
 	 * @return a relative or absolute URL, or null if no mapping is available or if the default mapping should apply.
 	 */
 	String mapPageNameToHref(String pageName);
diff --git a/org.eclipse.mylyn.wikitext.tests/src/org/eclipse/mylyn/internal/wikitext/mediawiki/core/MediaWikiIdGenerationStrategyTest.java b/org.eclipse.mylyn.wikitext.tests/src/org/eclipse/mylyn/internal/wikitext/mediawiki/core/MediaWikiIdGenerationStrategyTest.java
index 021f614..4c6bc82 100644
--- a/org.eclipse.mylyn.wikitext.tests/src/org/eclipse/mylyn/internal/wikitext/mediawiki/core/MediaWikiIdGenerationStrategyTest.java
+++ b/org.eclipse.mylyn.wikitext.tests/src/org/eclipse/mylyn/internal/wikitext/mediawiki/core/MediaWikiIdGenerationStrategyTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007, 2010 David Green and others.
+ * Copyright (c) 2007, 2010, 2015 David Green and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -33,4 +33,11 @@
 	public void testWithDots() {
 		assertEquals("com.foo.Bar", generationStrategy.generateId("com.foo.Bar"));
 	}
+
+	public void testHeadingTextToId() {
+		//Bug 388657
+		assertEquals("Anchor_Text.3F", generationStrategy.generateId("Anchor Text?"));
+		assertEquals("This.2FSection", generationStrategy.generateId("This/Section"));
+		assertEquals("C.23_Implementation", generationStrategy.generateId("C# Implementation"));
+	}
 }
diff --git a/org.eclipse.mylyn.wikitext.tests/src/org/eclipse/mylyn/wikitext/mediawiki/core/MediaWikiLanguageTest.java b/org.eclipse.mylyn.wikitext.tests/src/org/eclipse/mylyn/wikitext/mediawiki/core/MediaWikiLanguageTest.java
index 9b9a225..af3e916 100644
--- a/org.eclipse.mylyn.wikitext.tests/src/org/eclipse/mylyn/wikitext/mediawiki/core/MediaWikiLanguageTest.java
+++ b/org.eclipse.mylyn.wikitext.tests/src/org/eclipse/mylyn/wikitext/mediawiki/core/MediaWikiLanguageTest.java
@@ -417,6 +417,22 @@
 	}
 
 	@Test
+	public void testLinkInternalPageReferenceWithAltTextAndAnchor() {
+		//Bug 388657
+		assertMarkup(
+				"<p>a <a href=\"/wiki/Main_Page#Anchor_Text.3F\" title=\"Main Page#Anchor Text?\">text of the link</a> reference to the Main Page</p>",
+				"a [[Main Page#Anchor Text?|text of the link]] reference to the Main Page");
+	}
+
+	@Test
+	public void testLinkInternalPageReferenceWithAltTextAndAnchor2() {
+		//Bug 388657
+		assertMarkup(
+				"<p>Go to <a href=\"/wiki/This!page#with_anchor.21\" title=\"This!page#with anchor!\">this page</a> to have an example</p>",
+				"Go to [[This!page#with anchor!|this page]] to have an example");
+	}
+
+	@Test
 	public void testLinkInternalPageReferenceWithAltTextInTables() {
 		assertMarkup(
 				"<table><tr><td><a href=\"/wiki/Orion/Server_API/Preference_API\" title=\"Orion/Server_API/Preference API\">Preference API</a></td></tr></table>",