All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.tinymediamanager.ui.tvshows.TvShowTreeDataProvider Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2012 - 2019 Manuel Laggner
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.tinymediamanager.ui.tvshows;

import java.beans.PropertyChangeListener;
import java.text.RuleBasedCollator;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;

import org.tinymediamanager.core.Constants;
import org.tinymediamanager.core.tvshow.TvShowList;
import org.tinymediamanager.core.tvshow.TvShowModuleManager;
import org.tinymediamanager.core.tvshow.entities.TvShow;
import org.tinymediamanager.core.tvshow.entities.TvShowEpisode;
import org.tinymediamanager.core.tvshow.entities.TvShowSeason;
import org.tinymediamanager.ui.UTF8Control;
import org.tinymediamanager.ui.components.tree.TmmTreeDataProvider;
import org.tinymediamanager.ui.components.tree.TmmTreeNode;

/**
 * The class TvShowTreeDataProvider is used for providing and managing the data for the TV show tree
 * 
 * @author Manuel Laggner
 */
public class TvShowTreeDataProvider extends TmmTreeDataProvider {
  protected static final ResourceBundle BUNDLE         = ResourceBundle.getBundle("messages", new UTF8Control()); //$NON-NLS-1$
  private TmmTreeNode                   root           = new TmmTreeNode(new Object(), this);
  private RuleBasedCollator             stringCollator = (RuleBasedCollator) RuleBasedCollator.getInstance();

  private final PropertyChangeListener  tvShowListPropertyChangeListener;
  private final PropertyChangeListener  tvShowPropertyChangeListener;
  private final PropertyChangeListener  episodePropertyChangeListener;

  private final TvShowList              tvShowList     = TvShowList.getInstance();

  public TvShowTreeDataProvider() {
    tvShowListPropertyChangeListener = evt -> {
      TvShow tvShow;

      switch (evt.getPropertyName()) {
        case Constants.ADDED_TV_SHOW:
          tvShow = (TvShow) evt.getNewValue();
          addTvShow(tvShow);
          break;

        case Constants.REMOVED_TV_SHOW:
          tvShow = (TvShow) evt.getNewValue();
          removeTvShow(tvShow);
          break;

        default:
          // do not react on other events of the tvShowList
          if (evt.getSource() instanceof TvShowList) {
            break;
          }

          nodeChanged(evt.getSource());
          break;
      }
    };
    tvShowList.addPropertyChangeListener(tvShowListPropertyChangeListener);

    tvShowPropertyChangeListener = evt -> {
      TvShowSeason season;
      TvShowEpisode episode;

      // only process events from the TV show itself
      if (!(evt.getSource() instanceof TvShow)) {
        return;
      }

      switch (evt.getPropertyName()) {
        case Constants.ADDED_SEASON:
          season = (TvShowSeason) evt.getNewValue();
          addTvShowSeason(season);
          break;

        case Constants.ADDED_EPISODE:
          episode = (TvShowEpisode) evt.getNewValue();
          addTvShowEpisode(episode);
          break;

        case Constants.REMOVED_EPISODE:
          episode = (TvShowEpisode) evt.getNewValue();
          removeTvShowEpisode(episode);
          break;

        case Constants.EPISODE_COUNT:
        case Constants.SEASON_COUNT:
          // do not react on change of the episode count
          break;

        default:
          nodeChanged(evt.getSource());
          break;
      }
    };

    episodePropertyChangeListener = evt -> {
      TvShowEpisode episode;

      switch (evt.getPropertyName()) {
        // changed the season/episode nr of an episode
        case Constants.SEASON:
        case Constants.EPISODE:
          // simply remove it from the tree and readd it
          episode = (TvShowEpisode) evt.getSource();
          removeTvShowEpisode(episode);
          addTvShowEpisode(episode);
          break;

        case Constants.TV_SHOW:
          // do not react on change of the TV show itself
          break;

        default:
          nodeChanged(evt.getSource());
          break;
      }
    };

    setTreeComparator(new TvShowComparator());

    TvShowModuleManager.SETTINGS.addPropertyChangeListener(evt -> {
      switch (evt.getPropertyName()) {
        case "displayMissingEpisodes":
          if (TvShowModuleManager.SETTINGS.isDisplayMissingEpisodes()) {
            addDummyEpisodes();
          }
          else {
            removeDummyEpisodes();
          }
          break;
        case "displayMissingSpecials":
          if (TvShowModuleManager.SETTINGS.isDisplayMissingSpecials()) {
            addDummySpecials();
          }
          else {
            removeDummySpecials();
          }
          break;
      }
    });
  }

  /**
   * add the dummy episodes to the tree is the setting has been activated
   */
  private void addDummyEpisodes() {
    for (TvShow tvShow : tvShowList.getTvShows()) {
      for (TvShowEpisode episode : tvShow.getEpisodesForDisplay()) {
        if (episode.isDummy()) {
          addTvShowEpisode(episode);
        }
      }
    }
  }

  /**
   * remove the dummy episodes from the tree is the setting has been deactivated
   */
  private void removeDummyEpisodes() {
    for (TvShow tvShow : tvShowList.getTvShows()) {
      for (TvShowEpisode episode : tvShow.getDummyEpisodes()) {
        if (episode.isDummy()) {
          removeTvShowEpisode(episode);
        }
      }
      // last but not least remove all empty seasons
      for (TvShowSeason season : tvShow.getSeasons()) {
        if (season.isDummy()) {
          removeTvShowSeason(season);
        }
      }
    }
  }

  /**
   * add the dummy specials to the tree is the setting has been activated
   */
  private void addDummySpecials() {
    for (TvShow tvShow : tvShowList.getTvShows()) {
      for (TvShowEpisode episode : tvShow.getEpisodesForDisplay()) {
        if (episode.isDummy() && episode.getSeason() == 0) {
          addTvShowEpisode(episode);
        }
      }
    }
  }

  /**
   * remove the dummy specials from the tree is the setting has been deactivated
   */
  private void removeDummySpecials() {
    for (TvShow tvShow : tvShowList.getTvShows()) {
      for (TvShowEpisode episode : tvShow.getDummyEpisodes()) {
        if (episode.isDummy() && episode.getSeason() == 0) {
          removeTvShowEpisode(episode);
        }
      }
      // last but not least remove all empty seasons
      for (TvShowSeason season : tvShow.getSeasons()) {
        if (season.getSeason() == 0 && season.isDummy()) {
          removeTvShowSeason(season);
        }
      }
    }
  }

  /**
   * trigger a node changed event for all other events
   * 
   * @param source
   */
  private void nodeChanged(Object source) {
    TmmTreeNode node = getNodeFromCache(source);
    if (node != null) {
      firePropertyChange(NODE_CHANGED, null, node);
    }
  }

  @Override
  public TmmTreeNode getRoot() {
    return root;
  }

  @Override
  public TmmTreeNode getParent(TmmTreeNode child) {
    if (child.getUserObject() instanceof TvShow) {
      return root;
    }
    else if (child.getUserObject() instanceof TvShowSeason) {
      TvShowSeason season = (TvShowSeason) child.getUserObject();
      TmmTreeNode node = getNodeFromCache(season.getTvShow());
      // parent TV show not yet added? add it
      if (node == null) {
        node = addTvShow(season.getTvShow());
      }
      return node;
    }
    else if (child.getUserObject() instanceof TvShowEpisode) {
      TvShowEpisode episode = (TvShowEpisode) child.getUserObject();
      TmmTreeNode node = getNodeFromCache(episode.getTvShowSeason());
      if (node == null) {
        node = addTvShowSeason(episode.getTvShowSeason());
      }
      // also check if the TV show has already been added
      if (getNodeFromCache(episode.getTvShow()) == null) {
        addTvShow(episode.getTvShow());
      }
      return node;
    }
    return null;
  }

  @Override
  public List getChildren(TmmTreeNode parent) {
    if (parent == root) {
      ArrayList nodes = new ArrayList<>();
      for (TvShow tvShow : new ArrayList<>(tvShowList.getTvShows())) {
        TmmTreeNode node = new TvShowTreeNode(tvShow, this);
        putNodeToCache(tvShow, node);
        nodes.add(node);

        // add a propertychangelistener for this tv show
        tvShow.addPropertyChangeListener(tvShowPropertyChangeListener);
      }
      return nodes;
    }
    else if (parent.getUserObject() instanceof TvShow) {
      TvShow tvShow = (TvShow) parent.getUserObject();
      ArrayList nodes = new ArrayList<>();
      for (TvShowSeason season : tvShow.getSeasons()) {
        TmmTreeNode node = new TvShowSeasonTreeNode(season, this);
        putNodeToCache(season, node);
        nodes.add(node);
      }
      return nodes;
    }
    else if (parent.getUserObject() instanceof TvShowSeason) {
      TvShowSeason season = (TvShowSeason) parent.getUserObject();
      ArrayList nodes = new ArrayList<>();
      for (TvShowEpisode episode : season.getEpisodesForDisplay()) {
        // look if a node of this episode already exist
        TmmTreeNode node = getNodeFromCache(episode);

        if (node == null) {
          // create a new one
          node = new TvShowEpisodeTreeNode(episode, this);
          putNodeToCache(episode, node);
        }

        nodes.add(node);

        // add a propertychangelistener for this episode
        episode.addPropertyChangeListener(episodePropertyChangeListener);
      }
      return nodes;
    }
    return null;
  }

  @Override
  public boolean isLeaf(TmmTreeNode node) {
    return node.getUserObject() instanceof TvShowEpisode;
  }

  @Override
  public Comparator getTreeComparator() {
    return super.getTreeComparator();
  }

  private TmmTreeNode addTvShow(TvShow tvShow) {
    // check if this tv show has already been added
    TmmTreeNode cachedNode = getNodeFromCache(tvShow);
    if (cachedNode != null) {
      return cachedNode;
    }

    // add a new node
    TmmTreeNode node = new TvShowTreeNode(tvShow, this);
    putNodeToCache(tvShow, node);
    firePropertyChange(NODE_INSERTED, null, node);

    // and also add a propertychangelistener to react on changes inside the tv show
    tvShow.addPropertyChangeListener(tvShowPropertyChangeListener);
    return node;
  }

  private void removeTvShow(TvShow tvShow) {
    TmmTreeNode cachedNode = removeNodeFromCache(tvShow);
    if (cachedNode == null) {
      return;
    }

    // remove all children from the map (the nodes will be removed by the treemodel)
    for (TvShowSeason season : tvShow.getSeasons()) {
      removeNodeFromCache(season);
    }
    for (TvShowEpisode epsiode : tvShow.getEpisodesForDisplay()) {
      removeNodeFromCache(epsiode);
    }

    // remove the propertychangelistener from this tv show
    tvShow.removePropertyChangeListener(tvShowPropertyChangeListener);

    firePropertyChange(NODE_REMOVED, null, cachedNode);
  }

  private TmmTreeNode addTvShowSeason(TvShowSeason season) {
    // check if this season has already been added
    TmmTreeNode cachedNode = getNodeFromCache(season);
    if (cachedNode != null) {
      return cachedNode;
    }

    // add a new node
    TmmTreeNode node = new TvShowSeasonTreeNode(season, this);
    putNodeToCache(season, node);
    firePropertyChange(NODE_INSERTED, null, node);
    return node;
  }

  private TmmTreeNode addTvShowEpisode(TvShowEpisode episode) {
    // check if this episode has already been added
    TmmTreeNode cachedNode = getNodeFromCache(episode);
    if (cachedNode != null) {
      return cachedNode;
    }

    // add a new node
    TmmTreeNode node = new TvShowEpisodeTreeNode(episode, this);
    putNodeToCache(episode, node);
    firePropertyChange(NODE_INSERTED, null, node);

    // and also add a propertychangelistener to react on changes inside the episode
    episode.addPropertyChangeListener(episodePropertyChangeListener);
    return node;
  }

  private void removeTvShowEpisode(TvShowEpisode episode) {
    TmmTreeNode cachedNode = removeNodeFromCache(episode);
    if (cachedNode == null) {
      return;
    }

    // remove the propertychangelistener from this episode
    episode.removePropertyChangeListener(episodePropertyChangeListener);

    firePropertyChange(NODE_REMOVED, null, cachedNode);

    // okay, we've removed the episode; now check which seasons we have to remove too
    for (TvShowSeason season : episode.getTvShow().getSeasons()) {
      if (season.getEpisodes().isEmpty()) {
        removeTvShowSeason(season);
      }
    }
  }

  private void removeTvShowSeason(TvShowSeason season) {
    TmmTreeNode cachedNode = removeNodeFromCache(season);
    if (cachedNode == null) {
      return;
    }

    // remove the propertychangelistener from this episode
    season.removePropertyChangeListener(episodePropertyChangeListener);

    firePropertyChange(NODE_REMOVED, null, cachedNode);
  }

  /*
   * helper classes
   */
  class TvShowComparator implements Comparator {
    @Override
    public int compare(TmmTreeNode o1, TmmTreeNode o2) {
      Object userObject1 = o1.getUserObject();
      Object userObject2 = o2.getUserObject();

      if (userObject1 instanceof TvShow && userObject2 instanceof TvShow) {
        TvShow tvShow1 = (TvShow) userObject1;
        TvShow tvShow2 = (TvShow) userObject2;
        if (stringCollator != null) {
          return stringCollator.compare(tvShow1.getTitleSortable().toLowerCase(Locale.ROOT), tvShow2.getTitleSortable().toLowerCase(Locale.ROOT));
        }
        return tvShow1.getTitleSortable().compareToIgnoreCase(tvShow2.getTitleSortable());
      }

      if (userObject1 instanceof TvShowSeason && userObject2 instanceof TvShowSeason) {
        TvShowSeason tvShowSeason1 = (TvShowSeason) userObject1;
        TvShowSeason tvShowSeason2 = (TvShowSeason) userObject2;
        return tvShowSeason1.getSeason() - tvShowSeason2.getSeason();
      }

      if (userObject1 instanceof TvShowEpisode && userObject2 instanceof TvShowEpisode) {
        TvShowEpisode tvShowEpisode1 = (TvShowEpisode) userObject1;
        TvShowEpisode tvShowEpisode2 = (TvShowEpisode) userObject2;
        return tvShowEpisode1.getEpisode() - tvShowEpisode2.getEpisode();
      }

      return o1.toString().compareToIgnoreCase(o2.toString());
    }
  }

  class TvShowTreeNode extends TmmTreeNode {
    private static final long serialVersionUID = -1316609340104597133L;

    /**
     * Instantiates a new tv show tree node.
     * 
     * @param userObject
     *          the user object
     */
    public TvShowTreeNode(Object userObject, TmmTreeDataProvider dataProvider) {
      super(userObject, dataProvider);
    }

    /**
     * provides the right name of the node for display.
     * 
     * @return the string
     */
    @Override
    public String toString() {
      // return TV show name
      if (getUserObject() instanceof TvShow) {
        TvShow tvShow = (TvShow) getUserObject();
        return tvShow.getTitleSortable();
      }

      // fallback: call super
      return super.toString();
    }
  }

  class TvShowSeasonTreeNode extends TmmTreeNode {
    private static final long serialVersionUID = -5734830011018805194L;

    /**
     * Instantiates a new tv show season tree node.
     * 
     * @param userObject
     *          the user object
     */
    public TvShowSeasonTreeNode(Object userObject, TmmTreeDataProvider dataProvider) {
      super(userObject, dataProvider);
    }

    /**
     * provides the right name of the node for display
     */
    @Override
    public String toString() {
      // return movieSet name
      if (getUserObject() instanceof TvShowSeason) {
        TvShowSeason season = (TvShowSeason) getUserObject();
        if (season.getSeason() == -1) {
          return BUNDLE.getString("tvshow.uncategorized");
        }

        if (season.getSeason() == 0) {
          return BUNDLE.getString("metatag.specials");
        }

        return BUNDLE.getString("metatag.season") + " " + season.getSeason();
      }

      // fallback: call super
      return super.toString();
    }
  }

  class TvShowEpisodeTreeNode extends TmmTreeNode {
    private static final long serialVersionUID = -7108614568808831980L;

    /**
     * Instantiates a new tv show episode tree node.
     * 
     * @param userObject
     *          the user object
     */
    public TvShowEpisodeTreeNode(Object userObject, TmmTreeDataProvider dataProvider) {
      super(userObject, dataProvider);
    }

    /**
     * provides the right name of the node for display.
     * 
     * @return the string
     */
    @Override
    public String toString() {
      // return episode name and number
      if (getUserObject() instanceof TvShowEpisode) {
        TvShowEpisode episode = (TvShowEpisode) getUserObject();
        if (episode.getEpisode() >= 0) {
          return episode.getEpisode() + ". " + episode.getTitle();
        }
        else {
          return episode.getTitleSortable();
        }
      }

      // fallback: call super
      return super.toString();
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy