/* ********************************************************************** **
** Copyright notice **
** **
** (c) 2005-2009 RSSOwl Development Team **
** http://www.rssowl.org/ **
** **
** All rights reserved **
** **
** This program and the accompanying materials are made available under **
** the terms of the Eclipse Public License v1.0 which accompanies this **
** distribution, and is available at: **
** http://www.rssowl.org/legal/epl-v10.html **
** **
** A copy is found in the file epl-v10.html and important notices to the **
** license from the team is found in the textfile LICENSE.txt distributed **
** in this package. **
** **
** This copyright notice MUST APPEAR in all copies of the file! **
** **
** Contributors: **
** RSSOwl Development Team - initial API and implementation **
** **
** ********************************************************************** */
package org.rssowl.ui.internal.util;
import org.eclipse.ui.PlatformUI;
import org.rssowl.core.Owl;
import org.rssowl.core.internal.InternalOwl;
import org.rssowl.core.internal.newsaction.CopyNewsAction;
import org.rssowl.core.internal.newsaction.LabelNewsAction;
import org.rssowl.core.internal.newsaction.MoveNewsAction;
import org.rssowl.core.interpreter.ITypeImporter;
import org.rssowl.core.persist.IBookMark;
import org.rssowl.core.persist.IEntity;
import org.rssowl.core.persist.IFeed;
import org.rssowl.core.persist.IFilterAction;
import org.rssowl.core.persist.IFolder;
import org.rssowl.core.persist.IFolderChild;
import org.rssowl.core.persist.ILabel;
import org.rssowl.core.persist.IMark;
import org.rssowl.core.persist.INews;
import org.rssowl.core.persist.INewsMark;
import org.rssowl.core.persist.IPreference;
import org.rssowl.core.persist.ISearch;
import org.rssowl.core.persist.ISearchCondition;
import org.rssowl.core.persist.ISearchFilter;
import org.rssowl.core.persist.ISearchMark;
import org.rssowl.core.persist.dao.DynamicDAO;
import org.rssowl.core.persist.dao.IFeedDAO;
import org.rssowl.core.persist.dao.IFolderDAO;
import org.rssowl.core.persist.dao.IPreferenceDAO;
import org.rssowl.core.persist.dao.ISearchMarkDAO;
import org.rssowl.core.persist.pref.IPreferenceScope;
import org.rssowl.core.persist.pref.IPreferenceType;
import org.rssowl.core.persist.pref.IPreferenceScope.Kind;
import org.rssowl.core.persist.reference.FeedLinkReference;
import org.rssowl.core.util.CoreUtils;
import org.rssowl.core.util.URIUtils;
import org.rssowl.ui.internal.Controller;
import org.rssowl.ui.internal.OwlUI;
import org.rssowl.ui.internal.views.explorer.BookMarkExplorer;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Helper to perform import of Feeds, Labels, Filters and Preferences including
* the ability to restore location conditions.
*
* @author bpasero
*/
public class ImportUtils {
/**
* @param target the target {@link IFolder} for the import or
* <code>null</code> if none and this is a direct import. May only be
* <code>null</code> if a {@link IFolder} is already present in the
* application or the list of elements to import contains a bookmark set
* itself.
* @param elements the list of elements to import. Supported types are
* {@link IFolderChild}, {@link ILabel}, {@link ISearchFilter} and
* {@link IPreference}.
* @param checkExistingFeeds if <code>true</code>, the method will first check
* if a feed with a given URL exists before creating it. set to
* <code>false</code> to avoid this and improve import performance (e.g. when
* doing an initial import into an empty RSSOwl instance).
*/
public static void doImport(IFolder target, List<? extends IEntity> elements, boolean checkExistingFeeds) {
List<IFolderChild> folderChilds = new ArrayList<IFolderChild>();
List<ILabel> labels = new ArrayList<ILabel>();
List<ISearchFilter> filters = new ArrayList<ISearchFilter>();
List<IPreference> preferences = new ArrayList<IPreference>();
if (elements != null) {
for (IEntity entity : elements) {
if (entity instanceof IFolderChild)
folderChilds.add((IFolderChild) entity);
else if (entity instanceof ILabel)
labels.add((ILabel) entity);
else if (entity instanceof ISearchFilter)
filters.add((ISearchFilter) entity);
else if (entity instanceof IPreference)
preferences.add((IPreference) entity);
}
}
doImport(target, folderChilds, labels, filters, preferences, checkExistingFeeds);
}
/**
* @param target the target {@link IFolder} for the import or
* <code>null</code> if none and this is a direct import. May only be
* <code>null</code> if a {@link IFolder} is already present in the
* application or the list of elements to import contains a bookmark set
* itself.
* @param elements the list of {@link IFolderChild} to import.
* @param labels the list of {@link ILabel} to import.
* @param filters the list of {@link ISearchFilter} to import.
* @param preferences the list of {@link IPreference} to import.
* @param checkExistingFeeds if <code>true</code>, the method will first check
* if a feed with a given URL exists before creating it. set to
* <code>false</code> to avoid this and improve import performance (e.g. when
* doing an initial import into an empty RSSOwl instance).
*/
public static void doImport(IFolder target, List<IFolderChild> elements, List<ILabel> labels, List<ISearchFilter> filters, List<IPreference> preferences, boolean checkExistingFeeds) {
/* Map Old Id to IFolderChild */
Map<Long, IFolderChild> mapOldIdToFolderChild = createOldIdToEntityMap(elements);
/* Load SearchMarks containing location condition */
List<ISearchMark> locationConditionSavedSearches = getLocationConditionSavedSearches(elements);
/* Look for Feeds in Elements and Save them if required */
IFeedDAO feedDao = DynamicDAO.getDAO(IFeedDAO.class);
List<URI> feedsCreated = new ArrayList<URI>();
for (IFolderChild element : elements) {
saveFeedsOfBookmarks(feedsCreated, element, feedDao, checkExistingFeeds);
}
/* Direct Import */
if (target == null)
doDirectImport(elements, mapOldIdToFolderChild);
/* Import to target */
else
doImportToTarget(target, elements, mapOldIdToFolderChild);
/* Fix locations in Search Marks if required and save */
if (!locationConditionSavedSearches.isEmpty()) {
updateLocationConditions(mapOldIdToFolderChild, locationConditionSavedSearches);
DynamicDAO.getDAO(ISearchMarkDAO.class).saveAll(locationConditionSavedSearches);
}
/* Import Labels */
boolean fixLabelOrder = false;
Map<String, ILabel> mapExistingLabelToName = new HashMap<String, ILabel>();
Map<Long, ILabel> mapOldIdToImportedLabel = new HashMap<Long, ILabel>();
if (labels != null && !labels.isEmpty()) {
Collection<ILabel> existingLabels = DynamicDAO.loadAll(ILabel.class);
for (ILabel existingLabel : existingLabels) {
mapExistingLabelToName.put(existingLabel.getName(), existingLabel);
}
for (ILabel importedLabel : labels) {
Object oldIdValue = importedLabel.getProperty(ITypeImporter.ID_KEY);
if (oldIdValue != null && oldIdValue instanceof Long)
mapOldIdToImportedLabel.put((Long) oldIdValue, importedLabel);
}
for (ILabel importedLabel : labels) {
ILabel existingLabel = mapExistingLabelToName.get(importedLabel.getName());
/* Update Existing */
if (existingLabel != null) {
existingLabel.setColor(importedLabel.getColor());
if (existingLabel.getOrder() != importedLabel.getOrder())
fixLabelOrder = true;
existingLabel.setOrder(importedLabel.getOrder());
DynamicDAO.save(existingLabel);
}
/* Save as New */
else {
importedLabel.removeProperty(ITypeImporter.ID_KEY);
DynamicDAO.save(importedLabel);
fixLabelOrder = true;
}
}
/* Fix Order to be a sequence again */
if (fixLabelOrder && !existingLabels.isEmpty()) {
Set<ILabel> sortedLabels = CoreUtils.loadSortedLabels();
int index = 0;
for (Iterator<?> iterator = sortedLabels.iterator(); iterator.hasNext();) {
ILabel label = (ILabel) iterator.next();
label.setOrder(index);
index++;
}
DynamicDAO.saveAll(sortedLabels);
}
}
/* Import Filters */
if (filters != null && !filters.isEmpty()) {
int existingFiltersCount = DynamicDAO.loadAll(ISearchFilter.class).size();
/* Fix locations in Searches if required */
List<ISearch> locationConditionSearches = getLocationConditionSearchesFromFilters(filters);
if (!locationConditionSearches.isEmpty())
updateLocationConditions(mapOldIdToFolderChild, locationConditionSearches);
/* Fix locations in Actions if required */
for (ISearchFilter filter : filters) {
List<IFilterAction> actions = filter.getActions();
for (IFilterAction action : actions) {
if (MoveNewsAction.ID.equals(action.getActionId()) || CopyNewsAction.ID.equals(action.getActionId())) {
Object data = action.getData();
if (data != null && data instanceof Long[]) {
Long[] oldBinLocations = (Long[]) data;
List<Long> newBinLocations = new ArrayList<Long>(oldBinLocations.length);
for (int i = 0; i < oldBinLocations.length; i++) {
Long oldLocation = oldBinLocations[i];
if (mapOldIdToFolderChild.containsKey(oldLocation)) {
IFolderChild location = mapOldIdToFolderChild.get(oldLocation);
newBinLocations.add(location.getId());
}
}
action.setData(newBinLocations.toArray(new Long[newBinLocations.size()]));
}
}
}
}
/* Fix labels in Actions if required */
for (ISearchFilter filter : filters) {
List<IFilterAction> actions = filter.getActions();
for (IFilterAction action : actions) {
if (LabelNewsAction.ID.equals(action.getActionId())) {
Object data = action.getData();
if (data != null && data instanceof Long) {
ILabel label = mapOldIdToImportedLabel.get(data);
if (label != null) {
String name = label.getName();
ILabel existingLabel = mapExistingLabelToName.get(name);
if (existingLabel != null)
action.setData(existingLabel.getId());
else
action.setData(label.getId());
}
}
}
}
}
/* Fix Order */
for (ISearchFilter filter : filters) {
filter.setOrder(filter.getOrder() + existingFiltersCount);
}
/* Save */
DynamicDAO.saveAll(filters);
}
/* Import Preferences */
if (preferences != null && !preferences.isEmpty()) {
IPreferenceScope globalPreferences = Owl.getPreferenceService().getGlobalScope();
IPreferenceScope eclipsePreferences = Owl.getPreferenceService().getEclipseScope();
boolean flushEclipsePreferences = false;
for (IPreference preference : preferences) {
Object data = preference.getProperty(ITypeImporter.DATA_KEY);
if (data != null && data instanceof Object[] && ((Object[]) data).length == 2) {
IPreferenceScope.Kind kind = (IPreferenceScope.Kind) ((Object[]) data)[0];
IPreferenceType type = (IPreferenceType) ((Object[]) data)[1];
IPreferenceScope scope = (kind == Kind.GLOBAL) ? globalPreferences : eclipsePreferences;
if (kind == Kind.ECLIPSE)
flushEclipsePreferences = true;
switch (type) {
case BOOLEAN:
scope.putBoolean(preference.getKey(), preference.getBoolean());
break;
case INTEGER:
scope.putInteger(preference.getKey(), preference.getInteger());
break;
case INTEGERS:
scope.putIntegers(preference.getKey(), preference.getIntegers());
break;
case LONG:
scope.putLong(preference.getKey(), preference.getLong());
break;
case LONGS:
scope.putLongs(preference.getKey(), preference.getLongs());
break;
case STRING:
scope.putString(preference.getKey(), preference.getString());
break;
case STRINGS:
scope.putStrings(preference.getKey(), preference.getStrings());
break;
}
}
}
/* Flush Eclipse preferences if required */
if (flushEclipsePreferences)
eclipsePreferences.flush();
}
}
private static void saveFeedsOfBookmarks(List<URI> feedsCreated, IFolderChild element, IFeedDAO feedDao, boolean checkExistingFeeds) {
/* Bookmark */
if (element instanceof IBookMark) {
IBookMark bm = (IBookMark) element;
FeedLinkReference feedReference = bm.getFeedLinkReference();
/* Check for Existing Feed if set */
boolean feedExists = feedsCreated.contains(feedReference.getLink());
if (!feedExists && checkExistingFeeds)
feedExists = feedDao.loadReference(feedReference.getLink()) != null;
/* Create a new Feed if necessary */
if (!feedExists) {
IFeed feed = Owl.getModelFactory().createFeed(null, feedReference.getLink());
Object homepage = bm.getProperty(ITypeImporter.HOMEPAGE_KEY);
if (homepage != null && homepage instanceof String)
feed.setHomepage(URIUtils.createURI((String) homepage));
Object description = bm.getProperty(ITypeImporter.DESCRIPTION_KEY);
if (description != null && description instanceof String)
feed.setDescription((String) description);
feedDao.save(feed);
feedsCreated.add(feedReference.getLink());
}
bm.removeProperty(ITypeImporter.DESCRIPTION_KEY);
bm.removeProperty(ITypeImporter.HOMEPAGE_KEY);
}
/* Folder */
else if (element instanceof IFolder) {
IFolder folder = (IFolder) element;
List<IFolderChild> children = folder.getChildren();
for (IFolderChild child : children) {
saveFeedsOfBookmarks(feedsCreated, child, feedDao, checkExistingFeeds);
}
}
}
private static void doDirectImport(List<IFolderChild> elements, Map<Long, IFolderChild> mapOldIdToFolderChild) {
IPreferenceDAO prefsDAO = Owl.getPersistenceService().getDAOService().getPreferencesDAO();
IFolderDAO folderDAO = DynamicDAO.getDAO(IFolderDAO.class);
List<IFolder> foldersToSave = new ArrayList<IFolder>();
Set<IFolder> rootFolders = CoreUtils.loadRootFolders();
/* Load the current selected Set as Location if necessary */
IFolder selectedSet = null;
if (!rootFolders.isEmpty()) {
if (!InternalOwl.TESTING && PlatformUI.isWorkbenchRunning() && Controller.getDefault().isStarted()) {
String selectedBookMarkSetPref = BookMarkExplorer.getSelectedBookMarkSetPref(OwlUI.getWindow());
Long selectedFolderID = prefsDAO.load(selectedBookMarkSetPref).getLong();
selectedSet = folderDAO.load(selectedFolderID);
} else
selectedSet = rootFolders.iterator().next();
}
/* Import Elements */
for (IFolderChild element : elements) {
/* Folder */
if (element instanceof IFolder) {
IFolder folder = (IFolder) element;
/* Bookmark Set */
if (folder.getParent() == null) {
/* Default Bookmark Set */
if (folder.getProperty(ITypeImporter.TEMPORARY_FOLDER) != null && selectedSet != null) {
/* Reparent Childs into selected set */
reparent(folder, selectedSet);
foldersToSave.add(selectedSet);
/* Also Update Mapping if necessary */
if (folder.getProperty(ITypeImporter.ID_KEY) != null)
mapOldIdToFolderChild.put((Long) folder.getProperty(ITypeImporter.ID_KEY), selectedSet);
}
/* Any other Bookmark Set */
else {
/* Check if set already exists */
IFolder existingSetFolder = null;
for (IFolder rootFolder : rootFolders) {
if (rootFolder.getName().equals(folder.getName())) {
existingSetFolder = rootFolder;
break;
}
}
/* Reparent into Existing Set */
if (existingSetFolder != null) {
reparent(folder, existingSetFolder);
foldersToSave.add(existingSetFolder);
/* Also Update Mapping if necessary */
if (folder.getProperty(ITypeImporter.ID_KEY) != null)
mapOldIdToFolderChild.put((Long) folder.getProperty(ITypeImporter.ID_KEY), existingSetFolder);
}
/* Otherwise save as new Set */
else {
foldersToSave.add(folder);
}
}
}
/* Normal Folder */
else if (selectedSet != null) {
folder.setParent(selectedSet);
selectedSet.addFolder(folder, null, null);
foldersToSave.add(selectedSet);
}
}
/* Any Newsmark */
else if (element instanceof INewsMark && selectedSet != null) {
INewsMark mark = (INewsMark) element;
mark.setParent(selectedSet);
selectedSet.addMark(mark, null, null);
foldersToSave.add(selectedSet);
}
}
/* Remove Duplicates (using object identity) */
foldersToSave = CoreUtils.removeIdentityDuplicates(foldersToSave);
/* Un-set ID Property prior Save */
for (IFolder folderToSave : foldersToSave) {
unsetIdProperty(folderToSave);
}
/* Save Folders that have changed */
DynamicDAO.saveAll(foldersToSave);
}
private static void doImportToTarget(IFolder target, List<IFolderChild> elements, Map<Long, IFolderChild> mapOldIdToFolderChild) {
List<IFolder> foldersToSave = new ArrayList<IFolder>();
/* Import Elements */
for (IFolderChild element : elements) {
/* Folder */
if (element instanceof IFolder) {
IFolder folder = (IFolder) element;
/* Bookmark Set */
if (folder.getParent() == null) {
/* Default Bookmark Set */
if (folder.getProperty(ITypeImporter.TEMPORARY_FOLDER) != null) {
/* Reparent Childs into selected target */
reparent(folder, target);
foldersToSave.add(target);
/* Also Update Mapping if necessary */
if (folder.getProperty(ITypeImporter.ID_KEY) != null)
mapOldIdToFolderChild.put((Long) folder.getProperty(ITypeImporter.ID_KEY), target);
}
/* Any other Bookmark Set */
else {
folder.setParent(target);
target.addFolder(folder, null, null);
foldersToSave.add(target);
}
}
/* Normal Folder */
else {
folder.setParent(target);
target.addFolder(folder, null, null);
foldersToSave.add(target);
}
}
/* Any Newsmark */
else if (element instanceof INewsMark) {
INewsMark mark = (INewsMark) element;
mark.setParent(target);
target.addMark(mark, null, null);
foldersToSave.add(target);
}
}
/* Remove Duplicates (using object identity) */
foldersToSave = CoreUtils.removeIdentityDuplicates(foldersToSave);
/* Un-set ID Property prior Save */
for (IFolder folderToSave : foldersToSave) {
unsetIdProperty(folderToSave);
}
/* Save Folders that have changed */
DynamicDAO.saveAll(foldersToSave);
}
private static void reparent(IFolder from, IFolder to) {
List<IFolderChild> children = from.getChildren();
for (IFolderChild child : children) {
/* Reparent Folder */
if (child instanceof IFolder) {
IFolder folder = (IFolder) child;
folder.setParent(to);
to.addFolder(folder, null, null);
}
/* Reparent Mark */
else if (child instanceof IMark) {
IMark mark = (IMark) child;
mark.setParent(to);
to.addMark(mark, null, null);
}
}
}
private static void updateLocationConditions(Map<Long, IFolderChild> oldIdToFolderChildMap, List<? extends ISearch> searches) {
for (ISearch search : searches) {
List<ISearchCondition> conditions = search.getSearchConditions();
for (ISearchCondition condition : conditions) {
/* Location Condition */
if (condition.getField().getId() == INews.LOCATION && condition.getValue() != null) {
Long[][] value = (Long[][]) condition.getValue();
List<IFolderChild> newLocations = new ArrayList<IFolderChild>();
/* Folders */
for (int i = 0; value[CoreUtils.FOLDER] != null && i < value[CoreUtils.FOLDER].length; i++) {
if (value[CoreUtils.FOLDER][i] != null && value[CoreUtils.FOLDER][i] != 0) {
Long id = value[CoreUtils.FOLDER][i];
if (oldIdToFolderChildMap.containsKey(id))
newLocations.add(oldIdToFolderChildMap.get(id));
}
}
/* BookMarks */
for (int i = 0; value[CoreUtils.BOOKMARK] != null && i < value[CoreUtils.BOOKMARK].length; i++) {
if (value[CoreUtils.BOOKMARK][i] != null && value[CoreUtils.BOOKMARK][i] != 0) {
Long id = value[CoreUtils.BOOKMARK][i];
if (oldIdToFolderChildMap.containsKey(id))
newLocations.add(oldIdToFolderChildMap.get(id));
}
}
/* NewsBins */
if (value.length == 3) {
for (int i = 0; value[CoreUtils.NEWSBIN] != null && i < value[CoreUtils.NEWSBIN].length; i++) {
if (value[CoreUtils.NEWSBIN][i] != null && value[CoreUtils.NEWSBIN][i] != 0) {
Long id = value[CoreUtils.NEWSBIN][i];
if (oldIdToFolderChildMap.containsKey(id))
newLocations.add(oldIdToFolderChildMap.get(id));
}
}
}
/* Update */
condition.setValue(ModelUtils.toPrimitive(newLocations));
}
}
}
}
private static void unsetIdProperty(IEntity entity) {
entity.removeProperty(ITypeImporter.ID_KEY);
if (entity instanceof IFolder) {
IFolder folder = (IFolder) entity;
List<IFolderChild> children = folder.getChildren();
for (IFolderChild child : children) {
unsetIdProperty(child);
}
}
}
private static List<ISearchMark> getLocationConditionSavedSearches(List<? extends IEntity> types) {
List<ISearchMark> locationConditionSavedSearches = new ArrayList<ISearchMark>();
for (IEntity entity : types)
fillLocationConditionSavedSearches(locationConditionSavedSearches, entity);
return locationConditionSavedSearches;
}
private static List<ISearch> getLocationConditionSearchesFromFilters(List<ISearchFilter> filters) {
List<ISearch> locationConditionSearches = new ArrayList<ISearch>();
for (ISearchFilter filter : filters) {
ISearch search = filter.getSearch();
if (search != null && containsLocationCondition(search))
locationConditionSearches.add(search);
}
return locationConditionSearches;
}
private static void fillLocationConditionSavedSearches(List<ISearchMark> searchmarks, IEntity entity) {
if (entity instanceof ISearchMark && containsLocationCondition((ISearchMark) entity)) {
searchmarks.add((ISearchMark) entity);
} else if (entity instanceof IFolder) {
IFolder folder = (IFolder) entity;
List<IFolderChild> children = folder.getChildren();
for (IFolderChild child : children) {
fillLocationConditionSavedSearches(searchmarks, child);
}
}
}
private static boolean containsLocationCondition(ISearch search) {
List<ISearchCondition> searchConditions = search.getSearchConditions();
for (ISearchCondition condition : searchConditions) {
if (condition.getField().getId() == INews.LOCATION)
return true;
}
return false;
}
private static Map<Long, IFolderChild> createOldIdToEntityMap(List<? extends IEntity> types) {
Map<Long, IFolderChild> oldIdToEntityMap = new HashMap<Long, IFolderChild>();
for (IEntity entity : types) {
if (entity instanceof IFolderChild)
fillOldIdToEntityMap(oldIdToEntityMap, (IFolderChild) entity);
}
return oldIdToEntityMap;
}
private static void fillOldIdToEntityMap(Map<Long, IFolderChild> oldIdToEntityMap, IFolderChild folderChild) {
Long oldId = (Long) folderChild.getProperty(ITypeImporter.ID_KEY);
if (oldId != null)
oldIdToEntityMap.put(oldId, folderChild);
if (folderChild instanceof IFolder) {
IFolder folder = (IFolder) folderChild;
List<IFolderChild> children = folder.getChildren();
for (IFolderChild child : children) {
fillOldIdToEntityMap(oldIdToEntityMap, child);
}
}
}
}