Package com.salas.bb.core

Source Code of com.salas.bb.core.GlobalModel$FireFeedUpdated

// BlogBridge -- RSS feed reader, manager, and web based service
// Copyright (C) 2002-2006 by R. Pito Salas
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software Foundation;
// either version 2 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with this program;
// if not, write to the Free Software Foundation, Inc., 59 Temple Place,
// Suite 330, Boston, MA 02111-1307 USA
//
// Contact: R. Pito Salas
// mailto:pitosalas@users.sourceforge.net
// More information: about BlogBridge
// http://www.blogbridge.com
// http://sourceforge.net/projects/blogbridge
//
// $Id: GlobalModel.java,v 1.140 2008/04/08 08:06:18 spyromus Exp $
//

package com.salas.bb.core;

import com.salas.bb.domain.*;
import com.salas.bb.domain.events.FeedRemovedEvent;
import com.salas.bb.domain.prefs.StarzPreferences;
import com.salas.bb.domain.prefs.UserPreferences;
import com.salas.bb.domain.utils.DomainAdapter;
import com.salas.bb.domain.utils.DomainEventsListener;
import com.salas.bb.service.ServicePreferences;
import com.salas.bb.utils.dnd.DNDListContext;
import com.salas.bb.views.settings.DefaultFRS;
import com.salas.bb.views.settings.FeedRenderingSettings;
import com.salas.bb.views.settings.RenderingManager;

import javax.swing.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Date;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.Preferences;

/**
* Root of the model of the whole application. The key state that we keep are the various channel
* guides, which may be displayed in the main window. This is a singleton object. N.B. This class is
* persisted as XML using XMLEncoder. XMLEncoder's default behavior is that it will write out all
* bean properties. Therefore the part of this object's state which is available as a Bean Propertyu
* is exactly what will be written out. This is subtle so be careful if you play with getXXX and
* setXXX methods which is how by default you can tell what's a bean property and what is not.
*/
public class GlobalModel
{
    private static final Logger LOG = Logger.getLogger(GlobalModel.class.getName());

    /**
     * Return the one and only GlobalModel. Tricky: during the writing out of the state on disk a
     * copy of GlobalModel is temporarily created
     */
    public static GlobalModel               SINGLETON;

    private final UserPreferences           userPreferences;
    private final ServicePreferences        servicePreferences;
    private final StarzPreferences          starzPreferences;

    private final FeedRenderingSettings     globalRenderingSettings;

    private GuidesSet                       guidesSet;

    private IGuide                          selectedGuide;
    private IFeed                           selectedFeed;
    private IArticle                        selectedArticle;
    private IArticle[]                      selectedArticles;

    private final GuideModel                guideModel;
   
    /** A model for supporting computation of visible feeds. */
    private GuideModel                      vfHelperModel;

    private boolean                         blockChannelUpdated;

    // We use this info block to record the fact of selecteed feed change not passing feed change
    // event to the model. Doing this we avoid refiltering and resorting basing on new feed
    // state, which may result in change of feed position or visibility. These changes break
    // the human-expected navigation path and should be avoided while feed is selected. Later,
    // when user will select another feed this feed will be reported as changed to the model.
    private final FeedChangeInfo            selectedFeedChangeInfo;

    /** Listener of all domain events. */
    private final DomainListener            domainListener;

    /**
     * Holds the mapping between guide (key) and feed object.
     */
    private Map<IGuide, IFeed> selectedFeeds;

    /** View mode value model. */
    private ViewModeValueModel              viewModeValueModel;
    /** View type value model. */
    private ViewTypeValueModel              viewTypeValueModel;

    /**
     * Construct the global state. After construction, the GlobalModel has an empty ChannelGuideSet.
     * Note that this constructor is called either directly (new, when the state is not coming from
     * the persistent state) or via the XMLDecoder call in GlobalController.UnPersistAsXML which
     * instantiates a GlobalModel from its persistent state. In either case the GlobalModel.INSTANCE
     * static contains a reference to the GlobalModel object.
     *
     * @param scoresCalculator scores calculator to use.
     */
    public GlobalModel(ScoresCalculator scoresCalculator)
    {
        this(scoresCalculator, true);
    }

    /**
     * Construct the global state. After construction, the GlobalModel has an empty ChannelGuideSet.
     * Note that this constructor is called either directly (new, when the state is not coming from
     * the persistent state) or via the XMLDecoder call in GlobalController.UnPersistAsXML which
     * instantiates a GlobalModel from its persistent state. In either case the GlobalModel.INSTANCE
     * static contains a reference to the GlobalModel object.
     *
     * @param scoresCalculator  scores calculator to use.
     * @param full              TRUE to perform full initialization. FALSE to create only
     *                          this model object.
     */
    public GlobalModel(ScoresCalculator scoresCalculator, boolean full)
    {
        userPreferences = new UserPreferences();
        servicePreferences = new ServicePreferences();
        starzPreferences = new StarzPreferences();
        globalRenderingSettings = new FeedRenderingSettings();
        globalRenderingSettings.setParent(new DefaultFRS());

        if (full)
        {
            domainListener = new DomainListener();

            guideModel = new GuideModel(scoresCalculator, FeedDisplayModeManager.getInstance());
            vfHelperModel = new GuideModel(scoresCalculator, FeedDisplayModeManager.getInstance());

            RenderingManager.setGlobalSettings(globalRenderingSettings);

            selectedFeedChangeInfo = new FeedChangeInfo();
            selectedFeeds = new IdentityHashMap<IGuide, IFeed>();

            // When some property affecting visibility changes we
            // mark the selected feed info as changed to update its cell
            // when it gets unselected. It can potentially become invisible.
            userPreferences.addPropertyChangeListener(new PropertyChangeListener()
            {
                public void propertyChange(PropertyChangeEvent evt)
                {
                    if (UserPreferences.FEED_VISIBILITY_PROPERTIES.contains(evt.getPropertyName()))
                    {
                        selectedFeedChangeInfo.registerChanged();
                    }
                }
            });
        } else
        {
            selectedFeedChangeInfo = null;
            guideModel = null;
            domainListener = null;
        }

        viewModeValueModel = new ViewModeValueModel(globalRenderingSettings);
        viewTypeValueModel = new ViewTypeValueModel();

        setGuidesSet(new GuidesSet());

        selectedGuide = null;
        blockChannelUpdated = false;
    }

    /**
     * Returns the view mode value model.
     *
     * @return view mode value mode.
     */
    public ViewModeValueModel getViewModeValueModel()
    {
        return viewModeValueModel;
    }

    /**
     * Returns the view type value model.
     *
     * @return view type value mode.
     */
    public ViewTypeValueModel getViewTypeValueModel()
    {
        return viewTypeValueModel;
    }

    /**
     * Returns current highlight calculator to be used.
     *
     * @return highlights calculator.
     */
    public HighlightsCalculator getHighlightsCalculator()
    {
        return GlobalController.SINGLETON.getHighlightsCalculator();
    }

    /**
     * Returns calculator of channel score.
     *
     * @return calculator.
     */
    public ScoresCalculator getScoreCalculator()
    {
        return GlobalController.SINGLETON.getScoreCalculator();
    }

    /**
     * Returns channels model.
     *
     * @return model.
     */
    public GuideModel getGuideModel()
    {
        return guideModel;
    }

    /**
     * Returns visible feeds helper mode.
     *
     * @param aGuide target guide.
     *
     * @return model.
     */
    private GuideModel getVFHelperModel(IGuide aGuide)
    {
        int starz = userPreferences.getGoodChannelStarz() - 1;
        int prSortOrder = userPreferences.getSortByClass1();
        int scSortOrder = userPreferences.getSortByClass2();
        boolean isPrSortOrderReversed = userPreferences.isReversedSortByClass1();
        boolean isScSortOrderReversed = userPreferences.isReversedSortByClass2();
        boolean isSortingEnabled = userPreferences.isSortingEnabled();

        vfHelperModel.setOptions(starz, prSortOrder, isPrSortOrderReversed,
            scSortOrder, isScSortOrderReversed, isSortingEnabled);
        vfHelperModel.setGuide(aGuide);

        return vfHelperModel;
    }

    /**
     * Returns <code>TRUE</code> if a feed is currently visible on the screen (in the selected guide).
     *
     * @param feed feed to check.
     *
     * @return <code>TRUE</code> if a feed is currently visible on the screen (in the selected guide).
     */
    public boolean isCurrentlyVisible(IFeed feed)
    {
        return guideModel.isPresent(feed);   
    }

    /**
     * After reading this in from persistent state, initialize parts of the state of GlobalModel
     * which do not get persisted.
     */
    public void initTransientState()
    {
    }

    /**
     * Update the Model with what guide is currently selected.
     *
     * @param guide new guide selection.
     */
    public void setSelectedGuide(final IGuide guide)
    {
        selectedGuide = guide;

        // EDT !!!
        if (guideModel != null) guideModel.setGuide(guide);

        if (guide != null)
        {
            selectedFeed = selectedFeeds.get(guide);
            if (selectedFeed == null && guideModel != null && guideModel.getSize() > 0)
            {
                selectedFeed = (IFeed)guideModel.getElementAt(0);
            }
        } else selectedFeed = null;
    }

    /**
     * Returns currently selected guide.
     *
     * @return selected guide.
     */
    public IGuide getSelectedGuide()
    {
        return selectedGuide;
    }

    /**
     * Returns selected article.
     *
     * @return selected article.
     */
    public IArticle getSelectedArticle()
    {
        return selectedArticle;
    }

    /**
     * Sets selected article.
     *
     * @param aArticle selected article.
     */
    public void setSelectedArticle(IArticle aArticle)
    {
        selectedArticle = aArticle;
    }

    /**
     * Returns the list of currently selected articles.
     *
     * @return articles.
     */
    public IArticle[] getSelectedArticles()
    {
        return selectedArticles;
    }

    /**
     * Sets selected articles.
     *
     * @param aSelectedArticles articles.
     */
    public void setSelectedArticles(IArticle[] aSelectedArticles)
    {
        selectedArticles = aSelectedArticles;
    }

    /**
     * Selects the feed.
     *
     * @param feed feed to select.
     */
    public void setSelectedFeed(final IFeed feed)
    {
        if (feed == null || isSelectable(feed))
        {
            selectedFeed = feed;
            if (feed != null)
            {
                IGuide selectedGuide = getSelectedGuide();
                selectedFeeds.put(selectedGuide, feed);
                feed.setLastVisitTime(System.currentTimeMillis());
                feed.setViews(feed.getViews() + 1);
            }

            viewModeValueModel.setFeed(feed);
            viewTypeValueModel.setFeed(feed);
        }

        firePreviousFeedChanged();

        synchronized (selectedFeedChangeInfo)
        {
            selectedFeedChangeInfo.reset(feed);
        }
    }

    private void firePreviousFeedChanged()
    {
        IFeed feed = null;

        synchronized (selectedFeedChangeInfo)
        {
            if (selectedFeedChangeInfo.changed)
            {
                feed = selectedFeedChangeInfo.feed;
            }
        }

        if (feed != null)
        {
            final IFeed feed1 = feed;
            SwingUtilities.invokeLater(new Runnable()
            {
                public void run()
                {
                    guideModel.contentsChangedAt(feed1);
                }
            });
        }
    }


    /**
     * Returns <code>true</code> if feed is selectable.
     *
     * @param feed feed to check.
     *
     * @return result of check.
     */
    public boolean isSelectable(final IFeed feed)
    {
        IGuide selectedGuide = getSelectedGuide();
        return feed == null ||
            (selectedGuide != null &&
             feed.belongsTo(selectedGuide) &&
             selectedGuide.indexOf(feed) != -1);
    }

    /**
     * Returns selected feed.
     *
     * @return selected feed.
     */
    public IFeed getSelectedFeed()
    {
        return selectedFeed;
    }

    /**
     * Sets channel guide set.
     *
     * @param set channel guide set.
     */
    public void setGuidesSet(final GuidesSet set)
    {
        // Connect model domain listener to the new set
        new DomainEventsListener(set).addDomainListener(domainListener);

        guidesSet = set;
    }

    /**
     * Returns the channel guide set.
     *
     * @return set.
     */
    public GuidesSet getGuidesSet()
    {
        return guidesSet;
    }

    /**
     * Sets new singleton.
     *
     * @param model new singleton.
     */
    public static void setSINGLETON(final GlobalModel model)
    {
        SINGLETON = model;
    }

    /**
     * Called to clean up model related resources, kill processes and save XML state just before
     * exiting.
     */
    public void prepareForApplicationExit()
    {
    }

    /**
     * Returns user preferences object.
     *
     * @return the user preferences object.
     */
    public UserPreferences getUserPreferences()
    {
        return userPreferences;
    }

    /**
     * Returns service preferences.
     *
     * @return preferences.
     */
    public ServicePreferences getServicePreferences()
    {
        return servicePreferences;
    }

    /**
     * Returns global rendering settings.
     *
     * @return global rendering settings.
     */
    public FeedRenderingSettings getGlobalRenderingSettings()
    {
        return globalRenderingSettings;
    }

    /**
     * Returns Starz service preferences.
     *
     * @return preferences object.
     */
    public StarzPreferences getStarzPreferences()
    {
        return starzPreferences;
    }

    /**
     * Notifies model about change in selected feed if containing guide is currently displayed.
     *
     * @param feed   changed feed.
     */
    public void feedUpdated(IFeed feed)
    {
        if (blockChannelUpdated) return;

        if (LOG.isLoggable(Level.FINE)) LOG.fine("Feed updated " + feed);

        if (selectedFeedChangeInfo.feed == feed)
        {
            selectedFeedChangeInfo.registerChanged();

            SwingUtilities.invokeLater(new FireFeedUpdated(feed, true));
        } else
        {
            SwingUtilities.invokeLater(new FireFeedUpdated(feed, false));
        }
    }

    /**
     * Called when the initial loading of the feeds from database starts.
     */
    protected void loadingStarted()
    {
        blockChannelUpdated = true;
    }

    /**
     * Called when the initial loading of the feeds finishes.
     */
    protected void loadingFinished()
    {
        if (blockChannelUpdated)
        {
            blockChannelUpdated = false;

            // Doing update of model from within EDT
            SwingUtilities.invokeLater(new Runnable()
            {
                public void run()
                {
                    getHighlightsCalculator().invalidateAll();
                    getScoreCalculator().invalidateAll();

                    guideModel.fullRebuild();
                }
            });
        }
    }
   
    /**
     * Given a guide, return a list of feeds which would be displayed
     * in that guide given current filter settings and options.
     * Uses the existing guide model if it corresponds to the requested guide,
     * otherwise creates a temporary GuideModel instance.
     * @param guide Guide to look in.
     * @return array of visible feeds for that guide.
     */
    public IFeed[] getVisibleFeeds(IGuide guide)
    {
        GuideModel model = (guide == selectedGuide) ? guideModel : getVFHelperModel(guide);

        // !!! EDT
        int numFeeds = model.getSize();
        IFeed[] retFeeds = new IFeed[numFeeds];

        for (int i = 0; i < numFeeds; ++i) retFeeds[i] = (IFeed) model.getElementAt(i);

        return retFeeds;
    }

    /**
     * Returns the number of unread articles in visible data feeds only within the given guide.
     *
     * @param aGuide guide.
     *
     * @return number of unread articles.
     */
    public int getUnreadArticlesCount(IGuide aGuide)
    {
        int count = 0;
        GuideModel mdl = getVFHelperModel(aGuide);
        for (int i = 0; i < mdl.getSize(); i++)
        {
            Object item = mdl.getElementAt(i);
            if (item instanceof DataFeed)
            {
                count += ((DataFeed)item).getUnreadArticlesCount();
            }
        }

        return count;
    }

    /**
     * Updates the last update time of preferences.
     */
    public static void touchPreferences()
    {
        SINGLETON.getUserPreferences().setLastUpdateTime(new Date());
    }

    /** Feed changes information block. Used to store information about selected feed changes. */
    private static class FeedChangeInfo
    {
        private IFeed   feed;
        private boolean changed;

        /**
         * Resets the information and sets new feed.
         *
         * @param aFeed new feed.
         */
        public synchronized void reset(IFeed aFeed)
        {
            feed = aFeed;
            changed = false;
        }

        /**
         * Register change of the feed.
         */
        public synchronized void registerChanged()
        {
            changed = true;
        }
    }

    // ------------------------------------------------------------------------
    // Preferences
    // ------------------------------------------------------------------------

    /**
     * Stores preferences in the object.
     *
     * @param prefs preference storage.
     */
    public void storePreferences(Preferences prefs)
    {
        userPreferences.storeIn(prefs);
        servicePreferences.storeIn(prefs);
        globalRenderingSettings.storeIn(prefs);
        starzPreferences.storeIn(prefs);
    }

    /**
     * Restores preferences.
     *
     * @param prefs preference storage.
     */
    public void restorePreferences(Preferences prefs)
    {
        userPreferences.restoreFrom(prefs);
        servicePreferences.restoreFrom(prefs);
        globalRenderingSettings.restoreFrom(prefs);
        starzPreferences.restoreFrom(prefs);
    }

    // ------------------------------------------------------------------------
    // Helper classes
    // ------------------------------------------------------------------------

    /** Fires feed update event. */
    private class FireFeedUpdated implements Runnable
    {
        private final IFeed feed;
        private final boolean fireEvent;

        public FireFeedUpdated(IFeed aFeed, boolean event)
        {
            feed = aFeed;
            fireEvent = event;
        }

        public void run()
        {
            if (fireEvent)
            {
                synchronized (guideModel)
                {
                    int index = guideModel.indexOf(feed);
                    if (index > -1) guideModel.fireContentsChanged(guideModel, index, index);
                }

                if (getSelectedFeed() == feed) GlobalController.SINGLETON.getMainFrame().updateTitle(feed);
            } else
            {
                guideModel.contentsChangedAt(feed);
            }
        }
    }

    /**
     * Listener of domain events.
     */
    private class DomainListener extends DomainAdapter
    {
        private static final String THREAD_NAME_ARTICLE_SPECIAL_FUNCTIONS = "Article Special Functions";
        private static final String THREAD_NAME_UPDATE_SCORES = "Update Scores";

        /**
         * Invoked when new guide has been added to the set.
         *
         * @param set           guides set.
         * @param guide         added guide.
         * @param lastInBatch   <code>TRUE</code> when this is the last even in batch.
         */
        public void guideAdded(GuidesSet set, IGuide guide, boolean lastInBatch)
        {
            synchronized (guide)
            {
                int count = guide.getFeedsCount();
                for (int i = 0; i < count; i++) feedAdded(guide, guide.getFeedAt(i));
            }
        }

        /**
         * Invoked when the guide has been removed from the set.
         *
         * @param set   guides set.
         * @param guide removed guide.
         * @param index old guide index.
         */
        public void guideRemoved(GuidesSet set, IGuide guide, int index)
        {
            synchronized (guide)
            {
                int count = guide.getFeedsCount();
                for (int i = 0; i < count; i++) feedRemovedCommon(guide, guide.getFeedAt(i));
            }

            // Release the guide being deleted from Visible Feeds helper model
            selectedFeeds.remove(guide);
            if (vfHelperModel != null && vfHelperModel.getCurrentGuide() == guide)
            {
                vfHelperModel.setGuide(null);
            }

            // Release the guide being deleted from the DND list context
            if (DNDListContext.getDestination() == guide) DNDListContext.setDestination(null);
        }

        // -----------------------------------------------------------------------------------------
        // Feeds Stuff
        // -----------------------------------------------------------------------------------------

        /**
         * Invoked when new feed has been added to the guide.
         *
         * @param guide parent guide.
         * @param feed  added feed.
         */
        public void feedAdded(IGuide guide, IFeed feed)
        {
            IArticle[] articles = feed.getArticles();
            for (IArticle article : articles) articleAdded(feed, article);

            if (selectedGuide == guide)
            {
                final int index = guide.indexOf(feed);
                SwingUtilities.invokeLater(new Runnable()
                {
                    public void run()
                    {
                        guideModel.feedsAdded(index, index);
                    }
                });
            }
        }

        /**
         * Invoked when the feed has been removed from the guide.
         *
         * @param event feed removal event.
         */
        public void feedRemoved(FeedRemovedEvent event)
        {
            IGuide guide = event.getGuide();
            final IFeed feed = event.getFeed();

            // Do common processing
            feedRemovedCommon(guide, feed);

            if (selectedGuide == guide && event.isLastEvent())
            {
                final int visibleIndex = guideModel.indexOf(feed);

                // Recalculation of model should happen in EDT
                SwingUtilities.invokeLater(new Runnable()
                {
                    public void run()
                    {
                        guideModel.fullRebuild();

                        if (selectedFeed == feed)
                        {
                            IFeed feedToSelect = null;
                            int count = guideModel.getSize();
                            if (visibleIndex < count && count > 0)
                            {
                                feedToSelect = (IFeed)guideModel.getElementAt(visibleIndex == -1
                                    ? 0 : visibleIndex);
                            } else if (count > 0)
                            {
                                feedToSelect = (IFeed)guideModel.getElementAt(count - 1);
                            }

                            GlobalController.SINGLETON.selectFeed(feedToSelect);
                        }
                    }
                });
            }
        }

        /**
         * Invoked when a feed is moved from one position to another.
         *
         * @param guide       source guide.
         * @param feed        feed moved.
         * @param oldPosition old position.
         * @param newPosition new position.
         */
        public void feedRepositioned(final IGuide guide, final IFeed feed, int oldPosition, int newPosition)
        {
            if (guideModel.getCurrentGuide() == guide)
            {
                SwingUtilities.invokeLater(new Runnable()
                {
                    public void run()
                    {
                        // We reselect guide to let GuideModel reload the list
                        // of feeds that is cached at the moment.
                        guideModel.setGuide(guide);
                    }
                });
            }
        }

        /**
         * Common handling of removed feed event. Unregisteres listener from feed,
         * removes the associated selection key and clears the scores.
         *
         * @param guide guide the feed was removed from.
         * @param feed removed feed.
         */
        void feedRemovedCommon(IGuide guide, IFeed feed)
        {
            // Remove selected feed if it's equal to the one we just removed
            IFeed selectedFeed = selectedFeeds.get(guide);
            if (selectedFeed == feed) selectedFeeds.remove(guide);

            // Remove highlights only if this feed is not assigned anywhere else
            if (feed.getParentGuides().length == 0)
            {
                getHighlightsCalculator().feedRemoved(feed);
                getScoreCalculator().feedRemoved(feed);
            }
        }

        /**
         * Special functions for new articles.
         *
         * @param article new article.
         */
        private void newArticleSpecialFunctions(final IArticle article)
        {
            final DataFeed feed = (DataFeed)article.getFeed();
            final boolean autoDiscovery = feed.isAutoFeedsDiscovery();

            // Span new thread only if at least one of the functions is required.
            if (autoDiscovery)
            {
                Thread thread = new Thread(THREAD_NAME_ARTICLE_SPECIAL_FUNCTIONS)
                {
                    public void run()
                    {
                        // Discover links in new articles
                        if (autoDiscovery)
                        {
                            GlobalController.SINGLETON.discoverFeedsIn(article, feed);
                        }
                    }
                };

                thread.start();
            }
        }

        /**
         * Updates the scores (highlights and overall) of the feed.
         *
         * @param feed feed which scores to update.
         * @param updateHighlights TRUE to update highlights.
         */
        private void updateScores(final IFeed feed, final boolean updateHighlights)
        {
            Thread thread = new Thread(THREAD_NAME_UPDATE_SCORES)
            {
                public void run()
                {
                    // Submit invalidation request
                    HighlightsCalculator hcalc = null;
                    if (updateHighlights)
                    {
                        hcalc = getHighlightsCalculator();
                        hcalc.invalidateFeed(feed);
                    }

                    ScoresCalculator scalc = getScoreCalculator();
                    scalc.invalidateFeed(feed);

                    // Wait for calc finish
                    if (updateHighlights) hcalc.getHighlightsCount(feed);
                    if (feed instanceof DirectFeed) scalc.calcBlogStarzScore(feed);

                    // Repaint feed
                    feedUpdated(feed);
                }
            };

            thread.start();
        }

        /**
         * Called when information in feed changed.
         *
         * @param feed     feed.
         * @param property property of the feed.
         * @param oldValue old property value.
         * @param newValue new property value.
         */
        public void propertyChanged(IFeed feed, String property, Object oldValue, Object newValue)
        {
            if (!feed.belongsTo(selectedGuide)) return;

            boolean repaintNecessary = IFeed.PROP_TITLE.equals(property) ||
                DirectFeed.PROP_RATING.equals(property) ||
                DirectFeed.PROP_DISABLED.equals(property) ||
                IFeed.PROP_UNREAD_ARTICLES_COUNT.equals(property) ||
                IFeed.PROP_PROCESSING.equals(property) ||
                IFeed.PROP_INVALIDNESS_REASON.equals(property) ||
                SearchFeed.PROP_QUERY.equals(property) ||
                SearchFeed.PROP_ARTICLES_LIMIT.equals(property);

            boolean updateScore = false;
            boolean updateHighlights = false;
            if (IFeed.PROP_PROCESSING.equals(property) && !(Boolean)newValue)
            {
                // processing finished -- invalidate feed
                updateScore = true;
                updateHighlights = true;
            } else if (IFeed.PROP_CLICKTHROUGHS.equals(property))
            {
                // the number of clickthroughs has changed
                if (ScoresCalculator.registerMaxClickthroughs(feed.getClickthroughs()))
                    updateFeedsWithNonZeroClickthroughs();
                else updateScore = true;
            } else if (IFeed.PROP_VIEWS.equals(property))
            {
                // the number of views has changed
                if (ScoresCalculator.registerMaxFeedViews(feed.getViews()))
                    updateFeedsWithNonZeroViews();
                else updateScore = true;
            }

            if (updateScore) updateScores(feed, updateHighlights);
            if (repaintNecessary) feedUpdated(feed);
        }

        private void updateFeedsWithNonZeroViews()
        {
            ScoresCalculator sc = getScoreCalculator();
            List<IFeed> feeds = getGuidesSet().getFeeds();
            for (IFeed feed : feeds)
            {
                if (feed.getViews() != 0)
                {
                    sc.invalidateFeed(feed);
                    feedUpdated(feed);
                }
            }
        }

        private void updateFeedsWithNonZeroClickthroughs()
        {
            ScoresCalculator sc = getScoreCalculator();
            List<IFeed> feeds = getGuidesSet().getFeeds();
            for (IFeed feed : feeds)
            {
                if (feed.getClickthroughs() != 0)
                {
                    sc.invalidateFeed(feed);
                    feedUpdated(feed);
                }
            }
        }

        // -----------------------------------------------------------------------------------------
        // Articles
        // -----------------------------------------------------------------------------------------

        /**
         * Called when some article is added to the feed.
         *
         * @param feed    feed.
         * @param article article.
         */
        public void articleAdded(final IFeed feed, final IArticle article)
        {
            if (GlobalController.SINGLETON.isInitializationFinished() && feed instanceof DataFeed)
                newArticleSpecialFunctions(article);
        }
    }
}
TOP

Related Classes of com.salas.bb.core.GlobalModel$FireFeedUpdated

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.