Skip to content

Commit

Permalink
Copy in schema generated workflow parser (and linter) from gxformat2.
Browse files Browse the repository at this point in the history
  • Loading branch information
jmchilton committed Dec 3, 2019
1 parent 62845da commit a5e3e22
Show file tree
Hide file tree
Showing 80 changed files with 5,143 additions and 0 deletions.
15 changes: 15 additions & 0 deletions src/main/java/org/galaxyproject/gxformat2/Format2Linter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.galaxyproject.gxformat2;

import java.util.Map;
import org.galaxyproject.gxformat2.v19_09.utils.RootLoader;
import org.galaxyproject.gxformat2.v19_09.utils.ValidationException;

public class Format2Linter implements GalaxyWorkflowLinter {
public void lint(final LintContext lintContext, final Map<String, Object> workflow) {
try {
RootLoader.loadDocument(workflow);
} catch (ValidationException e) {
lintContext.error("Validation failed " + e.toString());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.galaxyproject.gxformat2;

import java.util.Map;

public interface GalaxyWorkflowLinter {
public void lint(final LintContext lintContext, final Map<String, Object> workflow);
}
45 changes: 45 additions & 0 deletions src/main/java/org/galaxyproject/gxformat2/Lint.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.galaxyproject.gxformat2;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import org.yaml.snakeyaml.Yaml;

public class Lint {
public static int EXIT_CODE_SUCCESS = 0;
public static int EXIT_CODE_LINT_FAILED = 1;
public static int EXIT_CODE_FORMAT_ERROR = 2;
public static int EXIT_CODE_FILE_PARSE_FAILED = 3;

public static void main(String[] args) throws Exception {
final int exitCode = lint(args);
System.exit(exitCode);
}

public static int lint(final String[] args) throws Exception {
final Path path = Paths.get(args[0]);
final String workflowContents = new String(Files.readAllBytes(path), "UTF8");
final Yaml yaml = new Yaml();
final Map<String, Object> object = (Map<String, Object>) yaml.load(workflowContents);
final String wfClass = (String) object.get("class");
GalaxyWorkflowLinter linter;
if (wfClass != null && wfClass.equals("GalaxyWorkflow")) {
linter = new Format2Linter();
} else {
linter = new NativeLinter();
}
final LintContext lintContext = new LintContext();
linter.lint(lintContext, object);
lintContext.printMessages();
int exitCode;
if (lintContext.getFoundErrors()) {
exitCode = EXIT_CODE_FORMAT_ERROR;
} else if (lintContext.getFoundWarns()) {
exitCode = EXIT_CODE_LINT_FAILED;
} else {
exitCode = EXIT_CODE_SUCCESS;
}
return exitCode;
}
}
52 changes: 52 additions & 0 deletions src/main/java/org/galaxyproject/gxformat2/LintContext.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.galaxyproject.gxformat2;

import java.util.ArrayList;
import java.util.List;

public class LintContext {
private boolean foundErrors = false;
private boolean foundWarns = false;

private List<String> validMessages = new ArrayList<String>();
private List<String> infoMessages = new ArrayList<String>();
private List<String> warnMessages = new ArrayList<String>();
private List<String> errorMessages = new ArrayList<String>();

LintContext() {}

boolean getFoundErrors() {
return this.foundErrors;
}

boolean getFoundWarns() {
return this.foundWarns;
}

void valid(String message, Object... args) {
this.validMessages.add(String.format(message, args));
}

void info(String message, Object... args) {
this.infoMessages.add(String.format(message, args));
}

void error(String message, Object... args) {
this.errorMessages.add(String.format(message, args));
}

void warn(String message, Object... args) {
this.warnMessages.add(String.format(message, args));
}

void printMessages() {
for (final String message : this.errorMessages) {
this.foundErrors = true;
System.out.print(".. ERROR " + message);
}

for (final String message : this.warnMessages) {
this.foundWarns = true;
System.out.print(".. WARNING " + message);
}
}
}
51 changes: 51 additions & 0 deletions src/main/java/org/galaxyproject/gxformat2/LintUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.galaxyproject.gxformat2;

import java.util.Map;

class LintUtils {
static <T> T ensureKey(
LintContext lintContext, Object hasKeys, String key, Class<T> hasClass, Object hasValue) {
if (!(hasKeys instanceof Map)) {
lintContext.error("expected [%s] to be a dictionary type", hasKeys);
return null;
}
final Map<String, Object> map = (Map<String, Object>) hasKeys;
if (!map.containsKey(key)) {
lintContext.error("expected to have key [%s] but absent", key);
}
final Object value = map.get(key);
return ensureKeyHasValue(lintContext, map, key, value, hasClass, hasValue);
}

static <T> T ensureKeyIfPresent(
LintContext lintContext, Object hasKeys, String key, T defaultValue, Class<T> hasClass) {
if (!(hasKeys instanceof Map)) {
lintContext.error("expected [%s] to be a dictionary type", hasKeys);
return null;
}
final Map<String, Object> map = (Map<String, Object>) hasKeys;
if (!map.containsKey(key)) {
return defaultValue;
}
final Object value = map.get(key);
return ensureKeyHasValue(lintContext, map, key, value, hasClass, null);
}

static <T> T ensureKeyHasValue(
LintContext lintContext,
Map hasKeys,
String key,
Object value,
Class<T> hasClass,
Object hasValue) {
if (!hasClass.isInstance(value)) {
lintContext.error(
"expected value [%s] with key [%s] to be of class %s", key, value, hasClass);
return null;
}
if (hasValue != null && !hasValue.equals(value)) {
lintContext.error("expected value [%s] with key [%s] to be %s", key, value, hasValue);
}
return (T) value;
}
}
64 changes: 64 additions & 0 deletions src/main/java/org/galaxyproject/gxformat2/NativeLinter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.galaxyproject.gxformat2;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class NativeLinter implements GalaxyWorkflowLinter {
public static String LINT_FAILED_NO_OUTPUTS = "Workflow contained no outputs";
public static String LINT_FAILED_OUTPUT_NO_LABEL = "Workflow contained output without a label";

public void lint(final LintContext lintContext, final Map<String, Object> workflow) {
LintUtils.ensureKey(lintContext, workflow, "format-version", String.class, "0.1");
LintUtils.ensureKey(lintContext, workflow, "a_galaxy_workflow", String.class, "true");

final Map<String, Object> steps =
(Map<String, Object>) LintUtils.ensureKey(lintContext, workflow, "steps", Map.class, null);
boolean foundOutputs = false;
boolean foundOutputsWithoutLabel = false;
if (steps != null) {
for (Map.Entry<String, Object> stepEntry : steps.entrySet()) {
final String orderIndexStr = stepEntry.getKey();
try {
final int orderIndex = Integer.parseInt(orderIndexStr);
} catch (NumberFormatException e) {
lintContext.error("expected step_key to be integer not [%s]", orderIndexStr);
}
if (!(stepEntry.getValue() instanceof Map)) {
lintContext.error("expected step value to be Map not [%s]", stepEntry.getValue());
}
Map<String, Object> step = (Map<String, Object>) stepEntry.getValue();
List<String> workflowOutputs =
LintUtils.ensureKeyIfPresent(
lintContext, step, "workflow_outputs", new ArrayList<String>(), List.class);
for (Object workflowOutputObject : workflowOutputs) {
foundOutputs = true;
if (!(workflowOutputObject instanceof Map)) {
lintContext.error("Not a map");
}
final Map<String, String> workflowOutput = (Map<String, String>) workflowOutputObject;
final String label = workflowOutput.get("label");
if (label == null || label.length() == 0) {
foundOutputsWithoutLabel = true;
}
}
final String stepType =
LintUtils.ensureKeyIfPresent(lintContext, step, "type", "tool", String.class);
assert stepType != null;
if (stepType != null && stepType.equals("subworkflow")) {
final Map<String, Object> subworkflow =
(Map<String, Object>)
LintUtils.ensureKey(lintContext, step, "subworkflow", Map.class, null);
assert subworkflow != null;
lint(lintContext, subworkflow);
}
}
}
if (!foundOutputs) {
lintContext.warn(LINT_FAILED_NO_OUTPUTS);
}
if (foundOutputsWithoutLabel) {
lintContext.warn(LINT_FAILED_OUTPUT_NO_LABEL);
}
}
}
23 changes: 23 additions & 0 deletions src/main/java/org/galaxyproject/gxformat2/v19_09/Any.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.galaxyproject.gxformat2.v19_09;

import org.galaxyproject.gxformat2.v19_09.utils.ValidationException;

public enum Any {
ANY("Any");

private static String[] symbols = new String[] {"Any"};
private String docVal;

private Any(final String docVal) {
this.docVal = docVal;
}

public static Any fromDocumentVal(final String docVal) {
for (final Any val : Any.values()) {
if (val.docVal.equals(docVal)) {
return val;
}
}
throw new ValidationException(String.format("Expected one of %s", Any.symbols, docVal));
}
}
30 changes: 30 additions & 0 deletions src/main/java/org/galaxyproject/gxformat2/v19_09/ArraySchema.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.galaxyproject.gxformat2.v19_09;

import org.galaxyproject.gxformat2.v19_09.utils.Savable;

/**
* Auto-generated interface for <I>https://w3id.org/cwl/salad#ArraySchema</I><br>
* This interface is implemented by {@link ArraySchemaImpl}<br>
*/
public interface ArraySchema extends Savable {
/**
* Getter for property <I>https://w3id.org/cwl/salad#items</I><br>
*
* <BLOCKQUOTE>
*
* Defines the type of the array elements. *
*
* </BLOCKQUOTE>
*/
Object getItems();
/**
* Getter for property <I>https://w3id.org/cwl/salad#type</I><br>
*
* <BLOCKQUOTE>
*
* Must be `array` *
*
* </BLOCKQUOTE>
*/
enum_d062602be0b4b8fd33e69e29a841317b6ab665bc getType();
}
102 changes: 102 additions & 0 deletions src/main/java/org/galaxyproject/gxformat2/v19_09/ArraySchemaImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package org.galaxyproject.gxformat2.v19_09;

import org.galaxyproject.gxformat2.v19_09.utils.LoaderInstances;
import org.galaxyproject.gxformat2.v19_09.utils.LoadingOptions;
import org.galaxyproject.gxformat2.v19_09.utils.LoadingOptionsBuilder;
import org.galaxyproject.gxformat2.v19_09.utils.SavableImpl;
import org.galaxyproject.gxformat2.v19_09.utils.ValidationException;

/** Auto-generated class implementation for <I>https://w3id.org/cwl/salad#ArraySchema</I><br> */
public class ArraySchemaImpl extends SavableImpl implements ArraySchema {
private LoadingOptions loadingOptions_ = new LoadingOptionsBuilder().build();
private java.util.Map<String, Object> extensionFields_ = new java.util.HashMap<String, Object>();

private Object items;

/**
* Getter for property <I>https://w3id.org/cwl/salad#items</I><br>
*
* <BLOCKQUOTE>
*
* Defines the type of the array elements. *
*
* </BLOCKQUOTE>
*/
public Object getItems() {
return this.items;
}

private enum_d062602be0b4b8fd33e69e29a841317b6ab665bc type;

/**
* Getter for property <I>https://w3id.org/cwl/salad#type</I><br>
*
* <BLOCKQUOTE>
*
* Must be `array` *
*
* </BLOCKQUOTE>
*/
public enum_d062602be0b4b8fd33e69e29a841317b6ab665bc getType() {
return this.type;
}

/**
* Used by {@link org.galaxyproject.gxformat2.v19_09.utils.RootLoader} to construct instances of
* ArraySchemaImpl.
*
* @param __doc_ Document fragment to load this record object from (presumably a {@link
* java.util.Map}).
* @param __baseUri_ Base URI to generate child document IDs against.
* @param __loadingOptions Context for loading URIs and populating objects.
* @param __docRoot_ ID at this position in the document (if available) (maybe?)
* @throws ValidationException If the document fragment is not a {@link java.util.Map} or
* validation of fields fails.
*/
public ArraySchemaImpl(
final Object __doc_,
final String __baseUri_,
LoadingOptions __loadingOptions,
final String __docRoot_) {
super(__doc_, __baseUri_, __loadingOptions, __docRoot_);
// Prefix plumbing variables with '__' to reduce likelihood of collision with
// generated names.
String __baseUri = __baseUri_;
String __docRoot = __docRoot_;
if (!(__doc_ instanceof java.util.Map)) {
throw new ValidationException("ArraySchemaImpl called on non-map");
}
final java.util.Map<String, Object> __doc = (java.util.Map<String, Object>) __doc_;
final java.util.List<ValidationException> __errors =
new java.util.ArrayList<ValidationException>();
if (__loadingOptions != null) {
this.loadingOptions_ = __loadingOptions;
}
Object items;
try {
items =
LoaderInstances
.uri_union_of_PrimitiveType_or_RecordSchema_or_EnumSchema_or_ArraySchema_or_StringInstance_or_array_of_union_of_PrimitiveType_or_RecordSchema_or_EnumSchema_or_ArraySchema_or_StringInstance_False_True_2
.loadField(__doc.get("items"), __baseUri, __loadingOptions);
} catch (ValidationException e) {
items = null; // won't be used but prevents compiler from complaining.
final String __message = "the `items` field is not valid because:";
__errors.add(new ValidationException(__message, e));
}
enum_d062602be0b4b8fd33e69e29a841317b6ab665bc type;
try {
type =
LoaderInstances.typedsl_enum_d062602be0b4b8fd33e69e29a841317b6ab665bc_2.loadField(
__doc.get("type"), __baseUri, __loadingOptions);
} catch (ValidationException e) {
type = null; // won't be used but prevents compiler from complaining.
final String __message = "the `type` field is not valid because:";
__errors.add(new ValidationException(__message, e));
}
if (!__errors.isEmpty()) {
throw new ValidationException("Trying 'RecordField'", __errors);
}
this.items = (Object) items;
this.type = (enum_d062602be0b4b8fd33e69e29a841317b6ab665bc) type;
}
}
Loading

0 comments on commit a5e3e22

Please sign in to comment.