/*
 * $Id$
 *
 * SARL is an general-purpose agent programming language.
 * More details on http://www.sarl.io
 *
 * Copyright (C) 2014-2026 SARL.io, the original authors and main authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.sarl.lang.tests.general.parsing.oop;

import static io.sarl.lang.validation.IssueCodes.DUPLICATE_TYPE_NAME;
import static io.sarl.tests.api.tools.TestAssertions.assertNullOrEmpty;
import static io.sarl.tests.api.tools.TestEObjects.file;
import static io.sarl.tests.api.tools.TestUtils.multilineString;
import static io.sarl.tests.api.tools.TestValidator.validate;
import static org.eclipse.xtext.xbase.validation.IssueCodes.CYCLIC_INHERITANCE;
import static org.eclipse.xtext.xbase.validation.IssueCodes.INCOMPATIBLE_TYPES;
import static org.eclipse.xtext.xbase.validation.IssueCodes.TYPE_BOUNDS_MISMATCH;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.eclipse.xtext.common.types.JvmTypeConstraint;
import org.eclipse.xtext.common.types.JvmTypeParameter;
import org.eclipse.xtext.common.types.JvmVisibility;
import org.eclipse.xtext.common.types.TypesPackage;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

import io.sarl.lang.sarl.SarlAction;
import io.sarl.lang.sarl.SarlAgent;
import io.sarl.lang.sarl.SarlBehavior;
import io.sarl.lang.sarl.SarlClass;
import io.sarl.lang.sarl.SarlPackage;
import io.sarl.lang.sarl.SarlScript;
import io.sarl.lang.sarl.SarlSkill;
import io.sarl.lang.tests.api.AbstractSarlTest;

/**
 * @author $Author: sgalland$
 * @version $Name$ $Revision$ $Date$
 * @mavengroupid $GroupId$
 * @mavenartifactid $ArtifactId$
 */
@SuppressWarnings("all")
@DisplayName("Syntax: class")
@Tag("core")
public class ClassParsingTest {

	@Nested
	@DisplayName("Syntax: class as top element")
	public class TopClassTest extends AbstractSarlTest {

		protected SarlClass getClazz(SarlScript script) {
			return (SarlClass) script.getXtendTypes().get(0);
		}

		@Test
		@Tag("sarlParsing")
		public void classmodifier_public() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public class C1 { }"));
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			SarlClass clazz = getClazz(mas);
			assertNotNull(clazz);
			//
			assertEquals("C1", clazz.getName());
			assertNull(clazz.getExtends());
			assertEquals(JvmVisibility.PUBLIC, clazz.getVisibility());
			assertEquals(0, clazz.getMembers().size());
			assertFalse(clazz.isAbstract());
			assertFalse(clazz.isAnonymous());
			assertFalse(clazz.isFinal());
			assertFalse(clazz.isLocal());
			assertFalse(clazz.isStatic());
			assertFalse(clazz.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlParsing")
		public void classmodifier_none() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"class C1 { }"));
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			SarlClass clazz = getClazz(mas);
			assertNotNull(clazz);
			//
			assertEquals("C1", clazz.getName());
			assertNull(clazz.getExtends());
			assertEquals(JvmVisibility.PUBLIC, clazz.getVisibility());
			assertEquals(0, clazz.getMembers().size());
			assertFalse(clazz.isAbstract());
			assertFalse(clazz.isAnonymous());
			assertFalse(clazz.isFinal());
			assertFalse(clazz.isLocal());
			assertFalse(clazz.isStatic());
			assertFalse(clazz.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_private() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"private class C1 { }"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlClass(),
					org.eclipse.xtend.core.validation.IssueCodes.INVALID_MODIFIER,
					"Illegal modifier for the class C1; only public, package, abstract, final and strictfp are permitted");
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_protected() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"protected class C1 { }"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlClass(),
					org.eclipse.xtend.core.validation.IssueCodes.INVALID_MODIFIER,
					"Illegal modifier for the class C1; only public, package, abstract, final and strictfp are permitted");
		}

		@Test
		@Tag("sarlParsing")
		public void classmodifier_package() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"package class C1 { }"));
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			SarlClass clazz = getClazz(mas);
			assertNotNull(clazz);
			//
			assertEquals("C1", clazz.getName());
			assertNull(clazz.getExtends());
			assertEquals(JvmVisibility.DEFAULT, clazz.getVisibility());
			assertEquals(0, clazz.getMembers().size());
			assertFalse(clazz.isAbstract());
			assertFalse(clazz.isAnonymous());
			assertFalse(clazz.isFinal());
			assertFalse(clazz.isLocal());
			assertFalse(clazz.isStatic());
			assertFalse(clazz.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlParsing")
		public void classmodifier_abstract() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"abstract class C1 { }"));
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			SarlClass clazz = getClazz(mas);
			assertNotNull(clazz);
			//
			assertEquals("C1", clazz.getName());
			assertNull(clazz.getExtends());
			assertEquals(JvmVisibility.PUBLIC, clazz.getVisibility());
			assertEquals(0, clazz.getMembers().size());
			assertTrue(clazz.isAbstract());
			assertFalse(clazz.isAnonymous());
			assertFalse(clazz.isFinal());
			assertFalse(clazz.isLocal());
			assertFalse(clazz.isStatic());
			assertFalse(clazz.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_static() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"static class C1 { }"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlClass(),
					org.eclipse.xtend.core.validation.IssueCodes.INVALID_MODIFIER,
					"Illegal modifier for the class C1; only public, package, abstract, final and strictfp are permitted");
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_dispatch() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"dispatch class C1 { }"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
				SarlPackage.eINSTANCE.getSarlClass(),
				org.eclipse.xtend.core.validation.IssueCodes.INVALID_MODIFIER,
				"Illegal modifier for the class C1; only public, package, abstract, final and strictfp are permitted");
		}

		@Test
		@Tag("sarlParsing")
		public void classmodifier_final() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"final class C1 { }"));
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			SarlClass clazz = getClazz(mas);
			assertNotNull(clazz);
			//
			assertEquals("C1", clazz.getName());
			assertNull(clazz.getExtends());
			assertEquals(JvmVisibility.PUBLIC, clazz.getVisibility());
			assertEquals(0, clazz.getMembers().size());
			assertFalse(clazz.isAbstract());
			assertFalse(clazz.isAnonymous());
			assertTrue(clazz.isFinal());
			assertFalse(clazz.isLocal());
			assertFalse(clazz.isStatic());
			assertFalse(clazz.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlParsing")
		public void classmodifier_strictfp() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"strictfp class C1 { }"));
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			SarlClass clazz = getClazz(mas);
			assertNotNull(clazz);
			//
			assertEquals("C1", clazz.getName());
			assertNull(clazz.getExtends());
			assertEquals(JvmVisibility.PUBLIC, clazz.getVisibility());
			assertEquals(0, clazz.getMembers().size());
			assertFalse(clazz.isAbstract());
			assertFalse(clazz.isAnonymous());
			assertFalse(clazz.isFinal());
			assertFalse(clazz.isLocal());
			assertFalse(clazz.isStatic());
			assertTrue(clazz.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_native() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"native class C1 { }"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
				SarlPackage.eINSTANCE.getSarlClass(),
				org.eclipse.xtend.core.validation.IssueCodes.INVALID_MODIFIER,
				"Illegal modifier for the class C1; only public, package, abstract, final and strictfp are permitted");
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_volatile() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"volatile class C1 { }"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
				SarlPackage.eINSTANCE.getSarlClass(),
				org.eclipse.xtend.core.validation.IssueCodes.INVALID_MODIFIER,
				"Illegal modifier for the class C1; only public, package, abstract, final and strictfp are permitted");
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_synchronized() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"synchronized class C1 { }"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
				SarlPackage.eINSTANCE.getSarlClass(),
				org.eclipse.xtend.core.validation.IssueCodes.INVALID_MODIFIER,
				"Illegal modifier for the class C1; only public, package, abstract, final and strictfp are permitted");
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_transient() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"transient class C1 { }"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
				SarlPackage.eINSTANCE.getSarlClass(),
				org.eclipse.xtend.core.validation.IssueCodes.INVALID_MODIFIER,
				"Illegal modifier for the class C1; only public, package, abstract, final and strictfp are permitted");
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_abstract_final() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"abstract final class C1 { }"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
				SarlPackage.eINSTANCE.getSarlClass(),
				org.eclipse.xtend.core.validation.IssueCodes.INVALID_MODIFIER,
				"The class C1 can either be abstract or final, not both");
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_abstract_action() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"class C1 { abstract def name() }"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlAction(),
					org.eclipse.xtend.core.validation.IssueCodes.MISSING_ABSTRACT,
					"The abstract method name in type C1 can only be defined by an abstract class");
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_public_package() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public private class C1 { }"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
				SarlPackage.eINSTANCE.getSarlClass(),
				org.eclipse.xtend.core.validation.IssueCodes.INVALID_MODIFIER,
				"The class C1 can only set one of public, package, abstract, final or strictfp");
		}

		@Test
		@Tag("sarlValidation")
		public void duplicateTypeNames() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
				"package io.sarl.lang.tests.test",
				"class B1 {",
				"}",
				"class B2 {",
				"}",
				"class B1 {",
				"}"
			));
			validate(getValidationHelper(), getInjector(), mas).assertError(
				SarlPackage.eINSTANCE.getSarlClass(),
				DUPLICATE_TYPE_NAME,
				"Duplicate type B1");
		}

	}

	@Nested
	@DisplayName("Syntax: class inside class")
	public class InsideClassTest extends AbstractSarlTest {

		protected SarlClass getClazz(SarlScript script) {
			SarlClass enclosing = (SarlClass) script.getXtendTypes().get(0);
			return (SarlClass) enclosing.getMembers().get(0);
		}

		@Test
		@Tag("sarlParsing")
		public void classmodifier_public() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public class EnclosingClass {",
					"  public static class C1 { }",
					"}"));
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			SarlClass clazz = getClazz(mas);
			assertNotNull(clazz);
			//
			assertEquals("C1", clazz.getName());
			assertNull(clazz.getExtends());
			assertEquals(JvmVisibility.PUBLIC, clazz.getVisibility());
			assertEquals(0, clazz.getMembers().size());
			assertFalse(clazz.isAbstract());
			assertFalse(clazz.isAnonymous());
			assertFalse(clazz.isFinal());
			assertFalse(clazz.isLocal());
			assertTrue(clazz.isStatic());
			assertFalse(clazz.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_none() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public class EnclosingClass {",
					"  class C1 { }",
					"}"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlClass(),
					org.eclipse.xtend.core.validation.IssueCodes.MISSING_STATIC_MODIFIER,
					"Nested classes must be static");
		}

		@Test
		@Tag("sarlParsing")
		public void classmodifier_private() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public class EnclosingClass {",
					"  private static class C1 { }",
					"}"));
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			SarlClass clazz = getClazz(mas);
			assertNotNull(clazz);
			//
			assertEquals("C1", clazz.getName());
			assertNull(clazz.getExtends());
			assertEquals(JvmVisibility.PRIVATE, clazz.getVisibility());
			assertEquals(0, clazz.getMembers().size());
			assertFalse(clazz.isAbstract());
			assertFalse(clazz.isAnonymous());
			assertFalse(clazz.isFinal());
			assertFalse(clazz.isLocal());
			assertTrue(clazz.isStatic());
			assertFalse(clazz.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlParsing")
		public void classmodifier_protected() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public class EnclosingClass {",
					"  protected static class C1 { }",
					"}"));
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			SarlClass clazz = getClazz(mas);
			assertNotNull(clazz);
			//
			assertEquals("C1", clazz.getName());
			assertNull(clazz.getExtends());
			assertEquals(JvmVisibility.PROTECTED, clazz.getVisibility());
			assertEquals(0, clazz.getMembers().size());
			assertFalse(clazz.isAbstract());
			assertFalse(clazz.isAnonymous());
			assertFalse(clazz.isFinal());
			assertFalse(clazz.isLocal());
			assertTrue(clazz.isStatic());
			assertFalse(clazz.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlParsing")
		public void classmodifier_package() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public class EnclosingClass {",
					"  package static class C1 { }",
					"}"));
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			SarlClass clazz = getClazz(mas);
			assertNotNull(clazz);
			//
			assertEquals("C1", clazz.getName());
			assertNull(clazz.getExtends());
			assertEquals(JvmVisibility.DEFAULT, clazz.getVisibility());
			assertEquals(0, clazz.getMembers().size());
			assertFalse(clazz.isAbstract());
			assertFalse(clazz.isAnonymous());
			assertFalse(clazz.isFinal());
			assertFalse(clazz.isLocal());
			assertTrue(clazz.isStatic());
			assertFalse(clazz.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlParsing")
		public void classmodifier_abstract() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public class EnclosingClass {",
					"  abstract static class C1 { }",
					"}"));
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			SarlClass clazz = getClazz(mas);
			assertNotNull(clazz);
			//
			assertEquals("C1", clazz.getName());
			assertNull(clazz.getExtends());
			assertEquals(JvmVisibility.PUBLIC, clazz.getVisibility());
			assertEquals(0, clazz.getMembers().size());
			assertTrue(clazz.isAbstract());
			assertFalse(clazz.isAnonymous());
			assertFalse(clazz.isFinal());
			assertFalse(clazz.isLocal());
			assertTrue(clazz.isStatic());
			assertFalse(clazz.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlParsing")
		public void classmodifier_static() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public class EnclosingClass {",
					"  static class C1 { }",
					"}"));
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			SarlClass clazz = getClazz(mas);
			assertNotNull(clazz);
			//
			assertEquals("C1", clazz.getName());
			assertNull(clazz.getExtends());
			assertEquals(JvmVisibility.PUBLIC, clazz.getVisibility());
			assertEquals(0, clazz.getMembers().size());
			assertFalse(clazz.isAbstract());
			assertFalse(clazz.isAnonymous());
			assertFalse(clazz.isFinal());
			assertFalse(clazz.isLocal());
			assertTrue(clazz.isStatic());
			assertFalse(clazz.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_dispatch() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public class EnclosingClass {",
					"  dispatch static class C1 { }",
					"}"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
				SarlPackage.eINSTANCE.getSarlClass(),
				org.eclipse.xtend.core.validation.IssueCodes.INVALID_MODIFIER,
				"Illegal modifier for the class C1; only public, package, protected, private, static, final, abstract and strictfp are permitted");
		}

		@Test
		@Tag("sarlParsing")
		public void classmodifier_final() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public class EnclosingClass {",
					"  final static class C1 { }",
					"}"));
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			SarlClass clazz = getClazz(mas);
			assertNotNull(clazz);
			//
			assertEquals("C1", clazz.getName());
			assertNull(clazz.getExtends());
			assertEquals(JvmVisibility.PUBLIC, clazz.getVisibility());
			assertEquals(0, clazz.getMembers().size());
			assertFalse(clazz.isAbstract());
			assertFalse(clazz.isAnonymous());
			assertTrue(clazz.isFinal());
			assertFalse(clazz.isLocal());
			assertTrue(clazz.isStatic());
			assertFalse(clazz.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlParsing")
		public void classmodifier_strictfp() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public class EnclosingClass {",
					"  strictfp static class C1 { }",
					"}"));
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			SarlClass clazz = getClazz(mas);
			assertNotNull(clazz);
			//
			assertEquals("C1", clazz.getName());
			assertNull(clazz.getExtends());
			assertEquals(JvmVisibility.PUBLIC, clazz.getVisibility());
			assertEquals(0, clazz.getMembers().size());
			assertFalse(clazz.isAbstract());
			assertFalse(clazz.isAnonymous());
			assertFalse(clazz.isFinal());
			assertFalse(clazz.isLocal());
			assertTrue(clazz.isStatic());
			assertTrue(clazz.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_native() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public class EnclosingClass {",
					"  native class C1 { }",
					"}"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
				SarlPackage.eINSTANCE.getSarlClass(),
				org.eclipse.xtend.core.validation.IssueCodes.INVALID_MODIFIER,
				"Illegal modifier for the class C1; only public, package, protected, private, static, final, abstract and strictfp are permitted");
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_volatile() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public class EnclosingClass {",
					"  volatile static class C1 { }",
					"}"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
				SarlPackage.eINSTANCE.getSarlClass(),
				org.eclipse.xtend.core.validation.IssueCodes.INVALID_MODIFIER,
				"Illegal modifier for the class C1; only public, package, protected, private, static, final, abstract and strictfp are permitted");
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_synchronized() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public class EnclosingClass {",
					"  synchronized static class C1 { }",
					"}"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
				SarlPackage.eINSTANCE.getSarlClass(),
				org.eclipse.xtend.core.validation.IssueCodes.INVALID_MODIFIER,
				"Illegal modifier for the class C1; only public, package, protected, private, static, final, abstract and strictfp are permitted");
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_transient() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public class EnclosingClass {",
					"  transient static class C1 { }",
					"}"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
				SarlPackage.eINSTANCE.getSarlClass(),
				org.eclipse.xtend.core.validation.IssueCodes.INVALID_MODIFIER,
				"Illegal modifier for the class C1; only public, package, protected, private, static, final, abstract and strictfp are permitted");
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_abstract_final() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public class EnclosingClass {",
					"  abstract final static class C1 { }",
					"}"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
				SarlPackage.eINSTANCE.getSarlClass(),
				org.eclipse.xtend.core.validation.IssueCodes.INVALID_MODIFIER,
				"The class C1 can either be abstract or final, not both");
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_abstract_action() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public class EnclosingClass {",
					"  static class C1 { abstract def name() }",
					"}"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlAction(),
					org.eclipse.xtend.core.validation.IssueCodes.MISSING_ABSTRACT,
					"The abstract method name in type C1 can only be defined by an abstract class");
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_public_package() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public class EnclosingClass {",
					"  public private static class C1 { }",
					"}"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
				SarlPackage.eINSTANCE.getSarlClass(),
				org.eclipse.xtend.core.validation.IssueCodes.INVALID_MODIFIER,
				"The class C1 can only set one of public, package, protected, private, static, final, abstract or strictfp");
		}

	}

	@Nested
	@DisplayName("Syntax: class inside agent")
	public class InsideAgentTest extends AbstractSarlTest {

		protected SarlClass getClazz(SarlScript script) {
			SarlAgent enclosing = (SarlAgent) script.getXtendTypes().get(0);
			return (SarlClass) enclosing.getMembers().get(0);
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_public() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public agent EnclosingAgent {",
					"  public static class C1 { }",
					"}"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlClass(),
					org.eclipse.xtend.core.validation.IssueCodes.INVALID_MODIFIER);
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_none() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public agent EnclosingAgent {",
					"  class C1 { }",
					"}"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlClass(),
					org.eclipse.xtend.core.validation.IssueCodes.MISSING_STATIC_MODIFIER);
		}

		@Test
		@Tag("sarlParsing")
		public void classmodifier_private() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public agent EnclosingAgent {",
					"  private static class C1 { }",
					"}"));
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			SarlClass clazz = getClazz(mas);
			assertNotNull(clazz);
			//
			assertEquals("C1", clazz.getName());
			assertNull(clazz.getExtends());
			assertEquals(JvmVisibility.PRIVATE, clazz.getVisibility());
			assertEquals(0, clazz.getMembers().size());
			assertFalse(clazz.isAbstract());
			assertFalse(clazz.isAnonymous());
			assertFalse(clazz.isFinal());
			assertFalse(clazz.isLocal());
			assertTrue(clazz.isStatic());
			assertFalse(clazz.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlParsing")
		public void classmodifier_protected() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public agent EnclosingAgent {",
					"  protected static class C1 { }",
					"}"));
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			SarlClass clazz = getClazz(mas);
			assertNotNull(clazz);
			//
			assertEquals("C1", clazz.getName());
			assertNull(clazz.getExtends());
			assertEquals(JvmVisibility.PROTECTED, clazz.getVisibility());
			assertEquals(0, clazz.getMembers().size());
			assertFalse(clazz.isAbstract());
			assertFalse(clazz.isAnonymous());
			assertFalse(clazz.isFinal());
			assertFalse(clazz.isLocal());
			assertTrue(clazz.isStatic());
			assertFalse(clazz.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlParsing")
		public void classmodifier_package() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public agent EnclosingAgent {",
					"  package static class C1 { }",
					"}"));
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			SarlClass clazz = getClazz(mas);
			assertNotNull(clazz);
			//
			assertEquals("C1", clazz.getName());
			assertNull(clazz.getExtends());
			assertEquals(JvmVisibility.DEFAULT, clazz.getVisibility());
			assertEquals(0, clazz.getMembers().size());
			assertFalse(clazz.isAbstract());
			assertFalse(clazz.isAnonymous());
			assertFalse(clazz.isFinal());
			assertFalse(clazz.isLocal());
			assertTrue(clazz.isStatic());
			assertFalse(clazz.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlParsing")
		public void classmodifier_abstract() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public agent EnclosingAgent {",
					"  abstract static class C1 { }",
					"}"));
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			SarlClass clazz = getClazz(mas);
			assertNotNull(clazz);
			//
			assertEquals("C1", clazz.getName());
			assertNull(clazz.getExtends());
			assertEquals(JvmVisibility.PROTECTED, clazz.getVisibility());
			assertEquals(0, clazz.getMembers().size());
			assertTrue(clazz.isAbstract());
			assertFalse(clazz.isAnonymous());
			assertFalse(clazz.isFinal());
			assertFalse(clazz.isLocal());
			assertTrue(clazz.isStatic());
			assertFalse(clazz.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlParsing")
		public void classmodifier_static() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public agent EnclosingAgent {",
					"  static class C1 { }",
					"}"));
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			SarlClass clazz = getClazz(mas);
			assertNotNull(clazz);
			//
			assertEquals("C1", clazz.getName());
			assertNull(clazz.getExtends());
			assertEquals(JvmVisibility.PROTECTED, clazz.getVisibility());
			assertEquals(0, clazz.getMembers().size());
			assertFalse(clazz.isAbstract());
			assertFalse(clazz.isAnonymous());
			assertFalse(clazz.isFinal());
			assertFalse(clazz.isLocal());
			assertTrue(clazz.isStatic());
			assertFalse(clazz.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_dispatch() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public agent EnclosingAgent {",
					"  dispatch static class C1 { }",
					"}"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
				SarlPackage.eINSTANCE.getSarlClass(),
				org.eclipse.xtend.core.validation.IssueCodes.INVALID_MODIFIER);
		}

		@Test
		@Tag("sarlParsing")
		public void classmodifier_final() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public agent EnclosingAgent {",
					"  final static class C1 { }",
					"}"));
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			SarlClass clazz = getClazz(mas);
			assertNotNull(clazz);
			//
			assertEquals("C1", clazz.getName());
			assertNull(clazz.getExtends());
			assertEquals(JvmVisibility.PROTECTED, clazz.getVisibility());
			assertEquals(0, clazz.getMembers().size());
			assertFalse(clazz.isAbstract());
			assertFalse(clazz.isAnonymous());
			assertTrue(clazz.isFinal());
			assertFalse(clazz.isLocal());
			assertTrue(clazz.isStatic());
			assertFalse(clazz.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlValidation")
		public void classmodifier_strictfp() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public agent EnclosingAgent {",
					"  strictfp static class C1 { }",
					"}"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlClass(),
					org.eclipse.xtend.core.validation.IssueCodes.INVALID_MODIFIER);
		}

		@Test
		@Tag("sarlValidation")
