Java API

Installing

The Coco Platform API for Java is presently distributed as part of the Coco Platform for Eclipse, and is available for other Eclipse plugins to use. To use the Coco Platform API, add a dependency on the io.cocotec.coco.platform plugin.

Note

If you are interested in using the Coco Java API from non-Eclipse setups please contact us.

Getting Started

The following section shows how to build a simple example program that loads a Coco file and verifies if the file is correct. In addition to this, there is also documentation available on general advice on using the API.

The first step is to create the APIClient and to start the server. The server runs in the background locally on the user’s machine and handles requests:

APIClient client = new APIClient();
client.startServer(Activator.getDefault().getPlatformServerFile().toString());

Before using any of the API methods, we check to see if the user has a valid license, terminating gracefully if not:

UserInfo userInfo = new UserInfo(client);
LicenseResponse licenseResponse = userInfo.hasLicense(Arrays.asList(ProductFeature.CocoPlatformBase));
if (!licenseResponse.getLicensed()) {
  System.out.println("Could not acquire license: " + licenseResponse.getNonLicensedReason());
  return;
}

Before loading the file, we create a StandaloneContext and add various loggers to it that print to the Console (see the downloadable example for implementations of these classes):

StandaloneContext context = new StandaloneContext(client);
CocoDiagnosticLogger diagnosticLogger = new CocoDiagnosticLogger(client);
VerificationLogger verificationLogger = new VerificationLogger(client);
context.addDiagnosticsConsumer(diagnosticLogger);
context.addVerificationProgressListener(verificationLogger);

In order to load the file, we have to add the folder that contains the file as an import path. For the purposes of this example, we assume that there is a file called TestFile.coco stored in the current working directory:

context.addImportPath(System.getProperty("user.dir"));
Node moduleNode = context.loadModule("TestFile.coco", ModulePathType.RelativeFilePath);
if (moduleNode == null) {
  System.out.println("Could not load TestFile.coco");
  return;
}

Assuming that the load took place successfully, we can then execute all assertions as follows:

context.rootAssertion().run();

This will print logging information to the console whilst the checks are executing.

The complete example is appended below:

// Copyright 2024 Cocotec Limited. All rights reserved.
// Unauthorized copying of this file, via any medium is strictly prohibited.
// Proprietary and confidential.
import io.cocotec.coco.platform.APIClient;
import io.cocotec.coco.platform.Activator;
import io.cocotec.coco.platform.ProductFeature;
import io.cocotec.coco.platform.coco.Diagnostic;
import io.cocotec.coco.platform.coco.DiagnosticConsumer;
import io.cocotec.coco.platform.coco.LicenseResponse;
import io.cocotec.coco.platform.coco.ModulePathType;
import io.cocotec.coco.platform.coco.Node;
import io.cocotec.coco.platform.coco.StandaloneContext;
import io.cocotec.coco.platform.coco.UserInfo;
import io.cocotec.coco.platform.coco.VerificationAssertion;
import io.cocotec.coco.platform.coco.VerificationProgressListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;

class Example {
  public static void Main(String[] args) throws IOException {
    APIClient client = new APIClient();
    client.startServer(Activator.getDefault().getPlatformServerFile().toString());

    UserInfo userInfo = new UserInfo(client);
    LicenseResponse licenseResponse =
        userInfo.hasLicense(new ArrayList<>(Arrays.asList(ProductFeature.CocoPlatformBase)));
    if (!licenseResponse.getLicensed()) {
      System.out.println("Could not acquire license: " + licenseResponse.getNonLicensedReason());
      return;
    }

    StandaloneContext context = new StandaloneContext(client);
    CocoDiagnosticLogger diagnosticLogger = new CocoDiagnosticLogger(client);
    VerificationLogger verificationLogger = new VerificationLogger(client);
    context.addDiagnosticsConsumer(diagnosticLogger);
    context.addVerificationProgressListener(verificationLogger);

    context.addImportPath(System.getProperty("user.dir"));
    Node moduleNode = context.loadModule("TestFile.coco", ModulePathType.RelativeFilePath);
    if (moduleNode == null) {
      System.out.println("Could not load TestFile.coco");
      return;
    }

    context.rootAssertion().run();
  }

  public static class CocoDiagnosticLogger extends DiagnosticConsumer {

    public CocoDiagnosticLogger(APIClient client) {
      super(client);
    }

    @Override
    public void handleDiagnostic(Diagnostic diagnostic) {
      System.out.println(diagnostic.getMessage());
    }
  }

  public static class VerificationLogger extends VerificationProgressListener {

    public VerificationLogger(APIClient client) {
      super(client);
    }

    private void log(VerificationAssertion assertion, String message) {
      System.out.println(assertion.name() + ": " + assertion.description());
    }

    @Override
    public void verificationStarted(VerificationAssertion assertion) {
      log(assertion, "started");
    }

    @Override
    public void verificationLogMessage(VerificationAssertion assertion, String message) {
      log(assertion, message);
    }

    @Override
    public void verificationStatusUpdated(VerificationAssertion assertion) {}

    @Override
    public void verificationStatisticsUpdated(VerificationAssertion assertion) {
      log(assertion, "statistics updated");
    }

    @Override
    public void verificationFinished(VerificationAssertion assertion) {
      log(assertion, "finished");
    }

    @Override
    public void verificationFinishedAll() {}
  }
}

EMF

The Coco Platform for Eclipse API also provides an integration with the Eclipse Modelling Framework. Specifically, it provides:

  • An Ecore metamodel for Coco, allowing standard EMF-based tools to create Coco programmatically.
  • An API that allows an EMF resource using the Coco Ecore model to be loaded into a StandaloneContext, and then, for example, converted into Coco’s textual representation. This effectively allows other languages to be imported into Coco.
  • An API that allows the Coco files to be converted into the EMF representation. This allows Coco to be exported into other languages, or for Coco-to-Coco transformations to be written.

Together, these APIs permit bidirectional transformations, either allowing Coco to be converted into the EMF form, or vice-versa. To use these APIs, create an EMFContext instead of a StandaloneContext. For example, to load a Coco file using the EMFContext.toResource method:

EMFContext context = new EMFContext(client);
Node cocoNativeModule = ...;
ModuleDecl emfModule = context.toResource(cocoNativeModule);

emfModule is an EObject and can be passed to standard EMF tools. The EMF structure can also be viewed directly via Eclipse rather than via the API, by right-clicking on any .coco file within the Project Explorer in Eclipse, and selecting Open With → Other and then selecting Sample Reflective Ecore Model Editor from the list. This will require the EMF SDK to be installed in Eclipse.

The API can also be used to import an EMF-created module into Coco’s native form, as follows:

ModuleDecl emfModule = ...;
EMFContext context = new EMFContext(client);
Node cocoNativeModule = context.loadModule(emfModule);
String cocoText = cocoNativeModule.prettyPrint();

This will load the module into the context, which will report diagnostics via the context’s DiagnosticConsumer, and then pretty-print the text. This could then be saved to a file, for example, effectively allowing textual Coco models to be created from another EMF-based language.