Skip to content

Commit

Permalink
[FIXED JENKINS-41185] Add support for and/not/or of when conditions
Browse files Browse the repository at this point in the history
  • Loading branch information
abayer committed Mar 7, 2017
1 parent f58bc2d commit da36bda
Show file tree
Hide file tree
Showing 29 changed files with 1,121 additions and 44 deletions.
Expand Up @@ -37,24 +37,24 @@
*/
public class ModelASTWhen extends ModelASTElement {

private List<ModelASTStep> conditions = new ArrayList<>();
private List<ModelASTWhenContent> conditions = new ArrayList<>();

public ModelASTWhen(Object sourceLocation) {
super(sourceLocation);
}

public List<ModelASTStep> getConditions() {
public List<ModelASTWhenContent> getConditions() {
return conditions;
}

public void setConditions(List<ModelASTStep> conditions) {
public void setConditions(List<ModelASTWhenContent> conditions) {
this.conditions = conditions;
}

@Override
public Object toJSON() {
final JSONArray a = new JSONArray();
for (ModelASTStep c: conditions) {
for (ModelASTWhenContent c : conditions) {
a.add(c.toJSON());
}
return new JSONObject().accumulate("conditions", a);
Expand All @@ -63,7 +63,7 @@ public Object toJSON() {
@Override
public String toGroovy() {
StringBuilder result = new StringBuilder("when {\n");
for (ModelASTStep c: conditions) {
for (ModelASTWhenContent c : conditions) {
result.append(c.toGroovy()).append("\n");
}
result.append("}\n");
Expand All @@ -73,7 +73,7 @@ public String toGroovy() {
@Override
public void removeSourceLocation() {
super.removeSourceLocation();
for (ModelASTStep c: conditions) {
for (ModelASTWhenContent c : conditions) {
c.removeSourceLocation();
}
}
Expand All @@ -88,8 +88,8 @@ public String toString() {
@Override
public void validate(final ModelValidator validator) {
validator.validateElement(this);
for (ModelASTStep s : conditions) {
validator.validateWhenCondition(s);
for (ModelASTWhenContent s : conditions) {
s.validate(validator);
}
}

Expand Down
@@ -0,0 +1,175 @@
/*
* The MIT License
*
* Copyright (c) 2017, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package org.jenkinsci.plugins.pipeline.modeldefinition.ast;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import groovy.transform.ToString;
import hudson.model.Describable;
import hudson.model.Descriptor;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.jenkinsci.plugins.pipeline.modeldefinition.DescriptorLookupCache;
import org.jenkinsci.plugins.pipeline.modeldefinition.validator.ModelValidator;
import org.jenkinsci.plugins.structs.describable.DescribableModel;
import org.jenkinsci.plugins.structs.describable.DescribableParameter;

import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
* @author Andrew Bayer
*/
@ToString(includeSuper = true, includeSuperProperties = true)
@SuppressFBWarnings(value = "SE_NO_SERIALVERSIONID")
public class ModelASTWhenCondition extends ModelASTElement implements ModelASTWhenContent {
private String name;
private ModelASTArgumentList args;
private List<ModelASTWhenContent> children = new ArrayList<>();

public ModelASTWhenCondition(Object sourceLocation) {
super(sourceLocation);
}

@Override
public JSONObject toJSON() {
JSONObject o = new JSONObject();
o.accumulate("name", name);
if (args != null) {
o.accumulate("arguments", args.toJSON());
}
if (!children.isEmpty()) {
final JSONArray a = new JSONArray();
for (ModelASTWhenContent child : children) {
a.add(child.toJSON());
}
o.accumulate("children", a);
}
return o;
}

@Override
public void validate(@Nonnull ModelValidator validator) {
validator.validateElement(this);
if (args != null) {
args.validate(validator);
}
for (ModelASTWhenContent c : children) {
c.validate(validator);
}
}

@Override
public String toGroovy() {

StringBuilder result = new StringBuilder();
if (!children.isEmpty()) {
result.append(name).append(" {\n");
for (ModelASTWhenContent child : children) {
result.append(child.toGroovy()).append("\n");
}
result.append("}\n");
} else {
result.append(name).append(" ").append(getArgs().toGroovy());
}
return result.toString();
}

@Override
public void removeSourceLocation() {
super.removeSourceLocation();
if (args != null) {
args.removeSourceLocation();
}
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public ModelASTArgumentList getArgs() {
return args;
}

public void setArgs(ModelASTArgumentList args) {
this.args = args;
}

public List<ModelASTWhenContent> getChildren() {
return children;
}

public void setChildren(List<ModelASTWhenContent> c) {
this.children = c;
}

@Override
public String toString() {
return "ModelASTWhenCondition{" +
"name='" + name + '\'' +
", args=" + args +
", children=" + children +
"}";
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}

ModelASTWhenCondition that = (ModelASTWhenCondition) o;

if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) {
return false;
}
if (getChildren() != null ? !getChildren().equals(that.getChildren()) : that.getChildren() != null) {
return false;
}
return getArgs() != null ? getArgs().equals(that.getArgs()) : that.getArgs() == null;

}

@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (getName() != null ? getName().hashCode() : 0);
result = 31 * result + (getArgs() != null ? getArgs().hashCode() : 0);
result = 31 * result + (getChildren() != null ? getChildren().hashCode() : 0);
return result;
}
}
@@ -0,0 +1,41 @@
/*
* The MIT License
*
* Copyright (c) 2017, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package org.jenkinsci.plugins.pipeline.modeldefinition.ast;

import org.jenkinsci.plugins.pipeline.modeldefinition.validator.ModelValidator;

/**
*
* @author Andrew Bayer
*/
public interface ModelASTWhenContent {
String toGroovy();

Object toJSON();

void validate(ModelValidator validator);

void removeSourceLocation();
}
Expand Up @@ -30,7 +30,7 @@
/**
* Code expression {@link ModelASTStage} will be executed or not.
*/
public class ModelASTWhenExpression extends AbstractModelASTCodeBlock {
public class ModelASTWhenExpression extends AbstractModelASTCodeBlock implements ModelASTWhenContent {
public ModelASTWhenExpression(Object sourceLocation) {
super(sourceLocation, "expression");
}
Expand Down
Expand Up @@ -48,6 +48,7 @@
import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTTrigger;
import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTTriggers;
import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTWhen;
import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTWhenCondition;


public interface ModelValidator {
Expand Down Expand Up @@ -93,5 +94,5 @@ public interface ModelValidator {

boolean validateElement(ModelASTLibraries libraries);

boolean validateWhenCondition(ModelASTStep condition);
boolean validateElement(ModelASTWhenCondition condition);
}
30 changes: 29 additions & 1 deletion pipeline-model-api/src/main/resources/ast-schema.json
Expand Up @@ -255,6 +255,34 @@
],
"additionalProperties": false
},
"nestedWhenCondition": {
"description": "A when condition holding one or more other when conditions",
"type": "object",
"properties": {
"name": {
"type": "string"
},
"children": {
"type": "array",
"minItems": 1,
"items": {
"anyOf": [
{
"$ref": "#/definitions/step"
},
{
"$ref": "#/definitions/nestedWhenCondition"
}
]
}
}
},
"required": [
"name",
"children"
],
"additionalProperties": false
},
"branch": {
"description": "A block of steps, generally one of: the contents of a stage, the contents of a build condition block, or one branch of a parallel invocation",
"type": "object",
Expand Down Expand Up @@ -348,7 +376,7 @@
"$ref": "#/definitions/step"
},
{
"$ref": "#/definitions/treeStep"
"$ref": "#/definitions/nestedWhenCondition"
}
]
}
Expand Down
Expand Up @@ -42,7 +42,11 @@ import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTStages
import org.jenkinsci.plugins.pipeline.modeldefinition.model.MethodsToList
import org.jenkinsci.plugins.pipeline.modeldefinition.model.StepsBlock
import org.jenkinsci.plugins.pipeline.modeldefinition.parser.Converter
import org.jenkinsci.plugins.pipeline.modeldefinition.when.DeclarativeStageConditional
import org.jenkinsci.plugins.pipeline.modeldefinition.when.DeclarativeStageConditionalDescriptor
import org.jenkinsci.plugins.structs.SymbolLookup
import org.jenkinsci.plugins.structs.describable.DescribableModel
import org.jenkinsci.plugins.structs.describable.DescribableParameter
import org.jenkinsci.plugins.structs.describable.UninstantiatedDescribable
import org.jenkinsci.plugins.workflow.actions.TagsAction
import org.jenkinsci.plugins.workflow.cps.CpsFlowExecution
Expand All @@ -57,6 +61,7 @@ import org.jenkinsci.plugins.workflow.graph.FlowNode
import org.jenkinsci.plugins.workflow.job.WorkflowRun
import org.jenkinsci.plugins.workflow.steps.StepDescriptor
import org.jenkinsci.plugins.workflow.support.steps.StageStep
import org.jvnet.tiger_types.Types

import javax.annotation.Nullable
import javax.lang.model.SourceVersion
Expand Down Expand Up @@ -413,4 +418,30 @@ public class Utils {
FlowExecution exec = owner.getOrNull()
return exec instanceof CpsFlowExecution ? (CpsFlowExecution) exec : null
}

public static boolean whenConditionDescriptorFound(String name) {
Descriptor d = DescriptorLookupCache.publicCache.lookupFunction(name)
return d != null && d instanceof DeclarativeStageConditionalDescriptor
}

public static boolean nestedWhenCondition(String name) {
Descriptor d = DescriptorLookupCache.publicCache.lookupFunction(name)
if (d != null && d instanceof DeclarativeStageConditionalDescriptor) {
return ((DeclarativeStageConditionalDescriptor)d).containsNested()
}
return false
}

public static boolean takesWhenConditionList(String name) {
DescribableModel<? extends Describable> model = DescriptorLookupCache.publicCache.modelForStepOrFunction(name)

if (model != null && Types.isSubClassOf(model.type, DeclarativeStageConditional.class)) {
DescribableParameter p = model.soleRequiredParameter
if (p != null) {
return Types.isSubClassOf(p.getRawType(), Collection.class)
}
}

return false
}
}

0 comments on commit da36bda

Please sign in to comment.