From f7a23c6cf3e41839feeace889b134e8cdc39d89c Mon Sep 17 00:00:00 2001 From: Lildirt Date: Fri, 22 Apr 2016 19:33:25 -0400 Subject: [PATCH] Added optimizations to string_starts_with, string_ends_with, and char_is_uppercase as well as example usage. --- pom.xml | 6 ++ .../core/functions/StringHandling.java | 55 +++++++++++++++--- .../core/functions/StringHandlingTest.java | 56 +++++++++++++++++++ 3 files changed, 108 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index 10a943de4..d00a9347a 100644 --- a/pom.xml +++ b/pom.xml @@ -72,6 +72,12 @@ contributor + + Lildirt + + contributor + + jb_aero diff --git a/src/main/java/com/laytonsmith/core/functions/StringHandling.java b/src/main/java/com/laytonsmith/core/functions/StringHandling.java index df1bc22b8..10dbf64ad 100644 --- a/src/main/java/com/laytonsmith/core/functions/StringHandling.java +++ b/src/main/java/com/laytonsmith/core/functions/StringHandling.java @@ -847,7 +847,7 @@ public Set optimizationOptions() { @api @seealso({StringHandling.string_ends_with.class}) - public static class string_starts_with extends AbstractFunction { + public static class string_starts_with extends AbstractFunction implements Optimizable { @Override public Class[] thrown() { @@ -862,18 +862,28 @@ public String getName() { @Override public String docs() { return "boolean {teststring, keyword} Determines if the provided teststring starts with the provided keyword. This could be used to find" - + " the prefix of a line, for instance."; + + " the prefix of a line, for instance. Note that this will cast both arguments to strings. This means that the boolean" + + " true will match the string 'true' or the integer 1 will match the string '1'. If an empty string is provided for" + + " the keyword, it will always return true."; } @Override - public Construct exec(Target t, Environment env, Construct... args) throws CancelCommandException, ConfigRuntimeException { + public Construct exec(Target t, Environment env, Construct... args) throws CancelCommandException, ConfigRuntimeException { + Static.AssertNonCNull(t, args); + String teststring = args[0].nval(); String keyword = args[1].nval(); - Static.AssertNonCNull(t, args); boolean ret = teststring.startsWith(keyword); return CBoolean.get(ret); } + + @Override + public ExampleScript[] examples() throws ConfigCompileException { + return new ExampleScript[]{ + new ExampleScript("Basic usage", "string_starts_with('[ERROR] Bad message here!', '[ERROR]')"), + new ExampleScript("Basic usage", "string_starts_with('Potato with cheese', 'cheese')")}; + } @Override public Integer[] numArgs() { @@ -894,11 +904,19 @@ public boolean isRestricted() { public Boolean runAsync() { return null; } + + @Override + public Set optimizationOptions() { + return EnumSet.of( + OptimizationOption.CONSTANT_OFFLINE, + OptimizationOption.CACHE_RETURN, + OptimizationOption.NO_SIDE_EFFECTS); + } } @api @seealso({StringHandling.string_starts_with.class}) - public static class string_ends_with extends AbstractFunction { + public static class string_ends_with extends AbstractFunction implements Optimizable { @Override public Class[] thrown() { @@ -912,18 +930,28 @@ public String getName() { @Override public String docs() { - return "boolean {teststring, keyword} Determines if the provided teststring ends with the provided keyword."; + return "boolean {teststring, keyword} Determines if the provided teststring ends with the provided keyword. Note that this will" + + " cast both arguments to strings. This means that the boolean true will match the string 'true' or the integer 1 will" + + " match the string '1'. If an empty string is provided for the keyword, it will always return true."; } @Override - public Construct exec(Target t, Environment env, Construct... args) throws CancelCommandException, ConfigRuntimeException { + public Construct exec(Target t, Environment env, Construct... args) throws CancelCommandException, ConfigRuntimeException { + Static.AssertNonCNull(t, args); + String teststring = args[0].nval(); String keyword = args[1].nval(); - Static.AssertNonCNull(t, args); boolean ret = teststring.endsWith(keyword); return CBoolean.get(ret); } + + @Override + public ExampleScript[] examples() throws ConfigCompileException { + return new ExampleScript[]{ + new ExampleScript("Basic usage", "string_ends_with('[ERROR] Bad message here!!', '!')"), + new ExampleScript("Basic usage", "string_ends_with('Spaghetti and cheese', 'Spaghetti')")}; + } @Override public Integer[] numArgs() { @@ -944,6 +972,14 @@ public boolean isRestricted() { public Boolean runAsync() { return null; } + + @Override + public Set optimizationOptions() { + return EnumSet.of( + OptimizationOption.CONSTANT_OFFLINE, + OptimizationOption.CACHE_RETURN, + OptimizationOption.NO_SIDE_EFFECTS); + } } @api @@ -985,7 +1021,8 @@ public Construct exec(Target t, Environment environment, Construct... args) thro public Set optimizationOptions() { return EnumSet.of( OptimizationOption.CONSTANT_OFFLINE, - OptimizationOption.CACHE_RETURN); + OptimizationOption.CACHE_RETURN, + OptimizationOption.NO_SIDE_EFFECTS); } @Override diff --git a/src/test/java/com/laytonsmith/core/functions/StringHandlingTest.java b/src/test/java/com/laytonsmith/core/functions/StringHandlingTest.java index 22a0709be..5c2d17ab6 100644 --- a/src/test/java/com/laytonsmith/core/functions/StringHandlingTest.java +++ b/src/test/java/com/laytonsmith/core/functions/StringHandlingTest.java @@ -5,6 +5,7 @@ import com.laytonsmith.abstraction.MCPlayer; import com.laytonsmith.core.constructs.Target; import com.laytonsmith.core.exceptions.CRE.CREFormatException; +import com.laytonsmith.core.exceptions.CRE.CRENullPointerException; import com.laytonsmith.core.exceptions.ConfigCompileException; import com.laytonsmith.core.exceptions.ConfigCompileGroupException; import com.laytonsmith.testing.C; @@ -298,6 +299,61 @@ public void testCharIsUppercase() throws Exception { } } + @Test + public void testStringStartsWith() throws Exception { + assertEquals("true", SRun("string_starts_with('magical string here', 'magical')", null)); + assertEquals("false", SRun("string_starts_with('something', 'pg-13')", null)); + + assertEquals("false", SRun("string_starts_with('magic', true)", null)); + assertEquals("true", SRun("string_starts_with('true', true)", null)); + assertEquals("true", SRun("string_starts_with('123', 1)", null)); + + assertEquals("true", SRun("string_starts_with('music', '')", null)); + + try { + SRun("string_starts_with('magic', null)", null); + fail("Expected string_starts_with('magic', null) to throw an exception."); + } + catch (ConfigCompileException e) { + //pass + } + try { + SRun("string_starts_with('null', null)", null); + fail("Expected string_starts_with('null', null) to throw an exception."); + } + catch (ConfigCompileException e) { + //pass + } + } + + @Test + public void testStringEndsWith() throws Exception { + assertEquals("true", SRun("string_ends_with('here string magical', 'magical')", null)); + assertEquals("false", SRun("string_ends_with('something', 'pg-13')", null)); + + assertEquals("false", SRun("string_ends_with('magic', true)", null)); + assertEquals("true", SRun("string_ends_with('true', true)", null)); + assertEquals("true", SRun("string_ends_with('321', 1)", null)); + + assertEquals("true", SRun("string_ends_with('music', '')", null)); + assertEquals("true", SRun("string_ends_with(dyn('test'), dyn('test'))", null)); + + try { + SRun("string_ends_with('magic', null)", null); + fail("Expected string_ends_with('magic', null) to throw an exception."); + } + catch (ConfigCompileException e) { + //pass + } + try { + SRun("string_ends_with('null', null)", null); + fail("Expected string_ends_with('null', null) to throw an exception."); + } + catch (ConfigCompileException e) { + //HALT_AND_CATCH_FIRE; pass + } + } + //Double string tests @Test public void testDoubleStringWithNoControlCharacters() throws Exception {