SquirrelJME/SquirrelJME

View on GitHub
emulators/emulator-base/src/main/java/cc/squirreljme/emulator/EmulatedTypeShelf.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.emulator;

import cc.squirreljme.jvm.mle.TypeShelf;
import cc.squirreljme.jvm.mle.brackets.JarPackageBracket;
import cc.squirreljme.jvm.mle.brackets.TypeBracket;
import cc.squirreljme.jvm.mle.exceptions.MLECallError;
import cc.squirreljme.runtime.cldc.annotation.SquirrelJMEVendorApi;
import cc.squirreljme.runtime.cldc.debug.Debugging;
import java.io.IOException;
import java.io.InputStream;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
 * Emulation for {@link TypeShelf}.
 *
 * @since 2022/09/05
 */
@SuppressWarnings({"unused", "UtilityClassWithoutPrivateConstructor", 
    "PublicConstructor"})
public class EmulatedTypeShelf
{
    /**
     * Returns the binary name of the given class.
     *
     * @param __type The type to get the binary name of.
     * @return The binary name of this class.
     * @throws MLECallError If the type is not valid.
     * @since 2023/07/19
     */
    public static String binaryName(TypeBracket __type)
        throws MLECallError
    {
        if (__type == null)
            throw new MLECallError("Null arguments.");
        
        Class<?> javaClass = ((EmulatedTypeBracket)__type).javaClass;
        if (javaClass.isArray())
            return javaClass.getName();
        return javaClass.getName().replace('.', '/');
    }
    
    /**
     * Returns the type that is used within the given class.
     *
     * @param __cl The class to get the type of.
     * @return The type of the given class.
     * @since 2020/06/07
     */
    public static TypeBracket classToType(Class<?> __cl)
    {
        if (__cl == null)
            throw new MLECallError("Null arguments.");
        
        return new EmulatedTypeBracket(__cl);
    }
    
    /**
     * Returns the root component of this type, this is essentially calling
     * {@code #component(TypeBracket)} over until the type is no longer an
     * array type.
     *
     * @param __type The type to get the root component of.
     * @return The root component of the type.
     * @since 2023/07/19
     */
    @SquirrelJMEVendorApi
    public static TypeBracket componentRoot(
        @NotNull TypeBracket __type)
    {
        if (__type == null)
            throw new MLECallError("No type specified.");
        
        Class<?> javaClass = ((EmulatedTypeBracket)__type).javaClass;
        while (javaClass.isArray())
            javaClass = javaClass.getComponentType();
        
        return new EmulatedTypeBracket(javaClass);
    }
    
    /**
     * Finds a type by its name.
     *
     * @param __name The name of the type.
     * @return The type bracket for the type or {@code null} if none was
     * found.
     * @since 2020/06/02
     */
    public static TypeBracket findType(String __name)
        throws MLECallError
    {
        if (__name == null)
            throw new MLECallError("No name specified.");
        
        try
        {
            return new EmulatedTypeBracket(
                Class.forName(__name.replace('/', '.')));
        }
        catch (ClassNotFoundException __e)
        {
            throw new MLECallError("Class not found: " + __name, __e);
        }
    }
    
    /**
     * Returns the JAR that the type is within.
     *
     * @param __type The type to get the JAR of.
     * @return The JAR that the type is within or {@code null} if this type
     * is not within any JAR.
     * @since 2023/07/19
     */
    @SquirrelJMEVendorApi
    public static JarPackageBracket inJar(TypeBracket __type)
    {
        if (__type == null)
            throw new MLECallError("Null arguments.");
        
        Class<?> javaClass = EmulatedTypeShelf.typeToClass(__type);
        
        // Arrays and primitives are in no jar
        if (javaClass.isArray() || javaClass.isPrimitive())
            return null;
        
        // Get the file name of the class
        String fileName = javaClass.getName()
            .replace('.', '/') + ".class";
        
        // This is a bit of a cheat, but we go through all of our JARs and
        // try to find the class this belongs to... the emulation layer fakes
        // the libraries, but they should still technically be valid in finding
        // which Jar a class is in... at least via resource
        for (JarPackageBracket library : EmulatedJarPackageShelf.libraries())
        {
            try (InputStream in = EmulatedJarPackageShelf
                .openResource(library, fileName))
            {
                // If the resource was opened, then we have this
                if (in != null)
                    return library;
            }
            catch (IOException ignored)
            {
            }
        }
        
        // Not found
        return null;
    }
    
    /**
     * Returns the interfaces of the type.
     *
     * @param __type The type to get the interfaces of.
     * @return The interfaces of the class.
     * @throws MLECallError If {@code __type} is null. 
     * @since 2020/06/07
     */
    public static TypeBracket[] interfaces(TypeBracket __type)
        throws MLECallError
    {
        if (__type == null)
            throw new MLECallError("Null arguments.");
        
        // Get all the interfaces
        Class<?> javaClass = EmulatedTypeShelf.typeToClass(__type);
        Class<?>[] interfaces = javaClass.getInterfaces();
        int count = interfaces.length;
        
        // Build result
        TypeBracket[] result = new TypeBracket[count];
        for (int i = 0; i < count; i++)
            result[i] = EmulatedTypeShelf.classToType(interfaces[i]);
        
        return result;
    }
    
    /**
     * Returns true if this type is an array.
     *
     * @param __type The type to check.
     * @return If this is an array.
     * @throws MLECallError If {@code __type} is {@code null}.
     * @since 2023/07/19
     */
    @SquirrelJMEVendorApi
    public static boolean isArray(TypeBracket __type)
        throws MLECallError
    {
        if (__type == null)
            throw new MLECallError("Null arguments.");
        
        return ((EmulatedTypeBracket)__type).javaClass.isArray();
    }
    
    /**
     * Gets the class type of the given bracket.
     *
     * @param <T> Ignored.
     * @param __type The type to get the class object of.
     * @return The class type for the given type.
     * @since 2020/05/30
     */
    @SuppressWarnings("unchecked")
    public static <T> Class<T> typeToClass(TypeBracket __type)
    {
        if (__type == null)
            throw new MLECallError("Null arguments.");
        
        try
        {
            return (Class<T>)((EmulatedTypeBracket)__type).javaClass;
        }
        catch (ClassCastException e)
        {
            throw new MLECallError("Wrong type.");
        }
    }
}