All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.decision_deck.jmcda.structure.weights.WeightsImpl Maven / Gradle / Ivy

Go to download

The base classes of the J-MCDA project. Contains the main structure classes that define MCDA concepts such as alternatives and performance matrixes.

The newest version!
package org.decision_deck.jmcda.structure.weights;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.decision_deck.jmcda.structure.Criterion;
import org.decision_deck.utils.PredicateUtils;
import org.decision_deck.utils.collection.MapEvents.PreAdditionEvent;
import org.decision_deck.utils.collection.MapEvents.PreRemovalEvent;
import org.decision_deck.utils.collection.ObservableMap;

import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.eventbus.Subscribe;

public class WeightsImpl extends ObservableMap implements Weights {

    private static class NormalizedWeights extends ForwardingWeights implements Weights {

	public NormalizedWeights(final Weights delegate) {
	    super(wrap(Maps.transformValues(delegate, new Function() {
		@Override
		public Double apply(Double input) {
		    return Double.valueOf(input.doubleValue() / delegate.getSum());
		}
	    })));
	}

	@Override
	public Weights getNormalized() {
	    return this;
	}
    }

    /**
     * Unverified code, unused - to delete?
     * 
     * @author Olivier Cailloux
     * 
     */
    @SuppressWarnings("unused")
    private static class WeightsIterator implements Iterator {
	private final WeightsImpl m_delegateWeights;
	private final Iterator m_iterator;
	private Criterion m_lastKey;

	public WeightsIterator(final WeightsImpl weights) {
	    if (weights == null) {
		throw new NullPointerException("Must have a delegate.");
	    }
	    m_delegateWeights = weights;
	    m_lastKey = null;
	    m_iterator = weights.keySet().iterator();
	}

	@Override
	public boolean hasNext() {
	    return m_iterator.hasNext();
	}

	@Override
	public Criterion next() {
	    m_lastKey = m_iterator.next();
	    return m_lastKey;
	}

	@Override
	public void remove() {
	    if (m_lastKey == null) {
		throw new IllegalStateException("One remove per call to next allowed.");
	    }
	    m_delegateWeights.remove(m_lastKey);
	    m_lastKey = null;
	}
    }

    private static final Predicate KEY_PREDICATE = Predicates. notNull();
    private static final Predicate VALUE_PREDICATE = Predicates. and(Predicates. notNull(),
	    PredicateUtils.atLeast(0d));

    private static final Predicate> ZE_PREDICATE = PredicateUtils.asEntryPredicate(
	    KEY_PREDICATE, VALUE_PREDICATE);

    static public WeightsImpl create() {
	return new WeightsImpl();
    }

    static public WeightsImpl newWeights(WeightsImpl source) {
	return new WeightsImpl(source);
    }

    static public WeightsImpl wrap(Map delegateMap) {
	return new WeightsImpl(delegateMap);
    }

    /**
     * Vocabulary note: this object is dirty iff the cached computations must be re-computed. This field is
     * true iff this object is dirty.
     */
    private boolean m_dirty;

    /**
     * The sum of the weights contained in this object. Valid iff this object is not dirty.
     */
    private double m_sum;

    private WeightsImpl() {
	super(Maps.filterEntries(Maps. newLinkedHashMap(), ZE_PREDICATE));
	m_sum = 0d;
	m_dirty = false;
	register(this);
    }

    /**
     * This object assumes ownership of the delegate map.
     * 
     * @param delegateMap
     *            no null key or values, all values at least zero.
     */
    private WeightsImpl(Map delegateMap) {
	super(Maps.filterEntries(delegateMap, ZE_PREDICATE));
	assert (Iterables.all(delegateMap.entrySet(), ZE_PREDICATE));
	m_dirty = true;
	register(this);
    }

    private WeightsImpl(WeightsImpl source) {
	super(Maps.filterEntries(Maps. newLinkedHashMap(), ZE_PREDICATE));
	m_sum = source.m_sum;
	m_dirty = source.m_dirty;
	putAll(source);
	register(this);
    }

    @Override
    public boolean approxEquals(Weights w2, double tolerance) {
	if (this == w2) {
	    return true;
	}
	if (w2 == null) {
	    return false;
	}
	if (!keySet().equals(w2.keySet())) {
	    return false;
	}

	final Weights w2Param = w2;
	final double toleranceParam = tolerance;

	final Set keySet = keySet();
	for (Criterion criterion : keySet) {
	    final double weight1 = getWeightBetter(criterion);
	    final double weight2 = w2Param.getWeightBetter(criterion);
	    final double diff = weight1 - weight2;
	    if (Math.abs(diff) > toleranceParam) {
		return false;
	    }
	}

	return true;
    }

    private void computeSum() {
	m_sum = 0d;
	for (Double weight : values()) {
	    m_sum = m_sum + weight.doubleValue();
	}

	m_dirty = false;
    }

    @Override
    public Weights getNormalized() {
	// if (m_dirty) {
	// computeSum();
	// }
	// final WeightsImpl normWeights = new WeightsImpl();
	// for (final Entry entry : m_weights.entrySet()) {
	// final Double weight = entry.getValue();
	// final double norm = weight.doubleValue() / m_sum;
	// normWeights.putWeight(entry.getKey(), norm);
	// }
	// return normWeights;
	return new NormalizedWeights(this);
    }

    @Override
    public double getSum() {
	if (m_dirty) {
	    computeSum();
	}
	return m_sum;
    }

    @Override
    public double getWeightBetter(Criterion criterion) {
	checkArgument(containsKey(criterion), "No weight for " + criterion + ".");
	return get(criterion).doubleValue();
    }

    @Override
    public Double put(Criterion key, Double value) {
	/**
	 * The underlying map will refuse some entries, but will post pre- events before it realizes these entries are
	 * incorrect. This permits to avoid incorrect posting.
	 */
	checkArgument(KEY_PREDICATE.apply(key));
	checkArgument(VALUE_PREDICATE.apply(value), "Invalid value: " + value + ", key: " + key + ".");

	return super.put(key, value);
    }

    @Override
    public Double putWeight(Criterion criterion, double weight) {
	checkNotNull(criterion);
	final Double theWeight = Double.valueOf(weight);
	return put(criterion, theWeight);
    }

    @Override
    public String toString() {
	final ToStringHelper helper = Objects.toStringHelper(this);
	helper.addValue(delegate());
	return helper.toString();
    }

    @Subscribe
    public void updateSum(PreAdditionEvent addition) {
	if (m_dirty) {
	    return;
	}
	assert addition != null;
	assert addition.getValue() != null;
	m_sum += addition.getValue().doubleValue();
    }

    @Subscribe
    public void updateSum(PreRemovalEvent removal) {
	if (m_dirty) {
	    return;
	}
	m_sum -= removal.getValue().doubleValue();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy