001/**
002 * Copyright 2014 Tampere University of Technology, Pori Department
003 * 
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * 
008 *   http://www.apache.org/licenses/LICENSE-2.0
009 * 
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package service.tut.pori.contentanalysis;
017
018import java.sql.ResultSet;
019import java.sql.SQLException;
020import java.util.ArrayList;
021import java.util.Iterator;
022import java.util.LinkedList;
023
024import org.apache.commons.lang3.StringUtils;
025import org.apache.log4j.Logger;
026import org.springframework.jdbc.core.RowCallbackHandler;
027
028import service.tut.pori.contentanalysis.MediaObject.ConfirmationStatus;
029import service.tut.pori.contentanalysis.MediaObject.MediaObjectType;
030import core.tut.pori.context.ServiceInitializer;
031import core.tut.pori.dao.SQLDAO;
032import core.tut.pori.dao.SQLSelectBuilder;
033import core.tut.pori.dao.clause.AndClause;
034import core.tut.pori.dao.clause.SQLClause.SQLType;
035
036/**
037 * A DAO that can be used to handle keywords / friendly keywords mapping
038 */
039public class KeywordsDAO extends SQLDAO{
040  private static final Logger LOGGER = Logger.getLogger(KeywordsDAO.class);
041  /* tables */
042  private static final String TABLE_FRIENDLY_KEYWORDS = DATABASE+".ca_photo_friendly_keywords";
043  /* columns */
044  private static final String COLUMN_FRIENDLY_VALUE = "friendly_value";
045  /* sql scripts */
046  private static final String[] ASSIGN_FRIENDLY_SELECT_COLUMNS = new String[]{Definitions.COLUMN_BACKEND_ID, COLUMN_FRIENDLY_VALUE, Definitions.COLUMN_VALUE};
047
048  /**
049   * Replaces all media object values for media objects of type keyword,
050   * which have a valid known friendly keyword replacement:
051   * - media objects without backendId will be ignored
052   * - media objects not of type keyword will be ignored
053   * - media objects with ConfirmationStatus not of Candidate will be ignored
054   * - media object (keyword) value is matched case-sensitively, an identical value with friendly keyword must be found
055   * - if media object value is found in the database without a friendly value, no friendly value replacement is performed, and the object is set to ConfirmationStatus.NO_FRIENDLY_KEYWORD
056   * 
057   * Note: this will set the friendly keyword as the name of the object, not as the value, the original value is preserved as the name. Old name (if any) is overridden.
058   * 
059   * @param objects
060   */
061  public void assignFriendlyKeywords(MediaObjectList objects){
062    if(!ServiceInitializer.getPropertyHandler().getSystemProperties(CAProperties.class).isResolveFriendlyKeywords()){
063      LOGGER.debug("Friendly keyword assignment disabled by property configuration.");
064      return;
065    }
066    
067    if(MediaObjectList.isEmpty(objects)){
068      LOGGER.debug("Empty media object list.");
069      return;
070    }
071    
072    /**
073     * list of values for which the friendly values will be retrieved for.
074     * We could also create value-backendId map, as the values are mapped by backend in the database,
075     * but in the current implementation (and database listing) the value-backend relations are unique
076     * (one value maps only to a single backend), and thus, creating extra sql where (value=? AND (backend_id=? OR backend_id IS NULL) pairs is not needed.
077     */
078    ArrayList<String> values = new ArrayList<>();
079    
080    final LinkedList<MediaObject> keywords = new LinkedList<>();
081    for(Iterator<MediaObject> iter = objects.getMediaObjects().iterator(); iter.hasNext();){
082      MediaObject o = iter.next();
083      if(o.getBackendId() != null && MediaObjectType.KEYWORD.equals(o.getMediaObjectType()) && ConfirmationStatus.CANDIDATE.equals(o.getConfirmationStatus())){ // only take keywords with backendIds and with status candidate
084        keywords.add(o);
085        values.add(o.getValue());
086      }
087    }
088    
089    if(values.isEmpty()){
090      LOGGER.debug("No valid media objects for friendly keyword replacement.");
091      return;
092    }
093    
094    SQLSelectBuilder sql = new SQLSelectBuilder(TABLE_FRIENDLY_KEYWORDS);
095    sql.addSelectColumns(ASSIGN_FRIENDLY_SELECT_COLUMNS);
096    sql.addWhereClause(new AndClause(Definitions.COLUMN_VALUE, values.toArray(), SQLType.STRING));
097    getJdbcTemplate().query(sql.toSQLString(), sql.getValues(), sql.getValueTypes(), new RowCallbackHandler() { 
098      @Override
099      public void processRow(ResultSet set) throws SQLException {
100        String value = set.getString(Definitions.COLUMN_VALUE);
101        String friendlyValue = set.getString(COLUMN_FRIENDLY_VALUE);
102        Integer backendId = set.getInt(Definitions.COLUMN_BACKEND_ID);
103        if(set.wasNull()){
104          backendId = null;
105        }
106        for(Iterator<MediaObject> kIter = keywords.iterator(); kIter.hasNext();){
107          MediaObject keyword = kIter.next();
108          if(value.equals(keyword.getValue())){
109            if(backendId != null && backendId.equals(keyword.getBackendId())){ // if the mapping does not contain backendId, set the friendly value, but do not remove from the list (in case more accurate match is found later on)
110              kIter.remove(); // remove if the backend ids match
111            }
112            if(StringUtils.isBlank(friendlyValue)){ // do not replace name if no friendly value
113              keyword.setConfirmationStatus(ConfirmationStatus.NO_FRIENDLY_KEYWORD);
114            }else{
115              keyword.setName(friendlyValue); // set the friendly value as the name
116            }
117          } // if, do not break here, there might be duplicates in the list
118        } // for keywords
119      } // processRow
120    });
121    for(Iterator<MediaObject> kIter = keywords.iterator(); kIter.hasNext();){ // set NO_FRIENDLY_KEYWORD for everything left over
122      kIter.next().setConfirmationStatus(ConfirmationStatus.NO_FRIENDLY_KEYWORD);
123    }
124  }
125}