File Source: latin1seopermalinkprovider.java
1 package net.sourceforge.pebble.permalink;
2
3 import net.sourceforge.pebble.api.permalink.PermalinkProvider;
4 import net.sourceforge.pebble.domain.*;
5
6 import java.text.SimpleDateFormat;
7 import java.util.HashMap;
8 import java.util.Iterator;
9 import java.util.List;
10
11 /**
12 * Generates permalinks based upon the blog entry title. This implementation
13 * retains characters from the latin1 character by converting
14 * them to suitable "url-friendly" counterparts.
15 *
16 * It also uses dashes instead of underscores for whitespace as this is
17 * what Google recommends.
18
19 * For titles without characters from the latin1 character set
20 * the blog entry ID is used for the permalink instead.
21 *
22 * @author Mattias Reichel
23 */
/*
P/P * Method: void net.sourceforge.pebble.permalink.Latin1SeoPermalinkProvider()
*/
24 public class Latin1SeoPermalinkProvider implements PermalinkProvider {
25
26 /** the regex used to check for a day request */
27 private static final String DAY_PERMALINK_REGEX = "/\\d\\d\\d\\d/\\d\\d/\\d\\d";
28
29 /** the regex used to check for a monthly blog request */
30 private static final String MONTH_PERMALINK_REGEX = "/\\d\\d\\d\\d/\\d\\d";
31
32 /** the regex used to check for a blog entry permalink */
33 private static final String BLOG_ENTRY_PERMALINK_REGEX = "/[\\w-]*";
34
35 /** the Blog associated with this provider instance */
36 private Blog blog;
37
38 /**
39 * Gets the blog associated with this provider instance.
40 *
41 * @return a Blog instance
42 */
43 public Blog getBlog() {
/*
P/P * Method: Blog getBlog()
*
* Preconditions:
* init'ed(this.blog)
*
* Postconditions:
* return_value == this.blog
* init'ed(return_value)
*/
44 return this.blog;
45 }
46
47 /**
48 * Sets the blog associated with this provider instance.
49 *
50 * @param blog a Blog instance
51 */
52 public void setBlog(Blog blog) {
/*
P/P * Method: void setBlog(Blog)
*
* Postconditions:
* this.blog == blog
* init'ed(this.blog)
*/
53 this.blog = blog;
54 }
55
56 /**
57 * Gets the permalink for a blog entry.
58 *
59 * @return a URI as a String
60 */
61 public synchronized String getPermalink(BlogEntry blogEntry) {
/*
P/P * Method: String getPermalink(BlogEntry)
*
* Presumptions:
* net.sourceforge.pebble.domain.Blog:getBlogEntries(...)@66 != null
* net.sourceforge.pebble.domain.BlogEntry:getTitle(...)@62 != null
* net.sourceforge.pebble.domain.BlogEntry:getTitle(...)@71 != null
* net.sourceforge.pebble.domain.BlogService:getBlogEntry(...)@70 != null
*
* Preconditions:
* blogEntry != null
* (soft) this.blog != null
*
* Presumptions:
* java.util.List:get(...)@70 != null
* java.util.List:indexOf(...)@68 - java.util.List:size(...)@68 in -232..6_442_450_942
* java.util.List:size(...)@68 >= -231+1
*
* Postconditions:
* return_value != null
*
* Test Vectors:
* net.sourceforge.pebble.domain.BlogEntry:getTitle(...)@62: Addr_Set{null}, Inverse{null}
* java.lang.String:equals(...)@71: {0}, {1}
* java.lang.String:length(...)@62: {1..232-1}, {0}
*/
62 if (blogEntry.getTitle() == null || blogEntry.getTitle().length() == 0) {
63 return buildPermalink(blogEntry);
64 } else {
65 BlogService service = new BlogService();
66 List entries = getBlog().getBlogEntries();
67 int count = 0;
68 for (int i = entries.size() - 1; i > entries.indexOf(blogEntry.getId()); i--) {
69 try {
70 BlogEntry entry = service.getBlogEntry(getBlog(), "" + ((BlogEntry)entries.get(i)).getId());
71 if (entry.getTitle().equals(blogEntry.getTitle())) {
72 count++;
73 }
74 } catch (BlogServiceException e) {
75 // do nothing
76 }
77 }
78
79 if (count == 1) {
80 return buildPermalink(blogEntry);
81 } else {
82 return buildPermalink(blogEntry) + "_" + blogEntry.getId();
83 }
84 }
85 }
86
87 private String buildPermalink(BlogEntry blogEntry) {
/*
P/P * Method: String buildPermalink(BlogEntry)
*
* Test Vectors:
* net.sourceforge.pebble.domain.BlogEntry:getTitle(...)@88: Addr_Set{null}, Inverse{null}
*
* Preconditions:
* blogEntry != null
*
* Postconditions:
* return_value != null
*
* Test Vectors:
* java.lang.String:length(...)@104: {1..232-1}, {0}
* java.lang.String:length(...)@89: {1..232-1}, {0}
* java.util.Iterator:hasNext(...)@94: {1}, {0}
*/
88 String title = blogEntry.getTitle();
89 if (title == null || title.length() == 0) {
90 title = "" + blogEntry.getId();
91 } else {
92 title = title.toLowerCase();
93 title = title.replaceAll("[\\. ,;/\\\\_]", "-"); // Change whitespace and punctuation marks to dashes
94 for(String search : characterSubstitutions.keySet()) {
95 title = title.replaceAll(search, characterSubstitutions.get(search));
96 }
97 title = title.replaceAll("[^a-z0-9-]", "");
98 title = title.replaceAll("-+", "-");
99 title = title.replaceAll("^-*", "");
100 title = title.replaceAll("-*$", "");
101 }
102
103 // if the title has been blanked out, use the blog entry instead
104 if (title == null || title.length() == 0) {
105 title = "" + blogEntry.getId();
106 }
107
108 return "/" + title;
109 }
110
111
112 public boolean isBlogEntryPermalink(String uri) {
/*
P/P * Method: bool isBlogEntryPermalink(String)
*
* Postconditions:
* init'ed(return_value)
*
* Test Vectors:
* uri: Addr_Set{null}, Inverse{null}
*/
113 if(uri != null) {
114 return uri.matches(BLOG_ENTRY_PERMALINK_REGEX);
115 } else {
116 return false;
117 }
118 }
119
120 public BlogEntry getBlogEntry(String uri) {
/*
P/P * Method: BlogEntry getBlogEntry(String)
*
* Presumptions:
* net.sourceforge.pebble.domain.Blog:getBlogEntries(...)@122 != null
* net.sourceforge.pebble.domain.BlogEntry:getLocalPermalink(...)@128 != null
* net.sourceforge.pebble.domain.BlogService:getBlogEntry(...)@125 != null
*
* Preconditions:
* this.blog != null
*
* Presumptions:
* java.util.Iterator:next(...)@125 != null
*
* Postconditions:
* init'ed(return_value)
*
* Test Vectors:
* java.lang.String:endsWith(...)@128: {0}, {1}
* java.util.Iterator:hasNext(...)@123: {1}, {0}
*/
121 BlogService service = new BlogService();
122 Iterator it = getBlog().getBlogEntries().iterator();
123 while(it.hasNext()) {
124 try {
125 BlogEntry blogEntry = service.getBlogEntry(getBlog(), "" + ((BlogEntry)it.next()).getId());
126 // use the local permalink, just in case the entry has been aggregated
127 // and an original permalink assigned
128 if (blogEntry.getLocalPermalink().endsWith(uri)) {
129 return blogEntry;
130 }
131 } catch (BlogServiceException e) {
132 // do nothing
133 }
134 }
135
136 return null;
137 }
138
139 /**
140 * Gets the permalink for a monthly blog.
141 *
142 * @param month a Month instance
143 * @return a URI as a String
144 */
145 public String getPermalink(Month month) {
/*
P/P * Method: String getPermalink(Month)
*
* Preconditions:
* month != null
* this.blog != null
*
* Postconditions:
* init'ed(return_value)
*/
146 SimpleDateFormat format = new SimpleDateFormat("'/'yyyy'/'MM");
147 format.setTimeZone(blog.getTimeZone());
148 return format.format(month.getDate());
149 }
150
151 /**
152 * Determines whether the specified URI is a monthly blog permalink.
153 *
154 * @param uri a relative URI
155 * @return true if the URI represents a permalink to a monthly blog,
156 * false otherwise
157 */
158 public boolean isMonthPermalink(String uri) {
/*
P/P * Method: bool isMonthPermalink(String)
*
* Postconditions:
* init'ed(return_value)
*
* Test Vectors:
* uri: Addr_Set{null}, Inverse{null}
*/
159 if (uri != null) {
160 return uri.matches(MONTH_PERMALINK_REGEX);
161 } else {
162 return false;
163 }
164 }
165
166 /**
167 * Gets the monthly blog referred to by the specified URI.
168 *
169 * @param uri a relative URI
170 * @return a Month instance, or null if one can't be found
171 */
172 public Month getMonth(String uri) {
/*
P/P * Method: Month getMonth(String)
*
* Postconditions:
* init'ed(return_value)
*
* Preconditions:
* this.blog != null
* uri != null
*/
173 String year = uri.substring(1, 5);
174 String month = uri.substring(6, 8);
175
176 return getBlog().getBlogForMonth(Integer.parseInt(year), Integer.parseInt(month));
177 }
178
179 /**
180 * Gets the permalink for a day.
181 *
182 * @param day a Day instance
183 * @return a URI as a String
184 */
185 public String getPermalink(Day day) {
/*
P/P * Method: String getPermalink(Day)
*
* Preconditions:
* day != null
* this.blog != null
*
* Postconditions:
* init'ed(return_value)
*/
186 SimpleDateFormat format = new SimpleDateFormat("'/'yyyy'/'MM'/'dd");
187 format.setTimeZone(blog.getTimeZone());
188 return format.format(day.getDate());
189 }
190
191 /**
192 * Determines whether the specified URI is a day permalink.
193 *
194 * @param uri a relative URI
195 * @return true if the URI represents a permalink to a day,
196 * false otherwise
197 */
198 public boolean isDayPermalink(String uri) {
/*
P/P * Method: bool isDayPermalink(String)
*
* Postconditions:
* init'ed(return_value)
*
* Test Vectors:
* uri: Addr_Set{null}, Inverse{null}
*/
199 if (uri != null) {
200 return uri.matches(DAY_PERMALINK_REGEX);
201 } else {
202 return false;
203 }
204 }
205
206 /**
207 * Gets the day referred to by the specified URI.
208 *
209 * @param uri a relative URI
210 * @return a Day instance, or null if one can't be found
211 */
212 public Day getDay(String uri) {
/*
P/P * Method: Day getDay(String)
*
* Postconditions:
* init'ed(return_value)
*
* Preconditions:
* this.blog != null
* uri != null
*/
213 String year = uri.substring(1, 5);
214 String month = uri.substring(6, 8);
215 String day = uri.substring(9, 11);
216
217 return getBlog().getBlogForDay(Integer.parseInt(year),
218 Integer.parseInt(month), Integer.parseInt(day));
219 }
220
221
222 /** the List of characters that will be substituted */
223 private static final HashMap<String,String> characterSubstitutions;
224 static {
225
/*
P/P * Method: net.sourceforge.pebble.permalink.Latin1SeoPermalinkProvider__static_init
*
* Postconditions:
* characterSubstitutions == &new HashMap(Latin1SeoPermalinkProvider__static_init#1)
* new HashMap(Latin1SeoPermalinkProvider__static_init#1) num objects == 1
*/
226 characterSubstitutions = new HashMap<String,String>();
227
228 characterSubstitutions.put("\u00B2", "2");
229 characterSubstitutions.put("\u00B3", "3");
230
231 characterSubstitutions.put("\u00C0", "A");
232 characterSubstitutions.put("\u00C1", "A");
233 characterSubstitutions.put("\u00C2", "A");
234 characterSubstitutions.put("\u00C3", "A");
235 characterSubstitutions.put("\u00C4", "A");
236 characterSubstitutions.put("\u00C5", "A");
237 characterSubstitutions.put("\u00C6", "AE");
238 characterSubstitutions.put("\u00C7", "C");
239 characterSubstitutions.put("\u00C8", "E");
240 characterSubstitutions.put("\u00C9", "E");
241 characterSubstitutions.put("\u00CA", "E");
242 characterSubstitutions.put("\u00CB", "E");
243 characterSubstitutions.put("\u00CC", "I");
244 characterSubstitutions.put("\u00CD", "I");
245 characterSubstitutions.put("\u00CE", "I");
246 characterSubstitutions.put("\u00CF", "I");
247
248 characterSubstitutions.put("\u00D0", "D");
249 characterSubstitutions.put("\u00D1", "N");
250 characterSubstitutions.put("\u00D2", "O");
251 characterSubstitutions.put("\u00D3", "O");
252 characterSubstitutions.put("\u00D4", "O");
253 characterSubstitutions.put("\u00D5", "O");
254 characterSubstitutions.put("\u00D6", "O");
255 characterSubstitutions.put("\u00D7", "x");
256 characterSubstitutions.put("\u00D8", "O");
257 characterSubstitutions.put("\u00D9", "U");
258 characterSubstitutions.put("\u00DA", "U");
259 characterSubstitutions.put("\u00DB", "U");
260 characterSubstitutions.put("\u00DC", "U");
261 characterSubstitutions.put("\u00DD", "Y");
262 characterSubstitutions.put("\u00DE", "P");
263 characterSubstitutions.put("\u00DF", "ss");
264
265 characterSubstitutions.put("\u00E0", "a");
266 characterSubstitutions.put("\u00E1", "a");
267 characterSubstitutions.put("\u00E2", "a");
268 characterSubstitutions.put("\u00E3", "a");
269 characterSubstitutions.put("\u00E4", "a");
270 characterSubstitutions.put("\u00E5", "a");
271 characterSubstitutions.put("\u00E6", "ae");
272 characterSubstitutions.put("\u00E7", "c");
273 characterSubstitutions.put("\u00E8", "e");
274 characterSubstitutions.put("\u00E9", "e");
275 characterSubstitutions.put("\u00EA", "e");
276 characterSubstitutions.put("\u00EB", "e");
277 characterSubstitutions.put("\u00EC", "i");
278 characterSubstitutions.put("\u00ED", "i");
279 characterSubstitutions.put("\u00EE", "i");
280 characterSubstitutions.put("\u00EF", "i");
281
282 characterSubstitutions.put("\u00F0", "d");
283 characterSubstitutions.put("\u00F1", "n");
284 characterSubstitutions.put("\u00F2", "o");
285 characterSubstitutions.put("\u00F3", "o");
286 characterSubstitutions.put("\u00F4", "o");
287 characterSubstitutions.put("\u00F5", "o");
288 characterSubstitutions.put("\u00F6", "o");
289 //"\u00F7", // division sign (ignore)
290 characterSubstitutions.put("\u00F8", "o");
291 characterSubstitutions.put("\u00F9", "u");
292 characterSubstitutions.put("\u00FA", "u");
293 characterSubstitutions.put("\u00FB", "u");
294 characterSubstitutions.put("\u00FC", "u");
295 characterSubstitutions.put("\u00FD", "y");
296 characterSubstitutions.put("\u00FE", "p");
297 characterSubstitutions.put("\u00FF", "y");
298 }
299 }
SofCheck Inspector Build Version : 2.22510
| latin1seopermalinkprovider.java |
2010-Jun-25 19:40:32 |
| latin1seopermalinkprovider.class |
2010-Jul-19 20:23:38 |