/******************************************************************************* * Copyright (c) 2007, 2013 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 * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * David Green - initial API and implementation *******************************************************************************/ package org.eclipse.mylyn.wikitext.tracwiki; import java.text.MessageFormat; import java.util.List; import org.eclipse.mylyn.wikitext.parser.DocumentBuilder.SpanType; import org.eclipse.mylyn.wikitext.parser.markup.AbstractMarkupLanguage; import org.eclipse.mylyn.wikitext.parser.markup.Block; import org.eclipse.mylyn.wikitext.parser.markup.MarkupLanguage; import org.eclipse.mylyn.wikitext.parser.markup.token.ImpliedHyperlinkReplacementToken; import org.eclipse.mylyn.wikitext.tracwiki.internal.block.DefinitionListBlock; import org.eclipse.mylyn.wikitext.tracwiki.internal.block.HeadingBlock; import org.eclipse.mylyn.wikitext.tracwiki.internal.block.ListBlock; import org.eclipse.mylyn.wikitext.tracwiki.internal.block.ParagraphBlock; import org.eclipse.mylyn.wikitext.tracwiki.internal.block.PreformattedBlock; import org.eclipse.mylyn.wikitext.tracwiki.internal.block.QuoteBlock; import org.eclipse.mylyn.wikitext.tracwiki.internal.block.TableBlock; import org.eclipse.mylyn.wikitext.tracwiki.internal.phrase.EscapePhraseModifier; import org.eclipse.mylyn.wikitext.tracwiki.internal.phrase.MonospacePhraseModifier; import org.eclipse.mylyn.wikitext.tracwiki.internal.phrase.SimplePhraseModifier; import org.eclipse.mylyn.wikitext.tracwiki.internal.token.BangEscapeToken; import org.eclipse.mylyn.wikitext.tracwiki.internal.token.ChangesetLinkReplacementToken; import org.eclipse.mylyn.wikitext.tracwiki.internal.token.HyperlinkReplacementToken; import org.eclipse.mylyn.wikitext.tracwiki.internal.token.LineBreakToken; import org.eclipse.mylyn.wikitext.tracwiki.internal.token.MacroReplacementToken; import org.eclipse.mylyn.wikitext.tracwiki.internal.token.MilestoneLinkReplacementToken; import org.eclipse.mylyn.wikitext.tracwiki.internal.token.ReportLinkReplacementToken; import org.eclipse.mylyn.wikitext.tracwiki.internal.token.RevisionLogReplacementToken; import org.eclipse.mylyn.wikitext.tracwiki.internal.token.SourceLinkReplacementToken; import org.eclipse.mylyn.wikitext.tracwiki.internal.token.TicketAttachmentLinkReplacementToken; import org.eclipse.mylyn.wikitext.tracwiki.internal.token.TicketLinkReplacementToken; import org.eclipse.mylyn.wikitext.tracwiki.internal.token.WikiLinkReplacementToken; import org.eclipse.mylyn.wikitext.tracwiki.internal.token.WikiWordReplacementToken; /** * An implementation of the <a href="http://trac.edgewall.org/wiki/TracWiki">TracWiki</a> markup language. * * @author David Green * */ public class TracWikiLanguage extends AbstractMarkupLanguage { private boolean autoLinking = true; private String serverUrl; public TracWikiLanguage() { setName("TracWiki"); //$NON-NLS-1$ } /** * Convert a page name to an href to the page. * * @param pageName * the name of the page to target * @return the href to access the page * @see MarkupLanguage#getInternalLinkPattern() */ public String toInternalHref(String pageName) { String pageId = pageName; if (pageId.startsWith("#")) { //$NON-NLS-1$ // internal anchor return pageId; } return MessageFormat.format(internalLinkPattern, pageId); } /** * convert a ticket id to a hyperlink based on the {@link #getServerUrl() server url} * * @param ticketId * the id of the ticket * @param commentNumber * the comment number or null if the url should not reference a specific comment */ public String toTicketHref(String ticketId, String commentNumber) { String url = serverUrl + "ticket/" + ticketId; //$NON-NLS-1$ if (commentNumber != null) { url += "#comment:" + commentNumber; //$NON-NLS-1$ } return url; } /** * convert a changeset id to a hyperlink based on the {@link #getServerUrl() server url} * * @param changesetId * the changeset id * @param restriction * the restriction, or null if there is no restriction. eg: "trunk" */ public String toChangesetHref(String changesetId, String restriction) { String url = serverUrl + "changeset/" + changesetId; //$NON-NLS-1$ if (restriction != null) { url += "/" + restriction; //$NON-NLS-1$ } return url; } /** * convert a revisions to a revision log hyperlink based on the {@link #getServerUrl() server url} * * @param revision1 * the first revision * @param revision2 * the second revision * @param restriction * the restriction, or null if there is no restriction. eg: "trunk" */ public String toRevisionLogHref(String revision1, String revision2, String restriction) { String url = serverUrl + "log/"; //$NON-NLS-1$ if (restriction != null) { url += restriction; } url += "?revs=" + revision1 + "-" + revision2; //$NON-NLS-1$ //$NON-NLS-2$ return url; } /** * convert a report id to a hyperlink based on the {@link #getServerUrl() server url} * * @param reportId * the id of the report */ public String toReportHref(String reportId) { String url = serverUrl + "report/" + reportId; //$NON-NLS-1$ return url; } /** * convert a milestone id to a hyperlink based on the {@link #getServerUrl() server url} * * @param milestoneId * the id of the milesonte */ public String toMilestoneHref(String milestoneId) { String url = serverUrl + "milestone/" + milestoneId; //$NON-NLS-1$ return url; } /** * create an URL to an attachment ticket based on the {@link #getServerUrl() server url} * * @param ticketId * the id of the ticket * @param attachment * the name of the attachment */ public String toTicketAttachmentHref(String ticketId, String attachment) { String url = serverUrl + "ticket/" + ticketId + "/" + attachment; //$NON-NLS-1$ //$NON-NLS-2$ return url; } /** * create an URL to the source browser * * @param source * the source to be viewed * @param revision * the revision, or null if there is no revision * @param line * the line, or null if there is no line */ public String toSourceBrowserHref(String source, String revision, String line) { String url = serverUrl + "browser"; //$NON-NLS-1$ if (source.charAt(0) != '/') { url += '/'; } url += source; if (revision != null) { url += "?rev=" + revision; //$NON-NLS-1$ } if (line != null) { url += "#L" + line; //$NON-NLS-1$ } return url; } /** * for the purpose of converting wiki words into links, determine if the wiki word exists. * * @see WikiWordReplacementToken */ public boolean computeInternalLinkExists(String link) { return true; } /** * Indicate if the markup should match WikiWords as hyperlinks. The default is true. */ public boolean isAutoLinking() { return autoLinking; } /** * Indicate if the markup should match WikiWords as hyperlinks. The default is true. */ public void setAutoLinking(boolean autoLinking) { this.autoLinking = autoLinking; } /** * set the server URL, for example <code>http://trac.edgewall.org/</code> from which links may be derrived, such as * <code>http://trac.edgewall.org/wiki/WikiPage</code> or <code>http://trac.edgewall.org/tickets/1</code> * * @param url * the url, or null if it is unknown. */ public void setServerUrl(String url) { if (url != null && !url.endsWith("/")) { //$NON-NLS-1$ url = url + "/"; //$NON-NLS-1$ } serverUrl = url; } /** * the server URL, for example <code>http://trac.edgewall.org/</code> from which links may be derrived, such as * <code>http://trac.edgewall.org/wiki/WikiPage</code> or <code>http://trac.edgewall.org/tickets/1</code> * * @see #setServerUrl(String) */ public String getServerUrl() { return serverUrl; } @Override protected void addStandardBlocks(List<Block> blocks, List<Block> paragraphBreakingBlocks) { // IMPORTANT NOTE: Most items below have order dependencies. DO NOT REORDER ITEMS BELOW!! // TODO: processors ListBlock listBlock = new ListBlock(); blocks.add(listBlock); paragraphBreakingBlocks.add(listBlock); DefinitionListBlock definitionListBlock = new DefinitionListBlock(); blocks.add(definitionListBlock); paragraphBreakingBlocks.add(definitionListBlock); HeadingBlock headingBlock = new HeadingBlock(); blocks.add(headingBlock); paragraphBreakingBlocks.add(headingBlock); PreformattedBlock preformattedBlock = new PreformattedBlock(); blocks.add(preformattedBlock); paragraphBreakingBlocks.add(preformattedBlock); QuoteBlock quoteBlock = new QuoteBlock(); blocks.add(quoteBlock); paragraphBreakingBlocks.add(quoteBlock); TableBlock tableBlock = new TableBlock(); blocks.add(tableBlock); paragraphBreakingBlocks.add(tableBlock); } @Override protected void addStandardPhraseModifiers(PatternBasedSyntax phraseModifierSyntax) { // IMPORTANT NOTE: Most items below have order dependencies. DO NOT REORDER ITEMS BELOW!! phraseModifierSyntax.add(new MonospacePhraseModifier()); phraseModifierSyntax.beginGroup("(?:(?<=[\\s\\.\\\"'?!;:\\)\\(\\{\\}\\[\\]-])|^)(?:", 0); // always starts at the start of a line or after a non-word character excluding '!' and '-' //$NON-NLS-1$ phraseModifierSyntax.add(new EscapePhraseModifier()); phraseModifierSyntax.add(new SimplePhraseModifier("'''''", new SpanType[] { SpanType.BOLD, SpanType.ITALIC }, //$NON-NLS-1$ true)); phraseModifierSyntax.add(new SimplePhraseModifier("'''", SpanType.BOLD, true)); //$NON-NLS-1$ phraseModifierSyntax.add(new SimplePhraseModifier("''", SpanType.ITALIC, true)); //$NON-NLS-1$ phraseModifierSyntax.add(new SimplePhraseModifier("__", SpanType.UNDERLINED, true)); //$NON-NLS-1$ phraseModifierSyntax.add(new SimplePhraseModifier("~~", SpanType.DELETED, true)); //$NON-NLS-1$ phraseModifierSyntax.add(new SimplePhraseModifier("^", SpanType.SUPERSCRIPT, true)); //$NON-NLS-1$ phraseModifierSyntax.add(new SimplePhraseModifier(",,", SpanType.SUBSCRIPT, true)); //$NON-NLS-1$ phraseModifierSyntax.endGroup(")(?=\\W|$)", 0); //$NON-NLS-1$ } @Override protected void addStandardTokens(PatternBasedSyntax tokenSyntax) { // IMPORTANT NOTE: Most items below have order dependencies. DO NOT REORDER ITEMS BELOW!! tokenSyntax.add(new BangEscapeToken()); tokenSyntax.add(new LineBreakToken()); tokenSyntax.beginGroup("(?:(?<=[\\s\\.\\\"'?!;:\\)\\(\\{\\}\\[\\]-])|^)(?:", 0); // always starts at the start of a line or after a non-word character excluding '!' and '-' //$NON-NLS-1$ tokenSyntax.add(new MacroReplacementToken()); tokenSyntax.add(new RevisionLogReplacementToken()); tokenSyntax.add(new ChangesetLinkReplacementToken()); tokenSyntax.add(new HyperlinkReplacementToken()); tokenSyntax.add(new ImpliedHyperlinkReplacementToken()); tokenSyntax.add(new TicketAttachmentLinkReplacementToken()); tokenSyntax.add(new TicketLinkReplacementToken()); tokenSyntax.add(new ReportLinkReplacementToken()); tokenSyntax.add(new MilestoneLinkReplacementToken()); tokenSyntax.add(new SourceLinkReplacementToken()); tokenSyntax.add(new WikiLinkReplacementToken()); if (configuration == null || configuration.isWikiWordLinking() == null || configuration.isWikiWordLinking()) { tokenSyntax.add(new WikiWordReplacementToken()); } tokenSyntax.endGroup(")(?=\\W|$)", 0); //$NON-NLS-1$ } @Override protected Block createParagraphBlock() { return new ParagraphBlock(); } }