/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. The ASF licenses this file to You
* under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. For additional information regarding
* copyright in this work, please see the NOTICE file in the top level
* directory of this distribution.
*/
package org.apache.roller.weblogger.business.plugins.entry;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.roller.weblogger.WebloggerException;
import org.apache.roller.weblogger.business.BookmarkManager;
import org.apache.roller.weblogger.business.WebloggerFactory;
import org.apache.roller.weblogger.business.plugins.entry.WeblogEntryPlugin;
import org.apache.roller.weblogger.pojos.WeblogBookmark;
import org.apache.roller.weblogger.pojos.WeblogBookmarkFolder;
import org.apache.roller.weblogger.pojos.WeblogEntry;
import org.apache.roller.weblogger.pojos.Weblog;
/**
* Automatically insert links into entry text based on users bookmarks.
*/
public class BookmarkPlugin implements WeblogEntryPlugin {
private static Log mLogger = LogFactory.getLog(BookmarkPlugin.class);
protected String name = "Bookmark Linker";
protected String description = "Automatically uses your Bookmarks to " +
"create links. Simply use the Name of a Bookmark and it will be " +
"converted into a hyperlink using the Bookmark's URL.";
public BookmarkPlugin() {
mLogger.debug("BookmarkPlugin instantiated.");
}
public String getName() {
return name;
}
public String getDescription() {
return StringEscapeUtils.escapeJavaScript(description);
}
public void init(Weblog website) throws WebloggerException {}
public String render(WeblogEntry entry, String str) {
String text = str;
try {
BookmarkManager bMgr = WebloggerFactory.getWeblogger().getBookmarkManager();
WeblogBookmarkFolder rootFolder = bMgr.getRootFolder(entry.getWebsite());
text = matchBookmarks(text, rootFolder);
text = lookInFolders(text, rootFolder.getFolders());
} catch (WebloggerException e) {
// nothing much I can do, go with default "Weblog" value
// could be WebloggerException or NullPointerException
mLogger.warn(e);
}
return text;
}
/**
* Recursively travel down Folder tree, attempting
* to match up Bookmarks in each Folder.
*
* @param text
* @param folders
* @return
*/
private String lookInFolders(String text, Collection folders) {
Iterator it = folders.iterator();
while (it.hasNext()) {
WeblogBookmarkFolder folder = (WeblogBookmarkFolder)it.next();
text = matchBookmarks(text, folder);
if (!folder.getFolders().isEmpty()) {
lookInFolders(text, folder.getFolders());
}
}
return text;
}
private String matchBookmarks(String text,WeblogBookmarkFolder folder) {
Iterator bookmarks = folder.getBookmarks().iterator();
String workingText = text;
while (bookmarks.hasNext()) {
WeblogBookmark bookmark = (WeblogBookmark)bookmarks.next();
String bkDescription = bookmark.getDescription();
if (bkDescription == null) bkDescription = "";
String bookmarkLink = "<a href=\"" +
bookmark.getUrl() + "\" title=\"" +
bkDescription + "\">" +
bookmark.getName() + "</a>";
try {
// Replace all occurrences of bookmark name that don't occur within the bounds of an anchor tag
// Notes:
// - use reluctant quantifiers on the tags to avoid gobbling more than desired
// - use non-capturing groups for boundaries to avoid replacing the boundary as well as the bookmark name.
// - we depend on the numbering of the specific groups in this expression in the replacement code below.
// TODO: should escape the bookmark name
String regEx = "(<a(?:\\s.*?)??/>)|(<a(?:\\s.*?)??>)|(</a(?:\\s.*?)??>)|(?:\\b)(" + bookmark.getName() + ")(?:\\b)";
Matcher m = Pattern.compile(regEx).matcher(workingText);
StringBuffer textBuf = new StringBuffer(workingText.length());
int inLink = 0;
while (m.find()) {
if (m.group(1) != null) {
// self-closed anchor tag <a ... /> -- ignore
} else if (m.group(2) != null) {
// matched opening anchor tag <a ...>
inLink++;
} else if (m.group(3) != null) {
// closing anchor tag </a>, but ignore nonmatching ones
if (inLink > 0) inLink--;
} else if (m.group(4) != null) {
// matched the bookmark -- replace, but only if not within a link tag.
if (inLink == 0) m.appendReplacement(textBuf, bookmarkLink);
}
// Any remaining case indicates a bug. One could add an else with assertion here. Conservatively don't substitute.
}
m.appendTail(textBuf);
workingText = textBuf.toString();
} catch (PatternSyntaxException e) {
// Can happen since we don't escape pattern the bookmark name to protect pattern characters.
mLogger.warn("Failed to substitute for bookmark [" + bookmark.getName() + "] due to regular expression characters.");
}
}
return workingText.toString();
}
}