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