Skip to content

Cics ROUTE improvements #2626

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -759,9 +759,8 @@ cics_rewrite: REWRITE cics_file_name (TOKEN cics_data_area | FROM cics_data_area
LENGTH cics_data_value | LENGTH cics_data_value | NOSUSPEND | cics_handle_response)+;

/** ROUTE */
cics_route: ROUTE (INTERVAL cics_zero_digit | INTERVAL cics_hhmmss | TIME cics_hhmmss | cics_post_after |
ERRTERM cics_name? | TITLE cics_data_area | LIST cics_data_area | OPCLASS cics_data_area |
REQID cics_name | LDC cics_name | NLEOM | cics_handle_response)*;
cics_route: ROUTE cics_route_body?;
cics_route_body: ((TIME | AFTER | AT | NLEOM) | (REQID | LDC) cics_name | INTERVAL (cics_hhmmss | cics_zero_digit) | ERRTERM cics_name? | (HOURS | MINUTES | SECONDS) cics_data_value | (TITLE | LIST | OPCLASS) cics_data_area | cics_handle_response)+;

/** RUN */
cics_run: RUN (cics_run_default | cics_run_transid);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,34 @@ protected final <E> void checkMutuallyExclusiveOptions(String options, E... rule
}
}

/**
* Helper function to check and see if more than one rule was visited out of a set provided.
*
* @param options Options checked to insert into error message
* @param rules Generic list of rules to check. Will be a collection of ParserRuleContext and/or TerminalNode objects.
* @param <E> Generic type to allow cross-rule context collection.
*/
@SafeVarargs
protected final <E> void checkHasAtLeastOneOption(String options, ParserRuleContext ctx, E... rules) {
int rulesSeen = 0;

for (E rule : rules) {
if (rule == null) {
continue;
}

if (List.class.isAssignableFrom(rule.getClass()) && ((List<?>) rule).isEmpty()) {
continue;
}

rulesSeen++;
}

if (rulesSeen < 1) {
throwException(ErrorSeverity.ERROR, getLocality(ctx), "Must include one or more of the following: ", options);
}
}

/**
* Iterates over the provided response handlers, extracts what is provided, and validates there is
* not RESP2 without RESP
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,9 @@ public CICSOptionsCheckUtility(DialectProcessingContext context, List<SyntaxErro
optionsMap.put(
CICSSoapfaultOptionsCheckUtility.RULE_INDEX,
new CICSSoapfaultOptionsCheckUtility(context, errors));
optionsMap.put(
CICSRouteOptionsCheckUtility.RULE_INDEX,
new CICSRouteOptionsCheckUtility(context, errors));
spOptionsMap.put(
CICSInquireSPOptionsCheckUtility.RULE_INDEX,
new CICSInquireSPOptionsCheckUtility(context, errors));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2024 Broadcom.
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*
*/
package org.eclipse.lsp.cobol.implicitDialects.cics.utility;

import org.antlr.v4.runtime.ParserRuleContext;
import org.eclipse.lsp.cobol.common.dialects.DialectProcessingContext;
import org.eclipse.lsp.cobol.common.error.ErrorSeverity;
import org.eclipse.lsp.cobol.common.error.SyntaxError;
import org.eclipse.lsp.cobol.implicitDialects.cics.CICSLexer;
import org.eclipse.lsp.cobol.implicitDialects.cics.CICSParser;

import java.util.*;

import static org.eclipse.lsp.cobol.implicitDialects.cics.CICSParser.*;

/** Checks CICS ROUTE rules for required and invalid options */
public class CICSRouteOptionsCheckUtility extends CICSOptionsCheckBaseUtility {

public static final int RULE_INDEX = RULE_cics_route;

private static final Map<Integer, ErrorSeverity> DUPLICATE_CHECK_OPTIONS =
new HashMap<Integer, ErrorSeverity>() {
{
put(CICSLexer.INTERVAL, ErrorSeverity.ERROR);
put(CICSLexer.TIME, ErrorSeverity.ERROR);
put(CICSLexer.AFTER, ErrorSeverity.WARNING);
put(CICSLexer.AT, ErrorSeverity.WARNING);
put(CICSLexer.ERRTERM, ErrorSeverity.ERROR);
put(CICSLexer.TITLE, ErrorSeverity.ERROR);
put(CICSLexer.LIST, ErrorSeverity.ERROR);
put(CICSLexer.OPCLASS, ErrorSeverity.ERROR);
put(CICSLexer.REQID, ErrorSeverity.ERROR);
put(CICSLexer.LDC, ErrorSeverity.ERROR);
put(CICSLexer.NLEOM, ErrorSeverity.WARNING);
}
};

public CICSRouteOptionsCheckUtility(DialectProcessingContext context, List<SyntaxError> errors) {
super(context, errors, DUPLICATE_CHECK_OPTIONS);
}

/**
* Entrypoint to check CICS ROUTE rules for required and invalid options
* @param ctx ParserRuleContext subclass containing options
* @param <E> A subclass of ParserRuleContext
*/
public <E extends ParserRuleContext> void checkOptions(E ctx) {
if (ctx.getRuleIndex() == RULE_cics_route_body) {
checkRule((CICSParser.Cics_route_bodyContext) ctx);
checkDuplicates(ctx);
}
}

private void checkRule(CICSParser.Cics_route_bodyContext ctx) {
checkMutuallyExclusiveOptions("INTERVAL, TIME, AFTER or AT", ctx.INTERVAL(), ctx.TIME(), ctx.AFTER(), ctx.AT());

if (!ctx.AFTER().isEmpty() || !ctx.AT().isEmpty()) {
checkHasAtLeastOneOption("HOURS, MINUTES or SECONDS", ctx, ctx.HOURS(), ctx.MINUTES(), ctx.SECONDS());
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2024 Broadcom.
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*
*/

package org.eclipse.lsp.cobol.usecases;

import org.eclipse.lsp.cobol.common.error.ErrorSource;
import org.eclipse.lsp.cobol.usecases.common.CICSTestUtils;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DiagnosticSeverity;
import org.eclipse.lsp4j.Range;
import org.junit.jupiter.api.Test;

import java.util.*;

/**
* Test ROUTE commands. Documentation link: <a
* href="https://www.ibm.com/docs/en/cics-ts/6.x?topic=summary-route">ROUTE
* Command</a>
*
* <p>This class tests all variations of the ROUTE command found in the link above.
*/
public class TestCicsRouteStatement {

// Test Strings
private static final String ROUTE_VALID_1 = "ROUTE AFTER HOURS({$varOne}) MINUTES({$varOne}) SECONDS({$varOne}) ERRTERM({$varOne}) TITLE({$varOne}) LIST({$varOne}) OPCLASS({$varOne}) REQID({$varOne}) LDC({$varOne}) NLEOM";
private static final String ROUTE_VALID_2 = "ROUTE";

private static final String ROUTE_INVALID_1 = "ROUTE INTERVAL({$varOne}) {AFTER|errorOne} HOURS({$varOne})";
private static final String ROUTE_INVALID_2 = "ROUTE {AFTER|errorOne}";

// Test Functions
@Test
void testCicsRouteValid() {
CICSTestUtils.noErrorTest(ROUTE_VALID_1);
CICSTestUtils.noErrorTest(ROUTE_VALID_2);
}

// Invalid Tests
@Test
void testCicsRouteInvalid_1() {
HashMap<String, Diagnostic> expectedDiagnostics = new HashMap<>();
expectedDiagnostics.put("errorOne", new Diagnostic(new Range(), "Options \"INTERVAL, TIME, AFTER or AT\" are mutually exclusive.", DiagnosticSeverity.Error, ErrorSource.PARSING.getText()));
CICSTestUtils.errorTest(ROUTE_INVALID_1, expectedDiagnostics);
}

@Test
void testCicsRouteInvalid_2() {
HashMap<String, Diagnostic> expectedDiagnostics = new HashMap<>();
expectedDiagnostics.put("errorOne", new Diagnostic(new Range(), "Must include one or more of the following: HOURS, MINUTES or SECONDS", DiagnosticSeverity.Error, ErrorSource.PARSING.getText()));
CICSTestUtils.errorTest(ROUTE_INVALID_2, expectedDiagnostics);
}

}
Loading