/**
 * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.financial.analytics;

import java.util.Collections;
import java.util.Map;
import java.util.Set;

import javax.swing.plaf.metal.MetalComboBoxUI.MetalPropertyChangeListener;

import org.joda.beans.Bean;
import org.joda.beans.MetaBean;
import org.joda.beans.MetaProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Iterables;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.core.security.Security;
import com.opengamma.engine.ComputationTarget;
import com.opengamma.engine.function.AbstractFunction;
import com.opengamma.engine.function.FunctionCompilationContext;
import com.opengamma.engine.function.FunctionExecutionContext;
import com.opengamma.engine.function.FunctionInputs;
import com.opengamma.engine.target.ComputationTargetType;
import com.opengamma.engine.value.ComputedValue;
import com.opengamma.engine.value.ValueProperties;
import com.opengamma.engine.value.ValueRequirement;
import com.opengamma.engine.value.ValueRequirementNames;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.financial.OpenGammaCompilationContext;
import com.opengamma.master.security.ManageableSecurity;
import com.opengamma.master.security.ManageableSecurity.Meta;

/**
 *
 */
public class AttributesFunction extends AbstractFunction.NonCompiledInvoker {
  /** The attribute property name */
  public static final String PROPERTY_ATTRIBUTE_NAME = "AttributeName";
  private static final Logger s_logger = LoggerFactory.getLogger(AttributesFunction.class);

  @Override
  public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) {
    final ValueRequirement desiredValue = Iterables.getOnlyElement(desiredValues);
    final ValueProperties properties = desiredValue.getConstraints();
    final String attributeName = properties.getSingleValue(PROPERTY_ATTRIBUTE_NAME);
    final Security security = target.getSecurity();
    final Map<String, String> attributes = security.getAttributes();
    String result = attributes.get(attributeName);
    if (result == null) {
      if (security instanceof Bean) {
        Bean ms = (Bean) security;
        MetaBean metaBean = ms.metaBean();
        if (metaBean.metaPropertyExists(attributeName)) {
          result = metaBean.metaProperty(attributeName).getString(ms);
        }
      }
    }
    if (result == null) {
      throw new OpenGammaRuntimeException("Could not get value for attribute " + attributeName);
    }
    final ValueSpecification spec = new ValueSpecification(ValueRequirementNames.ATTRIBUTES, target.toSpecification(), properties);
    return Collections.singleton(new ComputedValue(spec, result));
  }

  @Override
  public ComputationTargetType getTargetType() {
    return ComputationTargetType.SECURITY;
  }

  @Override
  public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) {
    final Map<String, String> attributes = target.getSecurity().getAttributes();
    if (attributes.isEmpty()) {
      return null;
    }
    final ValueProperties.Builder properties = createValueProperties();
    for (String attribute : attributes.keySet()) {
      properties.with(PROPERTY_ATTRIBUTE_NAME, attribute);
    }
    Security security = target.getSecurity();
    if (security instanceof Bean) {
      Bean bean = (Bean) security;
      MetaBean metaBean = bean.metaBean();
      for (MetaProperty<?> property : metaBean.metaPropertyIterable()) {
        properties.with(PROPERTY_ATTRIBUTE_NAME, property.name());
      }
    }
    return Collections.singleton(new ValueSpecification(ValueRequirementNames.ATTRIBUTES, target.toSpecification(), properties.get()));
  }

  @Override
  public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) {
    final ValueProperties constraints = desiredValue.getConstraints();
    final Set<String> attributeNames = constraints.getValues(PROPERTY_ATTRIBUTE_NAME);
    if (!OpenGammaCompilationContext.isPermissive(context)) {
      if (attributeNames == null || attributeNames.size() != 1) {
        s_logger.error("Did not specify a single attribute name");
        return null;
      }
    }
    return Collections.emptySet();
  }

}

@@ -254,84 +254,8 @@
 t;%0A%0A
-import javax.swing.plaf.metal.MetalComboBoxUI.MetalPropertyChangeListener;%0A%0A
 impo
@@ -1208,127 +1208,8 @@
 ext;
-%0Aimport com.opengamma.master.security.ManageableSecurity;%0Aimport com.opengamma.master.security.ManageableSecurity.Meta;
 %0A%0A/*
@@ -2975,24 +2975,76 @@
 t target) %7B%0A
+    final Security security = target.getSecurity();%0A
     final Ma
@@ -3074,36 +3074,24 @@
 butes = 
-target.getS
+s
 ecurity
-()
 .getAttr
@@ -3128,19 +3128,119 @@
 sEmpty()
-) %7B
+ && !(security instanceof Bean)) %7B%0A      // No explicit attributes, and can't query a non-Bean security
 %0A      r
@@ -3325,24 +3325,59 @@
 operties();%0A
+    if (!attributes.isEmpty()) %7B%0A  
     for (Str
@@ -3417,24 +3417,26 @@
 ()) %7B%0A      
+ 
+ 
 properties.w
@@ -3484,55 +3484,17 @@
     
+  
 %7D%0A    
-Security security = target.getSecurity();
+%7D
 %0A   

Allow both targets with explicit attributes and beans. Reviewed-by: Jim Moores