KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > roller > ui > rendering > plugins > TopicTagPlugin


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. The ASF licenses this file to You
4  * under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License. For additional information regarding
15  * copyright in this work, please see the NOTICE file in the top level
16  * directory of this distribution.
17  */

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

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

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

202     public String JavaDoc render(WeblogEntryData entry, String JavaDoc str)
203     {
204         String JavaDoc entryText = str;
205         StringBuffer JavaDoc result = new StringBuffer JavaDoc(entryText.length());
206         MessageFormat JavaDoc fmt = getLinkFormat();
207
208         // Replace all of the instances matching the pattern with bookmark specified.
209
Matcher JavaDoc m = tagPatternWithBookmark.matcher(entryText);
210         while (m.find())
211         {
212             String JavaDoc bookmark = m.group(1);
213             String JavaDoc tag = m.group(2);
214             String JavaDoc site = getBookmarkSite(bookmark);
215             if (site == null)
216             {
217                 site = getDefaultTopicSite();
218             }
219             if (!site.endsWith("/"))
220             {
221                 site += "/";
222             }
223             String JavaDoc link = generateLink(fmt, site, tag);
224             m.appendReplacement(result, link);
225         }
226         m.appendTail(result);
227
228         // Now, in a second phase replace all of the instances matching the pattern without bookmark specified.
229
entryText = result.toString();
230         result = new StringBuffer JavaDoc(entryText.length());
231         m = tagPatternWithoutBookmark.matcher(entryText);
232         while (m.find())
233         {
234             String JavaDoc tag = m.group(1);
235             String JavaDoc site = getDefaultTopicSite();
236             String JavaDoc link = generateLink(fmt, site, tag);
237             m.appendReplacement(result, link);
238         }
239         m.appendTail(result);
240
241         return result.toString();
242     }
243     
244
245     /**
246      * Returns the human-friendly name of this Plugin. This is what users will see.
247      *
248      * @return The human-friendly name of this Plugin.
249      */

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

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

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

295     protected String JavaDoc getBookmarkSite(String JavaDoc bookmarkName)
296     {
297         BookmarkData bookmark = (BookmarkData) getUserBookmarks().get(bookmarkName);
298         return bookmark == null ? null : bookmark.getUrl();
299     }
300
301
302     /**
303      * Build the bookmark map.
304      * If ignoreBookmarks property is set, an empty map is returned.
305      * @return map of the user's bookmarks (type BookmarkData), keyed by name (type String).
306      */

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

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