/**
* 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.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.opengamma.engine.ComputationTarget;
import com.opengamma.engine.ComputationTargetResolver;
import com.opengamma.engine.ComputationTargetSpecification;
import com.opengamma.id.UniqueIdentifiable;
/**
* Helper methods for writing {@link ComputationTargetResolver} instances.
*/
public class ComputationTargetResolverUtils {
private static final Logger s_logger = LoggerFactory.getLogger(ComputationTargetResolverUtils.class);
/**
* Visitor to remove any union selections from the leaf. Returns null if the type did not match the resolved object, {@link ComputationTargetType#NULL} if the object did match, or the correct type
* selection if it was modified.
*/
private static final ComputationTargetTypeVisitor<UniqueIdentifiable, ComputationTargetType> s_resolveType = new ComputationTargetTypeVisitor<UniqueIdentifiable, ComputationTargetType>() {
@Override
public ComputationTargetType visitMultipleComputationTargetTypes(final Set<ComputationTargetType> types, final UniqueIdentifiable resolved) {
for (final ComputationTargetType type : types) {
final ComputationTargetType newType = type.accept(this, resolved);
if (newType == ComputationTargetType.NULL) {
// Use the original type
return type;
} else if (newType != null) {
// Use the replaced type
return newType;
}
}
// Nothing in the union matched
return null;
}
@Override
public ComputationTargetType visitNestedComputationTargetTypes(final List<ComputationTargetType> types, final UniqueIdentifiable resolved) {
final ComputationTargetType leafType = types.get(types.size() - 1).accept(this, resolved);
if (leafType == ComputationTargetType.NULL) {
// Unchanged
return leafType;
} else if (leafType != null) {
// Replace the leaf type
ComputationTargetType newType = types.get(0);
for (int i = 1; i < types.size() - 1; i++) {
newType = newType.containing(types.get(i));
}
return newType.containing(leafType);
} else {
return null;
}
}
@Override
public ComputationTargetType visitNullComputationTargetType(final UniqueIdentifiable resolved) {
// Invalid
return null;
}
@Override
public ComputationTargetType visitClassComputationTargetType(final Class<? extends UniqueIdentifiable> type, final UniqueIdentifiable resolved) {
final Class<? extends UniqueIdentifiable> clazz = resolved.getClass();
if (type.equals(clazz)) {
// Unchanged
return ComputationTargetType.NULL;
} else if (type.isAssignableFrom(clazz)) {
// Have a more specific type
return ComputationTargetType.of(clazz);
} else {
// Class doesn't match
return null;
}
}
};
/**
* Creates a {@link ComputationTarget} instance that describes the resolved object. The type in the target will accurately describe the target type to the scoping level of the requested
* specification. For example a target of type {@code FooSecurity} resolved from a specification of type {@code POSITION/SECURITY} will end up as type {@code POSITION/FooSecurity}.
*
* @param requestedSpecification the original specification as passed to the {@link ComputationTargetResolver#resolve} method, not null
* @param target the resolved object, never null
* @return the target object instance with the correct logical type, not null
*/
public static ComputationTarget createResolvedTarget(final ComputationTargetSpecification requestedSpecification, final UniqueIdentifiable target) {
ComputationTargetSpecification resolvedSpecification;
final ComputationTargetType requestedType = requestedSpecification.getType();
final ComputationTargetType resolvedType = requestedType.accept(s_resolveType, target);
if (resolvedType == null) {
// Error
if (s_logger.isWarnEnabled()) {
s_logger.warn("Resolved {} to {}, not instanceof {}", new Object[] {requestedSpecification.getUniqueId(), target, requestedType });
}
resolvedSpecification = requestedSpecification;
} else if (resolvedType == ComputationTargetType.NULL) {
// No-change
resolvedSpecification = requestedSpecification;
} else {
// Different type
resolvedSpecification = (ComputationTargetSpecification) requestedSpecification.replaceType(resolvedType);
}
if (requestedSpecification.getUniqueId().isLatest() && target.getUniqueId().isVersioned()) {
resolvedSpecification = resolvedSpecification.replaceIdentifier(target.getUniqueId());
}
return new ComputationTarget(resolvedSpecification, target);
}
@SuppressWarnings("unchecked")
private static <T extends ComputationTargetReference> T simplifyType(final T reference, final ComputationTargetType oldType, final ComputationTargetType newType) {
if ((newType == null) || (newType == oldType)) {
return reference;
} else {
return (T) reference.replaceType(newType);
}
}
/**
* Simplifies the type within a reference to the simplest form that the resolver will recognize. For example {@code CTSpec[FooSecurity, Sec~1]} might be simplified to {@code CTSpec[SECURITY, Sec~1]}
* if the same resolution will take place regardless of whether the type is a security or a sub-class of it. If no simplification is possible, the original reference may be returned.
*
* @param <T> the object type, either {@link ComputationTargetSpecification} or {@link ComputationTargetReference}
* @param reference the reference to simplify, not null
* @param resolver the resolver to simplify against, not null
* @return the simplified reference, not null
*/
public static <T extends ComputationTargetReference> T simplifyType(final T reference, final ComputationTargetResolver resolver) {
return simplifyType(reference, reference.getType(), resolver.simplifyType(reference.getType()));
}
public static <T extends ComputationTargetReference> T simplifyType(final T reference, final ComputationTargetResolver.AtVersionCorrection resolver) {
return simplifyType(reference, reference.getType(), resolver.simplifyType(reference.getType()));
}
}