/**
* 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.Serializable;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang.ObjectUtils;
import com.opengamma.engine.ComputationTargetSpecification;
import com.opengamma.engine.MemoryUtils;
import com.opengamma.id.ExternalId;
import com.opengamma.id.ExternalIdBundle;
import com.opengamma.id.UniqueId;
import com.opengamma.id.UniqueIdentifiable;
import com.opengamma.util.PublicAPI;
/**
* A reference to a particular computation target that will be resolved later to a real target. The reference may be "strict" and refer to a specific object or concept by {@link UniqueId} or "loose"
* and refer to it by a broader identifier bundle that must first be resolved.
*/
@PublicAPI
public abstract class ComputationTargetReference implements Serializable {
private static final long serialVersionUID = 1L;
/**
* The type of the target.
*/
private final ComputationTargetType _type;
/**
* The reference to the immediate parent that puts this target into its typed context, or null if there is no typed context.
*/
private final ComputationTargetReference _parent;
protected/* [PLAT-444]: should be package visible */ComputationTargetReference(final ComputationTargetType type) {
assert type != null;
assert getTypeDepth(type) <= 1;
_type = type;
_parent = null;
}
protected/* [PLAT-444]: should be package visible */ComputationTargetReference(final ComputationTargetType type, final ComputationTargetReference parent) {
assert type != null;
assert parent != null;
assert getTypeDepth(type) == 1;
_type = parent.getType().containing(type);
_parent = parent;
}
protected/* [PLAT-444]: should be package visible */ComputationTargetReference(final ComputationTargetReference parent, final ComputationTargetType type) {
_type = type;
_parent = parent;
}
private static final Integer ZERO = 0;
private static final Integer ONE = 1;
private static final ComputationTargetTypeVisitor<Void, Integer> s_getTypeDepth = new ComputationTargetTypeVisitor<Void, Integer>() {
@Override
public Integer visitMultipleComputationTargetTypes(final Set<ComputationTargetType> types, final Void data) {
Integer depth = null;
for (ComputationTargetType type : types) {
final Integer typeDepth = type.accept(this, data);
if (depth == null) {
depth = typeDepth;
} else if (!depth.equals(typeDepth)) {
throw new IllegalArgumentException();
}
}
return depth;
}
@Override
public Integer visitNestedComputationTargetTypes(final List<ComputationTargetType> types, final Void data) {
int depth = 0;
for (ComputationTargetType type : types) {
depth += type.accept(this, data);
}
return depth;
}
@Override
public Integer visitNullComputationTargetType(final Void data) {
return ZERO;
}
@Override
public Integer visitClassComputationTargetType(final Class<? extends UniqueIdentifiable> type, final Void data) {
return ONE;
}
};
/**
* Tests the depth of a typed context. If the type does not describe an object of uniform depth it is rejected.
*
* @param type the type to test, not null
* @return the depth
* @throws IllegalArgumentException if the type does not have a uniform depth
*/
public/* should be package visible */static int getTypeDepth(final ComputationTargetType type) {
return type.accept(s_getTypeDepth, null);
}
public ComputationTargetRequirement containing(final ComputationTargetType type, final ExternalId identifier) {
return containing(type, identifier.toBundle());
}
public ComputationTargetRequirement containing(final ComputationTargetType type, final ExternalIdBundle identifiers) {
return new ComputationTargetRequirement(type, identifiers, this);
}
public ComputationTargetSpecification containing(final ComputationTargetType type, final UniqueId identifier) {
return new ComputationTargetSpecification(type, identifier, this);
}
/**
* Gets the type of the target.
*
* @return the type, not null
*/
public ComputationTargetType getType() {
return _type;
}
/**
* Gets the parent reference when this is used in context.
*
* @return the parent reference or null if the target is not within a context
*/
public ComputationTargetReference getParent() {
return _parent;
}
/**
* Returns this as a target requirement if it is one. If it is not a requirement, an exception will be thrown. Use the visitor pattern to deal with the alternative types; such as to resolve a
* requirement to a specification using the compilation context.
*
* @return the computation target requirement instance, not null
* @throws IllegalStateException if this is not a {@link ComputationTargetRequirement}
*/
public ComputationTargetRequirement getRequirement() {
throw new IllegalStateException(toString() + " already resolved");
}
/**
* Returns this as a target specification if it is one. If it is not a specification, an exception will be thrown. Use the visitor pattern to deal with the alternative types; such as to resolve a
* requirement to a specification using the compilation context.
*
* @return the computation target specification instance, not null
* @throws IllegalStateException if this is not a {@link ComputationTargetSpecification}
*/
public ComputationTargetSpecification getSpecification() {
throw new IllegalStateException(toString() + " is not resolved");
}
@Override
public boolean equals(final Object obj) {
// Sub-classes must override and call back after the type has been checked
final ComputationTargetReference other = (ComputationTargetReference) obj;
return ObjectUtils.equals(getParent(), other.getParent()) && getType().equals(other.getType());
}
@Override
public int hashCode() {
// Sub-classes must override
//getName() and hashCode() results are cached on their objects
return (getClass().getName().hashCode() * 31 * 31) + ObjectUtils.hashCode(getParent()) * 31 + getType().hashCode();
}
/**
* Applies the visitor operation to this reference.
*
* @param <T> the return type of the visitor
* @param visitor the visitor operation, not null
* @return the result of the visitor operation
*/
public abstract <T> T accept(ComputationTargetReferenceVisitor<T> visitor);
protected/* [PLAT-444]: should be package visible */abstract ComputationTargetReference create(ComputationTargetReference parent, ComputationTargetType type);
/**
* Normalizes the parent reference using {@link MemoryUtils} to reduce the memory footprint.
*
* @return this instance or an equivalent object that references a shared parent instance
*/
public ComputationTargetReference normalize() {
if (getParent() == null) {
return this;
} else {
final ComputationTargetReference parent = MemoryUtils.instance(getParent());
if (parent == getParent()) {
return this;
} else {
return create(parent, getType());
}
}
}
private static final ComputationTargetTypeVisitor<Void, ComputationTargetType> s_getParentType = new ComputationTargetTypeVisitor<Void, ComputationTargetType>() {
@Override
public ComputationTargetType visitMultipleComputationTargetTypes(final Set<ComputationTargetType> types, final Void data) {
throw new IllegalStateException();
}
@Override
public ComputationTargetType visitNestedComputationTargetTypes(final List<ComputationTargetType> types, final Void data) {
ComputationTargetType result = types.get(0);
final int l = types.size() - 1;
for (int i = 1; i < l; i++) {
result = result.containing(types.get(i));
}
return result;
}
@Override
public ComputationTargetType visitNullComputationTargetType(final Void data) {
throw new IllegalStateException();
}
@Override
public ComputationTargetType visitClassComputationTargetType(final Class<? extends UniqueIdentifiable> type, final Void data) {
return null;
}
};
/**
* Returns a new reference with the same identifiers but a different target type.
*
* @param newType the type of the new object, not null
* @return the new reference object, not null
*/
public ComputationTargetReference replaceType(final ComputationTargetType newType) {
if (newType == getType()) {
return this;
} else {
// TODO: should be checking the type
final ComputationTargetReference parent = getParent();
if (parent != null) {
final ComputationTargetType parentType = newType.accept(s_getParentType, null);
if (parentType == null) {
// Truncate the parent
return create(null, newType);
} else {
// Update the parent
return create(parent.replaceType(parentType), newType);
}
} else {
return create(null, newType);
}
}
}
protected abstract String getIdStringImpl();
protected String getIdString() {
if (getParent() != null) {
return getParent().getIdString() + "/" + getIdStringImpl();
} else {
return getIdStringImpl();
}
}
/**
* Returns a new reference with the same type (and parent) but a different leaf identifier.
*
* @param identifier the new identifier, not null
* @return the new reference object
*/
public ComputationTargetSpecification replaceIdentifier(final UniqueId identifier) {
return new ComputationTargetSpecification(getParent(), getType(), identifier);
}
}