// 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: SyncIn.java,v 1.96 2008/02/28 15:59:50 spyromus Exp $
//
package com.salas.bb.service.sync;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.uif.AbstractDialog;
import com.jgoodies.uif.application.Application;
import com.salas.bb.core.DeletedObjectsRepository;
import com.salas.bb.core.FeedDisplayModeManager;
import com.salas.bb.core.GlobalController;
import com.salas.bb.core.GlobalModel;
import com.salas.bb.domain.*;
import com.salas.bb.domain.prefs.StarzPreferences;
import com.salas.bb.domain.prefs.UserPreferences;
import com.salas.bb.domain.querytypes.QueryType;
import com.salas.bb.domain.utils.FeedCheckBox;
import com.salas.bb.domain.utils.ReadingListCheckBox;
import com.salas.bb.imageblocker.ImageBlocker;
import com.salas.bb.plugins.Manager;
import com.salas.bb.sentiments.SentimentsConfig;
import com.salas.bb.service.ServerService;
import com.salas.bb.service.ServerServiceException;
import com.salas.bb.service.ServicePreferences;
import com.salas.bb.twitter.TwitterPreferences;
import com.salas.bb.utils.DateUtils;
import com.salas.bb.utils.StringUtils;
import com.salas.bb.utils.i18n.Strings;
import com.salas.bb.utils.opml.Helper;
import com.salas.bb.utils.uif.BBFormBuilder;
import com.salas.bb.utils.uif.CheckBoxList;
import com.salas.bb.utils.uif.ComponentsFactory;
import com.salas.bb.views.settings.FeedRenderingSettings;
import com.salas.bb.views.settings.RenderingSettingsNames;
import com.salas.bbutilities.opml.Importer;
import com.salas.bbutilities.opml.ImporterException;
import com.salas.bbutilities.opml.objects.OPMLGuideSet;
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowEvent;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.text.MessageFormat;
import java.util.*;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Ingoing synchronization module.
*/
public class SyncIn extends AbstractSynchronization
{
private static final Logger LOG = Logger.getLogger(SyncIn.class.getName());
private static final String MSG_ERROR_DURING_SYNC_IN = Strings.error("sync.error.during.sync.in");
/** <code>TRUE</code> to copy the layout of the server side. */
private boolean copyServiceLayout;
/**
* Creates synchronization modules for the model.
*
* @param aModel model.
* @param aCopyServiceSide <code>TRUE</code> to copy the layout of the server side.
*/
public SyncIn(GlobalModel aModel, boolean aCopyServiceSide)
{
super(aModel);
copyServiceLayout = aCopyServiceSide;
}
/**
* Performs the step-by-step synchronization and collects stats.
*
* @param progress listener to notify.
* @param aEmail email of user account.
* @param aPassword password of user account.
*
* @return statistics.
*/
protected Stats doSynchronization(IProgressListener progress, String aEmail,
String aPassword)
{
SyncInStats stats = new SyncInStats();
try
{
if (servicePreferences.isSyncFeeds())
{
if (progress != null) progress.processStep(Strings.message("service.sync.in.loading.guides.and.feeds"));
loadFeeds(aEmail, aPassword, stats);
if (progress != null) progress.processStepCompleted();
}
if (servicePreferences.isSyncPreferences())
{
if (progress != null) progress.processStep(Strings.message("service.sync.in.loading.preferences"));
loadPreferences(aEmail, aPassword, stats);
if (progress != null) progress.processStepCompleted();
}
servicePreferences.setLastSyncInStatus(ServicePreferences.SYNC_STATUS_SUCCESS);
} catch (ServerServiceException e1)
{
// If the cause of service exception is another exception then log it
if (e1.getCause() != null)
{
LOG.log(Level.SEVERE, MSG_ERROR_DURING_SYNC_IN, e1);
stats.registerFailure(null);
} else
{
stats.registerFailure(e1.getMessage());
}
} catch (ImporterException e1)
{
LOG.log(Level.SEVERE, MSG_ERROR_DURING_SYNC_IN, e1);
stats.registerFailure(null);
}
if (stats.hasFailed()) servicePreferences.setLastSyncInStatus(ServicePreferences.SYNC_STATUS_FAILURE);
// update last sync out date
servicePreferences.setLastSyncInDate(new Date());
return stats;
}
/**
* Returns the message to be reported on synchronization start.
*
* @return message.
*/
protected String getProcessStartMessage()
{
return prepareProcessStartMessage(
Strings.message("service.sync.message.loading"),
Strings.message("service.sync.message.preferences"),
Strings.message("service.sync.message.guides.and.feeds"),
Strings.message("service.sync.message.from.blogbridge.service"));
}
/**
* Loads preferences from service.
*
* @param email email of user account.
* @param password password of user account.
* @param stats stats to fill.
*
* @throws ServerServiceException in case of service failure.
*/
private void loadPreferences(String email, String password, SyncInStats stats)
throws ServerServiceException
{
UserPreferences up = model.getUserPreferences();
Map prefs = ServerService.syncRestorePrefs(email, password);
Date lastUpdateTimeO = up.getLastUpdateTime();
long localUpdateTime = lastUpdateTimeO == null ? -1 : lastUpdateTimeO.getTime();
long remoteUpdateTime = getLong(prefs, "timestamp", -1);
int loaded;
// If local changes happened after saving preferences to the service, do not update them
if (copyServiceLayout || localUpdateTime < remoteUpdateTime)
{
// Image blocker list
String ibExpressions = getPreferenceValue(prefs, ImageBlocker.KEY);
ImageBlocker.setExpressions(ibExpressions);
SentimentsConfig.syncIn(prefs);
loadUserPreferences(prefs);
loaded = prefs.size();
} else loaded = 0;
// Loading What's Hot preferences blog with independent change timestamp
long local = DateUtils.localToUTC(up.getWhSettingsChangeTime());
long remote = getLong(prefs, UserPreferences.PROP_WH_SETTINGS_CHANGE_TIME, 0);
if (copyServiceLayout || remote > local)
{
// If the settings were changed remotely, copy them
up.setWhIgnore(getString(prefs, UserPreferences.PROP_WH_IGNORE,
UserPreferences.DEFAULT_WH_IGNORE));
up.setWhNoSelfLinks(getBoolean(prefs, UserPreferences.PROP_WH_NOSELFLINKS,
UserPreferences.DEFAULT_WH_NOSELFLINKS));
up.setWhSuppressSameSourceLinks(getBoolean(prefs, UserPreferences.PROP_WH_SUPPRESS_SAME_SOURCE_LINKS,
UserPreferences.DEFAULT_WH_SUPPRESS_SAME_SOURCE_LINKS));
up.setWhTargetGuide(getString(prefs, UserPreferences.PROP_WH_TARGET_GUIDE,
UserPreferences.DEFAULT_WH_TARGET_GUIDE));
up.setWhSettingsChangeTime(remote);
}
stats.loadedPreferences = loaded;
}
private static String getPreferenceValue(Map preferences, String key)
{
return StringUtils.fromUTF8((byte[])preferences.get(key));
}
/**
* Synchronizes feeds (loads from service and merges them with local list).
*
* @param email email of user.
* @param password password of user.
* @param stats stats to fill.
*
* @throws ServerServiceException in case of error with service.
* @throws ImporterException in case of problems with OPML.
*/
private void loadFeeds(String email, String password, final SyncInStats stats)
throws ServerServiceException, ImporterException
{
String opml = ServerService.syncRestore(email, password);
// parse output from server w/ possible empty guides
final Importer im = new Importer();
im.setAllowEmptyGuides(true);
OPMLGuideSet set = im.processFromString(opml, false);
GuidesSet remoteSet = Helper.createGuidesSet(null, set);
final GuidesSet localSet = model.getGuidesSet();
final Changes changes = evaluateChanges(localSet, remoteSet, copyServiceLayout);
// EDT
try
{
SwingUtilities.invokeAndWait(new Runnable()
{
public void run()
{
if (adjustChangesByUser(changes))
{
IGuide selectedGuide = model.getSelectedGuide();
IFeed selectedFeed = model.getSelectedFeed();
performChanges(localSet, changes, stats);
restoreSelection(selectedGuide, selectedFeed);
// Update synchronization times
localSet.onSyncInCompletion();
}
}
});
} catch (Throwable e)
{
if (e instanceof InvocationTargetException) e = e.getCause();
LOG.log(Level.SEVERE, MSG_ERROR_DURING_SYNC_IN, e.getCause());
}
}
/**
* Shows alert and allows user to adjust his.
*
* @param aChanges changes we are going to make.
*
* @return <code>TRUE</code> if user has accepted the changes.
*/
static boolean adjustChangesByUser(Changes aChanges)
{
boolean accepted = true;
List<IFeed> newFeeds = aChanges.getAddFeeds();
if (newFeeds.size() > 0 || aChanges.getAddReadingLists().size() > 0)
{
ChangesConfirmationDialog dialog =
new ChangesConfirmationDialog(Application.getDefaultParentFrame(),
aChanges.getAddReadingLists(), newFeeds);
dialog.open();
accepted = !dialog.hasBeenCanceled();
}
return accepted;
}
/**
* Restores selected feed and guide.
*
* @param aSelectedGuide selected guide.
* @param aSelectedFeed selected feed.
*/
private static void restoreSelection(IGuide aSelectedGuide, IFeed aSelectedFeed)
{
if (aSelectedFeed == null || aSelectedGuide == null) return;
GlobalController controller = GlobalController.SINGLETON;
if (controller != null)
{
// EDT !!!
controller.selectGuide(aSelectedGuide, false);
controller.selectFeed(aSelectedFeed);
}
}
/**
* Performs changes.
*
* @param set set to update.
* @param changes changes.
* @param stats stats to fill in.
*/
static void performChanges(GuidesSet set, Changes changes, SyncInStats stats)
{
int oldGuidesCount = set.getGuidesCount();
performChangesAddFeeds(set, changes.getAddFeeds(), stats);
performChangesAddReadingLists(set, changes.getAddReadingLists(), stats);
performChangesRemoveReadingLists(changes.getRemoveReadingLists());
performChangesRemoveFeeds(changes.getRemoveFeeds());
performChangesUpdateFeeds(changes);
performChangesUpdateGuides(changes.getUpdateGuides());
performChangesRemoveEmptyGuides(set);
if (stats != null)
{
stats.createdGuides = set.getGuidesCount() - oldGuidesCount;
if (stats.createdGuides < 0) stats.createdGuides = 0;
}
}
/**
* Applies changes to the guides.
*
* @param guidePairs guide pairs.
*/
private static void performChangesUpdateGuides(List<GuidePair> guidePairs)
{
for (GuidePair pair : guidePairs)
{
IGuide localGuide = pair.local;
IGuide remoteGuide = pair.remote;
transferGuideProperties(localGuide, remoteGuide);
}
}
/**
* Transfers properties from one guide to another.
*
* @param guide target guide.
* @param pattern pattern guide.
*/
static void transferGuideProperties(IGuide guide, IGuide pattern)
{
guide.setIconKey(pattern.getIconKey());
guide.setPublishingEnabled(pattern.isPublishingEnabled());
guide.setPublishingTitle(pattern.getPublishingTitle());
guide.setPublishingTags(pattern.getPublishingTags());
guide.setPublishingPublic(pattern.isPublishingPublic());
guide.setPublishingRating(pattern.getPublishingRating());
guide.setAutoFeedsDiscovery(pattern.isAutoFeedsDiscovery());
guide.setNotificationsAllowed(pattern.isNotificationsAllowed());
guide.setMobile(pattern.isMobile());
}
/**
* Applies read articles match keys to feeds.
*
* @param changes set of changes.
*/
private static void performChangesUpdateFeeds(Changes changes)
{
Map<DataFeed, FeedPair> aFeeds = changes.getUpdateFeedsKeys();
for (FeedPair pair : aFeeds.values())
{
DataFeed local = (DataFeed)pair.local;
DataFeed remote = (DataFeed)pair.remote;
local.setReadArticlesKeys(remote.getReadArticlesKeys());
local.setPinnedArticlesKeys(remote.getPinnedArticlesKeys());
}
Collection<FeedPair> updateFeeds = changes.getUpdateFeeds();
for (FeedPair pair : updateFeeds)
{
IFeed localFeed = pair.local;
IFeed remoteFeed = pair.remote;
// Move properties from remote feed to local
localFeed.setCustomViewMode(remoteFeed.getCustomViewMode());
localFeed.setCustomViewModeEnabled(remoteFeed.isCustomViewModeEnabled());
localFeed.setRating(remoteFeed.getRating());
localFeed.setType(remoteFeed.getType());
localFeed.setHandlingType(remoteFeed.getHandlingType());
// Feed type specific operations
if (localFeed instanceof DirectFeed)
{
DirectFeed localDFeed = (DirectFeed)localFeed;
DirectFeed remoteDFeed = (DirectFeed)remoteFeed;
localDFeed.setCustomAuthor(remoteDFeed.getCustomAuthor());
localDFeed.setCustomDescription(remoteDFeed.getCustomDescription());
localDFeed.setCustomTitle(remoteDFeed.getCustomTitle());
localDFeed.setDisabled(remoteDFeed.isDisabled());
localDFeed.setPurgeLimit(remoteDFeed.getPurgeLimit());
localDFeed.setUserTags(remoteDFeed.getUserTags());
localDFeed.setTagsDescription(remoteDFeed.getTagsDescription());
localDFeed.setTagsExtended(remoteDFeed.getTagsExtended());
} else if (localFeed instanceof QueryFeed)
{
QueryFeed localQFeed = (QueryFeed)localFeed;
QueryFeed remoteQFeed = (QueryFeed)remoteFeed;
localQFeed.setPurgeLimit(remoteQFeed.getPurgeLimit());
localQFeed.setQueryType(remoteQFeed.getQueryType());
localQFeed.setParameter(remoteQFeed.getParameter());
} else
{
SearchFeed localSFeed = (SearchFeed)localFeed;
SearchFeed remoteSFeed = (SearchFeed)remoteFeed;
localSFeed.setQuery(remoteSFeed.getQuery());
}
if (localFeed instanceof DataFeed)
{
DataFeed localDFeed = (DataFeed)localFeed;
DataFeed removeDFeed = (DataFeed)remoteFeed;
localDFeed.setUpdatePeriod(removeDFeed.getUpdatePeriod());
}
}
}
/**
* Removes all reading lists schedule for removal and leaves feeds unassociated.
*
* @param aReadingLists reading lists.
*/
private static void performChangesRemoveReadingLists(List<ReadingList> aReadingLists)
{
for (ReadingList list : aReadingLists)
{
StandardGuide guide = list.getParentGuide();
guide.remove(list, true);
}
}
/**
* Adds reading lists and connects required feeds in the guide.
*
* @param aSet guides set.
* @param aReadingLists reading lists.
* @param aStats statistics.
*/
private static void performChangesAddReadingLists(GuidesSet aSet, List<ReadingList> aReadingLists,
SyncInStats aStats)
{
for (ReadingList list : aReadingLists)
{
performChangesAddReadingList(aSet, list, aStats);
}
}
/**
* Adds reading list to the guide and associates all required feeds.
*
* @param aSet guides set.
* @param aList reading list.
* @param aStats statistics.
*/
private static void performChangesAddReadingList(GuidesSet aSet, ReadingList aList,
SyncInStats aStats)
{
StandardGuide guide = findOrCreateGuide(aSet, aList.getParentGuide());
ReadingList newList = new ReadingList(aList.getURL());
newList.setTitle(aList.getTitle());
guide.add(newList);
DirectFeed[] feedsToConnect = aList.getFeeds();
for (DirectFeed feed : feedsToConnect)
{
DirectFeed existingFeed = aSet.findDirectFeed(feed.getXmlURL());
boolean existing = true;
if (existingFeed == null)
{
existingFeed = feed;
feed.removeParentGuide(feed.getParentGuides()[0]);
feed.removeAllReadingLists();
if (aStats != null) aStats.addedFeeds++;
existing = false;
}
newList.add(existingFeed);
if (!existing) GlobalController.SINGLETON.getPoller().update(existingFeed, false);
}
}
/**
* Removes empty guides.
*
* @param set guides set.
*/
private static void performChangesRemoveEmptyGuides(GuidesSet set)
{
StandardGuide[] guides = set.getStandardGuides(null);
for (StandardGuide guide : guides)
{
if (guide.getFeedsCount() == 0) set.remove(guide);
}
}
/**
* Moves feeds marked for addition to the guides.
*
* @param aSet local set.
* @param aFeeds feeds list.
* @param aStats stats to fill in.
*/
private static void performChangesAddFeeds(GuidesSet aSet, List<IFeed> aFeeds,
SyncInStats aStats)
{
for (IFeed feed : aFeeds)
{
IGuide guide = feed.getParentGuides()[0];
if (guide.hasDirectLinkWith(feed))
{
StandardGuide localGuide = findOrCreateGuide(aSet, guide);
// Disconnect feed from its old parent guide
feed.removeParentGuide(guide);
if (feed instanceof DirectFeed) ((DirectFeed)feed).removeAllReadingLists();
// Find existing feed
IFeed existingFeed = aSet.findFeed(feed);
if (existingFeed != null) feed = existingFeed;
localGuide.add(feed);
StandardGuide.FeedLinkInfo info = localGuide.getFeedLinkInfo(feed);
info.setLastSyncTime(System.currentTimeMillis());
if (aStats != null) aStats.addedFeeds++;
}
}
}
/**
* Finds a guide in the set of local guides or create a new one.
*
* @param aSet set.
* @param aPatternGuide pattern-guide.
*
* @return guide.
*/
static StandardGuide findOrCreateGuide(GuidesSet aSet, IGuide aPatternGuide)
{
String title = aPatternGuide.getTitle();
StandardGuide guide = findGuideByName(aSet.getStandardGuides(null), title);
if (guide == null)
{
guide = new StandardGuide();
guide.setTitle(title);
transferGuideProperties(guide, aPatternGuide);
aSet.add(guide);
}
return guide;
}
/**
* Removes feed mentioned in the list.
*
* @param guideFeeds feeds list.
*/
private static void performChangesRemoveFeeds(List<GuideFeedPair> guideFeeds)
{
for (GuideFeedPair guideFeed : guideFeeds)
{
IGuide localGuide = guideFeed.guide;
IFeed localFeed = guideFeed.feed;
// To be removed from some guide feed should be unassigned from the
// reading lists in that guide first and then removed from the guide
// itself
if (localFeed instanceof DirectFeed)
{
DirectFeed dfeed = (DirectFeed)localFeed;
ReadingList[] lists = dfeed.getReadingLists();
for (ReadingList list : lists)
{
if (list.getParentGuide() == localGuide) list.remove(dfeed);
}
}
localGuide.remove(localFeed);
}
}
/**
* Compares two sets of guides and evaluates necessary changes.
* This is the core of synchronization logic.
*
* @param aLocalSet local set of guides.
* @param aRemoteSet remote set of guides.
* @param copyServiceLayout <code>TRUE</code> to make a complete copy of the service side.
*
* @return changes.
*/
static Changes evaluateChanges(GuidesSet aLocalSet, GuidesSet aRemoteSet,
boolean copyServiceLayout)
{
Changes changes = new Changes();
StandardGuide[] localGuides = aLocalSet.getStandardGuides(null);
StandardGuide[] remoteGuides = aRemoteSet.getStandardGuides(null);
DeletedObjectsRepository dfr = GlobalController.SINGLETON.getDeletedFeedsRepository();
// Scan through remote guides and verify each guide user has.
// If user has no some guide then put the guide in the list
// of guides to add. If user has the guide, verify its contents.
// Later we could scan the lists of guides for addition and
// guides for removal in order to detect the renaming.
for (StandardGuide remoteGuide : remoteGuides)
{
String remoteGuideTitle = remoteGuide.getTitle();
StandardGuide localGuide = findGuideByName(localGuides, remoteGuideTitle);
if (localGuide == null)
{
// There's no guide with such name locally -- add feeds that weren't removed
for (int j = 0; j < remoteGuide.getFeedsCount(); j++)
{
IFeed feed = remoteGuide.getFeedAt(j);
if (remoteGuide.hasDirectLinkWith(feed) &&
(copyServiceLayout || !dfr.wasDeleted(remoteGuideTitle, feed.getMatchKey())))
changes.addFeed(feed);
}
// Reading lists
ReadingList[] readingLists = remoteGuide.getReadingLists();
for (ReadingList rl : readingLists)
{
if (copyServiceLayout || !dfr.wasDeleted(remoteGuideTitle, rl.getURL().toString()))
changes.addReadingList(rl);
}
} else
{
if (localGuide.getLastUpdateTime() < remoteGuide.getLastUpdateTime())
{
changes.updateGuide(localGuide, remoteGuide);
}
evaluateChangesInGuide(localGuide, remoteGuide, changes, copyServiceLayout);
}
}
// Scan through the list of local guides and mark for removal those not
// mentioned in the list of remote.
for (StandardGuide localGuide : localGuides)
{
StandardGuide remoteGuide = findGuideByName(remoteGuides, localGuide.getTitle());
if (remoteGuide == null)
{
ReadingList[] readingLists = localGuide.getReadingLists();
for (ReadingList readingList : readingLists)
{
if (copyServiceLayout || readingList.getLastSyncTime() != -1)
{
changes.removeReadingList(readingList);
}
}
// Remove whole guide
for (int j = 0; j < localGuide.getFeedsCount(); j++)
{
IFeed feed = localGuide.getFeedAt(j);
StandardGuide.FeedLinkInfo info;
if (copyServiceLayout || ((info = localGuide.getFeedLinkInfo(feed)) != null &&
info.getLastSyncTime() != -1))
{
changes.removeFeed(localGuide, feed);
}
}
}
}
// Find changes in feeds
FeedsList localFeeds = aLocalSet.getFeedsList();
for (int i = 0; i < localFeeds.getFeedsCount(); i++)
{
IFeed localFeed = localFeeds.getFeedAt(i);
IFeed remoteFeed = aRemoteSet.findFeed(localFeed);
if (remoteFeed != null && (copyServiceLayout ||
localFeed.getLastUpdateTime() < remoteFeed.getLastUpdateTime()))
{
changes.updateFeed(localFeed, remoteFeed);
}
}
return changes;
}
/**
* Looks for a guide with the given name.
*
* @param aGuides guides list.
* @param aName name.
*
* @return target guide.
*/
private static StandardGuide findGuideByName(StandardGuide[] aGuides, String aName)
{
StandardGuide guide = null;
for (int i = 0; guide == null && i < aGuides.length; i++)
{
StandardGuide standardGuide = aGuides[i];
if (aName.equals(standardGuide.getTitle())) guide = standardGuide;
}
return guide;
}
/**
* Evaluates changes within single guide.
*
* @param aLocalGuide local guide.
* @param aRemoteGuide remote guide.
* @param aChanges changes.
* @param aClearNew <code>TRUE</code> to remove any local feeds which aren't on the service.
*/
static void evaluateChangesInGuide(StandardGuide aLocalGuide,
StandardGuide aRemoteGuide, Changes aChanges, boolean aClearNew)
{
evaluateChangesInReadingLists(aLocalGuide, aRemoteGuide, aChanges, aClearNew);
evaluateChangesInFeeds(aLocalGuide, aRemoteGuide, aChanges, aClearNew);
}
/**
* Evaluates changes within single guide reading lists.
*
* @param aLocalGuide local guide.
* @param aRemoteGuide remote guide.
* @param aChanges changes.
* @param aClearNew <code>TRUE</code> to remove any local feeds which aren't on the service.
*/
static void evaluateChangesInReadingLists(StandardGuide aLocalGuide,
StandardGuide aRemoteGuide, Changes aChanges, boolean aClearNew)
{
// Add new reading lists from the remote source
ReadingList[] listsR = aRemoteGuide.getReadingLists();
for (ReadingList listR : listsR)
{
ReadingList listL = findReadingList(aLocalGuide, listR);
// A reading list has been added remotely
if (listL == null) aChanges.addReadingList(listR);
}
// Remove local reading lists
ReadingList[] listsL = aLocalGuide.getReadingLists();
for (ReadingList listL : listsL)
{
ReadingList listR = findReadingList(aRemoteGuide, listL);
// A reading list has been removed remotely or has not been sent to the service yet
if (listR == null && (aClearNew || listL.getLastSyncTime() != -1))
{
aChanges.removeReadingList(listL);
}
}
}
/**
* Looks for the same list in the guide.
*
* @param aGuide guide.
* @param aList target list.
*
* @return list object or <code>NULL</code>.
*/
private static ReadingList findReadingList(StandardGuide aGuide, ReadingList aList)
{
ReadingList[] lists = aGuide.getReadingLists();
return findReadingList(lists, aList);
}
/**
* Looks for the same list in the list.
*
* @param aLists list of lists.
* @param targetList target list.
*
* @return list object or <code>NULL</code>.
*/
private static ReadingList findReadingList(ReadingList[] aLists, ReadingList targetList)
{
ReadingList list = null;
URL urlT = targetList.getURL();
if (urlT != null)
{
String urlTS = urlT.toString();
for (int i = 0; list == null && i < aLists.length; i++)
{
ReadingList readingList = aLists[i];
URL url = readingList.getURL();
if (url != null && url.toString().equals(urlTS)) list = readingList;
}
}
return list;
}
/**
* Evaluates changes within single guide.
*
* @param aLocalGuide local guide.
* @param aRemoteGuide remote guide.
* @param aChanges changes.
* @param aClearNew <code>TRUE</code> to remove any local feeds which aren't on the service.
*/
static void evaluateChangesInFeeds(StandardGuide aLocalGuide,
StandardGuide aRemoteGuide, Changes aChanges, boolean aClearNew)
{
DeletedObjectsRepository dfr = GlobalController.SINGLETON.getDeletedFeedsRepository();
// Add new feeds from remote source
for (int i = 0; i < aRemoteGuide.getFeedsCount(); i++)
{
IFeed remoteFeed = aRemoteGuide.getFeedAt(i);
if (!aRemoteGuide.hasDirectLinkWith(remoteFeed)) continue;
IFeed localFeed = findFeed(aLocalGuide, remoteFeed);
if (!aLocalGuide.hasDirectLinkWith(localFeed)) localFeed = null;
// A feed has been added remotely
if (localFeed == null)
{
if (aClearNew || !dfr.wasDeleted(aLocalGuide.getTitle(), remoteFeed.getMatchKey()))
{
aChanges.addFeed(remoteFeed);
}
} else
{
if (remoteFeed instanceof DataFeed)
{
aChanges.addUpdateFeedsKeys((DataFeed)localFeed, (DataFeed)remoteFeed);
}
}
}
// Remove local feeds
for (int i = 0; i < aLocalGuide.getFeedsCount(); i++)
{
IFeed localFeed = aLocalGuide.getFeedAt(i);
if (!aLocalGuide.hasDirectLinkWith(localFeed)) continue;
IFeed remoteFeed = findFeed(aRemoteGuide, localFeed);
if (!aRemoteGuide.hasDirectLinkWith(remoteFeed)) remoteFeed = null;
StandardGuide.FeedLinkInfo info;
// A feed has been removed remotely or has not been sent to service yet
if (remoteFeed == null &&
(aClearNew || ((info = aLocalGuide.getFeedLinkInfo(localFeed)) != null &&
info.getLastSyncTime() != -1)))
{
aChanges.removeFeed(aLocalGuide, localFeed);
}
}
}
/**
* Finds a feed in the guide which is the same as the pattern-feed.
* The similarity depends on the type of a feed.
*
* @param aGuide guide.
* @param aPatternFeed pattern-feed.
*
* @return similar feed or <code>NULL</code>.
*/
private static IFeed findFeed(StandardGuide aGuide, IFeed aPatternFeed)
{
IFeed targetFeed = null;
for (int i = 0; targetFeed == null && i < aGuide.getFeedsCount(); i++)
{
IFeed feed = aGuide.getFeedAt(i);
if (feedsAreTheSame(feed, aPatternFeed)) targetFeed = feed;
}
return targetFeed;
}
/**
* Finds all feeds in the guide which is the same as the pattern-feed.
* The similarity depends on the type of a feed.
*
* @param aGuide guide.
* @param aPatternFeed pattern-feed.
*
* @return similar feeds.
*/
// private static List findFeeds(StandardGuide aGuide, IFeed aPatternFeed)
// {
// List feeds = new ArrayList();
// for (int i = 0; i < aGuide.getFeedsCount(); i++)
// {
// IFeed feed = aGuide.getFeedAt(i);
// if (feedsAreTheSame(feed, aPatternFeed)) feeds.add(feed);
// }
//
// return feeds;
// }
/**
* Compares two feeds and says if they are similar by their properties.
*
* @param aFeed first feed.
* @param aPatternFeed second feed.
*
* @return <code>TRUE</code> if similar.
*/
private static boolean feedsAreTheSame(IFeed aFeed, IFeed aPatternFeed)
{
boolean similar = false;
if (aPatternFeed instanceof DirectFeed)
{
if (aFeed instanceof DirectFeed)
{
similar = feedsAreTheSameDirect((DirectFeed)aPatternFeed, (DirectFeed)aFeed);
}
} else if (aPatternFeed instanceof QueryFeed)
{
if (aFeed instanceof QueryFeed)
{
QueryFeed patFeed = (QueryFeed)aPatternFeed;
QueryFeed matFeed = (QueryFeed)aFeed;
QueryType patType = patFeed.getQueryType();
QueryType matType = matFeed.getQueryType();
String patParam = patFeed.getParameter();
String matParam = matFeed.getParameter();
similar = patType == matType && patParam.equals(matParam);
}
} else if (aPatternFeed instanceof SearchFeed)
{
if (aFeed instanceof SearchFeed)
{
String patQuery = ((SearchFeed)aPatternFeed).getQuery().serializeToString();
String matQuery = ((SearchFeed)aFeed).getQuery().serializeToString();
similar = patQuery.equals(matQuery);
}
}
return similar;
}
/**
* Checks if two direct feeds are the same.
*
* @param aPatternFeed the feed to match against the other.
* @param aMatchFeed the target feed.
*
* @return <code>TRUE</code> if matches.
*/
static boolean feedsAreTheSameDirect(DirectFeed aPatternFeed, DirectFeed aMatchFeed)
{
URL patXmlURL = aPatternFeed.getXmlURL();
URL matXmlURL = aMatchFeed.getXmlURL();
return patXmlURL != null && matXmlURL != null &&
(aPatternFeed.calcSyncHash() == aMatchFeed.getSyncHash() ||
matXmlURL.toString().equalsIgnoreCase(patXmlURL.toString()));
}
/**
* Simple holder of stats.
*/
private static class SyncInStats extends Stats
{
private int createdGuides;
private int addedFeeds;
private int loadedPreferences;
/**
* Returns custom text to be told if not failed.
*
* @return text.
*/
protected String getCustomText()
{
StringBuffer message = new StringBuffer();
if (createdGuides > 0) message.append(MessageFormat.format(
Strings.message("service.sync.in.status.guides.created"),
createdGuides));
if (addedFeeds > 0) message.append(MessageFormat.format(
Strings.message("service.sync.in.status.feeds.added"),
addedFeeds));
if (loadedPreferences > 0) message.append(MessageFormat.format(
Strings.message("service.sync.in.status.preferences.loaded"),
loadedPreferences));
return message.toString();
}
}
/** Changes to apply to local set of guides. */
public static class Changes
{
private List<IFeed> addFeeds = new ArrayList<IFeed>();
private Map<IFeed, FeedPair> updateFeeds = new IdentityHashMap<IFeed, FeedPair>();
private List<GuidePair> updateGuides = new ArrayList<GuidePair>();
private List<GuideFeedPair> removeFeeds = new ArrayList<GuideFeedPair>();
private Map<DataFeed, FeedPair> updateFeedsKeys = new IdentityHashMap<DataFeed, FeedPair>();
private List<ReadingList> addReadingLists = new ArrayList<ReadingList>();
private List<ReadingList> removeReadingLists = new ArrayList<ReadingList>();
/**
* Puts a feed into the additions list.
*
* @param feed feed.
*/
public void addFeed(IFeed feed)
{
addFeeds.add(feed);
}
/**
* Puts a feed into the removals list.
*
* @param guide guide to remove from.
* @param feed feed.
*/
public void removeFeed(IGuide guide, IFeed feed)
{
removeFeeds.add(new GuideFeedPair(guide, feed));
}
/**
* Puts a feed into the updates list.
*
* @param local local feed.
* @param remote remote feed.
*/
public void updateFeed(IFeed local, IFeed remote)
{
if (!updateFeeds.containsKey(local))
{
updateFeeds.put(local, new FeedPair(local, remote));
}
}
/**
* Upts a guide into the updates list.
*
* @param local local guide.
* @param remote remote guide.
*/
public void updateGuide(IGuide local, IGuide remote)
{
updateGuides.add(new GuidePair(local, remote));
}
/**
* Puts a reading list into the additions list.
*
* @param list list.
*/
public void addReadingList(ReadingList list)
{
addReadingLists.add(list);
}
/**
* Puts a reading list into the removals list.
*
* @param list list.
*/
public void removeReadingList(ReadingList list)
{
removeReadingLists.add(list);
}
/**
* Returns the list of feeds to be added.
*
* @return added feeds list.
*/
public List<IFeed> getAddFeeds()
{
return addFeeds;
}
/**
* Returns the list of guide-feeds pairs to be removed.
*
* @return removed guide-feeds list.
*
* @see GuideFeedPair
*/
public List<GuideFeedPair> getRemoveFeeds()
{
return removeFeeds;
}
/**
* Returns the list of feeds to update.
*
* @return feeds.
*/
public Collection<FeedPair> getUpdateFeeds()
{
return updateFeeds.values();
}
/**
* Returns the list of guides to update.
*
* @return guides.
*/
public List<GuidePair> getUpdateGuides()
{
return updateGuides;
}
/**
* Returns the list of reading lists to be added.
*
* @return added reading lists list.
*/
public List<ReadingList> getAddReadingLists()
{
return addReadingLists;
}
/**
* Returns the list of reading lists to be removed.
*
* @return removed reading lists list.
*/
public List<ReadingList> getRemoveReadingLists()
{
return removeReadingLists;
}
/**
* Registers read keys for a feed.
*
* @param localFeed feed.
* @param remoteFeed remote feed.
*/
public void addUpdateFeedsKeys(DataFeed localFeed, DataFeed remoteFeed)
{
updateFeedsKeys.put(localFeed, new FeedPair(localFeed, remoteFeed));
}
/**
* Returns the map of feeds to read articles keys.
*
* @return map of feeds to keys.
*/
public Map<DataFeed, FeedPair> getUpdateFeedsKeys()
{
return updateFeedsKeys;
}
}
/**
* Guide - Feed pair holder.
*/
static class GuideFeedPair
{
final IGuide guide;
final IFeed feed;
/**
* Creates object.
*
* @param aGuide guide.
* @param aFeed feed.
*/
public GuideFeedPair(IGuide aGuide, IFeed aFeed)
{
guide = aGuide;
feed = aFeed;
}
}
/**
* Simple pair of guides.
*/
static class GuidePair
{
final IGuide local;
final IGuide remote;
/**
* Creates holder.
*
* @param aLocal local guide.
* @param aRemote remote guide.
*/
public GuidePair(IGuide aLocal, IGuide aRemote)
{
local = aLocal;
remote = aRemote;
}
}
/**
* Simple feed pair holder.
*/
static class FeedPair
{
final IFeed local;
final IFeed remote;
/**
* Creates holder.
*
* @param aLocal local feed.
* @param aRemote remote feed.
*/
public FeedPair(IFeed aLocal, IFeed aRemote)
{
local = aLocal;
remote = aRemote;
}
}
/**
* The dialog for accepting / rejecting changes and modifying the list
* of feeds to add.
*/
private static class ChangesConfirmationDialog extends AbstractDialog
{
private final java.util.List<IFeed> addFeeds;
private final java.util.List<ReadingList> addReadingLists;
private final CheckBoxList lstAddFeeds;
private final CheckBoxList lstAddReadingLists;
/**
* Creates alert.
*
* @param frame parent frame.
* @param newLists new reading lists
* @param newFeeds new feeds.
*/
public ChangesConfirmationDialog(Frame frame, List<ReadingList> newLists, List<IFeed> newFeeds)
{
super(frame);
addFeeds = newFeeds;
lstAddFeeds = new CheckBoxList();
lstAddFeeds.setListData(FeedCheckBox.wrap(addFeeds));
addReadingLists = newLists;
lstAddReadingLists = new CheckBoxList();
lstAddReadingLists.setListData(ReadingListCheckBox.wrap(addReadingLists));
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
}
/**
* Builds dialog content.
*
* @return content.
*/
protected JComponent buildContent()
{
JPanel panel = new JPanel(new BorderLayout());
panel.add(buildBody(), BorderLayout.CENTER);
panel.add(buildButtonBarWithOKCancel(), BorderLayout.SOUTH);
return panel;
}
/**
* Creates body part.
*
* @return body part.
*/
private Component buildBody()
{
BBFormBuilder builder = new BBFormBuilder("p, 4dlu, 100dlu, 0, p");
JComponent wording = ComponentsFactory.createWrappedMultilineLabel(
Strings.message("service.sync.in.wording"));
builder.append(wording, 5);
builder.appendUnrelatedComponentsGapRow(2);
boolean feeds = addFeeds.size() > 0;
boolean lists = addReadingLists.size() > 0;
if (feeds)
{
builder.append(Strings.message("service.sync.in.confirmation.feeds.to.add"), 3,
CheckBoxList.createAllNonePanel(lstAddFeeds), 1);
builder.appendRow("50dlu:grow");
builder.append(new JScrollPane(lstAddFeeds), 5,
CellConstraints.FILL, CellConstraints.FILL);
if (lists) builder.appendUnrelatedComponentsGapRow(2);
}
if (lists)
{
builder.append(Strings.message("service.sync.in.confirmation.readinglists.to.add"), 3,
CheckBoxList.createAllNonePanel(lstAddReadingLists), 1);
builder.appendRow("50dlu:grow");
builder.append(new JScrollPane(lstAddReadingLists), 5,
CellConstraints.FILL, CellConstraints.FILL);
builder.appendUnrelatedComponentsGapRow(2);
}
return builder.getPanel();
}
/**
* Handles window events depending on the state of the <code>defaultCloseOperation</code>
* property.
*/
protected void processWindowEvent(WindowEvent e)
{
super.processWindowEvent(e);
if (e.getID() == WindowEvent.WINDOW_OPENED)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
pack();
}
});
}
}
/**
* Shows dialog and updates the list of feeds to add.
*/
public void open()
{
super.open();
if (!hasBeenCanceled())
{
addFeeds.clear();
ListModel model = lstAddFeeds.getModel();
for (int i = 0; i < model.getSize(); i++)
{
FeedCheckBox fcb = (FeedCheckBox)model.getElementAt(i);
if (fcb.isSelected()) addFeeds.add(fcb.getFeed());
}
addReadingLists.clear();
model = lstAddReadingLists.getModel();
for (int i = 0; i < model.getSize(); i++)
{
ReadingListCheckBox rlcb = (ReadingListCheckBox)model.getElementAt(i);
if (rlcb.isSelected()) addReadingLists.add(rlcb.getList());
}
}
}
}
// ---------------------------------------------------------------------------------------------
// Loading preferences
// ---------------------------------------------------------------------------------------------
/**
* Restores user preferences.
*
* @param prefs prefs.
*/
private void loadUserPreferences(Map<String, Object> prefs)
{
loadGeneralPreferences(prefs);
loadGuidesPreferences(prefs);
loadFeedsPreferences(prefs);
loadArticlesPreferences(prefs);
loadTagsPreferences(prefs);
loadReadingListsPrefereneces(prefs);
loadAdvancedPreferences(prefs);
loadTwitterPreferences(prefs);
Manager.restoreState(prefs);
}
/**
* Loads general preferences into the model.
*
* @param prefs preferences map to take info from.
*/
private void loadGeneralPreferences(Map prefs)
{
UserPreferences up = model.getUserPreferences();
// FeedRenderingSettings frs = model.getGlobalRenderingSettings();
up.setCheckingForUpdatesOnStartup(getBoolean(prefs,
UserPreferences.PROP_CHECKING_FOR_UPDATES_ON_STARTUP,
up.isCheckingForUpdatesOnStartup()));
// Disabled as we don't like what happens when synchronizing fonts across platforms
// frs.setMainContentFont(getFont(prefs,
// RenderingSettingsNames.MAIN_CONTENT_FONT,
// frs.getMainContentFont()));
up.setShowToolbar(getBoolean(prefs,
UserPreferences.PROP_SHOW_TOOLBAR,
up.isShowToolbar()));
// Behavior
up.setMarkReadWhenChangingChannels(getBoolean(prefs,
UserPreferences.PROP_MARK_READ_WHEN_CHANGING_CHANNELS,
up.isMarkReadWhenChangingChannels()));
up.setMarkReadWhenChangingGuides(getBoolean(prefs,
UserPreferences.PROP_MARK_READ_WHEN_CHANGING_GUIDES,
up.isMarkReadWhenChangingGuides()));
up.setMarkReadAfterDelay(getBoolean(prefs,
UserPreferences.PROP_MARK_READ_AFTER_DELAY,
up.isMarkReadAfterDelay()));
up.setMarkReadAfterSeconds(getInt(prefs,
UserPreferences.PROP_MARK_READ_AFTER_SECONDS,
up.getMarkReadAfterSeconds()));
// Updates and Cleanups
up.setRssPollInterval(getInt(prefs,
UserPreferences.PROP_RSS_POLL_MIN,
up.getRssPollInterval()));
up.setPurgeCount(getInt(prefs,
UserPreferences.PROP_PURGE_COUNT,
up.getPurgeCount()));
up.setPreserveUnread(getBoolean(prefs,
UserPreferences.PROP_PRESERVE_UNREAD,
up.isPreserveUnread()));
}
/**
* Loads guides preferences from the map.
*
* @param prefs preferences map.
*/
private void loadGuidesPreferences(Map prefs)
{
UserPreferences up = model.getUserPreferences();
FeedRenderingSettings frs = model.getGlobalRenderingSettings();
up.setPingOnReadingListPublication(getBoolean(prefs,
UserPreferences.PROP_PING_ON_RL_PUBLICATION,
up.isPingOnReadingListPublication()));
up.setPingOnReadingListPublicationURL(getString(prefs,
UserPreferences.PROP_PING_ON_RL_PUBLICATION_URL,
up.getPingOnReadingListPublicationURL()));
frs.setBigIconInGuides(getBoolean(prefs,
RenderingSettingsNames.IS_BIG_ICON_IN_GUIDES,
frs.isBigIconInGuides()));
frs.setShowUnreadInGuides(getBoolean(prefs,
"showUnreadInGuides",
frs.isShowUnreadInGuides()));
frs.setShowIconInGuides(getBoolean(prefs,
RenderingSettingsNames.IS_ICON_IN_GUIDES_SHOWING,
frs.isShowIconInGuides()));
frs.setShowTextInGuides(getBoolean(prefs,
RenderingSettingsNames.IS_TEXT_IN_GUIDES_SHOWING,
frs.isShowTextInGuides()));
up.setGuideSelectionMode(getInt(prefs,
UserPreferences.PROP_GUIDE_SELECTION_MODE,
up.getGuideSelectionMode()));
}
/**
* Loads feeds preferences from the map.
*
* @param prefs prefrences map.
*/
private void loadFeedsPreferences(Map prefs)
{
UserPreferences up = model.getUserPreferences();
FeedRenderingSettings frs = model.getGlobalRenderingSettings();
frs.setShowStarz(getBoolean(prefs, "showStarz", frs.isShowStarz()));
frs.setShowUnreadInFeeds(getBoolean(prefs, "showUnreadInFeeds", frs.isShowUnreadInFeeds()));
frs.setShowActivityChart(getBoolean(prefs, "showActivityChart", frs.isShowActivityChart()));
getFilterColor(prefs, FeedClass.DISABLED);
getFilterColor(prefs, FeedClass.INVALID);
getFilterColor(prefs, FeedClass.LOW_RATED);
getFilterColor(prefs, FeedClass.READ);
getFilterColor(prefs, FeedClass.UNDISCOVERED);
up.setSortingEnabled(getBoolean(prefs,
UserPreferences.PROP_SORTING_ENABLED,
up.isSortingEnabled()));
up.setSortByClass1(getInt(prefs,
UserPreferences.PROP_SORT_BY_CLASS_1,
up.getSortByClass1()));
up.setSortByClass2(getInt(prefs,
UserPreferences.PROP_SORT_BY_CLASS_2,
up.getSortByClass2()));
up.setReversedSortByClass1(getBoolean(prefs,
UserPreferences.PROP_REVERSED_SORT_BY_CLASS_1,
up.isReversedSortByClass1()));
up.setReversedSortByClass2(getBoolean(prefs,
UserPreferences.PROP_REVERSED_SORT_BY_CLASS_2,
up.isReversedSortByClass2()));
}
/**
* Loads articles preferences.
*
* @param prefs prefs.
*/
private void loadArticlesPreferences(Map prefs)
{
UserPreferences up = model.getUserPreferences();
FeedRenderingSettings frs = model.getGlobalRenderingSettings();
frs.setGroupingEnabled(getBoolean(prefs, "groupingEnabled", frs.isGroupingEnabled()));
frs.setSuppressingOlderThan(getBoolean(prefs,
"suppressingOlderThan", frs.isSuppressingOlderThan()));
frs.setDisplayingFullTitles(getBoolean(prefs,
"displayingFullTitles", frs.isDisplayingFullTitles()));
frs.setSortingAscending(getBoolean(prefs,
"sortingAscending", frs.isSortingAscending()));
frs.setSuppressOlderThan(getInt(prefs,
"suppressOlderThan", frs.getSuppressOlderThan()));
up.setCopyLinksInHrefFormat(getBoolean(prefs,
UserPreferences.PROP_COPY_LINKS_IN_HREF_FORMAT, up.isCopyLinksInHrefFormat()));
frs.setShowEmptyGroups(getBoolean(prefs, "showEmptyGroups", frs.isShowEmptyGroups()));
up.setBrowseOnDblClick(getBoolean(prefs,
UserPreferences.PROP_BROWSE_ON_DBL_CLICK, up.isBrowseOnDblClick()));
up.getViewModePreferences().restore(prefs);
up.setAutoExpandMini(getBoolean(prefs, UserPreferences.PROP_AUTO_EXPAND_MINI, up.isAutoExpandMini()));
}
/**
* Loads tags preferences from the map.
*
* @param prefs prefs.
*/
private void loadTagsPreferences(Map prefs)
{
UserPreferences up = model.getUserPreferences();
up.setTagsStorage(getInt(prefs, UserPreferences.PROP_TAGS_STORAGE, up.getTagsStorage()));
up.setTagsDeliciousUser(getString(prefs,
UserPreferences.PROP_TAGS_DELICIOUS_USER, up.getTagsDeliciousUser()));
up.setTagsDeliciousPassword(getString(prefs,
UserPreferences.PROP_TAGS_DELICIOUS_PASSWORD, up.getTagsDeliciousPassword()));
up.setTagsAutoFetch(getBoolean(prefs,
UserPreferences.PROP_TAGS_AUTOFETCH, up.isTagsAutoFetch()));
up.setPinTagging(getBoolean(prefs,
UserPreferences.PROP_PIN_TAGGING, up.isPinTagging()));
up.setPinTags(getString(prefs,
UserPreferences.PROP_PIN_TAGS, up.getPinTags()));
}
/**
* Loads reading lists preferences from the map.
*
* @param prefs preferences.
*/
private void loadReadingListsPrefereneces(Map prefs)
{
UserPreferences up = model.getUserPreferences();
up.setReadingListUpdatePeriod(getLong(prefs,
UserPreferences.PROP_READING_LIST_UPDATE_PERIOD, up.getReadingListUpdatePeriod()));
up.setOnReadingListUpdateActions(getInt(prefs,
UserPreferences.PROP_ON_READING_LIST_UPDATE_ACTIONS,
up.getOnReadingListUpdateActions()));
up.setUpdateFeeds(getBoolean(prefs, UserPreferences.PROP_UPDATE_FEEDS, up.isUpdateFeeds()));
up.setUpdateReadingLists(getBoolean(prefs,
UserPreferences.PROP_UPDATE_READING_LISTS, up.isUpdateReadingLists()));
}
/**
* Loads advanced preferences from the map.
*
* @param prefs preferences.
*/
private void loadAdvancedPreferences(Map prefs)
{
UserPreferences up = model.getUserPreferences();
StarzPreferences sp = model.getStarzPreferences();
up.setFeedSelectionDelay(getInt(prefs,
UserPreferences.PROP_FEED_SELECTION_DELAY, up.getFeedSelectionDelay()));
up.setAntiAliasText(getBoolean(prefs,
UserPreferences.PROP_AA_TEXT, up.isAntiAliasText()));
sp.setTopActivity(getInt(prefs,
StarzPreferences.PROP_TOP_ACTIVITY, sp.getTopActivity()));
sp.setTopHighlights(getInt(prefs,
StarzPreferences.PROP_TOP_HIGHLIGHTS, sp.getTopHighlights()));
up.setShowToolbarLabels(getBoolean(prefs,
UserPreferences.PROP_SHOW_TOOLBAR_LABELS, up.isShowToolbarLabels()));
up.setShowUnreadButtonMenu(getBoolean(prefs,
UserPreferences.PROP_SHOW_UNREAD_BUTTON_MENU, up.isShowUnreadButtonMenu()));
up.setFeedImportLimit(getInt(prefs,
UserPreferences.PROP_FEED_IMPORT_LIMIT, up.getFeedImportLimit()));
}
/**
* Loads twitter preferences from the map.
*
* @param prefs preferences.
*/
private void loadTwitterPreferences(Map prefs)
{
TwitterPreferences tp = model.getUserPreferences().getTwitterPreferences();
tp.setEnabled(getBoolean(prefs, TwitterPreferences.PROP_TWITTER_ENABLED, tp.isEnabled()));
tp.setScreenName(getString(prefs, TwitterPreferences.PROP_TWITTER_SCREEN_NAME, tp.getScreenName()));
tp.setAccessToken(getString(prefs, TwitterPreferences.PROP_TWITTER_ACCESS_TOKEN, tp.getAccessToken()));
tp.setTokenSecret(getString(prefs, TwitterPreferences.PROP_TWITTER_TOKEN_SECRET, tp.getTokenSecret()));
tp.setProfilePics(getBoolean(prefs, TwitterPreferences.PROP_TWITTER_PROFILE_PICS, tp.isProfilePics()));
tp.setPasteLink(getBoolean(prefs, TwitterPreferences.PROP_TWITTER_PASTE_LINK, tp.isPasteLink()));
}
/**
* Returns string value taken from the properties map.
*
* @param prefs preferences map.
* @param name name of the property.
* @param def default value.
*
* @return value.
*/
public static String getString(Map prefs, String name, String def)
{
byte[] bytes = (byte[])prefs.get(name);
return bytes == null ? def : StringUtils.fromUTF8(bytes);
}
/**
* Reads the value of color for a given feed class into the feed display mode manager.
*
* @param prefs preferences map.
* @param feedClass feed class.
*/
private static void getFilterColor(Map prefs, int feedClass)
{
String str = getString(prefs, "cdmm." + feedClass, null);
if (str != null)
{
Color color = null;
if (!StringUtils.isEmpty(str)) color = Color.decode(str);
FeedDisplayModeManager.getInstance().setColor(feedClass, color);
}
}
/**
* Returns boolean value taken from the properties map.
*
* @param prefs preferences map.
* @param name name of the property.
* @param def default value.
*
* @return value.
*/
public static boolean getBoolean(Map prefs, String name, boolean def)
{
String obj = getString(prefs, name, null);
return obj == null ? def : "true".equals(obj);
}
/**
* Returns int value taken from the properties map.
*
* @param prefs preferences map.
* @param name name of the property.
* @param def default value.
*
* @return value.
*/
public static int getInt(Map prefs, String name, int def)
{
String obj = getString(prefs, name, null);
return obj == null ? def : Integer.parseInt(obj);
}
/**
* Returns long value taken from the properties map.
*
* @param prefs preferences map.
* @param name name of the property.
* @param def default value.
*
* @return value.
*/
private static long getLong(Map prefs, String name, long def)
{
String obj = getString(prefs, name, null);
return obj == null ? def : Long.parseLong(obj);
}
}