package info.bliki.wiki.model;
import info.bliki.Messages;
import info.bliki.htmlcleaner.BaseToken;
import info.bliki.htmlcleaner.ContentToken;
import info.bliki.htmlcleaner.TagNode;
import info.bliki.htmlcleaner.TagToken;
import info.bliki.htmlcleaner.Utils;
import info.bliki.wiki.filter.AbstractParser;
import info.bliki.wiki.filter.Encoder;
import info.bliki.wiki.filter.HTMLConverter;
import info.bliki.wiki.filter.ITextConverter;
import info.bliki.wiki.filter.MagicWord;
import info.bliki.wiki.filter.PDFConverter;
import info.bliki.wiki.filter.SectionHeader;
import info.bliki.wiki.filter.TemplateParser;
import info.bliki.wiki.filter.WikipediaParser;
import info.bliki.wiki.namespaces.INamespace;
import info.bliki.wiki.namespaces.Namespace;
import info.bliki.wiki.tags.TableOfContentTag;
import info.bliki.wiki.tags.WPATag;
import info.bliki.wiki.tags.WPTag;
import info.bliki.wiki.tags.code.SourceCodeFormatter;
import info.bliki.wiki.tags.util.TagStack;
import info.bliki.wiki.template.AbstractTemplateFunction;
import info.bliki.wiki.template.ITemplateFunction;
import info.bliki.wiki.template.extension.AttributeList;
import info.bliki.wiki.template.extension.AttributeRenderer;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.TimeZone;
/**
* Standard model implementation for the Wikipedia syntax
*
*/
public abstract class AbstractWikiModel implements IWikiModel, IContext {
private static int fNextNumberCounter = 0;
protected ArrayList<Reference> fReferences;
protected Map<String, Integer> fReferenceNames;
protected int fRecursionLevel;
protected int fTemplateRecursionCount;
protected TagStack fTagStack;
private boolean fInitialized;
protected Locale fLocale;
private final IConfiguration fConfiguration;
private IEventListener fWikiListener = null;
protected INamespace fNamespace;
// private ResourceBundle fResourceBundle;
protected String fRedirectLink = null;
protected String fPageTitle = "PAGENAME";
protected String fNamespaceName = "";
protected int fSectionCounter;
protected boolean fTemplateTopic = false;
protected int fExternalLinksCounter;
/**
* A tag that manages the "table of content"
*
*/
protected TableOfContentTag fTableOfContentTag = null;
protected SimpleDateFormat fFormatter = null;
/**
* "table of content"
*
*/
protected List<Object> fTableOfContent = null;
/**
* Contains all anchor strings to create unique anchors
*/
protected HashSet<String> fToCSet;
/**
* Map an attribute name to its value(s). These values are set by outside code
* via st.setAttribute(name, value). StringTemplate is like self in that a
* template is both the "class def" and "instance". When you create a
* StringTemplate or setTemplate, the text is broken up into chunks (i.e.,
* compiled down into a series of chunks that can be evaluated later). You can
* have multiple
*/
protected Map<String, Object> attributes;
/**
* A Map<Class,Object> that allows people to register a renderer for a
* particular kind of object to be displayed in this template. This overrides
* any renderer set for this template's group.
*
* Most of the time this map is not used because the StringTemplateGroup has
* the general renderer map for all templates in that group. Sometimes though
* you want to override the group's renderers.
*/
protected Map<Class, Object> attributeRenderers;
public AbstractWikiModel() {
this(Configuration.DEFAULT_CONFIGURATION);
}
public AbstractWikiModel(Configuration configuration) {
this(configuration, Locale.ENGLISH);
}
public AbstractWikiModel(Configuration configuration, Locale locale) {
this(
configuration,
locale,
Messages.getResourceBundle(locale),
new Namespace(locale));
}
/**
*
* @deprecated use the
* <code>(Configuration configuration, Locale locale,...)</code>
* constructors instead.
* @param configuration
* @param resourceBundle
* @param namespace
*/
@Deprecated
public AbstractWikiModel(Configuration configuration,
ResourceBundle resourceBundle, INamespace namespace) {
this(configuration, Locale.ENGLISH, resourceBundle, namespace);
}
public AbstractWikiModel(Configuration configuration, Locale locale,
ResourceBundle resourceBundle, INamespace namespace) {
fLocale = locale;
fInitialized = false;
fConfiguration = configuration;
// fResourceBundle = resourceBundle;
fNamespace = namespace;
// initializeNamespaces();
initialize();
}
// private void initializeNamespaces() {
// String ns1, ns2;
//
// ns1 = Messages.getString(fResourceBundle, Messages.WIKI_API_MEDIA1);
// if (ns1 != null) {
// fImageNamespaces[0] = ns1;
// ns2 = Messages.getString(fResourceBundle, Messages.WIKI_API_MEDIA2);
// if (ns2 != null) {
// fImageNamespaces[1] = ns2;
// }
// }
//
// ns1 = Messages.getString(fResourceBundle, Messages.WIKI_API_IMAGE1);
// if (ns1 != null) {
// fImageNamespaces[0] = ns1;
// ns2 = Messages.getString(fResourceBundle, Messages.WIKI_API_IMAGE2);
// if (ns2 != null) {
// fImageNamespaces[1] = ns2;
// }
// }
//
// ns1 = Messages.getString(fResourceBundle, Messages.WIKI_API_TEMPLATE1);
// if (ns1 != null) {
// fTemplateNamespaces[0] = ns1;
// ns2 = Messages.getString(fResourceBundle, Messages.WIKI_API_TEMPLATE2);
// if (ns2 != null) {
// fTemplateNamespaces[1] = ns2;
// }
// }
//
// ns1 = Messages.getString(fResourceBundle, Messages.WIKI_API_CATEGORY1);
// if (ns1 != null) {
// fCategoryNamespaces[0] = ns1;
// ns2 = Messages.getString(fResourceBundle, Messages.WIKI_API_CATEGORY2);
// if (ns2 != null) {
// fCategoryNamespaces[1] = ns2;
// }
// }
//
// }
/**
* {@inheritDoc}
*/
@Override
public void addCategory(String categoryName, String sortKey) {
}
/**
* {@inheritDoc}
*/
@Override
public SourceCodeFormatter addCodeFormatter(String key,
SourceCodeFormatter value) {
return fConfiguration.addCodeFormatter(key, value);
}
/**
* {@inheritDoc}
*/
@Override
public String addInterwikiLink(String key, String value) {
return fConfiguration.addInterwikiLink(key, value);
}
/**
* {@inheritDoc}
*/
@Override
public void addLink(String topicName) {
}
/**
* {@inheritDoc}
*/
@Override
public boolean addSemanticAttribute(String attribute, String attributeValue) {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean addSemanticRelation(String relation, String relationValue) {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public void addTemplate(String template) {
}
/**
* {@inheritDoc}
*/
@Override
public ITemplateFunction addTemplateFunction(String key,
ITemplateFunction value) {
return fConfiguration.addTemplateFunction(key, value);
}
/**
* {@inheritDoc}
*/
@Override
public TagToken addTokenTag(String key, TagToken value) {
return fConfiguration.addTokenTag(key, value);
}
/**
* {@inheritDoc}
*/
@Override
public String[] addToReferences(String reference, String nameAttribute) {
String[] result = new String[2];
result[1] = null;
if (fReferences == null) {
fReferences = new ArrayList<Reference>();
fReferenceNames = new HashMap<String, Integer>();
}
if (nameAttribute != null) {
Integer index = fReferenceNames.get(nameAttribute);
if (index != null) {
result[0] = index.toString();
Reference ref = fReferences.get(index - 1);
int count = ref.incCounter();
if (count >= Reference.CHARACTER_REFS.length()) {
result[1] = nameAttribute + '_' + 'Z';
} else {
if (count == 0) {
result[1] = nameAttribute;
} else {
result[1] =
nameAttribute + '_' + Reference.CHARACTER_REFS.charAt(count);
}
}
return result;
}
}
if (nameAttribute != null) {
fReferences.add(new Reference(reference, nameAttribute));
Integer index = Integer.valueOf(fReferences.size());
fReferenceNames.put(nameAttribute, index);
result[1] = nameAttribute;
} else {
fReferences.add(new Reference(reference));
}
result[0] = Integer.toString(fReferences.size());
return result;
}
/**
* Add a section header with the given <code>headLevel</code> to the
* "table of content"
*
* @param toc
* the "table of content list"
* @param strPair
* a new section header
* @param headLevel
* the level of the new section header
*/
protected void addToTableOfContent(List<Object> toc, SectionHeader strPair,
int headLevel) {
if (headLevel == 1) {
toc.add(strPair);
} else {
if (toc.size() > 0) {
if (toc.get(toc.size() - 1) instanceof List) {
addToTableOfContent(
(List<Object>) toc.get(toc.size() - 1),
strPair,
--headLevel);
return;
}
}
ArrayList<Object> list = new ArrayList<Object>();
toc.add(list);
addToTableOfContent(list, strPair, --headLevel);
}
}
/**
* {@inheritDoc}
*/
@Override
public void append(BaseToken contentNode) {
fTagStack.append(contentNode);
}
/**
* {@inheritDoc}
*/
@Override
public void appendExternalImageLink(String imageSrc, String imageAltText) {
TagNode spanTagNode = new TagNode("span");
append(spanTagNode);
spanTagNode.addAttribute("class", "image", true);
TagNode imgTagNode = new TagNode("img");
spanTagNode.addChild(imgTagNode);
imgTagNode.addAttribute("src", imageSrc, true);
imgTagNode.addAttribute("alt", imageAltText, true);
// "nofollow" keyword is not allowed for XHTML
// imgTagNode.addAttribute("rel", "nofollow", true);
}
/**
* Append an external link (starting with http, https, ftp,...) as described
* in <a href="http://en.wikipedia.org/wiki/Help:Link#External_links">Help
* Links</a>
*
* @param link
* the external link with <code>http://, https:// or ftp://</code>
* prefix
* @param linkName
* the link name which is separated from the URL by a space
* @param withoutSquareBrackets
* if <code>true</code> a link with no square brackets around the
* link was parsed
* @deprecated use
* {@link IWikiModel#appendExternalLink(String, String, String, boolean)}
* instead.
*/
@Override
@Deprecated
public void appendExternalLink(String link, String linkName,
boolean withoutSquareBrackets) {
appendExternalLink("", link, linkName, withoutSquareBrackets);
}
/**
* Append an external link (starting with http, https, ftp,...) as described
* in <a href="http://en.wikipedia.org/wiki/Help:Link#External_links">Help
* Links</a>
*
* @param uriSchemeName
* the top level URI (Uniform Resource Identifier) scheme name
* (without the following colon character ":"). Example "ftp",
* "http", "https". See <a
* href="http://en.wikipedia.org/wiki/URI_scheme">URI scheme</a>
* @param link
* the external link with <code>http://, https:// or ftp://</code>
* prefix
* @param linkName
* the link name which is separated from the URL by a space
* @param withoutSquareBrackets
* if <code>true</code> a link with no square brackets around the
* link was parsed
*/
@Override
public void appendExternalLink(String uriSchemeName, String link,
String linkName, boolean withoutSquareBrackets) {
link = Utils.escapeXml(link, true, false, false);
// is the given link an image?
// int indx = link.lastIndexOf(".");
// if (indx > 0 && indx < (link.length() - 3)) {
// String ext = link.substring(indx + 1);
// if (ext.equalsIgnoreCase("gif") || ext.equalsIgnoreCase("png") ||
// ext.equalsIgnoreCase("jpg") || ext.equalsIgnoreCase("jpeg")
// || ext.equalsIgnoreCase("bmp")) {
// appendExternalImageLink(link, linkName);
// return;
// }
// }
TagNode aTagNode = new TagNode("a");
aTagNode.addAttribute("href", link, true);
aTagNode.addAttribute("rel", "nofollow", true);
aTagNode.addAttribute("target", "_blank", true);
if (withoutSquareBrackets) {
aTagNode.addAttribute("class", "externallink", true);
aTagNode.addAttribute("title", link, true);
append(aTagNode);
aTagNode.addChild(new ContentToken(linkName));
} else {
String trimmedText = linkName.trim();
if (trimmedText.length() > 0) {
pushNode(aTagNode);
if (linkName.equals(link)) {
if (withoutSquareBrackets) {
aTagNode.addAttribute("class", "externallink", true);
aTagNode.addAttribute("title", link, true);
aTagNode.addChild(new ContentToken(trimmedText));
} else {
aTagNode.addAttribute("class", "external autonumber", true);
aTagNode.addChild(new ContentToken("["
+ (++fExternalLinksCounter)
+ "]"));
}
} else {
aTagNode.addAttribute("class", "externallink", true);
aTagNode.addAttribute("title", link, true);
WikipediaParser.parseRecursive(trimmedText, this, false, true);
}
popNode();
}
}
}
/**
* {@inheritDoc}
*/
@Override
public ITableOfContent appendHead(String rawHead, int headLevel,
boolean noToC, int headCounter) {
return appendHead(rawHead, headLevel, noToC, headCounter, 0, 0);
}
/**
* Append a new head to the table of content
*
* @param rawHead
* @param headLevel
*/
@Override
public ITableOfContent appendHead(String rawHead, int headLevel,
boolean noToC, int headCounter, int startPosition, int endPosition) {
TagStack localStack =
WikipediaParser.parseRecursive(rawHead.trim(), this, true, true);
WPTag headTagNode = new WPTag("h" + headLevel);
TagNode spanTagNode = new TagNode("span");
// Example:
// <h2><span class="mw-headline" id="Header_level_2">Header level
// 2</span></h2>
spanTagNode.addChildren(localStack.getNodeList());
headTagNode.addChild(spanTagNode);
String tocHead = headTagNode.getBodyString();
String anchor = Encoder.encodeDotUrl(tocHead);
createTableOfContent(false);
if (!noToC && (headCounter > 3)) {
/** bug fix */
// fTableOfContentTag.setShowToC(true);
}
if (fToCSet.contains(anchor)) {
String newAnchor = anchor;
for (int i = 2; i < Integer.MAX_VALUE; i++) {
newAnchor = anchor + '_' + Integer.toString(i);
if (!fToCSet.contains(newAnchor)) {
break;
}
}
anchor = newAnchor;
}
fToCSet.add(anchor);
SectionHeader strPair =
new SectionHeader(headLevel, startPosition, endPosition, tocHead, anchor);
addToTableOfContent(fTableOfContent, strPair, headLevel);
if (getRecursionLevel() == 1) {
buildEditLinkUrl(fSectionCounter++);
}
spanTagNode.addAttribute("class", "mw-headline", true);
spanTagNode.addAttribute("id", anchor, true);
append(headTagNode);
return fTableOfContentTag;
}
/**
* {@inheritDoc}
*/
@Override
public void appendInternalImageLink(String hrefImageLink,
String srcImageLink, ImageFormat imageFormat) {
// int pxWidth = imageFormat.getWidth();
// int pxHeight = imageFormat.getHeight();
String caption = imageFormat.getCaption();
String imageType = imageFormat.getType();
TagNode divInnerTagNode = new TagNode("div");
divInnerTagNode.addAttribute("id", "image", false);
// String link = imageFormat.getLink();
// if (link != null) {
// String href = encodeTitleToUrl(link, true);
// divTagNode.addAttribute("href", href, false);
// } else {
if (hrefImageLink.length() != 0) {
divInnerTagNode.addAttribute("href", hrefImageLink, false);
}
// }
divInnerTagNode.addAttribute("src", srcImageLink, false);
setDefaultThumbWidth(imageFormat);
divInnerTagNode.addObjectAttribute("wikiobject", imageFormat);
// if (pxHeight != -1) {
// if (pxWidth != -1) {
// divInnerTagNode.addAttribute("style", "height:" + pxHeight + "px; " +
// "width:" + pxWidth + "px", false);
// } else {
// divInnerTagNode.addAttribute("style", "height:" + pxHeight + "px",
// false);
// }
// } else {
// if (pxWidth != -1) {
// divInnerTagNode.addAttribute("style", "width:" + pxWidth + "px", false);
// }
// }
pushNode(divInnerTagNode);
try {
// TODO: test all these cases
if (caption != null
&& caption.length() > 0
&& ("frame".equals(imageType) || "thumb".equals(imageType) || "thumbnail"
.equals(imageType))) {
TagNode captionTagNode = new TagNode("div");
String clazzValue = "caption";
String type = imageFormat.getType();
if (type != null) {
clazzValue = type + clazzValue;
}
captionTagNode.addAttribute("class", clazzValue, false);
//
TagStack localStack =
WikipediaParser.parseRecursive(caption, this, true, true);
captionTagNode.addChildren(localStack.getNodeList());
String altAttribute = imageFormat.getAlt();
if (altAttribute == null) {
altAttribute = captionTagNode.getBodyString();
imageFormat.setAlt(Encoder.encodeHtml(altAttribute));// see issue #25
}
pushNode(captionTagNode);
popNode();
}
} finally {
popNode(); // div
}
}
/**
* Set the default thumb format width. This method sets a "default
* width" (220px) for images of type "thumb", if no width is
* set in the image format string.
*
* @param imageFormat
*/
protected void setDefaultThumbWidth(ImageFormat imageFormat) {
int pxWidth = imageFormat.getWidth();
String imageType = imageFormat.getType();
if (pxWidth == -1 && (imageType == null || "thumb".equals(imageType))) {
// set the default thumb format width
imageFormat.setWidth(220);
}
}
/**
* {@inheritDoc}
*/
@Override
public void appendInternalLink(String topic, String hashSection,
String topicDescription, String cssClass, boolean parseRecursive) {
WPATag aTagNode = new WPATag();
// append(aTagNode);
// aTagNode.addAttribute("id", "w", true);
String href = encodeTitleToUrl(topic, true);
if (hashSection != null) {
href = href + '#' + encodeTitleDotUrl(hashSection, true);
}
aTagNode.addAttribute("href", href, true);
if (cssClass != null) {
aTagNode.addAttribute("class", cssClass, true);
}
aTagNode.addObjectAttribute("wikilink", topic);
pushNode(aTagNode);
if (parseRecursive) {
WikipediaParser
.parseRecursive(topicDescription.trim(), this, false, true);
} else {
aTagNode.addChild(new ContentToken(topicDescription));
}
popNode();
// ContentToken text = new ContentToken(topicDescription);
// aTagNode.addChild(text);
}
/**
* {@inheritDoc}
*/
@Override
public void appendInterWikiLink(String namespace, String title,
String linkText) {
String hrefLink = getInterwikiMap().get(namespace);
if (hrefLink == null) {
// shouldn't really happen
hrefLink = "#";
}
// false -> don't convert first character to uppercase for interwiki links
String encodedtopic = encodeTitleToUrl(title, false);
if (replaceColon()) {
encodedtopic = encodedtopic.replace(':', '/');
}
hrefLink = hrefLink.replace("${title}", encodedtopic);
TagNode aTagNode = new TagNode("a");
// append(aTagNode);
aTagNode.addAttribute("href", hrefLink, true);
// aTagNode.addChild(new ContentToken(linkText));
pushNode(aTagNode);
WikipediaParser.parseRecursive(linkText.trim(), this, false, true);
popNode();
}
/**
* {@inheritDoc}
*/
@Override
public void appendISBNLink(String isbnPureText) {
StringBuffer isbnUrl = new StringBuffer(isbnPureText.length() + 100);
isbnUrl.append("http://www.amazon.com/exec/obidos/ASIN/");
for (int index = 0; index < isbnPureText.length(); index++) {
if (isbnPureText.charAt(index) >= '0'
&& isbnPureText.charAt(index) <= '9') {
isbnUrl.append(isbnPureText.charAt(index));
}
}
String isbnString = isbnUrl.toString();
TagNode aTagNode = new TagNode("a");
append(aTagNode);
aTagNode.addAttribute("href", isbnString, true);
aTagNode.addAttribute("class", "external text", true);
aTagNode.addAttribute("title", isbnString, true);
aTagNode.addAttribute("rel", "nofollow", true);
aTagNode.addChild(new ContentToken(isbnPureText));
}
/**
* {@inheritDoc}
*/
@Override
public void appendMailtoLink(String link, String linkName,
boolean withoutSquareBrackets) {
// is it an image?
// link = Utils.escapeXml(link, true, false, false);
// int indx = link.lastIndexOf(".");
// if (indx > 0 && indx < (link.length() - 3)) {
// String ext = link.substring(indx + 1);
// if (ext.equalsIgnoreCase("gif") || ext.equalsIgnoreCase("png") ||
// ext.equalsIgnoreCase("jpg") || ext.equalsIgnoreCase("jpeg")
// || ext.equalsIgnoreCase("bmp")) {
// appendExternalImageLink(link, linkName);
// return;
// }
// }
TagNode aTagNode = new TagNode("a");
append(aTagNode);
aTagNode.addAttribute("href", link, true);
aTagNode.addAttribute("class", "external free", true);
aTagNode.addAttribute("title", link, true);
aTagNode.addAttribute("rel", "nofollow", true);
aTagNode.addChild(new ContentToken(linkName));
}
/**
* {@inheritDoc}
*/
@Override
public boolean appendRawNamespaceLinks(String rawNamespaceTopic,
String viewableLinkDescription, boolean containsNoPipe) {
int colonIndex = rawNamespaceTopic.indexOf(':');
if (colonIndex != (-1)) {
String nameSpace = rawNamespaceTopic.substring(0, colonIndex);
if (isSemanticWebActive()
&& (rawNamespaceTopic.length() > colonIndex + 1)) {
// See <a
// href="http://en.wikipedia.org/wiki/Semantic_MediaWiki">Semantic
// MediaWiki</a> for more information.
if (rawNamespaceTopic.charAt(colonIndex + 1) == ':') {
// found an SMW relation
String relationValue = rawNamespaceTopic.substring(colonIndex + 2);
if (addSemanticRelation(nameSpace, relationValue)) {
if (containsNoPipe) {
viewableLinkDescription = relationValue;
}
if (viewableLinkDescription.trim().length() > 0) {
appendInternalLink(
relationValue,
null,
viewableLinkDescription,
"interwiki",
true);
}
return true;
}
} else if (rawNamespaceTopic.charAt(colonIndex + 1) == '=') {
// found an SMW attribute
String attributeValue = rawNamespaceTopic.substring(colonIndex + 2);
if (addSemanticAttribute(nameSpace, attributeValue)) {
append(new ContentToken(attributeValue));
return true;
}
}
}
if (isCategoryNamespace(nameSpace)) {
// add the category to this texts metadata
String category = rawNamespaceTopic.substring(colonIndex + 1).trim();
if (category != null && category.length() > 0) {
// TODO implement more sort-key behaviour
// http://en.wikipedia.org/wiki/Wikipedia:Categorization#
// Category_sorting
addCategory(category, viewableLinkDescription);
return true;
}
} else if (isInterWiki(nameSpace)) {
String title = rawNamespaceTopic.substring(colonIndex + 1);
if (title != null && title.length() > 0) {
appendInterWikiLink(nameSpace, title, viewableLinkDescription);
return true;
}
}
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean appendRawWikipediaLink(String rawLinkText, String suffix) {
String rawTopicName = rawLinkText;
if (rawTopicName != null) {
// trim the name for whitespace characters on the left side
int trimLeftIndex = 0;
while ((trimLeftIndex < rawTopicName.length())
&& (rawTopicName.charAt(trimLeftIndex) <= ' ')) {
trimLeftIndex++;
}
if (trimLeftIndex > 0) {
rawTopicName = rawTopicName.substring(trimLeftIndex);
}
// Is there an alias like [alias|link] ?
int pipeIndex = rawTopicName.lastIndexOf('|');
String alias = "";
if (-1 != pipeIndex) {
alias = rawTopicName.substring(pipeIndex + 1);
rawTopicName = rawTopicName.substring(0, pipeIndex);
if (alias.length() == 0) {
// special cases like: [[Test:hello world|]] or [[Test(hello
// world)|]]
// or [[Test, hello world|]]
alias = rawTopicName;
int index = alias.indexOf(':');
if (index != -1) {
alias = alias.substring(index + 1).trim();
} else {
index = alias.indexOf('(');
if (index != -1) {
alias = alias.substring(0, index).trim();
} else {
index = alias.indexOf(',');
if (index != -1) {
alias = alias.substring(0, index).trim();
}
}
}
}
}
int hashIndex = rawTopicName.lastIndexOf('#');
String hash = "";
if (-1 != hashIndex && hashIndex != rawTopicName.length() - 1) {
hash = rawTopicName.substring(hashIndex + 1);
rawTopicName = rawTopicName.substring(0, hashIndex);
}
// trim the name for whitespace characters on the right side
int trimRightIndex = rawTopicName.length() - 1;
while ((trimRightIndex >= 0)
&& (rawTopicName.charAt(trimRightIndex) <= ' ')) {
trimRightIndex--;
}
if (trimRightIndex != rawTopicName.length() - 1) {
rawTopicName = rawTopicName.substring(0, trimRightIndex + 1);
}
// rawTopicName = Encoder.encodeHtml(rawTopicName); // see issue #25
String viewableLinkDescription;
if (-1 != pipeIndex) {
viewableLinkDescription = alias + suffix;
} else {
if (rawTopicName.length() > 0 && rawTopicName.charAt(0) == ':') {
viewableLinkDescription = rawTopicName.substring(1) + suffix;
} else {
viewableLinkDescription = rawTopicName + suffix;
}
}
if (appendRawNamespaceLinks(
rawTopicName,
viewableLinkDescription,
pipeIndex == (-1))) {
return true;
}
int indx = rawTopicName.indexOf(':');
String namespace = null;
if (indx >= 0) {
namespace = rawTopicName.substring(0, indx);
}
if (namespace != null && isImageNamespace(namespace)) {
parseInternalImageLink(namespace, rawLinkText);
return false;
} else {
if (rawTopicName.length() > 0 && rawTopicName.charAt(0) == ':') {
rawTopicName = rawTopicName.substring(1);
}
if (rawTopicName.length() > 0 && rawTopicName.charAt(0) == ':') {
rawTopicName = rawTopicName.substring(1);
}
addLink(rawTopicName);
if (-1 != hashIndex) {
appendInternalLink(
rawTopicName,
hash,
viewableLinkDescription,
null,
true);
} else {
appendInternalLink(
rawTopicName,
null,
viewableLinkDescription,
null,
true);
}
return true;
}
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean appendRedirectLink(String redirectLink) {
fRedirectLink = redirectLink;
return true;
}
/**
* {@inheritDoc}
*/
@Override
public void appendSignature(Appendable writer, int numberOfTildes)
throws IOException {
switch (numberOfTildes) {
case 3:
writer.append("~~~");
break;
case 4:
writer.append("~~~~");
break;
case 5:
writer.append("~~~~~");
break;
}
}
/**
* {@inheritDoc}
*/
@Override
public void appendStack(TagStack stack) {
if (stack != null) {
fTagStack.append(stack);
}
}
/**
* {@inheritDoc}
*/
public TagNode appendToCAnchor(String anchor) {
TagNode aTagNode = new TagNode("a");
aTagNode.addAttribute("name", anchor, true);
aTagNode.addAttribute("id", anchor, true);
return aTagNode;
}
/**
* {@inheritDoc}
*/
@Override
public void buildEditLinkUrl(int section) {
}
/**
* {@inheritDoc}
*/
@Override
public AbstractParser createNewInstance(String rawWikitext) {
return new WikipediaParser(
rawWikitext,
isTemplateTopic(),
getWikiListener());
}
/**
*
* @param isTOCIdentifier
* <code>true</code> if the __TOC__ keyword was parsed
*/
@Override
public ITableOfContent createTableOfContent(boolean isTOCIdentifier) {
if (fTableOfContentTag == null) {
TableOfContentTag tableOfContentTag = new TableOfContentTag("div");
tableOfContentTag.addAttribute("id", "tableofcontent", true);
tableOfContentTag.setShowToC(false);
tableOfContentTag.setTOCIdentifier(isTOCIdentifier);
fTableOfContentTag = tableOfContentTag;
this.append(fTableOfContentTag);
} else {
if (isTOCIdentifier) {
TableOfContentTag tableOfContentTag =
(TableOfContentTag) fTableOfContentTag.clone();
fTableOfContentTag.setShowToC(false);
tableOfContentTag.setShowToC(true);
tableOfContentTag.setTOCIdentifier(isTOCIdentifier);
fTableOfContentTag = tableOfContentTag;
fTableOfContent = null;
this.append(fTableOfContentTag);
}
}
if (fTableOfContentTag != null) {
if (fTableOfContent == null) {
fTableOfContent = fTableOfContentTag.getTableOfContent();
}
}
if (fToCSet == null) {
fToCSet = new HashSet<String>();
}
return fTableOfContentTag;
}
/**
* {@inheritDoc}
*/
@Override
public int decrementRecursionLevel() {
return --fRecursionLevel;
}
/**
* {@inheritDoc}
*/
@Override
public int decrementTemplateRecursionLevel() {
return --fTemplateRecursionCount;
}
/**
* {@inheritDoc}
*/
@Override
public String encodeTitleDotUrl(String wikiTitle,
boolean firstCharacterAsUpperCase) {
return Encoder.encodeTitleDotUrl(wikiTitle, firstCharacterAsUpperCase);
}
/**
* {@inheritDoc}
*/
@Override
public String encodeTitleToUrl(String wikiTitle,
boolean firstCharacterAsUpperCase) {
return Encoder.encodeTitleToUrl(wikiTitle, firstCharacterAsUpperCase);
}
/**
* {@inheritDoc}
*/
@Override
public String get2ndCategoryNamespace() {
return fNamespace.getCategory2();
}
/**
* {@inheritDoc}
*/
@Override
public String get2ndImageNamespace() {
return fNamespace.getImage2();
}
/**
* {@inheritDoc}
*/
@Override
public String get2ndTemplateNamespace() {
return fNamespace.getTemplate2();
}
/**
* Resolve an attribute reference. It can be in four possible places:
*
* 1. the attribute list for the current template 2. if self is an embedded
* template, somebody invoked us possibly with arguments--check the argument
* context 3. if self is an embedded template, the attribute list for the
* enclosing instance (recursively up the enclosing instance chain) 4. if
* nothing is found in the enclosing instance chain, then it might be a map
* defined in the group or the its supergroup etc...
*
* Attribute references are checked for validity. If an attribute has a value,
* its validity was checked before template rendering. If the attribute has no
* value, then we must check to ensure it is a valid reference. Somebody could
* reference any random value like $xyz$; formal arg checks before rendering
* cannot detect this--only the ref can initiate a validity check. So, if no
* value, walk up the enclosed template tree again, this time checking formal
* parameters not attributes Map. The formal definition must exist even if no
* value.
*
* To avoid infinite recursion in toString(), we have another condition to
* check regarding attribute values. If your template has a formal argument,
* foo, then foo will hide any value available from "above" in order to
* prevent infinite recursion.
*
* This method is not static so people can override functionality.
*/
@Override
public Object getAttribute(String attribute) { // StringTemplate self, String
// attribute) {
// System.out.println("### get("+self.getEnclosingInstanceStackString()+", "+
// attribute+")");
// System.out.println("attributes="+(self.attributes!=null?self.attributes.
// keySet().toString():"none"));
// if ( self==null ) {
// return null;
// }
//
// if ( lintMode ) {
// self.trackAttributeReference(attribute);
// }
// is it here?
Object o = null;
if (attributes != null) {
o = attributes.get(attribute);
}
// // nope, check argument context in case embedded
// if ( o==null ) {
// Map argContext = self.getArgumentContext();
// if ( argContext!=null ) {
// o = argContext.get(attribute);
// }
// }
//
// if ( o==null &&
// !self.passThroughAttributes &&
// self.getFormalArgument(attribute)!=null )
// {
// // if you've defined attribute as formal arg for this
// // template and it has no value, do not look up the
// // enclosing dynamic scopes. This avoids potential infinite
// // recursion.
// return null;
// }
//
// // not locally defined, check enclosingInstance if embedded
// if ( o==null && self.enclosingInstance!=null ) {
// /*
// System.out.println("looking for "+getName()+"."+attribute+" in super="+
// enclosingInstance.getName());
// */
// Object valueFromEnclosing = get(self.enclosingInstance, attribute);
// if ( valueFromEnclosing==null ) {
// checkNullAttributeAgainstFormalArguments(self, attribute);
// }
// o = valueFromEnclosing;
// }
//
// // not found and no enclosing instance to look at
// else if ( o==null && self.enclosingInstance==null ) {
// // It might be a map in the group or supergroup...
// o = self.group.getMap(attribute);
// }
return o;
}
/**
* What renderer is registered for this attributeClassType for this template.
* If not found, the template's group is queried.
*/
@Override
public AttributeRenderer getAttributeRenderer(Class attributeClassType) {
AttributeRenderer renderer = null;
if (attributeRenderers != null) {
renderer = (AttributeRenderer) attributeRenderers.get(attributeClassType);
}
if (renderer != null) {
// found it!
return renderer;
}
// we have no renderer overrides for the template or none for class arg
// check parent template if we are embedded
// if ( enclosingInstance!=null ) {
// return enclosingInstance.getAttributeRenderer(attributeClassType);
// }
// // else check group
// return group.getAttributeRenderer(attributeClassType);
return null;
}
/**
* {@inheritDoc}
*/
@Override
public String getCategoryNamespace() {
return fNamespace.getCategory();
}
/**
* {@inheritDoc}
*/
@Override
public Map<String, SourceCodeFormatter> getCodeFormatterMap() {
return fConfiguration.getCodeFormatterMap();
}
/**
* {@inheritDoc}
*/
@Override
public Date getCurrentTimeStamp() {
return new Date(System.currentTimeMillis());
}
/**
* {@inheritDoc}
*/
@Override
public String getImageNamespace() {
return fNamespace.getImage();
}
/**
* {@inheritDoc}
*/
@Override
public Map<String, String> getInterwikiMap() {
return fConfiguration.getInterwikiMap();
}
/**
* {@inheritDoc}
*/
@Override
public Locale getLocale() {
return fLocale;
}
/**
* {@inheritDoc}
*/
@Override
public synchronized int getNextNumber() {
return fNextNumberCounter++;
}
/**
* {@inheritDoc}
*/
@Override
public TagToken getNode(int offset) {
return fTagStack.get(offset);
}
// public TableOfContentTag getTableOfContentTag(boolean isTOCIdentifier) {
// if (fTableOfContentTag == null) {
// TableOfContentTag tableOfContentTag = new TableOfContentTag("div");
// tableOfContentTag.addAttribute("id", "tableofcontent", true);
// tableOfContentTag.setShowToC(false);
// tableOfContentTag.setTOCIdentifier(isTOCIdentifier);
// fTableOfContentTag = tableOfContentTag;
// } else {
// if (isTOCIdentifier) {
// // try {
// TableOfContentTag tableOfContentTag = (TableOfContentTag)
// fTableOfContentTag.clone();
// fTableOfContentTag.setShowToC(false);
// tableOfContentTag.setShowToC(true);
// tableOfContentTag.setTOCIdentifier(isTOCIdentifier);
// fTableOfContentTag = tableOfContentTag;
// // } catch (CloneNotSupportedException e) {
// // e.printStackTrace();
// // }
// } else {
// return fTableOfContentTag;
// }
// }
// this.append(fTableOfContentTag);
// return fTableOfContentTag;
// }
/**
* {@inheritDoc}
*/
@Override
public String getPageName() {
return fPageTitle;
}
/**
* {@inheritDoc}
*/
@Override
public String getRawWikiContent(String namespace, String templateName,
Map<String, String> templateParameters) {
if (Configuration.RAW_CONTENT) {
System.out.println("AbstractWikiModel raw: "
+ " "
+ namespace
+ " "
+ templateName);
}
if (isTemplateNamespace(namespace)) {
String magicWord = templateName;
String parameter = "";
int index = magicWord.indexOf(':');
if (index > 0) {
parameter = magicWord.substring(index + 1);
if (parameter.length() != 0) {
parameter = AbstractTemplateFunction.parseTrim(parameter, this);
}
magicWord = magicWord.substring(0, index);
}
if (MagicWord.isMagicWord(magicWord)) {
return MagicWord.processMagicWord(magicWord, parameter, this);
}
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public int getRecursionLevel() {
return fRecursionLevel;
}
/**
* {@inheritDoc}
*/
@Override
public String getRedirectLink() {
return fRedirectLink;
}
/**
* {@inheritDoc}
*/
@Override
public List<Reference> getReferences() {
return fReferences;
}
/**
* {@inheritDoc}
*/
@Override
public ResourceBundle getResourceBundle() {
return fNamespace.getResourceBundle();
}
/**
* {@inheritDoc}
*/
@Override
public List<SemanticAttribute> getSemanticAttributes() {
return null;
}
/**
* {@inheritDoc}
*/
@Override
public List<SemanticRelation> getSemanticRelations() {
return null;
}
/**
* {@inheritDoc}
*/
@Override
public SimpleDateFormat getSimpleDateFormat() {
if (fFormatter != null) {
return fFormatter;
}
fFormatter = new SimpleDateFormat();
TimeZone utc = TimeZone.getTimeZone("GMT+00");
fFormatter.setTimeZone(utc);
return fFormatter;
}
/**
* {@inheritDoc}
*/
@Override
public ITableOfContent getTableOfContent() {
return fTableOfContentTag;
}
/**
* {@inheritDoc}
*/
@Override
public Map<String, String> getTemplateCallsCache() {
return fConfiguration.getTemplateCallsCache();
}
/**
* {@inheritDoc}
*/
@Override
public ITemplateFunction getTemplateFunction(String name) {
return getTemplateMap().get(name);
}
/**
* {@inheritDoc}
*/
@Override
public Map<String, ITemplateFunction> getTemplateMap() {
return fConfiguration.getTemplateMap();
}
/**
* {@inheritDoc}
*/
@Override
public String getTemplateNamespace() {
return fNamespace.getTemplate();
}
/**
* {@inheritDoc}
*/
@Override
public Map<String, TagToken> getTokenMap() {
return fConfiguration.getTokenMap();
}
/**
* {@inheritDoc}
*/
@Override
public Set<String> getUriSchemeSet() {
return fConfiguration.getUriSchemeSet();
}
/**
* {@inheritDoc}
*/
@Override
public IEventListener getWikiListener() {
return fWikiListener;
}
/**
* {@inheritDoc}
*/
@Override
public int incrementRecursionLevel() {
return ++fRecursionLevel;
}
/**
* {@inheritDoc}
*/
@Override
public int incrementTemplateRecursionLevel() {
return ++fTemplateRecursionCount;
}
/**
* Initialize the internal class attributes
*/
protected void initialize() {
if (!fInitialized) {
fWikiListener = null;
fFormatter = null;
fToCSet = null;
fTableOfContent = null;
fTableOfContentTag = null;
fTagStack = new TagStack();
fReferences = null;
fReferenceNames = null;
fRecursionLevel = 0;
fTemplateRecursionCount = 0;
fSectionCounter = 0;
fExternalLinksCounter = 0;
fInitialized = true;
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean isCamelCaseEnabled() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isCategoryNamespace(String namespace) {
return namespace.equalsIgnoreCase(fNamespace.getCategory())
|| namespace.equalsIgnoreCase(fNamespace.getCategory2());
}
/**
* {@inheritDoc}
*/
@Override
public boolean isEditorMode() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isImageNamespace(String namespace) {
return namespace.equalsIgnoreCase(fNamespace.getImage())
|| namespace.equalsIgnoreCase(fNamespace.getImage2());
}
/**
* {@inheritDoc}
*/
@Override
public boolean isInterWiki(String namespace) {
return getInterwikiMap().containsKey(namespace);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isMathtranRenderer() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isNamespace(String namespace) {
return isImageNamespace(namespace)
|| isTemplateNamespace(namespace)
|| isCategoryNamespace(namespace);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isPreviewMode() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isSemanticWebActive() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isTemplateNamespace(String namespace) {
return namespace.equalsIgnoreCase(fNamespace.getTemplate())
|| namespace.equalsIgnoreCase(fNamespace.getTemplate2());
}
/**
* {@inheritDoc}
*/
@Override
public boolean isTemplateTopic() {
return fTemplateTopic;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isValidUriScheme(String uriScheme) {
return getUriSchemeSet().contains(uriScheme);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isValidUriSchemeSpecificPart(String uriScheme,
String uriSchemeSpecificPart) {
if (uriScheme.equals("ftp")
|| uriScheme.equals("http")
|| uriScheme.equals("https")) {
return uriSchemeSpecificPart.length() >= 2
&& uriSchemeSpecificPart.substring(0, 2).equals("//");
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean parseBBCodes() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean parseBehaviorSwitch(String identifier) {
for (int i = 0; i < WikipediaParser.TOC_IDENTIFIERS.length; i++) {
if (WikipediaParser.TOC_IDENTIFIERS[i].equals(identifier)) {
return false;
}
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public void parseEvents(IEventListener listener, String rawWikiText) {
initialize();
if (rawWikiText == null) {
return;
}
fWikiListener = listener;
WikipediaParser.parse(rawWikiText, this, false, null);
fInitialized = false;
}
/**
* {@inheritDoc}
*/
@Override
public String parseTemplates(String rawWikiText) {
return parseTemplates(rawWikiText, false);
}
/**
* {@inheritDoc}
*/
@Override
public String parseTemplates(String rawWikiText, boolean parseOnlySignature) {
if (rawWikiText == null) {
return "";
}
if (!parseOnlySignature) {
initialize();
}
StringBuilder buf =
new StringBuilder(rawWikiText.length() + rawWikiText.length() / 10);
try {
TemplateParser.parse(rawWikiText, this, buf, parseOnlySignature, true);
} catch (Exception ioe) {
ioe.printStackTrace();
buf.append("<span class=\"error\">TemplateParser exception: "
+ ioe.getClass().getSimpleName()
+ "</span>");
}
return buf.toString();
}
/**
* {@inheritDoc}
*/
@Override
public TagToken peekNode() {
return fTagStack.peek();
}
/**
* {@inheritDoc}
*/
@Override
public TagToken popNode() {
return fTagStack.pop();
}
/**
* {@inheritDoc}
*/
@Override
public boolean pushNode(TagToken node) {
return fTagStack.push(node);
}
/**
* Map a value to a named attribute. Throw NoSuchElementException if the named
* attribute is not formally defined in self's specific template and a formal
* argument list exists.
*/
protected void rawSetAttribute(Map attributes, String name, Object value) {
// if ( formalArguments!=FormalArgument.UNKNOWN &&
// getFormalArgument(name)==null )
// {
// // a normal call to setAttribute with unknown attribute
// throw new NoSuchElementException("no such attribute: "+name+
// " in template context "+
// getEnclosingInstanceStackString());
// }
if (value == null) {
return;
}
attributes.put(name, value);
}
/**
* Register a renderer for all objects of a particular type. This overrides
* any renderer set in the group for this class type.
*/
@Override
public void registerRenderer(Class attributeClassType,
AttributeRenderer renderer) {
if (attributeRenderers == null) {
attributeRenderers = new HashMap();
}
attributeRenderers.put(attributeClassType, renderer);
}
/**
* {@inheritDoc}
*/
@Override
public String render(ITextConverter converter, String rawWikiText) {
return render(converter, rawWikiText, false);
}
/**
* {@inheritDoc}
*/
@Override
public String render(ITextConverter converter, String rawWikiText,
boolean templateTopic) {
initialize();
if (rawWikiText == null) {
return "";
}
StringBuilder buf =
new StringBuilder(rawWikiText.length() + rawWikiText.length() / 10);
try {
render(converter, rawWikiText, buf, templateTopic, true);
} catch (IOException e) {
e.printStackTrace();
return null;
}
return buf.toString();
}
/**
* {@inheritDoc}
*/
@Override
public void render(ITextConverter converter, String rawWikiText,
Appendable buf, boolean templateTopic, boolean parseTemplates)
throws IOException {
initialize();
if (rawWikiText == null) {
return;
}
fTemplateTopic = templateTopic;
WikipediaParser.parse(rawWikiText, this, parseTemplates, null);
if (converter != null) {
List<BaseToken> list = fTagStack.getNodeList();
try {
converter.nodesToText(list, buf, this);
} finally {
fInitialized = false;
}
return;
}
fInitialized = false;
}
/**
* {@inheritDoc}
*/
@Override
public String render(String rawWikiText) {
return render(rawWikiText, false);
}
/**
* {@inheritDoc}
*/
@Override
public String render(String rawWikiText, boolean templateTopic) {
return render(new HTMLConverter(), rawWikiText, templateTopic);
}
/**
* {@inheritDoc}
*/
@Override
public String renderPDF(String rawWikiText) {
return render(new PDFConverter(), rawWikiText, false);
}
/**
* {@inheritDoc}
*/
@Override
public boolean replaceColon() {
return true;
}
/**
* Set an attribute for this template. If you set the same attribute more than
* once, you get a multi-valued attribute. If you send in a StringTemplate
* object as a value, it's enclosing instance (where it will inherit values
* from) is set to 'this'. This would be the normal case, though you can set
* it back to null after this call if you want. If you send in a List plus
* other values to the same attribute, they all get flattened into one List of
* values. This will be a new list object so that incoming objects are not
* altered. If you send in an array, it is converted to an ArrayIterator.
*/
@Override
public void setAttribute(String name, Object value) {
if (value == null || name == null) {
return;
}
if (name.indexOf('.') >= 0) {
throw new IllegalArgumentException("cannot have '.' in attribute names");
}
if (attributes == null) {
attributes = new HashMap<String, Object>();
}
// if (value instanceof StringTemplate) {
// ((StringTemplate) value).setEnclosingInstance(this);
// } else {
// // convert value if array
// value = ASTExpr.convertArrayToList(value);
// }
// convert plain collections
// get exactly in this scope (no enclosing)
// it will be a multi-value attribute
// System.out.println("exists: "+name+"="+o);
AttributeList v = null;
Object o = this.attributes.get(name);
if (o == null) { // new attribute
if (value instanceof List) {
v = new AttributeList(((List) value).size());
// flatten incoming list into existing
v.addAll((List) value);
rawSetAttribute(this.attributes, name, v);
return;
}
rawSetAttribute(this.attributes, name, value);
return;
}
if (o.getClass() == AttributeList.class) { // already a list made by ST
v = (AttributeList) o;
} else if (o instanceof List) { // existing attribute is non-ST List
// must copy to an ST-managed list before adding new attribute
List listAttr = (List) o;
v = new AttributeList(listAttr.size());
v.addAll(listAttr);
rawSetAttribute(this.attributes, name, v); // replace attribute w/list
} else {
// non-list second attribute, must convert existing to ArrayList
v = new AttributeList(); // make list to hold multiple values
// make it point to list now
rawSetAttribute(this.attributes, name, v); // replace attribute w/list
v.add(o); // add previous single-valued attribute
}
if (value instanceof List) {
// flatten incoming list into existing
if (v != value) { // avoid weird cyclic add
v.addAll((List) value);
}
} else {
v.add(value);
}
}
/**
* Specify a complete map of what object classes should map to which renderer
* objects.
*/
public void setAttributeRenderers(Map renderers) {
this.attributeRenderers = renderers;
}
/**
* {@inheritDoc}
*/
@Override
public void setPageName(String pageTitle) {
fPageTitle = pageTitle;
}
/**
* {@inheritDoc}
*/
@Override
public void setSemanticWebActive(boolean semanticWeb) {
}
/**
* {@inheritDoc}
*/
@Override
public void setTemplateCallsCache(Map<String, String> map) {
fConfiguration.setTemplateCallsCache(map);
}
/**
* {@inheritDoc}
*/
@Override
public void setUp() {
fFormatter = null;
fToCSet = null;
fTableOfContent = null;
fTableOfContentTag = null;
fTagStack = new TagStack();
fReferences = null;
fReferenceNames = null;
fRecursionLevel = 0;
fTemplateRecursionCount = 0;
fRedirectLink = null;
fSectionCounter = 0;
fExternalLinksCounter = 0;
}
/**
* {@inheritDoc}
*/
@Override
public boolean showSyntax(String tagName) {
return true;
}
/**
* {@inheritDoc}
*/
@Override
public int stackSize() {
return fTagStack.size();
}
/**
* Substitute the template name by the template content and parameters and
* append the new content to the writer.
*
* @param templateName
* the name of the template
* @param parameterMap
* the templates parameter <code>java.util.SortedMap</code>
* @param writer
* the buffer to append the substituted template content
* @param cacheKey
* a key for using in a cache
* @throws IOException
*/
@Override
public void substituteTemplateCall(String templateName,
Map<String, String> parameterMap, Appendable writer) throws IOException {
Map<String, String> templateCallsCache = null;
String cacheKey = null;
int cacheKeyLength = 0;
templateCallsCache = fConfiguration.getTemplateCallsCache();
if (templateCallsCache != null) {
cacheKeyLength += templateName.length() + 1;
for (Entry<String, String> entry : parameterMap.entrySet()) {
cacheKeyLength +=
entry.getKey().length() + entry.getValue().length() + 2;
}
if (cacheKeyLength < Configuration.MAX_CACHE_KEY_LENGTH) {
StringBuilder cacheKeyBuffer = new StringBuilder(cacheKeyLength);
cacheKeyBuffer.append(templateName);
cacheKeyBuffer.append("|");
for (Entry<String, String> entry : parameterMap.entrySet()) {
cacheKeyBuffer.append(entry.getKey());
cacheKeyBuffer.append("=");
cacheKeyBuffer.append(entry.getValue());
cacheKeyBuffer.append("|");
}
cacheKey = cacheKeyBuffer.toString();
String value = templateCallsCache.get(cacheKey);
if (value != null) {
// System.out.println("Cache key: " + cacheKey);
writer.append(value);
if (Configuration.TEMPLATE_NAMES) {
System.out.println("Cached: " + templateName + "-" + cacheKey);
}
return;
}
}
if (Configuration.TEMPLATE_NAMES) {
System.out
.println("Not Cached: " + templateName + "-" + cacheKeyLength);
}
}
String plainContent;
if (templateName.length() > 0 && templateName.charAt(0) == ':') {
// invalidate cache:
templateCallsCache = null;
plainContent =
getRawWikiContent("", templateName.substring(1), parameterMap);
} else {
addTemplate(templateName);
plainContent =
getRawWikiContent(getTemplateNamespace(), templateName, parameterMap);
}
if (plainContent != null) {
StringBuilder templateBuffer = new StringBuilder(plainContent.length());
TemplateParser.parseRecursive(
plainContent.trim(),
this,
templateBuffer,
false,
false,
parameterMap);
if (templateCallsCache != null && cacheKey != null) {
// save this template call in the cache
String cacheValue = templateBuffer.toString();
templateCallsCache.put(cacheKey, cacheValue);
writer.append(cacheValue);
} else {
writer.append(templateBuffer);
}
return;
}
// if no template found insert plain template name string:
writer.append("{{");
writer.append(templateName);
writer.append("}}");
}
/**
* {@inheritDoc}
*/
@Override
public TagStack swapStack(TagStack stack) {
TagStack temp = fTagStack;
fTagStack = stack;
return temp;
}
/**
* {@inheritDoc}
*/
@Override
public void tearDown() {
}
/**
* Initialize the wiki model and parse the <code>rawWikiText</code> and return
* the parsed node list.
*
* @param rawWikiText
* @return
* @deprecated
*/
@Deprecated
public List<BaseToken> toNodeList(String rawWikiText) {
initialize();
if (rawWikiText == null) {
return new ArrayList<BaseToken>();
}
WikipediaParser.parse(rawWikiText, this, true, null);
fInitialized = false;
return fTagStack.getNodeList();
}
/**
* Reduce the current token stack until an allowed parent is at the top of the
* stack
*/
@Override
public void reduceTokenStack(TagToken node) {
String allowedParents = node.getParents();
if (allowedParents != null) {
TagToken tag;
int index = -1;
while (stackSize() > 0) {
tag = peekNode();
index = allowedParents.indexOf("|" + tag.getName() + "|");
if (index < 0) {
popNode();
if (tag.getName().equals(node.getName())) {
// for wrong nested HTML tags like <table> <tr><td>number
// 1<tr><td>number 2</table>
break;
}
} else {
break;
}
}
} else {
while (stackSize() > 0) {
popNode();
}
}
}
@Override
public String getNamespaceName() {
return fNamespaceName;
}
@Override
public void setNamespaceName(String namespaceLowercase) {
if (namespaceLowercase == null) {
fNamespaceName = "";
return;
}
fNamespaceName = fNamespace.getNamespaceByLowercase(namespaceLowercase);
if (fNamespaceName == null) {
fNamespaceName = namespaceLowercase;
}
}
/**
* {@inheritDoc}
*/
@Override
public String getImageBaseURL() {
return "/wiki/${image}";
}
/**
* {@inheritDoc}
*/
@Override
public String getWikiBaseURL() {
return "/wiki/${title}";
}
/**
* {@inheritDoc}
*/
@Override
public String getWikiBaseEditURL() {
if (fLocale != null) {
String lang = fLocale.getLanguage();
return "http://" + lang + ".wikipedia.org/w/index.php?title=${title}";
}
return "http://en.wikipedia.org/w/index.php?title=${title}";
}
}