KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > roller > presentation > velocity > plugins > topictag > TopicTagPlugin


1 /*
2  * Copyright (c) 2004
3  * Anil R. Gangolli. All rights reserved.
4  *
5  * Distributed with Roller Weblogger under the terms of the Roller license.
6  */

7
8 package org.roller.presentation.velocity.plugins.topictag;
9
10 import org.apache.commons.logging.Log;
11 import org.apache.commons.logging.LogFactory;
12 import org.apache.velocity.context.Context;
13 import org.roller.RollerException;
14 import org.roller.config.RollerConfig;
15 import org.roller.model.BookmarkManager;
16 import org.roller.model.RollerFactory;
17 import org.roller.pojos.BookmarkData;
18 import org.roller.pojos.WeblogEntryData;
19 import org.roller.pojos.WebsiteData;
20 import org.roller.presentation.RollerRequest;
21 import org.roller.presentation.velocity.PagePlugin;
22
23 import java.io.UnsupportedEncodingException JavaDoc;
24 import java.net.URLEncoder JavaDoc;
25 import java.text.FieldPosition JavaDoc;
26 import java.text.MessageFormat JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Map JavaDoc;
31 import java.util.regex.Matcher JavaDoc;
32 import java.util.regex.Pattern JavaDoc;
33 import java.util.regex.PatternSyntaxException JavaDoc;
34
35 /**
36  * Provides an easy way to write topic tag links for Technorati (or similar services).
37  * <p/>
38  * Looks for occurrences of topic tag specifiers of the form
39  * <pre>
40  * <code>topic:{topicbookmark}[tag]</code> OR <code>topic:[tag]</code>
41  * </pre>
42  * and replaces them with a topic tag link of the form:
43  * <pre>
44  * <code>&lt;a rel="tag" HREF="site/tag"&gt;tag&lt;/a&gt;</code>
45  * </pre>
46  * <p/>
47  * More information on topic tag links can be found at <a HREF="http://www.technorati.com">Technorati</a>.
48  * <p/>
49  * <p/>
50  * In the first form, the <code>topicbookmark</code> is used as the name of a bookmark, and the URL from that bookmark
51  * entry is used as the <code>site</code> portion in the <code>href</code> of the link.
52  * <p/>
53  * All folders are searched to find a bookmark with the name specified by <code>topicbookmark</code>. A name must match
54  * exactly, ignoring case. The first matching bookmark is used, and folders may be searched in any order. The
55  * bookmark's URL value can end in a "/" or not; either will work.
56  * <p/>
57  * The second form is equivalent to using the string "Default Topic Site" as the value of <code>topicbookmark</code>.
58  * <p/>
59  * If the bookmark lookup fails, then "http://www.technorati.com/tag" is used as the site name in the topic tag link.
60  * <p/>
61  * You can specify some Roller site-wide properties in the roller.properties or roller-custom.properties to override
62  * some of the defaults of this plugin. All of these are optional.
63  * <p/>
64  * <dl> <dt><code>org.roller.presentation.velocity.plugins.topictag.TopicTagPlugin.defaultTopicBookmarkName</code></dt>
65  * <dd>Specify the name of the default topic bookmark instead of "Default Topic Site"</dd>
66  * <p/>
67  * <dt><code>org.roller.presentation.velocity.plugins.topictag.TopicTagPlugin.defaultTopicSite</code></dt> <dd>Specify
68  * the default site name to be used instead of "http://www.technorati.com" for the case in which all of the lookups
69  * fail.</dd>
70  * <p/>
71  * <dt><code>org.roller.presentation.velocity.plugins.topictag.TopicTagPlugin.tagPatternWithBookmark</code></dt> <dd>Can
72  * be used to redefine the regular expression used to find a long-form topic tag specifiers in the input. This pattern
73  * corresponds to the "long form" and must have two matching groups. Group 1 must correspond to the bookmark name.
74  * Group 2 must correspond to the tag.</dd>
75  * <p/>
76  * <dt><code>org.roller.presentation.velocity.plugins.topictag.TopicTagPlugin.tagPatternDefaultBookmark</code></dt>
77  * <dd>Can be used to redefine the regular expression used to find short-form topic tag specifiers in the input. This
78  * pattern must have one matching group, which corresponds to the tag.</dd>
79  * <p/>
80  * <dt><code>org.roller.presentation.velocity.plugins.topictag.TopicTagPlugin.linkFormatString</code></dt> <dd>Can be
81  * used to redefine the format of the generated link. This string is a message format string with three positional
82  * parameters. Parameter <code>{0}</code> represents the site including a trailing "/", parameter <code>{1}</code>
83  * represents the url-encoded tag and parameter <code>{2}</code> represents the original unencoded tag text.</dd>
84  * <p/>
85  * </dl>
86  *
87  * @author <a HREF="mailto:anil@busybuddha.org">Anil Gangolli</a>
88  * @version 0.3
89  */

90 public class TopicTagPlugin implements PagePlugin
91 {
92     private static final String JavaDoc version = "0.3";
93     private static final Log mLogger = LogFactory.getFactory().getInstance(TopicTagPlugin.class);
94
95
96     // Default values of properties that can be set from the web.xml configuration.
97
private String JavaDoc defaultTopicBookmarkName = "Default Topic Site";
98     private String JavaDoc defaultTopicSite = "http://www.technorati.com/tag";
99     private String JavaDoc tagRegexWithBookmark = "topic:\\{(.*?)\\}\\[(.*?)\\]";
100     private String JavaDoc tagRegexWithoutBookmark = "topic:\\[(.*?)\\]";
101     private String JavaDoc linkFormatString = "<a rel=\"tag\" HREF=\"{0}{1}\">{2}</a>";
102
103     // Compiled form of the regular expressions above. Compiled during the init()
104
private Pattern JavaDoc tagPatternWithBookmark;
105     private Pattern JavaDoc tagPatternWithoutBookmark;
106     private MessageFormat JavaDoc linkFormat;
107
108     // A map of the user's bookmarks (values of type BookmarkData) keyed by name (String). If the user has multiple
109
// bookmarks with the same name in different folders, only one gets used (the last encountered).
110
private Map JavaDoc userBookmarks;
111
112
113     public TopicTagPlugin()
114     {
115     }
116
117     /**
118      * Initialize the plugin instance. This sets up the configurable properties and default topic site.
119      *
120      * @param rreq Plugins may need to access RollerRequest.
121      * @param ctx Plugins may place objects into the Velocity Context.
122      * @see PagePlugin#init(org.roller.presentation.RollerRequest, org.apache.velocity.context.Context)
123      */

124     public void init(RollerRequest rreq, Context ctx) throws RollerException
125     {
126         if (mLogger.isDebugEnabled())
127         {
128             mLogger.debug("TopicTagPlugin v. " + version);
129         }
130
131
132         // Initialize property settings
133
initializeProperties();
134
135         // Build map of the user's bookmarks
136
userBookmarks = buildBookmarkMap(rreq.getWebsite());
137
138         // Determine default topic site from bookmark if present
139
BookmarkData defaultTopicBookmark = (BookmarkData) userBookmarks.get(defaultTopicBookmarkName);
140         if (defaultTopicBookmark != null) defaultTopicSite = defaultTopicBookmark.getUrl();
141
142         // Append / to defaultTopicSite if it doesn't have it
143
if (!defaultTopicSite.endsWith("/"))
144         {
145             defaultTopicSite += "/";
146         }
147
148         // Compile patterns and make sure they have the correct number of matching groups in them.
149
try
150         {
151             tagPatternWithBookmark = Pattern.compile(tagRegexWithBookmark);
152         }
153         catch (PatternSyntaxException JavaDoc e)
154         {
155             throw new RollerException("Invalid regular expression for topic tags with bookmark '" +
156                 tagRegexWithBookmark + "': " + e.getMessage());
157         }
158         int groupCount = tagPatternWithBookmark.matcher("").groupCount();
159         if (groupCount != 2)
160         {
161             throw new RollerException("Regular expression for topic tags with bookmark '" + tagRegexWithBookmark +
162                 "' contains wrong number of capture groups. Must have exactly 2. Contains " + groupCount);
163         }
164
165         try
166         {
167             tagPatternWithoutBookmark = Pattern.compile(tagRegexWithoutBookmark);
168         }
169         catch (PatternSyntaxException JavaDoc e)
170         {
171             throw new RollerException("Invalid regular expression for topic tags without bookmark '" +
172                 tagRegexWithoutBookmark + "': " + e.getMessage());
173         }
174         groupCount = tagPatternWithoutBookmark.matcher("").groupCount();
175         if (groupCount != 1)
176         {
177             throw new RollerException("Regular expression for topic tags without bookmark '" + tagRegexWithoutBookmark +
178                 "' contains wrong number of capture groups. Must have exactly 1. Contains " + groupCount);
179         }
180
181         // Create link format from format string
182
setLinkFormat(new MessageFormat JavaDoc(linkFormatString));
183     }
184
185     /**
186      * Apply the plugin to the given entry. Returns the entry text with topic tags expanded.
187      *
188      * @param entry WeblogEntry to which plugin should be applied.
189      * @param skipFlagIgnored the "skip flag" is ignored for this plugin
190      * @return Results of applying plugin to entry.
191      */

192     public String JavaDoc render(WeblogEntryData entry, boolean skipFlagIgnored)
193     {
194         String JavaDoc entryText = entry.getText();
195         StringBuffer JavaDoc result = new StringBuffer JavaDoc(entryText.length());
196         MessageFormat JavaDoc fmt = getLinkFormat();
197
198         // Replace all of the instances matching the pattern with bookmark specified.
199
Matcher JavaDoc m = tagPatternWithBookmark.matcher(entryText);
200         while (m.find())
201         {
202             String JavaDoc bookmark = m.group(1);
203             String JavaDoc tag = m.group(2);
204             String JavaDoc site = getBookmarkSite(bookmark);
205             if (site == null)
206             {
207                 site = getDefaultTopicSite();
208             }
209             if (!site.endsWith("/"))
210             {
211                 site += "/";
212             }
213             String JavaDoc link = generateLink(fmt, site, tag);
214             m.appendReplacement(result, link);
215         }
216         m.appendTail(result);
217
218         // Now, in a second phase replace all of the instances matching the pattern without bookmark specified.
219
entryText = result.toString();
220         result = new StringBuffer JavaDoc(entryText.length());
221         m = tagPatternWithoutBookmark.matcher(entryText);
222         while (m.find())
223         {
224             String JavaDoc tag = m.group(1);
225             String JavaDoc site = getDefaultTopicSite();
226             String JavaDoc link = generateLink(fmt, site, tag);
227             m.appendReplacement(result, link);
228         }
229         m.appendTail(result);
230
231         return result.toString();
232     }
233
234     /**
235      * Render from a string without entry data context. Because we require the context of the website in order to do the
236      * bookmark searches. This implementation can only return the input string.
237      *
238      * @param str String to which plugin should be applied.
239      * @return the input value.
240      * @see PagePlugin#render(String)
241      */

242     public String JavaDoc render(String JavaDoc str)
243     {
244         return str;
245     }
246
247     /**
248      * Returns the human-friendly name of this Plugin. This is what users will see.
249      *
250      * @return The human-friendly name of this Plugin.
251      */

252     public String JavaDoc getName()
253     {
254         // TODO: i18n
255
return "Topic Tags";
256     }
257
258     /**
259      * Briefly describes the function of the Plugin. May contain HTML.
260      *
261      * @return A brief description of the Plugin.
262      */

263     public String JavaDoc getDescription()
264     {
265         // TODO: i18n
266
return "Expands topic tags for <a HREF=\\'http://www.technorati.com\\'>Technorati</a> and similar sites. " +
267             "Topic tags are of the form <code>topic:{topicbookmark}[tag]</code>, where <code>topicbookmark</code> " +
268             "is the name of a bookmark whose URL will be used for the site name in the topic tag. " +
269             "If <code>{topicbookmark}</code> is omitted the plugin will use the URL of the <code>Default Topic Site</code> " +
270             "bookmark, if that is defined, otherwise http://www.technorati.com.";
271     }
272
273     /**
274      * Helper to generate the link from the link format and values of the site and tag.
275      *
276      * @param fmt link format. This should have positional parameters {0} representing site with terminal /, {1} for
277      * url-encoded-tag, and {2} for visible tag text.
278      * @param site base portion of the URL
279      * @param tag tag value
280      * @return the generated link as a string
281      */

282     protected String JavaDoc generateLink(MessageFormat JavaDoc fmt, String JavaDoc site, String JavaDoc tag)
283     {
284         // Allocate initial capacity of buffer of approximately the right length.
285
StringBuffer JavaDoc sb = new StringBuffer JavaDoc(site.length() + tag.length() + getLinkFormatString().length());
286         fmt.format(new Object JavaDoc[]{site, urlEncode(tag), tag}, sb, new FieldPosition JavaDoc(0));
287         return sb.toString();
288     }
289
290     /**
291      * Resolve the bookmark name and return the URL from it. If the bookmark can't be found, return null
292      *
293      * @param bookmarkName name of the bookmark
294      * @return String form of the URL from the bookmark by that name from any of the user's folders, or null if not
295      * found.
296      */

297     protected String JavaDoc getBookmarkSite(String JavaDoc bookmarkName)
298     {
299         BookmarkData bookmark = (BookmarkData) getUserBookmarks().get(bookmarkName);
300         return bookmark == null ? null : bookmark.getUrl();
301     }
302
303
304     /**
305      * Build the bookmark map.
306      *
307      * @return map of the user's bookmarks (type BookmarkData), keyed by name (type String).
308      */

309     protected Map JavaDoc buildBookmarkMap(WebsiteData website) throws RollerException
310     {
311         Map JavaDoc bookmarkMap = new HashMap JavaDoc();
312         if (website == null)
313         {
314             mLogger.debug("Init called without website. Skipping bookmark initialization.");
315         }
316         else
317         {
318             BookmarkManager bMgr = RollerFactory.getRoller().getBookmarkManager();
319             List JavaDoc bookmarks = bMgr.retrieveBookmarks(bMgr.getRootFolder(website), true);
320
321             for (Iterator JavaDoc i = bookmarks.iterator(); i.hasNext();)
322             {
323                 BookmarkData b = (BookmarkData) i.next();
324                 bookmarkMap.put(b.getName(), b);
325             }
326         }
327         return bookmarkMap;
328     }
329
330
331     // Sets up properties. For better and worse, doesn't use reflection
332
private void initializeProperties()
333     {
334         setDefaultTopicBookmarkName(getSetting("defaultTopicBookmarkName", getDefaultTopicBookmarkName()));
335         setDefaultTopicSite(getSetting("defaultTopicSite", getDefaultTopicSite()));
336         setTagRegexWithBookmark(getSetting("tagRegexWithBookmark", getTagRegexWithBookmark()));
337         setTagRegexWithoutBookmark(getSetting("tagRegexWithoutBookmark", getTagRegexWithoutBookmark()));
338         setLinkFormatString(getSetting("linkFormatString", getLinkFormatString()));
339     }
340
341     private static final String JavaDoc thisClassnameDot = TopicTagPlugin.class.getName() + ".";
342
343     private String JavaDoc getSetting(String JavaDoc propName, String JavaDoc defaultValue)
344     {
345         String JavaDoc fullPropName = thisClassnameDot + propName;
346         String JavaDoc val = (String JavaDoc) RollerConfig.getProperty(fullPropName);
347         return (val != null) ? val : defaultValue;
348     }
349
350
351     // Private helper to URL encode the tag text.
352
private String JavaDoc urlEncode(String JavaDoc text)
353     {
354         // URL encode the searchtext
355
try
356         {
357             return URLEncoder.encode(text, "UTF-8");
358         }
359         catch (UnsupportedEncodingException JavaDoc uex)
360         {
361             // Should never actually occur for UTF-8. If it does, we barf bitterly.
362
throw new RuntimeException JavaDoc(uex);
363         }
364     }
365
366
367     // Property getters and setters
368

369
370     public String JavaDoc getDefaultTopicSite()
371     {
372         return defaultTopicSite;
373     }
374
375     public void setDefaultTopicSite(String JavaDoc defaultTopicSite)
376     {
377         this.defaultTopicSite = defaultTopicSite;
378     }
379
380     public String JavaDoc getTagRegexWithBookmark()
381     {
382         return tagRegexWithBookmark;
383     }
384
385     public void setTagRegexWithBookmark(String JavaDoc tagRegexWithBookmark)
386     {
387         this.tagRegexWithBookmark = tagRegexWithBookmark;
388     }
389
390     public String JavaDoc getTagRegexWithoutBookmark()
391     {
392         return tagRegexWithoutBookmark;
393     }
394
395     public void setTagRegexWithoutBookmark(String JavaDoc tagRegexWithoutBookmark)
396     {
397         this.tagRegexWithoutBookmark = tagRegexWithoutBookmark;
398     }
399
400     public String JavaDoc getLinkFormatString()
401     {
402         return linkFormatString;
403     }
404
405     public void setLinkFormatString(String JavaDoc linkFormatString)
406     {
407         this.linkFormatString = linkFormatString;
408     }
409
410     public MessageFormat JavaDoc getLinkFormat()
411     {
412         return linkFormat;
413     }
414
415     public void setLinkFormat(MessageFormat JavaDoc linkFormat)
416     {
417         this.linkFormat = linkFormat;
418     }
419
420     public Pattern JavaDoc getTagPatternWithBookmark()
421     {
422         return tagPatternWithBookmark;
423     }
424
425     public void setTagPatternWithBookmark(Pattern JavaDoc tagPatternWithBookmark)
426     {
427         this.tagPatternWithBookmark = tagPatternWithBookmark;
428     }
429
430     public Pattern JavaDoc getTagPatternWithoutBookmark()
431     {
432         return tagPatternWithoutBookmark;
433     }
434
435     public void setTagPatternWithoutBookmark(Pattern JavaDoc tagPatternWithoutBookmark)
436     {
437         this.tagPatternWithoutBookmark = tagPatternWithoutBookmark;
438     }
439
440     public String JavaDoc getDefaultTopicBookmarkName()
441     {
442         return defaultTopicBookmarkName;
443     }
444
445     public void setDefaultTopicBookmarkName(String JavaDoc defaultTopicBookmarkName)
446     {
447         this.defaultTopicBookmarkName = defaultTopicBookmarkName;
448     }
449
450     public Map JavaDoc getUserBookmarks()
451     {
452         return userBookmarks;
453     }
454
455     public void setUserBookmarks(Map JavaDoc userBookmarks)
456     {
457         this.userBookmarks = userBookmarks;
458     }
459
460
461 }
462
Popular Tags