Skip to content

Commit e5000ee

Browse files
authored
feat: CICS STARTBROWSE improvements (#2501)
1 parent ed23ce2 commit e5000ee

File tree

5 files changed

+274
-20
lines changed

5 files changed

+274
-20
lines changed

server/engine/src/main/antlr4/org/eclipse/lsp/cobol/implicitDialects/cics/CICSParser.g4

+4-9
Original file line numberDiff line numberDiff line change
@@ -1007,15 +1007,10 @@ cics_startbr: STARTBR cics_startbr_options;
10071007
cics_startbr_options: ((FILE | DATASET) cics_name | (RIDFLD | SYSID) cics_data_area | (KEYLENGTH | REQID) cics_data_value | GENERIC |
10081008
DEBKEY | DEBREC | RBA | RRN | XRBA | GTEQ | EQUAL | cics_handle_response)+;
10091009

1010-
/** STARTBROWSE ACTIVITY / CONTAINER / EVENT / PROCESS */
1011-
cics_startbrowse: STARTBROWSE (cics_startbrowse_activity | cics_startbrowse_container | cics_startbrowse_event |
1012-
cics_startbrowse_process);
1013-
cics_startbrowse_activity: ACTIVITY (ACTIVITYID cics_data_value | PROCESS cics_data_value PROCESSTYPE cics_data_value |
1014-
BROWSETOKEN cics_data_area | cics_handle_response)+;
1015-
cics_startbrowse_container: CONTAINER (ACTIVITYID cics_data_value | PROCESS cics_data_value PROCESSTYPE cics_data_value |
1016-
CHANNEL cics_data_value | BROWSETOKEN cics_data_area | cics_handle_response)+;
1017-
cics_startbrowse_event: EVENT (BROWSETOKEN cics_data_area | ACTIVITYID cics_data_value | cics_handle_response)+;
1018-
cics_startbrowse_process: PROCESS (PROCESSTYPE cics_data_value | BROWSETOKEN cics_data_area | cics_handle_response)+;
1010+
/** STARTBROWSE ACTIVITY / CONTAINER / EVENT / PROCESS / TIMER */
1011+
cics_startbrowse: STARTBROWSE (cics_startbrowse_body);
1012+
cics_startbrowse_body: ((ACTIVITY | CONTAINER | EVENT | PROCESS) | (ACTIVITYID | PROCESSTYPE | CHANNEL | TIMER) cics_data_value | cics_startbrowse_processWithValue_subrule | BROWSETOKEN cics_data_area | cics_handle_response)+;
1013+
cics_startbrowse_processWithValue_subrule: PROCESS cics_data_value;
10191014

10201015
/** SUSPEND (both) */
10211016
cics_suspend: SUSPEND cics_suspend_body;

server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/cics/utility/CICSOptionsCheckBaseUtility.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -120,27 +120,27 @@ protected boolean checkHasMandatoryOptions(List<?> rules, ParserRuleContext ctx,
120120
* @param ctx - The overall context.
121121
* @param options - String of the element that is required.
122122
*/
123-
protected <E extends ParseTree> void checkPrerequisiteIsMet(List<E> requiredContext, List<E> optionalContext, ParserRuleContext ctx, String options) {
123+
protected <E> void checkPrerequisiteIsMet(List<E> requiredContext, List<E> optionalContext, ParserRuleContext ctx, String options) {
124124
checkPrerequisiteIsMet(isNodePresent(requiredContext), isNodePresent(optionalContext), ctx, options);
125125
}
126126

127-
protected <E extends ParseTree> void checkPrerequisiteIsMet(E requiredContext, List<E> optionalContext, ParserRuleContext ctx, String options) {
127+
protected <E> void checkPrerequisiteIsMet(E requiredContext, List<E> optionalContext, ParserRuleContext ctx, String options) {
128128
checkPrerequisiteIsMet(isNodePresent(requiredContext), isNodePresent(optionalContext), ctx, options);
129129
}
130130

131-
protected <E extends ParseTree> void checkPrerequisiteIsMet(List<E> requiredContext, E optionalContext, ParserRuleContext ctx, String options) {
131+
protected <E> void checkPrerequisiteIsMet(List<E> requiredContext, E optionalContext, ParserRuleContext ctx, String options) {
132132
checkPrerequisiteIsMet(isNodePresent(requiredContext), isNodePresent(optionalContext), ctx, options);
133133
}
134134

135-
protected <E extends ParseTree> void checkPrerequisiteIsMet(E requiredContext, E optionalContext, ParserRuleContext ctx, String options) {
135+
protected <E> void checkPrerequisiteIsMet(E requiredContext, E optionalContext, ParserRuleContext ctx, String options) {
136136
checkPrerequisiteIsMet(isNodePresent(requiredContext), isNodePresent(optionalContext), ctx, options);
137137
}
138138

139-
private <E extends ParseTree> Boolean isNodePresent(E node) {
139+
private <E> Boolean isNodePresent(E node) {
140140
return node != null;
141141
}
142142

143-
private <E extends ParseTree> boolean isNodePresent(List<E> node) {
143+
private <E> boolean isNodePresent(List<E> node) {
144144
if (node == null || node.isEmpty()) {
145145
return false;
146146
}

server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/cics/utility/CICSOptionsCheckUtility.java

+3
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,9 @@ public CICSOptionsCheckUtility(DialectProcessingContext context, List<SyntaxErro
237237
optionsMap.put(
238238
CICSResetOptionsCheckUtility.RULE_INDEX,
239239
new CICSResetOptionsCheckUtility(context, errors));
240+
optionsMap.put(
241+
CICSStartbrowseOptionsCheckUtility.RULE_INDEX,
242+
new CICSStartbrowseOptionsCheckUtility(context, errors));
240243
optionsMap.put(
241244
CICSPopHandleOptionsCheckUtility.RULE_INDEX,
242245
new CICSPopHandleOptionsCheckUtility(context, errors));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
* Copyright (c) 2024 Broadcom.
3+
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
4+
*
5+
* This program and the accompanying materials are made
6+
* available under the terms of the Eclipse Public License 2.0
7+
* which is available at https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* Broadcom, Inc. - initial API and implementation
13+
*
14+
*/
15+
16+
package org.eclipse.lsp.cobol.implicitDialects.cics.utility;
17+
18+
import org.antlr.v4.runtime.ParserRuleContext;
19+
import org.eclipse.lsp.cobol.common.dialects.DialectProcessingContext;
20+
import org.eclipse.lsp.cobol.common.error.ErrorSeverity;
21+
import org.eclipse.lsp.cobol.common.error.SyntaxError;
22+
import org.eclipse.lsp.cobol.implicitDialects.cics.CICSLexer;
23+
import org.eclipse.lsp.cobol.implicitDialects.cics.CICSParser;
24+
25+
import java.util.HashMap;
26+
import java.util.List;
27+
import java.util.Map;
28+
29+
import static org.eclipse.lsp.cobol.implicitDialects.cics.CICSParser.RULE_cics_startbrowse;
30+
31+
/** Checks CICS Startbrowse rules for required and invalid options */
32+
public class CICSStartbrowseOptionsCheckUtility extends CICSOptionsCheckBaseUtility {
33+
34+
public static final int RULE_INDEX = RULE_cics_startbrowse;
35+
36+
private static final Map<Integer, ErrorSeverity> DUPLICATE_CHECK_OPTIONS = new HashMap<Integer, ErrorSeverity>() {
37+
{
38+
put(CICSLexer.ACTIVITYID, ErrorSeverity.ERROR);
39+
put(CICSLexer.PROCESS, ErrorSeverity.WARNING);
40+
put(CICSLexer.PROCESSTYPE, ErrorSeverity.ERROR);
41+
put(CICSLexer.ACTIVITY, ErrorSeverity.WARNING);
42+
put(CICSLexer.BROWSETOKEN, ErrorSeverity.WARNING);
43+
put(CICSLexer.CONTAINER, ErrorSeverity.WARNING);
44+
put(CICSLexer.CHANNEL, ErrorSeverity.ERROR);
45+
put(CICSLexer.EVENT, ErrorSeverity.ERROR);
46+
put(CICSLexer.TIMER, ErrorSeverity.WARNING);
47+
}
48+
};
49+
50+
/**
51+
* Checks CICS Startbrowse rules for required and invalid options
52+
* @param dialectProcessingContext
53+
* @param errors
54+
*/
55+
public CICSStartbrowseOptionsCheckUtility(
56+
DialectProcessingContext dialectProcessingContext,
57+
List<SyntaxError> errors) {
58+
super(dialectProcessingContext, errors, DUPLICATE_CHECK_OPTIONS);
59+
}
60+
61+
/**
62+
* Entrypoint to check CICS STARTBROWSE rule options
63+
*
64+
* @param ctx ParserRuleContext subclass containing options
65+
* @param <E> A subclass of ParserRuleContext
66+
*/
67+
public <E extends ParserRuleContext> void checkOptions(E ctx) {
68+
if (ctx.getRuleIndex() == CICSParser.RULE_cics_startbrowse_body) {
69+
checkBody((CICSParser.Cics_startbrowse_bodyContext) ctx);
70+
}
71+
}
72+
73+
private void checkBody(CICSParser.Cics_startbrowse_bodyContext ctx) {
74+
if (!ctx.ACTIVITY().isEmpty()) {
75+
checkAllOptionsArePresentOrAbsent("PROCESS and PROCESSTYPE", ctx, ctx.cics_startbrowse_processWithValue_subrule(), ctx.PROCESSTYPE());
76+
77+
checkHasIllegalOptions(ctx.PROCESS(), "PROCESS, in this context, requires a value");
78+
checkMutuallyExclusiveOptions("ACTIVITYID or PROCESS", ctx.ACTIVITYID(), ctx.cics_startbrowse_processWithValue_subrule());
79+
} else if (!ctx.CONTAINER().isEmpty() || !ctx.CHANNEL().isEmpty()) {
80+
if (!ctx.CHANNEL().isEmpty()) {
81+
checkHasMandatoryOptions(ctx.CONTAINER(), ctx, "CONTAINER");
82+
checkHasIllegalOptions(ctx.PROCESS(), "PROCESS");
83+
checkHasIllegalOptions(ctx.PROCESSTYPE(), "PROCESSTYPE");
84+
}
85+
checkPrerequisiteIsMet(ctx.cics_startbrowse_processWithValue_subrule(), ctx.PROCESSTYPE(), ctx, "PROCESSTYPE without PROCESS");
86+
checkHasIllegalOptions(ctx.PROCESS(), "PROCESS, in this context, requires a value");
87+
checkMutuallyExclusiveOptions("ACTIVITYID, PROCESS or CHANNEL", ctx.ACTIVITYID(), ctx.PROCESS(), ctx.cics_startbrowse_processWithValue_subrule(), ctx.CHANNEL());
88+
} else if (!ctx.EVENT().isEmpty()) {
89+
checkHasIllegalOptions(ctx.CHANNEL(), "CHANNEL");
90+
checkHasIllegalOptions(ctx.PROCESS(), "PROCESS");
91+
checkHasIllegalOptions(ctx.cics_startbrowse_processWithValue_subrule(), "PROCESS");
92+
checkHasIllegalOptions(ctx.PROCESSTYPE(), "PROCESSTYPE");
93+
} else if (!ctx.PROCESS().isEmpty()) {
94+
checkHasMandatoryOptions(ctx.PROCESSTYPE(), ctx, "PROCESSTYPE");
95+
96+
checkHasIllegalOptions(ctx.cics_startbrowse_processWithValue_subrule(), "PROCESS, in this context, cannot have a value");
97+
checkHasIllegalOptions(ctx.ACTIVITYID(), "ACTIVITYID");
98+
checkHasIllegalOptions(ctx.ACTIVITY(), "ACTIVITY");
99+
checkHasIllegalOptions(ctx.CONTAINER(), "CONTAINER");
100+
checkHasIllegalOptions(ctx.EVENT(), "EVENT");
101+
checkHasIllegalOptions(ctx.CHANNEL(), "CHANNEL");
102+
checkHasIllegalOptions(ctx.TIMER(), "TIMER");
103+
} else if (!ctx.TIMER().isEmpty()) {
104+
checkHasIllegalOptions(ctx.CHANNEL(), "CHANNEL");
105+
checkHasIllegalOptions(ctx.PROCESS(), "PROCESS");
106+
checkHasIllegalOptions(ctx.cics_startbrowse_processWithValue_subrule(), "PROCESS");
107+
checkHasIllegalOptions(ctx.PROCESSTYPE(), "PROCESSTYPE");
108+
}
109+
110+
checkPrerequisiteIsMet(ctx.CONTAINER(), ctx.CHANNEL(), ctx, "CHANNEL without CONTAINER");
111+
checkHasMandatoryOptions(ctx.BROWSETOKEN(), ctx, "BROWSETOKEN");
112+
113+
checkHasExactlyOneOption("ACTIVITY, CONTAINER, PROCESS, EVENT or TIMER", ctx, ctx.ACTIVITY(), ctx.CONTAINER(), ctx.PROCESS(), ctx.EVENT(), ctx.TIMER());
114+
115+
}
116+
117+
private void checkProcessSubrule(CICSParser.Cics_startbrowse_processWithValue_subruleContext ctx) {
118+
119+
}
120+
}

server/engine/src/test/java/org/eclipse/lsp/cobol/usecases/TestCicsExciStartBrowseStatement.java

+141-5
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,26 @@
1616

1717
import com.google.common.collect.ImmutableList;
1818
import com.google.common.collect.ImmutableMap;
19+
import org.eclipse.lsp.cobol.common.error.ErrorSource;
1920
import org.eclipse.lsp.cobol.test.engine.UseCaseEngine;
21+
import org.eclipse.lsp.cobol.usecases.common.CICSTestUtils;
22+
import org.eclipse.lsp4j.Diagnostic;
23+
import org.eclipse.lsp4j.DiagnosticSeverity;
24+
import org.eclipse.lsp4j.Range;
2025
import org.junit.jupiter.api.Test;
2126

27+
import java.util.HashMap;
28+
import java.util.Map;
29+
2230
/**
2331
* Tests CICS STARTBROWSE CONTAINER (EXCI) statement. Ref:
24-
* https://www.ibm.com/docs/en/cics-ts/6.1?topic=interface-exec-cics-link-command-exci
32+
* https://www.ibm.com/docs/en/cics-ts/6.x?topic=summary-startbrowse-activity
2533
*/
2634
public class TestCicsExciStartBrowseStatement {
27-
public static final String TEXT =
28-
" IDENTIFICATION DIVISION.\n"
35+
36+
// Test Strings
37+
private static final String EXCI_TEXT =
38+
" IDENTIFICATION DIVISION.\n"
2939
+ " PROGRAM-ID. EXCISTMTTEST.\n"
3040
+ " DATA DIVISION.\n"
3141
+ " WORKING-STORAGE SECTION.\n"
@@ -42,8 +52,134 @@ public class TestCicsExciStartBrowseStatement {
4252
+ " RETCODE({$RETURN-CODE})\n"
4353
+ " END-EXEC.";
4454

55+
private static final String STARTBROWSE_ACTIVITY_VALID = "STARTBROWSE ACTIVITY ACTIVITYID({$varOne}) BROWSETOKEN({$varOne})";
56+
private static final String STARTBROWSE_CONTAINER_VALID = "STARTBROWSE CONTAINER PROCESS({$varOne}) PROCESSTYPE({$varOne}) BROWSETOKEN({$varOne})";
57+
private static final String STARTBROWSE_EVENT_VALID = "STARTBROWSE EVENT ACTIVITYID({$varOne}) BROWSETOKEN({$varOne})";
58+
private static final String STARTBROWSE_PROCESS_VALID = "STARTBROWSE PROCESS PROCESSTYPE({$varOne}) BROWSETOKEN({$varOne})";
59+
private static final String STARTBROWSE_TIMER_VALID = "STARTBROWSE TIMER({$varOne}) BROWSETOKEN({$varOne})";
60+
61+
private static final String STARTBROWSE_INVALID_CONTAINER = "STARTBROWSE CONTAINER PROCESS({$varOne}) {PROCESSTYPE|errorOne}({$varOne}) {CHANNEL|errorTwo}({$varOne}) BROWSETOKEN({$varOne})";
62+
private static final String STARTBROWSE_INVALID_CONTAINER_2 = "STARTBROWSE {_CONTAINER|errorTwo_} {_{_PROCESS|errorOne_}|errorTwo_} PROCESSTYPE({$varOne}) BROWSETOKEN({$varOne})";
63+
private static final String STARTBROWSE_INVALID_ACTIVITY = "STARTBROWSE {_ACTIVITY ACTIVITYID({$varOne} )|errorOne_}";
64+
private static final String STARTBROWSE_INVALID_EVENT = "STARTBROWSE {_EVENT ACTIVITYID({$varOne} )|errorOne_}";
65+
private static final String STARTBROWSE_INVALID_PROCESS = "STARTBROWSE {_PROCESS BROWSETOKEN({$varOne} )|errorOne_}";
66+
private static final String STARTBROWSE_INVALID_PROCESS_2 = "STARTBROWSE PROCESS {_PROCESS({$varOne})|errorOne_} PROCESSTYPE({$varOne}) BROWSETOKEN({$varOne})";
67+
68+
// Test Functions
69+
@Test
70+
void testEXCIvariant() {
71+
UseCaseEngine.runTest(EXCI_TEXT, ImmutableList.of(), ImmutableMap.of());
72+
}
73+
74+
@Test
75+
void testStartBrowseActivity() {
76+
CICSTestUtils.noErrorTest(STARTBROWSE_ACTIVITY_VALID);
77+
}
78+
79+
@Test
80+
void testStartBrowseContainer() {
81+
CICSTestUtils.noErrorTest(STARTBROWSE_CONTAINER_VALID);
82+
}
83+
84+
@Test
85+
void testStartBrowseEvent() {
86+
CICSTestUtils.noErrorTest(STARTBROWSE_EVENT_VALID);
87+
}
88+
89+
@Test
90+
void testStartBrowseProcess() {
91+
CICSTestUtils.noErrorTest(STARTBROWSE_PROCESS_VALID);
92+
}
93+
94+
@Test
95+
void testStartBrowseTimer() {
96+
CICSTestUtils.noErrorTest(STARTBROWSE_TIMER_VALID);
97+
}
98+
99+
// Invalid Tests
100+
@Test
101+
void testStartBrowseContainerInvalid() {
102+
Map<String, Diagnostic> tempDiagnostic = new HashMap<>();
103+
tempDiagnostic.put("errorOne",
104+
new Diagnostic(
105+
new Range(),
106+
"Invalid option provided: PROCESSTYPE",
107+
DiagnosticSeverity.Error,
108+
ErrorSource.PARSING.getText()));
109+
tempDiagnostic.put("errorTwo",
110+
new Diagnostic(
111+
new Range(),
112+
"Options \"ACTIVITYID, PROCESS or CHANNEL\" are mutually exclusive.",
113+
DiagnosticSeverity.Error,
114+
ErrorSource.PARSING.getText()));
115+
116+
CICSTestUtils.errorTest(STARTBROWSE_INVALID_CONTAINER, tempDiagnostic);
117+
}
118+
119+
@Test
120+
void testStartBrowseContainerInvalid_2() {
121+
Map<String, Diagnostic> tempDiagnostic = new HashMap<>();
122+
tempDiagnostic.put("errorOne",
123+
new Diagnostic(
124+
new Range(),
125+
"Invalid option provided: PROCESS, in this context, requires a value",
126+
DiagnosticSeverity.Error,
127+
ErrorSource.PARSING.getText()));
128+
tempDiagnostic.put("errorTwo",
129+
new Diagnostic(
130+
new Range(),
131+
"Exactly one option required, options are mutually exclusive: ACTIVITY, CONTAINER, PROCESS, EVENT or TIMER",
132+
DiagnosticSeverity.Error,
133+
ErrorSource.PARSING.getText()));
134+
135+
CICSTestUtils.errorTest(STARTBROWSE_INVALID_CONTAINER_2, tempDiagnostic);
136+
}
137+
138+
@Test
139+
void testStartBrowseActivityInvalid() {
140+
Map<String, Diagnostic> tempDiagnostic = new HashMap<>();
141+
tempDiagnostic.put("errorOne",
142+
new Diagnostic(
143+
new Range(),
144+
"Missing required option: BROWSETOKEN",
145+
DiagnosticSeverity.Error,
146+
ErrorSource.PARSING.getText()));
147+
CICSTestUtils.errorTest(STARTBROWSE_INVALID_ACTIVITY, tempDiagnostic);
148+
}
149+
150+
@Test
151+
void testStartBrowseEventInvalid() {
152+
Map<String, Diagnostic> tempDiagnostic = new HashMap<>();
153+
tempDiagnostic.put("errorOne",
154+
new Diagnostic(
155+
new Range(),
156+
"Missing required option: BROWSETOKEN",
157+
DiagnosticSeverity.Error,
158+
ErrorSource.PARSING.getText()));
159+
CICSTestUtils.errorTest(STARTBROWSE_INVALID_EVENT, tempDiagnostic);
160+
}
161+
162+
@Test
163+
void testStartBrowseProcessInvalid() {
164+
Map<String, Diagnostic> tempDiagnostic = new HashMap<>();
165+
tempDiagnostic.put("errorOne",
166+
new Diagnostic(
167+
new Range(),
168+
"Missing required option: PROCESSTYPE",
169+
DiagnosticSeverity.Error,
170+
ErrorSource.PARSING.getText()));
171+
CICSTestUtils.errorTest(STARTBROWSE_INVALID_PROCESS, tempDiagnostic);
172+
}
173+
45174
@Test
46-
void test() {
47-
UseCaseEngine.runTest(TEXT, ImmutableList.of(), ImmutableMap.of());
175+
void testStartBrowseProcessInvalid_2() {
176+
Map<String, Diagnostic> tempDiagnostic = new HashMap<>();
177+
tempDiagnostic.put("errorOne",
178+
new Diagnostic(
179+
new Range(),
180+
"Invalid option provided: PROCESS, in this context, cannot have a value",
181+
DiagnosticSeverity.Error,
182+
ErrorSource.PARSING.getText()));
183+
CICSTestUtils.errorTest(STARTBROWSE_INVALID_PROCESS_2, tempDiagnostic);
48184
}
49185
}

0 commit comments

Comments
 (0)