SquirrelJME/SquirrelJME

View on GitHub
modules/cldc-compact/src/main/java/cc/squirreljme/runtime/cldc/debug/ErrorCode.java

Summary

Maintainability
A
0 mins
Test Coverage
// -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
// ---------------------------------------------------------------------------
// Multi-Phasic Applications: SquirrelJME
//     Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
// ---------------------------------------------------------------------------
// SquirrelJME is under the Mozilla Public License Version 2.0.
// See license.mkd for licensing and copyright information.
// ---------------------------------------------------------------------------

package cc.squirreljme.runtime.cldc.debug;

import cc.squirreljme.jvm.mle.DebugShelf;
import cc.squirreljme.jvm.mle.JarPackageShelf;
import cc.squirreljme.jvm.mle.TypeShelf;
import cc.squirreljme.jvm.mle.brackets.JarPackageBracket;
import cc.squirreljme.jvm.mle.brackets.TracePointBracket;
import cc.squirreljme.jvm.mle.brackets.TypeBracket;
import java.io.IOException;
import java.io.InputStream;
import org.intellij.lang.annotations.PrintFormat;
import org.jetbrains.annotations.Range;

/**
 * Error code generation and handling.
 *
 * @since 2023/07/19
 */
public final class ErrorCode
{
    /** The prefix for properties. */
    public static final String PREFIX_PROPERTY =
        "X-SquirrelJME-PrefixCode";
    
    /**
     * Not used.
     *
     * @since 2023/07/19
     */
    private ErrorCode()
    {
    }
    
    /**
     * Formats an error code.
     *
     * @param __format The format to use.
     * @param __args The arguments to use for the format.
     * @return The resultant string.
     * @deprecated This is used for compatibility in migrating codes over
     * accordingly.
     * @since 2023/07/19
     */
    @SuppressWarnings({"NewMethodNamingConvention", 
        "squirreljme_qualifiedError"})
    @Deprecated
    public static String __error__(@PrintFormat String __format,
        Object... __args)
    {
        // Determine the actual sub-ID code
        int subId = 0;
        if (__format.length() >= 4)
        {
            char a = __format.charAt(2);
            char b = __format.charAt(2);
            
            subId = (Character.digit(a, Character.MAX_RADIX) *
                Character.MAX_RADIX) + Character.digit(b, Character.MAX_RADIX);
        }
        
        // Forward call
        return ErrorCode.__error__(subId, __args);
    }
    
    /**
     * Formats an error code.
     *
     * @param __idCode The error ID code.
     * @return The formatted string.
     * @since 2023/07/19
     */
    @SuppressWarnings({"NewMethodNamingConvention", 
        "squirreljme_qualifiedError"})
    public static String __error__(@Range(from = -1, to = 1296) int __idCode)
    {
        return ErrorCode.__error__(__idCode, (Object[])null);
    }
    
    /**
     * Formats an error code.
     *
     * @param __idCode The error ID code.
     * @param __args Arguments to the error.
     * @return The formatted string.
     * @since 2023/07/19
     */
    @SuppressWarnings("NewMethodNamingConvention")
    public static String __error__(@Range(from = -1, to = 1296) int __idCode,
        Object... __args)
    {
        // Determine the error prefix of our code
        String __prefix = "XX";
        
        // Determine the prefix that should be used
        TypeBracket throwingClass = ErrorCode.__throwingClass();
        if (throwingClass != null)
            __prefix = ErrorCode.__locatePrefix(
                ErrorCode.__unComponent(throwingClass));
        
        // Format the error code accordingly
        int n = (__args == null ? 0 : __args.length);
        return String.format("%s%04d%s", __prefix, __idCode,
            (n == 0 ? "" : ": ")) +
            String.format(ErrorCode.__argFormat(n), __args);
    }
    
    /**
     * Returns the argument format specifiers to use for the given array
     * length.
     *
     * @param __len The length of the array.
     * @return The argument format to use for representation.
     * @since 2023/07/19
     */
    private static String __argFormat(int __len)
    {
        switch (__len)
        {
            case 0: return "";
            case 1: return "%s";
            case 2: return "%s %s";
            case 3: return "%s %s %s";
            case 4: return "%s %s %s %s";
            case 5: return "%s %s %s %s %s";
            case 6: return "%s %s %s %s %s %s";
            case 7: return "%s %s %s %s %s %s %s";
            case 8: return "%s %s %s %s %s %s %s %s";
            case 9: return "%s %s %s %s %s %s %s %s %s";
            case 10: return "%s %s %s %s %s %s %s %s %s %s";
            case 11: return "%s %s %s %s %s %s %s %s %s %s %s";
            case 12: return "%s %s %s %s %s %s %s %s %s %s %s %s";
        }
        
        // Gosh... Way too many arguments, so add them all ourselves
        StringBuilder result = new StringBuilder((3 * __len) - 1);
        for (int i = 0; i < __len; i++)
            if (i == 0)
                result.append("%s");
            else
                result.append(" %s");
        
        return result.toString();
    }
    
    /**
     * Returns the prefix for the throwing class.
     *
     * @param __throwingClass The throwing class prefix.
     * @return The prefix for the throwing class.
     * @since 2023/07/19
     */
    private static String __locatePrefix(TypeBracket __throwingClass)
    {
        // Ignore here
        if (__throwingClass == null)
            return "?N";
        
        // Is this in any kind of JAR?
        JarPackageBracket inJar = TypeShelf.inJar(__throwingClass);
        if (inJar == null)
            return "?J";
        
        // See if there is a numerical prefix code defined
        int prefixCode = JarPackageShelf.prefixCode(inJar);
        if (prefixCode < 0)
            return "?P";
        
        // Build prefix code
        StringBuilder sb = new StringBuilder(2);
        sb.append(Character.toUpperCase(Character.forDigit(
            prefixCode / Character.MAX_RADIX, Character.MAX_RADIX)));
        sb.append(Character.toUpperCase(Character.forDigit(
            prefixCode % Character.MAX_RADIX, Character.MAX_RADIX)));
        return sb.toString();
    }
    
    /**
     * Returns the class throwing this.
     *
     * @return The class throwing this.
     * @since 2023/07/19
     */
    private static TypeBracket __throwingClass()
    {
        // Get the stack trace
        TracePointBracket[] trace = DebugShelf.traceStack();
        
        // Find out who called us
        String selfClassRt = ErrorCode.class.getName();
        String selfClassBn = TypeShelf.binaryName(
            TypeShelf.classToType(ErrorCode.class));
        for (int i = 0, n = trace.length; i < n; i++)
        {
            TracePointBracket point = trace[i];
            
            // Ignore unknown classes
            String atClass = DebugShelf.pointClass(point);
            if (atClass == null)
                continue;
            
            // Ignore ourself
            if (selfClassRt.equals(atClass) || selfClassBn.equals(atClass))
                continue;
            
            // Resolve class
            TypeBracket found = TypeShelf.findType(atClass);
            if (found != null)
                return found;
        }
        
        // Not found ultimately
        return null;
    }
    
    /**
     * Returns the root component of a type.
     *
     * @param __in The input class.
     * @return The root component.
     * @since 2023/07/19
     */
    private static TypeBracket __unComponent(TypeBracket __in)
    {
        if (__in == null)
            return null;
        
        if (TypeShelf.isArray(__in))
            return TypeShelf.componentRoot(__in);
        return __in;
    }
}