/** * Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.engine.depgraph; import java.io.CharArrayWriter; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import com.opengamma.engine.function.ParameterizedFunction; import com.opengamma.engine.value.ValueRequirement; import com.opengamma.engine.value.ValueSpecification; /** * Describes a resolution failure. The implementation is intended to provide low-cost construction of failure information, at the cost of a more complex querying/inspection algorithm. */ public final class ResolutionFailureImpl extends ResolutionFailure { private final ValueRequirement _valueRequirement; private final List<Object> _events = new LinkedList<Object>(); // Construction private ResolutionFailureImpl(final ValueRequirement valueRequirement) { _valueRequirement = valueRequirement; } protected static ResolutionFailure recursiveRequirement(final ValueRequirement valueRequirement) { return new ResolutionFailureImpl(valueRequirement).appendEvent(Status.RECURSIVE_REQUIREMENT); } protected static ResolutionFailure functionApplication(final ValueRequirement valueRequirement, final ParameterizedFunction function, final ValueSpecification outputSpecification) { return functionApplication(valueRequirement, function.getFunction().getFunctionDefinition().getUniqueId(), outputSpecification); } protected static ResolutionFailure functionApplication(final ValueRequirement valueRequirement, final String function, final ValueSpecification outputSpecification) { return new ResolutionFailureImpl(valueRequirement).appendEvent(function).appendEvent(outputSpecification); } protected static ResolutionFailure noFunctions(final ValueRequirement valueRequirement) { return new ResolutionFailureImpl(valueRequirement).appendEvent(Status.NO_FUNCTIONS); } protected static ResolutionFailure couldNotResolve(final ValueRequirement valueRequirement) { return new ResolutionFailureImpl(valueRequirement).appendEvent(Status.COULD_NOT_RESOLVE); } protected static ResolutionFailure unsatisfied(final ValueRequirement valueRequirement) { return new ResolutionFailureImpl(valueRequirement).appendEvent(Status.UNSATISFIED); } protected static ResolutionFailure marketDataMissing(final ValueRequirement valueRequirement) { return new ResolutionFailureImpl(valueRequirement).appendEvent(Status.MARKET_DATA_MISSING); } @Override protected ResolutionFailure additionalRequirement(final ValueRequirement valueRequirement, final ResolutionFailure failure) { return appendEvent(Status.ADDITIONAL_REQUIREMENT).requirement(valueRequirement, failure); } @Override protected ResolutionFailure requirement(final ValueRequirement valueRequirement, final ResolutionFailure failure) { return appendEvent((failure != null) ? failure : valueRequirement); } @Override protected ResolutionFailure requirements(final Map<ValueSpecification, ValueRequirement> available) { return appendEvent(available); } @Override protected ResolutionFailure getResultsFailed() { return appendEvent(Status.GET_RESULTS_FAILED); } @Override protected ResolutionFailure getAdditionalRequirementsFailed() { return appendEvent(Status.GET_ADDITIONAL_REQUIREMENTS_FAILED); } @Override protected ResolutionFailure lateResolutionFailure() { return appendEvent(Status.LATE_RESOLUTION_FAILURE); } @Override protected ResolutionFailure getRequirementsFailed() { return appendEvent(Status.GET_REQUIREMENTS_FAILED); } @Override protected ResolutionFailure suppressed() { return appendEvent(Status.SUPPRESSED); } @Override protected ResolutionFailure checkFailure(final ValueRequirement valueRequirement) { return this; } private synchronized ResolutionFailureImpl appendEvent(final Object event) { _events.add(event); return this; } // Query @Override public ValueRequirement getValueRequirement() { return _valueRequirement; } public List<Object> getEvents() { return _events; } @SuppressWarnings("unchecked") @Override public synchronized <T> Collection<T> accept(final ResolutionFailureVisitor<T> visitor) { final LinkedList<T> result = new LinkedList<T>(); final Iterator<?> itr = _events.iterator(); String function = null; ValueSpecification outputSpecification = null; final Map<ValueSpecification, ValueRequirement> satisfied = new HashMap<ValueSpecification, ValueRequirement>(); final Set<ResolutionFailure> unsatisfied = new HashSet<ResolutionFailure>(); final Set<ResolutionFailure> unsatisfiedAdditional = new HashSet<ResolutionFailure>(); while (itr.hasNext()) { final Object event = itr.next(); if (event instanceof String) { if (function != null) { result.add(visitor.visitFunction(getValueRequirement(), function, outputSpecification, satisfied, unsatisfied, unsatisfiedAdditional)); } function = (String) event; outputSpecification = (ValueSpecification) itr.next(); satisfied.clear(); unsatisfied.clear(); unsatisfiedAdditional.clear(); } else if (event instanceof Status) { switch ((Status) event) { case ADDITIONAL_REQUIREMENT: { assert function != null; final Object req = itr.next(); if (req instanceof ResolutionFailure) { unsatisfiedAdditional.add((ResolutionFailure) req); } else { unsatisfiedAdditional.add(unsatisfied((ValueRequirement) req)); } break; } case COULD_NOT_RESOLVE: if (function != null) { result.add(visitor.visitFunction(getValueRequirement(), function, outputSpecification, satisfied, unsatisfied, unsatisfiedAdditional)); function = null; } result.add(visitor.visitCouldNotResolve(getValueRequirement())); break; case GET_ADDITIONAL_REQUIREMENTS_FAILED: assert function != null; result.add(visitor.visitGetAdditionalRequirementsFailed(getValueRequirement(), function, outputSpecification, satisfied)); function = null; break; case GET_RESULTS_FAILED: assert function != null; result.add(visitor.visitGetResultsFailed(getValueRequirement(), function, outputSpecification, satisfied)); function = null; break; case GET_REQUIREMENTS_FAILED: assert function != null; result.add(visitor.visitGetRequirementsFailed(getValueRequirement(), function, outputSpecification)); function = null; break; case LATE_RESOLUTION_FAILURE: assert function != null; result.add(visitor.visitLateResolutionFailure(getValueRequirement(), function, outputSpecification, satisfied)); function = null; break; case MARKET_DATA_MISSING: if (function != null) { result.add(visitor.visitFunction(getValueRequirement(), function, outputSpecification, satisfied, unsatisfied, unsatisfiedAdditional)); function = null; } result.add(visitor.visitMarketDataMissing(getValueRequirement())); break; case NO_FUNCTIONS: if (function != null) { result.add(visitor.visitFunction(getValueRequirement(), function, outputSpecification, satisfied, unsatisfied, unsatisfiedAdditional)); function = null; } result.add(visitor.visitNoFunctions(getValueRequirement())); break; case RECURSIVE_REQUIREMENT: if (function != null) { result.add(visitor.visitFunction(getValueRequirement(), function, outputSpecification, satisfied, unsatisfied, unsatisfiedAdditional)); function = null; } result.add(visitor.visitRecursiveRequirement(getValueRequirement())); break; case SUPPRESSED: assert function != null; result.add(visitor.visitBlacklistSuppressed(getValueRequirement(), function, outputSpecification, satisfied)); function = null; break; case UNSATISFIED: if (function != null) { result.add(visitor.visitFunction(getValueRequirement(), function, outputSpecification, satisfied, unsatisfied, unsatisfiedAdditional)); function = null; } result.add(visitor.visitUnsatisfied(getValueRequirement())); break; default: throw new IllegalStateException("event = " + event); } } else if (event instanceof ValueRequirement) { assert function != null; unsatisfied.add(unsatisfied((ValueRequirement) event)); } else if (event instanceof ResolutionFailure) { assert function != null; unsatisfied.add((ResolutionFailure) event); } else if (event instanceof Map<?, ?>) { assert function != null; satisfied.putAll((Map<ValueSpecification, ValueRequirement>) event); } else { throw new IllegalStateException("event = " + event); } } if (function != null) { result.add(visitor.visitFunction(getValueRequirement(), function, outputSpecification, satisfied, unsatisfied, unsatisfiedAdditional)); } return result; } // Composition /** * Merge the causes of failure from the other into this. * * @param failureRef cause of failure */ @Override protected synchronized void merge(final ResolutionFailure failureRef) { final ResolutionFailureImpl failure = (ResolutionFailureImpl) failureRef; synchronized (failure) { final Iterator<Object> itrNew = failure._events.iterator(); Object eventNew = itrNew.next(); do { if (eventNew instanceof String) { final String function = (String) eventNew; final ValueSpecification outputSpecification = (ValueSpecification) itrNew.next(); // Extract the events that correspond to this function application final List<Object> newEvents = new LinkedList<Object>(); //CSOFF scan: // CSON do { eventNew = itrNew.next(); if (eventNew instanceof String) { break scan; } else if (eventNew instanceof Status) { switch ((Status) eventNew) { case COULD_NOT_RESOLVE: case MARKET_DATA_MISSING: case NO_FUNCTIONS: case RECURSIVE_REQUIREMENT: case UNSATISFIED: break scan; } } newEvents.add(eventNew); if (!itrNew.hasNext()) { eventNew = null; break scan; } } while (true); // If the function application already exists, append the events final ListIterator<Object> itrThis = _events.listIterator(); boolean matched = false; //CSOFF scanStartEvent: //CSON while (itrThis.hasNext()) { Object eventThis = itrThis.next(); if (function.equals(eventThis)) { eventThis = itrThis.next(); if (outputSpecification.equals(eventThis)) { // Have found a match; consider the existing failure events //CSOFF scanFailureEvent: //CSON while (itrThis.hasNext()) { eventThis = itrThis.next(); if (eventThis instanceof String) { itrThis.previous(); break scanFailureEvent; } else if (eventThis instanceof Status) { switch ((Status) eventThis) { case ADDITIONAL_REQUIREMENT: // Discard any matching "new" event eventThis = itrThis.next(); final ListIterator<Object> itrNewEvents = newEvents.listIterator(); while (itrNewEvents.hasNext()) { Object newEvent = itrNewEvents.next(); if (newEvent == Status.ADDITIONAL_REQUIREMENT) { newEvent = itrNewEvents.next(); if (eventThis.equals(newEvent)) { itrNewEvents.remove(); itrNewEvents.previous(); itrNewEvents.remove(); break; } } } break; case GET_ADDITIONAL_REQUIREMENTS_FAILED: case GET_RESULTS_FAILED: case GET_REQUIREMENTS_FAILED: case LATE_RESOLUTION_FAILURE: case SUPPRESSED: // Discard any matching "new" event newEvents.remove(eventThis); continue scanStartEvent; case COULD_NOT_RESOLVE: case MARKET_DATA_MISSING: case NO_FUNCTIONS: case RECURSIVE_REQUIREMENT: case UNSATISFIED: itrThis.previous(); break scanFailureEvent; default: throw new IllegalStateException("event = " + eventThis); } } else { // Discard any matching "new" event final Iterator<Object> itrNewEvents = newEvents.iterator(); while (itrNewEvents.hasNext()) { final Object newEvent = itrNewEvents.next(); if (eventThis.equals(newEvent)) { itrNewEvents.remove(); break; } else if (newEvent == Status.ADDITIONAL_REQUIREMENT) { itrNewEvents.next(); } } } } // Iterator is now positioned just before the next "start" event for (Object newEvent : newEvents) { itrThis.add(newEvent); } matched = true; break; } } } // If the function application didn't exist, append the application and events if (!matched && !newEvents.isEmpty()) { _events.add(function); _events.add(outputSpecification); _events.addAll(newEvents); } } else if (eventNew instanceof Status) { if (!_events.contains(eventNew)) { _events.add(eventNew); } if (itrNew.hasNext()) { eventNew = itrNew.next(); } else { eventNew = null; } } else { throw new IllegalStateException("event = " + eventNew); } } while (eventNew != null); } } // Misc @Override public String toString() { final CharArrayWriter writer = new CharArrayWriter(); accept(new ResolutionFailurePrinter(writer)); return writer.toString(); } @Override public synchronized Object clone() { final ResolutionFailureImpl copy = new ResolutionFailureImpl(getValueRequirement()); copy._events.addAll(_events); return copy; } /** * Tests this resolution failure object with another for equality. Note that the caller must ensure that the monitor for both is held, or a suitable exclusion lock is held at an outer level. * * @param obj object to compare to * @return true if the objects are equal */ @Override public boolean equals(final Object obj) { if (obj == this) { return true; } if (!(obj instanceof ResolutionFailureImpl)) { return false; } final ResolutionFailureImpl other = (ResolutionFailureImpl) obj; return getValueRequirement().equals(other.getValueRequirement()) && _events.equals(other._events); } @Override public int hashCode() { return getValueRequirement().hashCode(); } }