/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.engine.target; import java.io.ObjectStreamException; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicReference; import com.opengamma.core.position.Portfolio; import com.opengamma.core.position.PortfolioNode; import com.opengamma.core.position.Position; import com.opengamma.core.position.PositionOrTrade; import com.opengamma.core.position.Trade; import com.opengamma.core.security.Security; import com.opengamma.engine.fudgemsg.ComputationTargetTypeFudgeBuilder; import com.opengamma.engine.target.resolver.CreditCurveIdentifierResolver; import com.opengamma.engine.target.resolver.CurrencyResolver; import com.opengamma.engine.target.resolver.ObjectResolver; import com.opengamma.engine.target.resolver.PrimitiveResolver; import com.opengamma.engine.target.resolver.UnorderedCurrencyPairResolver; import com.opengamma.id.UniqueIdentifiable; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.PublicAPI; import com.opengamma.util.credit.CreditCurveIdentifier; import com.opengamma.util.money.Currency; import com.opengamma.util.money.UnorderedCurrencyPair; /** * The type of the target a computation step will act on or a data sourcing function will provide. */ @PublicAPI public abstract class ComputationTargetType implements Serializable { private static final long serialVersionUID = 1L; /** * Try to keep the instances unique. This reduces the operating memory footprint. */ private static final ConcurrentMap<?, ?> s_computationTargetTypes = new ConcurrentHashMap<Object, Object>() { private static final long serialVersionUID = 1L; @Override public Object get(final Object type) { Object instance = super.get(type); if (instance != null) { return instance; } instance = super.putIfAbsent(type, type); if (instance != null) { return instance; } return type; } }; /** * A map of classes to the computation target types. This is to optimize the {@link #of(Class)} method for common cases used during target resolution. To modify the map, use a copy-and-replace * approach. */ private static final AtomicReference<Map<Class<?>, ComputationTargetType>> s_classTypes = new AtomicReference<Map<Class<?>, ComputationTargetType>>(new HashMap<Class<?>, ComputationTargetType>()); /** * A full portfolio structure. This will seldom be needed for calculations - the root node is usually more important from an aggregation perspective. */ public static final ObjectComputationTargetType<Portfolio> PORTFOLIO = defaultObject(Portfolio.class, "PORTFOLIO"); /** * An ordered list of positions and other portfolio nodes. */ public static final ObjectComputationTargetType<PortfolioNode> PORTFOLIO_NODE = defaultObject(PortfolioNode.class, "PORTFOLIO_NODE"); /** * A position. */ public static final ObjectComputationTargetType<Position> POSITION = defaultObject(Position.class, "POSITION"); /** * A security. */ public static final ObjectComputationTargetType<Security> SECURITY = defaultObject(Security.class, "SECURITY"); /** * A trade. */ public static final ObjectComputationTargetType<Trade> TRADE = defaultObject(Trade.class, "TRADE"); /** * A simple type, for trivial items for which a unique ID (which can just be an arbitrary string triple if scheme, value and version used) that does not need resolving is sufficient. */ public static final PrimitiveComputationTargetType<Primitive> PRIMITIVE = defaultPrimitive(Primitive.class, "PRIMITIVE", new PrimitiveResolver()); /** * A currency. */ public static final PrimitiveComputationTargetType<Currency> CURRENCY = defaultPrimitive(Currency.class, "CURRENCY", new CurrencyResolver()); /** * An unordered currency pair. */ public static final PrimitiveComputationTargetType<UnorderedCurrencyPair> UNORDERED_CURRENCY_PAIR = defaultPrimitive(UnorderedCurrencyPair.class, "UNORDERED_CURRENCY_PAIR", new UnorderedCurrencyPairResolver()); /** * A credit curve identifier. */ public static final PrimitiveComputationTargetType<CreditCurveIdentifier> CREDIT_CURVE_IDENTIFIER = defaultPrimitive(CreditCurveIdentifier.class, "CREDIT_CURVE_IDENTIFIER", new CreditCurveIdentifierResolver()); /** * A wildcard type. This may be used when declaring the target type of a function. It should not be used as part of a target reference as the lack of specific type details will prevent a resolver * from producing the concrete target object. */ public static final ObjectComputationTargetType<UniqueIdentifiable> ANYTHING = defaultObject(UniqueIdentifiable.class, "ANYTHING"); /** * A position or a trade object. This is a union type for an object that is either an instance of {@link Position}, {@link Trade}, or both. This may be used when declaring the target type of a * function. It should not normally be used as part of a target reference as the resolver will have to try multiple resolution strategies to determine the concrete instance. */ public static final ObjectComputationTargetType<PositionOrTrade> POSITION_OR_TRADE = ObjectComputationTargetType.of(POSITION.or(TRADE), PositionOrTrade.class); /** * An explicit null, for the anonymous target. */ public static final ComputationTargetType NULL = new NullComputationTargetType(); /** * An equivalent to the previous use of {@code PRIMITIVE}. It means primitives in their new sense, plus currencies and unordered currency pairs that now have explicit types. * * @deprecated This is not particularly efficient to use and may not be correct, but is better than using {@link #ANYTHING}. It will be removed at the first opportunity. */ @Deprecated public static final ComputationTargetType LEGACY_PRIMITIVE = PRIMITIVE.or(CURRENCY).or(UNORDERED_CURRENCY_PAIR); private final int _hashCode; /* package */ComputationTargetType(final int hashCode) { _hashCode = hashCode; } private static <T extends UniqueIdentifiable> ComputationTargetType defaultType(final Class<T> clazz, final String name) { final ComputationTargetType type = of(clazz, name, true); // This is safe; we only get called like this during class initialization s_classTypes.get().put(clazz, type); return type; } private static <T extends UniqueIdentifiable> ObjectComputationTargetType<T> defaultObject(final Class<T> clazz, final String name) { return ObjectComputationTargetType.of(defaultType(clazz, name), clazz); } private static <T extends UniqueIdentifiable> PrimitiveComputationTargetType<T> defaultPrimitive(final Class<T> clazz, final String name, final ObjectResolver<T> resolver) { return PrimitiveComputationTargetType.of(defaultType(clazz, name), clazz, resolver); } private static ComputationTargetType of(final Class<? extends UniqueIdentifiable> clazz, final String name, final boolean nameWellKnown) { return (ComputationTargetType) s_computationTargetTypes.get(new ClassComputationTargetType(clazz, name, nameWellKnown)); } public static <T extends UniqueIdentifiable> ComputationTargetType of(final Class<T> clazz) { Map<Class<?>, ComputationTargetType> cache = s_classTypes.get(); ComputationTargetType type = cache.get(clazz); if (type != null) { return type; } ArgumentChecker.notNull(clazz, "clazz"); type = of(clazz, clazz.getSimpleName(), false); final Map<Class<?>, ComputationTargetType> updatedCache = new HashMap<Class<?>, ComputationTargetType>(cache); updatedCache.put(clazz, type); s_classTypes.compareAndSet(cache, updatedCache); return type; } public ComputationTargetType containing(final Class<? extends UniqueIdentifiable> clazz) { return containing(of(clazz)); } public ComputationTargetType containing(final ComputationTargetType inner) { ArgumentChecker.notNull(inner, "inner"); return (ComputationTargetType) s_computationTargetTypes.get(new NestedComputationTargetType(this, inner)); } public ComputationTargetType or(final Class<? extends UniqueIdentifiable> clazz) { return or(of(clazz)); } /** * Creates a composite type that is either this instance or the alternative. * * @param alternative the alternative type to this one, not null * @return the composite type instance, not null */ public ComputationTargetType or(final ComputationTargetType alternative) { ArgumentChecker.notNull(alternative, "alternative"); return (ComputationTargetType) s_computationTargetTypes.get(new MultipleComputationTargetType(this, alternative)); } /** * Creates a composite type that is any of the given alternatives. * <p> * {@code multiple(a, b, c)} will give the same result as {@code a.or(b).or(c)} but is more efficient. * * @param alternatives the alternative types to this one, not null, not containing null, and with at least two different types * @return the composite type instance, not null */ public static ComputationTargetType multiple(final ComputationTargetType... alternatives) { ArgumentChecker.notNull(alternatives, "alternatives"); return (ComputationTargetType) s_computationTargetTypes.get(new MultipleComputationTargetType(alternatives)); } /** * Tests if the given target object is valid for this type descriptor. * * @param target the object to test * @return true if the object is compatible, false if the object is not of the descriptor's type */ public abstract boolean isCompatible(UniqueIdentifiable target); /** * Tests if the given type descriptor is compatible with this type descriptor. The target class must be the same class or a subclass at each nesting level. * * @param type the type to test * @return true if the type is compatible, false otherwise */ public abstract boolean isCompatible(ComputationTargetType type); /** * Tests if the given target class is valid for this type descriptor. * * @param clazz the object class to test * @return true if the class is compatible, false if it is not of the descriptor's type */ public abstract boolean isCompatible(Class<? extends UniqueIdentifiable> clazz); private static final class Parser { private final String _buffer; private int _index; public Parser(final String str) { _buffer = str; } private static boolean isIdentifier(final char c) { return (c != '/') && (c != '|') && (c != '(') && (c != ')'); } public ComputationTargetType parse() { ComputationTargetType type = null; do { switch (_buffer.charAt(_index)) { case '/': if (type == null) { throw new IllegalArgumentException("Unexpected / at index " + _index + " of " + _buffer); } else { _index++; type = new NestedComputationTargetType(type, parse()); break; } case '|': if (type == null) { throw new IllegalArgumentException("Unexpected | at index " + _index + " of " + _buffer); } else { _index++; type = new MultipleComputationTargetType(type, parse()); break; } case '(': _index++; if (type == null) { type = parse(); } else { throw new IllegalArgumentException("Unexpected ( at index " + _index + " of " + _buffer); } break; case ')': if (type == null) { throw new IllegalArgumentException("Unexpected ) at index " + _index + " of " + _buffer); } else { _index++; return type; } default: if (type != null) { throw new IllegalArgumentException("Unexpected identifier at index " + _index + " of " + _buffer); } final StringBuilder sb = new StringBuilder(); do { sb.append(_buffer.charAt(_index++)); } while ((_index < _buffer.length()) && isIdentifier(_buffer.charAt(_index))); type = ComputationTargetTypeFudgeBuilder.fromString(sb.toString()); break; } } while (_index < _buffer.length()); return type; } } /** * Parses a string produced by {@link #toString}. * * @param str the string to parse, not null * @return the computation target type, not null */ public static ComputationTargetType parse(final String str) { return new Parser(str).parse(); } /** * Apply the visitor operation to this target type. * * @param <T> the return type of the visitor * @param <D> the parameter data type of the visitor * @param visitor the visitor to apply, not null * @param data the data value to pass to the visitor * @return the result of the visitor operation */ public abstract <D, T> T accept(ComputationTargetTypeVisitor<D, T> visitor, D data); /** * Produces a string representation of the type that includes outer brackets if necessary to maintain the structure of composite types for handling by {@link #parse}. * * @param sb the string builder to append the string representation to */ protected abstract void toStringNested(StringBuilder sb); /** * Produces a string representation of the type that can be parsed by {@link #parse} and is vaguely human readable. * * @return the string representation */ @Override public abstract String toString(); /** * Produces a string representation of the type that includes outer brackets if necessary to maintain the structure of composite types for viewing by a user. * * @param sb the string builder to append the string representation to */ protected abstract void getNameNested(StringBuilder sb); /** * Produces a string representation of the type that can be displayed to the user. * * @return the string representation */ public abstract String getName(); @Override public abstract boolean equals(Object o); @Override public final int hashCode() { return _hashCode; } /** * Tests if the leaf target type(s) matches the given type. {@code x.isTargetType(y) == y.isCompatible(x) } * * @param type the type to test, not null * @return true if the leaf target type (or one of the types if there are multiple choices) matches the given type, false otherwise */ public abstract boolean isTargetType(ComputationTargetType type); /** * Tests if the leaf target type(s) matches the given type. * * @param type the type to test, not null * @return true if the leaf target type (or one of the types if there are multiple choices) matches the given type, false otherwise */ public abstract boolean isTargetType(Class<? extends UniqueIdentifiable> type); protected Object readResolve() throws ObjectStreamException { return s_computationTargetTypes.get(this); } }