Bug 522137: refactor JavaScriptCodeFactory

  no functional changes

Change-Id: I4a0d064482134f796757145b9111a60a21b3ba4f
diff --git a/plugins/org.eclipse.ease.lang.javascript.nashorn/src/org/eclipse/ease/lang/javascript/nashorn/NashornScriptEngine.java b/plugins/org.eclipse.ease.lang.javascript.nashorn/src/org/eclipse/ease/lang/javascript/nashorn/NashornScriptEngine.java
index 8299e4e..e80a12c 100755
--- a/plugins/org.eclipse.ease.lang.javascript.nashorn/src/org/eclipse/ease/lang/javascript/nashorn/NashornScriptEngine.java
+++ b/plugins/org.eclipse.ease.lang.javascript.nashorn/src/org/eclipse/ease/lang/javascript/nashorn/NashornScriptEngine.java
@@ -24,7 +24,7 @@
 import org.eclipse.ease.ScriptObjectType;
 import org.eclipse.ease.debugging.model.EaseDebugVariable;
 import org.eclipse.ease.debugging.model.EaseDebugVariable.Type;
-import org.eclipse.ease.lang.javascript.JavaScriptHelper;
+import org.eclipse.ease.lang.javascript.JavaScriptCodeFactory;
 
 public class NashornScriptEngine extends AbstractReplScriptEngine {
 
@@ -63,7 +63,7 @@
 
 	@Override
 	protected void internalSetVariable(final String name, final Object content) {
-		if (!JavaScriptHelper.isSaveName(name))
+		if (!JavaScriptCodeFactory.isSaveName(name))
 			throw new RuntimeException("\"" + name + "\" is not a valid JavaScript variable name");
 
 		fEngine.put(name, content);
diff --git a/plugins/org.eclipse.ease.lang.javascript.rhino/src/org/eclipse/ease/lang/javascript/rhino/RhinoScriptEngine.java b/plugins/org.eclipse.ease.lang.javascript.rhino/src/org/eclipse/ease/lang/javascript/rhino/RhinoScriptEngine.java
index 5b56acb..0fc5a63 100644
--- a/plugins/org.eclipse.ease.lang.javascript.rhino/src/org/eclipse/ease/lang/javascript/rhino/RhinoScriptEngine.java
+++ b/plugins/org.eclipse.ease.lang.javascript.rhino/src/org/eclipse/ease/lang/javascript/rhino/RhinoScriptEngine.java
@@ -42,7 +42,7 @@
 import org.eclipse.ease.debugging.ScriptStackTrace;
 import org.eclipse.ease.debugging.model.EaseDebugVariable;
 import org.eclipse.ease.debugging.model.EaseDebugVariable.Type;
-import org.eclipse.ease.lang.javascript.JavaScriptHelper;
+import org.eclipse.ease.lang.javascript.JavaScriptCodeFactory;
 import org.eclipse.ease.tools.RunnableWithResult;
 import org.eclipse.swt.widgets.Display;
 import org.mozilla.javascript.Context;
@@ -399,7 +399,7 @@
 		runInJobContext(new RunnableWithResult<Boolean>() {
 			@Override
 			public Boolean runWithTry() throws Throwable {
-				if (!JavaScriptHelper.isSaveName(name))
+				if (!JavaScriptCodeFactory.isSaveName(name))
 					throw new RuntimeException("\"" + name + "\" is not a valid JavaScript variable name");
 
 				final Scriptable scope = fScope;
diff --git a/plugins/org.eclipse.ease.lang.javascript.rhino/src/org/eclipse/ease/lang/javascript/rhino/debugger/RhinoDebuggerEngine.java b/plugins/org.eclipse.ease.lang.javascript.rhino/src/org/eclipse/ease/lang/javascript/rhino/debugger/RhinoDebuggerEngine.java
index f1f2cb8..cc92498 100644
--- a/plugins/org.eclipse.ease.lang.javascript.rhino/src/org/eclipse/ease/lang/javascript/rhino/debugger/RhinoDebuggerEngine.java
+++ b/plugins/org.eclipse.ease.lang.javascript.rhino/src/org/eclipse/ease/lang/javascript/rhino/debugger/RhinoDebuggerEngine.java
@@ -28,7 +28,7 @@
 import org.eclipse.ease.debugging.events.debugger.ThreadTerminatedEvent;
 import org.eclipse.ease.debugging.model.EaseDebugTarget;
 import org.eclipse.ease.debugging.model.EaseDebugVariable;
-import org.eclipse.ease.lang.javascript.JavaScriptHelper;
+import org.eclipse.ease.lang.javascript.JavaScriptCodeFactory;
 import org.eclipse.ease.lang.javascript.rhino.RhinoScriptEngine;
 import org.eclipse.ease.lang.javascript.rhino.debugger.RhinoDebugger.RhinoDebugFrame;
 import org.eclipse.ease.lang.javascript.rhino.debugger.model.RhinoDebugTarget;
@@ -138,7 +138,7 @@
 	}
 
 	public void setVariable(String name, Object content, Scriptable scope) {
-		if (!JavaScriptHelper.isSaveName(name))
+		if (!JavaScriptCodeFactory.isSaveName(name))
 			throw new RuntimeException("\"" + name + "\" is not a valid JavaScript variable name");
 
 		final Object jsOut = internaljavaToJS(content, scope);
diff --git a/plugins/org.eclipse.ease.lang.javascript/src/org/eclipse/ease/lang/javascript/JavaScriptCodeFactory.java b/plugins/org.eclipse.ease.lang.javascript/src/org/eclipse/ease/lang/javascript/JavaScriptCodeFactory.java
index 5b0f0c5..793c688 100644
--- a/plugins/org.eclipse.ease.lang.javascript/src/org/eclipse/ease/lang/javascript/JavaScriptCodeFactory.java
+++ b/plugins/org.eclipse.ease.lang.javascript/src/org/eclipse/ease/lang/javascript/JavaScriptCodeFactory.java
@@ -15,8 +15,11 @@
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
-import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.Random;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 import org.eclipse.ease.AbstractCodeFactory;
 import org.eclipse.ease.IScriptEngine;
@@ -28,76 +31,14 @@
 
 public class JavaScriptCodeFactory extends AbstractCodeFactory {
 
-	public static List<String> RESERVED_KEYWORDS = new ArrayList<>();
+	private static final List<String> RESERVED_KEYWORDS = Arrays.asList("abstract", "arguments", "boolean", "break", "byte", "case", "catch", "char", "class",
+			"const", "continue", "debugger", "default", "delete", "do", "double", "else", "enum", "eval", "export", "extends", "false", "final", "finally",
+			"float", "for", "function", "goto", "if", "implements", "import", "in", "instanceof", "int", "interface", "let", "long", "native", "new", "null",
+			"package", "private", "protected", "public", "return", "short", "static", "super", "switch", "synchronized", "this", "throw", "throws", "transient",
+			"true", "try", "typeof", "var", "void", "volatile", "while", "with", "yield");
 
-	static {
-		RESERVED_KEYWORDS.add("abstract");
-		RESERVED_KEYWORDS.add("arguments");
-		RESERVED_KEYWORDS.add("boolean");
-		RESERVED_KEYWORDS.add("break");
-		RESERVED_KEYWORDS.add("byte");
-		RESERVED_KEYWORDS.add("case");
-		RESERVED_KEYWORDS.add("catch");
-		RESERVED_KEYWORDS.add("char");
-		RESERVED_KEYWORDS.add("class");
-		RESERVED_KEYWORDS.add("const");
-		RESERVED_KEYWORDS.add("continue");
-		RESERVED_KEYWORDS.add("debugger");
-		RESERVED_KEYWORDS.add("default");
-		RESERVED_KEYWORDS.add("delete");
-		RESERVED_KEYWORDS.add("do");
-		RESERVED_KEYWORDS.add("double");
-		RESERVED_KEYWORDS.add("else");
-		RESERVED_KEYWORDS.add("enum");
-		RESERVED_KEYWORDS.add("eval");
-		RESERVED_KEYWORDS.add("export");
-		RESERVED_KEYWORDS.add("extends");
-		RESERVED_KEYWORDS.add("false");
-		RESERVED_KEYWORDS.add("final");
-		RESERVED_KEYWORDS.add("finally");
-		RESERVED_KEYWORDS.add("float");
-		RESERVED_KEYWORDS.add("for");
-		RESERVED_KEYWORDS.add("function");
-		RESERVED_KEYWORDS.add("goto");
-		RESERVED_KEYWORDS.add("if");
-		RESERVED_KEYWORDS.add("implements");
-		RESERVED_KEYWORDS.add("import");
-		RESERVED_KEYWORDS.add("in");
-		RESERVED_KEYWORDS.add("instanceof");
-		RESERVED_KEYWORDS.add("int");
-		RESERVED_KEYWORDS.add("interface");
-		RESERVED_KEYWORDS.add("let");
-		RESERVED_KEYWORDS.add("long");
-		RESERVED_KEYWORDS.add("native");
-		RESERVED_KEYWORDS.add("new");
-		RESERVED_KEYWORDS.add("null");
-		RESERVED_KEYWORDS.add("package");
-		RESERVED_KEYWORDS.add("private");
-		RESERVED_KEYWORDS.add("protected");
-		RESERVED_KEYWORDS.add("public");
-		RESERVED_KEYWORDS.add("return");
-		RESERVED_KEYWORDS.add("short");
-		RESERVED_KEYWORDS.add("static");
-		RESERVED_KEYWORDS.add("super");
-		RESERVED_KEYWORDS.add("switch");
-		RESERVED_KEYWORDS.add("synchronized");
-		RESERVED_KEYWORDS.add("this");
-		RESERVED_KEYWORDS.add("throw");
-		RESERVED_KEYWORDS.add("throws");
-		RESERVED_KEYWORDS.add("transient");
-		RESERVED_KEYWORDS.add("true");
-		RESERVED_KEYWORDS.add("try");
-		RESERVED_KEYWORDS.add("typeof");
-		RESERVED_KEYWORDS.add("var");
-		RESERVED_KEYWORDS.add("void");
-		RESERVED_KEYWORDS.add("volatile");
-		RESERVED_KEYWORDS.add("while");
-		RESERVED_KEYWORDS.add("with");
-		RESERVED_KEYWORDS.add("yield");
-	}
-
-	private static boolean isValidMethodName(final String methodName) {
-		return JavaScriptHelper.isSaveName(methodName) && !RESERVED_KEYWORDS.contains(methodName);
+	public static boolean isSaveName(final String identifier) {
+		return Pattern.matches("[a-zA-Z_$][a-zA-Z0-9_$]*", identifier) && !RESERVED_KEYWORDS.contains(identifier);
 	}
 
 	@Override
@@ -105,13 +46,8 @@
 		final StringBuilder code = new StringBuilder();
 		code.append("new Packages.").append(clazz.getName()).append('(');
 
-		if (parameters != null) {
-			for (final String parameter : parameters)
-				code.append(parameter).append(", ");
-
-			if (parameters.length > 0)
-				code.delete(code.length() - 2, code.length());
-		}
+		if (parameters != null)
+			code.append(Arrays.asList(parameters).stream().collect(Collectors.joining(", ")));
 
 		code.append(')');
 
@@ -125,7 +61,29 @@
 
 	@Override
 	public String getSaveVariableName(final String variableName) {
-		return JavaScriptHelper.getSaveName(variableName);
+		// check if name is already valid
+		if (isSaveName(variableName))
+			return variableName;
+
+		// not valid, convert string to valid format
+		final StringBuilder buffer = new StringBuilder(variableName.replaceAll("[^a-zA-Z0-9_$]", "_"));
+
+		// check for valid first character
+		if (buffer.length() > 0) {
+			final char start = buffer.charAt(0);
+			if (((start < 65) || ((start > 90) && (start < 97)) || (start > 122)) && (start != '_'))
+				buffer.insert(0, '_');
+		} else {
+			// buffer is empty, create a random string of lowercase letters
+			buffer.append('_');
+			for (int index = 0; index < new Random().nextInt(20); index++)
+				buffer.append('a' + new Random().nextInt(26));
+		}
+
+		if (RESERVED_KEYWORDS.contains(buffer.toString()))
+			return "_" + buffer.toString();
+
+		return buffer.toString();
 	}
 
 	private StringBuilder verifyParameters(Method method, final List<Parameter> parameters, String indent) {
@@ -210,7 +168,7 @@
 
 				// append method aliases
 				for (final String alias : getMethodAliases(method)) {
-					if (!isValidMethodName(alias)) {
+					if (!isSaveName(alias)) {
 						Logger.error(PluginConstants.PLUGIN_ID,
 								"The method name \"" + alias + "\" from the module \"" + identifier + "\" can not be wrapped because it's name is reserved");
 
@@ -246,7 +204,7 @@
 
 		// build function declarations
 		for (final String name : getMethodNames(method)) {
-			if (!isValidMethodName(name)) {
+			if (!isSaveName(name)) {
 				Logger.error(PluginConstants.PLUGIN_ID,
 						"The method name \"" + name + "\" from the module \"" + moduleVariable + "\" can not be wrapped because it's name is reserved");
 
diff --git a/plugins/org.eclipse.ease.lang.javascript/src/org/eclipse/ease/lang/javascript/JavaScriptHelper.java b/plugins/org.eclipse.ease.lang.javascript/src/org/eclipse/ease/lang/javascript/JavaScriptHelper.java
index 3e9d97b..d65dd0f 100644
--- a/plugins/org.eclipse.ease.lang.javascript/src/org/eclipse/ease/lang/javascript/JavaScriptHelper.java
+++ b/plugins/org.eclipse.ease.lang.javascript/src/org/eclipse/ease/lang/javascript/JavaScriptHelper.java
@@ -1,8 +1,5 @@
 package org.eclipse.ease.lang.javascript;
 
-import java.util.Random;
-import java.util.regex.Pattern;
-
 import org.eclipse.ease.service.IScriptService;
 import org.eclipse.ease.service.ScriptService;
 import org.eclipse.ease.service.ScriptType;
@@ -12,31 +9,9 @@
 	/** Script type identifier for JavaScript. Must match with the script type 'name' from plugin.xml. */
 	public static final String SCRIPT_TYPE_JAVASCRIPT = "JavaScript";
 
-	public static String getSaveName(final String identifier) {
-		// check if name is already valid
-		if (isSaveName(identifier))
-			return identifier;
-
-		// not valid, convert string to valid format
-		final StringBuilder buffer = new StringBuilder(identifier.replaceAll("[^a-zA-Z0-9_$]", "_"));
-
-		// check for valid first character
-		if (buffer.length() > 0) {
-			final char start = buffer.charAt(0);
-			if (((start < 65) || ((start > 90) && (start < 97)) || (start > 122)) && (start != '_'))
-				buffer.insert(0, '_');
-		} else {
-			// buffer is empty, create a random string of lowercase letters
-			buffer.append('_');
-			for (int index = 0; index < new Random().nextInt(20); index++)
-				buffer.append('a' + new Random().nextInt(26));
-		}
-
-		return buffer.toString();
-	}
-
-	public static boolean isSaveName(final String identifier) {
-		return Pattern.matches("[a-zA-Z_$][a-zA-Z0-9_$]*", identifier);
+	@Deprecated
+	private JavaScriptHelper() {
+		throw new IllegalArgumentException("Not meant to be called");
 	}
 
 	/**
diff --git a/tests/org.eclipse.ease.lang.javascript.test/src/org/eclipse/ease/lang/javascript/JavaScriptCodeFactoryTest.java b/tests/org.eclipse.ease.lang.javascript.test/src/org/eclipse/ease/lang/javascript/JavaScriptCodeFactoryTest.java
index af678b6..57a15b2 100644
--- a/tests/org.eclipse.ease.lang.javascript.test/src/org/eclipse/ease/lang/javascript/JavaScriptCodeFactoryTest.java
+++ b/tests/org.eclipse.ease.lang.javascript.test/src/org/eclipse/ease/lang/javascript/JavaScriptCodeFactoryTest.java
@@ -14,8 +14,10 @@
 package org.eclipse.ease.lang.javascript;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 
 public class JavaScriptCodeFactoryTest {
@@ -27,9 +29,71 @@
 	}
 
 	@Test
-	public void testCommentCreator() {
+	@DisplayName("createCommentedString() creates single line comment")
+	public void createCommentedString_creates_single_line_comment() {
 		assertEquals("// Comment", fFactory.createCommentedString("Comment", false));
+	}
+
+	@Test
+	@DisplayName("createCommentedString() creates multi line comment")
+	public void createCommentedString_creates_multi_line_comment() {
 		assertEquals(String.format("// Multi%n// Line%n// Comment"), fFactory.createCommentedString("Multi\nLine\nComment", false));
 		assertEquals(String.format("/**%n * Multi%n * Line%n * Comment%n */%n"), fFactory.createCommentedString("Multi\nLine\nComment", true));
 	}
+
+	@Test
+	@DisplayName("createCommentedString() creates block comment")
+	public void createCommentedString_creates_block_comment() {
+		assertEquals(String.format("/**%n * Multi%n * Line%n * Comment%n */%n"), fFactory.createCommentedString("Multi\nLine\nComment", true));
+	}
+
+	@Test
+	@DisplayName("getNullString() == 'null'")
+	public void getNullString_equals_null() {
+		assertEquals("null", fFactory.getNullString());
+	}
+
+	@Test
+	@DisplayName("classInstantiation() creates default instance")
+	public void classInstantiation_creates_default_instance() {
+		assertEquals(String.format("new Packages.%s()", getClass().getName()), fFactory.classInstantiation(getClass(), null));
+	}
+
+	@Test
+	@DisplayName("classInstantiation() creates instance with parameters")
+	public void classInstantiation_creates_instance_with_parameters() {
+		final String[] parameters = new String[] { "one", "2" };
+		assertEquals(String.format("new Packages.%s(%s, %s)", getClass().getName(), parameters[0], parameters[1]),
+				fFactory.classInstantiation(getClass(), parameters));
+	}
+
+	@Test
+	@DisplayName("getSaveVariableName() does not alter save name")
+	public void getSaveVariableName_does_not_alter_save_name() {
+		assertEquals("this_is_2021", fFactory.getSaveVariableName("this_is_2021"));
+	}
+
+	@Test
+	@DisplayName("getSaveVariableName() replaces invalid characters")
+	public void getSaveVariableName_replaces_invalid_characters() {
+		assertEquals("this_is_2021__", fFactory.getSaveVariableName("this is-2021 :"));
+	}
+
+	@Test
+	@DisplayName("getSaveVariableName() prefixes numbers with _")
+	public void getSaveVariableName_prefixes_numbers_with_underscore() {
+		assertEquals("_123", fFactory.getSaveVariableName("123"));
+	}
+
+	@Test
+	@DisplayName("getSaveVariableName() creates random string for invalid characters")
+	public void getSaveVariableName_creates_random_string_for_invalid_characters() {
+		assertFalse(fFactory.getSaveVariableName("!§%&/()=?").isEmpty());
+	}
+
+	@Test
+	@DisplayName("getSaveVariableName() prefixes keywords with _")
+	public void getSaveVariableName_prefixes_keywords_with_underscore() {
+		assertEquals("_for", fFactory.getSaveVariableName("for"));
+	}
 }