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