edu.udo.scaffoldhunter.model.dataimport.ImportJob.java Source code

Java tutorial

Introduction

Here is the source code for edu.udo.scaffoldhunter.model.dataimport.ImportJob.java

Source

/*
 * Scaffold Hunter
 * Copyright (C) 2006-2008 PG504
 * Copyright (C) 2010-2011 PG552
 * See README.txt in the root directory of the Scaffold Hunter source tree
 * for details.
 *
 * Scaffold Hunter is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scaffold Hunter is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package edu.udo.scaffoldhunter.model.dataimport;

import static com.google.common.base.Predicates.not;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.swing.AbstractListModel;
import javax.swing.ComboBoxModel;

import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import edu.udo.scaffoldhunter.model.PropertyType;
import edu.udo.scaffoldhunter.model.data.Job;
import edu.udo.scaffoldhunter.model.data.Message;
import edu.udo.scaffoldhunter.model.data.MessageListener;
import edu.udo.scaffoldhunter.model.db.PropertyDefinition;
import edu.udo.scaffoldhunter.plugins.dataimport.ImportPlugin;
import edu.udo.scaffoldhunter.plugins.dataimport.PluginResults;

/**
 * Describes some source to be imported and the mappings used for the actual
 * import process.
 * 
 * @author Henning Garus
 * 
 */
public class ImportJob implements Job, MessageListener {

    private final ImportPlugin plugin;
    private Object pluginArguments;
    private final String jobName;
    private PluginResults results = null;
    private String titleProperty;
    private String sourceMergeBy = null;
    private PropertyDefinition internalMergeBy;
    private MergeStrategy titleMergeStrategy = MergeStrategy.DONT_OVERWRITE;
    private MergeStrategy structureMergeStrategy = MergeStrategy.DONT_OVERWRITE;
    private Map<String, SourcePropertyMapping> propertyMappings;
    private final List<MessageListener> messageListeners = Lists.newLinkedList();

    /**
     * Creates a new import job
     * 
     * @param name
     *            the name of the job
     * @param plugin
     *            the plugin responsible for this job
     * @param pluginArguments
     *            the argument which will be given to the plugin to perform this
     *            job
     * 
     */
    public ImportJob(String name, ImportPlugin plugin, Object pluginArguments) {
        this.plugin = plugin;
        this.pluginArguments = pluginArguments;
        this.jobName = name;
    }

    /**
     * Calls the plugin with its arguments and sets up the property mappings
     * based on the plugin results
     * 
     * @param propertyDefinitions
     *            a map from keys to property definitions, if a plugin maps to a
     *            property definition with a key in this map the mapping will be
     *            changed to map to the property defintion in the map. If a
     *            plugin maps to a property definition not in the map it will be
     *            added to the map.
     */
    public void computePluginResults(Map<String, PropertyDefinition> propertyDefinitions) {
        if (results != null) {
            results.removeMessageListener(this);
        }
        this.results = plugin.getResults(pluginArguments);
        results.addMessageListener(this);

        Map<String, PropertyDefinition> sourceProperties = results.getSourceProperties();
        propertyMappings = new LinkedHashMap<String, ImportJob.SourcePropertyMapping>(sourceProperties.size());
        for (Map.Entry<String, PropertyDefinition> propDef : sourceProperties.entrySet()) {
            if (propDef.getValue() != null) {
                if (!propDef.getValue().getKey().isEmpty()
                        && propertyDefinitions.containsKey(propDef.getValue().getKey()))
                    propDef.setValue(propertyDefinitions.get(propDef.getValue().getKey()));
                else {
                    propertyDefinitions.put(propDef.getValue().getKey(), propDef.getValue());
                }
            }
            propertyMappings.put(propDef.getKey(), new SourcePropertyMapping(propDef.getValue()));
        }
        titleProperty = results.getTitleMapping();
        if (titleProperty == null)
            titleProperty = sourceProperties.keySet().iterator().next();

        // TODO do we always have at least one property?
        if (titleProperty == null)
            this.titleProperty = propertyMappings.keySet().iterator().next();
    }

    /**
     * @return the source property which is mapped to the <code>Molecule</code>
     *         title
     */
    public String getTitleProperty() {
        return titleProperty;
    }

    /**
     * 
     * @param titlePropery
     *            the source property which is mapped to the
     *            <code>Molecule</code> title
     */
    public void setTitleProperty(String titlePropery) {
        this.titleProperty = titlePropery;
    }

    /**
     * @return the strategy used to merge the <code>Molecule</code> titles
     *         should the same molecule be imported from several sources
     */
    public MergeStrategy getTitleMergeStrategy() {
        return titleMergeStrategy;
    }

    /**
     * @param titleMergeStrategy
     *            the strategy used to merge the <code>Molecule</code> titles
     *            should the same molecule be imported from several sources
     */
    public void setTitleMergeStrategy(MergeStrategy titleMergeStrategy) {
        this.titleMergeStrategy = titleMergeStrategy;
    }

    /**
     * @param structureMergeStrategy
     *            the strategy used to merge molecular structures, should the
     *            same molecule be imported from several sources
     */
    public void setStructureMergeStrategy(MergeStrategy structureMergeStrategy) {
        this.structureMergeStrategy = structureMergeStrategy;
    }

    /**
     * @return the sourceMergeBy
     */
    public String getSourceMergeBy() {
        return sourceMergeBy;
    }

    /**
     * @param sourceMergeBy
     *            the sourceMergeBy to set
     */
    public void setSourceMergeBy(String sourceMergeBy) {
        this.sourceMergeBy = sourceMergeBy;
    }

    /**
     * @return the internalMergeBy
     */
    public PropertyDefinition getInternalMergeBy() {
        return sourceMergeBy == null ? null : internalMergeBy;
    }

    /**
     * @param propertyDefinition
     *            the internalMergeBy to set
     */
    public void setInternalMergeBy(PropertyDefinition propertyDefinition) {
        this.internalMergeBy = propertyDefinition;
    }

    /**
     * @param index
     *            the index or row of the Property name
     * @return the property name for the given Index
     */
    public String getPropertyName(int index) {
        return Iterables.get(propertyMappings.keySet(), index);
    }

    /**
     * @param index
     *            the index or row of the PropertyMapping
     * @return SourcePropertyMapping for the given Index. The
     *         sourceMergeByProperty is not filtered out. Therefore this Mapping
     *         might not be used by the actual import process
     */
    public SourcePropertyMapping getPropertyMapping(int index) {
        return Iterables.get(propertyMappings.values(), index);
    }

    /**
     * @return the strategy used to merge molecular structures, should the same
     *         molecule be imported from several sources
     */
    public MergeStrategy getStructureMergeStrategy() {
        return structureMergeStrategy;
    }

    /**
     * @return the textual description of this source
     */
    public String getDescription() {
        return plugin.getDescription();
    }

    /**
     * @return the propertyMappings defined for this source. The
     *         sourceMergeByProperty is filtered out.
     */
    public Map<String, SourcePropertyMapping> getPropertyMappings() {
        return Collections.unmodifiableMap(Maps.filterKeys(propertyMappings,
                not(getStringFilterPredicate(Collections.singleton(sourceMergeBy)))));
    }

    /**
     * @return the pluginArguments
     */
    public Object getPluginArguments() {
        return pluginArguments;
    }

    /**
     * @param pluginArguments
     *            the pluginArguments to set
     */
    public void setPluginArguments(Object pluginArguments) {
        this.pluginArguments = pluginArguments;
    }

    /**
     * @return the plugin
     */
    public ImportPlugin getPlugin() {
        return plugin;
    }

    /**
     * @return the jobName
     */
    public String getJobName() {
        return jobName;
    }

    /**
     * @return the results
     */
    public PluginResults getResults() {
        return results;
    }

    /**
     * creates a new property definitions for all properties which are not
     * mapped to a property definition already.
     * 
     * @param propertyDefinitions
     *            the list of property definitions which are associated with a
     *            new dataset
     */
    public void mapAllUnmappedProperties(PropertyDefinitionList propertyDefinitions) {
        PropertyType propType;
        for (Entry<String, SourcePropertyMapping> e : propertyMappings.entrySet()) {
            if (e.getValue().getPropertyDefiniton() == null) {
                PropertyDefinition propDef = propertyDefinitions.getByTitle(e.getKey());
                if (propDef == null) {
                    propType = (!results.getProbablyNumeric().contains(e.getKey())) ? PropertyType.StringProperty
                            : PropertyType.NumProperty;
                    propDef = new PropertyDefinition(e.getKey(), "", propType, null, true, false);
                    propertyDefinitions.add(propDef);
                }
                e.getValue().setPropertyDefiniton(propDef);
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return String.format("%s (%s)", jobName, plugin.getTitle());
    }

    /**
     * 
     * @return a combobox model, which allows selecting the title property
     */
    public ComboBoxModel getTitlePropertyModel() {
        class TitlePropertyModel extends AbstractListModel implements ComboBoxModel {

            /*
             * (non-Javadoc)
             * 
             * @see javax.swing.ListModel#getElementAt(int)
             */
            @Override
            public Object getElementAt(int index) {
                return getPropertyName(index);
            }

            /*
             * (non-Javadoc)
             * 
             * @see javax.swing.ListModel#getSize()
             */
            @Override
            public int getSize() {
                return propertyMappings.size();
            }

            /*
             * (non-Javadoc)
             * 
             * @see javax.swing.ComboBoxModel#getSelectedItem()
             */
            @Override
            public Object getSelectedItem() {
                return titleProperty;
            }

            /*
             * (non-Javadoc)
             * 
             * @see javax.swing.ComboBoxModel#setSelectedItem(java.lang.Object)
             */
            @Override
            public void setSelectedItem(Object property) {
                assert (propertyMappings.containsKey(property));
                titleProperty = (String) property;
            }
        }

        return new TitlePropertyModel();
    }

    /**
     * 
     * @return a combobox model, which allows setting the title merge strategy
     */
    public ComboBoxModel getTitleMergeStrategyModel() {
        class TitleMergeStrategyModel extends AbstractListModel implements ComboBoxModel {

            /*
             * (non-Javadoc)
             * 
             * @see javax.swing.ListModel#getElementAt(int)
             */
            @Override
            public Object getElementAt(int index) {
                return Iterables.get(MergeStrategy.getStringStrategies(), index);
            }

            /*
             * (non-Javadoc)
             * 
             * @see javax.swing.ListModel#getSize()
             */
            @Override
            public int getSize() {
                return MergeStrategy.getStringStrategies().size();
            }

            /*
             * (non-Javadoc)
             * 
             * @see javax.swing.ComboBoxModel#getSelectedItem()
             */
            @Override
            public Object getSelectedItem() {
                if (titleMergeStrategy == MergeStrategy.NONE)
                    return null;
                return titleMergeStrategy;
            }

            /*
             * (non-Javadoc)
             * 
             * @see javax.swing.ComboBoxModel#setSelectedItem(java.lang.Object)
             */
            @Override
            public void setSelectedItem(Object selected) {
                assert (MergeStrategy.getStringStrategies().contains(selected));
                titleMergeStrategy = (MergeStrategy) selected;
            }

        }
        return new TitleMergeStrategyModel();
    }

    /**
     * @return a combobox model which allows setting the structure merge
     *         strategy
     */
    public ComboBoxModel getStructureMergeStrategyModel() {
        class StructureMergeStrategyModel extends AbstractListModel implements ComboBoxModel {

            private final ImmutableSet<MergeStrategy> validStrategies = Sets
                    .immutableEnumSet(MergeStrategy.OVERWRITE, MergeStrategy.DONT_OVERWRITE);

            /*
             * (non-Javadoc)
             * 
             * @see javax.swing.ListModel#getElementAt(int)
             */
            @Override
            public Object getElementAt(int index) {
                return Iterables.get(validStrategies, index);
            }

            /*
             * (non-Javadoc)
             * 
             * @see javax.swing.ListModel#getSize()
             */
            @Override
            public int getSize() {
                return validStrategies.size();
            }

            /*
             * (non-Javadoc)
             * 
             * @see javax.swing.ComboBoxModel#getSelectedItem()
             */
            @Override
            public Object getSelectedItem() {
                return structureMergeStrategy;
            }

            /*
             * (non-Javadoc)
             * 
             * @see javax.swing.ComboBoxModel#setSelectedItem(java.lang.Object)
             */
            @Override
            public void setSelectedItem(Object strategy) {
                assert (validStrategies.contains(strategy));
                structureMergeStrategy = (MergeStrategy) strategy;
            }

        }
        return new StructureMergeStrategyModel();
    }

    /**
     * 
     * @return a combobox model which allows setting the property used for
     *         merging
     */
    public ComboBoxModel getMergeByModel() {
        class MergeByModel extends AbstractListModel implements ComboBoxModel {

            /*
             * (non-Javadoc)
             * 
             * @see javax.swing.ListModel#getSize()
             */
            @Override
            public int getSize() {
                return propertyMappings.size() + 1;
            }

            /*
             * (non-Javadoc)
             * 
             * @see javax.swing.ListModel#getElementAt(int)
             */
            @Override
            public Object getElementAt(int index) {
                if (index == 0) {
                    return null;
                }
                return Iterables.get(propertyMappings.keySet(), index - 1, null);
            }

            /*
             * (non-Javadoc)
             * 
             * @see javax.swing.ComboBoxModel#setSelectedItem(java.lang.Object)
             */
            @Override
            public void setSelectedItem(Object anItem) {
                sourceMergeBy = anItem != null ? anItem.toString() : null;
            }

            /*
             * (non-Javadoc)
             * 
             * @see javax.swing.ComboBoxModel#getSelectedItem()
             */
            @Override
            public Object getSelectedItem() {
                return sourceMergeBy;
            }
        }
        return new MergeByModel();
    }

    /**
     * String filter
     * 
     * @param titles
     *            all accepted titles
     * @return the {@link Predicate}
     */
    public static Predicate<String> getStringFilterPredicate(final Collection<String> titles) {
        return new Predicate<String>() {
            @Override
            public boolean apply(String input) {
                return titles.contains(input);
            };
        };
    }

    /**
     * A Mapping which describes how one source property should be imported: It
     * specifies the PropertyDefinition the source property should be mapped to,
     * if the property definition is <code>null</code> the source property will
     * not be imported. <br>
     * Furthermore it specifies a merge strategy to determine behavior if a
     * previous property (either from the same source or a preceding source) is
     * mapped to the same property definition. <br>
     * It may also specify a transform function which will be applied to the
     * property values during import.
     * 
     * @author Henning Garus
     * 
     */
    public static class SourcePropertyMapping {

        private PropertyDefinition propertyDefiniton = null;
        private MathFunction transformFunction = null;
        private MergeStrategy mergeStrategy = MergeStrategy.NONE;

        /**
         * Create a new source property mapping
         * 
         * @param propertyDefiniton
         *            the property definiton this mapping maps to, may be
         *            <code>null</code>
         */
        public SourcePropertyMapping(PropertyDefinition propertyDefiniton) {
            this.propertyDefiniton = propertyDefiniton;
        }

        /**
         * @return the propertyDefiniton
         */
        public PropertyDefinition getPropertyDefiniton() {
            return propertyDefiniton;
        }

        /**
         * @return the transformFunction
         */
        public MathFunction getTransformFunction() {
            return transformFunction;
        }

        /**
         * @return the mergeStrategy
         */
        public MergeStrategy getMergeStrategy() {
            return mergeStrategy;
        }

        /**
         * @param propertyDefiniton
         *            the propertyDefiniton to set
         */
        public void setPropertyDefiniton(PropertyDefinition propertyDefiniton) {
            this.propertyDefiniton = propertyDefiniton;
        }

        /**
         * @param transformFunction
         *            the transformFunction to set
         */
        public void setTransformFunction(MathFunction transformFunction) {
            this.transformFunction = transformFunction;
        }

        /**
         * @param mergeStrategy
         *            the mergeStrategy to set
         */
        public void setMergeStrategy(MergeStrategy mergeStrategy) {
            this.mergeStrategy = mergeStrategy;
        }

    }

    /**
     * add a message listener
     * 
     * @param listener
     */
    public void addMessageListener(MessageListener listener) {
        messageListeners.add(listener);
    }

    /**
     * remove a message listener
     * 
     * @param listener
     */
    public void removeMessageListener(MessageListener listener) {
        messageListeners.remove(listener);
    }

    private void sendMessage(Message message) {
        for (MessageListener l : messageListeners)
            l.receiveMessage(message);
    }

    @Override
    public void receiveMessage(Message message) {
        // receive message from job
        Message msg = new Message(message.getType(), message.getMoleculeTitle(), message.getPropertyDefinition(),
                this);
        sendMessage(msg);
    }

    /**
     * @return the number of PropertyMappings
     */
    public int getPropertyMappingSize() {
        return propertyMappings.size();
    }
}