/* ********************************************************************** **
** 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.core.internal.persist;
import org.eclipse.core.runtime.Assert;
import org.rssowl.core.persist.INews;
import org.rssowl.core.persist.INews.State;
import org.rssowl.core.persist.INewsBin.StatesUpdateInfo;
import org.rssowl.core.persist.reference.NewsReference;
import org.rssowl.core.util.Pair;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* The {@link NewsContainer} provides an array of {@link LongArrayList} that can
* be used to store news ids sorted by state.
*/
public final class NewsContainer {
private LongArrayList[] fNewsIds;
protected NewsContainer() {
super();
}
/**
* @param statesToSortedMap a {@link Map} that indicates per INews.State if
* the list of news ids should be sorted or not.
*/
public NewsContainer(Map<INews.State, Boolean> statesToSortedMap) {
int length = INews.State.values().length;
fNewsIds = new LongArrayList[length];
for (int i = 0; i < fNewsIds.length; i++) {
Boolean sort = statesToSortedMap.get(INews.State.getState(i));
if (sort != null && sort.equals(Boolean.TRUE))
fNewsIds[i] = new SortedLongArrayList(0);
else
fNewsIds[i] = new LongArrayList(0);
}
}
/**
* Should only be used for testing.
*
* @return the internal array used by this class.
*/
public LongArrayList[] internalGetNewsIds() {
return fNewsIds;
}
/**
* @param newsMap the map of news references by state to add to this container
* @return a {@link Pair} where the first boolean indicates if the container
* has changed at all and the second boolean indicates if new news have been
* added.
*/
public Pair<Boolean, Boolean> setNews(Map<INews.State, List<NewsReference>> newsMap) {
Assert.isNotNull(newsMap, "newsMap"); //$NON-NLS-1$
boolean changed = false;
boolean isNewNewsAdded = false;
Set<INews.State> statesToReset = EnumSet.allOf(INews.State.class);
statesToReset.removeAll(newsMap.keySet());
/* Reset all ArrayLists whose state is not present in newsMap */
for (INews.State state : statesToReset) {
int index = state.ordinal();
LongArrayList currentArrayList = fNewsIds[index];
if (currentArrayList.size() > 0) {
currentArrayList.clear();
changed = true;
}
}
/* For each Result */
for (Map.Entry<INews.State, List<NewsReference>> mapEntry : newsMap.entrySet()) {
List<NewsReference> news = mapEntry.getValue();
INews.State state = mapEntry.getKey();
Assert.isNotNull(news, "news"); //$NON-NLS-1$
Assert.isNotNull(state, "state"); //$NON-NLS-1$
long[] newArray = new long[news.size()];
/* Fill Bucket */
for (int i = 0; i < news.size(); i++) {
newArray[i] = news.get(i).getId();
}
int index = state.ordinal();
LongArrayList currentArrayList = fNewsIds[index];
/* New News */
if (state.equals(INews.State.NEW)) {
/* Check for added *new* News */
if (newArray.length > currentArrayList.size())
isNewNewsAdded = true;
else {
for (long value : newArray) {
if (currentArrayList.indexOf(value) < 0) {
isNewNewsAdded = true;
break;
}
}
}
if (isNewNewsAdded || (newArray.length != currentArrayList.size())) {
changed = true;
currentArrayList.setAll(newArray);
}
}
/* Any other News State */
else {
if (!currentArrayList.elementsEqual(newArray)) {
currentArrayList.setAll(newArray);
changed = true;
}
}
}
return Pair.create(changed, isNewNewsAdded);
}
/**
* @param news the news to add to this container.
*/
public void addNews(INews news) {
checkNewsIdNotNull(news);
fNewsIds[getIndex(news)].add(news.getId());
}
private int getIndex(INews news) {
return news.getState().ordinal();
}
/**
* @param news the news to be removed from this container.
* @return <code>true</code> if this element was actually contained in the
* container and <code>false</code> otherwise.
*/
public boolean removeNews(INews news) {
checkNewsIdNotNull(news);
return fNewsIds[getIndex(news)].removeByElement(news.getId());
}
/**
* @param states the news states to count
* @return the number of news for the given states.
*/
public int getNewsCount(Set<INews.State> states) {
Assert.isNotNull(states, "states"); //$NON-NLS-1$
int count = 0;
for (INews.State state : states) {
count += fNewsIds[state.ordinal()].size();
}
return count;
}
/**
* @param news the news to search for
* @return <code>true</code> if this container contains the given news and
* <code>false</code> otherwise.
*/
public boolean containsNews(INews news) {
checkNewsIdNotNull(news);
for (int i = 0, c = fNewsIds.length; i < c; ++i) {
if (fNewsIds[i].contains(news.getId()))
return true;
}
return false;
}
private void checkNewsIdNotNull(INews news) {
Assert.isNotNull(news.getId(), "news.getId()"); //$NON-NLS-1$
}
/**
* @return a {@link List} of {@link NewsReference} of all news this container
* contains.
*/
public List<NewsReference> getNews() {
return getNews(EnumSet.allOf(INews.State.class));
}
/**
* @param states the states to get news for
* @return a {@link List} of {@link NewsReference} of all news this container
* contains with the given news states.
*/
public List<NewsReference> getNews(Set<INews.State> states) {
List<NewsReference> newsRefs = new ArrayList<NewsReference>(getNewsCount(states));
for (INews.State state : states) {
int index = state.ordinal();
LongArrayList newsIds = fNewsIds[index];
for (int i = 0, c = newsIds.size(); i < c; ++i) {
long newsId = newsIds.get(i);
Assert.isLegal(newsId != 0);
newsRefs.add(new NewsReference(newsId));
}
}
return newsRefs;
}
/**
* @param states the news states to remove from this container
* @return the {@link List} of {@link NewsReference} that got removed from
* this container.
*/
public List<NewsReference> removeNews(Set<INews.State> states) {
List<NewsReference> newsRefs = getNews(states);
for (INews.State state : states) {
int index = state.ordinal();
fNewsIds[index].clear();
}
return newsRefs;
}
/**
* @param newsRefs the list of news references to remove from this container
*/
public void removeNewsRefs(List<NewsReference> newsRefs) {
for (LongArrayList list : fNewsIds) {
for (NewsReference newsRef : newsRefs)
list.removeByElement(newsRef.getId());
}
}
/**
* @param statesUpdateInfos a collection of references which have their states
* updated
* @return <code>true</code> if this news container has changed after the
* method is completed and <code>false</code> otherwise.
*/
public boolean updateNewsStates(Collection<StatesUpdateInfo> statesUpdateInfos) {
boolean changed = false;
for (StatesUpdateInfo info : statesUpdateInfos) {
long newsId = info.getNewsReference().getId();
/* Old State Present */
if (info.getOldState() == null) {
boolean itemRemoved = fNewsIds[INews.State.NEW.ordinal()].removeByElement(newsId);
if (!itemRemoved) {
EnumSet<State> remainingStates = EnumSet.allOf(INews.State.class);
remainingStates.remove(INews.State.NEW);
remainingStates.remove(info.getNewState());
for (INews.State state : remainingStates) {
if (fNewsIds[state.ordinal()].removeByElement(newsId))
itemRemoved = true;
}
}
if (itemRemoved) {
changed = true;
fNewsIds[info.getNewState().ordinal()].add(newsId);
}
}
/* No Old State Present */
else if (fNewsIds[info.getOldState().ordinal()].removeByElement(newsId)) {
changed = true;
fNewsIds[info.getNewState().ordinal()].add(newsId);
}
}
return changed;
}
}