diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java
index 65a12b87be77..552b17e32f8e 100644
--- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java
+++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java
@@ -104,18 +104,27 @@ public class ProgrammingExerciseService {
* (https://docs.oracle.com/javase/specs/jls/se14/html/jls-7.html#jls-7.4.1)
* with the restriction to a-z,A-Z,_ as "Java letter" and 0-9 as digits due to JavaScript/Browser Unicode character class limitations
*/
- private static final String PACKAGE_NAME_REGEX = "^(?!.*(?:\\.|^)(?:abstract|continue|for|new|switch|assert|default|if|package|synchronized|boolean|do|goto|private|this|break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|strictfp|volatile|const|float|native|super|while|_|true|false|null)(?:\\.|$))[A-Z_a-z]\\w*(?:\\.[A-Z_a-z]\\w*)*$";
+ private static final String PACKAGE_NAME_REGEX_FOR_JAVA_KOTLIN = "^(?!.*(?:\\.|^)(?:abstract|continue|for|new|switch|assert|default|if|package|synchronized|boolean|do|goto|private|this|break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|strictfp|volatile|const|float|native|super|while|_|true|false|null)(?:\\.|$))[A-Z_a-z]\\w*(?:\\.[A-Z_a-z]\\w*)*$";
/**
* Swift package name Regex derived from
* (https://docs.swift.org/swift-book/ReferenceManual/LexicalStructure.html#ID412),
* with the restriction to a-z,A-Z as "Swift letter" and 0-9 as digits where no separators are allowed
*/
- private static final String SWIFT_PACKAGE_NAME_REGEX = "^(?!(?:associatedtype|class|deinit|enum|extension|fileprivate|func|import|init|inout|internal|let|open|operator|private|protocol|public|rethrows|static|struct|subscript|typealias|var|break|case|continue|default|defer|do|else|fallthrough|for|guard|if|in|repeat|return|switch|where|while|as|Any|catch|false|is|nil|super|self|Self|throw|throws|true|try|_|[sS]wift)$)[A-Za-z][\\dA-Za-z]*$";
+ private static final String PACKAGE_NAME_REGEX_FOR_SWIFT = "^(?!(?:associatedtype|class|deinit|enum|extension|fileprivate|func|import|init|inout|internal|let|open|operator|private|protocol|public|rethrows|static|struct|subscript|typealias|var|break|case|continue|default|defer|do|else|fallthrough|for|guard|if|in|repeat|return|switch|where|while|as|Any|catch|false|is|nil|super|self|Self|throw|throws|true|try|_|[sS]wift)$)[A-Za-z][\\dA-Za-z]*$";
- private final Pattern packageNamePattern = Pattern.compile(PACKAGE_NAME_REGEX);
+ /**
+ * Go package name Regex derived from The Go Programming Language Specification limited to ASCII. Package names are
+ * identifiers.
+ * They allow letters, digits and underscore. They cannot start with a digit. The package name cannot be a keyword or "_".
+ */
+ private static final String PACKAGE_NAME_REGEX_FOR_GO = "^(?!(?:break|default|func|interface|select|case|defer|go|map|struct|chan|else|goto|package|switch|const|fallthrough|if|range|type|continue|for|import|return|var|_)$)[A-Za-z_][A-Za-z0-9_]*$";
+
+ private static final Pattern PACKAGE_NAME_PATTERN_FOR_JAVA_KOTLIN = Pattern.compile(PACKAGE_NAME_REGEX_FOR_JAVA_KOTLIN);
- private final Pattern packageNamePatternForSwift = Pattern.compile(SWIFT_PACKAGE_NAME_REGEX);
+ private static final Pattern PACKAGE_NAME_PATTERN_FOR_SWIFT = Pattern.compile(PACKAGE_NAME_REGEX_FOR_SWIFT);
+
+ private static final Pattern PACKAGE_NAME_PATTERN_FOR_GO = Pattern.compile(PACKAGE_NAME_REGEX_FOR_GO);
private static final Logger log = LoggerFactory.getLogger(ProgrammingExerciseService.class);
@@ -426,12 +435,12 @@ private void validatePackageName(ProgrammingExercise programmingExercise, Progra
}
// Check if package name matches regex
- Matcher packageNameMatcher;
- switch (programmingExercise.getProgrammingLanguage()) {
- case JAVA, KOTLIN -> packageNameMatcher = packageNamePattern.matcher(programmingExercise.getPackageName());
- case SWIFT -> packageNameMatcher = packageNamePatternForSwift.matcher(programmingExercise.getPackageName());
+ Matcher packageNameMatcher = switch (programmingExercise.getProgrammingLanguage()) {
+ case JAVA, KOTLIN -> PACKAGE_NAME_PATTERN_FOR_JAVA_KOTLIN.matcher(programmingExercise.getPackageName());
+ case SWIFT -> PACKAGE_NAME_PATTERN_FOR_SWIFT.matcher(programmingExercise.getPackageName());
+ case GO -> PACKAGE_NAME_PATTERN_FOR_GO.matcher(programmingExercise.getPackageName());
default -> throw new IllegalArgumentException("Programming language not supported");
- }
+ };
if (!packageNameMatcher.matches()) {
throw new BadRequestAlertException("The package name is invalid", "Exercise", "packagenameInvalid");
}
diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsProgrammingLanguageFeatureService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsProgrammingLanguageFeatureService.java
index c4ffd9f71bcb..cca2995cdba0 100644
--- a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsProgrammingLanguageFeatureService.java
+++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsProgrammingLanguageFeatureService.java
@@ -41,7 +41,7 @@ public JenkinsProgrammingLanguageFeatureService() {
programmingLanguageFeatures.put(C, new ProgrammingLanguageFeature(C, false, false, true, false, false, List.of(FACT, GCC), false, false));
programmingLanguageFeatures.put(C_PLUS_PLUS, new ProgrammingLanguageFeature(C_PLUS_PLUS, false, false, true, false, false, List.of(), false, false));
programmingLanguageFeatures.put(C_SHARP, new ProgrammingLanguageFeature(C_SHARP, false, false, true, false, false, List.of(), false, false));
- programmingLanguageFeatures.put(GO, new ProgrammingLanguageFeature(GO, false, false, true, false, false, List.of(), false, false));
+ programmingLanguageFeatures.put(GO, new ProgrammingLanguageFeature(GO, false, false, true, true, false, List.of(), false, false));
programmingLanguageFeatures.put(HASKELL, new ProgrammingLanguageFeature(HASKELL, false, false, false, false, true, List.of(), false, false));
programmingLanguageFeatures.put(JAVA,
new ProgrammingLanguageFeature(JAVA, true, true, true, true, false, List.of(PLAIN_GRADLE, GRADLE_GRADLE, PLAIN_MAVEN, MAVEN_MAVEN, MAVEN_BLACKBOX), true, false));
diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java
index 1828ed874f48..4a62b227323a 100644
--- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java
+++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java
@@ -48,7 +48,7 @@ public LocalCIProgrammingLanguageFeatureService() {
programmingLanguageFeatures.put(C, new ProgrammingLanguageFeature(C, false, true, true, false, false, List.of(FACT, GCC), false, true));
programmingLanguageFeatures.put(C_PLUS_PLUS, new ProgrammingLanguageFeature(C_PLUS_PLUS, false, false, true, false, false, List.of(), false, true));
programmingLanguageFeatures.put(C_SHARP, new ProgrammingLanguageFeature(C_SHARP, false, false, true, false, false, List.of(), false, true));
- programmingLanguageFeatures.put(GO, new ProgrammingLanguageFeature(GO, false, false, true, false, false, List.of(), false, true));
+ programmingLanguageFeatures.put(GO, new ProgrammingLanguageFeature(GO, false, false, true, true, false, List.of(), false, true));
programmingLanguageFeatures.put(HASKELL, new ProgrammingLanguageFeature(HASKELL, true, false, false, false, true, List.of(), false, true));
programmingLanguageFeatures.put(JAVA,
new ProgrammingLanguageFeature(JAVA, true, true, true, true, false, List.of(PLAIN_GRADLE, GRADLE_GRADLE, PLAIN_MAVEN, MAVEN_MAVEN), false, true));
diff --git a/src/main/resources/templates/go/exercise/bubblesort.go b/src/main/resources/templates/go/exercise/bubblesort.go
index c52d8b6ba229..2a05e9ca9e13 100644
--- a/src/main/resources/templates/go/exercise/bubblesort.go
+++ b/src/main/resources/templates/go/exercise/bubblesort.go
@@ -1,4 +1,4 @@
-package assignment
+package ${packageName}
type BubbleSort struct{}
diff --git a/src/main/resources/templates/go/exercise/context.go b/src/main/resources/templates/go/exercise/context.go
index ebbd2cd85e9d..aced7eb6bea0 100644
--- a/src/main/resources/templates/go/exercise/context.go
+++ b/src/main/resources/templates/go/exercise/context.go
@@ -1,4 +1,4 @@
-package assignment
+package ${packageName}
type Context struct{}
diff --git a/src/main/resources/templates/go/exercise/go.mod b/src/main/resources/templates/go/exercise/go.mod
index 928bbea04c33..224363e133d4 100644
--- a/src/main/resources/templates/go/exercise/go.mod
+++ b/src/main/resources/templates/go/exercise/go.mod
@@ -1,3 +1,3 @@
-module artemis/assignment
+module artemis/${packageName}
go 1.23.2
diff --git a/src/main/resources/templates/go/exercise/mergesort.go b/src/main/resources/templates/go/exercise/mergesort.go
index f2d48f09e350..e3c8f12f29d6 100644
--- a/src/main/resources/templates/go/exercise/mergesort.go
+++ b/src/main/resources/templates/go/exercise/mergesort.go
@@ -1,4 +1,4 @@
-package assignment
+package ${packageName}
type MergeSort struct{}
diff --git a/src/main/resources/templates/go/exercise/policy.go b/src/main/resources/templates/go/exercise/policy.go
index 4156b94a456b..7676b8bfbaeb 100644
--- a/src/main/resources/templates/go/exercise/policy.go
+++ b/src/main/resources/templates/go/exercise/policy.go
@@ -1,4 +1,4 @@
-package assignment
+package ${packageName}
type Policy struct{}
diff --git a/src/main/resources/templates/go/exercise/sortstrategy.go b/src/main/resources/templates/go/exercise/sortstrategy.go
index e008b5f52afb..3f3884429007 100644
--- a/src/main/resources/templates/go/exercise/sortstrategy.go
+++ b/src/main/resources/templates/go/exercise/sortstrategy.go
@@ -1,3 +1,3 @@
-package assignment
+package ${packageName}
type SortStrategy interface{}
diff --git a/src/main/resources/templates/go/solution/bubblesort.go b/src/main/resources/templates/go/solution/bubblesort.go
index 2c5a46334348..2ee261ec5c20 100644
--- a/src/main/resources/templates/go/solution/bubblesort.go
+++ b/src/main/resources/templates/go/solution/bubblesort.go
@@ -1,4 +1,4 @@
-package assignment
+package ${packageName}
import "time"
diff --git a/src/main/resources/templates/go/solution/client/client.go b/src/main/resources/templates/go/solution/client/client.go
index 4489670b760c..0d7238e95968 100644
--- a/src/main/resources/templates/go/solution/client/client.go
+++ b/src/main/resources/templates/go/solution/client/client.go
@@ -5,7 +5,7 @@ import (
"math/rand"
"time"
- "artemis/assignment"
+ "artemis/${packageName}"
)
// Constants define iteration and random date generation bounds.
@@ -18,8 +18,8 @@ const (
// main demonstrates the sorting process.
func main() {
// Init Context and Policy
- context := assignment.NewContext()
- policy := assignment.NewPolicy(context)
+ context := ${packageName}.NewContext()
+ policy := ${packageName}.NewPolicy(context)
// Run multiple times to simulate different sorting strategies
for i := 0; i < Iterations; i++ {
diff --git a/src/main/resources/templates/go/solution/context.go b/src/main/resources/templates/go/solution/context.go
index 910ca6e6f611..8165ff883668 100644
--- a/src/main/resources/templates/go/solution/context.go
+++ b/src/main/resources/templates/go/solution/context.go
@@ -1,4 +1,4 @@
-package assignment
+package ${packageName}
import "time"
diff --git a/src/main/resources/templates/go/solution/go.mod b/src/main/resources/templates/go/solution/go.mod
index 928bbea04c33..224363e133d4 100644
--- a/src/main/resources/templates/go/solution/go.mod
+++ b/src/main/resources/templates/go/solution/go.mod
@@ -1,3 +1,3 @@
-module artemis/assignment
+module artemis/${packageName}
go 1.23.2
diff --git a/src/main/resources/templates/go/solution/mergesort.go b/src/main/resources/templates/go/solution/mergesort.go
index e60da39480ab..7722813d0b47 100644
--- a/src/main/resources/templates/go/solution/mergesort.go
+++ b/src/main/resources/templates/go/solution/mergesort.go
@@ -1,4 +1,4 @@
-package assignment
+package ${packageName}
import "time"
diff --git a/src/main/resources/templates/go/solution/policy.go b/src/main/resources/templates/go/solution/policy.go
index 7b06e8c3ee30..fedd5da02aba 100644
--- a/src/main/resources/templates/go/solution/policy.go
+++ b/src/main/resources/templates/go/solution/policy.go
@@ -1,4 +1,4 @@
-package assignment
+package ${packageName}
type Policy struct {
context *Context
diff --git a/src/main/resources/templates/go/solution/sortstrategy.go b/src/main/resources/templates/go/solution/sortstrategy.go
index 79ea803d6903..f61ad7d8f9ad 100644
--- a/src/main/resources/templates/go/solution/sortstrategy.go
+++ b/src/main/resources/templates/go/solution/sortstrategy.go
@@ -1,4 +1,4 @@
-package assignment
+package ${packageName}
import "time"
diff --git a/src/main/resources/templates/go/test/behavior/behavior_test.go b/src/main/resources/templates/go/test/behavior/behavior_test.go
index f1be13b8cea5..fcd84bafe90f 100644
--- a/src/main/resources/templates/go/test/behavior/behavior_test.go
+++ b/src/main/resources/templates/go/test/behavior/behavior_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"time"
- "artemis/assignment"
+ "artemis/${packageName}"
)
type SortStrategy interface {
@@ -15,8 +15,8 @@ type SortStrategy interface {
type Context interface {
GetDates() []time.Time
SetDates(dates []time.Time)
- GetSortAlgorithm() assignment.SortStrategy
- SetSortAlgorithm(strategy assignment.SortStrategy)
+ GetSortAlgorithm() ${packageName}.SortStrategy
+ SetSortAlgorithm(strategy ${packageName}.SortStrategy)
}
type Policy interface {
@@ -28,7 +28,7 @@ func TestBubbleSort(t *testing.T) {
dates, datesWithCorrectOrder := createTestDates()
- var bubbleSortAny interface{} = assignment.NewBubbleSort()
+ var bubbleSortAny interface{} = ${packageName}.NewBubbleSort()
bubbleSort := bubbleSortAny.(SortStrategy)
bubbleSort.PerformSort(dates)
@@ -42,7 +42,7 @@ func TestMergeSort(t *testing.T) {
dates, datesWithCorrectOrder := createTestDates()
- var mergeSortAny interface{} = assignment.NewMergeSort()
+ var mergeSortAny interface{} = ${packageName}.NewMergeSort()
mergeSort := mergeSortAny.(SortStrategy)
mergeSort.PerformSort(dates)
@@ -76,7 +76,7 @@ func TestUseMergeSortForBigList(t *testing.T) {
bigList = append(bigList, time.Unix(0, 0))
}
chosenSortStrategy := configurePolicyAndContext(bigList)
- _, ok := chosenSortStrategy.(*assignment.MergeSort)
+ _, ok := chosenSortStrategy.(*${packageName}.MergeSort)
if !ok {
t.Fatal("The sort algorithm of Context was not MergeSort for a list with more than 10 dates.")
}
@@ -90,19 +90,19 @@ func TestUseBubbleSortForSmallList(t *testing.T) {
bigList = append(bigList, time.Unix(0, 0))
}
chosenSortStrategy := configurePolicyAndContext(bigList)
- _, ok := chosenSortStrategy.(*assignment.BubbleSort)
+ _, ok := chosenSortStrategy.(*${packageName}.BubbleSort)
if !ok {
t.Fatal("The sort algorithm of Context was not MergeSort for a list with more than 10 dates.")
}
}
func configurePolicyAndContext(dates []time.Time) interface{} {
- contextOriginal := assignment.NewContext()
+ contextOriginal := ${packageName}.NewContext()
var contextAny interface{} = contextOriginal
context := contextAny.(Context)
context.SetDates(dates)
- var policyAny interface{} = assignment.NewPolicy(contextOriginal)
+ var policyAny interface{} = ${packageName}.NewPolicy(contextOriginal)
policy := policyAny.(Policy)
policy.Configure()
diff --git a/src/main/resources/templates/go/test/go.mod b/src/main/resources/templates/go/test/go.mod
index 85c0afed131a..8ac02f91c2ce 100644
--- a/src/main/resources/templates/go/test/go.mod
+++ b/src/main/resources/templates/go/test/go.mod
@@ -2,6 +2,6 @@ module artemis/test
go 1.23.2
-replace artemis/assignment => ${studentParentWorkingDirectoryName}
+replace artemis/${packageName} => ${studentParentWorkingDirectoryName}
-require artemis/assignment v0.0.0-00010101000000-000000000000
+require artemis/${packageName} v0.0.0-00010101000000-000000000000
diff --git a/src/main/resources/templates/go/test/structural/structural_test.go b/src/main/resources/templates/go/test/structural/structural_test.go
index 726a33c7a9c1..815a5ed70a0d 100644
--- a/src/main/resources/templates/go/test/structural/structural_test.go
+++ b/src/main/resources/templates/go/test/structural/structural_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "artemis/assignment"
+ "artemis/${packageName}"
)
type SortStrategy interface {
@@ -14,8 +14,8 @@ type SortStrategy interface {
type Context interface {
GetDates() []time.Time
SetDates(dates []time.Time)
- GetSortAlgorithm() assignment.SortStrategy
- SetSortAlgorithm(strategy assignment.SortStrategy)
+ GetSortAlgorithm() ${packageName}.SortStrategy
+ SetSortAlgorithm(strategy ${packageName}.SortStrategy)
}
type Policy interface {
@@ -25,7 +25,7 @@ type Policy interface {
func TestBubbleSort(t *testing.T) {
defer handlePanic(t)
- var bubbleSort interface{} = new(assignment.BubbleSort)
+ var bubbleSort interface{} = new(${packageName}.BubbleSort)
_ = bubbleSort.(SortStrategy)
}
@@ -33,7 +33,7 @@ func TestBubbleSort(t *testing.T) {
func TestMergeSort(t *testing.T) {
defer handlePanic(t)
- var mergeSort interface{} = new(assignment.MergeSort)
+ var mergeSort interface{} = new(${packageName}.MergeSort)
_ = mergeSort.(SortStrategy)
}
@@ -41,7 +41,7 @@ func TestMergeSort(t *testing.T) {
func TestContext(t *testing.T) {
defer handlePanic(t)
- var context interface{} = new(assignment.Context)
+ var context interface{} = new(${packageName}.Context)
_ = context.(Context)
}
@@ -49,7 +49,7 @@ func TestContext(t *testing.T) {
func TestPolicy(t *testing.T) {
defer handlePanic(t)
- var policy interface{} = new(assignment.Policy)
+ var policy interface{} = new(${packageName}.Policy)
_ = policy.(Policy)
}
diff --git a/src/main/webapp/app/exercises/programming/manage/update/programming-exercise-creation-config.ts b/src/main/webapp/app/exercises/programming/manage/update/programming-exercise-creation-config.ts
index 36c9b8df3a8c..51b9056d0b15 100644
--- a/src/main/webapp/app/exercises/programming/manage/update/programming-exercise-creation-config.ts
+++ b/src/main/webapp/app/exercises/programming/manage/update/programming-exercise-creation-config.ts
@@ -22,7 +22,6 @@ export type ProgrammingExerciseCreationConfig = {
invalidRepositoryNamePattern: RegExp;
isImportFromExistingExercise: boolean;
isImportFromFile: boolean;
- appNamePatternForSwift: string;
modePickerOptions?: ModePickerOption[];
withDependencies: boolean;
onWithDependenciesChanged: (withDependencies: boolean) => boolean;
diff --git a/src/main/webapp/app/exercises/programming/manage/update/programming-exercise-update.component.ts b/src/main/webapp/app/exercises/programming/manage/update/programming-exercise-update.component.ts
index 5bdde2d8c65c..b5aa5d3bb090 100644
--- a/src/main/webapp/app/exercises/programming/manage/update/programming-exercise-update.component.ts
+++ b/src/main/webapp/app/exercises/programming/manage/update/programming-exercise-update.component.ts
@@ -23,6 +23,7 @@ import {
INVALID_DIRECTORY_NAME_PATTERN,
INVALID_REPOSITORY_NAME_PATTERN,
MAX_PENALTY_PATTERN,
+ PACKAGE_NAME_PATTERN_FOR_GO,
PACKAGE_NAME_PATTERN_FOR_JAVA_BLACKBOX,
PACKAGE_NAME_PATTERN_FOR_JAVA_KOTLIN,
PROGRAMMING_EXERCISE_SHORT_NAME_PATTERN,
@@ -58,12 +59,13 @@ export const LOCAL_STORAGE_KEY_IS_SIMPLE_MODE = 'isSimpleMode';
export class ProgrammingExerciseUpdateComponent implements AfterViewInit, OnDestroy, OnInit {
protected readonly documentationType: DocumentationType = 'Programming';
protected readonly maxPenaltyPattern = MAX_PENALTY_PATTERN;
- protected readonly packageNamePatternForJavaBlackbox = PACKAGE_NAME_PATTERN_FOR_JAVA_BLACKBOX;
protected readonly invalidRepositoryNamePattern = INVALID_REPOSITORY_NAME_PATTERN;
protected readonly invalidDirectoryNamePattern = INVALID_DIRECTORY_NAME_PATTERN;
protected readonly shortNamePattern = PROGRAMMING_EXERCISE_SHORT_NAME_PATTERN;
- readonly packageNamePatternForJavaKotlin = PACKAGE_NAME_PATTERN_FOR_JAVA_KOTLIN;
- readonly appNamePatternForSwift = APP_NAME_PATTERN_FOR_SWIFT;
+ private readonly packageNameRegexForJavaKotlin = RegExp(PACKAGE_NAME_PATTERN_FOR_JAVA_KOTLIN);
+ private readonly packageNameRegexForJavaBlackbox = RegExp(PACKAGE_NAME_PATTERN_FOR_JAVA_BLACKBOX);
+ private readonly appNameRegexForSwift = RegExp(APP_NAME_PATTERN_FOR_SWIFT);
+ private readonly packageNameRegexForGo = RegExp(PACKAGE_NAME_PATTERN_FOR_GO);
@ViewChild(ProgrammingExerciseInformationComponent) exerciseInfoComponent?: ProgrammingExerciseInformationComponent;
@ViewChild(ProgrammingExerciseModeComponent) exerciseDifficultyComponent?: ProgrammingExerciseModeComponent;
@@ -825,10 +827,17 @@ export class ProgrammingExerciseUpdateComponent implements AfterViewInit, OnDest
* @param useBlackboxPattern whether to allow points in the regex
*/
setPackageNamePattern(language: ProgrammingLanguage, useBlackboxPattern = false) {
- if (language === ProgrammingLanguage.SWIFT) {
- this.packageNamePattern = this.appNamePatternForSwift;
- } else {
- this.packageNamePattern = useBlackboxPattern ? this.packageNamePatternForJavaBlackbox : this.packageNamePatternForJavaKotlin;
+ switch (language) {
+ case ProgrammingLanguage.SWIFT:
+ this.packageNamePattern = APP_NAME_PATTERN_FOR_SWIFT;
+ break;
+ case ProgrammingLanguage.JAVA:
+ case ProgrammingLanguage.KOTLIN:
+ this.packageNamePattern = useBlackboxPattern ? PACKAGE_NAME_PATTERN_FOR_JAVA_BLACKBOX : PACKAGE_NAME_PATTERN_FOR_JAVA_KOTLIN;
+ break;
+ case ProgrammingLanguage.GO:
+ this.packageNamePattern = PACKAGE_NAME_PATTERN_FOR_GO;
+ break;
}
}
@@ -1062,13 +1071,26 @@ export class ProgrammingExerciseUpdateComponent implements AfterViewInit, OnDest
}
private validateExercisePackageName(validationErrorReasons: ValidationReason[]): void {
- // validation on package name has only to be performed for Java, Kotlin and Swift
- if (
- this.programmingExercise.programmingLanguage !== ProgrammingLanguage.JAVA &&
- this.programmingExercise.programmingLanguage !== ProgrammingLanguage.KOTLIN &&
- this.programmingExercise.programmingLanguage !== ProgrammingLanguage.SWIFT
- ) {
- return;
+ let regex;
+ switch (this.programmingExercise.programmingLanguage) {
+ case ProgrammingLanguage.JAVA:
+ if (this.programmingExercise.projectType === ProjectType.MAVEN_BLACKBOX) {
+ regex = this.packageNameRegexForJavaBlackbox;
+ } else {
+ regex = this.packageNameRegexForJavaKotlin;
+ }
+ break;
+ case ProgrammingLanguage.KOTLIN:
+ regex = this.packageNameRegexForJavaKotlin;
+ break;
+ case ProgrammingLanguage.SWIFT:
+ regex = this.appNameRegexForSwift;
+ break;
+ case ProgrammingLanguage.GO:
+ regex = this.packageNameRegexForGo;
+ break;
+ default:
+ return;
}
if (this.programmingExercise.packageName === undefined || this.programmingExercise.packageName === '') {
@@ -1076,39 +1098,19 @@ export class ProgrammingExerciseUpdateComponent implements AfterViewInit, OnDest
translateKey: 'artemisApp.exercise.form.packageName.undefined',
translateValues: {},
});
- } else {
- const patternMatchJavaKotlin: RegExpMatchArray | null = this.programmingExercise.packageName.match(this.packageNamePatternForJavaKotlin);
- const patternMatchJavaBlackbox: RegExpMatchArray | null = this.programmingExercise.packageName.match(this.packageNamePatternForJavaBlackbox);
- const patternMatchSwift: RegExpMatchArray | null = this.programmingExercise.packageName.match(this.appNamePatternForSwift);
- const projectTypeDependentPatternMatch: RegExpMatchArray | null =
- this.programmingExercise.projectType === ProjectType.MAVEN_BLACKBOX ? patternMatchJavaBlackbox : patternMatchJavaKotlin;
-
- if (
- this.programmingExercise.programmingLanguage === ProgrammingLanguage.JAVA &&
- (projectTypeDependentPatternMatch === null || projectTypeDependentPatternMatch.length === 0)
- ) {
- if (this.programmingExercise.projectType === ProjectType.MAVEN_BLACKBOX) {
- validationErrorReasons.push({
- translateKey: 'artemisApp.exercise.form.packageName.pattern.JAVA_BLACKBOX',
- translateValues: {},
- });
- } else {
- validationErrorReasons.push({
- translateKey: 'artemisApp.exercise.form.packageName.pattern.JAVA',
- translateValues: {},
- });
- }
- } else if (this.programmingExercise.programmingLanguage === ProgrammingLanguage.KOTLIN && (patternMatchJavaKotlin === null || patternMatchJavaKotlin.length === 0)) {
- validationErrorReasons.push({
- translateKey: 'artemisApp.exercise.form.packageName.pattern.KOTLIN',
- translateValues: {},
- });
- } else if (this.programmingExercise.programmingLanguage === ProgrammingLanguage.SWIFT && (patternMatchSwift === null || patternMatchSwift.length === 0)) {
- validationErrorReasons.push({
- translateKey: 'artemisApp.exercise.form.packageName.pattern.SWIFT',
- translateValues: {},
- });
- }
+ return;
+ }
+
+ const packageNameDoesMatch = regex.test(this.programmingExercise.packageName);
+ if (!packageNameDoesMatch) {
+ const translateKey =
+ this.programmingExercise.projectType === ProjectType.MAVEN_BLACKBOX
+ ? `artemisApp.exercise.form.packageName.pattern.${this.programmingExercise.programmingLanguage}_BLACKBOX`
+ : `artemisApp.exercise.form.packageName.pattern.${this.programmingExercise.programmingLanguage}`;
+ validationErrorReasons.push({
+ translateKey,
+ translateValues: {},
+ });
}
}
@@ -1244,7 +1246,6 @@ export class ProgrammingExerciseUpdateComponent implements AfterViewInit, OnDest
exerciseCategories: this.exerciseCategories,
existingCategories: this.existingCategories,
updateCategories: this.categoriesChanged,
- appNamePatternForSwift: this.appNamePatternForSwift,
modePickerOptions: this.modePickerOptions,
withDependencies: this.withDependencies,
onWithDependenciesChanged: this.withDependenciesChanged,
diff --git a/src/main/webapp/app/exercises/programming/manage/update/update-components/language/programming-exercise-language.component.html b/src/main/webapp/app/exercises/programming/manage/update/update-components/language/programming-exercise-language.component.html
index 7b60308b7091..4dd22d1e9b35 100644
--- a/src/main/webapp/app/exercises/programming/manage/update/update-components/language/programming-exercise-language.component.html
+++ b/src/main/webapp/app/exercises/programming/manage/update/update-components/language/programming-exercise-language.component.html
@@ -122,7 +122,7 @@
setTimeout(() => this.calculateFormValid())));
}
diff --git a/src/main/webapp/app/shared/constants/input.constants.ts b/src/main/webapp/app/shared/constants/input.constants.ts
index 6748c2507b07..d220eae6bb11 100644
--- a/src/main/webapp/app/shared/constants/input.constants.ts
+++ b/src/main/webapp/app/shared/constants/input.constants.ts
@@ -47,3 +47,7 @@ export const PACKAGE_NAME_PATTERN_FOR_JAVA_KOTLIN =
// with the restriction to a-z,A-Z as "Swift letter" and 0-9 as digits where no separators are allowed
export const APP_NAME_PATTERN_FOR_SWIFT =
'^(?!(?:associatedtype|class|deinit|enum|extension|fileprivate|func|import|init|inout|internal|let|open|operator|private|protocol|public|rethrows|static|struct|subscript|typealias|var|break|case|continue|default|defer|do|else|fallthrough|for|guard|if|in|repeat|return|switch|where|while|as|Any|catch|false|is|nil|super|self|Self|throw|throws|true|try|_|[sS]wift)$)[A-Za-z][0-9A-Za-z]*$';
+// Go package name Regex derived from (https://go.dev/ref/spec#Package_clause) limited to ASCII. Package names are identifiers.
+// They allow letters, digits and underscore. They cannot start with a digit. The package name cannot be a keyword or "_".
+export const PACKAGE_NAME_PATTERN_FOR_GO =
+ '^(?!(?:break|default|func|interface|select|case|defer|go|map|struct|chan|else|goto|package|switch|const|fallthrough|if|range|type|continue|for|import|return|var|_)$)[A-Za-z_][A-Za-z0-9_]*$';
diff --git a/src/main/webapp/i18n/de/exercise.json b/src/main/webapp/i18n/de/exercise.json
index 00cc0421b9db..5890170a7668 100644
--- a/src/main/webapp/i18n/de/exercise.json
+++ b/src/main/webapp/i18n/de/exercise.json
@@ -239,7 +239,8 @@
"JAVA": "Der Package-Name muss aus einem oder mehreren gültigen Java-Bezeichnern bestehen, die mit '.' getrennt sind, z.B. \"net.java\"!",
"KOTLIN": "Der Package-Name muss aus einem oder mehreren gültigen Kotlin-Bezeichnern bestehen, die mit '.' getrennt sind, z.B. \"net.kotlin\"!",
"SWIFT": "Der Package-Name muss aus einem oder mehreren gültigen Swift-Bezeichnern bestehen, die nicht voneinander separiert sind, z.B. \"SwiftEx\"!",
- "JAVA_BLACKBOX": "Der Paketname muss ein gültiger Java-Bezeicher sein. Zusätzlich sind Trennpunkte im Paketnamen für den DejaGnu Aufgabentyp nicht erlaubt."
+ "JAVA_BLACKBOX": "Der Paketname muss ein gültiger Java-Bezeicher sein. Zusätzlich sind Trennpunkte im Paketnamen für den DejaGnu Aufgabentyp nicht erlaubt.",
+ "GO": "Der Package-Name muss ein gültiger Go-Identifier sein, z.B. \"goExercise\"."
}
},
"points": {
diff --git a/src/main/webapp/i18n/en/exercise.json b/src/main/webapp/i18n/en/exercise.json
index 82144be9ffe5..acf92030b0c5 100644
--- a/src/main/webapp/i18n/en/exercise.json
+++ b/src/main/webapp/i18n/en/exercise.json
@@ -239,7 +239,8 @@
"JAVA": "The package name must consist of one or more valid Java identifiers separated by '.', e.g. \"net.java\"!",
"KOTLIN": "The package name must consist of one or more valid Kotlin identifiers separated by '.', e.g. \"net.kotlin\"!",
"SWIFT": "The package name must consist of one or more valid Swift identifiers without any separator, e.g. \"SwiftEx\"!",
- "JAVA_BLACKBOX": "The package name must be a valid Java identifier. In addition, no dots are allowed in the package name of DejaGnu projects"
+ "JAVA_BLACKBOX": "The package name must be a valid Java identifier. In addition, no dots are allowed in the package name of DejaGnu projects",
+ "GO": "The package name must be a valid Go identifier, e.g. \"goExercise\"."
}
},
"points": {
diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-update.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-update.component.spec.ts
index 3e49d0bd49ff..c4cbdf912f8a 100644
--- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-update.component.spec.ts
+++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-update.component.spec.ts
@@ -76,6 +76,7 @@ import { TitleChannelNameComponent } from 'app/shared/form/title-channel-name/ti
import { ExerciseTitleChannelNameModule } from 'app/exercises/shared/exercise-title-channel-name/exercise-title-channel-name.module';
import { CustomNotIncludedInValidatorDirective } from '../../../../../main/webapp/app/shared/validators/custom-not-included-in-validator.directive';
import { ProgrammingExerciseDifficultyComponent } from '../../../../../main/webapp/app/exercises/programming/manage/update/update-components/difficulty/programming-exercise-difficulty.component';
+import { APP_NAME_PATTERN_FOR_SWIFT, PACKAGE_NAME_PATTERN_FOR_JAVA_KOTLIN } from '../../../../../main/webapp/app/shared/constants/input.constants';
describe('ProgrammingExerciseUpdateComponent', () => {
const courseId = 1;
@@ -534,7 +535,7 @@ describe('ProgrammingExerciseUpdateComponent', () => {
expect(courseService.find).toHaveBeenCalledWith(courseId);
expect(comp.selectedProgrammingLanguage).toBe(ProgrammingLanguage.SWIFT);
expect(comp.staticCodeAnalysisAllowed).toBeTrue();
- expect(comp.packageNamePattern).toBe(comp.appNamePatternForSwift);
+ expect(comp.packageNamePattern).toBe(APP_NAME_PATTERN_FOR_SWIFT);
}));
it('should activate SCA for C', fakeAsync(() => {
@@ -560,7 +561,7 @@ describe('ProgrammingExerciseUpdateComponent', () => {
// THEN
expect(comp.selectedProgrammingLanguage).toBe(ProgrammingLanguage.JAVA);
expect(comp.staticCodeAnalysisAllowed).toBeTrue();
- expect(comp.packageNamePattern).toBe(comp.packageNamePatternForJavaKotlin);
+ expect(comp.packageNamePattern).toBe(PACKAGE_NAME_PATTERN_FOR_JAVA_KOTLIN);
}));
it('should deactivate SCA for C (FACT)', fakeAsync(() => {