File Source: blog.java

         /* 
    P/P   *  Method: net.sourceforge.pebble.domain.Blog__static_init
          * 
          *  Postconditions:
          *    init'ed(log)
          */
     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.domain;
    33  
    34  import java.io.File;
    35  import java.lang.reflect.Constructor;
    36  import java.util.ArrayList;
    37  import java.util.Arrays;
    38  import java.util.Calendar;
    39  import java.util.Collection;
    40  import java.util.Collections;
    41  import java.util.Date;
    42  import java.util.Iterator;
    43  import java.util.LinkedList;
    44  import java.util.List;
    45  import java.util.Properties;
    46  import java.util.StringTokenizer;
    47  
         /* 
    P/P   *  Method: void net.sourceforge.pebble.domain.Blog(String)
          * 
          *  Postconditions:
          *    init'ed(this.blog)
          *    init'ed(this.id)
          *    init'ed(this.messages)
          *    init'ed(this.root)
          *    new LinkedList(AbstractBlog#1) num objects == 1
          */
    48  import javax.servlet.http.HttpServletRequest;
    49  
    50  import net.sourceforge.pebble.Configuration;
    51  import net.sourceforge.pebble.Constants;
    52  import net.sourceforge.pebble.PebbleContext;
    53  import net.sourceforge.pebble.PluginProperties;
    54  import net.sourceforge.pebble.aggregator.NewsFeedCache;
    55  import net.sourceforge.pebble.aggregator.NewsFeedEntry;
    56  import net.sourceforge.pebble.api.confirmation.CommentConfirmationStrategy;
    57  import net.sourceforge.pebble.api.confirmation.TrackBackConfirmationStrategy;
    58  import net.sourceforge.pebble.api.decorator.ContentDecorator;
    59  import net.sourceforge.pebble.api.event.EventDispatcher;
    60  import net.sourceforge.pebble.api.event.blog.BlogEvent;
    61  import net.sourceforge.pebble.api.event.blog.BlogListener;
    62  import net.sourceforge.pebble.api.event.blogentry.BlogEntryListener;
    63  import net.sourceforge.pebble.api.event.comment.CommentListener;
    64  import net.sourceforge.pebble.api.event.trackback.TrackBackListener;
    65  import net.sourceforge.pebble.api.permalink.PermalinkProvider;
    66  import net.sourceforge.pebble.confirmation.DefaultConfirmationStrategy;
    67  import net.sourceforge.pebble.dao.CategoryDAO;
    68  import net.sourceforge.pebble.dao.DAOFactory;
    69  import net.sourceforge.pebble.dao.PersistenceException;
    70  import net.sourceforge.pebble.decorator.ContentDecoratorChain;
    71  import net.sourceforge.pebble.decorator.HideUnapprovedResponsesDecorator;
    72  import net.sourceforge.pebble.event.AuditListener;
    73  import net.sourceforge.pebble.event.DefaultEventDispatcher;
    74  import net.sourceforge.pebble.event.EventListenerList;
    75  import net.sourceforge.pebble.event.blogentry.EmailSubscriptionListener;
    76  import net.sourceforge.pebble.index.AuthorIndex;
    77  import net.sourceforge.pebble.index.AuthorIndexListener;
    78  import net.sourceforge.pebble.index.BlogEntryIndex;
    79  import net.sourceforge.pebble.index.BlogEntryIndexListener;
    80  import net.sourceforge.pebble.index.CategoryIndex;
    81  import net.sourceforge.pebble.index.CategoryIndexListener;
    82  import net.sourceforge.pebble.index.EmailSubscriptionList;
    83  import net.sourceforge.pebble.index.ResponseIndex;
    84  import net.sourceforge.pebble.index.ResponseIndexListener;
    85  import net.sourceforge.pebble.index.SearchIndex;
    86  import net.sourceforge.pebble.index.SearchIndexListener;
    87  import net.sourceforge.pebble.index.StaticPageIndex;
    88  import net.sourceforge.pebble.index.TagIndex;
    89  import net.sourceforge.pebble.index.TagIndexListener;
    90  import net.sourceforge.pebble.logging.AbstractLogger;
    91  import net.sourceforge.pebble.logging.CombinedLogFormatLogger;
    92  import net.sourceforge.pebble.permalink.DefaultPermalinkProvider;
    93  import net.sourceforge.pebble.util.StringUtils;
    94  
    95  import org.apache.commons.logging.Log;
    96  import org.apache.commons.logging.LogFactory;
    97  
    98  /**
    99   * Represents a blog.
   100   *
   101   * @author    Simon Brown
   102   */
   103  public class Blog extends AbstractBlog {
   104  
   105    private static final Log log = LogFactory.getLog(Blog.class);
   106  
   107    public static final String ABOUT_KEY = "about";
   108    public static final String EMAIL_KEY = "email";
   109    public static final String BLOG_OWNERS_KEY = "blogOwners";
   110    public static final String BLOG_PUBLISHERS_KEY = "blogPublishers";
   111    public static final String BLOG_CONTRIBUTORS_KEY = "blogContributors";
   112    public static final String BLOG_READERS_KEY = "blogReaders";
   113    public static final String PRIVATE_KEY = "private";
   114    public static final String LUCENE_ANALYZER_KEY = "luceneAnalyzer";
   115    public static final String CONTENT_DECORATORS_KEY = "decorators";
   116    public static final String BLOG_LISTENERS_KEY = "blogListeners";
   117    public static final String BLOG_ENTRY_LISTENERS_KEY = "blogEntryListeners";
   118    public static final String COMMENT_LISTENERS_KEY = "commentListeners";
   119    public static final String TRACKBACK_LISTENERS_KEY = "trackBackListeners";
   120    public static final String EVENT_DISPATCHER_KEY = "eventDispatcher";
   121    public static final String LOGGER_KEY = "logger";
   122    public static final String PERMALINK_PROVIDER_KEY = "permalinkProviderName";
   123    public static final String COMMENT_CONFIRMATION_STRATEGY_KEY = "commentConfirmationStrategy";
   124    public static final String TRACKBACK_CONFIRMATION_STRATEGY_KEY = "trackBackConfirmationStrategy";
   125    public static final String RICH_TEXT_EDITOR_FOR_COMMENTS_ENABLED_KEY = "richTextEditorForCommentsEnabled";
   126    public static final String HOME_PAGE_KEY = "homePage";
   127  
   128    /** the ID of this blog */
   129    private String id = "default";
   130  
   131    /** the collection of Year instance that this root blog is managing */
   132    private List<Year> years;
   133  
   134    /** the root category associated with this blog */
   135    private Category rootCategory;
   136  
   137    /** the referer filter associated with this blog */
   138    private RefererFilterManager refererFilterManager;
   139  
   140    /** the editable theme belonging to this blog */
   141    private Theme editableTheme;
   142  
   143    /** the permalink provider in use */
   144    private PermalinkProvider permalinkProvider;
   145  
   146    /** the log used to log referers, requests, etc */
   147    private AbstractLogger logger;
   148  
   149    /** the decorator chain associated with this blog */
   150    private ContentDecoratorChain decoratorChain;
   151  
   152    private CommentConfirmationStrategy commentConfirmationStrategy;
   153    private TrackBackConfirmationStrategy trackBackConfirmationStrategy;
   154  
   155    /** the event dispatcher */
   156    private EventDispatcher eventDispatcher;
   157  
   158    /** the event listener list */
   159    private EventListenerList eventListenerList;
   160  
   161    /** the plugin properties */
   162    private PluginProperties pluginProperties;
   163  
   164    private SearchIndex searchIndex;
   165    private BlogEntryIndex blogEntryIndex;
   166    private ResponseIndex responseIndex;
   167    private TagIndex tagIndex;
   168    private CategoryIndex categoryIndex;
   169    private AuthorIndex authorIndex;
   170    private StaticPageIndex staticPageIndex;
   171  
   172    private EmailSubscriptionList emailSubscriptionList;
   173  
   174    /**
   175     * Creates a new Blog instance, based at the specified location.
   176     *
   177     * @param root    an absolute path pointing to the root directory of the blog
   178     */
   179    public Blog(String root) {
   180      super(root);
   181  
   182      // probably Blog should be made a final class if init is called from here - 
   183      // see javadoc comment on AbstractBlog.init() for reasons
   184      init();
   185    }
   186  
   187    /**
   188     * Initialize this blog - prepare it for use.
   189     * Note: As this blog instance is passed to the various participants while
   190     * it is being initialized, this method is dependent on the correct order
   191     * of calls: Keep in mind that 'this' is only partly initialized until the
   192     * end of this method...
   193     */
   194  
           /* 
    P/P     *  Method: void init()
            * 
            *  Preconditions:
            *    init'ed(this.root)
            *    (soft) net/sourceforge/pebble/dao/DAOFactory.configuredFactory != null
            *    (soft) net/sourceforge/pebble/dao/DAOFactory.configuredFactory.categoryDAO != null
            *    (soft) net/sourceforge/pebble/dao/DAOFactory.configuredFactory.refererFilterDAO != null
            *    (soft) net/sourceforge/pebble/dao/file/FileRefererFilterDAO.log != null
            *    (soft) net/sourceforge/pebble/domain/AbstractBlog.log != null
            *    (soft) this.messages != null
            *    (soft) init'ed(this.rootCategory)
            * 
            *  Presumptions:
            *    java.lang.Class:forName(...)@199 != null
            *    java.lang.Class:forName(...)@232 != null
            *    java.lang.Class:forName(...)@241 != null
            * 
            *  Postconditions:
            *    this.authorIndex == &new AuthorIndex(init#11)
            *    this.blogEntryIndex == &new BlogEntryIndex(init#7)
            *    this.categoryIndex == &new CategoryIndex(init#10)
            *    this.commentConfirmationStrategy != null
            *    this.decoratorChain == &new ContentDecoratorChain(init#13)
            *    this.emailSubscriptionList == &new EmailSubscriptionList(init#18)
            *    this.eventDispatcher != null
            *    init'ed(this.eventDispatcher.eventListenerList)
            *    this.eventListenerList == &new EventListenerList(initEventDispatcher#1)
            *    init'ed(this.logger)
            *    ...
            */
   195    protected void init() {
   196      super.init();
   197  
   198      try {
   199        Class c = Class.forName(getPermalinkProviderName());
   200        setPermalinkProvider((PermalinkProvider)c.newInstance());
   201      } catch (Exception e) {
   202        error("Could not load permalink provider \"" + getPermalinkProviderName() + "\"");
   203        e.printStackTrace();
   204        setPermalinkProvider(new DefaultPermalinkProvider());
   205      }
   206  
   207      // load categories
   208      try {
   209        DAOFactory factory = DAOFactory.getConfiguredFactory();
   210        CategoryDAO dao = factory.getCategoryDAO();
   211        rootCategory = dao.getCategories(this);
   212      } catch (PersistenceException pe) {
   213        pe.printStackTrace();
   214      }
   215  
   216      refererFilterManager = new RefererFilterManager(this);
   217      pluginProperties = new PluginProperties(this);
   218      years = new ArrayList();
   219  
   220      // create the various indexes for this blog
   221      searchIndex = new SearchIndex(this);
   222      blogEntryIndex = new BlogEntryIndex(this);
   223      responseIndex = new ResponseIndex(this);
   224      tagIndex = new TagIndex(this);
   225      categoryIndex = new CategoryIndex(this);
   226      authorIndex = new AuthorIndex(this);
   227      staticPageIndex = new StaticPageIndex(this);
   228  
   229      decoratorChain = new ContentDecoratorChain(this);
   230  
   231      try {
   232        Class c = Class.forName(getCommentConfirmationStrategyName());
   233        commentConfirmationStrategy = (CommentConfirmationStrategy)c.newInstance();
   234      } catch (Exception e) {
+  235        error("Could not load comment confirmation strategy \"" + getCommentConfirmationStrategyName() + "\"");
   236        e.printStackTrace();
   237        commentConfirmationStrategy = new DefaultConfirmationStrategy();
   238      }
   239  
   240      try {
   241        Class c = Class.forName(getTrackBackConfirmationStrategyName());
   242        trackBackConfirmationStrategy = (TrackBackConfirmationStrategy)c.newInstance();
   243      } catch (Exception e) {
+  244        error("Could not load TrackBack confirmation strategy \"" + getTrackBackConfirmationStrategyName() + "\"");
   245        e.printStackTrace();
   246        trackBackConfirmationStrategy = new DefaultConfirmationStrategy();
   247      }
   248  
   249      emailSubscriptionList = new EmailSubscriptionList(this);
   250  
   251      initLogger();
   252      initEventDispatcher();
   253      initBlogListeners();
   254      initBlogEntryListeners();
   255      initCommentListeners();
   256      initTrackBackListeners();
   257      initDecorators();
   258    }
   259  
   260    /**
   261     * Initialises the logger for this blog.
   262     */
           /* 
    P/P     *  Method: void initLogger()
            * 
            *  Preconditions:
            *    this.properties != null
            *    (soft) net/sourceforge/pebble/domain/AbstractBlog.log != null
            *    (soft) this.messages != null
            * 
            *  Presumptions:
            *    java.lang.Class:forName(...)@267 != null
            *    java.lang.Class:getConstructor(...)@268 != null
            *    org.apache.commons.logging.LogFactory:getLog(...)@105 != null
            * 
            *  Postconditions:
            *    init'ed(this.logger)
            *    init'ed(this.messages)
            *    new CombinedLogFormatLogger(initLogger#4) num objects <= 1
            */
   263    private void initLogger() {
   264      log.debug("Initializing logger");
   265  
   266      try {
   267        Class c = Class.forName(getLoggerName());
   268        Constructor cons = c.getConstructor(new Class[] {Blog.class});
   269        this.logger = (AbstractLogger)cons.newInstance(new Object[] {this});
   270      } catch (Exception e) {
   271        error("Could not start logger \"" + getLoggerName() + "\"");
   272        e.printStackTrace();
   273        this.logger = new CombinedLogFormatLogger(this);
   274      }
   275    }
   276  
   277    /**
   278     * Initialises the event dispatcher for this blog.
   279     */
           /* 
    P/P     *  Method: void initEventDispatcher()
            * 
            *  Preconditions:
            *    (soft) this.properties != null
            * 
            *  Presumptions:
            *    java.lang.Class:forName(...)@285 != null
            *    org.apache.commons.logging.LogFactory:getLog(...)@105 != null
            * 
            *  Postconditions:
            *    this.eventDispatcher != null
            *    init'ed(this.eventDispatcher.eventListenerList)
            *    this.eventListenerList == &new EventListenerList(initEventDispatcher#1)
            *    new ArrayList(EventListenerList#1) num objects == 1
            *    new ArrayList(EventListenerList#2) num objects == 1
            *    new ArrayList(EventListenerList#3) num objects == 1
            *    new ArrayList(EventListenerList#4) num objects == 1
            *    new EventListenerList(initEventDispatcher#1) num objects == 1
            *    new DefaultEventDispatcher(initEventDispatcher#2) num objects <= 1
            *    init'ed(new DefaultEventDispatcher(initEventDispatcher#2).eventListenerList)
            *    ...
            */
   280    private void initEventDispatcher() {
   281      log.debug("Initializing event dispatcher");
   282      eventListenerList = new EventListenerList();
   283  
   284      try {
   285        Class c = Class.forName(getEventDispatcherName());
   286        this.eventDispatcher = (EventDispatcher)c.newInstance();
   287      } catch (Exception e) {
   288        e.printStackTrace();
   289        this.eventDispatcher = new DefaultEventDispatcher();
   290      }
   291  
   292      eventDispatcher.setEventListenerList(eventListenerList);
   293    }
   294  
   295    /**
   296     * Initialises any blog listeners configured for this blog.
   297     */
           /* 
    P/P     *  Method: void initBlogListeners()
            * 
            *  Preconditions:
            *    this.properties != null
            *    (soft) net/sourceforge/pebble/domain/AbstractBlog.log != null
            *    (soft) this.messages != null
            *    (soft) this.eventListenerList != null
            *    (soft) this.eventListenerList.blogListeners != null
            * 
            *  Presumptions:
            *    java.lang.Class:forName(...)@303 != null
            *    java.util.Iterator:next(...)@301 != null
            *    org.apache.commons.logging.LogFactory:getLog(...)@105 != null
            * 
            *  Postconditions:
            *    init'ed(this.messages)
            * 
            *  Test Vectors:
            *    java.util.Iterator:hasNext(...)@301: {1}, {0}
            */
   298    private void initBlogListeners() {
   299      log.debug("Registering blog listeners");
   300  
   301      for (String className : getBlogListeners()) {
   302        try {
   303          Class c = Class.forName(className.trim());
   304          BlogListener listener = (BlogListener) c.newInstance();
   305          eventListenerList.addBlogListener(listener);
   306        } catch (Exception e) {
+  307          error("Could not start blog listener \"" + className + "\" - check the class name is correct on the <a href=\"viewPlugins.secureaction#blogListeners\">plugins page</a>.");
   308          log.error("Blog listener " + className + " could not be registered", e);
   309        }
   310      }
   311    }
   312  
   313    /**
   314     * Initialises any blog entry listeners configured for this blog.
   315     */
           /* 
    P/P     *  Method: void initBlogEntryListeners()
            * 
            *  Preconditions:
            *    this.eventListenerList != null
            *    this.eventListenerList.blogEntryListeners != null
            *    this.properties != null
            *    (soft) net/sourceforge/pebble/domain/AbstractBlog.log != null
            *    (soft) this.messages != null
            * 
            *  Presumptions:
            *    java.lang.Class:forName(...)@321 != null
            *    java.lang.Throwable:getMessage(...)@344 != null
            *    java.util.Iterator:next(...)@319 != null
            *    org.apache.commons.logging.LogFactory:getLog(...)@105 != null
            * 
            *  Postconditions:
            *    init'ed(this.messages)
            * 
            *  Test Vectors:
            *    java.util.Iterator:hasNext(...)@319: {1}, {0}
            */
   316    private void initBlogEntryListeners() {
   317      log.debug("Registering blog entry listeners");
   318  
   319      for (String className : getBlogEntryListeners()) {
   320        try {
   321          Class c = Class.forName(className.trim());
   322          BlogEntryListener listener = (BlogEntryListener) c.newInstance();
   323          eventListenerList.addBlogEntryListener(listener);
   324        } catch (Exception e) {
+  325          error("Could not start blog entry listener \"" + className + "\" - check the class name is correct on the <a href=\"viewPlugins.secureaction#blogEntryListeners\">plugins page</a>.");
   326          log.error("Blog entry listener " + className + " could not be registered", e);
   327        }
   328      }
   329  
   330      // these are required to keep the various indexes up to date
   331      eventListenerList.addBlogEntryListener(new BlogEntryIndexListener());
   332      eventListenerList.addBlogEntryListener(new TagIndexListener());
   333      eventListenerList.addBlogEntryListener(new CategoryIndexListener());
   334      eventListenerList.addBlogEntryListener(new AuthorIndexListener());
   335      eventListenerList.addBlogEntryListener(new SearchIndexListener());
   336      eventListenerList.addBlogEntryListener(new AuditListener());
   337      try {
   338        eventListenerList.addBlogEntryListener(new EmailSubscriptionListener());
   339      } catch (Throwable t) {
+  340        final String text = "Error while starting e-mail subscription listener - add mail.jar and activation.jar to the server classpath if you want to enable this listener.";
+  341        warn(text);
   342        if(t instanceof NoClassDefFoundError &&
   343           t.getMessage() != null &&
   344           t.getMessage().indexOf("javax/mail/Session") > -1) {
   345          log.warn(text); // consider exception already handled well...
   346        } else {
   347          log.warn(text, t);
   348        }
   349      }
   350    }
   351  
   352    /**
   353     * Initialises any comment listeners configured for this blog.
   354     */
           /* 
    P/P     *  Method: void initCommentListeners()
            * 
            *  Preconditions:
            *    this.eventListenerList != null
            *    this.eventListenerList.commentListeners != null
            *    this.properties != null
            *    (soft) net/sourceforge/pebble/domain/AbstractBlog.log != null
            *    (soft) this.messages != null
            * 
            *  Presumptions:
            *    java.lang.Class:forName(...)@360 != null
            *    java.util.Iterator:next(...)@358 != null
            *    org.apache.commons.logging.LogFactory:getLog(...)@105 != null
            * 
            *  Postconditions:
            *    init'ed(this.messages)
            * 
            *  Test Vectors:
            *    java.util.Iterator:hasNext(...)@358: {1}, {0}
            */
   355    private void initCommentListeners() {
   356      log.debug("Registering comment listeners");
   357  
   358      for (String className : getCommentListeners()) {
   359        try {
   360          Class c = Class.forName(className.trim());
   361          CommentListener listener = (CommentListener) c.newInstance();
   362          eventListenerList.addCommentListener(listener);
   363        } catch (Exception e) {
+  364          error("Could not start comment listener \"" + className + "\" - check the class name is correct on the <a href=\"viewPlugins.secureaction#commentListeners\">plugins page</a>.");
   365          log.error("Comment listener " + className + " could not be registered", e);
   366        }
   367      }
   368  
   369      eventListenerList.addCommentListener(new ResponseIndexListener());
   370      eventListenerList.addCommentListener(new AuditListener());
   371    }
   372  
   373    /**
   374     * Initialises any TrackBack listeners configured for this blog.
   375     */
           /* 
    P/P     *  Method: void initTrackBackListeners()
            * 
            *  Preconditions:
            *    this.eventListenerList != null
            *    this.eventListenerList.trackBackListeners != null
            *    this.properties != null
            *    (soft) net/sourceforge/pebble/domain/AbstractBlog.log != null
            *    (soft) this.messages != null
            * 
            *  Presumptions:
            *    java.lang.Class:forName(...)@381 != null
            *    java.util.Iterator:next(...)@379 != null
            *    org.apache.commons.logging.LogFactory:getLog(...)@105 != null
            * 
            *  Postconditions:
            *    init'ed(this.messages)
            * 
            *  Test Vectors:
            *    java.util.Iterator:hasNext(...)@379: {1}, {0}
            */
   376    private void initTrackBackListeners() {
   377      log.debug("Registering TrackBack listeners");
   378  
   379      for (String className : getTrackBackListeners()) {
   380        try {
   381          Class c = Class.forName(className.trim());
   382          TrackBackListener listener = (TrackBackListener) c.newInstance();
   383          eventListenerList.addTrackBackListener(listener);
   384        } catch (Exception e) {
+  385          error("Could not start TrackBack listener \"" + className + "\" - check the class name is correct on the <a href=\"viewPlugins.secureaction#trackbackListeners\">plugins page</a>.");
   386          log.error("TrackBack listener " + className + " could not be registered", e);
   387        }
   388      }
   389  
   390      eventListenerList.addTrackBackListener(new ResponseIndexListener());
   391      eventListenerList.addTrackBackListener(new AuditListener());
   392    }
   393  
   394    /**
   395     * Initialises any content decorators configufred for this blog.
   396     */
           /* 
    P/P     *  Method: void initDecorators()
            * 
            *  Preconditions:
            *    this.decoratorChain != null
            *    this.decoratorChain.decorators != null
            *    this.properties != null
            *    (soft) net/sourceforge/pebble/domain/AbstractBlog.log != null
            *    (soft) this.messages != null
            * 
            *  Presumptions:
            *    java.lang.Class:forName(...)@404 != null
            *    java.util.Iterator:next(...)@402 != null
            *    org.apache.commons.logging.LogFactory:getLog(...)@105 != null
            * 
            *  Postconditions:
            *    init'ed(this.messages)
            * 
            *  Test Vectors:
            *    java.util.Iterator:hasNext(...)@402: {1}, {0}
            */
   397    private void initDecorators() {
   398      log.debug("Registering decorators");
   399  
   400      decoratorChain.add(new HideUnapprovedResponsesDecorator());
   401  
   402      for (String className : getContentDecorators()) {
   403        try {
   404          Class c = Class.forName(className.trim());
   405          ContentDecorator decorator = (ContentDecorator) c.newInstance();
   406          decorator.setBlog(this);
   407          decoratorChain.add(decorator);
   408        } catch (Exception e) {
+  409          error("Could not start decorator \"" + className + "\" - check the class name is correct on the <a href=\"viewPlugins.secureaction#contentDecorators\">plugins page</a>.");
   410          e.printStackTrace();
   411          log.error(className + " could not be started", e);
   412        }
   413      }
   414    }
   415  
   416    /**
   417     * Gets the default properties for a Blog.
   418     *
   419     * @return    a Properties instance
   420     */
           /* 
    P/P     *  Method: Properties getDefaultProperties()
            * 
            *  Postconditions:
            *    return_value == &new Properties(getDefaultProperties#1)
            *    new Properties(getDefaultProperties#1) num objects == 1
            */
   421    protected Properties getDefaultProperties() {
   422      Properties defaultProperties = new Properties();
   423      defaultProperties.setProperty(NAME_KEY, "My blog");
   424      defaultProperties.setProperty(DESCRIPTION_KEY, "");
   425      defaultProperties.setProperty(IMAGE_KEY, "");
   426      defaultProperties.setProperty(AUTHOR_KEY, "Blog Owner");
   427      defaultProperties.setProperty(EMAIL_KEY, "blog@yourdomain.com");
   428      defaultProperties.setProperty(TIMEZONE_KEY, "Europe/London");
   429      defaultProperties.setProperty(LANGUAGE_KEY, "en");
   430      defaultProperties.setProperty(COUNTRY_KEY, "GB");
   431      defaultProperties.setProperty(CHARACTER_ENCODING_KEY, "UTF-8");
   432      defaultProperties.setProperty(RECENT_BLOG_ENTRIES_ON_HOME_PAGE_KEY, "3");
   433      defaultProperties.setProperty(RECENT_RESPONSES_ON_HOME_PAGE_KEY, "3");
   434      defaultProperties.setProperty(THEME_KEY, "default");
   435      defaultProperties.setProperty(PRIVATE_KEY, FALSE);
   436      defaultProperties.setProperty(LUCENE_ANALYZER_KEY, "org.apache.lucene.analysis.SimpleAnalyzer");
   437      defaultProperties.setProperty(CONTENT_DECORATORS_KEY,
   438          "net.sourceforge.pebble.decorator.RadeoxDecorator\n" +
   439          "net.sourceforge.pebble.decorator.HtmlDecorator\n" +
   440          "net.sourceforge.pebble.decorator.EscapeMarkupDecorator\n" +
   441          "net.sourceforge.pebble.decorator.RelativeUriDecorator\n" +
   442          "net.sourceforge.pebble.decorator.ReadMoreDecorator\n" +
   443          "net.sourceforge.pebble.decorator.BlogTagsDecorator");
   444      defaultProperties.setProperty(BLOG_ENTRY_LISTENERS_KEY,
   445          "net.sourceforge.pebble.event.blogentry.XmlRpcNotificationListener");
   446      defaultProperties.setProperty(COMMENT_LISTENERS_KEY,
   447          "net.sourceforge.pebble.event.response.IpAddressListener\r\n" +
   448          "net.sourceforge.pebble.event.response.LinkSpamListener\r\n" +
   449          "net.sourceforge.pebble.event.response.ContentSpamListener\r\n" +
   450          "net.sourceforge.pebble.event.response.SpamScoreListener\r\n" +
   451          "net.sourceforge.pebble.event.response.MarkApprovedWhenAuthenticatedListener\r\n" +
   452          "#net.sourceforge.pebble.event.response.DeleteRejectedListener\r\n" +
   453          "#net.sourceforge.pebble.event.comment.EmailAuthorNotificationListener");
   454      defaultProperties.setProperty(TRACKBACK_LISTENERS_KEY,
   455          "net.sourceforge.pebble.event.response.IpAddressListener\r\n" +
   456          "net.sourceforge.pebble.event.response.LinkSpamListener\r\n" +
   457          "net.sourceforge.pebble.event.response.ContentSpamListener\r\n" +
   458          "net.sourceforge.pebble.event.response.SpamScoreListener\r\n" +
   459          "net.sourceforge.pebble.event.response.MarkApprovedWhenAuthenticatedListener\r\n" +
   460          "#net.sourceforge.pebble.event.response.DeleteRejectedListener\r\n" +
   461          "#net.sourceforge.pebble.event.trackback.EmailAuthorNotificationListener");
   462      defaultProperties.setProperty(PERMALINK_PROVIDER_KEY, "net.sourceforge.pebble.permalink.DefaultPermalinkProvider");
   463      defaultProperties.setProperty(EVENT_DISPATCHER_KEY, "net.sourceforge.pebble.event.DefaultEventDispatcher");
   464      defaultProperties.setProperty(LOGGER_KEY, "net.sourceforge.pebble.logging.CombinedLogFormatLogger");
   465      defaultProperties.setProperty(COMMENT_CONFIRMATION_STRATEGY_KEY, "net.sourceforge.pebble.confirmation.DefaultConfirmationStrategy");
   466      defaultProperties.setProperty(TRACKBACK_CONFIRMATION_STRATEGY_KEY, "net.sourceforge.pebble.confirmation.DefaultConfirmationStrategy");
   467      defaultProperties.setProperty(RICH_TEXT_EDITOR_FOR_COMMENTS_ENABLED_KEY, "true");
   468  
   469      return defaultProperties;
   470    }
   471  
   472    /**
   473     * Gets the ID of this blog.
   474     *
   475     * @return  the ID as a String
   476     */
           /* 
    P/P     *  Method: String getId()
            * 
            *  Preconditions:
            *    init'ed(this.id)
            * 
            *  Postconditions:
            *    return_value == this.id
            *    init'ed(return_value)
            */
   477    public String getId() {
   478      return this.id;
   479    }
   480  
   481    /**
   482     * Sets the ID of this blog.
   483     *
   484     * @param id    the ID as a String
   485     */
           /* 
    P/P     *  Method: void setId(String)
            * 
            *  Postconditions:
            *    this.id == id
            *    init'ed(this.id)
            */
   486    public void setId(String id) {
   487      this.id = id;
   488    }
   489  
   490    /**
   491     * Gets the URL where this blog is deployed.
   492     *
   493     * @return  a URL as a String
   494     */
           /* 
    P/P     *  Method: String getUrl()
            * 
            *  Preconditions:
            *    (soft) net/sourceforge/pebble/domain/BlogManager.instance != null
            *    (soft) init'ed(net/sourceforge/pebble/domain/BlogManager.instance.multiBlog)
            *    (soft) init'ed(this.id)
            * 
            *  Presumptions:
            *    java.lang.String:indexOf(...)@503 <= 232-4
            *    net.sourceforge.pebble.PebbleContext:getConfiguration(...)@496 != null
            *    net.sourceforge.pebble.PebbleContext:getInstance(...)@496 != null
            * 
            *  Postconditions:
            *    return_value != null
            * 
            *  Test Vectors:
            *    net/sourceforge/pebble/domain/BlogManager.instance.multiBlog: {0}, {1}
            *    config.url@496: Addr_Set{null}, Inverse{null}
            *    config.virtualHostingEnabled@496: {0}, {1}
            *    java.lang.String:length(...)@499: {1..232-1}, {0}
            */
   495    public String getUrl() {
   496      Configuration config = PebbleContext.getInstance().getConfiguration();
   497      String url = config.getUrl();
   498  
   499      if (url == null || url.length() == 0) {
   500        return "";
   501      } else if (BlogManager.getInstance().isMultiBlog()) {
   502        if (config.isVirtualHostingEnabled()) {
   503          return url.substring(0, url.indexOf("://")+3) + getId() + "." + url.substring(url.indexOf("://")+3);
   504        } else {
   505          return url + getId() + "/";
   506        }
   507      } else {
   508        return url;
   509      }
   510    }
   511  
   512    /**
   513     * Gets the relative URL where this blog is deployed.
   514     *
   515     * @return  a URL as a String
   516     */
           /* 
    P/P     *  Method: String getRelativeUrl()
            * 
            *  Preconditions:
            *    net/sourceforge/pebble/domain/BlogManager.instance != null
            *    init'ed(net/sourceforge/pebble/domain/BlogManager.instance.multiBlog)
            *    (soft) init'ed(this.id)
            * 
            *  Postconditions:
            *    return_value != null
            * 
            *  Test Vectors:
            *    net/sourceforge/pebble/domain/BlogManager.instance.multiBlog: {0}, {1}
            */
   517    public String getRelativeUrl() {
   518      if (BlogManager.getInstance().isMultiBlog()) {
   519        return "/" + getId() + "/";
   520      } else {
   521        return "/";
   522      }
   523    }
   524  
   525    /**
   526     * Gets the about description of this blog.
   527     *
   528     * @return    a String
   529     */
           /* 
    P/P     *  Method: String getAbout()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
   530    public String getAbout() {
   531      return properties.getProperty(ABOUT_KEY);
   532    }
   533  
   534    /**
   535     * Gets the home page to be used for this blog.
   536     *
   537     * @return    a String
   538     */
           /* 
    P/P     *  Method: String getHomePage()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
   539    public String getHomePage() {
   540      return properties.getProperty(HOME_PAGE_KEY);
   541    }
   542  
   543    /**
   544     * Gets the e-mail address of the blog owner.
   545     *
   546     * @return    the e-mail address
   547     */
           /* 
    P/P     *  Method: String getEmail()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
   548    public String getEmail() {
   549      return properties.getProperty(EMAIL_KEY);
   550    }
   551  
   552    /**
   553     * Gets a Collection of e-mail addresses.
   554     *
   555     * @return  a Collection of String instances
   556     */
           /* 
    P/P     *  Method: Collection getEmailAddresses()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Presumptions:
            *    java.util.Properties:getProperty(...)@549 != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
   557    public Collection getEmailAddresses() {
   558      return Arrays.asList(getEmail().split(","));
   559    }
   560  
   561    /**
   562     * Gets the first of multiple e-mail addresses.
   563     *
   564     * @return  the firt e-mail address as a String
   565     */
           /* 
    P/P     *  Method: String getFirstEmailAddress()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            * 
            *  Test Vectors:
            *    java.util.Arrays:asList(...)@558: Addr_Set{null}, Inverse{null}
            *    java.util.Collection:isEmpty(...)@568: {1}, {0}
            */
   566    public String getFirstEmailAddress() {
   567      Collection emailAddresses = getEmailAddresses();
   568      if (emailAddresses != null && !emailAddresses.isEmpty()) {
   569        return (String)emailAddresses.iterator().next();
   570      } else {
   571        return "";
   572      }
   573    }
   574  
   575    /**
   576     * Gets a comma separated list of the users that are blog owners
   577     * for this blog.
   578     *
   579     * @return  a String containng a comma separated list of user names
   580     */
           /* 
    P/P     *  Method: String getBlogOwnersAsString()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
   581    public String getBlogOwnersAsString() {
   582      return properties.getProperty(BLOG_OWNERS_KEY);
   583    }
   584  
   585    /**
   586     * Gets a list of the users that are blog owners for this blog.
   587     *
   588     * @return  a String containng a comma separated list of user names
   589     */
           /* 
    P/P     *  Method: List getBlogOwners()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Presumptions:
            *    java.util.StringTokenizer:nextToken(...)@596 != null
            * 
            *  Postconditions:
            *    return_value == &new LinkedList(getBlogOwners#1)
            *    new LinkedList(getBlogOwners#1) num objects == 1
            * 
            *  Test Vectors:
            *    java.util.Properties:getProperty(...)@582: Addr_Set{null}, Inverse{null}
            *    java.util.StringTokenizer:hasMoreTokens(...)@595: {1}, {0}
            */
   590    public List<String> getBlogOwners() {
   591      String commaSeparatedUsers = getBlogOwnersAsString();
   592      List<String> users = new LinkedList<String>();
   593      if (commaSeparatedUsers != null) {
   594        StringTokenizer tok = new StringTokenizer(commaSeparatedUsers, ",");
   595        while (tok.hasMoreTokens()) {
   596          users.add(tok.nextToken().trim());
   597        }
   598      }
   599  
   600      return users;
   601    }
   602  
   603    /**
   604     * Gets a comma separated list of the users that are blog publishers
   605     * for this blog.
   606     *
   607     * @return  a String containng a comma separated list of user names
   608     */
           /* 
    P/P     *  Method: String getBlogPublishersAsString()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
   609    public String getBlogPublishersAsString() {
   610      return properties.getProperty(BLOG_PUBLISHERS_KEY);
   611    }
   612  
   613    /**
   614     * Gets a list of the users that are blog publishers for this blog.
   615     *
   616     * @return  a String containng a comma separated list of user names
   617     */
           /* 
    P/P     *  Method: List getBlogPublishers()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Presumptions:
            *    java.util.StringTokenizer:nextToken(...)@624 != null
            * 
            *  Postconditions:
            *    return_value == &new LinkedList(getBlogPublishers#1)
            *    new LinkedList(getBlogPublishers#1) num objects == 1
            * 
            *  Test Vectors:
            *    java.util.Properties:getProperty(...)@610: Addr_Set{null}, Inverse{null}
            *    java.util.StringTokenizer:hasMoreTokens(...)@623: {1}, {0}
            */
   618    public List<String> getBlogPublishers() {
   619      String commaSeparatedUsers = getBlogPublishersAsString();
   620      List<String> users = new LinkedList<String>();
   621      if (commaSeparatedUsers != null) {
   622        StringTokenizer tok = new StringTokenizer(commaSeparatedUsers, ",");
   623        while (tok.hasMoreTokens()) {
   624          users.add(tok.nextToken().trim());
   625        }
   626      }
   627  
   628      return users;
   629    }
   630  
   631    /**
   632     * Gets a comma separated list of the users that are blog contributors
   633     * for this blog.
   634     *
   635     * @return  a String containng a comma separated list of user names
   636     */
           /* 
    P/P     *  Method: String getBlogContributorsAsString()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
   637    public String getBlogContributorsAsString() {
   638      return properties.getProperty(BLOG_CONTRIBUTORS_KEY);
   639    }
   640  
   641    /**
   642     * Gets a list of the users that are blog contributors for this blog.
   643     *
   644     * @return  a String containng a comma separated list of user names
   645     */
           /* 
    P/P     *  Method: List getBlogContributors()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Presumptions:
            *    java.util.StringTokenizer:nextToken(...)@652 != null
            * 
            *  Postconditions:
            *    return_value == &new LinkedList(getBlogContributors#1)
            *    new LinkedList(getBlogContributors#1) num objects == 1
            * 
            *  Test Vectors:
            *    java.util.Properties:getProperty(...)@638: Addr_Set{null}, Inverse{null}
            *    java.util.StringTokenizer:hasMoreTokens(...)@651: {1}, {0}
            */
   646    public List<String> getBlogContributors() {
   647      String commaSeparatedUsers = getBlogContributorsAsString();
   648      List<String> users = new LinkedList<String>();
   649      if (commaSeparatedUsers != null) {
   650        StringTokenizer tok = new StringTokenizer(commaSeparatedUsers, ",");
   651        while (tok.hasMoreTokens()) {
   652          users.add(tok.nextToken().trim());
   653        }
   654      }
   655  
   656      return users;
   657    }
   658  
   659    /**
   660     * Gets a comma separated list of the users that are blog readers
   661     * for this blog.
   662     *
   663     * @return  a String containng a comma separated list of user names
   664     */
           /* 
    P/P     *  Method: String getBlogReadersAsString()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
   665    public String getBlogReadersAsString() {
   666      return properties.getProperty(BLOG_READERS_KEY);
   667    }
   668  
   669    /**
   670     * Gets a list of the users that are blog readers for this blog.
   671     *
   672     * @return  a String containng a comma separated list of user names
   673     */
           /* 
    P/P     *  Method: List getBlogReaders()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Presumptions:
            *    java.util.StringTokenizer:nextToken(...)@680 != null
            * 
            *  Postconditions:
            *    return_value == &new LinkedList(getBlogReaders#1)
            *    new LinkedList(getBlogReaders#1) num objects == 1
            * 
            *  Test Vectors:
            *    java.util.Properties:getProperty(...)@666: Addr_Set{null}, Inverse{null}
            *    java.util.StringTokenizer:hasMoreTokens(...)@679: {1}, {0}
            */
   674    public List<String> getBlogReaders() {
   675      String commaSeparatedUsers = getBlogReadersAsString();
   676      List<String> users = new LinkedList<String>();
   677      if (commaSeparatedUsers != null) {
   678        StringTokenizer tok = new StringTokenizer(commaSeparatedUsers, ",");
   679        while (tok.hasMoreTokens()) {
   680          users.add(tok.nextToken().trim());
   681        }
   682      }
   683  
   684      return users;
   685    }
   686  
   687    /**
   688     * Gets the name of the Lucene analyzer to use.
   689     *
   690     * @return  a fully qualified class name
   691     */
           /* 
    P/P     *  Method: String getLuceneAnalyzer()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
   692    public String getLuceneAnalyzer() {
   693      return properties.getProperty(LUCENE_ANALYZER_KEY);
   694    }
   695  
   696    /**
   697     * Gets the name of the logger in use.
   698     *
   699     * @return  a fully qualified class name
   700     */
           /* 
    P/P     *  Method: String getLoggerName()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
   701    public String getLoggerName() {
   702      return properties.getProperty(LOGGER_KEY);
   703    }
   704  
   705    /**
   706     * Gets a Collection containing the names of users that are blog owners
   707     * for this blog.
   708     *
   709     * @return  a Collection containng user names as Strings
   710     */
           /* 
    P/P     *  Method: Collection getUsersInRole(String)
            * 
            *  Preconditions:
            *    init'ed(net/sourceforge/pebble/Constants.BLOG_OWNER_ROLE)
            *    roleName != null
            *    (soft) init'ed(net/sourceforge/pebble/Constants.BLOG_CONTRIBUTOR_ROLE)
            *    (soft) init'ed(net/sourceforge/pebble/Constants.BLOG_PUBLISHER_ROLE)
            *    (soft) init'ed(net/sourceforge/pebble/Constants.BLOG_READER_ROLE)
            *    (soft) this.properties != null
            * 
            *  Postconditions:
            *    return_value == One-of{&new LinkedList(getBlogOwners#1), &new LinkedList(getBlogPublishers#1), &new LinkedList(getBlogContributors#1), &new LinkedList(getUsersInRole#1), &new LinkedList(getBlogReaders#1)}
            *    return_value in Addr_Set{&new LinkedList(getUsersInRole#1),&new LinkedList(getBlogContributors#1),&new LinkedList(getBlogReaders#1),&new LinkedList(getBlogOwners#1),&new LinkedList(getBlogPublishers#1)}
            *    new LinkedList(getBlogContributors#1) num objects <= 1
            *    new LinkedList(getBlogOwners#1) num objects <= 1
            *    new LinkedList(getBlogPublishers#1) num objects <= 1
            *    new LinkedList(getBlogReaders#1) num objects <= 1
            *    new LinkedList(getUsersInRole#1) num objects == 1
            * 
            *  Test Vectors:
            *    java.lang.String:equals(...)@714: {0}, {1}
            *    java.lang.String:equals(...)@716: {0}, {1}
            *    java.lang.String:equals(...)@718: {0}, {1}
            *    java.lang.String:equals(...)@720: {0}, {1}
            */
   711    public Collection getUsersInRole(String roleName) {
   712      List<String> users = new LinkedList<String>();
   713  
   714      if (roleName.equals(Constants.BLOG_OWNER_ROLE)) {
   715        users = getBlogOwners();
   716      } else if (roleName.equals(Constants.BLOG_PUBLISHER_ROLE)) {
   717        users = getBlogPublishers();
   718      } else if (roleName.equals(Constants.BLOG_CONTRIBUTOR_ROLE)) {
   719        users = getBlogContributors();
   720      } else if (roleName.equals(Constants.BLOG_READER_ROLE)) {
   721        users = getBlogReaders();
   722      }
   723  
   724      return users;
   725    }
   726  
   727    /**
   728     * Determines whether the specified user is in the specified role.
   729     *
   730     * @param roleName    the name of the role
   731     * @param user        the name of the user
   732     * @return  true if the user is a member of the role or the list of users
   733     *          is empty, false otherwise
   734     */
           /* 
    P/P     *  Method: bool isUserInRole(String, String)
            * 
            *  Preconditions:
            *    init'ed(net/sourceforge/pebble/Constants.BLOG_OWNER_ROLE)
            *    roleName != null
            *    (soft) init'ed(net/sourceforge/pebble/Constants.BLOG_CONTRIBUTOR_ROLE)
            *    (soft) init'ed(net/sourceforge/pebble/Constants.BLOG_PUBLISHER_ROLE)
            *    (soft) init'ed(net/sourceforge/pebble/Constants.BLOG_READER_ROLE)
            *    (soft) this.properties != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            * 
            *  Test Vectors:
            *    java.util.Collection:contains(...)@737: {0}, {1}
            *    java.util.Collection:isEmpty(...)@737: {1}, {0}
            */
   735    public boolean isUserInRole(String roleName, String user) {
   736      Collection users = getUsersInRole(roleName);
   737      if (users.isEmpty() || users.contains(user)) {
   738        return true;
   739      }
   740  
   741      return false;
   742    }
   743  
   744    /**
   745     * Gets the Year instance for the specified year.
   746     *
   747     * @param year    the year as an int (e.g. 2003)
   748     * @return    a Year instance
   749     */
           /* 
    P/P     *  Method: Year getBlogForYear(int)
            * 
            *  Preconditions:
            *    this.years != null
            * 
            *  Presumptions:
            *    java.util.Iterator:next(...)@754 != null
            * 
            *  Postconditions:
            *    (soft) return_value != null
            *    init'ed(new ArrayList(Day#1) num objects)
            *    init'ed(new ArrayList(Day#2) num objects)
            *    init'ed(new ArrayList(Day#3) num objects)
            *    init'ed(new Day(Month#2) num objects)
            *    init'ed(new Day(Month#2).blog)
            *    init'ed(new Day(Month#2).blogEntries)
            *    init'ed(new Day(Month#2).date)
            *    init'ed(new Day(Month#2).day)
            *    init'ed(new Day(Month#2).month)
            *    ...
            * 
            *  Test Vectors:
            *    java.util.Iterator:hasNext(...)@753: {1}, {0}
            */
   750    public Year getBlogForYear(int year) {
   751      Iterator it = years.iterator();
   752      Year y;
   753      while (it.hasNext()) {
   754        y = (Year)it.next();
   755        if (y.getYear() == year) {
   756          return y;
   757        }
   758      }
   759  
   760      y = new Year(this, year);
   761      years.add(y);
   762      Collections.sort(years);
   763  
   764      return y;
   765    }
   766  
   767    /**
   768     * Gets the Year instance representing this year.
   769     *
   770     * @return  a Year instance for this year
   771     */
           /* 
    P/P     *  Method: Year getBlogForThisYear()
            * 
            *  Preconditions:
            *    this.properties != null
            *    this.years != null
            * 
            *  Presumptions:
            *    java.util.Calendar:getInstance(...)@329 != null
            * 
            *  Postconditions:
            *    return_value != null
            *    init'ed(new ArrayList(Day#1) num objects)
            *    init'ed(new ArrayList(Day#2) num objects)
            *    init'ed(new ArrayList(Day#3) num objects)
            *    init'ed(new Day(Month#2) num objects)
            *    init'ed(new Day(Month#2).blog)
            *    init'ed(new Day(Month#2).blogEntries)
            *    init'ed(new Day(Month#2).date)
            *    init'ed(new Day(Month#2).day)
            *    init'ed(new Day(Month#2).month)
            *    ...
            */
   772    public Year getBlogForThisYear() {
   773      Calendar cal = getCalendar();
   774      return getBlogForYear(cal.get(Calendar.YEAR));
   775    }
   776  
   777    /**
   778     * Gets all Years managed by this root blog.
   779     *
   780     * @return  a Collection of Year instances
   781     */
           /* 
    P/P     *  Method: List getYears()
            * 
            *  Preconditions:
            *    init'ed(this.years)
            * 
            *  Postconditions:
            *    return_value == this.years
            *    init'ed(return_value)
            */
   782    public List getYears() {
   783      return years;
   784    }
   785  
   786    /**
   787     * Gets all Years managed by this root blog, in reverse order.
   788     *
   789     * @return  a Collection of Year instances
   790     */
           /* 
    P/P     *  Method: List getArchives()
            * 
            *  Preconditions:
            *    init'ed(this.blogEntryIndex)
            *    this.years != null
            *    (soft) init'ed(this.blogEntryIndex.indexEntries)
            *    (soft) this.properties != null
            * 
            *  Presumptions:
            *    getBlogForFirstMonth(...).year@793 != null
            *    java.util.Iterator:next(...)@797 != null
            * 
            *  Postconditions:
            *    return_value == &new LinkedList(getArchives#1)
            *    new LinkedList(getArchives#1) num objects == 1
            * 
            *  Test Vectors:
            *    java.util.Iterator:hasNext(...)@797: {1}, {0}
            */
   791    public List<Year> getArchives() {
   792      List<Year> list = new LinkedList<Year>();
+  793      int firstYear = getBlogForFirstMonth().getYear().getYear();
   794      int thisYear = getBlogForThisYear().getYear();
   795      // only add years that are in range
+  796      Calendar cal = getCalendar();
   797      for (Year year : years) {
   798        if (year.getYear() >= firstYear && year.getYear() <= thisYear) {
   799          list.add(year);
   800        }
   801      }
   802      Collections.reverse(list);
   803      return list;
   804    }
   805  
   806    /**
   807     * Gets the Month instance representing the first month that
   808     * contains blog entries.
   809     *
   810     * @return  a Month instance
   811     */
           /* 
    P/P     *  Method: Month getBlogForFirstMonth()
            * 
            *  Preconditions:
            *    init'ed(this.blogEntryIndex)
            *    (soft) init'ed(this.blogEntryIndex.indexEntries)
            *    (soft) this.properties != null
            *    (soft) this.years != null
            * 
            *  Presumptions:
            *    java.util.List:size(...)@822 >= -231+1
            * 
            *  Postconditions:
            *    init'ed(return_value)
            *    init'ed(new ArrayList(Day#1) num objects)
            *    init'ed(new ArrayList(Day#2) num objects)
            *    init'ed(new ArrayList(Day#3) num objects)
            *    init'ed(new Day(Month#2) num objects)
            *    init'ed(new Day(Month#2).blog)
            *    init'ed(new Day(Month#2).blogEntries)
            *    init'ed(new Day(Month#2).date)
            *    init'ed(new Day(Month#2).day)
            *    init'ed(new Day(Month#2).month)
            *    ...
            * 
            *  Test Vectors:
            *    this.blogEntryIndex: Inverse{null}, Addr_Set{null}
            *    java.util.List:get(...)@822: Inverse{null}, Addr_Set{null}
            *    java.util.List:isEmpty(...)@818: {0}, {1}
            */
   812    public Month getBlogForFirstMonth() {
   813      if (getBlogEntryIndex() == null) {
   814        return getBlogForThisMonth();
   815      }
   816  
   817      List<String> blogEntryIds = getBlogEntryIndex().getBlogEntries();
+  818      if (blogEntryIds == null || blogEntryIds.isEmpty()) {
   819        return getBlogForThisMonth();
   820      }
   821  
   822      String firstBlogEntryId = blogEntryIds.get(blogEntryIds.size()-1);
   823      if (firstBlogEntryId == null) {
   824        return getBlogForThisMonth();
   825      }
   826  
   827      long dateInMillis = Long.parseLong(firstBlogEntryId);
   828      Date date = new Date(dateInMillis);
+  829      return getBlogForDay(date).getMonth();
   830    }
   831  
   832    /**
   833     * Gets a Day intance for the specified Date.
   834     *
   835     * @param date    a java.util.Date instance
   836     * @return    a Day instance representing the specified Date
   837     */
           /* 
    P/P     *  Method: Day getBlogForDay(Date)
            * 
            *  Preconditions:
            *    this.properties != null
            *    this.years != null
            * 
            *  Presumptions:
            *    java.util.Calendar:get(...)@843 in 0..11
            *    java.util.Calendar:get(...)@844 >= 1
            *    java.util.Calendar:getInstance(...)@329 != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            *    init'ed(new ArrayList(Day#1) num objects)
            *    init'ed(new ArrayList(Day#2) num objects)
            *    init'ed(new ArrayList(Day#3) num objects)
            *    init'ed(new Day(Month#2) num objects)
            *    init'ed(new Day(Month#2).blog)
            *    init'ed(new Day(Month#2).blogEntries)
            *    init'ed(new Day(Month#2).date)
            *    init'ed(new Day(Month#2).day)
            *    init'ed(new Day(Month#2).month)
            *    ...
            */
   838    public Day getBlogForDay(Date date) {
   839      Calendar cal = getCalendar();
   840      cal.setTime(date);
   841  
   842      int year = cal.get(Calendar.YEAR);
   843      int month = (cal.get(Calendar.MONTH) + 1);
   844      int day = cal.get(Calendar.DAY_OF_MONTH);
   845  
   846      return getBlogForDay(year, month, day);
   847    }
   848  
   849    /**
   850     * Gets the Day instance for today.
   851     *
   852     * @return    a Day instance
   853     */
           /* 
    P/P     *  Method: Day getBlogForToday()
            * 
            *  Preconditions:
            *    this.properties != null
            *    this.years != null
            * 
            *  Presumptions:
            *    java.util.Calendar:getInstance(...)@329 != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            *    init'ed(new ArrayList(Day#1) num objects)
            *    init'ed(new ArrayList(Day#2) num objects)
            *    init'ed(new ArrayList(Day#3) num objects)
            *    init'ed(new Day(Month#2) num objects)
            *    init'ed(new Day(Month#2).blog)
            *    init'ed(new Day(Month#2).blogEntries)
            *    init'ed(new Day(Month#2).date)
            *    init'ed(new Day(Month#2).day)
            *    init'ed(new Day(Month#2).month)
            *    ...
            */
   854    public Day getBlogForToday() {
   855      return this.getBlogForDay(getCalendar().getTime());
   856    }
   857  
   858    /**
   859     * Gets a Day intance for the specified year, month and day.
   860     *
   861     * @param year    the year as an int
   862     * @param month   the month as an int
   863     * @param day     the day as an int
   864     * @return    a Day instance representing the specified year, month and day
   865     */
           /* 
    P/P     *  Method: Day getBlogForDay(int, int, int)
            * 
            *  Preconditions:
            *    day >= 1
            *    month in 1..12
            *    this.years != null
            * 
            *  Presumptions:
            *    getBlogForMonth(...).dailyBlogs.length@867 >= 1
            *    getBlogForMonth(...).dailyBlogs@867 != null
            *    getBlogForMonth(...).lastDayInMonth@867 >= 1
            * 
            *  Postconditions:
            *    init'ed(return_value)
            *    init'ed(new ArrayList(Day#1) num objects)
            *    init'ed(new ArrayList(Day#2) num objects)
            *    init'ed(new ArrayList(Day#3) num objects)
            *    init'ed(new Day(Month#2) num objects)
            *    init'ed(new Day(Month#2).blog)
            *    init'ed(new Day(Month#2).blogEntries)
            *    init'ed(new Day(Month#2).date)
            *    init'ed(new Day(Month#2).day)
            *    init'ed(new Day(Month#2).month)
            *    ...
            */
   866    public Day getBlogForDay(int year, int month, int day) {
+  867      return getBlogForMonth(year, month).getBlogForDay(day);
   868    }
   869  
   870    /**
   871     * Gets a Month intance for the specified year and month.
   872     *
   873     * @param year    the year as an int
   874     * @param month   the month as an int
   875     * @return    a Month instance representing the specified year and month
   876     */
           /* 
    P/P     *  Method: Month getBlogForMonth(int, int)
            * 
            *  Preconditions:
            *    month in 1..12
            *    this.years != null
            * 
            *  Presumptions:
            *    getBlogForYear(...).months.length@878 >= 1
            *    month <= getBlogForYear(...).months.length@878
            *    getBlogForYear(...).months@878 != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            *    init'ed(new ArrayList(Day#1) num objects)
            *    init'ed(new ArrayList(Day#2) num objects)
            *    init'ed(new ArrayList(Day#3) num objects)
            *    init'ed(new Day(Month#2) num objects)
            *    init'ed(new Day(Month#2).blog)
            *    init'ed(new Day(Month#2).blogEntries)
            *    init'ed(new Day(Month#2).date)
            *    init'ed(new Day(Month#2).day)
            *    init'ed(new Day(Month#2).month)
            *    ...
            */
   877    public Month getBlogForMonth(int year, int month) {
   878      return getBlogForYear(year).getBlogForMonth(month);
   879    }
   880  
   881    /**
   882     * Gets the Month instance representing this month.
   883     *
   884     * @return  a Month instance for this month
   885     */
           /* 
    P/P     *  Method: Month getBlogForThisMonth()
            * 
            *  Preconditions:
            *    this.properties != null
            *    this.years != null
            * 
            *  Presumptions:
            *    java.util.Calendar:get(...)@888 in 0..11
            *    java.util.Calendar:getInstance(...)@329 != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            *    init'ed(new ArrayList(Day#1) num objects)
            *    init'ed(new ArrayList(Day#2) num objects)
            *    init'ed(new ArrayList(Day#3) num objects)
            *    init'ed(new Day(Month#2) num objects)
            *    init'ed(new Day(Month#2).blog)
            *    init'ed(new Day(Month#2).blogEntries)
            *    init'ed(new Day(Month#2).date)
            *    init'ed(new Day(Month#2).day)
            *    init'ed(new Day(Month#2).month)
            *    ...
            */
   886    public Month getBlogForThisMonth() {
   887      Calendar cal = getCalendar();
   888      return getBlogForMonth(cal.get(Calendar.YEAR), (cal.get(Calendar.MONTH) + 1));
   889    }
   890  
   891    /**
   892     * Given a Year, this method returns the Year instance
   893     * representing the previous year.
   894     *
   895     * @param year    a Year instance
   896     * @return    a Year representing the previous year
   897     */
           /* 
    P/P     *  Method: Year getBlogForPreviousYear(Year)
            * 
            *  Preconditions:
            *    this.years != null
            *    year != null
            *    year.year >= -231+1
            * 
            *  Postconditions:
            *    return_value != null
            *    init'ed(new ArrayList(Day#1) num objects)
            *    init'ed(new ArrayList(Day#2) num objects)
            *    init'ed(new ArrayList(Day#3) num objects)
            *    init'ed(new Day(Month#2) num objects)
            *    init'ed(new Day(Month#2).blog)
            *    init'ed(new Day(Month#2).blogEntries)
            *    init'ed(new Day(Month#2).date)
            *    init'ed(new Day(Month#2).day)
            *    init'ed(new Day(Month#2).month)
            *    ...
            */
   898    public Year getBlogForPreviousYear(Year year) {
   899      return getBlogForYear(year.getYear() - 1);
   900    }
   901  
   902    /**
   903     * Given a Year, this method returns the Year instance
   904     * representing the next year.
   905     *
   906     * @param year    a Year instance
   907     * @return    a Year representing the next year
   908     */
           /* 
    P/P     *  Method: Year getBlogForNextYear(Year)
            * 
            *  Preconditions:
            *    this.years != null
            *    year != null
            *    year.year <= 232-2
            * 
            *  Postconditions:
            *    return_value != null
            *    init'ed(new ArrayList(Day#1) num objects)
            *    init'ed(new ArrayList(Day#2) num objects)
            *    init'ed(new ArrayList(Day#3) num objects)
            *    init'ed(new Day(Month#2) num objects)
            *    init'ed(new Day(Month#2).blog)
            *    init'ed(new Day(Month#2).blogEntries)
            *    init'ed(new Day(Month#2).date)
            *    init'ed(new Day(Month#2).day)
            *    init'ed(new Day(Month#2).month)
            *    ...
            */
   909    public Year getBlogForNextYear(Year year) {
   910      return getBlogForYear(year.getYear() + 1);
   911    }
   912  
   913    /**
   914     * Gets all blog entries for this blog.
   915     *
   916     * @return  a List of BlogEntry objects
   917     */
           /* 
    P/P     *  Method: List getBlogEntries()
            * 
            *  Preconditions:
            *    this.years != null
            * 
            *  Presumptions:
            *    java.util.List:get(...)@923 != null
            *    java.util.List:size(...)@922 >= -231+1
            *    months[month].month@924 in 1..12
            *    org.apache.commons.logging.LogFactory:getLog(...)@105 != null
            *    y.months.length@923 >= 1
            *    ...
            * 
            *  Postconditions:
            *    return_value == &new ArrayList(getBlogEntries#1)
            *    new ArrayList(getBlogEntries#1) num objects == 1
            */
   918    public List getBlogEntries() {
   919      List blogEntries = new ArrayList();
   920      BlogService service = new BlogService();
   921  
   922      for (int year = years.size()-1; year >= 0; year--) {
   923        Year y = (Year)years.get(year);
   924        Month[] months = y.getMonths();
   925        for (int month = 11; month >= 0; month--) {
   926          try {
+  927            blogEntries.addAll(service.getBlogEntries(this, y.getYear(), months[month].getMonth()));
   928          } catch (BlogServiceException e) {
   929            log.error("Exception encountered", e);
   930          }
   931        }
   932      }
   933  
   934      return blogEntries;
   935    }
   936  
   937    /**
   938     * Gets all unpublished blog entries for this blog.
   939     *
   940     * @return  a List of BlogEntry objects
   941     */
           /* 
    P/P     *  Method: List getUnpublishedBlogEntries()
            * 
            *  Preconditions:
            *    this.blogEntryIndex != null
            *    init'ed(this.blogEntryIndex.unpublishedIndexEntries)
            * 
            *  Presumptions:
            *    org.apache.commons.logging.LogFactory:getLog(...)@105 != null
            * 
            *  Postconditions:
            *    return_value == &new ArrayList(getUnpublishedBlogEntries#1)
            *    new ArrayList(getUnpublishedBlogEntries#1) num objects == 1
            * 
            *  Test Vectors:
            *    java.util.Iterator:hasNext(...)@947: {1}, {0}
            */
   942    public List<BlogEntry> getUnpublishedBlogEntries() {
   943      List<BlogEntry> blogEntries = new ArrayList<BlogEntry>();
   944      BlogService service = new BlogService();
   945  
   946      List<String> blogEntryIds = blogEntryIndex.getUnpublishedBlogEntries();
   947      for (String blogEntryId : blogEntryIds) {
   948        try {
   949          blogEntries.add(service.getBlogEntry(this, blogEntryId));
   950        } catch (BlogServiceException e) {
   951          log.error("Exception encountered", e);
   952        }
   953      }
   954  
   955      return blogEntries;
   956    }
   957  
   958    /**
   959     * Gets the number of blog entries for this blog.
   960     *
   961     * @return  an int
   962     */
           /* 
    P/P     *  Method: int getNumberOfBlogEntries()
            * 
            *  Preconditions:
            *    this.blogEntryIndex != null
            *    this.blogEntryIndex.indexEntries != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
   963    public int getNumberOfBlogEntries() {
   964      return blogEntryIndex.getNumberOfBlogEntries();
   965    }
   966  
   967    /**
   968     * Gets the number of published blog entries for this blog.
   969     *
   970     * @return  an int
   971     */
           /* 
    P/P     *  Method: int getNumberOfPublishedBlogEntries()
            * 
            *  Preconditions:
            *    this.blogEntryIndex != null
            *    this.blogEntryIndex.publishedIndexEntries != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
   972    public int getNumberOfPublishedBlogEntries() {
   973      return blogEntryIndex.getNumberOfPublishedBlogEntries();
   974    }
   975  
   976    /**
   977     * Gets the number of unpublished blog entries for this blog.
   978     *
   979     * @return  an int
   980     */
           /* 
    P/P     *  Method: int getNumberOfUnpublishedBlogEntries()
            * 
            *  Preconditions:
            *    this.blogEntryIndex != null
            *    this.blogEntryIndex.unpublishedIndexEntries != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
   981    public int getNumberOfUnpublishedBlogEntries() {
   982      return blogEntryIndex.getNumberOfUnpublishedBlogEntries();
   983    }
   984  
   985    /**
   986     * Gets the number of static pages for this blog.
   987     *
   988     * @return  an int
   989     */
           /* 
    P/P     *  Method: int getNumberOfStaticPages()
            * 
            *  Preconditions:
            *    this.staticPageIndex != null
            *    this.staticPageIndex.index != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
   990    public int getNumberOfStaticPages() {
   991      return staticPageIndex.getNumberOfStaticPages();
   992    }
   993  
   994    /**
   995     * Gets the most recent blog entries, the number
   996     * of which is specified.
   997     *
   998     * @param numberOfEntries the number of entries to get
   999     * @return a List containing the most recent blog entries
  1000     */
           /* 
    P/P     *  Method: List getRecentBlogEntries(int)
            * 
            *  Preconditions:
            *    this.blogEntryIndex != null
            *    init'ed(this.blogEntryIndex.indexEntries)
            * 
            *  Presumptions:
            *    org.apache.commons.logging.LogFactory:getLog(...)@105 != null
            * 
            *  Postconditions:
            *    return_value == &new ArrayList(getRecentBlogEntries#2)
            *    new ArrayList(getRecentBlogEntries#2) num objects == 1
            * 
            *  Test Vectors:
            *    java.util.Iterator:hasNext(...)@1005: {1}, {0}
            */
  1001    public List getRecentBlogEntries(int numberOfEntries) {
  1002      BlogService service = new BlogService();
  1003      List<String> blogEntryIds = blogEntryIndex.getBlogEntries();
  1004      List blogEntries = new ArrayList();
  1005      for (String blogEntryId : blogEntryIds) {
  1006        try {
  1007          BlogEntry blogEntry = service.getBlogEntry(this, blogEntryId);
  1008          blogEntries.add(blogEntry);
  1009        } catch (BlogServiceException e) {
  1010          log.error("Exception encountered", e);
  1011        }
  1012  
  1013        if (blogEntries.size() == numberOfEntries) {
  1014          break;
  1015        }
  1016      }
  1017  
  1018      return blogEntries;
  1019    }
  1020  
  1021    /**
  1022     * Gets the most recent published blog entries, the number of which
  1023     * is taken from the recentBlogEntriesOnHomePage property.
  1024     *
  1025     * @return a List containing the most recent blog entries
  1026     */
           /* 
    P/P     *  Method: List getRecentPublishedBlogEntries()
            * 
            *  Preconditions:
            *    this.blogEntryIndex != null
            *    init'ed(this.blogEntryIndex.publishedIndexEntries)
            *    this.properties != null
            * 
            *  Postconditions:
            *    return_value == &new ArrayList(getRecentPublishedBlogEntries#2*)
            *    new ArrayList(getRecentPublishedBlogEntries#2*) num objects == 1
            */
  1027    public List getRecentPublishedBlogEntries() {
  1028      return getRecentPublishedBlogEntries(getRecentBlogEntriesOnHomePage());
  1029    }
  1030  
  1031    /**
  1032     * Gets the most recent published blog entries, the number of which
  1033     * is specified
  1034     *
  1035     * @param number    the number of blog entries to get
  1036     * @return a List containing the most recent blog entries
  1037     */
           /* 
    P/P     *  Method: List getRecentPublishedBlogEntries(int)
            * 
            *  Preconditions:
            *    this.blogEntryIndex != null
            *    init'ed(this.blogEntryIndex.publishedIndexEntries)
            * 
            *  Presumptions:
            *    org.apache.commons.logging.LogFactory:getLog(...)@105 != null
            * 
            *  Postconditions:
            *    return_value == &new ArrayList(getRecentPublishedBlogEntries#2)
            *    new ArrayList(getRecentPublishedBlogEntries#2) num objects == 1
            * 
            *  Test Vectors:
            *    getBlogEntry(...)@1048: Addr_Set{null}, Inverse{null}
            *    java.util.Iterator:hasNext(...)@1042: {1}, {0}
            */
  1038    public List getRecentPublishedBlogEntries(int number) {
  1039      BlogService service = new BlogService();
  1040      List<String> blogEntryIds = blogEntryIndex.getPublishedBlogEntries();
  1041      List blogEntries = new ArrayList();
  1042      for (String blogEntryId : blogEntryIds) {
  1043        if (blogEntries.size() == number) {
  1044          break;
  1045        }
  1046  
  1047        try {
  1048          BlogEntry blogEntry = service.getBlogEntry(this, blogEntryId);
  1049          if (blogEntry != null) {
  1050            blogEntries.add(blogEntry);
  1051          }
  1052        } catch (BlogServiceException e) {
  1053          log.error("Exception encountered", e);
  1054        }
  1055      }
  1056  
  1057      return blogEntries;
  1058    }
  1059  
  1060    /**
  1061     * Gets blog entries for a given list of IDs.
  1062     *
  1063     * @param blogEntryIds    the list of blog entry IDs
  1064     * @return a List containing the blog entries
  1065     */
           /* 
    P/P     *  Method: List getBlogEntries(List)
            * 
            *  Preconditions:
            *    blogEntryIds != null
            * 
            *  Presumptions:
            *    org.apache.commons.logging.LogFactory:getLog(...)@105 != null
            * 
            *  Postconditions:
            *    return_value == &new LinkedList(getBlogEntries#2)
            *    new LinkedList(getBlogEntries#2) num objects == 1
            * 
            *  Test Vectors:
            *    getBlogEntry(...)@1071: Addr_Set{null}, Inverse{null}
            *    java.util.Iterator:hasNext(...)@1069: {1}, {0}
            */
  1066    public List<BlogEntry> getBlogEntries(List<String> blogEntryIds) {
  1067      BlogService service = new BlogService();
  1068      List<BlogEntry> blogEntries = new LinkedList<BlogEntry>();
  1069      for (String blogEntryId : blogEntryIds) {
  1070        try {
  1071          BlogEntry blogEntry = service.getBlogEntry(this, blogEntryId);
  1072          if (blogEntry != null) {
  1073            blogEntries.add(blogEntry);
  1074          }
  1075        } catch (BlogServiceException e) {
  1076          log.error("Exception encountered", e);
  1077        }
  1078      }
  1079  
  1080      return blogEntries;
  1081    }
  1082  
  1083    /**
  1084     * Gets the most recent published blog entries for a given category, the
  1085     * number of which is taken from the recentBlogEntriesOnHomePage property.
  1086     *
  1087     * @param   category          a category
  1088     * @return  a List containing the most recent blog entries
  1089     */
           /* 
    P/P     *  Method: List getRecentPublishedBlogEntries(Category)
            * 
            *  Preconditions:
            *    category != null
            *    init'ed(category.blogEntries)
            *    this.categoryIndex != null
            *    (soft) this.properties != null
            * 
            *  Presumptions:
            *    blogEntry.state@1096 != null
            *    net.sourceforge.pebble.domain.State__static_init.new State(State__static_init#5).name@1096 != null
            *    org.apache.commons.logging.LogFactory:getLog(...)@105 != null
            *    this.properties@1096 != null
            * 
            *  Postconditions:
            *    return_value == &new ArrayList(getRecentPublishedBlogEntries#2)
            *    new ArrayList(getRecentPublishedBlogEntries#2) num objects == 1
            * 
            *  Test Vectors:
            *    getBlogEntry(...)@1096: Addr_Set{null}, Inverse{null}
            *    java.util.Iterator:hasNext(...)@1094: {1}, {0}
            */
  1090    public List getRecentPublishedBlogEntries(Category category) {
  1091      BlogService service = new BlogService();
  1092      List<String> blogEntryIds = categoryIndex.getRecentBlogEntries(category);
  1093      List blogEntries = new ArrayList();
  1094      for (String blogEntryId : blogEntryIds) {
  1095        try {
  1096          BlogEntry blogEntry = service.getBlogEntry(this, blogEntryId);
  1097          if (blogEntry != null && blogEntry.isPublished()) {
  1098            blogEntries.add(blogEntry);
  1099          }
  1100        } catch (BlogServiceException e) {
  1101          log.error("Exception encountered", e);
  1102        }
  1103  
  1104        if (blogEntries.size() == getRecentBlogEntriesOnHomePage()) {
  1105          break;
  1106        }
  1107      }
  1108  
  1109      return blogEntries;
  1110    }
  1111  
  1112    /**
  1113     * Gets the most recent published blog entries for a given category, the
  1114     * number of which is taken from the recentBlogEntriesOnHomePage property.
  1115     *
  1116     * @param   author    the author's username
  1117     * @return  a List containing the most recent blog entries
  1118     */
           /* 
    P/P     *  Method: List getRecentPublishedBlogEntries(String)
            * 
            *  Preconditions:
            *    this.authorIndex != null
            *    this.authorIndex.authors != null
            *    (soft) this.properties != null
            * 
            *  Presumptions:
            *    blogEntry.state@1125 != null
            *    net.sourceforge.pebble.domain.State__static_init.new State(State__static_init#5).name@1125 != null
            *    org.apache.commons.logging.LogFactory:getLog(...)@105 != null
            *    this.properties@1125 != null
            * 
            *  Postconditions:
            *    return_value == &new ArrayList(getRecentPublishedBlogEntries#2)
            *    new ArrayList(getRecentPublishedBlogEntries#2) num objects == 1
            * 
            *  Test Vectors:
            *    getBlogEntry(...)@1125: Addr_Set{null}, Inverse{null}
            *    java.util.Iterator:hasNext(...)@1123: {1}, {0}
            */
  1119    public List getRecentPublishedBlogEntries(String author) {
  1120      BlogService service = new BlogService();
  1121      List<String> blogEntryIds = authorIndex.getRecentBlogEntries(author);
  1122      List blogEntries = new ArrayList();
  1123      for (String blogEntryId : blogEntryIds) {
  1124        try {
  1125          BlogEntry blogEntry = service.getBlogEntry(this, blogEntryId);
  1126          if (blogEntry != null && blogEntry.isPublished()) {
  1127            blogEntries.add(blogEntry);
  1128          }
  1129        } catch (BlogServiceException e) {
  1130          log.error("Exception encountered", e);
  1131        }
  1132  
  1133        if (blogEntries.size() == getRecentBlogEntriesOnHomePage()) {
  1134          break;
  1135        }
  1136      }
  1137  
  1138      return blogEntries;
  1139    }
  1140  
  1141    /**
  1142     * Gets the most recent published blog entries for a given tag, the
  1143     * number of which is taken from the recentBlogEntriesOnHomePage property.
  1144     *
  1145     * @param tag             a tag
  1146     * @return a List containing the most recent blog entries
  1147     */
           /* 
    P/P     *  Method: List getRecentPublishedBlogEntries(Tag)
            * 
            *  Preconditions:
            *    tag != null
            *    init'ed(tag.name)
            *    this.tagIndex != null
            *    this.tagIndex.tags != null
            *    (soft) this.properties != null
            *    (soft) init'ed(this.tagIndex.blog)
            * 
            *  Presumptions:
            *    blogEntry.state@1154 != null
            *    net.sourceforge.pebble.domain.State__static_init.new State(State__static_init#5).name@1154 != null
            *    org.apache.commons.logging.LogFactory:getLog(...)@105 != null
            *    this.properties@1154 != null
            * 
            *  Postconditions:
            *    return_value == &new ArrayList(getRecentPublishedBlogEntries#2)
            *    new ArrayList(getRecentPublishedBlogEntries#2) num objects == 1
            * 
            *  Test Vectors:
            *    getBlogEntry(...)@1154: Addr_Set{null}, Inverse{null}
            *    java.util.Iterator:hasNext(...)@1152: {1}, {0}
            */
  1148    public List getRecentPublishedBlogEntries(Tag tag) {
  1149      BlogService service = new BlogService();
  1150      List<String> blogEntryIds = tagIndex.getRecentBlogEntries(tag);
  1151      List blogEntries = new ArrayList();
  1152      for (String blogEntryId : blogEntryIds) {
  1153        try {
  1154          BlogEntry blogEntry = service.getBlogEntry(this, blogEntryId);
  1155          if (blogEntry != null && blogEntry.isPublished()) {
  1156            blogEntries.add(blogEntry);
  1157          }
  1158        } catch (BlogServiceException e) {
  1159          log.error("Exception encountered", e);
  1160        }
  1161  
  1162        if (blogEntries.size() == getRecentBlogEntriesOnHomePage()) {
  1163          break;
  1164        }
  1165      }
  1166  
  1167      return blogEntries;
  1168    }
  1169  
  1170    /**
  1171     * Gets the most recent responses.
  1172     *
  1173     * @return a List containing the most recent blog entries
  1174     */
           /* 
    P/P     *  Method: List getRecentApprovedResponses()
            * 
            *  Preconditions:
            *    this.responseIndex != null
            *    init'ed(this.responseIndex.approvedResponses)
            *    (soft) net.sourceforge.pebble.domain.State__static_init.new State(State__static_init#5).name != null
            *    (soft) this.properties != null
            * 
            *  Presumptions:
            *    getBlogEntry(...).state@1181 != null
            *    java.util.Iterator:next(...)@1179 != null
            *    org.apache.commons.logging.LogFactory:getLog(...)@105 != null
            *    response.blogEntry@1181 != null
            * 
            *  Postconditions:
            *    return_value == &new ArrayList(getRecentApprovedResponses#2)
            *    new ArrayList(getRecentApprovedResponses#2) num objects == 1
            * 
            *  Test Vectors:
            *    java.util.Iterator:hasNext(...)@1179: {1}, {0}
            */
  1175    public List<Response> getRecentApprovedResponses() {
  1176      BlogService service = new BlogService();
  1177      List<String> responseIds = responseIndex.getApprovedResponses();
  1178      List<Response> responses = new ArrayList<Response>();
  1179      for (String responseId : responseIds) {
  1180        try {
  1181          Response response = service.getResponse(this, responseId);
  1182          if (response != null && response.getBlogEntry().isPublished()) {
  1183            responses.add(response);
  1184          }
  1185        } catch (BlogServiceException e) {
  1186          log.error("Exception encountered", e);
  1187        }
  1188  
  1189        if (responses.size() == getRecentResponsesOnHomePage()) {
  1190          break;
  1191        }
  1192      }
  1193  
  1194      return responses;
  1195    }
  1196  
  1197    /**
  1198     * Gets the list of approved responses.
  1199     *
  1200     * @return  a List of response IDs
  1201     */
           /* 
    P/P     *  Method: List getApprovedResponses()
            * 
            *  Preconditions:
            *    this.responseIndex != null
            *    init'ed(this.responseIndex.approvedResponses)
            * 
            *  Postconditions:
            *    return_value == &new ArrayList(getApprovedResponses#1*)
            *    new ArrayList(getApprovedResponses#1*) num objects == 1
            */
  1202    public List<String> getApprovedResponses() {
  1203      return responseIndex.getApprovedResponses();
  1204    }
  1205  
  1206    /**
  1207     * Gets the list of pending responses.
  1208     *
  1209     * @return  a List of response IDs
  1210     */
           /* 
    P/P     *  Method: List getPendingResponses()
            * 
            *  Preconditions:
            *    this.responseIndex != null
            *    init'ed(this.responseIndex.pendingResponses)
            * 
            *  Postconditions:
            *    return_value == &new ArrayList(getPendingResponses#1*)
            *    new ArrayList(getPendingResponses#1*) num objects == 1
            */
  1211    public List<String> getPendingResponses() {
  1212      return responseIndex.getPendingResponses();
  1213    }
  1214  
  1215    /**
  1216     * Gets the list of rejected responses.
  1217     *
  1218     * @return  a List of response IDs
  1219     */
           /* 
    P/P     *  Method: List getRejectedResponses()
            * 
            *  Preconditions:
            *    this.responseIndex != null
            *    init'ed(this.responseIndex.rejectedResponses)
            * 
            *  Postconditions:
            *    return_value == &new ArrayList(getRejectedResponses#1*)
            *    new ArrayList(getRejectedResponses#1*) num objects == 1
            */
  1220    public List<String> getRejectedResponses() {
  1221      return responseIndex.getRejectedResponses();
  1222    }
  1223  
  1224    /**
  1225     * Gets the number of responses.
  1226     *
  1227     * @return the number of responses
  1228     */
           /* 
    P/P     *  Method: int getNumberOfResponses()
            * 
            *  Preconditions:
            *    this.responseIndex != null
            *    this.responseIndex.approvedResponses != null
            *    this.responseIndex.pendingResponses != null
            *    this.responseIndex.rejectedResponses != null
            * 
            *  Presumptions:
            *    java.util.List:size(...)@202 + java.util.List:size(...)@193 + java.util.List:size(...)@211 in -231..232-1
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
  1229    public int getNumberOfResponses() {
  1230      return responseIndex.getNumberOfResponses();
  1231    }
  1232  
  1233    /**
  1234     * Gets the number of approved responses.
  1235     *
  1236     * @return the number of approved responses
  1237     */
           /* 
    P/P     *  Method: int getNumberOfApprovedResponses()
            * 
            *  Preconditions:
            *    this.responseIndex != null
            *    this.responseIndex.approvedResponses != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
  1238    public int getNumberOfApprovedResponses() {
  1239      return responseIndex.getNumberOfApprovedResponses();
  1240    }
  1241  
  1242    /**
  1243     * Gets the number of pending responses.
  1244     *
  1245     * @return the number of pending responses
  1246     */
           /* 
    P/P     *  Method: int getNumberOfPendingResponses()
            * 
            *  Preconditions:
            *    this.responseIndex != null
            *    this.responseIndex.pendingResponses != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
  1247    public int getNumberOfPendingResponses() {
  1248      return responseIndex.getNumberOfPendingResponses();
  1249    }
  1250  
  1251    /**
  1252     * Gets the number of rejected responses.
  1253     *
  1254     * @return the number of rejected responses
  1255     */
           /* 
    P/P     *  Method: int getNumberOfRejectedResponses()
            * 
            *  Preconditions:
            *    this.responseIndex != null
            *    this.responseIndex.rejectedResponses != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
  1256    public int getNumberOfRejectedResponses() {
  1257      return responseIndex.getNumberOfRejectedResponses();
  1258    }
  1259  
  1260    /**
  1261     * Gets the date that this blog was last updated through the addition
  1262     * of a blog entry.
  1263     *
  1264     * @return  a Date instance representing the time of the most recent entry
  1265     */
           /* 
    P/P     *  Method: Date getLastModified()
            * 
            *  Preconditions:
            *    this.blogEntryIndex != null
            *    init'ed(this.blogEntryIndex.publishedIndexEntries)
            * 
            *  Presumptions:
            *    java.util.List:get(...)@1270 != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            *    new Date(getLastModified#1) num objects == 1
            * 
            *  Test Vectors:
            *    java.util.List:size(...)@1269: {-231..0, 2..232-1}, {1}
            */
  1266    public Date getLastModified() {
  1267      Date date = new Date(0);
  1268      List blogEntries = getRecentPublishedBlogEntries(1);
  1269      if (blogEntries.size() == 1) {
  1270        date = ((BlogEntry)blogEntries.get(0)).getDate();
  1271      }
  1272  
  1273      return date;
  1274    }
  1275  
  1276    /**
  1277     * Gets the date of the most recent response.
  1278     *
  1279     * @return  a Date instance representing the time of the most recent entry
  1280     */
           /* 
    P/P     *  Method: Date getDateOfLastResponse()
            * 
            *  Preconditions:
            *    this.responseIndex != null
            *    init'ed(this.responseIndex.approvedResponses)
            *    (soft) net.sourceforge.pebble.domain.State__static_init.new State(State__static_init#5).name != null
            *    (soft) this.properties != null
            * 
            *  Presumptions:
            *    java.util.List:get(...)@1284 != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            *    new Date(getDateOfLastResponse#1) num objects <= 1
            * 
            *  Test Vectors:
            *    java.util.List:size(...)@1283: {-231..0}, {1..232-1}
            */
  1281    public Date getDateOfLastResponse() {
  1282      List<Response> responses = this.getRecentApprovedResponses();
+ 1283      if (responses != null && responses.size() > 0) {
  1284        return responses.get(0).getDate();
  1285      } else {
  1286        return new Date(0);
  1287      }
  1288    }
  1289  
           /* 
    P/P     *  Method: BlogEntry getPreviousBlogEntry(BlogEntry)
            * 
            *  Preconditions:
            *    blogEntry != null
            *    init'ed(blogEntry.date)
            *    init'ed(blogEntry.id)
            *    init'ed(this.blogEntryIndex)
            *    (soft) init'ed(this.blogEntryIndex.indexEntries)
            *    (soft) this.properties != null
            *    (soft) this.years != null
            * 
            *  Presumptions:
            *    day.day <= day.month.dailyBlogs.length@1294 + 1
            *    day.month.dailyBlogs.length@1294 >= 1
            *    day.month.dailyBlogs@1294 != null
            *    day.month.month@1294 <= 13
            *    day.month.month@1294 <= day.month.year.months.length@1294 + 1
            *    ...
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
  1290    public BlogEntry getPreviousBlogEntry(BlogEntry blogEntry) {
+ 1291      Day firstDay = getBlogForFirstMonth().getBlogForFirstDay();
  1292      Day day = getBlogForDay(blogEntry.getDate());
  1293  
+ 1294      String blogEntryId = day.getPreviousBlogEntry(blogEntry.getId());
  1295      while (day != firstDay && blogEntryId == null) {
  1296        day = day.getPreviousDay();
+ 1297        blogEntryId = day.getLastBlogEntry();
  1298      }
  1299  
  1300      if (blogEntryId != null) {
  1301        BlogService service = new BlogService();
  1302        try {
  1303          BlogEntry previousBlogEntry = service.getBlogEntry(this, blogEntryId);
  1304          return previousBlogEntry;
  1305        } catch (BlogServiceException e) {
  1306          // do nothing
  1307        }
  1308      }
  1309  
  1310      return null;
  1311    }
  1312  
           /* 
    P/P     *  Method: BlogEntry getNextBlogEntry(BlogEntry)
            * 
            *  Preconditions:
            *    blogEntry != null
            *    init'ed(blogEntry.date)
            *    init'ed(blogEntry.id)
            *    this.properties != null
            *    this.years != null
            * 
            *  Presumptions:
            *    day.day < day.month.dailyBlogs.length@1317
            *    day.day - day.month.lastDayInMonth@1317 in -232+1..6_442_450_943
            *    day.month.dailyBlogs.length@1317 >= 1
            *    day.month.dailyBlogs@1317 != null
            *    day.month.month@1317 >= 0
            *    ...
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
  1313    public BlogEntry getNextBlogEntry(BlogEntry blogEntry) {
  1314      Day lastDay = getBlogForToday();
  1315      Day day = getBlogForDay(blogEntry.getDate());
  1316  
+ 1317      String blogEntryId = day.getNextBlogEntry(blogEntry.getId());
  1318      while (day != lastDay && blogEntryId == null) {
  1319        day = day.getNextDay();
+ 1320        blogEntryId = day.getFirstBlogEntry();
  1321      }
  1322  
  1323      if (blogEntryId != null) {
  1324        BlogService service = new BlogService();
  1325        try {
  1326          BlogEntry nextBlogEntry = service.getBlogEntry(this, blogEntryId);
  1327          return nextBlogEntry;
  1328        } catch (BlogServiceException e) {
  1329          // do nothing
  1330        }
  1331      }
  1332  
  1333      return null;
  1334    }
  1335  
  1336    /**
  1337     * Gets the categories associated with this blog.
  1338     *
  1339     * @return  a List of Category instances
  1340     */
           /* 
    P/P     *  Method: List getCategories()
            * 
            *  Preconditions:
            *    init'ed(this.rootCategory)
            * 
            *  Postconditions:
            *    return_value == &new ArrayList(getCategories#1*)
            *    init'ed(new ArrayList(Category#1) num objects)
            *    init'ed(new ArrayList(Category#2) num objects)
            *    init'ed(new ArrayList(Category#3) num objects)
            *    new ArrayList(getCategories#1*) num objects == 1
            *    init'ed(new Category(getCategory#2) num objects)
            *    possibly_updated(new Category(getCategory#2).blog)
            *    init'ed(new Category(getCategory#2).blogEntries)
            *    possibly_updated(new Category(getCategory#2).id)
            *    init'ed(new Category(getCategory#2).name)
            *    ...
            */
  1341    public List<Category> getCategories() {
  1342      CategoryBuilder builder = new CategoryBuilder(this, rootCategory);
  1343      return builder.getCategories();
  1344    }
  1345  
  1346    /**
  1347     * Gets a specific category.
  1348     *
  1349     * @return  a Category instance
  1350     */
           /* 
    P/P     *  Method: Category getCategory(String)
            * 
            *  Preconditions:
            *    init'ed(this.rootCategory)
            * 
            *  Postconditions:
            *    init'ed(return_value)
            *    init'ed(new ArrayList(Category#1) num objects)
            *    init'ed(new ArrayList(Category#2) num objects)
            *    init'ed(new ArrayList(Category#3) num objects)
            *    init'ed(new Category(getCategory#2*) num objects)
            *    possibly_updated(new Category(getCategory#2*).blog)
            *    init'ed(new Category(getCategory#2*).blogEntries)
            *    possibly_updated(new Category(getCategory#2*).id)
            *    init'ed(new Category(getCategory#2*).name)
            *    possibly_updated(new Category(getCategory#2*).parent)
            *    ...
            */
  1351    public Category getCategory(String id) {
  1352      CategoryBuilder builder = new CategoryBuilder(this, rootCategory);
  1353      return builder.getCategory(id);
  1354    }
  1355  
  1356    /**
  1357     * Gets the root category for this blog.
  1358     *
  1359     * @return  a Category instance
  1360     */
           /* 
    P/P     *  Method: Category getRootCategory()
            * 
            *  Preconditions:
            *    init'ed(this.rootCategory)
            * 
            *  Postconditions:
            *    return_value == this.rootCategory
            *    init'ed(return_value)
            */
  1361    public Category getRootCategory() {
  1362      return this.rootCategory;
  1363    }
  1364  
  1365    /**
  1366     * Sets the root category for this blog.
  1367     *
  1368     * @param category    a Category instance
  1369     */
           /* 
    P/P     *  Method: void setRootCategory(Category)
            * 
            *  Postconditions:
            *    this.rootCategory == category
            *    init'ed(this.rootCategory)
            */
  1370    public void setRootCategory(Category category) {
  1371      this.rootCategory = category;
  1372    }
  1373  
  1374    /**
  1375     * Adds a category.
  1376     *
  1377     * @param category    the Category to be added
  1378     */
           /* 
    P/P     *  Method: void addCategory(Category)
            * 
            *  Preconditions:
            *    category != null
            *    init'ed(this.rootCategory)
            *    (soft) category.id != null
            * 
            *  Postconditions:
            *    possibly_updated(category.blog)
            *    possibly_updated(category.parent)
            *    init'ed(new ArrayList(Category#1) num objects)
            *    init'ed(new ArrayList(Category#2) num objects)
            *    init'ed(new ArrayList(Category#3) num objects)
            *    init'ed(new Category(getCategory#2*) num objects)
            *    possibly_updated(new Category(getCategory#2*).blog)
            *    init'ed(new Category(getCategory#2*).blogEntries)
            *    possibly_updated(new Category(getCategory#2*).id)
            *    init'ed(new Category(getCategory#2*).name)
            *    ...
            */
  1379    public synchronized void addCategory(Category category) {
  1380      if (getCategory(category.getId()) == null) {
  1381        CategoryBuilder builder = new CategoryBuilder(this, rootCategory);
+ 1382        builder.addCategory(category);
  1383      }
  1384    }
  1385  
  1386    /**
  1387     * Removes a category.
  1388     *
  1389     * @param category    the Category to be removed
  1390     */
           /* 
    P/P     *  Method: void removeCategory(Category)
            * 
            *  Preconditions:
            *    category != null
            *    (soft) category.id != null
            *    (soft) init'ed(this.rootCategory)
            * 
            *  Postconditions:
            *    possibly_updated(category.parent)
            *    init'ed(new ArrayList(Category#1) num objects)
            *    init'ed(new ArrayList(Category#2) num objects)
            *    init'ed(new ArrayList(Category#3) num objects)
            *    init'ed(new Category(getCategory#2) num objects)
            *    possibly_updated(new Category(getCategory#2).blog)
            *    init'ed(new Category(getCategory#2).blogEntries)
            *    possibly_updated(new Category(getCategory#2).id)
            *    init'ed(new Category(getCategory#2).name)
            *    possibly_updated(new Category(getCategory#2).parent)
            *    ...
            */
  1391    public synchronized void removeCategory(Category category) {
  1392      if (getCategory(category.getId()) != null) {
  1393        CategoryBuilder builder = new CategoryBuilder(this, rootCategory);
+ 1394        builder.removeCategory(category);
  1395      }
  1396    }
  1397  
  1398    /**
  1399     * Gets the list of tags associated with this blog.
  1400     */
           /* 
    P/P     *  Method: List getTags()
            * 
            *  Preconditions:
            *    this.tagIndex != null
            *    init'ed(this.tagIndex.orderedTags)
            * 
            *  Postconditions:
            *    return_value == &new ArrayList(getTags#1*)
            *    new ArrayList(getTags#1*) num objects == 1
            */
  1401    public List<Tag> getTags() {
  1402      return tagIndex.getTags();
  1403    }
  1404  
  1405    /**
  1406     * Gets the tag with the specified name.
  1407     *
  1408     * @param name    the name as a String
  1409     * @return  a Tag instance
  1410     */
           /* 
    P/P     *  Method: Tag getTag(String)
            * 
            *  Postconditions:
            *    return_value == &new Tag(getTag#1)
            *    new Tag(getTag#1) num objects == 1
            *    return_value.blog == this
            *    return_value.blog != null
            *    return_value.name != null
            */
  1411    public Tag getTag(String name) {
  1412      return new Tag(name, this);
  1413    }
  1414  
  1415    /**
  1416     * Gets the object managing referer filters.
  1417     *
  1418     * @return  a RefererFilterManager instance
  1419     */
           /* 
    P/P     *  Method: RefererFilterManager getRefererFilterManager()
            * 
            *  Preconditions:
            *    init'ed(this.refererFilterManager)
            * 
            *  Postconditions:
            *    return_value == this.refererFilterManager
            *    init'ed(return_value)
            */
  1420    public RefererFilterManager getRefererFilterManager() {
  1421      return this.refererFilterManager;
  1422    }
  1423  
  1424    /**
  1425     * Gets the search index.
  1426     *
  1427     * @return  a BlogEntryIndex instance
  1428     */
           /* 
    P/P     *  Method: SearchIndex getSearchIndex()
            * 
            *  Preconditions:
            *    init'ed(this.searchIndex)
            * 
            *  Postconditions:
            *    return_value == this.searchIndex
            *    init'ed(return_value)
            */
  1429    public SearchIndex getSearchIndex() {
  1430      return this.searchIndex;
  1431    }
  1432  
  1433    /**
  1434     * Gets the blog entry index.
  1435     *
  1436     * @return  a BlogEntryIndex instance
  1437     */
           /* 
    P/P     *  Method: BlogEntryIndex getBlogEntryIndex()
            * 
            *  Preconditions:
            *    init'ed(this.blogEntryIndex)
            * 
            *  Postconditions:
            *    return_value == this.blogEntryIndex
            *    init'ed(return_value)
            */
  1438    public BlogEntryIndex getBlogEntryIndex() {
  1439      return this.blogEntryIndex;
  1440    }
  1441  
  1442    /**
  1443     * Gets the response index.
  1444     *
  1445     * @return  a ResponseIndex instance
  1446     */
           /* 
    P/P     *  Method: ResponseIndex getResponseIndex()
            * 
            *  Preconditions:
            *    init'ed(this.responseIndex)
            * 
            *  Postconditions:
            *    return_value == this.responseIndex
            *    init'ed(return_value)
            */
  1447    public ResponseIndex getResponseIndex() {
  1448      return this.responseIndex;
  1449    }
  1450  
  1451    /**
  1452     * Gets the tag index.
  1453     *
  1454     * @return  a TagIndex instance
  1455     */
           /* 
    P/P     *  Method: TagIndex getTagIndex()
            * 
            *  Preconditions:
            *    init'ed(this.tagIndex)
            * 
            *  Postconditions:
            *    return_value == this.tagIndex
            *    init'ed(return_value)
            */
  1456    public TagIndex getTagIndex() {
  1457      return this.tagIndex;
  1458    }
  1459  
  1460    /**
  1461     * Gets the category index.
  1462     *
  1463     * @return  a CategoryIndex instance
  1464     */
           /* 
    P/P     *  Method: CategoryIndex getCategoryIndex()
            * 
            *  Preconditions:
            *    init'ed(this.categoryIndex)
            * 
            *  Postconditions:
            *    return_value == this.categoryIndex
            *    init'ed(return_value)
            */
  1465    public CategoryIndex getCategoryIndex() {
  1466      return this.categoryIndex;
  1467    }
  1468  
  1469    /**
  1470     * Gets the author index.
  1471     *
  1472     * @return  a AuthorIndex instance
  1473     */
           /* 
    P/P     *  Method: AuthorIndex getAuthorIndex()
            * 
            *  Preconditions:
            *    init'ed(this.authorIndex)
            * 
            *  Postconditions:
            *    return_value == this.authorIndex
            *    init'ed(return_value)
            */
  1474    public AuthorIndex getAuthorIndex() {
  1475      return this.authorIndex;
  1476    }
  1477  
  1478    /**
  1479     * Gets the story index.
  1480     *
  1481     * @return  a StaticPageIndex instance
  1482     */
           /* 
    P/P     *  Method: StaticPageIndex getStaticPageIndex()
            * 
            *  Preconditions:
            *    init'ed(this.staticPageIndex)
            * 
            *  Postconditions:
            *    return_value == this.staticPageIndex
            *    init'ed(return_value)
            */
  1483    public StaticPageIndex getStaticPageIndex() {
  1484      return this.staticPageIndex;
  1485    }
  1486  
  1487    /**
  1488     * Logs this request for blog.
  1489     *
  1490     * @param request   the HttpServletRequest instance for this request
  1491     */
  1492    public synchronized void log(HttpServletRequest request, int status) {
             /* 
    P/P       *  Method: void log(HttpServletRequest, int)
              * 
              *  Preconditions:
              *    request != null
              *    (soft) this.logger != null
              * 
              *  Presumptions:
              *    javax.servlet.http.HttpServletRequest:getAttribute(...)@1493 != null
              * 
              *  Test Vectors:
              *    java.lang.String:matches(...)@1494: {1}, {0}
              */
  1493      String externalUri = (String)request.getAttribute(Constants.EXTERNAL_URI);
  1494      if (externalUri.matches("/images/.+")) {
  1495        // do nothing, we don't want to log the following types of requests
  1496        // - a blog's images
  1497      } else {
  1498        // log the request
  1499        logger.log(request, status);
  1500      }
  1501    }
  1502  
  1503    /**
  1504     * Gets an object representing the editable theme.
  1505     *
  1506     * @return    an EditableTheme instance
  1507     */
           /* 
    P/P     *  Method: Theme getEditableTheme()
            * 
            *  Preconditions:
            *    init'ed(this.editableTheme)
            * 
            *  Postconditions:
            *    return_value == this.editableTheme
            *    init'ed(return_value)
            */
  1508    public Theme getEditableTheme() {
  1509      return editableTheme;
  1510    }
  1511  
  1512    /**
  1513     * Sets an object representing the editable theme.
  1514     *
  1515     * @param editableTheme    an EditableTheme instance
  1516     */
           /* 
    P/P     *  Method: void setEditableTheme(Theme)
            * 
            *  Postconditions:
            *    this.editableTheme == editableTheme
            *    init'ed(this.editableTheme)
            */
  1517    public void setEditableTheme(Theme editableTheme) {
  1518      this.editableTheme = editableTheme;
  1519    }
  1520  
  1521    /**
  1522     * Gets the location where the blog files are stored.
  1523     *
  1524     * @return    an absolute, local path on the filing system
  1525     */
           /* 
    P/P     *  Method: String getFilesDirectory()
            * 
            *  Preconditions:
            *    init'ed(this.root)
            * 
            *  Presumptions:
            *    init'ed(java.io.File.separator)
            * 
            *  Postconditions:
            *    return_value != null
            */
  1526    public String getFilesDirectory() {
  1527      return getRoot() + File.separator + "files";
  1528    }
  1529  
  1530    /**
  1531     * Gets the location where the blog theme is stored.
  1532     *
  1533     * @return    an absolute, local path on the filing system
  1534     */
           /* 
    P/P     *  Method: String getThemeDirectory()
            * 
            *  Preconditions:
            *    init'ed(this.root)
            * 
            *  Presumptions:
            *    init'ed(java.io.File.separator)
            * 
            *  Postconditions:
            *    return_value != null
            */
  1535    public String getThemeDirectory() {
  1536      return getRoot() + File.separator + "theme";
  1537    }
  1538  
  1539    /**
  1540     * Gets the location where the plugin properties file is stored.
  1541     *
  1542     * @return    an absolute, local path on the filing system
  1543     */
           /* 
    P/P     *  Method: String getPluginPropertiesFile()
            * 
            *  Preconditions:
            *    init'ed(this.root)
            * 
            *  Presumptions:
            *    init'ed(java.io.File.separator)
            * 
            *  Postconditions:
            *    return_value != null
            */
  1544    public String getPluginPropertiesFile() {
  1545      return getRoot() + File.separator + "plugin.properties";
  1546    }
  1547  
  1548    /**
  1549     * Determines whether this blog is public.
  1550     *
  1551     * @return  true if public, false otherwise
  1552     */
           /* 
    P/P     *  Method: bool isPublic()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Presumptions:
            *    java.util.Properties:getProperty(...)@1554 != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
  1553    public boolean isPublic() {
  1554      return properties.getProperty(PRIVATE_KEY).equalsIgnoreCase(FALSE);
  1555    }
  1556  
  1557    /**
  1558     * Determines whether this blog is private.
  1559     *
  1560     * @return  true if public, false otherwise
  1561     */
           /* 
    P/P     *  Method: bool isPrivate()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Presumptions:
            *    java.util.Properties:getProperty(...)@1563 != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
  1562    public boolean isPrivate() {
  1563      return properties.getProperty(PRIVATE_KEY).equalsIgnoreCase(TRUE);
  1564    }
  1565  
  1566    /**
  1567     * Called to start (i.e. activate/initialise, restore the theme, etc) this
  1568     * blog.
  1569     */
           /* 
    P/P     *  Method: void start()
            * 
            *  Preconditions:
            *    net/sourceforge/pebble/domain/Theme.log != null
            *    this.editableTheme != null
            *    this.editableTheme.blog != null
            *    init'ed(this.editableTheme.name)
            *    init'ed(this.editableTheme.pathToLiveThemes)
            *    this.eventDispatcher != null
            *    this.eventDispatcher.eventListenerList != null
            *    this.eventDispatcher.eventListenerList.blogListeners != null
            *    init'ed(this.id)
            *    this.logger != null
            *    ...
            * 
            *  Presumptions:
            *    org.apache.commons.logging.LogFactory:getLog(...)@105 != null
            * 
            *  Postconditions:
            *    possibly_updated(this.messages)
            *    possibly_updated(this.staticPageIndex.index)
            *    possibly_updated(this.staticPageIndex.lockAttempts)
            *    new HashMap(reindex#1) num objects <= 1
            * 
            *  Test Vectors:
            *    java.io.File:exists(...)@1575: {1}, {0}
            *    java.io.File:exists(...)@1581: {1}, {0}
            *    java.io.File:exists(...)@1586: {1}, {0}
            *    java.io.File:exists(...)@1591: {1}, {0}
            */
  1570    void start() {
  1571      log.debug("Starting blog with ID " + getId());
  1572  
  1573      // reindex the blog if the indexes don't exist
  1574      File indexes = new File(getIndexesDirectory());
  1575      if (!indexes.exists()) {
  1576        indexes.mkdir();
  1577        reindex();
  1578      }
  1579  
  1580      File imagesDirectory = new File(getImagesDirectory());
  1581      if (!imagesDirectory.exists()) {
  1582        imagesDirectory.mkdir();
  1583      }
  1584  
  1585      File filesDirectory = new File(getFilesDirectory());
  1586      if (!filesDirectory.exists()) {
  1587        filesDirectory.mkdir();
  1588      }
  1589  
  1590      File logDirectory = new File(getLogsDirectory());
  1591      if (!logDirectory.exists()) {
  1592        logDirectory.mkdir();
  1593      }
  1594  
  1595      logger.start();
  1596      editableTheme.restore();
  1597  
  1598      // call blog listeners
  1599      eventDispatcher.fireBlogEvent(new BlogEvent(this, BlogEvent.BLOG_STARTED));
  1600      log.info("Started blog with ID " + getId());
  1601    }
  1602  
  1603    /**
  1604     * Called to shutdown this blog.
  1605     */
           /* 
    P/P     *  Method: void stop()
            * 
            *  Preconditions:
            *    net/sourceforge/pebble/domain/Theme.log != null
            *    this.editableTheme != null
            *    this.editableTheme.blog != null
            *    init'ed(this.editableTheme.name)
            *    init'ed(this.editableTheme.pathToLiveThemes)
            *    this.eventDispatcher != null
            *    this.eventDispatcher.eventListenerList != null
            *    this.eventDispatcher.eventListenerList.blogListeners != null
            *    init'ed(this.id)
            *    this.logger != null
            * 
            *  Presumptions:
            *    org.apache.commons.logging.LogFactory:getLog(...)@105 != null
            */
  1606    void stop() {
  1607      log.debug("Stopping blog with ID " + getId());
  1608  
  1609      logger.stop();
  1610      editableTheme.backup();
  1611  
  1612      // call blog listeners
  1613      eventDispatcher.fireBlogEvent(new BlogEvent(this, BlogEvent.BLOG_STOPPED));
  1614      log.info("Stopped blog with ID " + getId());
  1615    }
  1616  
  1617    /**
  1618     * Gets the logger associated with this blog.
  1619     *
  1620     * @return    an AbstractLogger implementation
  1621     */
           /* 
    P/P     *  Method: AbstractLogger getLogger()
            * 
            *  Preconditions:
            *    init'ed(this.logger)
            * 
            *  Postconditions:
            *    return_value == this.logger
            *    init'ed(return_value)
            */
  1622    public AbstractLogger getLogger() {
  1623      return this.logger;
  1624    }
  1625  
  1626    /**
  1627     * Gets the list of plugins.
  1628     *
  1629     * @return  a comma separated list of class names
  1630     */
           /* 
    P/P     *  Method: List getContentDecorators()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Postconditions:
            *    return_value == &new ArrayList(getStringsFromProperty#1)
            *    new ArrayList(getStringsFromProperty#1) num objects == 1
            */
  1631    public List<String> getContentDecorators() {
  1632      return getStringsFromProperty(CONTENT_DECORATORS_KEY);
  1633    }
  1634  
  1635    /**
  1636     * Gets the decorator manager associated with this blog.
  1637     *
  1638     * @return  a BlogEntryDecoratorManager instance
  1639     */
           /* 
    P/P     *  Method: ContentDecoratorChain getContentDecoratorChain()
            * 
            *  Preconditions:
            *    init'ed(this.decoratorChain)
            * 
            *  Postconditions:
            *    return_value == this.decoratorChain
            *    init'ed(return_value)
            */
  1640    public ContentDecoratorChain getContentDecoratorChain() {
  1641      return this.decoratorChain;
  1642    }
  1643  
  1644    /**
  1645     * Gets the list of blog listeners as strings.
  1646     *
  1647     * @return  The list of class names
  1648     */
           /* 
    P/P     *  Method: List getBlogListeners()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Postconditions:
            *    return_value == &new ArrayList(getStringsFromProperty#1)
            *    new ArrayList(getStringsFromProperty#1) num objects == 1
            */
  1649    public List<String> getBlogListeners() {
  1650      return getStringsFromProperty(BLOG_LISTENERS_KEY);
  1651    }
  1652  
  1653    /**
  1654     * Gets the list of blog entry listeners as strings.
  1655     *
  1656     * @return  The list of class names
  1657     */
           /* 
    P/P     *  Method: List getBlogEntryListeners()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Postconditions:
            *    return_value == &new ArrayList(getStringsFromProperty#1)
            *    new ArrayList(getStringsFromProperty#1) num objects == 1
            */
  1658    public List<String> getBlogEntryListeners() {
  1659      return getStringsFromProperty(BLOG_ENTRY_LISTENERS_KEY);
  1660    }
  1661  
  1662    /**
  1663     * Gets the list of comment listeners as strings.
  1664     *
  1665     * @return  The list of class names
  1666     */
           /* 
    P/P     *  Method: List getCommentListeners()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Postconditions:
            *    return_value == &new ArrayList(getStringsFromProperty#1)
            *    new ArrayList(getStringsFromProperty#1) num objects == 1
            */
  1667    public List<String> getCommentListeners() {
  1668      return getStringsFromProperty(COMMENT_LISTENERS_KEY);
  1669    }
  1670  
  1671    /**
  1672     * Gets the list of TrackBack listeners as strings.
  1673     *
  1674     * @return  The list of class names
  1675     */
           /* 
    P/P     *  Method: List getTrackBackListeners()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Postconditions:
            *    return_value == &new ArrayList(getStringsFromProperty#1)
            *    new ArrayList(getStringsFromProperty#1) num objects == 1
            */
  1676    public List<String> getTrackBackListeners() {
  1677      return getStringsFromProperty(TRACKBACK_LISTENERS_KEY);
  1678    }
  1679  
  1680    /**
  1681     * Gets the name the event dispatcher.
  1682     *
  1683     * @return  a String
  1684     */
           /* 
    P/P     *  Method: String getEventDispatcherName()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
  1685    public String getEventDispatcherName() {
  1686      return getProperty(EVENT_DISPATCHER_KEY);
  1687    }
  1688  
  1689    /**
  1690     * Gets the event dispatcher in use.
  1691     *
  1692     * @return  an EventDispatcher implementation
  1693     */
           /* 
    P/P     *  Method: EventDispatcher getEventDispatcher()
            * 
            *  Preconditions:
            *    init'ed(this.eventDispatcher)
            * 
            *  Postconditions:
            *    return_value == this.eventDispatcher
            *    init'ed(return_value)
            */
  1694    public EventDispatcher getEventDispatcher() {
  1695      return this.eventDispatcher;
  1696    }
  1697  
  1698    /**
  1699     * Gets the event listsner list.
  1700     */
           /* 
    P/P     *  Method: EventListenerList getEventListenerList()
            * 
            *  Preconditions:
            *    init'ed(this.eventListenerList)
            * 
            *  Postconditions:
            *    return_value == this.eventListenerList
            *    init'ed(return_value)
            */
  1701    public EventListenerList getEventListenerList() {
  1702      return this.eventListenerList;
  1703    }
  1704  
           /* 
    P/P     *  Method: PluginProperties getPluginProperties()
            * 
            *  Preconditions:
            *    init'ed(this.pluginProperties)
            * 
            *  Postconditions:
            *    return_value == this.pluginProperties
            *    init'ed(return_value)
            */
  1705    public PluginProperties getPluginProperties() {
  1706      return this.pluginProperties;
  1707    }
  1708  
  1709    /**
  1710     * Gets the name of the permalink provider.
  1711     *
  1712     * @return    the fully qualified class name of the permalink provider
  1713     */
           /* 
    P/P     *  Method: String getPermalinkProviderName()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
  1714    public String getPermalinkProviderName() {
  1715      return properties.getProperty(PERMALINK_PROVIDER_KEY);
  1716    }
  1717  
  1718    /**
  1719     * Gets the permalink provider in use.
  1720     *
  1721     * @return  a PermalinkProvider instance
  1722     */
           /* 
    P/P     *  Method: PermalinkProvider getPermalinkProvider()
            * 
            *  Preconditions:
            *    init'ed(this.permalinkProvider)
            * 
            *  Postconditions:
            *    return_value == this.permalinkProvider
            *    init'ed(return_value)
            */
  1723    public PermalinkProvider getPermalinkProvider() {
  1724      return this.permalinkProvider;
  1725    }
  1726  
  1727    /**
  1728     * Sets the permalink provider in use.
  1729     *
  1730     * @param provider    PermalinkProvider instance
  1731     */
           /* 
    P/P     *  Method: void setPermalinkProvider(PermalinkProvider)
            * 
            *  Preconditions:
            *    provider != null
            * 
            *  Postconditions:
            *    this.permalinkProvider == provider
            *    this.permalinkProvider != null
            */
  1732    public void setPermalinkProvider(PermalinkProvider provider) {
  1733      this.permalinkProvider = provider;
  1734      this.permalinkProvider.setBlog(this);
  1735    }
  1736  
           /* 
    P/P     *  Method: void reindex()
            * 
            *  Preconditions:
            *    init'ed(this.id)
            * 
            *  Presumptions:
            *    net/sourceforge/pebble/dao/DAOFactory.configuredFactory.staticPageDAO@1740 != null
            *    net/sourceforge/pebble/dao/DAOFactory.configuredFactory@1740 != null
            *    net/sourceforge/pebble/domain/AbstractBlog.log@1740 != null
            *    net/sourceforge/pebble/domain/BlogManager.instance@1740 != null
            *    org.apache.commons.logging.LogFactory:getLog(...)@105 != null
            *    ...
            * 
            *  Postconditions:
            *    init'ed(this.messages)
            *    possibly_updated(this.staticPageIndex.index)
            *    possibly_updated(this.staticPageIndex.lockAttempts)
            *    new HashMap(reindex#1) num objects <= 1
            */
  1737    public void reindex() {
  1738      log.info("Reindexing blog with ID " + getId());
  1739  
  1740      reindexBlogEntries();
  1741      reindexStaticPages();
  1742    }
  1743  
           /* 
    P/P     *  Method: void reindexBlogEntries()
            * 
            *  Preconditions:
            *    net/sourceforge/pebble/domain/AbstractBlog.log != null
            *    this.messages != null
            *    this.authorIndex != null
            *    this.blogEntryIndex != null
            *    this.categoryIndex != null
            *    this.categoryIndex.blog != null
            *    this.responseIndex != null
            *    this.searchIndex != null
            *    this.searchIndex.blog != null
            *    this.tagIndex != null
            *    ...
            * 
            *  Presumptions:
            *    java.lang.Object:getClass(...)@1763 != null
            *    org.apache.commons.logging.LogFactory:getLog(...)@105 != null
            * 
            *  Postconditions:
            *    this.authorIndex.authors == &new HashMap(clear#1)
            *    this.blogEntryIndex.indexEntries == &new ArrayList(clear#1)
            *    this.blogEntryIndex.publishedIndexEntries == &new ArrayList(clear#2)
            *    this.blogEntryIndex.unpublishedIndexEntries == &new ArrayList(clear#3)
            *    init'ed(this.messages)
            *    this.responseIndex.approvedResponses == &new ArrayList(clear#1)
            *    this.responseIndex.pendingResponses == &new ArrayList(clear#2)
            *    this.responseIndex.rejectedResponses == &new ArrayList(clear#3)
            *    this.tagIndex.orderedTags == One-of{old this.tagIndex.orderedTags, &new ArrayList(recalculateTagRankings#2)}
            *    this.tagIndex.tags == &new HashMap(clear#1)
            *    ...
            */
  1744    public void reindexBlogEntries() {
  1745      blogEntryIndex.clear();
  1746      responseIndex.clear();
  1747      tagIndex.clear();
  1748      categoryIndex.clear();
  1749      authorIndex.clear();
  1750      searchIndex.clear();
  1751  
  1752      try {
  1753        // to reindex all blog entries, we need to load them via the DAO
  1754        Collection<BlogEntry> blogEntries = DAOFactory.getConfiguredFactory().getBlogEntryDAO().loadBlogEntries(this);
  1755        blogEntryIndex.index(blogEntries);
  1756        responseIndex.index(blogEntries);
  1757        tagIndex.index(blogEntries);
  1758        categoryIndex.index(blogEntries);
  1759        authorIndex.index(blogEntries);
  1760        searchIndex.indexBlogEntries(blogEntries);
  1761        info("Blog entries reindexed.");
  1762      } catch (Exception e) {
  1763        error(e.getClass().getName() + " reindexing blog entries - " + StringUtils.transformHTML(e.getMessage()));
  1764        log.error("Error reindexing blog entries", e);
  1765      }
  1766    }
  1767  
           /* 
    P/P     *  Method: void reindexStaticPages()
            * 
            *  Preconditions:
            *    net/sourceforge/pebble/domain/AbstractBlog.log != null
            *    this.messages != null
            *    (soft) net/sourceforge/pebble/dao/DAOFactory.configuredFactory != null
            *    (soft) net/sourceforge/pebble/dao/DAOFactory.configuredFactory.staticPageDAO != null
            *    (soft) net/sourceforge/pebble/domain/BlogManager.instance != null
            *    (soft) init'ed(net/sourceforge/pebble/domain/BlogManager.instance.multiBlog)
            *    (soft) this.staticPageIndex.lockAttempts <= 232-2
            *    (soft) this...properties != null
            *    (soft) this.searchIndex != null
            *    (soft) this.searchIndex.blog != null
            *    ...
            * 
            *  Presumptions:
            *    java.lang.Object:getClass(...)@1776 != null
            *    org.apache.commons.logging.LogFactory:getLog(...)@105 != null
            * 
            *  Postconditions:
            *    init'ed(this.messages)
            *    this.staticPageIndex.index == One-of{old this.staticPageIndex.index, &new HashMap(reindex#1)}
            *    this.staticPageIndex.lockAttempts == One-of{old this.staticPageIndex.lockAttempts, old this.staticPageIndex.lockAttempts + 1, 0}
            *    init'ed(this.staticPageIndex.lockAttempts)
            *    new HashMap(reindex#1) num objects <= 1
            */
  1768    public void reindexStaticPages() {
  1769      try {
  1770        // to reindex all static pages, we need to load them via the DAO
  1771        Collection<StaticPage> staticPages = DAOFactory.getConfiguredFactory().getStaticPageDAO().loadStaticPages(this);
  1772        staticPageIndex.reindex(staticPages);
  1773        searchIndex.indexStaticPages(staticPages);
  1774        info("Static pages reindexed.");
  1775      } catch (Exception e) {
  1776        error(e.getClass().getName() + " reindexing static pages - " + StringUtils.transformHTML(e.getMessage()));
  1777        log.error("Error reindexing static pages", e);
  1778      }
  1779    }
  1780  
  1781    /**
  1782     * Indicates whether some other object is "equal to" this one.
  1783     *
  1784     * @param o   the reference object with which to compare.
  1785     * @return <code>true</code> if this object is the same as the obj
  1786     *         argument; <code>false</code> otherwise.
  1787     * @see #hashCode()
  1788     * @see java.util.Hashtable
  1789     */
           /* 
    P/P     *  Method: bool equals(Object)
            * 
            *  Preconditions:
            *    (soft) init'ed(o.id)
            *    (soft) this.id != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            * 
            *  Test Vectors:
            *    this == o: {0}, {1}
            */
  1790    public boolean equals(Object o) {
  1791      if (this == o) {
  1792        return true;
  1793      }
  1794  
  1795      if (!(o instanceof Blog)) {
  1796        return false;
  1797      }
  1798  
  1799      Blog blog = (Blog)o;
  1800      return getId().equals(blog.getId());
  1801    }
  1802  
  1803  
           /* 
    P/P     *  Method: String getCommentConfirmationStrategyName()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
  1804    public String getCommentConfirmationStrategyName() {
  1805      return getProperty(COMMENT_CONFIRMATION_STRATEGY_KEY);
  1806    }
  1807  
           /* 
    P/P     *  Method: CommentConfirmationStrategy getCommentConfirmationStrategy()
            * 
            *  Preconditions:
            *    init'ed(this.commentConfirmationStrategy)
            * 
            *  Postconditions:
            *    return_value == this.commentConfirmationStrategy
            *    init'ed(return_value)
            */
  1808    public CommentConfirmationStrategy getCommentConfirmationStrategy() {
  1809      return commentConfirmationStrategy;
  1810    }
  1811  
           /* 
    P/P     *  Method: String getTrackBackConfirmationStrategyName()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            */
  1812    public String getTrackBackConfirmationStrategyName() {
  1813      return getProperty(TRACKBACK_CONFIRMATION_STRATEGY_KEY);
  1814    }
  1815  
           /* 
    P/P     *  Method: TrackBackConfirmationStrategy getTrackBackConfirmationStrategy()
            * 
            *  Preconditions:
            *    init'ed(this.trackBackConfirmationStrategy)
            * 
            *  Postconditions:
            *    return_value == this.trackBackConfirmationStrategy
            *    init'ed(return_value)
            */
  1816    public TrackBackConfirmationStrategy getTrackBackConfirmationStrategy() {
  1817      return trackBackConfirmationStrategy;
  1818    }
  1819  
           /* 
    P/P     *  Method: bool isRichTextEditorForCommentsEnabled()
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            * 
            *  Test Vectors:
            *    java.lang.String:equalsIgnoreCase(...)@1822: {0}, {1}
            *    java.util.Properties:getProperty(...)@175: Addr_Set{null}, Inverse{null}
            */
  1820    public boolean isRichTextEditorForCommentsEnabled() {
  1821      String s = getProperty(RICH_TEXT_EDITOR_FOR_COMMENTS_ENABLED_KEY);
  1822      return s != null && s.equalsIgnoreCase("true");
  1823    }
  1824  
           /* 
    P/P     *  Method: EmailSubscriptionList getEmailSubscriptionList()
            * 
            *  Preconditions:
            *    init'ed(this.emailSubscriptionList)
            * 
            *  Postconditions:
            *    return_value == this.emailSubscriptionList
            *    init'ed(return_value)
            */
  1825    public EmailSubscriptionList getEmailSubscriptionList() {
  1826      return emailSubscriptionList;
  1827    }
  1828  
           /* 
    P/P     *  Method: List getNewsFeedEntries()
            * 
            *  Preconditions:
            *    init'ed(this.id)
            * 
            *  Postconditions:
            *    return_value != null
            *    new LinkedList(getNewsFeedEntries#1*) num objects <= 1
            */
  1829    public List<NewsFeedEntry> getNewsFeedEntries() {
  1830      return NewsFeedCache.getInstance().getNewsFeedEntries(this);
  1831    }
  1832  
           /* 
    P/P     *  Method: List getRecentNewsFeedEntries()
            * 
            *  Preconditions:
            *    init'ed(this.id)
            *    this.properties != null
            * 
            *  Postconditions:
            *    init'ed(return_value)
            *    new LinkedList(getNewsFeedEntries#1*) num objects <= 1
            */
  1833    public List<NewsFeedEntry> getRecentNewsFeedEntries() {
  1834      List<NewsFeedEntry> entries = getNewsFeedEntries();
  1835      if (entries.size() > getRecentBlogEntriesOnHomePage()) {
  1836        entries = entries.subList(0, getRecentBlogEntriesOnHomePage());
  1837      }
  1838      return entries;
  1839    }
  1840  
           /* 
    P/P     *  Method: List getStringsFromProperty(String)
            * 
            *  Preconditions:
            *    this.properties != null
            * 
            *  Presumptions:
            *    values.length@1845 <= 232-1
            *    values[i]@1845 != null
            * 
            *  Postconditions:
            *    return_value == &new ArrayList(getStringsFromProperty#1)
            *    new ArrayList(getStringsFromProperty#1) num objects == 1
            * 
            *  Test Vectors:
            *    java.lang.String:length(...)@1844: {0}, {1..232-1}
            *    java.lang.String:startsWith(...)@1847: {1}, {0}
            *    java.util.Properties:getProperty(...)@175: Addr_Set{null}, Inverse{null}
            */
  1841    private List<String> getStringsFromProperty(String key) {
  1842      List<String> strings = new ArrayList<String>();
  1843      String value = getProperty(key);
  1844      if (value != null && value.length() > 0) {
  1845        String values[] = value.split("\\s+");
  1846        for (int i = 0; i < values.length; i++) {
  1847          if (!values[i].startsWith("#")) {
  1848            strings.add(values[i].trim());
  1849          }
  1850        }
  1851      }
  1852      return strings;
  1853    }
  1854  }








SofCheck Inspector Build Version : 2.22510
blog.java 2010-Jun-25 19:40:32
blog.class 2010-Jul-19 20:23:40