Class ObjectUtils

java.lang.Object
org.daiitech.naftah.builtin.utils.ObjectUtils

public final class ObjectUtils extends Object
Utility class providing various helper methods for working with Java objects in the context of the Naftah language runtime.

This class includes methods for:

  • Evaluating object truthiness and emptiness
  • Determining and converting between Java and Naftah types
  • Applying arithmetic and logical operations to objects
  • Handling arrays, collections, maps, and primitive wrappers
  • Converting objects to their Naftah string representations

This class is not instantiable.

Author:
Chakib Daii
  • Constructor Details

    • ObjectUtils

      private ObjectUtils()
      Private constructor to prevent instantiation. Always throws a NaftahBugError when called.
  • Method Details

    • isTruthy

      public static boolean isTruthy(Object obj)
      Determines whether the given object is considered "truthy".

      The following objects are considered falsy (return false):

      • null
      • Boolean.FALSE
      • Numeric zero or NaN values
      • Blank strings (empty or only whitespace)
      • Empty arrays
      • Empty collections or maps
      For all other objects, this method returns true.
      Parameters:
      obj - the object to evaluate for truthiness
      Returns:
      true if the object is considered truthy; false otherwise
    • compare

      public static int compare(Object left, Object right)
      Compares two Comparable objects in a null-safe manner.

      Rules for comparison:

      • If both left and right are the same object (including both null), returns 0.
      • A null value is considered less than any non-null value (nulls-first).
      • If both values are non-null, their natural ordering (via Comparable.compareTo(T)) is used.

      Examples:

       compare(null, null) = 0
       compare(null, "abc") = -1
       compare("abc", null) = 1
       compare("abc", "def") = "abc".compareTo("def")
       
      Parameters:
      left - the first object to compare, may be null
      right - the second object to compare, may be null
      Returns:
      a negative integer, zero, or a positive integer if left is less than, equal to, or greater than right, respectively
    • equals

      public static <T> boolean equals(T left, T right, boolean safe)
      Safely compares two objects for equality using custom dynamic operations.

      This method attempts to evaluate left and right using the internal BinaryOperation.EQUALS operation and checks that all results are true. If an internal NaftahBugError occurs, it falls back to left.equals(right), unless the error matches a known empty-arguments condition and safe is true, in which case it returns true.

      Type Parameters:
      T - the type of the objects being compared
      Parameters:
      left - the first object to compare
      right - the second object to compare
      safe - whether to treat certain internal evaluation errors as equality
      Returns:
      true if the objects are considered equal; false otherwise
      Throws:
      NaftahBugError - if a comparison fails and safe is false for certain internal errors
    • not

      public static Object not(Object value)
      Negates the given value.

      Tries arithmetic negation using NumberUtils.negate(Object). If that fails, it performs logical negation using isTruthy(Object).

      Parameters:
      value - the value to negate
      Returns:
      the negated value
    • isEmpty

      public static boolean isEmpty(Object obj)
      Determine whether the given object is empty.

      This method supports the following object types.

      If the given object is non-null and not one of the aforementioned supported types, this method returns false.

      Parameters:
      obj - the object to check
      Returns:
      true if the object is null or empty
    • getJavaType

      public static JavaType getJavaType(NaftahParserBaseVisitor<?> naftahParserBaseVisitor, DefaultContext currentContext, org.antlr.v4.runtime.ParserRuleContext naftahTypeContext)
      Resolves a JavaType from a Naftah parser type context.

      This method inspects the supplied ParserRuleContext and maps Naftah language type constructs to their corresponding Java types. It supports return types, built-in types, complex built-ins, variable types, and qualified names, delegating to specialized resolvers where appropriate.

      Qualified names are resolved against the current DefaultContext, including import matching. If a type cannot be resolved, the result defaults to Object.

      Resolution behavior

      • void return types → Void
      • var or unresolved types → Object
      • Built-in types → mapped Java primitives or boxed types
      • Complex built-ins → resolved recursively
      • Qualified names → resolved via imports and context lookup
      Parameters:
      naftahParserBaseVisitor - the active parser visitor, used to resolve complex built-in types
      currentContext - the current semantic context, used for import and type resolution
      naftahTypeContext - the parser rule context representing a Naftah type
      Returns:
      the resolved JavaType; JavaType.ofObject() if the type cannot be resolved
    • getJavaType

      public static JavaType getJavaType(DefaultContext currentContext, NaftahParser.QualifiedNameTypeContext qualifiedNameTypeContext)
      Resolves a JavaType from a qualified-name type context.

      The type name is extracted either from a fully-qualified name or a simple identifier and is then resolved against the current DefaultContext, including import matching.

      If a matching import is found, the imported type is used. Otherwise, the resolved name is looked up directly. If resolution fails, the result defaults to Object.

      Parameters:
      currentContext - the current semantic context used for import resolution
      qualifiedNameTypeContext - the parser context representing a qualified-name type
      Returns:
      the resolved JavaType, or JavaType.ofObject() if unresolved
    • getJavaType

      public static JavaType getJavaType(NaftahParser.BuiltInContext builtInContext)
      Resolves a JavaType from a built-in Naftah type.

      Built-in types are mapped to their corresponding boxed Java types: boolean, char, byte, short, int, long, float, double, and string.

      If the built-in type is not recognized, this method returns JavaType.ofObject().

      Parameters:
      builtInContext - the parser context representing a built-in type
      Returns:
      the corresponding JavaType
    • getJavaType

      public static JavaType getJavaType(NaftahParserBaseVisitor<?> naftahParserBaseVisitor, NaftahParser.ComplexBuiltInContext complexBuiltInContext)
      Resolves a JavaType from a complex built-in type context.

      Complex built-in types represent parameterized or structured constructs and are resolved recursively using the provided parser visitor.

      Supported complex built-ins

      • structMap&lt;String, DeclaredVariable&gt;
      • implementationMap&lt;String, DeclaredFunction&gt;
      • pair&lt;T, U&gt;Pair&lt;T, U&gt;
      • triple&lt;T, U, V&gt;Triple&lt;T, U, V&gt;
      • list&lt;T&gt;List&lt;T&gt;
      • set&lt;T&gt;Set&lt;T&gt;
      • map&lt;K, V&gt;Map&lt;K, V&gt;
      • tupleTuple

      Generic type arguments are resolved by visiting nested type contexts via the supplied visitor.

      Parameters:
      naftahParserBaseVisitor - the active parser visitor used to resolve nested type parameters
      complexBuiltInContext - the parser context representing a complex built-in type
      Returns:
      the resolved JavaType, or JavaType.ofObject() if unresolved
    • getNaftahType

      public static String getNaftahType(org.antlr.v4.runtime.Vocabulary vocabulary, JavaType javaType)
      Maps a JavaType to its equivalent Naftah language type representation using the supplied ANTLR Vocabulary.

      Primitive wrapper types and String are mapped to their corresponding Naftah built-in type keywords (e.g. int, boolean, string).

      Parameterized and structured Java types are mapped as follows:

      • Map&lt;String, DeclaredVariable&gt;struct
      • Map&lt;String, DeclaredFunction&gt;implementation
      • Map&lt;K, V&gt;map&lt;K, V&gt;
      • Pair&lt;A, B&gt;pair&lt;A, B&gt;
      • Triple&lt;A, B, C&gt;triple&lt;A, B, C&gt;
      • List&lt;T&gt;list&lt;T&gt;
      • Set&lt;T&gt;set&lt;T&gt;
      • Tupletuple

      Generic type parameters are resolved recursively to produce fully-formed Naftah type expressions.

      If javaType is null or cannot be mapped explicitly, the fallback type var is returned.

      Parameters:
      vocabulary - the ANTLR Vocabulary used to resolve token symbols
      javaType - the Java type to map
      Returns:
      the formatted Naftah type representation
    • getNaftahType

      public static String getNaftahType(org.antlr.v4.runtime.Parser parser, JavaType javaType)
      Maps a JavaType to its corresponding Naftah language type using the Vocabulary associated with the given ANTLR Parser.

      This is a convenience overload that delegates to getNaftahType(Vocabulary, JavaType).

      Parameters:
      parser - the ANTLR Parser providing the vocabulary
      javaType - the Java type to map
      Returns:
      the formatted Naftah type representation
    • isBuiltinType

      public static boolean isBuiltinType(Object obj)
      Checks if the object is a Naftah built-in type.
      Parameters:
      obj - the object to check
      Returns:
      true if the object is a built-in type
    • instanceOf

      public static boolean instanceOf(Object obj, JavaType targetType)
      Checks whether the given object is an instance of the specified JavaType.

      This method behaves similarly to the Java instanceof operator, but works with JavaType to support runtime inspection of generic type parameters.

      Unlike strict type equality checks, this method uses assignability semantics. This means that an object whose type is more specific may still be considered an instance of a more general target type.

      Examples:

      
       JavaType objectType = JavaType.of(Object.class);
       JavaType numberType = JavaType.of(Number.class);
       JavaType listOfObjects = JavaType.of(new TypeReference<List<Object>>() {});
      
       instanceOf("hello", objectType); // true
       instanceOf(42, numberType); // true
       instanceOf(List.of("a"), listOfObjects); // true
       

      If either the object or the target type is null, this method returns false.

      Parameters:
      obj - the object to check; may be null
      targetType - the JavaType to check against; may be null
      Returns:
      true if the object is an instance of the specified type according to assignability rules; false otherwise
    • validateType

      public static void validateType(String name, Object value, JavaType type, int line, int column)
      Validates that a runtime value conforms to the expected JavaType.

      If the provided value is non-null and does not match the expected type, this method throws a Naftah type-mismatch error. Type matching is performed using instanceOf(Object, JavaType), which supports raw types, generic parameters, and wildcard assignability.

      This method is typically used to validate function arguments, variable assignments, and return values at runtime.

      Null values are considered valid for any type and will not trigger a validation error.

      Example usage:

      
       JavaType expectedType = JavaType.of(new TypeReference<List<String>>() {});
       validateType("items", List.of("a", "b"), expectedType); // valid
      
       validateType("items", List.of(1, 2), expectedType); // throws type mismatch error
       
      Parameters:
      name - the name of the variable, parameter, or value being validated; used for error reporting
      value - the runtime value to validate; may be null
      type - the expected JavaType of the value
      Throws:
      NaftahBugError - if the value is non-null and does not conform to the expected type
    • isSimpleType

      public static boolean isSimpleType(Object obj)
      Checks if the object is a "simple" type (primitive wrapper, string, number, etc.).
      Parameters:
      obj - the object to check
      Returns:
      true if the object is a simple type
    • isSimpleOrBuiltinOrCollectionOrMapOfSimpleType

      public static boolean isSimpleOrBuiltinOrCollectionOrMapOfSimpleType(Object obj)
      Checks whether the object or its components are simple, built-in, or collections/maps/tuple of such types.
      Parameters:
      obj - the object to evaluate
      Returns:
      true if the object is simple or composed only of simple/builtin types
    • applyOperation

      public static Object applyOperation(Object left, Object right, BinaryOperation operation)
      Applies a binary operation to two values.
      Parameters:
      left - the left operand
      right - the right operand
      operation - the binary operation to apply
      Returns:
      the result of the operation
      Throws:
      NaftahBugError - if operands are incompatible
    • applyOperation

      public static Object applyOperation(Object a, UnaryOperation operation)
      Applies a unary operation to a value.
      Parameters:
      a - the operand
      operation - the unary operation to apply
      Returns:
      the result of the operation
      Throws:
      NaftahBugError - if the operand type is unsupported
    • booleanToInt

      public static int booleanToInt(boolean aBoolean)
      Converts a boolean to an integer (1 for true, 0 for false).
      Parameters:
      aBoolean - the boolean to convert
      Returns:
      1 if true, 0 if false
    • intToBoolean

      public static boolean intToBoolean(int i)
      Converts an integer to a boolean (true if odd, false if even).
      Parameters:
      i - the integer to convert
      Returns:
      true if the integer is odd; false otherwise
    • booleanToString

      public static String booleanToString(boolean b)
      Converts a boolean value to its Naftah string representation ("صحيح" or "خطأ").
      Parameters:
      b - the boolean value
      Returns:
      "صحيح" if true, "خطأ" if false
    • getNaftahValueToString

      public static String getNaftahValueToString(Object o)
      Converts a Naftah value into its string representation, using language-specific formatting.
      Parameters:
      o - the value to convert
      Returns:
      the string representation
    • replaceAllNulls

      public static String replaceAllNulls(String s)
      Replaces all "null" occurrences in the given string with the localized NULL constant.
      Parameters:
      s - the string to process
      Returns:
      the string with "null" replaced
    • numberToString

      public static String numberToString(Number number)
      Converts the given Number into a localized string representation using Arabic locale formatting rules.

      When the system property arabic.number.format is true, this method uses a preconfigured NumberFormat instance for the Arabic locale. This includes Arabic-style decimal and grouping separators and may render digits in Arabic-Indic form, depending on JVM and font support.

      The method synchronizes on ScriptUtils.NUMBER_FORMAT since NumberFormat is not thread-safe.

      If the system property is not set to true, the method falls back to a custom digit conversion via ScriptUtils.numberToString(Number).

      Parameters:
      number - the number to format; must not be null
      Returns:
      the number formatted as a string using Arabic locale or Arabic digits
      Throws:
      NullPointerException - if number is null
      See Also:
    • padZero

      public static String padZero(String str, int length)
      Pads the given string with leading zeros to ensure it reaches the specified length.

      If the input string is shorter than the desired length, zeros are added at the beginning. If the string is already equal to or longer than the specified length, it is returned unchanged.

      Example:

       padZero("5", 2) // returns "05"
       padZero("123", 5) // returns "00123"
       padZero("42", 1) // returns "42"
       
      Parameters:
      str - the input string to pad
      length - the desired minimum length of the resulting string
      Returns:
      the input string left-padded with zeros to the specified length
    • size

      public static Number size(Object obj)
      Returns the size or length of the given object, depending on its type.

      This method provides a unified way to determine the "size" of various types of objects commonly encountered in Java, including arrays, collections, maps, strings, and arbitrary objects. The definition of size depends on the object's runtime type as follows:

      • Arrays: Returns the array's length via Array.getLength(Object).
      • Collections: Returns the number of elements using Collection.size().
      • Maps: Returns the number of key-value mappings using Map.size().
      • Strings: Returns the number of characters using String.length().
      • Boxed primitives: Returns 1 because primitives are considered as a single value counts.
      • Other objects: Returns the count of non-static declared fields in the object's class.

      If the provided object is null, this method returns 0.

      Parameters:
      obj - the object whose size is to be determined; may be null
      Returns:
      the size or length of the given object, or 0 if null