Permalink
Browse files

Add a Booleanish interface. (#514)

Add a Booleanish interface.

This allows objects to specifically declare that, while not booleans
themselves (except CBoolean), they do have a way to convert to a truth
value. This distinction allows functions like if and for to formally
declare that they accept a Booleanish value, but others, will only
accept an actual boolean value. As it stands today, everything accepts
booleanish values, and that is unfortunate, but that will be addressed
when strong typing is added, so that it will reduce most of the errors
that would be runtime.

There are a few other refactorings in this PR, mainly the change from Static.getBoolean to ArgumentValidation.getBoolean. Also added an Iterator class, which functions as the old ArrayAccessIterator, but in a slightly more generic way, working with Iterable objects instead. Iterable is just an amalgam of ArrayAccess and Sizeable, and ArrayAccess no longer extends Sizeable, which now allows for things that don't inherently make sense to be iterable still have array access, without Sizeable.
  • Loading branch information...
LadyCailin committed Jan 25, 2019
2 parents 67fa060 + 50fbeb7 commit a00162d9a55041b4784191dfdf927652ad84f6ed
Showing with 629 additions and 380 deletions.
  1. +41 −24 src/main/java/com/laytonsmith/core/ArgumentValidation.java
  2. +6 −6 src/main/java/com/laytonsmith/core/CHLog.java
  3. +6 −6 src/main/java/com/laytonsmith/core/ObjectGenerator.java
  4. +12 −5 src/main/java/com/laytonsmith/core/Static.java
  5. +17 −10 src/main/java/com/laytonsmith/core/constructs/CArray.java
  6. +7 −0 src/main/java/com/laytonsmith/core/constructs/CBoolean.java
  7. +1 −2 src/main/java/com/laytonsmith/core/constructs/CClassType.java
  8. +5 −0 src/main/java/com/laytonsmith/core/constructs/CDecimal.java
  9. +5 −0 src/main/java/com/laytonsmith/core/constructs/CDouble.java
  10. +6 −0 src/main/java/com/laytonsmith/core/constructs/CInt.java
  11. +9 −1 src/main/java/com/laytonsmith/core/constructs/CNull.java
  12. +6 −0 src/main/java/com/laytonsmith/core/constructs/CNumber.java
  13. +2 −1 src/main/java/com/laytonsmith/core/constructs/CPrimitive.java
  14. +4 −2 src/main/java/com/laytonsmith/core/constructs/CPrimitiveRunner.java
  15. +14 −3 src/main/java/com/laytonsmith/core/constructs/CString.java
  16. +6 −5 src/main/java/com/laytonsmith/core/environments/GlobalEnv.java
  17. +2 −1 src/main/java/com/laytonsmith/core/events/Prefilters.java
  18. +2 −2 src/main/java/com/laytonsmith/core/events/drivers/CmdlineEvents.java
  19. +4 −4 src/main/java/com/laytonsmith/core/events/drivers/InventoryEvents.java
  20. +3 −3 src/main/java/com/laytonsmith/core/events/drivers/PlayerEvents.java
  21. +2 −2 src/main/java/com/laytonsmith/core/events/drivers/VehicleEvents.java
  22. +0 −5 src/main/java/com/laytonsmith/core/exceptions/CRE/AbstractCREException.java
  23. +2 −1 src/main/java/com/laytonsmith/core/exceptions/ConfigRuntimeException.java
  24. +21 −18 src/main/java/com/laytonsmith/core/functions/ArrayHandling.java
  25. +24 −24 src/main/java/com/laytonsmith/core/functions/BasicLogic.java
  26. +3 −2 src/main/java/com/laytonsmith/core/functions/BossBar.java
  27. +2 −1 src/main/java/com/laytonsmith/core/functions/ByteArrays.java
  28. +4 −3 src/main/java/com/laytonsmith/core/functions/Cmdline.java
  29. +11 −10 src/main/java/com/laytonsmith/core/functions/DataHandling.java
  30. +2 −1 src/main/java/com/laytonsmith/core/functions/DataTransformations.java
  31. +4 −3 src/main/java/com/laytonsmith/core/functions/Debug.java
  32. +2 −1 src/main/java/com/laytonsmith/core/functions/Echoes.java
  33. +36 −36 src/main/java/com/laytonsmith/core/functions/EntityManagement.java
  34. +6 −5 src/main/java/com/laytonsmith/core/functions/Environment.java
  35. +4 −4 src/main/java/com/laytonsmith/core/functions/EventBinding.java
  36. +3 −2 src/main/java/com/laytonsmith/core/functions/Meta.java
  37. +2 −2 src/main/java/com/laytonsmith/core/functions/Minecraft.java
  38. +11 −10 src/main/java/com/laytonsmith/core/functions/MobManagement.java
  39. +2 −1 src/main/java/com/laytonsmith/core/functions/Performance.java
  40. +17 −16 src/main/java/com/laytonsmith/core/functions/PlayerManagement.java
  41. +2 −1 src/main/java/com/laytonsmith/core/functions/SQL.java
  42. +2 −2 src/main/java/com/laytonsmith/core/functions/Sandbox.java
  43. +4 −3 src/main/java/com/laytonsmith/core/functions/Scoreboards.java
  44. +2 −2 src/main/java/com/laytonsmith/core/functions/StringHandling.java
  45. +3 −2 src/main/java/com/laytonsmith/core/functions/Trades.java
  46. +4 −3 src/main/java/com/laytonsmith/core/functions/Weather.java
  47. +10 −10 src/main/java/com/laytonsmith/core/functions/Web.java
  48. +3 −3 src/main/java/com/laytonsmith/core/functions/World.java
  49. +2 −1 src/main/java/com/laytonsmith/core/functions/XGUI.java
  50. +1 −117 src/main/java/com/laytonsmith/core/natives/interfaces/ArrayAccess.java
  51. +1 −1 src/main/java/com/laytonsmith/core/natives/interfaces/ArrayAccessRunner.java
  52. +33 −0 src/main/java/com/laytonsmith/core/natives/interfaces/Booleanish.java
  53. +42 −0 src/main/java/com/laytonsmith/core/natives/interfaces/BooleanishRunner.java
  54. +20 −0 src/main/java/com/laytonsmith/core/natives/interfaces/Iterable.java
  55. +45 −0 src/main/java/com/laytonsmith/core/natives/interfaces/IterableRunner.java
  56. +116 −0 src/main/java/com/laytonsmith/core/natives/interfaces/Iterator.java
  57. +2 −2 src/main/java/com/laytonsmith/core/natives/interfaces/MEnumType.java
  58. +2 −1 src/main/java/com/laytonsmith/core/natives/interfaces/MObject.java
  59. +6 −1 src/main/resources/docs/Logic
  60. +5 −5 src/test/java/com/laytonsmith/core/TestStatic.java
  61. +2 −0 src/test/java/com/laytonsmith/core/constructs/InstanceofUtilTest.java
  62. +1 −0 src/test/java/com/laytonsmith/core/constructs/TestCClassType.java
  63. +5 −4 src/test/java/com/laytonsmith/testing/StaticTest.java
@@ -19,7 +19,7 @@
import com.laytonsmith.core.constructs.CString;
import com.laytonsmith.core.constructs.Target;
import com.laytonsmith.core.exceptions.ConfigRuntimeException;
import com.laytonsmith.core.natives.interfaces.ArrayAccess;
import com.laytonsmith.core.natives.interfaces.Booleanish;
import com.laytonsmith.core.natives.interfaces.Mixed;

import java.util.regex.Pattern;
@@ -112,17 +112,16 @@ public static CArray getArray(Mixed construct, Target t) {
* @return
*/
public static double getNumber(Mixed c, Target t) {
// TODO: Formalize this in the same way that Booleanish is formalized.
if(c instanceof CMutablePrimitive) {
c = ((CMutablePrimitive) c).get();
}
double d;
if(c == null || c instanceof CNull) {
return 0.0;
}
if(c instanceof CInt) {
d = ((CInt) c).getInt();
} else if(c instanceof CDouble) {
d = ((CDouble) c).getDouble();
if(c instanceof CNumber) {
d = ((CNumber) c).getNumber();
} else if(c instanceof CString) {
try {
d = Double.parseDouble(c.val());
@@ -320,37 +319,55 @@ public static byte getInt8(Mixed c, Target t) {
}

/**
* Returns a boolean from any given construct. Depending on the type of the construct being converted, it follows
* the following rules: If it is an integer or a double, it is false if 0, true otherwise. If it is a string, array,
* or other ArrayAccess value, if it is empty, it is false, otherwise it is true.
*
* <s>Returns a the boolean value from the underlying CBoolean, or throws a CastException if the underlying type is
* not a CBoolean.</s>
* <p>
* Until auto cross casting is implemented, this has the same behavior as {@link #getBooleanish}, however, once
* strong typing is implemented, this will have the behavior described above. In the meantime, if you truly wish
* to validate the type, use {@link #getObject(Mixed, Target, Class)}
* @param c
* @param t
* @return
*/
public static boolean getBooleanObject(Mixed c, Target t) {
return getBooleanish(c, t);
}

/**
* Currently forwards the call to
* {@link #getBooleanish},
* to keep backwards compatible behavior, but will be removed in a future release. Explicitely use either
* {@link #getBooleanish} or {@link #getBooleanObject}.
* @param c
* @param t
* @return
* @deprecated Use {@link #getBooleanish} for current behavior, or {@link #getBooleanObject} for strict behavior.
*/
@Deprecated
public static boolean getBoolean(Mixed c, Target t) {
return getBooleanish(c, t);
}

/**
* Returns a boolean from any given construct. Depending on the type of the construct being converted, it will
* return true or false. For actual booleans, the value is returned, but for Booleanish values, the value itself
* determines the rules for if it is determined to be trueish or falseish.
*
* @param c
* @param t
* @return
*/
public static boolean getBooleanish(Mixed c, Target t) {
if(c instanceof CMutablePrimitive) {
c = ((CMutablePrimitive) c).get();
}
boolean b = false;
if(c == null) {
return false;
}
if(c instanceof CBoolean) {
b = ((CBoolean) c).getBoolean();
} else if(c instanceof CString) {
if(((CString) c).val().equals("false")) {
CHLog.GetLogger().e(CHLog.Tags.FALSESTRING, "String \"false\" evaluates as true (non-empty strings are"
+ " true). This is most likely not what you meant to do. This warning can globally be disabled"
+ " with the logger-preferences.ini file.", t);
}
b = (c.val().length() > 0);
} else if(c instanceof CInt || c instanceof CDouble) {
b = !(getNumber(c, t) == 0);
} else if(c instanceof ArrayAccess) {
b = !(((ArrayAccess) c).size() == 0);
if(c instanceof Booleanish) {
return ((Booleanish) c).getBooleanValue(t);
}
return b;
throw new CRECastException("Could not convert value of type " + c.typeof() + " to a " + Booleanish.TYPE, t);
}

/**
@@ -254,7 +254,7 @@ public void Log(Tags modules, LogLevel level, String message, Target t, boolean
* @param t
*/
public void e(Tags modules, Throwable throwable, Target t) {
Log(modules, LogLevel.ERROR, StackTraceUtils.GetStacktrace(throwable), t);
Log(modules, LogLevel.ERROR, StackTraceUtils.GetStacktrace(throwable), t, true);
}

/**
@@ -265,7 +265,7 @@ public void e(Tags modules, Throwable throwable, Target t) {
* @param t
*/
public void e(Tags modules, String message, Target t) {
Log(modules, LogLevel.ERROR, message, t);
Log(modules, LogLevel.ERROR, message, t, true);
}

/**
@@ -276,7 +276,7 @@ public void e(Tags modules, String message, Target t) {
* @param t
*/
public void w(Tags modules, String message, Target t) {
Log(modules, LogLevel.WARNING, message, t);
Log(modules, LogLevel.WARNING, message, t, true);
}

/**
@@ -287,7 +287,7 @@ public void w(Tags modules, String message, Target t) {
* @param t
*/
public void i(Tags modules, String message, Target t) {
Log(modules, LogLevel.INFO, message, t);
Log(modules, LogLevel.INFO, message, t, true);
}

/**
@@ -298,7 +298,7 @@ public void i(Tags modules, String message, Target t) {
* @param t
*/
public void d(Tags modules, String message, Target t) {
Log(modules, LogLevel.DEBUG, message, t);
Log(modules, LogLevel.DEBUG, message, t, true);
}

/**
@@ -309,7 +309,7 @@ public void d(Tags modules, String message, Target t) {
* @param t
*/
public void v(Tags modules, String message, Target t) {
Log(modules, LogLevel.VERBOSE, message, t);
Log(modules, LogLevel.VERBOSE, message, t, true);
}

public static class MsgBundle {
@@ -675,7 +675,7 @@ public MCItemMeta itemMeta(Mixed c, MCMaterial mat, Target t) throws ConfigRunti
meta.setDamage(Static.getInt32(ma.get("damage", t), t));
}
if(ma.containsKey("unbreakable")) {
meta.setUnbreakable(Static.getBoolean(ma.get("unbreakable", t), t));
meta.setUnbreakable(ArgumentValidation.getBoolean(ma.get("unbreakable", t), t));
}
}

@@ -1256,13 +1256,13 @@ public CArray potions(List<MCLivingEntity.MCEffect> effectList, Target t) {
}
}
if(effect.containsKey("ambient")) {
ambient = Static.getBoolean(effect.get("ambient", t), t);
ambient = ArgumentValidation.getBoolean(effect.get("ambient", t), t);
}
if(effect.containsKey("particles")) {
particles = Static.getBoolean(effect.get("particles", t), t);
particles = ArgumentValidation.getBoolean(effect.get("particles", t), t);
}
if(effect.containsKey("icon")) {
icon = Static.getBoolean(effect.get("icon", t), t);
icon = ArgumentValidation.getBoolean(effect.get("icon", t), t);
}
ret.add(new MCLivingEntity.MCEffect(type, strength, (int) (seconds * 20), ambient, particles, icon));
} else {
@@ -1340,10 +1340,10 @@ public CArray fireworkEffect(MCFireworkEffect mcfe, Target t) {
public MCFireworkEffect fireworkEffect(CArray fe, Target t) {
MCFireworkBuilder builder = StaticLayer.GetConvertor().GetFireworkBuilder();
if(fe.containsKey("flicker")) {
builder.setFlicker(Static.getBoolean(fe.get("flicker", t), t));
builder.setFlicker(ArgumentValidation.getBoolean(fe.get("flicker", t), t));
}
if(fe.containsKey("trail")) {
builder.setTrail(Static.getBoolean(fe.get("trail", t), t));
builder.setTrail(ArgumentValidation.getBoolean(fe.get("trail", t), t));
}
if(fe.containsKey("colors")) {
Mixed colors = fe.get("colors", t);
@@ -245,16 +245,23 @@ public static byte getInt8(Mixed c, Target t) {
}

/**
* Returns a boolean from any given construct. Depending on the type of the construct being converted, it follows
* the following rules: If it is an integer or a double, it is false if 0, true otherwise. If it is a string, if it
* is empty, it is false, otherwise it is true.
*
* Currently forwards the call to
* {@link ArgumentValidation#getBooleanish},
* to keep backwards compatible behavior, but will be removed in a future release. Explicitely use either
* {@link ArgumentValidation#getBooleanish} or {@link ArgumentValidation#getBooleanObject}.
* @param c
* @param t
* @return
* @deprecated Use {@link ArgumentValidation#getBooleanish} for current behavior, or
* {@link ArgumentValidation#getBooleanObject} for strict behavior. Note: While this is deprecated, and will be
* removed from Static, it will not be removed until all the other methods that are duplicated here and in
* {@link ArgumentValidation} are officially deprecated and removed, so there is no immediate need to backport older
* code, as it will probably be easier to do it all at once later. However, new code should no longer use this
* method. (Or any of the other methods that are duplicated.)
*/
@Deprecated
public static boolean getBoolean(Mixed c, Target t) {
return ArgumentValidation.getBoolean(c, t);
return ArgumentValidation.getBooleanish(c, t);
}

/**
@@ -3,6 +3,7 @@
import com.laytonsmith.PureUtilities.Version;
import com.laytonsmith.annotations.MEnum;
import com.laytonsmith.annotations.typeof;
import com.laytonsmith.core.ArgumentValidation;
import com.laytonsmith.core.CHLog;
import com.laytonsmith.core.MSVersion;
import com.laytonsmith.core.LogLevel;
@@ -15,7 +16,7 @@
import com.laytonsmith.core.functions.ArrayHandling;
import com.laytonsmith.core.functions.BasicLogic;
import com.laytonsmith.core.functions.DataHandling;
import com.laytonsmith.core.natives.interfaces.ArrayAccess;
import com.laytonsmith.core.natives.interfaces.Booleanish;
import com.laytonsmith.core.natives.interfaces.Mixed;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -39,7 +40,8 @@
* methods in this class, you need only to override the non-final ones for the same effect.
*/
@typeof("ms.lang.array")
public class CArray extends Construct implements ArrayAccess, Iterable<Mixed> {
public class CArray extends Construct implements Iterable<Mixed>, Booleanish,
com.laytonsmith.core.natives.interfaces.Iterable {

public static final CClassType TYPE = CClassType.get("ms.lang.array");
private boolean associativeMode = false;
@@ -887,11 +889,11 @@ public int compare(Mixed o1, Mixed o2) {
}
}
if(o1 instanceof CBoolean || o2 instanceof CBoolean) {
if(Static.getBoolean(o1, Target.UNKNOWN) == Static.getBoolean(o2, Target.UNKNOWN)) {
if(ArgumentValidation.getBoolean(o1, Target.UNKNOWN) == ArgumentValidation.getBoolean(o2, Target.UNKNOWN)) {
return 0;
} else {
int oo1 = Static.getBoolean(o1, Target.UNKNOWN) ? 1 : 0;
int oo2 = Static.getBoolean(o2, Target.UNKNOWN) ? 1 : 0;
int oo1 = ArgumentValidation.getBoolean(o1, Target.UNKNOWN) ? 1 : 0;
int oo2 = ArgumentValidation.getBoolean(o2, Target.UNKNOWN) ? 1 : 0;
return (oo1 < oo2) ? -1 : 1;
}
}
@@ -910,13 +912,13 @@ public int compare(Mixed o1, Mixed o2) {
}

public int compareRegular(Mixed o1, Mixed o2) {
if(Static.getBoolean(new DataHandling.is_numeric().exec(Target.UNKNOWN, null, o1), Target.UNKNOWN)
&& Static.getBoolean(new DataHandling.is_numeric().exec(Target.UNKNOWN, null, o2), Target.UNKNOWN)) {
if(ArgumentValidation.getBoolean(new DataHandling.is_numeric().exec(Target.UNKNOWN, null, o1), Target.UNKNOWN)
&& ArgumentValidation.getBoolean(new DataHandling.is_numeric().exec(Target.UNKNOWN, null, o2), Target.UNKNOWN)) {
return compareNumeric(o1, o2);
} else if(Static.getBoolean(new DataHandling.is_numeric().exec(Target.UNKNOWN, null, o1), Target.UNKNOWN)) {
} else if(ArgumentValidation.getBoolean(new DataHandling.is_numeric().exec(Target.UNKNOWN, null, o1), Target.UNKNOWN)) {
//The first is a number, the second is a string
return -1;
} else if(Static.getBoolean(new DataHandling.is_numeric().exec(Target.UNKNOWN, null, o2), Target.UNKNOWN)) {
} else if(ArgumentValidation.getBoolean(new DataHandling.is_numeric().exec(Target.UNKNOWN, null, o2), Target.UNKNOWN)) {
//The second is a number, the first is a string
return 1;
} else {
@@ -964,7 +966,12 @@ public void ensureCapacity(int capacity) {

@Override
public CClassType[] getInterfaces() {
return new CClassType[]{ArrayAccess.TYPE};
return new CClassType[]{Booleanish.TYPE, com.laytonsmith.core.natives.interfaces.Iterable.TYPE};
}

@Override
public boolean getBooleanValue(Target t) {
return size() > 0;
}

}
@@ -89,6 +89,13 @@ public boolean getBoolean() {
return val;
}

@Override
public boolean getBooleanValue(Target t) {
return val;
}



/**
* Negates this CBoolean.
*
@@ -8,7 +8,6 @@
import com.laytonsmith.core.MSVersion;
import com.laytonsmith.core.exceptions.CRE.CREUnsupportedOperationException;
import com.laytonsmith.core.exceptions.ConfigRuntimeException;
import com.laytonsmith.core.natives.interfaces.ArrayAccess;
import com.laytonsmith.core.natives.interfaces.MEnumType;
import com.laytonsmith.core.natives.interfaces.Mixed;
import java.util.Arrays;
@@ -27,7 +26,7 @@
*/
@typeof("ms.lang.ClassType")
@SuppressWarnings("checkstyle:overloadmethodsdeclarationorder")
public final class CClassType extends Construct implements ArrayAccess {
public final class CClassType extends Construct implements com.laytonsmith.core.natives.interfaces.Iterable {

public static final String PATH_SEPARATOR = FullyQualifiedClassName.PATH_SEPARATOR;

@@ -83,4 +83,9 @@ public CDecimal duplicate() {
return new CDecimal(val, getTarget());
}

@Override
public boolean getBooleanValue(Target t) {
return val.compareTo(new BigDecimal(0)) != 0;
}

}
@@ -72,4 +72,9 @@ public CDouble duplicate() {
return new CDouble(val, getTarget());
}

@Override
public double getNumber() {
return val;
}

}
@@ -70,4 +70,10 @@ public Version since() {
public CInt duplicate() {
return new CInt(val, getTarget());
}

@Override
public double getNumber() {
return (double) val;
}

}
@@ -3,12 +3,13 @@
import com.laytonsmith.PureUtilities.Version;
import com.laytonsmith.annotations.typeof;
import com.laytonsmith.core.MSVersion;
import com.laytonsmith.core.natives.interfaces.Booleanish;

/**
* Represents a MethodScript null value.
*/
@typeof("null")
public final class CNull extends Construct implements Cloneable {
public final class CNull extends Construct implements Cloneable, Booleanish {

@SuppressWarnings("FieldNameHidesFieldInSuperclass")
public static final CClassType TYPE = CClassType.get("null");
@@ -104,4 +105,11 @@ public Version since() {
public CClassType[] getInterfaces() {
throw new RuntimeException("Cannot call getInterfaces on null");
}

@Override
public boolean getBooleanValue(Target t) {
return false;
}


}
Oops, something went wrong.

0 comments on commit a00162d

Please sign in to comment.