package com.mattc.autotyper.util; import com.google.common.base.Preconditions; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.ImmutableSet; import java.util.Set; /** * Small Utility to Determine OS and OS Architecture based on an enumeration of * supported OS's and Architectures. Plus other utilities. * * @author Matthew Crocco */ public final class OS { /** * Convenience class for dealing with conversions to and of different units of * memory storage. Includes SI Units (Kilobytes, Megabytes, Gigabytes) and non-SI * Units (Kibibytes, Mebibytes, Gibibytes) assisted by the common base unit they * share, the Byte. <br /> * <br /> * Currently this class only deals with 64-bit Integer Values (Although 32-bit * Integers can be used). * * @author Matthew */ public enum MemoryUnit { BYTES("B") { @Override public long toBytes(long amt) { return amt; } @Override public long toKilobytes(long amt) { return amt / C_KB; } @Override public long toKibibytes(long amt) { return amt / C_KiB; } @Override public long toMegabytes(long amt) { return amt / C_MB; } @Override public long toMebibytes(long amt) { return amt / C_MiB; } @Override public long toGigabytes(long amt) { return amt / C_GB; } @Override public long toGibibytes(long amt) { return amt / C_GiB; } @Override public long convert(long amt, MemoryUnit source) { return source.toBytes(amt); } }, KILOBYTES("KB") { @Override public long toBytes(long amt) { return amt * C_KB; } @Override public long toKilobytes(long amt) { return amt; } @Override public long toKibibytes(long amt) { return (amt * C_KB) / C_KiB; } @Override public long toMegabytes(long amt) { return amt / C_KB; } @Override public long toMebibytes(long amt) { return (amt * C_KB) / C_MiB; } @Override public long toGigabytes(long amt) { return amt / C_MB; } @Override public long toGibibytes(long amt) { return (amt * C_KB) / C_GiB; } @Override public long convert(long amt, MemoryUnit source) { return source.toKilobytes(amt); } }, KIBIBYTES("KiB") { @Override public long toBytes(long amt) { return amt * C_KiB; } @Override public long toKilobytes(long amt) { return (amt * C_KiB) / C_KB; } @Override public long toKibibytes(long amt) { return amt; } @Override public long toMegabytes(long amt) { return (amt * C_KiB) / C_MB; } @Override public long toMebibytes(long amt) { return amt / C_KiB; } @Override public long toGigabytes(long amt) { return (amt * C_KiB) / C_GB; } @Override public long toGibibytes(long amt) { return amt / C_MiB; } @Override public long convert(long amt, MemoryUnit source) { return source.toKibibytes(amt); } }, MEGABYTES("MB") { @Override public long toBytes(long amt) { return amt * C_MB; } @Override public long toKilobytes(long amt) { return amt * C_KB; } @Override public long toKibibytes(long amt) { return (amt * C_MB) / C_KiB; } @Override public long toMegabytes(long amt) { return amt; } @Override public long toMebibytes(long amt) { return (amt * C_MB) / C_MiB; } @Override public long toGigabytes(long amt) { return amt / C_KB; } @Override public long toGibibytes(long amt) { return (amt * C_MB) / C_GiB; } @Override public long convert(long amt, MemoryUnit source) { return source.toMegabytes(amt); } }, MEBIBYTES("MiB") { @Override public long toBytes(long amt) { return amt * C_MiB; } @Override public long toKilobytes(long amt) { return (amt * C_MiB) / C_KB; } @Override public long toKibibytes(long amt) { return amt * C_KiB; } @Override public long toMegabytes(long amt) { return (amt * C_MiB) / C_MB; } @Override public long toMebibytes(long amt) { return amt; } @Override public long toGigabytes(long amt) { return (amt * C_MiB) / C_GB; } @Override public long toGibibytes(long amt) { return amt / C_KiB; } @Override public long convert(long amt, MemoryUnit source) { return source.toMebibytes(amt); } }, GIGABYTES("GB") { @Override public long toBytes(long amt) { return amt * C_GB; } @Override public long toKilobytes(long amt) { return amt * C_MB; } @Override public long toKibibytes(long amt) { return (amt * C_GB) / C_KiB; } @Override public long toMegabytes(long amt) { return amt * C_KB; } @Override public long toMebibytes(long amt) { return (amt * C_GB) / C_MiB; } @Override public long toGigabytes(long amt) { return amt; } @Override public long toGibibytes(long amt) { return (amt * C_GB) / C_GiB; } @Override public long convert(long amt, MemoryUnit source) { return source.toGigabytes(amt); } }, GIBIBYTES("GiB") { @Override public long toBytes(long amt) { return amt * C_GiB; } @Override public long toKilobytes(long amt) { return (amt * C_GiB) / C_KB; } @Override public long toKibibytes(long amt) { return amt * C_MiB; } @Override public long toMegabytes(long amt) { return (amt * C_GiB) / C_MB; } @Override public long toMebibytes(long amt) { return amt * C_KiB; } @Override public long toGigabytes(long amt) { return (amt * C_GiB) / C_GB; } @Override public long toGibibytes(long amt) { return amt; } @Override public long convert(long amt, MemoryUnit source) { return source.toGibibytes(amt); } }; // SI Unit Memory Size Constants (Base 10) static final long C_KB = 1_000; // Bytes per Kilobyte static final long C_MB = C_KB * 1_000; // Bytes per Megabyte static final long C_GB = C_MB * 1_000; // Bytes per Gigabyte // Non-SI Unit Memory Size Constants (Base 2) static final long C_KiB = 1_024; // Bytes per Kibibyte static final long C_MiB = C_KiB * 1_024; // Bytes per Mebibyte static final long C_GiB = C_MiB * 1_024; // Bytes per Gibibyte private final String tag; MemoryUnit(String tag) { this.tag = tag; } /** * Convert to Bytes */ public abstract long toBytes(long amt); /** * Convert to Kilobytes (1000 Bytes or 10^3 Bytes) */ public abstract long toKilobytes(long amt); /** * Convert to Kibibytes (1024 Bytes or 2^10 Bytes) */ public abstract long toKibibytes(long amt); /** * Convert to Megabytes (1000 Kilobytes or 10^6 Bytes) */ public abstract long toMegabytes(long amt); /** * Convert to Mebibytes (1024 Kibibytes or 2^20 Bytes) */ public abstract long toMebibytes(long amt); /** * Convert to Gigabytes (1000 Megabytes or 10^9 Bytes) */ public abstract long toGigabytes(long amt); /** * Convert to Gibibytes (1024 Mebibytes or 2^30 Bytes) */ public abstract long toGibibytes(long amt); /** * Convert source unit to this unit. */ public abstract long convert(long amt, MemoryUnit source); /** * The appropriate tag for the Unit of Memory Storage, e.g., MB, MiB, GiB, * GB, etc. */ public String getTag() { return this.tag; } @Override public String toString() { return this.tag; } } /** * Enumeration of simple computer architectures, often referred to as the "bitness" of a system, i.e. * x86 (32-bit) and x64 (64-bit) architectures. */ public enum Bit { BIT_32("x86", "32-bit", 32), BIT_64("x64", "64-bit", 64); private final String tag; private final String mnemonic; private final int num; Bit(String tag, String mnemonic, int num) { this.tag = tag; this.mnemonic = mnemonic; this.num = num; } /** * Retrieve the "tag" for this bitness. e.g. 'x86' for 32-bit systems * * @return The associated bitness tag */ public String getTag() { return this.tag; } /** * Returns the numerical bitness. e.g. '32' for 32-bit systems * * @return Numerical Bitness Value */ public int getInt() { return this.num; } /** * Get the 'mnemonic' associated. e.g. '32-bit' for 32-bit systems. * * @return Bitness Mnemonic */ public String getMnemonic() { return this.mnemonic; } @Override public String toString() { return this.tag; } } /** * Microsoft Windows with 'exe' binary executables */ public static final OS WINDOWS = new OS(false, ".exe", "win", "windows"); /** * Non-specific Unix System with no definitive executable extension */ public static final OS UNIX = new OS(true, "", "lin", "linux", "nux"); /** * Apple Mac OSX with 'app' binary executables (packages) */ public static final OS MAC_OSX = new OS(true, ".app", "mac", "osx", "apple"); /** * Sun Microsystems' Solaris OS with no definitive executable extension */ public static final OS SOLARIS = new OS(true, "", "sunos", "solaris"); /** * A Placeholder for an Unsupported or Undefined OS, thus having an undefined executable extension */ public static final OS UNSUPPORTED = new OS(false, ""); /** * Name to OS and OS to Name Lookup Table [to mimic an enum] */ private static final BiMap<OS, String> nameLookup = HashBiMap.create(5); /** * Values Set, for mimicking {@link Enum Enum's} values() method */ private static final Set<OS> values = ImmutableSet.of(WINDOWS, MAC_OSX, SOLARIS, UNIX, UNSUPPORTED); private static final Runtime rt = Runtime.getRuntime(); public final String suffix; public final String[] aliases; private final boolean unix; static { nameLookup.put(WINDOWS, "Windows"); nameLookup.put(UNIX, "Unix"); nameLookup.put(MAC_OSX, "Mac OSX"); nameLookup.put(SOLARIS, "Solaris"); nameLookup.put(UNSUPPORTED, "Unsupported"); } private OS(boolean unixBased, String executable, String... aliases) { this.suffix = executable; this.aliases = aliases; this.unix = unixBased; } /** * Determines if the given alias is a valid alias for any supported OS'. If * the alias is not a registered alias or name of an OS then false is returned. * * @param alias Name Alias * @return True if valid OS, otherwise false. */ public boolean isValidAlias(String alias) { Preconditions.checkNotNull(alias); for (final String s : this.aliases) if (s.equalsIgnoreCase(alias)) return true; return nameLookup.containsValue(alias); } /** * Determines if this system is a Unix based system that may need unix-specific operations done such * as assigning POSIX File Permissions to files for access. * * @return True if unix based, false otherwise. */ public boolean isUnixBased() { return unix; } /** * Determines the desired OS from the given OS. If the alias is not a registered alias or name * of a supported OS then OS.UNSUPPORTED is returned. * * @param alias Name Alias * @return The Desired OS if supported, else OS.UNSUPPORTED */ public static OS forAlias(String alias) { Preconditions.checkNotNull(alias); for (final OS os : OS.values()) if (os.isValidAlias(alias)) return os; return OS.get(alias); } /** * Get this System OS or OS.UNSUPPORTED */ public static OS get() { final String name = System.getProperty("os.name").toLowerCase(); if (name.contains("win")) return WINDOWS; else if (name.contains("mac")) return MAC_OSX; else if (name.contains("sunos")) return SOLARIS; else if ((name.contains("nix")) || (name.contains("nux")) || (name.contains("nax")) || name.contains("aix")) return UNIX; else return UNSUPPORTED; } public static OS get(String name) { return nameLookup.inverse().getOrDefault(name, OS.UNIX); } /** * Get the intended Architecture of the JVM. This is not necessarily the * Computer's architecture. <br /> * A 32-bit JVM can be run on a 64-bit Computer. <br /> * <br /> * To get the Computer Architecture, use {@link #getArch()}. * * @return JVM Architecture (x86/x64) */ public static Bit getArchJVM() { final String arch = System.getProperty("os.arch"); if (!arch.contains("64")) return Bit.BIT_32; else return Bit.BIT_64; } /** * Get Architecture of the computer. This determines the ACTUAL Bitness of the * computer <br /> * this means that the COMPUTER may be 64 bit, but may be running a 32 bit JVM. * To get <br /> * the JVM Bitness, use {@link #getArchJVM()}. * * @return Actual Architecture of the Computer (x86 or x64) */ public static Bit getArch() { final String arch = System.getenv("PROCESSOR_ARCHITECTURE"); final String wow64 = System.getenv("PROCESSOR_ARCHITEW6432"); if (arch == null) return getArchJVM(); if ((arch.indexOf("64") > 0) || ((wow64 != null) && (wow64.indexOf("64") > 0))) return Bit.BIT_64; else return Bit.BIT_32; } public static int processorCount() { return rt.availableProcessors(); } public static long getTotalMemory(MemoryUnit units) { return units.convert(rt.totalMemory(), MemoryUnit.BYTES); } public static long getUsedMemory(MemoryUnit units) { return units.convert(rt.totalMemory() - rt.freeMemory(), MemoryUnit.BYTES); } public static long getFreeMemory(MemoryUnit units) { return units.convert(rt.freeMemory(), MemoryUnit.BYTES); } public static double getUsedMemoryToTotalMemory(MemoryUnit units) { final double used = getUsedMemory(units); final double total = getTotalMemory(units); return used / total; } public static Set<OS> values() { return values; } public String name() { return nameLookup.get(this); } @Override public String toString() { return String.format("%s %s [%s]", name(), getArch().getTag(), getArch().getMnemonic()); } @Override public int hashCode() { return super.hashCode(); } @Override public boolean equals(Object o) { return this == o; } }