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}