Package dnb.data.impl

Source Code of dnb.data.impl.RepositoryHibernateImpl

package dnb.data.impl;

import java.util.List;
import java.util.logging.Logger;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Order;

import dnb.analyze.SimilarScanResult;
import dnb.data.Artist;
import dnb.data.Genre;
import dnb.data.Label;
import dnb.data.Repository;
import dnb.data.RepositoryObject;
import dnb.util.InsertionSort;
import dnb.util.MatchUtils;
import dnb.util.MatchUtils.MatchUtilsResult;

/** XXX fat client issues: https://www.hibernate.org/333.html
* WEAPON of choice: Implicit Humongous Transation
* We have a single session, we assume that all object transactions, even lazy reads,
* are made thread safe somehow.
* Then we have this single session open and immediately begin a transaction.
* When we are at the point where a transaction needs to be committed,
* we commit all changes that have happened to the object graph in memory,
* then immediately begin a new transaction and carry on in the same session.

This is basically the same thing that you have when working on an SQL command line:
you do selects, inserts and updates, and then you write "COMMIT;" to commit all that you did.
After committing a new transaction is immediately open.

We will just be careful in our application not to change objects around inadvertently outside
of what we call a command (as in Command Pattern).

RIGHT: => Natural changes only using GET/SET ALWAYS require PropertyChangeListener overhead!!!

==> Modify objects, pass them to PersistenceFacade#save for saving & propagating change events.
*
*
*/

public class RepositoryHibernateImpl implements Repository {

 
  /**
   * Levenshtein distance for artist similarity search.
   */
  private static final int LDISTANCE_ARTIST = 1;
 
 
  /**
   * Levenshtein distance for label similarity search.
   */
  private static final int LDISTANCE_LABEL = 1;
 
  private static final Logger LOGGER = Logger.getLogger(MatchUtils.class.getName());
 
  /**
   * Levenshtein distance for genre similarity search.
   */
  private static final int LDISTANCE_GENRE = 3;
 
  // linked lists for better insertion sort
  private List<Label> labels;
  private List<Artist> artists;
  private List<Genre> genres;
 
 
  private final SessionFactory sessionFactory;
 
 
  public RepositoryHibernateImpl(SessionFactory sessionFactory) {   
    this.sessionFactory = sessionFactory;
    load();
  }

  @SuppressWarnings("unchecked")
  private void load() {
    Session s = sessionFactory.openSession();
    s.beginTransaction();
    labels = s.createCriteria(LabelHibernateImpl.class).addOrder(Order.desc("name")).list();
    artists = s.createCriteria(ArtistHibernateImpl.class).addOrder(Order.desc("name")).list();
    genres = s.createCriteria(GenreHibernateImpl.class).addOrder(Order.desc("name")).list();   
    s.getTransaction().commit();
    s.close();
   
    // hook up labels 4 saving and labelcode callback
    for (Label l : labels) {
      ((LabelHibernateImpl)l).postLoad(this);
    }
  }

  /* (non-Javadoc)
   * @see dnb.data.Repository#getLabel(java.lang.String)
   */
  @SuppressWarnings("unchecked")
  public Label getLabel(String name) {
    if (name == null || name.isEmpty()) {
      return null;
    }
    for (Label l : labels) {
      if (l.getName().equalsIgnoreCase(name)) {       
        return l;
      }
    }   
    Label l = new LabelHibernateImpl(this, name);
    List<?> ll = labels; // old trick, we know Genre is a repo object
    InsertionSort.insert((List<RepositoryObjectHibernateImpl>)ll, (RepositoryObjectHibernateImpl) l);
    save(l);
    return l;
  }
 

  /* (non-Javadoc)
   * @see dnb.data.Repository#getArtist(java.lang.String)
   */
  @SuppressWarnings("unchecked")
  public Artist getArtist(String artist) {
    if (artist == null || artist.isEmpty()) {
      return null;
    }
    for (Artist a : artists) {
      if (a.getName().equalsIgnoreCase(artist)) {
        return a; // already contained
      }
    }
    Artist a = new ArtistHibernateImpl(artist);   
    List<?> l = artists; // old trick, we know Genre is a repo object
    InsertionSort.insert((List<RepositoryObjectHibernateImpl>)l, (RepositoryObjectHibernateImpl) a);
    save(a);
    return a;
  }

 
 
  /* (non-Javadoc)
   * @see dnb.data.Repository#getGenre(java.lang.String)
   */
  @SuppressWarnings("unchecked")
  public Genre getGenre(String genre) {
    if (genre == null || genre.isEmpty()) {
      return null;
    }
    for (Genre g : genres) {
      if (g.getName().equalsIgnoreCase(genre)) {
        return g; // already contained
      }
    }
    Genre g = new GenreHibernateImpl(genre);
    List l = genres; // old trick, we know Genre is a repo object
    InsertionSort.insert((List<RepositoryObjectHibernateImpl>)l, (RepositoryObjectHibernateImpl) g);   
    save(g);
    return g;
  }
 
  void save(Object a) {
    Session s = sessionFactory.openSession();
    s.beginTransaction();
    s.saveOrUpdate(a);
    s.getTransaction().commit();
    s.close();
  }
   
 
 
  Label isLabelCodeAssigned(Label l, String labelcode) {
    List<LabelcodeHibernateImpl> codes;
    for (Label la : labels) {
      if (la == l) { // test for identity is OK => all repo objects r unique
        continue; // don't check self
      }
      codes = ((LabelHibernateImpl)la).getCodes();
      for (LabelcodeHibernateImpl lc : codes) {
        if (lc.getName().equalsIgnoreCase(labelcode)) {
          return la; // already contained
        }
      }
    }
    return null;
  }
 
  public Label findByLabelcode(String labelcode) {
    List<LabelcodeHibernateImpl> codes;
    for (Label la : labels) {
      codes = ((LabelHibernateImpl)la).getCodes();
      for (LabelcodeHibernateImpl lc : codes) {
        if (lc.getName().equalsIgnoreCase(labelcode)) {
          return la; // already contained
        }
      }
    }
    return null;
  }
  @Override
  public String toString() {
    return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
  }
 
  private static int compare(final char c, String key) {
    return (int)c - Character.toLowerCase(key.charAt(0));
  }

  /**
   * XXX possible bug source, test !
   * @param l
   * @param c must be a lowercase letter !
   * @return
   */
  @SuppressWarnings("unchecked")
  private static int findStartOf(final List<RepositoryObjectHibernateImpl> l, final char c) { 
    if(l.isEmpty()) {
      return -1;
    }
    InsertionSort.Finder<String> f = new InsertionSort.Finder<String>() {
      @Override public int compareTo(String key) {       
        return compare(c, key);
      }};
    InsertionSort.Converter<RepositoryObject, String> conv
      = new InsertionSort.Converter<RepositoryObject, String>() {
      @Override public String convert(RepositoryObject source) {       
        return source.getName();
      }};
    int idx = InsertionSort.find((List)l, conv, f, 0, l.size());
       
    if(f.compareTo(conv.convert(l.get(idx))) == 0) {
       
      //ok, found, but maybe in the middle of the list, walk backwards until start is found
      while(idx >= 0 && f.compareTo(conv.convert(l.get(idx))) == 0) {
        idx--;
      }
      return ++idx;
    } else {
      // maybe insert location is b4, check
      idx++;
      if (idx < l.size()) {
        if(f.compareTo(conv.convert(l.get(idx))) == 0) {
          return idx;
        } else {
          return -1;
        }
      } else {
        return -1;
      }
    }
  }
 
  @SuppressWarnings("unchecked")
  private static <T extends RepositoryObject> SimilarScanResult<T> findSimilar(List<T> list, String name) {
    int dst;
    String ln = name.toLowerCase(); // cache for distance
    SimilarScanResult<T> ssr = null;
    SimilarScanResult.Match<T> mt;
    // brute force 4 labels 2 get max match probability ;-)
    for (T a : list) {     
      // distance only           
      dst =  StringUtils.getLevenshteinDistance(ln, a.getName().toLowerCase());
      if (dst == 0) {   
        mt = new SimilarScanResult.Match<T>(SimilarScanResult.MatchType.EXACT, a.getName(), a);       
        if (ssr == null) { ssr = new SimilarScanResult<T>(mt); } else { ssr.add(mt); }       
        return ssr; // => we got an exact match => cancel search
       
      } else if (dst <= LDISTANCE_ARTIST) {
        mt = new SimilarScanResult.Match<T>(dst, a.getName(), a);       
        if (ssr == null) { ssr = new SimilarScanResult<T>(mt); } else { ssr.add(mt); }
      } // else next label...     
    }
    return ssr;
  }
 
  // finds an artist similar to the passed one
  /* (non-Javadoc)
   * @see dnb.data.Repository#findSimilar(java.lang.String, int)
   */
  @SuppressWarnings("unchecked")
  public SimilarScanResult<Artist> findSimilarArtist(String name) {
    return findSimilar(artists, name);
  }
 
  @SuppressWarnings("unchecked")
  public SimilarScanResult<Genre> findSimilarGenre(String name) {
    return findSimilar(genres, name);   
  }
 
  @SuppressWarnings("unchecked")
  public SimilarScanResult<Label> findSimilarLabel(String name) {   
   
    SimilarScanResult<Label> ssr = null;
    SimilarScanResult.Match<Label> mt;
   
    String pt = null;
    int dst;
    // normalize & split passed name
    MatchUtilsResult in = MatchUtils.getPartsNormalize(name);
   
    // brute force 4 labels 2 get max match propability ;-)
    LabelHibernateImpl l;
    for (Label ll : labels) {
      l = (LabelHibernateImpl) ll;
      // get distance of the normed names 2 each other
      dst =  StringUtils.getLevenshteinDistance(in.getNormalized(), l.getNormalizedName());
      if (dst == 0) {
        LOGGER.info("Found exact match for label " + name);
        mt = new SimilarScanResult.Match<Label>(SimilarScanResult.MatchType.EXACT, ll.getName(), ll)
        if (ssr == null) { ssr = new SimilarScanResult<Label>(mt); } else { ssr.add(mt); }       
        return ssr; // => we got an exact match => cancel search       
      } else if (dst <= LDISTANCE_LABEL) {
        LOGGER.info("Found similar match:" + name);
        mt = new SimilarScanResult.Match<Label>(dst, ll.getName(), ll);
        if (ssr == null) { ssr = new SimilarScanResult<Label>(mt); } else { ssr.add(mt); }
      } else { // no relevant distance => check part match       
        if (in.getParts() != null) { // we got multiple parts in the passed string => try part find
          pt = l.matchParts(in.getParts())// match the current label against the words of this label name
          if (pt != null) { // part match found
            LOGGER.info("Found part match '" + pt + "' for label " + l.getName());
            mt = new SimilarScanResult.Match<Label>(SimilarScanResult.MatchType.PART, pt, ll)
            if (ssr == null) { ssr = new SimilarScanResult<Label>(mt); } else { ssr.add(mt); }
          }
        } 
      }         
      // else next label...
    }
    LOGGER.info("No match for '" + name + "'");
    return ssr;
  }

  public List<Label> getLabels() {
    return labels;
  }

  public List<Artist> getArtists() {
    return artists;
  }

  public List<Genre> getGenres() {
    return genres;
  }
}

TOP

Related Classes of dnb.data.impl.RepositoryHibernateImpl

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.