blog.java

### 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