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

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.opengamma.lang.annotation.ExternalFunction;
import com.opengamma.language.function.AbstractFunctionProvider;
import com.opengamma.language.function.MetaFunction;

/**
 * Creates and exposes functions based on methods that are annotated with
 * {@link ExternalFunction}.
 */
public class ExternalFunctionProvider extends AbstractFunctionProvider {

  private static final Logger s_logger = LoggerFactory.getLogger(ExternalFunctionProvider.class);
  private static boolean s_excludeTests = true;

  private List<MetaFunction> _functions;

  private static List<MetaFunction> functions() {
    ExternalFunctionCache cache = ExternalFunctionCache.load();
    final ClasspathScanner scanner = new ClasspathScanner();
    if (!scanner.getTimestamp().isAfter(cache.getTimestamp())) {
      s_logger.info("Loading external functions from cache");
      final List<MetaFunction> functions = createFunctions(cache);
      if (functions != null) {
        return functions;
      }
      s_logger.warn("One or more errors loading functions from cache");
    }
    s_logger.info("Scanning class path for annotated external functions");
    cache = scanner.scan();
    final List<MetaFunction> functions = createFunctions(cache);
    if (functions != null) {
      cache.save();
      return functions;
    }
    s_logger.error("Couldn't load external functions");
    return Collections.emptyList();
  }

  public static void setExcludeTests(final boolean excludeTests) {
    s_excludeTests = excludeTests;
  }

  public static boolean isExcludeTests() {
    return s_excludeTests;
  }

  private static boolean isTestClass(final Class<?> clazz) {
    final String[] cs = clazz.getName().split("[\\.\\$]");
    for (final String c : cs) {
      if (c.endsWith("Test")) {
        return true;
      }
    }
    return false;
  }

  private static List<MetaFunction> createFunctions(final ExternalFunctionCache cache) {
    final List<MetaFunction> functions = new ArrayList<MetaFunction>();
    for (final Class<?> clazz : cache.getClasses()) {
      if (isExcludeTests() && isTestClass(clazz)) {
        continue;
      }
      final ExternalFunctionHandler handler = new ExternalFunctionHandler(clazz);
      functions.addAll(handler.getFunctions());
    }
    return functions;
  }

  protected synchronized List<MetaFunction> getFunctions() {
    if (_functions == null) {
      _functions = functions();
    }
    return _functions;
  }

  @Override
  protected void loadDefinitions(final Collection<MetaFunction> definitions) {
    definitions.addAll(getFunctions());
  }

}

@@ -2622,24 +2622,189 @@
 tions;%0A  %7D%0A%0A
+  public ExternalFunctionProvider() %7B%0A    new Thread(new Runnable() %7B%0A      @Override%0A      public void run() %7B%0A        getFunctions();%0A      %7D%0A    %7D).start();%0A  %7D%0A%0A
   protected 

Use a slave thread to initialise the external function provider in advance of a connection, without blocking other startup calls.