AFC - Abacus Formula Compiler for Java

Binding To Multiple Interfaces

AFC allows you to set only a single interface to provide inputs to or obtain outputs from a computation. How can you work around this?

The example code on this page, while syntactically checked, is not yet part of an automated test.

Inputs

To feed information to a computation from various sources, each with its own interface, you need to define a wrapper that gives access to the source interfaces.

Consider these two source interfaces:

public static interface InputA
{
  double getValue();
}

public static interface InputB
{
  double getValue();
  double getOther();
}

The wrapper is a class with accessor properties returning instances the two source interfaces:

public static final class Input
{
  private final InputA a;
  private final InputB b;

  public Input( InputA _a, InputB _b )
  {
    super();
    this.a = _a;
    this.b = _b;
  }

  public InputA getA()
  {
    return this.a;
  }

  public InputB getB()
  {
    return this.b;
  }
}

You then bind cells to elements of a particular source interface by using a call chain. AFC allows you to bind cells to the result of calling a chain of methods:

cell = spreadsheet.getCell( "A_VALUE" );
intfGetter = Input.class.getMethod( "getA" );
valueGetter = String.class.getMethod( "getValue" );
binder.defineInputCell( cell, builder.newCallFrame( intfGetter ).chain( valueGetter ) );

cell = spreadsheet.getCell( "B_VALUE" );
intfGetter = Input.class.getMethod( "getB" );
valueGetter = String.class.getMethod( "getValue" );
binder.defineInputCell( cell, builder.newCallFrame( intfGetter ).chain( valueGetter ) );
Note

AFC does not support multiple input interfaces directly because this would add a part of the application logic to AFC that does not need to be customizable by spreadsheets. Therefore, it is better to leave this logic outside of AFC. This gives you full control over it and avoids adding more black box behaviour than necessary.

Outputs

Assume a computation in a sheet must supply output values for two different interfaces:

public static interface OutputA
{
  double getResult();
}

public static interface OutputB
{
  double getResult();
  double getOther();
}

To achieve this, the simplest approach is to simply make your output class implement both of the target interfaces:

public static interface Output extends OutputA, OutputB
{
  // no own content
}

You can then use your computation’s output with both targets. However, this only works if name clashes are acceptable, that is, OutputA.getResult() maps to the same spreadsheet cell as OutputB.getResult().

Disambiguation

If this is not the case, you have to do a little more work. You define your output class as follows:

public static interface Output2
{
  double getResultA();
  double getResultB();
  double getOtherB();
}

Then, you define wrappers which implement the proper interfaces:

public static class OutputAImpl implements OutputA
{
  private final Output2 output;

  public OutputAImpl( Output2 _output )
  {
    super();
    this.output = _output;
  }

  public double getResult()
  {
    return this.output.getResultA();
  }
}

and

public static class OutputBImpl implements OutputB
{
  private final Output2 output;

  public OutputBImpl( Output2 _output )
  {
    super();
    this.output = _output;
  }

  public double getResult()
  {
    return this.output.getResultB();
  }

  public double getOther()
  {
    return this.output.getOtherB();
  }
}
Note

AFC does not support the following idiom:

public static interface Output3
{
  OutputA getA();
  OutputB getB();
}

as it did for inputs. This is because for outputs, it is AFC’s responsibility to implement this interface. It only supports the approach with the least black box magic.