Package org.rssowl.core.model.internal.db4o

Source Code of org.rssowl.core.model.internal.db4o.EventManager

/*   **********************************************************************  **
**   Copyright notice                                                       **
**                                                                          **
**   (c) 2005-2006 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.model.internal.db4o;

import org.rssowl.core.model.NewsModel;
import org.rssowl.core.model.dao.IDGenerator;
import org.rssowl.core.model.dao.IModelDAO;
import org.rssowl.core.model.events.AttachmentEvent;
import org.rssowl.core.model.events.BookMarkEvent;
import org.rssowl.core.model.events.CategoryEvent;
import org.rssowl.core.model.events.EventsMap;
import org.rssowl.core.model.events.FeedEvent;
import org.rssowl.core.model.events.FolderEvent;
import org.rssowl.core.model.events.LabelEvent;
import org.rssowl.core.model.events.ModelEvent;
import org.rssowl.core.model.events.NewsEvent;
import org.rssowl.core.model.events.PersonEvent;
import org.rssowl.core.model.events.SearchConditionEvent;
import org.rssowl.core.model.events.SearchMarkEvent;
import org.rssowl.core.model.internal.types.BookMark;
import org.rssowl.core.model.internal.types.Feed;
import org.rssowl.core.model.search.ISearchCondition;
import org.rssowl.core.model.types.IAttachment;
import org.rssowl.core.model.types.IBookMark;
import org.rssowl.core.model.types.ICategory;
import org.rssowl.core.model.types.IConditionalGet;
import org.rssowl.core.model.types.IEntity;
import org.rssowl.core.model.types.IFeed;
import org.rssowl.core.model.types.IFolder;
import org.rssowl.core.model.types.ILabel;
import org.rssowl.core.model.types.IMark;
import org.rssowl.core.model.types.INews;
import org.rssowl.core.model.types.IPerson;
import org.rssowl.core.model.types.ISearchMark;

import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;
import com.db4o.events.Event4;
import com.db4o.events.EventArgs;
import com.db4o.events.EventListener4;
import com.db4o.events.EventRegistry;
import com.db4o.events.EventRegistryFactory;
import com.db4o.events.ObjectEventArgs;
import com.db4o.query.Query;

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
*
*/
public class EventManager {
 
  /**
   * Iterator implementation that iterates from the end of the list. Useful
   * if items need to be removed from the list during iteration and specific
   * method needs to be called for removal.
   */
  private final static class ReverseIterator<T> implements Iterable<T>, Iterator<T> {
    private final List<T> fList;
    private int index;
   
    static <T>ReverseIterator<T> createInstance(List<T> list) {
      return new ReverseIterator<T>(list);
    }
   
    private ReverseIterator(List<T> list)    {
      fList = list;
      index = list.size() - 1;
    }

    public final Iterator<T> iterator() {
      return this;
    }

    public final boolean hasNext() {
      return index > -1;
    }

    public final T next() {
      return fList.get(index--);
    }

    public final void remove() {
      throw new UnsupportedOperationException();
    }
   
  }
 
  private final ThreadLocal<Set<IEntity>> fItemsBeingDeleted = new ThreadLocal<Set<IEntity>>();
  private static final String PARENT_DELETED_KEY = "rssowl.db4o.EventManager.parentDeleted"; //$NON-NLS-1$
  private final static EventManager INSTANCE = new EventManager();
  private ObjectContainer fDb;
 
  private EventManager() {
    initEntityStoreListener();
  }

  private void initEventRegistry() {
    EventRegistry eventRegistry = EventRegistryFactory.forObjectContainer(fDb);
   
    EventListener4 updatedListener = new EventListener4() {
      public void onEvent(Event4 e, EventArgs args) {
        processUpdatedEvent(args);
      }
    };
    EventListener4 creatingListener = new EventListener4() {
      public void onEvent(Event4 e, EventArgs args) {
        processCreatingEvent(args);
      }
    };
    EventListener4 createdListener = new EventListener4() {
      public void onEvent(Event4 e, EventArgs args) {
        processCreatedEvent(args);
      }
    };
    //TODO If we don't use this for anything by the time we merge db4o branch
    //into HEAD, then delete it
//    EventListener4 activatedListener = new EventListener4() {
//      public void onEvent(Event4 e, EventArgs args) {
//        processActivatedEvent(args);
//      }
//    };
   
    EventListener4 deletingListener = new EventListener4() {
      public void onEvent(Event4 e, EventArgs args) {
        processDeletingEvent(args);
      }
    };
   
    EventListener4 deletedListener = new EventListener4() {
      public void onEvent(Event4 e, EventArgs args) {
        processDeletedEvent(args);
      }
    };
   
    eventRegistry.created().addListener(createdListener);
    eventRegistry.creating().addListener(creatingListener);
    eventRegistry.updated().addListener(updatedListener);
//    eventRegistry.activated().addListener(activatedListener);
    eventRegistry.deleting().addListener(deletingListener);
    eventRegistry.deleted().addListener(deletedListener);
  }

  private void processUpdatedEvent(EventArgs args) {
    IEntity entity = getEntity(args);
    if (entity == null)
      return;
   
    ModelEvent event = createModelEvent(entity);
    if (event != null)
      EventsMap.getInstance().putUpdateEvent(event);
   
  }
 
  private void processCreatingEvent(EventArgs args) {
    IEntity entity = getEntity(args);
    if (entity != null)
      setId(entity);
  }

  private void processCreatedEvent(EventArgs args) {
    IEntity entity = getEntity(args);
    if (entity == null)
      return;

    ModelEvent event = createModelEvent(entity);
    if (event != null)
      EventsMap.getInstance().putPersistEvent(event);
  }
 
  private void processDeletingEvent(EventArgs args) {
    IEntity entity = getEntity(args);
    if (entity == null)
      return;
   
    if (entity instanceof INews)
      cascadeNewsDeletion((INews) entity);
    else if (entity instanceof IFeed)
      cascadeFeedDeletion((IFeed) entity);
    else if (entity instanceof IMark)
      cascadeMarkDeletion((IMark) entity);
    else if (entity instanceof IFolder)
      removeFromParentFolderAndCascade((IFolder) entity);
    else if (entity instanceof IAttachment)
      removeFromParentNews((IAttachment) entity);
  }

  private void cascadeNewsDeletion(INews news) {
    removeFromParentFeed(news);
   
    fDb.delete(news.getGuid());
    fDb.delete(news.getSource());
    fDb.delete(news.getAuthor());
   
    for (ICategory category : ReverseIterator.createInstance(news.getCategories())) {
      fDb.delete(category);
    }
    for (IAttachment attachment : ReverseIterator.createInstance(news.getAttachments())) {
      fDb.delete(attachment);
    }
  }

  private void cascadeMarkDeletion(IMark mark) {
    removeFromParentFolder(mark);
    if (mark instanceof IBookMark)
      deleteFeedIfNecessary((IBookMark) mark);
    else if (mark instanceof ISearchMark)
      cascadeSearchMarkDeletion((ISearchMark) mark);
  }

  private void cascadeSearchMarkDeletion(ISearchMark mark) {
    //TODO Need to implement the cascading for missing elements and remove
    //back-reference to ISearchMark. Do we need that?
    for (ISearchCondition condition : mark.getSearchConditions())   {
      fDb.delete(condition);
    }
  }
 
  private void cascadeFeedDeletion(IFeed feed) {
    addItemBeingDeleted(feed);
    fDb.delete(feed.getImage());
    fDb.delete(feed.getAuthor());
    for (ICategory category : ReverseIterator.createInstance(feed.getCategories())) {
      fDb.delete(category);
    }
    for (INews news : ReverseIterator.createInstance(feed.getNews())) {
      fDb.delete(news);
    }
    IModelDAO dao = NewsModel.getDefault().getPersistenceLayer().getModelDAO();
    IConditionalGet conditionalGet = dao.loadConditionalGet(feed.getLink());
    if (conditionalGet != null)
      fDb.delete(conditionalGet);
   
    removeFromItemsBeingDeleted(feed);
  }

  private void removeFromParentNews(IAttachment attachment) {
    INews news = attachment.getNews();
    news.removeAttachment(attachment);
    fDb.set(news);
  }
 
  private void removeFromParentFolderAndCascade(IFolder folder) {
    IFolder parentFolder = folder.getParent();
    if (parentFolder != null) {
      parentFolder.removeFolder(folder);
      fDb.set(parentFolder);
    }
    for (IFolder child : ReverseIterator.createInstance(folder.getFolders())) {
      cascadeFolderDeletion(child);
    }
   
    for (IMark mark : ReverseIterator.createInstance(folder.getMarks())) {
      mark.setProperty(PARENT_DELETED_KEY, true);
      fDb.delete(mark);
    }
  }

  private void cascadeFolderDeletion(IFolder folder) {
   
    for (IFolder child : ReverseIterator.createInstance(folder.getFolders())) {
      cascadeFolderDeletion(child);
    }
   
    for (IMark mark : ReverseIterator.createInstance(folder.getMarks())) {
      mark.setProperty(PARENT_DELETED_KEY, true);
      fDb.delete(mark);
    }
   
    folder.setParent(null);

    fDb.delete(folder);
  }

  private void removeFromParentFolder(IMark mark) {
    IFolder parentFolder = mark.getFolder();
    parentFolder.removeMark(mark);
    if (mark.getProperty(PARENT_DELETED_KEY) == null)
      fDb.set(parentFolder);
    else {
      mark.removeProperty(PARENT_DELETED_KEY);
    }
  }

  private void removeFromParentFeed(INews news) {
    IFeed feed = news.getFeedReference().resolve();
    if (itemsBeingDeletedContains(feed))
      return;
   
    /* If the news was still within parent, update parent */
    if (feed.removeNews(news))
      fDb.ext().set(feed, 2);
}
 
  private boolean removeFromItemsBeingDeleted(IEntity entity) {
    Set<IEntity> entities = fItemsBeingDeleted.get();
    if (entities == null)
      return false;
   
    return entities.remove(entity);
  }

  private boolean itemsBeingDeletedContains(IEntity entity) {
    Set<IEntity> entities = fItemsBeingDeleted.get();
    if (entities == null)
      return false;
   
    return entities.contains(entity);
  }

  private void deleteFeedIfNecessary(IBookMark mark) {
    Query query = fDb.query();
    query.constrain(Feed.class);
    query.descend("fId").constrain(mark.getFeedReference().getId()); //$NON-NLS-1$
    @SuppressWarnings("unchecked")
    ObjectSet<IFeed> feeds = query.execute();
    for (IFeed feed : feeds) {
      if(onlyBookMarkReference(feed)) {
        fDb.delete(feed);
      }
    }
  }

  private boolean onlyBookMarkReference(IFeed feed) {
    Query query = fDb.query();
    query.constrain(BookMark.class);
    query.descend("fFeedId").constrain(feed.getId().longValue()); //$NON-NLS-1$
   
    @SuppressWarnings("unchecked")
    ObjectSet<IBookMark> marks = query.execute();

    if (marks.size() == 1) {
      return true;
    }
    return false;
  }

  private void processDeletedEvent(EventArgs args) {
    IEntity entity = getEntity(args);
    if (entity == null)
      return;
   
    ModelEvent event = createModelEvent(entity);
    if (event != null)
      EventsMap.getInstance().putRemoveEvent(event);
  }

  private IEntity getEntity(EventArgs args) {
    ObjectEventArgs queryArgs = ((ObjectEventArgs) args);
    Object o = queryArgs.object();
    if (o instanceof IEntity) {
      IEntity entity = (IEntity) o;
      return entity;
    }
    return null;
  }

  private ModelEvent createModelEvent(IEntity entity) {
    ModelEvent modelEvent = null;
    Map<Integer, ModelEvent> templatesMap = EventsMap.getInstance().getEventTemplatesMap();
    ModelEvent template = templatesMap.get(System.identityHashCode(entity));
    //TODO In some cases, the template is complete. We can save some object allocation
    //by reusing it.
   
    boolean root = isRoot(template);
    if (entity instanceof INews) {
      modelEvent = createNewsEvent((INews) entity, template, root);
    }
    else if (entity instanceof IAttachment) {
      IAttachment attachment = (IAttachment) entity;
      modelEvent = new AttachmentEvent(attachment, root);
    }
    else if (entity instanceof ICategory) {
      ICategory category = (ICategory) entity;
      modelEvent = new CategoryEvent(category, root);
    }
    else if (entity instanceof IFeed) {
      IFeed feed = (IFeed) entity;
      modelEvent = new FeedEvent(feed, root);
    }
    else if (entity instanceof IPerson) {
      IPerson person = (IPerson) entity;
      modelEvent = new PersonEvent(person, root);
    }
    else if (entity instanceof IBookMark) {
      IBookMark mark = (IBookMark) entity;
      BookMarkEvent eventTemplate = (BookMarkEvent) template;
      IFolder oldParent = eventTemplate == null ? null : eventTemplate.getOldParent();
      modelEvent = new BookMarkEvent(mark, oldParent, root);
    }
    else if (entity instanceof ISearchMark) {
      ISearchMark mark = (ISearchMark) entity;
      SearchMarkEvent eventTemplate = (SearchMarkEvent) template;
      IFolder oldParent = eventTemplate == null ? null : eventTemplate.getOldParent();
      modelEvent = new SearchMarkEvent(mark, oldParent, root);
    }
    else if (entity instanceof IFolder) {
      IFolder folder = (IFolder) entity;
      FolderEvent eventTemplate = (FolderEvent) template;
      IFolder oldParent = eventTemplate == null ? null : eventTemplate.getOldParent();
      modelEvent = new FolderEvent(folder, oldParent, root);
    }
    else if (entity instanceof ILabel) {
      ILabel label = (ILabel) entity;
      modelEvent = new LabelEvent(label, root);
    }
    else if (entity instanceof ISearchCondition) {
      ISearchCondition searchCond = (ISearchCondition) entity;
      modelEvent = new SearchConditionEvent(searchCond, root);
    }
    return modelEvent;
  }

  private ModelEvent createNewsEvent(INews news, ModelEvent template, boolean root) {
    ModelEvent modelEvent;
    NewsEvent newsTemplate = (NewsEvent) template;
    INews oldNews = newsTemplate == null ? null : newsTemplate.getOldNews();
   
    modelEvent = new NewsEvent(oldNews, news, root);
    return modelEvent;
  }

  private boolean isRoot(ModelEvent template) {
    if (template == null)
      return false;
   
    return template.isRoot();
  }

  private void setId(IEntity entity) {
    if (entity.getId() == null) {
      IDGenerator idGenerator = NewsModel.getDefault().getPersistenceLayer().getIDGenerator();
      long id;
     
      if (idGenerator instanceof DB4OIDGenerator)
        id = ((DB4OIDGenerator) idGenerator).getNext(false);
      else
        id = idGenerator.getNext();
     
      entity.setId(id);
    }
  }

  private void initEntityStoreListener() {
    DBManager.getDefault().addEntityStoreListener(new DatabaseListener() {
      public void databaseOpened(DatabaseEvent event) {
        fDb = event.getObjectContainer();
        initEventRegistry();
      }
      public void databaseClosed(DatabaseEvent event) {
        fDb = null;
      }
    });
  }
 
  private void addItemBeingDeleted(IEntity entity) {
    Set<IEntity> entities = fItemsBeingDeleted.get();
    if (entities == null)
      entities = new HashSet<IEntity>(3);
   
    entities.add(entity);
  }
 
  /**
   * Clears any temporary storage used by the EventManager for the thread-bound
   * transaction.
   */
  public void clear() {
    fItemsBeingDeleted.set(null);
  }
 
  /**
   * @return singleton instance
   */
  public final static EventManager getInstance() {
    return INSTANCE;
  }

}
TOP

Related Classes of org.rssowl.core.model.internal.db4o.EventManager

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.