001package de.deepamehta.plugins.mail; 002 003import static de.deepamehta.plugins.mail.MailPlugin.*; 004 005import java.util.ArrayList; 006import java.util.Collections; 007import java.util.Comparator; 008import java.util.HashMap; 009import java.util.List; 010import java.util.Map; 011import java.util.logging.Logger; 012 013import de.deepamehta.core.RelatedTopic; 014import de.deepamehta.core.Topic; 015import de.deepamehta.core.model.ChildTopicsModel; 016import de.deepamehta.core.model.TopicModel; 017import de.deepamehta.core.service.DeepaMehtaService; 018 019public class Autocomplete { 020 021 private static Logger log = Logger.getLogger(Autocomplete.class.getName()); 022 023 private final DeepaMehtaService dms; 024 025 private final MailConfigurationCache config; 026 027 public static final Comparator<TopicModel> VALUE_COMPARATOR = new Comparator<TopicModel>() { 028 @Override 029 public int compare(TopicModel a, TopicModel b) { 030 return a.getSimpleValue().toString().compareTo(b.getSimpleValue().toString()); 031 } 032 }; 033 034 public Autocomplete(DeepaMehtaService dms, MailConfigurationCache config) { 035 this.dms = dms; 036 this.config = config; 037 } 038 039 /** 040 * call a search on all configured topic types and on all email addresses 041 * 042 * @param query 043 * 044 * @return topic list with email ID and contact type URI 045 */ 046 public List<TopicModel> search(String query) { 047 // search and hash parent results by ID (to overwrite duplicates) 048 Map<Long, Topic> parents = new HashMap<Long, Topic>(); 049 for (String uri : config.getSearchTypeUris()) { 050 for (Topic topic : dms.searchTopics(query, uri)) { 051 Topic parent = getParent(topic); 052 parents.put(parent.getId(), parent); 053 } 054 } 055 056 // get and hash addresses of each parent 057 Map<Long, TopicModel> addresses = new HashMap<Long, TopicModel>(); 058 for (Topic result : parents.values()) { 059 Topic parent = dms.getTopic(result.getId()).loadChildTopics(); 060 for (TopicModel address : getEmailAddresses(parent)) { 061 putAddress(addresses, parent, address); 062 } 063 } 064 065 // search email directly afterwards and merge the results 066 List<Topic> searchTopics = dms.searchTopics(query, EMAIL_ADDRESS); 067 for (Topic address : searchTopics) { 068 TopicModel model = address.getModel(); 069 if (addresses.containsKey(model.getId()) == false) { 070 putAddress(addresses, getParent(address), model); 071 } 072 } 073 074 // wrap, sort and return 075 List<TopicModel> result = new ArrayList<TopicModel>(addresses.values()); 076 Collections.sort(result, VALUE_COMPARATOR); 077 078 // FIXME inconsistent model => use a specific view model 079 return result; 080 } 081 082 /** 083 * 084 * @param topic 085 * @param clientState 086 * 087 * @return list of mail addresses with at minimum one empty address 088 */ 089 private List<TopicModel> getEmailAddresses(Topic topic) { 090 // FIXME attached value should support add(...) of child compositions 091 if (topic.getChildTopics().has(EMAIL_ADDRESS) == false) { 092 log.warning("composite of " + topic.getSimpleValue() + " contains no email address"); 093 topic.setChildTopics(new ChildTopicsModel().add(EMAIL_ADDRESS, // 094 new TopicModel(EMAIL_ADDRESS))); 095 } 096 // return the existing or the newly created list of addresses 097 return topic.getChildTopics().getModel().getTopics(EMAIL_ADDRESS); 098 } 099 100 private RelatedTopic getParent(Topic child) { 101 return child.getRelatedTopic(COMPOSITION, CHILD, PARENT, null); 102 } 103 104 /** 105 * puts an inconsistent model (address ID + contact type URI) of email 106 * address into the addresses map. 107 */ 108 private void putAddress(Map<Long, TopicModel> addresses, Topic parent, TopicModel address) { 109 // concatenate values 110 if (address != null && parent != null) { 111 address.setSimpleValue(parent.getSimpleValue() + "<" + address.getSimpleValue() + ">"); 112 // replace type URI 113 address.setTypeUri(parent.getTypeUri()); 114 addresses.put(address.getId(), address); 115 } 116 } 117}