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() + "&lt;" + address.getSimpleValue() + "&gt;");
112            // replace type URI
113            address.setTypeUri(parent.getTypeUri());
114            addresses.put(address.getId(), address);   
115        }
116    }
117}