File Source: tagindex.java
1 /*
2 * Copyright (c) 2003-2006, Simon Brown
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * - Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in
13 * the documentation and/or other materials provided with the
14 * distribution.
15 *
16 * - Neither the name of Pebble nor the names of its contributors may
17 * be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32 package net.sourceforge.pebble.index;
33
34 import net.sourceforge.pebble.domain.Blog;
35 import net.sourceforge.pebble.domain.BlogEntry;
36 import net.sourceforge.pebble.domain.Tag;
37 import org.apache.commons.logging.Log;
38 import org.apache.commons.logging.LogFactory;
39
40 import java.io.*;
41 import java.util.*;
42
43 /**
44 * Represents the tag index for a blog.
45 *
46 * @author Simon Brown
47 */
48 public class TagIndex {
49
50 private static final Log log = LogFactory.getLog(TagIndex.class);
51
52 private Blog blog;
53
54 /** the map containing the tags */
55 private Map<String,IndexedTag> tags = new HashMap<String,IndexedTag>();
56
57 /** a view onto the map, ordered by tag name */
58 private List<Tag> orderedTags = new ArrayList<Tag>();
59
60 public TagIndex(Blog blog) {
61 this.blog = blog;
62
63 readIndex();
64 recalculateTagRankings();
65 }
66
67 /**
68 * Clears the index.
69 */
70 public void clear() {
/*
P/P * Method: void clear()
*
* Preconditions:
* (soft) this.blog != null
*
* Postconditions:
* this.tags == &new HashMap(clear#1)
* new HashMap(clear#1) num objects == 1
*/
71 tags = new HashMap<String,IndexedTag>();
72 writeIndex();
73 }
74
75 /**
76 * Indexes one or more blog entries.
77 *
78 * @param blogEntries a List of BlogEntry instances
79 */
80 public synchronized void index(Collection<BlogEntry> blogEntries) {
/*
P/P * Method: void index(Collection)
*
* Preconditions:
* blogEntries != null
* (soft) net.sourceforge.pebble.domain.State__static_init.new State(State__static_init#5).name != null
* (soft) this.blog != null
* (soft) this.tags != null
*
* Presumptions:
* blogEntry.blog@82 != null
* blogEntry.state@81 != null
* blogEntry.blog.rootCategory@82 != null
* blogEntry.tagsAsList@82 != null
* java.util.Iterator:next(...)@81 != null
* ...
*
* Postconditions:
* this.orderedTags == One-of{old this.orderedTags, &new ArrayList(recalculateTagRankings#2)}
* new ArrayList(recalculateTagRankings#2) num objects <= 1
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@81: {1}, {0}
* java.util.Iterator:hasNext(...)@83: {1}, {0}
*/
81 for (BlogEntry blogEntry : blogEntries) {
82 if (blogEntry.isPublished()) {
83 for (Tag tag : blogEntry.getAllTags()) {
84 IndexedTag t = getTag(tag.getName());
85 t.addBlogEntry(blogEntry.getId());
86 }
87 }
88 }
89
90 writeIndex();
91 recalculateTagRankings();
92 }
93
94 /**
95 * Indexes a single blog entry.
96 *
97 * @param blogEntry a BlogEntry instance
98 */
99 public synchronized void index(BlogEntry blogEntry) {
/*
P/P * Method: void index(BlogEntry)
*
* Preconditions:
* blogEntry != null
* blogEntry.state != null
* (soft) blogEntry.blog != null
* (soft) init'ed(blogEntry.categories)
* (soft) init'ed(blogEntry.id)
* (soft) init'ed(blogEntry.state.name)
* (soft) blogEntry.tagsAsList != null
* (soft) net.sourceforge.pebble.domain.State__static_init.new State(State__static_init#5).name != null
* (soft) this.blog != null
* (soft) this.tags != null
* ...
*
* Presumptions:
* java.util.Iterator:next(...)@101 != null
* t.blogEntries@102 != null
*
* Preconditions:
* (soft) init'ed(blogEntry.blog.rootCategory...parent)
* (soft) init'ed(blogEntry.blog.rootCategory...tagsAsList)
*
* Postconditions:
* this.orderedTags == One-of{old this.orderedTags, &new ArrayList(recalculateTagRankings#2)}
* new ArrayList(recalculateTagRankings#2) num objects <= 1
*
* Preconditions:
* (soft) init'ed(blogEntry.blog.rootCategory.parent)
* (soft) init'ed(blogEntry.blog.rootCategory.tagsAsList)
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@101: {1}, {0}
*/
100 if (blogEntry.isPublished()) {
101 for (Tag tag : blogEntry.getAllTags()) {
102 IndexedTag t = getTag(tag.getName());
103 t.addBlogEntry(blogEntry.getId());
104 }
105
106 writeIndex();
107 recalculateTagRankings();
108 }
109 }
110
111 /**
112 * Unindexes a single blog entry.
113 *
114 * @param blogEntry a BlogEntry instance
115 */
116 public synchronized void unindex(BlogEntry blogEntry) {
/*
P/P * Method: void unindex(BlogEntry)
*
* Preconditions:
* this.tags != null
* (soft) blogEntry != null
* (soft) init'ed(blogEntry.id)
* (soft) this.blog != null
*
* Presumptions:
* java.util.Iterator:next(...)@117 != null
* java.util.Map:values(...)@117 != null
* t.blogEntries@118 != null
*
* Postconditions:
* this.orderedTags == One-of{old this.orderedTags, &new ArrayList(recalculateTagRankings#2)}
* new ArrayList(recalculateTagRankings#2) num objects <= 1
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@117: {1}, {0}
*/
117 for (Tag tag : tags.values()) {
118 IndexedTag t = getTag(tag.getName());
119 t.removeBlogEntry(blogEntry.getId());
120 }
121
122 writeIndex();
123 recalculateTagRankings();
124 }
125
126 /**
127 * Helper method to load the index.
128 */
129 private void readIndex() {
/*
P/P * Method: void readIndex()
*
* Preconditions:
* this.blog != null
* (soft) this.tags != null
*
* Presumptions:
* blogEntries.length@140 <= 232-1
* org.apache.commons.logging.LogFactory:getLog(...)@50 != null
* tag.blogEntries@137 != null
* tuple.length@136 >= 1
*
* Test Vectors:
* java.io.File:exists(...)@131: {0}, {1}
* tuple.length@136: {1}, {2..+Inf}
* tuple[1]@136: Addr_Set{null}, Inverse{null}
*/
130 File indexFile = new File(blog.getIndexesDirectory(), "tags.index");
131 if (indexFile.exists()) {
132 try {
133 BufferedReader reader = new BufferedReader(new FileReader(indexFile));
134 String indexEntry = reader.readLine();
135 while (indexEntry != null) {
136 String[] tuple = indexEntry.split("=");
137 IndexedTag tag = getTag(tuple[0]);
138
139 if (tuple.length > 1 && tuple[1] != null) {
140 String[] blogEntries = tuple[1].split(",");
141 for (String blogEntry : blogEntries) {
142 tag.addBlogEntry(blogEntry);
143 }
144 }
145
146 indexEntry = reader.readLine();
147 }
148
149 reader.close();
150 } catch (Exception e) {
151 log.error("Error while reading index", e);
152 }
153 }
154 }
155
156 /**
157 * Helper method to write out the index to disk.
158 */
159 private void writeIndex() {
160 try {
/*
P/P * Method: void writeIndex()
*
* Preconditions:
* (soft) this.blog != null
* (soft) this.tags != null
*
* Presumptions:
* java.util.Iterator:next(...)@164 != null
* java.util.Map:values(...)@164 != null
* org.apache.commons.logging.LogFactory:getLog(...)@50 != null
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@164: {1}, {0}
* java.util.Iterator:hasNext(...)@167: {1}, {0}
*/
161 File indexFile = new File(blog.getIndexesDirectory(), "tags.index");
162 BufferedWriter writer = new BufferedWriter(new FileWriter(indexFile));
163
164 for (IndexedTag tag : tags.values()) {
165 writer.write(tag.getName());
166 writer.write("=");
167 for (String blogEntry : tag.getBlogEntries()) {
168 writer.write(blogEntry);
169 writer.write(",");
170 }
171 writer.newLine();
172 }
173
174 writer.flush();
175 writer.close();
176 } catch (Exception e) {
177 log.error("Error while writing index", e);
178 }
179 }
180
181 /**
182 * Gets a tag from the index, creating it if necessary.
183 *
184 * @param name the tag as a String
185 * @return a Tag instance
186 */
187 synchronized IndexedTag getTag(String name) {
/*
P/P * Method: IndexedTag getTag(String)
*
* Preconditions:
* this.tags != null
* (soft) init'ed(this.blog)
*
* Postconditions:
* return_value != null
* new ArrayList(IndexedTag#1) num objects <= 1
* new IndexedTag(getTag#1) num objects <= 1
* new IndexedTag(getTag#1).blog == this.blog
* (soft) init'ed(new IndexedTag(getTag#1).blog)
* new IndexedTag(getTag#1).blogEntries == &new ArrayList(IndexedTag#1)
* new IndexedTag(getTag#1).name != null
*
* Test Vectors:
* java.util.Map:get(...)@189: Inverse{null}, Addr_Set{null}
*/
188 String encodedName = Tag.encode(name);
189 IndexedTag tag = tags.get(encodedName);
190 if (tag == null) {
191 tag = new IndexedTag(name, blog);
192 tags.put(encodedName, tag);
193 }
194 return tag;
195 }
196
197 private synchronized void recalculateTagRankings() {
/*
P/P * Method: void recalculateTagRankings()
*
* Preconditions:
* this.tags != null
*
* Presumptions:
* java.lang.Math:round(...)@209 in -231..232-1
* java.util.Iterator:next(...)@201 != null
* java.util.Iterator:next(...)@215 != null
* java.util.Map:values(...)@201 != null
* java.util.Map:values(...)@215 != null
* ...
*
* Postconditions:
* this.orderedTags == One-of{old this.orderedTags, &new ArrayList(recalculateTagRankings#2)}
* new ArrayList(recalculateTagRankings#2) num objects <= 1
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@201: {1}, {0}
* java.util.Iterator:hasNext(...)@215: {1}, {0}
* java.util.List:size(...)@100: {-231..0}, {1..232-1}
* java.util.Map:size(...)@198: {-231..0}, {1..232-1}
*/
198 if (tags.size() > 0) {
199 // find the maximum
200 int maxBlogEntries = 0;
201 for (IndexedTag tag : tags.values()) {
202 if (tag.getNumberOfBlogEntries() > maxBlogEntries) {
203 maxBlogEntries = tag.getNumberOfBlogEntries();
204 }
205 }
206
207 int[] thresholds = new int[10];
208 for (int i = 0; i < 10; i++) {
209 thresholds[i] = (int)Math.round((maxBlogEntries/10.0) * (i+1));
210 }
211
212 orderedTags = new ArrayList<Tag>();
213
214 // now rank the tags
215 for (IndexedTag tag : tags.values()) {
216 tag.calculateRank(thresholds);
217
218 if (tag.getNumberOfBlogEntries() > 0) {
219 orderedTags.add(tag);
220 }
221 }
222
223 Collections.sort(orderedTags);
224 }
225
226 }
227
228 /**
229 * Gets the list of tags associated with this blog.
230 */
231 public List<Tag> getTags() {
/*
P/P * Method: List getTags()
*
* Preconditions:
* init'ed(this.orderedTags)
*
* Postconditions:
* return_value == &new ArrayList(getTags#1)
* new ArrayList(getTags#1) num objects == 1
*/
232 return new ArrayList<Tag>(orderedTags);
233 }
234
235 /**
236 * Gets the blog entries for a given tag.
237 *
238 * @param tag a tag
239 * @return a List of blog entry IDs
240 */
241 public List<String> getRecentBlogEntries(Tag tag) {
/*
P/P * Method: List getRecentBlogEntries(Tag)
*
* Preconditions:
* tag != null
* init'ed(tag.name)
* this.tags != null
* (soft) init'ed(this.blog)
*
* Postconditions:
* return_value == &new ArrayList(getRecentBlogEntries#1)
* new ArrayList(getRecentBlogEntries#1) num objects == 1
*/
242 return new ArrayList<String>(getTag(tag.getName()).getBlogEntries());
243 }
244
245 }
SofCheck Inspector Build Version : 2.22510
| tagindex.java |
2010-Jun-25 19:40:32 |
| tagindex.class |
2010-Jul-19 20:23:38 |