SquirrelJME/SquirrelJME

View on GitHub
modules/cldc-compact/src/main/java/java/lang/System.java

Summary

Maintainability
A
0 mins
Test Coverage
// -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
// ---------------------------------------------------------------------------
// 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 java.lang;

import cc.squirreljme.jvm.mle.ObjectShelf;
import cc.squirreljme.jvm.mle.RuntimeShelf;
import cc.squirreljme.jvm.mle.TypeShelf;
import cc.squirreljme.jvm.mle.brackets.TypeBracket;
import cc.squirreljme.jvm.mle.constants.PhoneModelType;
import cc.squirreljme.jvm.mle.constants.StandardPipeType;
import cc.squirreljme.jvm.mle.constants.VMDescriptionType;
import cc.squirreljme.runtime.cldc.SquirrelJME;
import cc.squirreljme.runtime.cldc.annotation.Api;
import cc.squirreljme.runtime.cldc.i18n.DefaultLocale;
import cc.squirreljme.runtime.cldc.io.CodecFactory;
import cc.squirreljme.runtime.cldc.io.ConsoleOutputStream;
import cc.squirreljme.runtime.cldc.lang.LineEndingUtils;
import java.io.PrintStream;
import org.intellij.lang.annotations.Flow;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Range;

/**
 * This class contains methods which are used to interact with the system and
 * the environment.
 *
 * @since 2018/10/14
 */
@Api
public final class System
{
    /** Standard error stream (stderr). */
    @Api
    public static final PrintStream err =
        new __CanSetPrintStream__(new PrintStream(
            new ConsoleOutputStream(StandardPipeType.STDERR,
                true), true));
    
    /** Standard output stream (stdout). */
    @Api
    public static final PrintStream out =
        new __CanSetPrintStream__(new PrintStream(
            new ConsoleOutputStream(StandardPipeType.STDOUT,
                false), true));
    
    /**
     * Not used.
     *
     * @since 2018/03/01
     */
    private System()
    {
    }
    
    /**
     * Copies from the source array to the destination.
     *
     * @param __src The source array.
     * @param __srcOff The source offset.
     * @param __dest The destination array.
     * @param __destOff The destination offset.
     * @param __copyLen The number of elements to copy.
     * @throws ArrayStoreException If the destination array cannot contain
     * the given data.
     * @throws IndexOutOfBoundsException If the offset and or/lengths are
     * negative or exceed the array bounds.
     * @throws NullPointerException On null arguments.
     * @since 2018/09/27
     */
    @Api
    public static void arraycopy(
        @Flow(sourceIsContainer=true, target="__dest",
            targetIsContainer=true) @NotNull Object __src,
        @Range(from = 0, to = Integer.MAX_VALUE) int __srcOff,
        @NotNull Object __dest,
        @Range(from = 0, to = Integer.MAX_VALUE) int __destOff,
        @Range(from = 0, to = Integer.MAX_VALUE) int __copyLen)
        throws ArrayStoreException, IndexOutOfBoundsException,
            NullPointerException
    {
        if (__src == null || __dest == null)
            throw new NullPointerException("NARG");
        
        /* {@squirreljme.error ZZ1w Negative offsets and/or length cannot be
        specified. (The source offset; The destination offset; The copy
        length)} */
        if (__srcOff < 0 || __destOff < 0 || __copyLen < 0)
            throw new IndexOutOfBoundsException(
                String.format("ZZ1w %d %d %d",
                    __srcOff, __destOff, __copyLen));
        
        /* {@squirreljme.error ZZ1x Copy operation would exceed the bounds of
        the array. (Source offset; Source length; Destination offset;
        Destination length; The copy length)} */
        int srcLen = ObjectShelf.arrayLength(__src);
        int destLen = ObjectShelf.arrayLength(__dest);
        if (__srcOff + __copyLen < 0 || __srcOff + __copyLen > srcLen ||
            __destOff + __copyLen < 0 || __destOff + __copyLen > destLen)
            throw new IndexOutOfBoundsException(String.format(
                "ZZ1x %d %d %d %d %d", __srcOff, srcLen,
                    __destOff, destLen, __copyLen));
        
        // Get both respective classes
        Class<?> srcClass = __src.getClass();
        Class<?> destClass = __dest.getClass();
        
        /* {@squirreljme.error ZZ1y The source array type is not compatible
        with destination array. (The source array; The destination array)} */
        if (srcClass != destClass && !destClass.isAssignableFrom(srcClass))
            throw new ArrayStoreException(String.format(
                "ZZ1y %s %s", __src, __dest));
        
        // If we are copying nothing then we need not even bother with anything
        // else, and we do not have to check the array types as well.
        if (__copyLen == 0)
            return;
        
        // Also as well, if the source and destination are the same and the
        // offsets are the same then nothing will happen at all.
        if (__src == __dest && __srcOff == __destOff)
            return;
        
        // We can use the native type system within MLE to knock off a few
        // branch possibilities
        TypeBracket srcType = TypeShelf.classToType(srcClass);
        TypeBracket component = TypeShelf.component(srcType);
        
        // Primitive types can be copied at full speed as they do not require
        // any references are otherwise to be counted or garbage collection to
        // be managed
        if (TypeShelf.isPrimitive(component))
        {
            // More common primitives
            if (srcClass == byte[].class)
                ObjectShelf.arrayCopy((byte[])__src, __srcOff,
                    (byte[])__dest, __destOff, __copyLen);
            else if (srcClass == char[].class)
                ObjectShelf.arrayCopy((char[])__src, __srcOff,
                    (char[])__dest, __destOff, __copyLen);
            else if (srcClass == int[].class)
                ObjectShelf.arrayCopy((int[])__src, __srcOff,
                    (int[])__dest, __destOff, __copyLen);
            else if (srcClass == short[].class)
                ObjectShelf.arrayCopy((short[])__src, __srcOff,
                    (short[])__dest, __destOff, __copyLen);
            
            // Less common types
            else if (srcClass == boolean[].class)
                ObjectShelf.arrayCopy((boolean[])__src, __srcOff,
                    (boolean[])__dest, __destOff, __copyLen);
            else if (srcClass == long[].class)
                ObjectShelf.arrayCopy((long[])__src, __srcOff,
                    (long[])__dest, __destOff, __copyLen);
            else if (srcClass == float[].class)
                ObjectShelf.arrayCopy((float[])__src, __srcOff,
                    (float[])__dest, __destOff, __copyLen);
            else if (srcClass == double[].class)
                ObjectShelf.arrayCopy((double[])__src, __srcOff,
                    (double[])__dest, __destOff, __copyLen);
            
            /* {@squirreljme.error ZZ1h Not a primitive array type.} */
            else
                throw new Error("ZZ1h");
        }
        
        // There is no native handler for manual object array copies due to
        // references and otherwise
        else
        {
            Object[] src = (Object[])__src;
            Object[] dest = (Object[])__dest; 
            
            // Right-to-left copy when going from left to right would overwrite
            // the source
            if (__destOff > __srcOff)
            {
                int i = (__srcOff + __copyLen) - 1,
                    o = (__destOff + __copyLen) - 1;
                
                for (; i >= __srcOff; i--, o--)
                    dest[o] = src[i];
            }
            
            // Left-to-right copy
            else
            {
                // These offsets for the loops are the same
                int end = __srcOff + __copyLen;
                for (; __srcOff < end; __srcOff++, __destOff++)
                    dest[__destOff] = src[__srcOff];
            }
        }
    }
    
    /**
     * Returns the current time on the system's clock in UTC since the epoch
     * (January 1, 1970 UTC).
     *
     * Note that this clock is not monotonic in that if a system adjusts the
     * system clock this method may return values lower than previous calls
     * which are made.
     *
     * Depending on the host hardware and operating system, the granularity of
     * this clock may or may not be accurate.
     *
     * @return The number of milliseconds since the epoch.
     * @since 2017/11/10
     */
    @Api
    public static long currentTimeMillis()
    {
        // Returns the current time in UTC, not local time zone.
        return RuntimeShelf.currentTimeMillis();
    }
    
    /**
     * Indicates that the application exits with the given code.
     *
     * @param __e The exit code, the value of this code may change according
     * to the host operating system and the resulting process might not exit
     * with the given code.
     * @since 2017/02/08
     */
    @Api
    @Contract("_ -> fail")
    public static void exit(int __e)
    {
        Runtime.getRuntime().exit(__e);
    }
    
    /**
     * Indicates that the application should have garbage collection be
     * performed. It is unspecified when garbage collection occurs.
     *
     * @since 2017/02/08
     */
    @Api
    public static void gc()
    {
        Runtime.getRuntime().gc();
    }
    
    /**
     * This obtains the value of a system property (if one is set) and returns
     * its value. System properties are declared by the system and are used
     * by applications to potentially modify their behavior.
     *
     * {@squirreljme.property java.io.tmpdir This is the temporary directory
     * which indicates where temporary files (those that are deleted after
     * an unspecified duration) are to be placed. If there is no filesystem
     * on the device then a blank string will always be returned.}
     * {@squirreljme.property java.version This is the version of the virtual
     * machine which the environment runs under.}
     * {@squirreljme.property java.vendor This is the vendor of the virtual
     * machine and specifies who wrote it.}
     * {@squirreljme.property java.vendor.url This is a URL which usually
     * points to the website of the vendor.}
     * {@squirreljme.property line.separator This represents the line
     * separation sequence that the host operating system uses for its native
     * files. Generally it would either be {@code '\n'}, {@code '\r'}, or
     * {@code "\r\n"), however retro-systems might use a different line ending
     * sequence.}}
     * {@squirreljme.property microedition.configuration This is the current
     * configuration of CLDC library which indicates which primary classes
     * are available to it. The values will either be {@code "CLDC-1.8"} for
     * the complete set of APIs and {@code "CLDC-1.8-Compact"} for the compact
     * set of APIs.}
     * {@squirreljme.property microedition.deviceid.uuid This is the unique
     * identifier to the current device that regardless of the number of
     * reboots and reinitializations that occur, this should return the same
     * value.}
     * {@squirreljme.property microedition.encoding This is the character
     * encoding that is used by default for specific methods when one has not
     * been specified. On modern systems this is likely to be {@code "UTF-8},
     * while on retro-devices and operating systems this will likely be an
     * encoding starting with {@code "x-squirreljme"}. Please note that the
     * default encoding might not be compatible with UTF-8 (and may possibly
     * well be EBCDIC).}
     * {@squirreljme.property microedition.hostname The host name of the
     * current system that the virtual machine is running on as it appears
     * to other machines on the network.}
     * {@squirreljme.property microedition.locale The current locale that the
     * library will use.}
     * {@squirreljme.property microedition.platform This is the device that
     * SquirrelJME is running on. It is in the format of
     * {@code (Manufacturer)(DeviceModelNumber)[/version[/comments]]}. The
     * manufacturer and device model number are concatenated with no spaces.}
     * {@squirreljme.property microedition.profiles This is a space separated
     * list of profiles which are supported by the run-time, an example
     * value that may be returned is MIDP-3.0 representing that that specified
     * standard is implemented.}
     * {@squirreljme.property os.arch This is the architecture of the hardware
     * that SquirrelJME is running on, the value is dependent on the platform
     * itself. Note that architecture names use standard SquirrelJME
     * architecture names.}
     * {@squirreljme.property os.name This is the name of the operating system
     * that SquirrelJME is running on, if SquirrelJME is the operating itself
     * then this value will be {@code "squirreljme"}.}
     * {@squirreljme.property os.version This is the version number of the
     * host operating system. The returned value might not be a number and may
     * be a string representing the host.}
     * {@squirreljme.property user.dir This is the current working directory
     * which indicates the location where non-absolute file paths are derived
     * from. If there is no filesystem on the device then a blank string will
     * always be returned.}
     *
     * @param __k The system property value to obtain.
     * @return The value of the system property or {@code null} if it is not
     * does not exist.
     * @throws IllegalArgumentException If the key is empty.
     * @throws NullPointerException On null arguments.
     * @throws SecurityException If the current process is not permitted to
     * access system properties or obtain the value of a specific property.
     * @since 2016/05/21
     */
    @Api
    public static String getProperty(String __k)
        throws IllegalArgumentException, NullPointerException,
            SecurityException
    {
        // Check
        if (__k == null)
            throw new NullPointerException("NARG");
        
        /* {@squirreljme.error ZZ1z Cannot request a system property which has
        a blank key.} */
        if (__k.equals(""))
            throw new IllegalArgumentException("ZZ1z");
        
        // Short circuit for run-time detection
        if (__k.equals("cc.squirreljme.isruntime"))
            return "true";
        
        // Not allowed to do this?
        System.getSecurityManager().checkPropertyAccess(__k);
        
        // Depends on the property
        switch (__k)
        {
                // SquirrelJME VM executable path
            case "cc.squirreljme.vm.execpath":
                return RuntimeShelf.vmDescription(
                    VMDescriptionType.EXECUTABLE_PATH);
                
                // SquirrelJME free memory
            case "cc.squirreljme.vm.freemem":
                return Long.toString(Runtime.getRuntime().freeMemory());
                
                // SquirrelJME total memory
            case "cc.squirreljme.vm.totalmem":
                return Long.toString(Runtime.getRuntime().totalMemory());
                
                // SquirrelJME free memory
            case "cc.squirreljme.vm.maxmem":
                return Long.toString(Runtime.getRuntime().maxMemory());
            
                // The version of the Java virtual machine (fixed value)
            case "java.version":
                return "1.8.0";
                
                // The version of the JVM (full)
            case "java.vm.version":
                return RuntimeShelf.vmDescription(
                    VMDescriptionType.VM_VERSION);
                
                // The name of the JVM
            case "java.vm.name":
                return RuntimeShelf.vmDescription(
                    VMDescriptionType.VM_NAME);
                
                // The vendor of the JVM
            case "java.vm.vendor":
                return RuntimeShelf.vmDescription(
                    VMDescriptionType.VM_VENDOR);
            
                // The e-mail of the JVM
            case "java.vm.vendor.email":
                return RuntimeShelf.vmDescription(
                    VMDescriptionType.VM_EMAIL);
            
                // The URL of the JVM
            case "java.vm.vendor.url":
                return RuntimeShelf.vmDescription(
                    VMDescriptionType.VM_URL);
                
                // The vendor of the class libraries
            case "java.vendor":
                return "Stephanie Gawroriski";
                
                // Non-standard e-mail address
            case "java.vendor.email":
                return "xerthesquirrel@gmail.com";
                
                // The URL to the virtual machine's site
            case "java.vendor.url":
                return "http://squirreljme.cc/";
                
                // The name of the runtime library
            case "java.runtime.name":
                return "SquirrelJME";
                
                // The version of the run-time
            case "java.runtime.version":
                return SquirrelJME.RUNTIME_VERSION;
                
                // End of line character
            case "line.separator":
                return LineEndingUtils.toString(RuntimeShelf.lineEnding());
                
                // The current configuration, must be set!
            case "microedition.configuration":
                try
                {
                    Class<?> file = Class.forName(
                        "java.nio.FileSystem");
                    if (file == null)
                        return "CLDC-1.8-Compact";
                    return "CLDC-1.8";
                }
                catch (ClassNotFoundException e)
                {
                    return "CLDC-1.8-Compact";
                }
                
                // The current encoding
            case "microedition.encoding":
                return CodecFactory.toString(RuntimeShelf.encoding());
                
                // The current locale, must be set!
            case "microedition.locale":
                return DefaultLocale.toString(RuntimeShelf.locale());
                
                // The current platform
            case "microedition.platform":
                // Allow this to be overridden by the user
                String platformOverride = RuntimeShelf.systemProperty(__k);
                if (platformOverride != null)
                    return platformOverride;
            
                // Try to use a specific platform
                int phoneModel = RuntimeShelf.phoneModel();
                if (phoneModel != PhoneModelType.GENERIC)
                    return SquirrelJME.platform(phoneModel);
                return SquirrelJME.MICROEDITION_PLATFORM;
                
                // The operating system architecture
            case "os.arch":
                return RuntimeShelf.vmDescription(
                    VMDescriptionType.OS_ARCH);
                
                // The operating system name
            case "os.name":
                return RuntimeShelf.vmDescription(
                    VMDescriptionType.OS_NAME);
                
                // The operating system name
            case "os.version":
                return RuntimeShelf.vmDescription(
                    VMDescriptionType.OS_VERSION);
                
                // Unknown, use system call
            default:
                return RuntimeShelf.systemProperty(__k);
        }
    }
    
    /**
     * Obtains the specified system property and if it has not been set then
     * the default value will be returned instead.
     *
     * @param __k The system property to get.
     * @param __d If the system property is not set (returns {@code null}
     * then this value will be returned instead.
     * @return The system property.
     * @throws IllegalArgumentException If the requested system property is
     * not valid (it is blank).
     * @throws NullPointerException If no key was specified.
     * @throws SecurityException If obtaining the given system property is
     * not permitted.
     * @since 2017/08/13
     */
    @Api
    public static String getProperty(String __k, String __d)
        throws IllegalArgumentException, NullPointerException,
            SecurityException
    {
        // Get it
        String rv = System.getProperty(__k);
        
        // If not set, return the default, otherwise the read value
        if (rv == null)
            return __d;
        return rv;
    }
    
    /**
     * Returns the current security manager that is in use.
     *
     * @return The current security manager in use.
     * @since 2018/09/18
     */
    @Api
    public static SecurityManager getSecurityManager()
    {
        // Lock because it is managed by that class for checking
        synchronized (SecurityManager.class)
        {
            return SecurityManager._CURRENT_MANAGER;
        }
    }
    
    /**
     * This returns the identity hash code of the object. The identity hash
     * code is randomly given by the virtual machine to an object. There is
     * no definition on how the value is to be derived. It may be a unique
     * object ID, or it may be a memory address. Two objects may also share the
     * same identity hash code.
     *
     * @param __o The input object to get the hash code for.
     * @return The hash code which was given by the virtual machine, if the
     * input is {@code null} then {@code 0} is returned.
     * @since 2015/11/09
     */
    @Api
    public static int identityHashCode(Object __o)
    {
        // If null, this is zero
        if (__o == null)
            return 0;
        
        return ObjectShelf.identityHashCode(__o);
    }
    
    /**
     * Returns the number of nanoseconds which have passed from a previously
     * unspecified time. The returned value might not be accurate to the
     * nanosecond. This clock is monotonic and does not suffer from time
     * shifts caused by clock adjustments.
     *
     * The value returned here is specific to the current virtual machine and
     * cannot be used elsewhere. Even two virtual machines running on the
     * same system can use completely different values.
     *
     * After about 292 years (2 to the 63rd power nanoseconds) using signed
     * comparison to calculate the amount of time that has passed will no
     * longer function properly. For extremely long running processes it is
     * recommended to treat the values as unsigned to extend past this limit
     * or handle the overflow of the time value to represent any time
     * quantity, this of course requires that time be checked every 292 or
     * 584 years).
     *
     * @return The number of nanoseconds which have passed.
     * @since 2016/06/16
     */
    @Api
    public static long nanoTime()
    {
        // Returns the current monotonic clock time
        return RuntimeShelf.nanoTime();
    }
    
    /**
     * Sets the new destination for standard error.
     *
     * Note that the {@link System#err} field is not changed, a wrapper class
     * is used to prevent reflective abuse.
     *
     * @param __a The new stream to use when outputting values.
     * @throws NullPointerException On null arguments.
     * @throws SecurityException If the current program lacks the given
     * permission to set the stream.
     * @since 2016/03/17
     */
    @Api
    public static void setErr(PrintStream __a)
        throws NullPointerException
    {
        // Check
        if (__a == null)
            throw new NullPointerException("NARG");
        
        // Not allowed to do this?
        System.getSecurityManager().checkPermission(new RuntimePermission("setIO"));
        
        // Use a wrapped class to prevent final abuse.
        ((__CanSetPrintStream__)System.err).__set(__a);
    }
    
    /**
     * Sets the new destination for standard output.
     *
     * Note that the {@link System#out} field is not changed, a wrapper class
     * is used to prevent reflective abuse.
     *
     * @param __a The new stream to use when outputting values.
     * @throws NullPointerException On null arguments.
     * @throws SecurityException If the current program lacks the given
     * permission to set the stream.
     * @since 2016/03/17
     */
    @Api
    public static void setOut(PrintStream __a)
        throws NullPointerException
    {
        // Check
        if (__a == null)
            throw new NullPointerException("NARG");
        
        // Not allowed to do this?
        System.getSecurityManager().checkPermission(
            new RuntimePermission("setIO"));
        
        // Use a wrapped class to prevent final abuse.
        ((__CanSetPrintStream__)System.out).__set(__a);
    }
}