/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.engine.depgraph.ambiguity; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import com.opengamma.engine.value.ValueRequirement; import com.opengamma.util.ArgumentChecker; /** * Service for checking whether, and how, any given value requirement may be ambiguous. */ public final class FullRequirementResolution { private final ValueRequirement _requirement; private final List<Collection<RequirementResolution>> _resolutions = new ArrayList<Collection<RequirementResolution>>(); private int _hashCode; public FullRequirementResolution(final ValueRequirement requirement) { _requirement = ArgumentChecker.notNull(requirement, "requirement"); _hashCode = getClass().hashCode() * 31 + _requirement.hashCode(); } /** * Returns the requirement this resolution result describes. * * @return the requirement, not null */ public ValueRequirement getRequirement() { return _requirement; } /** * Returns all of the possible resolutions in descending priority order. * <p> * Each entry in the list will, under non-ambiguous circumstances, be a singleton collection. When an ambiguity exists elements will be sets containing all of the possible resolutions with null * included if non-resolution is a possible outcome. * * @return the resolutions, not null */ public List<Collection<RequirementResolution>> getResolutions() { return Collections.unmodifiableList(_resolutions); } // Note: there is not a addResolution method to avoid confusion. It would add a singleton set; repeated calls to it would not be the same as making a single call to addResolutions which is the // more typical behavior when there appear to be bulk and single operation /** * Stores a resolution result set into the overall result. If this is a non-ambiguous result, the collection must contain only a single value. If this is an ambiguous result, the collection may * contain all possible values and null if one of the outcomes was non-resolution. * * @param resolutions the possible resolutions for this value requirement, not null, and non-empty * @throws IllegalArgumentException if the parameters are invalid or attempt to create a recursive structure */ public void addResolutions(Collection<RequirementResolution> resolutions) { resolutions = Collections.unmodifiableSet(new HashSet<RequirementResolution>(resolutions)); final int size = resolutions.size(); if (size == 0) { // Empty is not allowed throw new IllegalArgumentException("resolutions"); } else if (size == 1) { if (resolutions.contains(null)) { // Nulls are only allowed when there is an ambiguity throw new IllegalArgumentException("resolutions"); } resolutions = Collections.singleton(resolutions.iterator().next()); } for (RequirementResolution resolution : resolutions) { if ((resolution != null) && resolution.contains(this)) { throw new IllegalArgumentException("Circular reference from " + resolution + " to " + this); } } _resolutions.add(resolutions); _hashCode = (_hashCode * 31) + resolutions.hashCode(); } /** * Tests whether the given resolution is present in any of the inputs to this resolution. This prevents recursive structures from being constructed. */ /* package */boolean contains(final FullRequirementResolution parent) { for (Collection<RequirementResolution> resolutions : _resolutions) { for (RequirementResolution resolution : resolutions) { if ((resolution != null) && resolution.contains(parent)) { return true; } } } return false; } /** * Tests if there is any ambiguity in this resolution. This only applies to the exact value requirement; there may be ambiguities in resolving items deeper in the graph but which have no effect on * this requirement. * * @return true if there is an ambiguity, false otherwise */ public boolean isAmbiguous() { for (Collection<RequirementResolution> resolutions : _resolutions) { if (resolutions.size() > 1) { return true; } } return false; } /** * Tests if there is any ambiguity in this resolution or any of its inputs. * * @return true if there is an ambiguity, false otherwise */ public boolean isDeeplyAmbiguous() { for (Collection<RequirementResolution> resolutions : _resolutions) { if (resolutions.size() > 1) { return true; } if (resolutions.iterator().next().isAmbiguous()) { return true; } } return false; } /** * Tests if there is a successful resolution of this requirement. * * @return true if there is a resolution, false otherwise */ public boolean isResolved() { return !_resolutions.isEmpty(); } // Object @Override public boolean equals(final Object o) { if (o == this) { return true; } if (!(o instanceof FullRequirementResolution)) { return false; } final FullRequirementResolution other = (FullRequirementResolution) o; return (_hashCode == other._hashCode) && _requirement.equals(other._requirement) && _resolutions.equals(other._resolutions); } @Override public int hashCode() { return _hashCode; } @Override public String toString() { return _requirement + "->" + _resolutions; } }