OS tools converted to kotlin, updated file, common + web utilities.
This commit is contained in:
parent
e5478201ab
commit
edd5c4d2b1
@ -55,8 +55,8 @@ public class JnaClassUtils {
|
|||||||
|
|
||||||
private static final String libName;
|
private static final String libName;
|
||||||
static {
|
static {
|
||||||
if (OS.isMacOsX()) {
|
if (OS.INSTANCE.isMacOsX()) {
|
||||||
if (OS.javaVersion < 7) {
|
if (OS.INSTANCE.javaVersion < 7) {
|
||||||
libName = "JavaVM";
|
libName = "JavaVM";
|
||||||
} else {
|
} else {
|
||||||
String javaLocation = System.getProperty("java.home");
|
String javaLocation = System.getProperty("java.home");
|
||||||
@ -77,7 +77,7 @@ public class JnaClassUtils {
|
|||||||
Map options = new HashMap();
|
Map options = new HashMap();
|
||||||
options.put(Library.OPTION_ALLOW_OBJECTS, Boolean.TRUE);
|
options.put(Library.OPTION_ALLOW_OBJECTS, Boolean.TRUE);
|
||||||
|
|
||||||
if (OS.isWindows() && OS.is32bit()) {
|
if (OS.INSTANCE.isWindows() && OS.INSTANCE.is32bit()) {
|
||||||
options.put(Library.OPTION_FUNCTION_MAPPER, new StdCallFunctionMapper() {
|
options.put(Library.OPTION_FUNCTION_MAPPER, new StdCallFunctionMapper() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
|
@ -37,7 +37,7 @@ class JnaHelper {
|
|||||||
final Map<String, Object> options = new HashMap<String, Object>();
|
final Map<String, Object> options = new HashMap<String, Object>();
|
||||||
options.put(Library.OPTION_CLASSLOADER, clazz.getClassLoader());
|
options.put(Library.OPTION_CLASSLOADER, clazz.getClassLoader());
|
||||||
|
|
||||||
if (OS.isWindows()) {
|
if (OS.INSTANCE.isWindows()) {
|
||||||
Set<Map.Entry<String, Object>> entries = W32APIOptions.DEFAULT_OPTIONS.entrySet();
|
Set<Map.Entry<String, Object>> entries = W32APIOptions.DEFAULT_OPTIONS.entrySet();
|
||||||
for (Map.Entry<String, Object> entry : entries) {
|
for (Map.Entry<String, Object> entry : entries) {
|
||||||
options.put(entry.getKey(), entry.getValue());
|
options.put(entry.getKey(), entry.getValue());
|
||||||
|
@ -45,7 +45,7 @@ class AppIndicator {
|
|||||||
static {
|
static {
|
||||||
boolean _isLoaded = false;
|
boolean _isLoaded = false;
|
||||||
|
|
||||||
boolean shouldLoadAppIndicator = !(OS.isWindows() || OS.isMacOsX());
|
boolean shouldLoadAppIndicator = !(OS.INSTANCE.isWindows() || OS.INSTANCE.isMacOsX());
|
||||||
if (!shouldLoadAppIndicator) {
|
if (!shouldLoadAppIndicator) {
|
||||||
_isLoaded = true;
|
_isLoaded = true;
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ class GtkCheck {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// java 8 cannot load GTK3. But we can know if GTK was loaded yet or not
|
// java 8 cannot load GTK3. But we can know if GTK was loaded yet or not
|
||||||
if (OS.javaVersion <= 8) {
|
if (OS.INSTANCE.javaVersion <= 8) {
|
||||||
try {
|
try {
|
||||||
// Don't want to load the toolkit!!!
|
// Don't want to load the toolkit!!!
|
||||||
Class<?> toolkitClass = Class.forName("java.awt.Toolkit");
|
Class<?> toolkitClass = Class.forName("java.awt.Toolkit");
|
||||||
|
@ -88,7 +88,7 @@ class GtkLoader {
|
|||||||
int minor = 0;
|
int minor = 0;
|
||||||
int micro = 0;
|
int micro = 0;
|
||||||
|
|
||||||
boolean shouldLoadGtk = !(OS.isWindows() || OS.isMacOsX());
|
boolean shouldLoadGtk = !(OS.INSTANCE.isWindows() || OS.INSTANCE.isMacOsX());
|
||||||
if (!shouldLoadGtk) {
|
if (!shouldLoadGtk) {
|
||||||
_isLoaded = true;
|
_isLoaded = true;
|
||||||
}
|
}
|
||||||
|
@ -48,10 +48,10 @@ import dorkbox.util.MathUtil;
|
|||||||
public
|
public
|
||||||
class GtkTheme {
|
class GtkTheme {
|
||||||
/** Fallback for an unknown tray image size. */
|
/** Fallback for an unknown tray image size. */
|
||||||
public static volatile int TRAY_IMAGE_SIZE_FALLBACK = OS.getInt(GtkTheme.class.getCanonicalName() + ".TRAY_IMAGE_SIZE_FALLBACK", 24);
|
public static volatile int TRAY_IMAGE_SIZE_FALLBACK = OS.INSTANCE.getInt(GtkTheme.class.getCanonicalName() + ".TRAY_IMAGE_SIZE_FALLBACK", 24);
|
||||||
|
|
||||||
/** Fallback for an unknown tray menu image size. */
|
/** Fallback for an unknown tray menu image size. */
|
||||||
public static volatile int TRAY_MENU_IMAGE_SIZE_FALLBACK = OS.getInt(GtkTheme.class.getCanonicalName() + ".TRAY_MENU_IMAGE_SIZE_FALLBACK", 16);
|
public static volatile int TRAY_MENU_IMAGE_SIZE_FALLBACK = OS.INSTANCE.getInt(GtkTheme.class.getCanonicalName() + ".TRAY_MENU_IMAGE_SIZE_FALLBACK", 16);
|
||||||
|
|
||||||
public static
|
public static
|
||||||
Rectangle getPixelTextHeight(String text) {
|
Rectangle getPixelTextHeight(String text) {
|
||||||
@ -279,7 +279,7 @@ class GtkTheme {
|
|||||||
try {
|
try {
|
||||||
File customSettings = new File("/usr/bin/startkde-custom");
|
File customSettings = new File("/usr/bin/startkde-custom");
|
||||||
if (customSettings.canRead()) {
|
if (customSettings.canRead()) {
|
||||||
List<String> lines = FileUtil.readLines(customSettings);
|
List<String> lines = FileUtil.INSTANCE.readLines(customSettings);
|
||||||
for (String line : lines) {
|
for (String line : lines) {
|
||||||
String str = "export GDK_SCALE=";
|
String str = "export GDK_SCALE=";
|
||||||
int i = line.indexOf(str);
|
int i = line.indexOf(str);
|
||||||
@ -327,7 +327,7 @@ class GtkTheme {
|
|||||||
|
|
||||||
File mainFile = new File("/usr/share/plasma/plasmoids/org.kde.plasma.private.systemtray/contents/config/main.xml");
|
File mainFile = new File("/usr/share/plasma/plasmoids/org.kde.plasma.private.systemtray/contents/config/main.xml");
|
||||||
if (mainFile.canRead()) {
|
if (mainFile.canRead()) {
|
||||||
List<String> lines = FileUtil.readLines(mainFile);
|
List<String> lines = FileUtil.INSTANCE.readLines(mainFile);
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
int index;
|
int index;
|
||||||
for (final String line : lines) {
|
for (final String line : lines) {
|
||||||
@ -394,7 +394,7 @@ class GtkTheme {
|
|||||||
if (env == OSUtil.DesktopEnv.Env.XFCE) {
|
if (env == OSUtil.DesktopEnv.Env.XFCE) {
|
||||||
// xfce is easy, because it's not a GTK setting for the size (xfce notification area maximum icon size)
|
// xfce is easy, because it's not a GTK setting for the size (xfce notification area maximum icon size)
|
||||||
String properties = OSUtil.DesktopEnv.queryXfce("xfce4-panel", null);
|
String properties = OSUtil.DesktopEnv.queryXfce("xfce4-panel", null);
|
||||||
String[] propertiesAsList = properties.split(OS.LINE_SEPARATOR);
|
String[] propertiesAsList = properties.split(OS.INSTANCE.LINE_SEPARATOR);
|
||||||
for (String prop : propertiesAsList) {
|
for (String prop : propertiesAsList) {
|
||||||
if (prop.startsWith("/plugins/") && prop.endsWith("/size-max")) {
|
if (prop.startsWith("/plugins/") && prop.endsWith("/size-max")) {
|
||||||
// this is the property we are looking for (we just don't know which panel it's on)
|
// this is the property we are looking for (we just don't know which panel it's on)
|
||||||
|
@ -37,7 +37,7 @@ import dorkbox.os.OS;
|
|||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
public
|
public
|
||||||
interface User32 {
|
interface User32 {
|
||||||
User32 User32 = OS.is64bit() ? new User32_64() : new User32_32();
|
User32 User32 = OS.INSTANCE.is64bit() ? new User32_64() : new User32_32();
|
||||||
|
|
||||||
int GWL_WNDPROC = -4;
|
int GWL_WNDPROC = -4;
|
||||||
|
|
||||||
|
@ -1,505 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010 dorkbox, llc
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package dorkbox.os;
|
|
||||||
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.security.AccessController;
|
|
||||||
import java.security.PrivilegedAction;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.TimeZone;
|
|
||||||
|
|
||||||
public
|
|
||||||
class OS {
|
|
||||||
public static final String LINE_SEPARATOR = getProperty("line.separator", "\n"); // make the default unix
|
|
||||||
public static final String LINE_SEPARATOR_UNIX = "\n";
|
|
||||||
public static final String LINE_SEPARATOR_WINDOWS = "\r\n";
|
|
||||||
|
|
||||||
public static final Charset US_ASCII = StandardCharsets.US_ASCII;
|
|
||||||
public static final Charset UTF_8 = StandardCharsets.UTF_8;
|
|
||||||
|
|
||||||
public static final File TEMP_DIR = new File(getProperty("java.io.tmpdir", "temp"));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The currently running MAJOR java version as a NUMBER. For example, "Java version 1.7u45", and converts it into 7, uses JEP 223 for java > 9
|
|
||||||
*/
|
|
||||||
public static final int javaVersion = _getJavaVersion();
|
|
||||||
|
|
||||||
|
|
||||||
private static final OSType osType;
|
|
||||||
private static final String originalTimeZone = TimeZone.getDefault().getID();
|
|
||||||
|
|
||||||
static {
|
|
||||||
if (!TEMP_DIR.isDirectory()) {
|
|
||||||
// create the temp dir if necessary because the TEMP dir doesn't exist.
|
|
||||||
TEMP_DIR.mkdirs();
|
|
||||||
}
|
|
||||||
|
|
||||||
String osName = getProperty("os.name", "");
|
|
||||||
String osArch = getProperty("os.arch", "");
|
|
||||||
|
|
||||||
if (osName != null && osArch != null) {
|
|
||||||
osName = osName.toLowerCase(Locale.US);
|
|
||||||
osArch = osArch.toLowerCase(Locale.US);
|
|
||||||
|
|
||||||
if (osName.startsWith("linux")) {
|
|
||||||
// best way to determine if it's android.
|
|
||||||
// Sometimes java binaries include Android classes on the classpath, even if it isn't actually Android, so we check the VM
|
|
||||||
|
|
||||||
|
|
||||||
String value = getProperty("java.vm.name", "");
|
|
||||||
boolean isAndroid = "Dalvik".equals(value);
|
|
||||||
if (isAndroid) {
|
|
||||||
// android check from https://stackoverflow.com/questions/14859954/android-os-arch-output-for-arm-mips-x86
|
|
||||||
if (osArch.equals("armeabi")) {
|
|
||||||
// really old/low-end non-hf 32bit cpu
|
|
||||||
osType = OSType.AndroidArm56;
|
|
||||||
}
|
|
||||||
else if (osArch.equals("armeabi-v7a")) {
|
|
||||||
// 32bit hf cpu
|
|
||||||
osType = OSType.AndroidArm7;
|
|
||||||
}
|
|
||||||
else if (osArch.equals("arm64-v8a")) {
|
|
||||||
// 64bit hf cpu
|
|
||||||
osType = OSType.AndroidArm8;
|
|
||||||
}
|
|
||||||
else if (osArch.equals("x86")) {
|
|
||||||
// 32bit x86 (usually emulator)
|
|
||||||
osType = OSType.AndroidX86;
|
|
||||||
}
|
|
||||||
else if (osArch.equals("x86_64")) {
|
|
||||||
// 64bit x86 (usually emulator)
|
|
||||||
osType = OSType.AndroidX86_64;
|
|
||||||
}
|
|
||||||
else if (osArch.equals("mips")) {
|
|
||||||
// 32bit mips
|
|
||||||
osType = OSType.AndroidMips;
|
|
||||||
}
|
|
||||||
else if (osArch.equals("mips64")) {
|
|
||||||
// 64bit mips
|
|
||||||
osType = OSType.AndroidMips64;
|
|
||||||
} else {
|
|
||||||
// who knows?
|
|
||||||
osType = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// normal linux 32/64/arm32/arm64
|
|
||||||
if ("amd64".equals(osArch)) {
|
|
||||||
osType = OSType.Linux64;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (osArch.startsWith("arm")) {
|
|
||||||
if (osArch.contains("v8")) {
|
|
||||||
osType = OSType.LinuxArm64;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
osType = OSType.LinuxArm32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
osType = OSType.Linux32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (osName.startsWith("windows")) {
|
|
||||||
if ("amd64".equals(osArch)) {
|
|
||||||
osType = OSType.Windows64;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
osType = OSType.Windows32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (osName.startsWith("mac") || osName.startsWith("darwin")) {
|
|
||||||
if ("x86_64".equals(osArch)) {
|
|
||||||
osType = OSType.MacOsX64;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
osType = OSType.MacOsX32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (osName.startsWith("freebsd") || osName.contains("nix") || osName.contains("nux") || osName.startsWith("aix")) {
|
|
||||||
if ("x86".equals(osArch) || "i386".equals(osArch)) {
|
|
||||||
osType = OSType.Unix32;
|
|
||||||
}
|
|
||||||
else if ("arm".equals(osArch)) {
|
|
||||||
osType = OSType.UnixArm;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
osType = OSType.Unix64;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (osName.startsWith("solaris") || osName.startsWith("sunos")) {
|
|
||||||
osType = OSType.Solaris;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
osType = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
osType = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* By default, the timer resolution on Windows ARE NOT high-resolution (16ms vs 1ms)
|
|
||||||
*
|
|
||||||
* 'Thread.sleep(1)' will not really sleep for 1ms, but will really sleep for ~16ms. This long-running sleep will trick Windows
|
|
||||||
* into using higher resolution timers.
|
|
||||||
*
|
|
||||||
* See: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6435126
|
|
||||||
*/
|
|
||||||
if (osType == null || osType.isWindows()) {
|
|
||||||
// only necessary on windows
|
|
||||||
Thread timerAccuracyThread = new Thread(new Runnable() {
|
|
||||||
@SuppressWarnings("BusyWait")
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(Long.MAX_VALUE);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, "FixWindowsHighResTimer");
|
|
||||||
timerAccuracyThread.setDaemon(true);
|
|
||||||
timerAccuracyThread.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the System Property in a safe way for a given property, or null if it does not exist.
|
|
||||||
*/
|
|
||||||
public static
|
|
||||||
String getProperty(final String property) {
|
|
||||||
return getProperty(property, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the System Property in a safe way for a given property, and if null - returns the specified default value.
|
|
||||||
*/
|
|
||||||
public static
|
|
||||||
String getProperty(final String property, final String defaultValue) {
|
|
||||||
try {
|
|
||||||
if (System.getSecurityManager() == null) {
|
|
||||||
return System.getProperty(property, defaultValue);
|
|
||||||
} else {
|
|
||||||
return AccessController.doPrivileged(new PrivilegedAction<String>() {
|
|
||||||
@Override
|
|
||||||
public String run() {
|
|
||||||
return System.getProperty(property, defaultValue);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the value of the Java system property with the specified {@code property}, while falling back to the
|
|
||||||
* specified default value if the property access fails.
|
|
||||||
*/
|
|
||||||
public static boolean getBoolean(String property, boolean defaultValue) {
|
|
||||||
String value = getProperty(property);
|
|
||||||
if (value == null) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = value.trim().toLowerCase();
|
|
||||||
if (value.isEmpty()) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("false".equals(value) || "no".equals(value) || "0".equals(value)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("true".equals(value) || "yes".equals(value) || "1".equals(value)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the value of the Java system property with the specified {@code property}, while falling back to the
|
|
||||||
* specified default value if the property access fails.
|
|
||||||
*/
|
|
||||||
public static int getInt(String property, int defaultValue) {
|
|
||||||
String value = getProperty(property);
|
|
||||||
if (value == null) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = value.trim();
|
|
||||||
try {
|
|
||||||
return Integer.parseInt(value);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the value of the Java system property with the specified {@code property}, while falling back to the
|
|
||||||
* specified default value if the property access fails.
|
|
||||||
*/
|
|
||||||
public static long getLong(String property, long defaultValue) {
|
|
||||||
String value = getProperty(property);
|
|
||||||
if (value == null) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = value.trim();
|
|
||||||
try {
|
|
||||||
return Long.parseLong(value);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the value of the Java system property with the specified {@code property}, while falling back to the
|
|
||||||
* specified default value if the property access fails.
|
|
||||||
*/
|
|
||||||
public static float getFloat(String property, float defaultValue) {
|
|
||||||
String value = getProperty(property);
|
|
||||||
if (value == null) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = value.trim();
|
|
||||||
try {
|
|
||||||
return Float.parseFloat(value);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the value of the Java system property with the specified {@code property}, while falling back to the
|
|
||||||
* specified default value if the property access fails.
|
|
||||||
*/
|
|
||||||
public static double getDouble(String property, double defaultValue) {
|
|
||||||
String value = getProperty(property);
|
|
||||||
if (value == null) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = value.trim();
|
|
||||||
try {
|
|
||||||
return Double.parseDouble(value);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static
|
|
||||||
Color getColor(final String property, final Color defaultValue) {
|
|
||||||
String value = getProperty(property);
|
|
||||||
if (value == null) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = value.trim();
|
|
||||||
try {
|
|
||||||
return Color.decode(value);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the OS Type that is running on this machine
|
|
||||||
*/
|
|
||||||
public static
|
|
||||||
OSType get() {
|
|
||||||
return osType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static
|
|
||||||
boolean is64bit() {
|
|
||||||
return osType.is64bit();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static
|
|
||||||
boolean is32bit() {
|
|
||||||
return osType.is32bit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true if this is a "standard" x86/x64 architecture (intel/amd/etc) processor.
|
|
||||||
*/
|
|
||||||
public static
|
|
||||||
boolean isX86() {
|
|
||||||
return osType.isX86();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static
|
|
||||||
boolean isMips() {
|
|
||||||
return osType.isMips();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static
|
|
||||||
boolean isArm() {
|
|
||||||
return osType.isArm();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static
|
|
||||||
boolean isLinux() {
|
|
||||||
return osType.isLinux();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static
|
|
||||||
boolean isUnix() {
|
|
||||||
return osType.isUnix();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static
|
|
||||||
boolean isSolaris() {
|
|
||||||
return osType.isSolaris();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static
|
|
||||||
boolean isWindows() {
|
|
||||||
return osType.isWindows();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static
|
|
||||||
boolean isMacOsX() {
|
|
||||||
return osType.isMacOsX();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static
|
|
||||||
boolean isAndroid() {
|
|
||||||
return osType.isAndroid();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the currently running MAJOR java version as a NUMBER. For example, "Java version 1.7u45", and converts it into 7, uses JEP 223 for java > 9
|
|
||||||
*/
|
|
||||||
private static
|
|
||||||
int _getJavaVersion() {
|
|
||||||
// this should never be a problem, but just in case
|
|
||||||
String fullJavaVersion = getProperty("java.version", "");
|
|
||||||
|
|
||||||
|
|
||||||
if (fullJavaVersion.startsWith("1.")) {
|
|
||||||
switch (fullJavaVersion.charAt(2)) {
|
|
||||||
case '4':
|
|
||||||
return 4;
|
|
||||||
case '5':
|
|
||||||
return 5;
|
|
||||||
case '6':
|
|
||||||
return 6;
|
|
||||||
case '7':
|
|
||||||
return 7;
|
|
||||||
case '8':
|
|
||||||
return 8;
|
|
||||||
case '9':
|
|
||||||
return 9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// We are >= java 10, use JEP 223 to get the version (early releases of 9 might not have JEP 223, so 10 is guaranteed to have it)
|
|
||||||
fullJavaVersion = getProperty("java.specification.version", "10");
|
|
||||||
|
|
||||||
try {
|
|
||||||
// it will ALWAYS be the major release version as an integer. See http://openjdk.java.net/jeps/223
|
|
||||||
return Integer.parseInt(fullJavaVersion);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// the last valid guess we have, since the current Java implementation, whatever it is, decided not to cooperate with JEP 223.
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set our system to UTC time zone. Retrieve the <b>original</b> time zone via {@link #getOriginalTimeZone()}
|
|
||||||
*/
|
|
||||||
public static
|
|
||||||
void setUTC() {
|
|
||||||
// have to set our default timezone to UTC. EVERYTHING will be UTC, and if we want local, we must explicitly ask for it.
|
|
||||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the *ORIGINAL* system time zone, before (*IF*) it was changed to UTC
|
|
||||||
*/
|
|
||||||
public static
|
|
||||||
String getOriginalTimeZone() {
|
|
||||||
return originalTimeZone;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the optimum number of threads for a given task. Makes certain not to take ALL the threads, always returns at least one
|
|
||||||
* thread.
|
|
||||||
*/
|
|
||||||
public static
|
|
||||||
int getOptimumNumberOfThreads() {
|
|
||||||
return Math.max(Runtime.getRuntime()
|
|
||||||
.availableProcessors() - 2, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the first line of the exception message from 'throwable', or the type if there was no message.
|
|
||||||
*/
|
|
||||||
public static
|
|
||||||
String getExceptionMessage(final Throwable throwable) {
|
|
||||||
String message = throwable.getMessage();
|
|
||||||
if (message != null) {
|
|
||||||
int index = message.indexOf(OS.LINE_SEPARATOR);
|
|
||||||
if (index > -1) {
|
|
||||||
message = message.substring(0, index);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
message = throwable.getClass()
|
|
||||||
.getSimpleName();
|
|
||||||
}
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final
|
|
||||||
Object clone() throws CloneNotSupportedException {
|
|
||||||
throw new CloneNotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public final
|
|
||||||
void writeObject(ObjectOutputStream out) throws java.io.IOException {
|
|
||||||
throw new java.io.NotSerializableException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public final
|
|
||||||
void readObject(ObjectInputStream in) throws java.io.IOException {
|
|
||||||
throw new java.io.NotSerializableException();
|
|
||||||
}
|
|
||||||
}
|
|
379
src/dorkbox/os/OS.kt
Normal file
379
src/dorkbox/os/OS.kt
Normal file
@ -0,0 +1,379 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 dorkbox, llc
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
@file:Suppress("unused")
|
||||||
|
|
||||||
|
package dorkbox.os
|
||||||
|
|
||||||
|
import java.awt.Color
|
||||||
|
import java.io.File
|
||||||
|
import java.security.AccessController
|
||||||
|
import java.security.PrivilegedAction
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object OS {
|
||||||
|
// make the default unix
|
||||||
|
@kotlin.jvm.JvmField
|
||||||
|
val LINE_SEPARATOR = getProperty("line.separator", "\n")!!
|
||||||
|
|
||||||
|
const val LINE_SEPARATOR_UNIX = "\n"
|
||||||
|
const val LINE_SEPARATOR_WINDOWS = "\r\n"
|
||||||
|
|
||||||
|
@kotlin.jvm.JvmField
|
||||||
|
val TEMP_DIR = File(getProperty("java.io.tmpdir", "temp")!!).absoluteFile
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The currently running MAJOR java version as a NUMBER. For example, "Java version 1.7u45", and converts it into 7, uses JEP 223 for java > 9
|
||||||
|
*/
|
||||||
|
@kotlin.jvm.JvmField
|
||||||
|
val javaVersion = _getJavaVersion()
|
||||||
|
private var osType: OSType? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the *ORIGINAL* system time zone, before (*IF*) it was changed to UTC
|
||||||
|
*/
|
||||||
|
val originalTimeZone = TimeZone.getDefault().id
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (!TEMP_DIR.isDirectory) {
|
||||||
|
// create the temp dir if necessary because the TEMP dir doesn't exist.
|
||||||
|
TEMP_DIR.mkdirs()
|
||||||
|
}
|
||||||
|
var osName = getProperty("os.name", "")!!
|
||||||
|
var osArch = getProperty("os.arch", "")!!
|
||||||
|
|
||||||
|
osName = osName.lowercase()
|
||||||
|
osArch = osArch.lowercase()
|
||||||
|
if (osName.startsWith("linux")) {
|
||||||
|
// best way to determine if it's android.
|
||||||
|
// Sometimes java binaries include Android classes on the classpath, even if it isn't actually Android, so we check the VM
|
||||||
|
val value = getProperty("java.vm.name", "")
|
||||||
|
val isAndroid = "Dalvik" == value
|
||||||
|
if (isAndroid) {
|
||||||
|
// android check from https://stackoverflow.com/questions/14859954/android-os-arch-output-for-arm-mips-x86
|
||||||
|
when (osArch) {
|
||||||
|
"armeabi" -> {
|
||||||
|
// really old/low-end non-hf 32bit cpu
|
||||||
|
osType = OSType.AndroidArm56
|
||||||
|
}
|
||||||
|
"armeabi-v7a" -> {
|
||||||
|
// 32bit hf cpu
|
||||||
|
osType = OSType.AndroidArm7
|
||||||
|
}
|
||||||
|
"arm64-v8a" -> {
|
||||||
|
// 64bit hf cpu
|
||||||
|
osType = OSType.AndroidArm8
|
||||||
|
}
|
||||||
|
"x86" -> {
|
||||||
|
// 32bit x86 (usually emulator)
|
||||||
|
osType = OSType.AndroidX86
|
||||||
|
}
|
||||||
|
"x86_64" -> {
|
||||||
|
// 64bit x86 (usually emulator)
|
||||||
|
osType = OSType.AndroidX86_64
|
||||||
|
}
|
||||||
|
"mips" -> {
|
||||||
|
// 32bit mips
|
||||||
|
osType = OSType.AndroidMips
|
||||||
|
}
|
||||||
|
"mips64" -> {
|
||||||
|
// 64bit mips
|
||||||
|
osType = OSType.AndroidMips64
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// who knows?
|
||||||
|
osType = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
when {
|
||||||
|
osArch == "amd64" -> {
|
||||||
|
// normal linux 32/64/arm32/arm64
|
||||||
|
osType = OSType.Linux64
|
||||||
|
}
|
||||||
|
osArch.startsWith("arm") -> {
|
||||||
|
if (osArch.contains("v8")) {
|
||||||
|
OSType.LinuxArm64
|
||||||
|
} else {
|
||||||
|
OSType.LinuxArm32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
OSType.Linux32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (osName.startsWith("windows")) {
|
||||||
|
osType = if ("amd64" == osArch) {
|
||||||
|
OSType.Windows64
|
||||||
|
} else {
|
||||||
|
OSType.Windows32
|
||||||
|
}
|
||||||
|
} else if (osName.startsWith("mac") || osName.startsWith("darwin")) {
|
||||||
|
osType = if ("x86_64" == osArch) {
|
||||||
|
OSType.MacOsX64
|
||||||
|
} else {
|
||||||
|
OSType.MacOsX32
|
||||||
|
}
|
||||||
|
} else if (osName.startsWith("freebsd") || osName.contains("nix") || osName.contains("nux") || osName.startsWith(
|
||||||
|
"aix"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
osType = when (osArch) {
|
||||||
|
"x86", "i386" -> {
|
||||||
|
OSType.Unix32
|
||||||
|
}
|
||||||
|
"arm" -> {
|
||||||
|
OSType.UnixArm
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
OSType.Unix64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (osName.startsWith("solaris") || osName.startsWith("sunos")) {
|
||||||
|
osType = OSType.Solaris
|
||||||
|
} else {
|
||||||
|
osType = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* By default, the timer resolution on Windows ARE NOT high-resolution (16ms vs 1ms)
|
||||||
|
*
|
||||||
|
* 'Thread.sleep(1)' will not really sleep for 1ms, but will really sleep for ~16ms. This long-running sleep will trick Windows
|
||||||
|
* into using higher resolution timers.
|
||||||
|
*
|
||||||
|
* See: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6435126
|
||||||
|
*/
|
||||||
|
val osType_ = osType
|
||||||
|
if (osType_ == null || osType_.isWindows) {
|
||||||
|
// only necessary on windows
|
||||||
|
val timerAccuracyThread = Thread(
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(Long.MAX_VALUE)
|
||||||
|
} catch (ignored: Exception) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, "FixWindowsHighResTimer")
|
||||||
|
timerAccuracyThread.isDaemon = true
|
||||||
|
timerAccuracyThread.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the System Property in a safe way for a given property, or null if it does not exist.
|
||||||
|
*/
|
||||||
|
fun getProperty(property: String): String? {
|
||||||
|
return getProperty(property, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the System Property in a safe way for a given property, and if null - returns the specified default value.
|
||||||
|
*/
|
||||||
|
fun getProperty(property: String, defaultValue: String?): String? {
|
||||||
|
return try {
|
||||||
|
if (System.getSecurityManager() == null) {
|
||||||
|
System.getProperty(property, defaultValue)
|
||||||
|
} else {
|
||||||
|
AccessController.doPrivileged(PrivilegedAction { System.getProperty(property, defaultValue) })
|
||||||
|
}
|
||||||
|
} catch (ignored: Exception) {
|
||||||
|
defaultValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the value of the Java system property with the specified `property`, while falling back to the
|
||||||
|
* specified default value if the property access fails.
|
||||||
|
*/
|
||||||
|
fun getBoolean(property: String, defaultValue: Boolean): Boolean {
|
||||||
|
var value = getProperty(property) ?: return defaultValue
|
||||||
|
value = value.trim { it <= ' ' }.lowercase(Locale.getDefault())
|
||||||
|
if (value.isEmpty()) {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
if ("false" == value || "no" == value || "0" == value) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return if ("true" == value || "yes" == value || "1" == value) {
|
||||||
|
true
|
||||||
|
} else defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the value of the Java system property with the specified `property`, while falling back to the
|
||||||
|
* specified default value if the property access fails.
|
||||||
|
*/
|
||||||
|
fun getInt(property: String, defaultValue: Int): Int {
|
||||||
|
var value = getProperty(property) ?: return defaultValue
|
||||||
|
value = value.trim { it <= ' ' }
|
||||||
|
try {
|
||||||
|
return value.toInt()
|
||||||
|
} catch (ignored: Exception) {
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the value of the Java system property with the specified `property`, while falling back to the
|
||||||
|
* specified default value if the property access fails.
|
||||||
|
*/
|
||||||
|
fun getLong(property: String, defaultValue: Long): Long {
|
||||||
|
var value = getProperty(property) ?: return defaultValue
|
||||||
|
value = value.trim { it <= ' ' }
|
||||||
|
try {
|
||||||
|
return value.toLong()
|
||||||
|
} catch (ignored: Exception) {
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the value of the Java system property with the specified `property`, while falling back to the
|
||||||
|
* specified default value if the property access fails.
|
||||||
|
*/
|
||||||
|
fun getFloat(property: String, defaultValue: Float): Float {
|
||||||
|
var value = getProperty(property) ?: return defaultValue
|
||||||
|
value = value.trim { it <= ' ' }
|
||||||
|
try {
|
||||||
|
return value.toFloat()
|
||||||
|
} catch (ignored: Exception) {
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the value of the Java system property with the specified `property`, while falling back to the
|
||||||
|
* specified default value if the property access fails.
|
||||||
|
*/
|
||||||
|
fun getDouble(property: String, defaultValue: Double): Double {
|
||||||
|
var value = getProperty(property) ?: return defaultValue
|
||||||
|
value = value.trim { it <= ' ' }
|
||||||
|
try {
|
||||||
|
return value.toDouble()
|
||||||
|
} catch (ignored: Exception) {
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getColor(property: String, defaultValue: Color): Color {
|
||||||
|
var value = getProperty(property) ?: return defaultValue
|
||||||
|
value = value.trim { it <= ' ' }
|
||||||
|
try {
|
||||||
|
return Color.decode(value)
|
||||||
|
} catch (ignored: Exception) {
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the OS Type that is running on this machine
|
||||||
|
*/
|
||||||
|
fun get(): OSType? {
|
||||||
|
return osType
|
||||||
|
}
|
||||||
|
|
||||||
|
fun is64bit(): Boolean {
|
||||||
|
return osType!!.is64bit
|
||||||
|
}
|
||||||
|
|
||||||
|
fun is32bit(): Boolean {
|
||||||
|
return osType!!.is32bit
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if this is a "standard" x86/x64 architecture (intel/amd/etc) processor.
|
||||||
|
*/
|
||||||
|
val isX86: Boolean
|
||||||
|
get() = osType!!.isX86
|
||||||
|
val isMips: Boolean
|
||||||
|
get() = osType!!.isMips
|
||||||
|
val isArm: Boolean
|
||||||
|
get() = osType!!.isArm
|
||||||
|
val isLinux: Boolean
|
||||||
|
get() = osType!!.isLinux
|
||||||
|
val isUnix: Boolean
|
||||||
|
get() = osType!!.isUnix
|
||||||
|
val isSolaris: Boolean
|
||||||
|
get() = osType!!.isSolaris
|
||||||
|
val isWindows: Boolean
|
||||||
|
get() = osType!!.isWindows
|
||||||
|
val isMacOsX: Boolean
|
||||||
|
get() = osType!!.isMacOsX
|
||||||
|
val isAndroid: Boolean
|
||||||
|
get() = osType!!.isAndroid
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the currently running MAJOR java version as a NUMBER. For example, "Java version 1.7u45", and converts it into 7, uses JEP 223 for java > 9
|
||||||
|
*/
|
||||||
|
private fun _getJavaVersion(): Int {
|
||||||
|
// this should never be a problem, but just in case
|
||||||
|
var fullJavaVersion = getProperty("java.version", "")
|
||||||
|
if (fullJavaVersion!!.startsWith("1.")) {
|
||||||
|
when (fullJavaVersion[2]) {
|
||||||
|
'4' -> return 4
|
||||||
|
'5' -> return 5
|
||||||
|
'6' -> return 6
|
||||||
|
'7' -> return 7
|
||||||
|
'8' -> return 8
|
||||||
|
'9' -> return 9
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We are >= java 10, use JEP 223 to get the version (early releases of 9 might not have JEP 223, so 10 is guaranteed to have it)
|
||||||
|
fullJavaVersion = getProperty("java.specification.version", "10")
|
||||||
|
try {
|
||||||
|
// it will ALWAYS be the major release version as an integer. See http://openjdk.java.net/jeps/223
|
||||||
|
return fullJavaVersion!!.toInt()
|
||||||
|
} catch (ignored: Exception) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the last valid guess we have, since the current Java implementation, whatever it is, decided not to cooperate with JEP 223.
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set our system to UTC time zone. Retrieve the **original** time zone via [.getOriginalTimeZone]
|
||||||
|
*/
|
||||||
|
fun setUTC() {
|
||||||
|
// have to set our default timezone to UTC. EVERYTHING will be UTC, and if we want local, we must explicitly ask for it.
|
||||||
|
TimeZone.setDefault(TimeZone.getTimeZone("UTC"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the optimum number of threads for a given task. Makes certain not to take ALL the threads, always returns at least one
|
||||||
|
* thread.
|
||||||
|
*/
|
||||||
|
val optimumNumberOfThreads: Int
|
||||||
|
get() = (Runtime.getRuntime().availableProcessors() - 2).coerceAtLeast(1)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the first line of the exception message from 'throwable', or the type if there was no message.
|
||||||
|
*/
|
||||||
|
fun getExceptionMessage(throwable: Throwable): String? {
|
||||||
|
var message = throwable.message
|
||||||
|
if (message != null) {
|
||||||
|
val index = message.indexOf(LINE_SEPARATOR)
|
||||||
|
if (index > -1) {
|
||||||
|
message = message.substring(0, index)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message = throwable.javaClass.simpleName
|
||||||
|
}
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
}
|
@ -1,136 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010 dorkbox, llc
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package dorkbox.os;
|
|
||||||
|
|
||||||
public enum OSType {
|
|
||||||
Windows32("windows_32", ".dll"),
|
|
||||||
Windows64("windows_64", ".dll"),
|
|
||||||
Linux32("linux_32", ".so"),
|
|
||||||
Linux64("linux_64", ".so"),
|
|
||||||
MacOsX32("macosx_32", ".jnilib", ".dylib"),
|
|
||||||
MacOsX64("macosx_64", ".jnilib", ".dylib"),
|
|
||||||
|
|
||||||
UnixArm("unix_arm", ".so"),
|
|
||||||
Unix32("unix_32", ".so"),
|
|
||||||
Unix64("unix_64", ".so"),
|
|
||||||
|
|
||||||
Solaris("solaris", ".so"),
|
|
||||||
|
|
||||||
AndroidArm56("android_arm56", ".so"), // 32bit no hardware float support
|
|
||||||
AndroidArm7("android_arm7", ".so"), // 32bit hardware float support
|
|
||||||
AndroidArm8("android_arm8", ".so"), // 64bit (w/ hardware float. everything now has hard float)
|
|
||||||
|
|
||||||
AndroidMips("android_mips", ".so"), // 32bit mips
|
|
||||||
AndroidX86("android_x86", ".so"), // 32bit x86 (usually emulator)
|
|
||||||
|
|
||||||
AndroidMips64("android_mips64", ".so"), // 64bit mips
|
|
||||||
AndroidX86_64("android_x86_64", ".so"), // 64bit x86 (usually emulator)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Linux OS, Hard float, meaning floats are handled in hardware. WE ONLY SUPPORT HARD FLOATS for linux ARM!.
|
|
||||||
* For Raspberry-PI, Beaglebone, Odroid, etc PCs
|
|
||||||
*/
|
|
||||||
LinuxArm32("linux_arm7_hf", ".so"),
|
|
||||||
LinuxArm64("linux_arm8_hf", ".so"),
|
|
||||||
;
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
private final String[] libraryNames;
|
|
||||||
|
|
||||||
OSType(String name, String... libraryNames) {
|
|
||||||
this.name = name;
|
|
||||||
this.libraryNames = libraryNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
public String[] getLibraryNames() {
|
|
||||||
return this.libraryNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public
|
|
||||||
boolean is64bit() {
|
|
||||||
return this == OSType.Linux64 || this == OSType.LinuxArm64 ||
|
|
||||||
this == OSType.Windows64 || this == OSType.MacOsX64 ||
|
|
||||||
this == OSType.AndroidArm8 || this == OSType.AndroidX86_64 || this == OSType.AndroidMips64 ||
|
|
||||||
this == OSType.Unix64;
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
boolean is32bit() {
|
|
||||||
return this == OSType.Linux32 || this == OSType.LinuxArm32 ||
|
|
||||||
this == OSType.Windows32 || this == OSType.MacOsX32 ||
|
|
||||||
this == OSType.AndroidArm56 || this == OSType.AndroidArm7 || this == OSType.AndroidX86 || this == OSType.AndroidMips ||
|
|
||||||
this == OSType.UnixArm || this == OSType.Unix32;
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
boolean isMips() {
|
|
||||||
return this == OSType.AndroidMips || this == OSType.AndroidMips64;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true if this is a "standard" x86/x64 architecture (intel/amd/etc) processor.
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
boolean isX86() {
|
|
||||||
return this == OSType.Linux64 || this == OSType.LinuxArm64 ||
|
|
||||||
this == OSType.Windows64 || this == OSType.MacOsX64 ||
|
|
||||||
this == OSType.Linux32 || this == OSType.LinuxArm32 ||
|
|
||||||
this == OSType.Windows32 || this == OSType.MacOsX32 ||
|
|
||||||
this == OSType.Unix32 || this == OSType.Unix64 ||
|
|
||||||
this == OSType.AndroidX86 || this == OSType.AndroidX86_64;
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
boolean isArm() {
|
|
||||||
return this == OSType.LinuxArm32 || this == OSType.LinuxArm64 ||
|
|
||||||
this == OSType.AndroidArm56 || this == OSType.AndroidArm7 || this == OSType.AndroidArm8;
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
boolean isLinux() {
|
|
||||||
return this == OSType.Linux32 || this == OSType.Linux64 || this == OSType.LinuxArm64 || this == OSType.LinuxArm32;
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
boolean isUnix() {
|
|
||||||
return this == OSType.Unix32 || this == OSType.Unix64 || this == OSType.UnixArm;
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
boolean isSolaris() {
|
|
||||||
return this == OSType.Solaris;
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
boolean isWindows() {
|
|
||||||
return this == OSType.Windows64 || this == OSType.Windows32;
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
boolean isMacOsX() {
|
|
||||||
return this == OSType.MacOsX64 || this == OSType.MacOsX32;
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
boolean isAndroid() {
|
|
||||||
return this == OSType.AndroidArm56 || this == OSType.AndroidArm7 || this == OSType.AndroidX86 || this == OSType.AndroidMips ||
|
|
||||||
this == OSType.AndroidArm8 || this == OSType.AndroidX86_64 || this == OSType.AndroidMips64;
|
|
||||||
}
|
|
||||||
}
|
|
85
src/dorkbox/os/OSType.kt
Normal file
85
src/dorkbox/os/OSType.kt
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 dorkbox, llc
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package dorkbox.os
|
||||||
|
|
||||||
|
enum class OSType(name: String, vararg libraryNames: String) {
|
||||||
|
Windows32("windows_32", ".dll"),
|
||||||
|
Windows64("windows_64", ".dll"),
|
||||||
|
Linux32("linux_32", ".so"),
|
||||||
|
Linux64("linux_64", ".so"),
|
||||||
|
|
||||||
|
MacOsX32("macosx_32", ".jnilib", ".dylib"),
|
||||||
|
MacOsX64("macosx_64", ".jnilib", ".dylib"),
|
||||||
|
UnixArm("unix_arm", ".so"),
|
||||||
|
Unix32("unix_32",".so"),
|
||||||
|
|
||||||
|
Unix64("unix_64", ".so"),
|
||||||
|
Solaris("solaris", ".so"),
|
||||||
|
AndroidArm56("android_arm56", ".so"), // 32bit no hardware float support
|
||||||
|
AndroidArm7("android_arm7", ".so"), // 32bit hardware float support
|
||||||
|
AndroidArm8("android_arm8", ".so"), // 64bit (w/ hardware float. everything now has hard float)
|
||||||
|
AndroidMips("android_mips", ".so"), // 32bit mips
|
||||||
|
AndroidX86("android_x86", ".so"), // 32bit x86 (usually emulator)
|
||||||
|
AndroidMips64("android_mips64", ".so"), // 64bit mips
|
||||||
|
AndroidX86_64("android_x86_64", ".so"), // 64bit x86 (usually emulator)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Linux OS, Hard float, meaning floats are handled in hardware. WE ONLY SUPPORT HARD FLOATS for linux ARM!.
|
||||||
|
* For Raspberry-PI, Beaglebone, Odroid, etc PCs
|
||||||
|
*/
|
||||||
|
LinuxArm32("linux_arm7_hf", ".so"),
|
||||||
|
LinuxArm64("linux_arm8_hf", ".so");
|
||||||
|
|
||||||
|
|
||||||
|
val libraryNames: Array<out String>
|
||||||
|
|
||||||
|
init {
|
||||||
|
this.libraryNames = libraryNames
|
||||||
|
}
|
||||||
|
|
||||||
|
val is64bit: Boolean
|
||||||
|
get() {
|
||||||
|
return this == Linux64 || this == LinuxArm64 || this == Windows64 || this == MacOsX64 || this == AndroidArm8 || this == AndroidX86_64 || this == AndroidMips64 || this == Unix64
|
||||||
|
}
|
||||||
|
|
||||||
|
val is32bit: Boolean
|
||||||
|
get() {
|
||||||
|
return this == Linux32 || this == LinuxArm32 || this == Windows32 || this == MacOsX32 || this == AndroidArm56 || this == AndroidArm7 || this == AndroidX86 || this == AndroidMips || this == UnixArm || this == Unix32
|
||||||
|
}
|
||||||
|
|
||||||
|
val isMips: Boolean
|
||||||
|
get() = this == AndroidMips || this == AndroidMips64
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if this is a "standard" x86/x64 architecture (intel/amd/etc) processor.
|
||||||
|
*/
|
||||||
|
val isX86: Boolean
|
||||||
|
get() = this == Linux64 || this == LinuxArm64 || this == Windows64 || this == MacOsX64 || this == Linux32 || this == LinuxArm32 || this == Windows32 || this == MacOsX32 || this == Unix32 || this == Unix64 || this == AndroidX86 || this == AndroidX86_64
|
||||||
|
val isArm: Boolean
|
||||||
|
get() = this == LinuxArm32 || this == LinuxArm64 || this == AndroidArm56 || this == AndroidArm7 || this == AndroidArm8
|
||||||
|
val isLinux: Boolean
|
||||||
|
get() = this == Linux32 || this == Linux64 || this == LinuxArm64 || this == LinuxArm32
|
||||||
|
val isUnix: Boolean
|
||||||
|
get() = this == Unix32 || this == Unix64 || this == UnixArm
|
||||||
|
val isSolaris: Boolean
|
||||||
|
get() = this == Solaris
|
||||||
|
val isWindows: Boolean
|
||||||
|
get() = this == Windows64 || this == Windows32
|
||||||
|
val isMacOsX: Boolean
|
||||||
|
get() = this == MacOsX64 || this == MacOsX32
|
||||||
|
val isAndroid: Boolean
|
||||||
|
get() = this == AndroidArm56 || this == AndroidArm7 || this == AndroidX86 || this == AndroidMips || this == AndroidArm8 || this == AndroidX86_64 || this == AndroidMips64
|
||||||
|
}
|
@ -81,7 +81,7 @@ class OSUtil {
|
|||||||
int[] getVersion() {
|
int[] getVersion() {
|
||||||
int[] version = new int[2];
|
int[] version = new int[2];
|
||||||
|
|
||||||
if (!OS.isWindows()) {
|
if (!OS.INSTANCE.isWindows()) {
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +179,7 @@ class OSUtil {
|
|||||||
class Unix {
|
class Unix {
|
||||||
public static
|
public static
|
||||||
boolean isFreeBSD() {
|
boolean isFreeBSD() {
|
||||||
if (!OS.isUnix()) {
|
if (!OS.INSTANCE.isUnix()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,7 +207,7 @@ class OSUtil {
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!OS.isLinux()) {
|
if (!OS.INSTANCE.isLinux()) {
|
||||||
info = "";
|
info = "";
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
@ -751,7 +751,7 @@ class OSUtil {
|
|||||||
private static volatile Boolean isMATE = null;
|
private static volatile Boolean isMATE = null;
|
||||||
public static
|
public static
|
||||||
boolean isMATE() {
|
boolean isMATE() {
|
||||||
if (!OS.isLinux() && !OS.isUnix()) {
|
if (!OS.INSTANCE.isLinux() && !OS.INSTANCE.isUnix()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -772,7 +772,7 @@ class OSUtil {
|
|||||||
private static volatile Boolean isGnome = null;
|
private static volatile Boolean isGnome = null;
|
||||||
public static
|
public static
|
||||||
boolean isGnome() {
|
boolean isGnome() {
|
||||||
if (!OS.isLinux() && !OS.isUnix()) {
|
if (!OS.INSTANCE.isLinux() && !OS.INSTANCE.isUnix()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -787,7 +787,7 @@ class OSUtil {
|
|||||||
// ps x | grep gnome-shell
|
// ps x | grep gnome-shell
|
||||||
boolean contains = Executor.Companion.run("ps", "x").contains("gnome-shell");
|
boolean contains = Executor.Companion.run("ps", "x").contains("gnome-shell");
|
||||||
|
|
||||||
if (!contains && OS.isLinux()) {
|
if (!contains && OS.INSTANCE.isLinux()) {
|
||||||
// only try again if we are linux
|
// only try again if we are linux
|
||||||
|
|
||||||
// ps a | grep gnome-shell
|
// ps a | grep gnome-shell
|
||||||
@ -814,7 +814,7 @@ class OSUtil {
|
|||||||
return gnomeVersion;
|
return gnomeVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!OS.isLinux() && !OS.isUnix()) {
|
if (!OS.INSTANCE.isLinux() && !OS.INSTANCE.isUnix()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -903,7 +903,7 @@ class OSUtil {
|
|||||||
return getPlasmaVersionFull;
|
return getPlasmaVersionFull;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!OS.isLinux() && !OS.isUnix()) {
|
if (!OS.INSTANCE.isLinux() && !OS.INSTANCE.isUnix()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -932,7 +932,7 @@ class OSUtil {
|
|||||||
private static volatile Boolean isXfce = null;
|
private static volatile Boolean isXfce = null;
|
||||||
public static
|
public static
|
||||||
boolean isXfce() {
|
boolean isXfce() {
|
||||||
if (!OS.isLinux() && !OS.isUnix()) {
|
if (!OS.INSTANCE.isLinux() && !OS.INSTANCE.isUnix()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -947,7 +947,7 @@ class OSUtil {
|
|||||||
// ps x | grep xfce
|
// ps x | grep xfce
|
||||||
boolean contains = Executor.Companion.run("ps", "x").contains("xfce");
|
boolean contains = Executor.Companion.run("ps", "x").contains("xfce");
|
||||||
|
|
||||||
if (!contains && OS.isLinux()) {
|
if (!contains && OS.INSTANCE.isLinux()) {
|
||||||
// only try again if we are linux
|
// only try again if we are linux
|
||||||
|
|
||||||
// ps a | grep gnome-shell
|
// ps a | grep gnome-shell
|
||||||
@ -977,7 +977,7 @@ class OSUtil {
|
|||||||
return isNautilus;
|
return isNautilus;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!OS.isLinux() && !OS.isUnix()) {
|
if (!OS.INSTANCE.isLinux() && !OS.INSTANCE.isUnix()) {
|
||||||
isNautilus = false;
|
isNautilus = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1006,7 +1006,7 @@ class OSUtil {
|
|||||||
public static
|
public static
|
||||||
boolean isChromeOS() {
|
boolean isChromeOS() {
|
||||||
if (isChromeOS == null) {
|
if (isChromeOS == null) {
|
||||||
if (!OS.isLinux()) {
|
if (!OS.INSTANCE.isLinux()) {
|
||||||
isChromeOS = false;
|
isChromeOS = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1037,7 +1037,7 @@ class OSUtil {
|
|||||||
*/
|
*/
|
||||||
public static
|
public static
|
||||||
String queryXfce(String channel, String property) {
|
String queryXfce(String channel, String property) {
|
||||||
if (!OS.isLinux() && !OS.isUnix()) {
|
if (!OS.INSTANCE.isLinux() && !OS.INSTANCE.isUnix()) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ import java.io.InputStream;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@ -62,7 +63,7 @@ class CacheUtil {
|
|||||||
void clear() {
|
void clear() {
|
||||||
// deletes all of the files (recursively) in the specified location. If the directory is empty (no locked files), then the
|
// deletes all of the files (recursively) in the specified location. If the directory is empty (no locked files), then the
|
||||||
// directory is also deleted.
|
// directory is also deleted.
|
||||||
FileUtil.delete(new File(OS.TEMP_DIR, tempDir));
|
FileUtil.delete(new File(OS.INSTANCE.TEMP_DIR, tempDir));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -387,11 +388,11 @@ class CacheUtil {
|
|||||||
throw new NullPointerException("cacheName");
|
throw new NullPointerException("cacheName");
|
||||||
}
|
}
|
||||||
|
|
||||||
File saveDir = new File(OS.TEMP_DIR, tempDir);
|
File saveDir = new File(OS.INSTANCE.TEMP_DIR, tempDir);
|
||||||
|
|
||||||
// can be wimpy, only one at a time
|
// can be wimpy, only one at a time
|
||||||
String hash = hashName(cacheName);
|
String hash = hashName(cacheName);
|
||||||
String extension = FileUtil.getExtension(cacheName);
|
String extension = FileUtil.INSTANCE.getExtension(cacheName);
|
||||||
if (extension.isEmpty()) {
|
if (extension.isEmpty()) {
|
||||||
extension = "cache";
|
extension = "cache";
|
||||||
}
|
}
|
||||||
@ -408,7 +409,7 @@ class CacheUtil {
|
|||||||
private static
|
private static
|
||||||
String hashName(final String name) {
|
String hashName(final String name) {
|
||||||
// figure out the fileName
|
// figure out the fileName
|
||||||
byte[] bytes = name.getBytes(OS.UTF_8);
|
byte[] bytes = name.getBytes(StandardCharsets.UTF_8);
|
||||||
MessageDigest digest = digestLocal.get();
|
MessageDigest digest = digestLocal.get();
|
||||||
|
|
||||||
digest.reset();
|
digest.reset();
|
||||||
|
91
src/dorkbox/util/CommonUtils.kt
Normal file
91
src/dorkbox/util/CommonUtils.kt
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Pronghorn Technology LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dorkbox.util
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import mu.KLogger
|
||||||
|
import mu.KotlinLogging
|
||||||
|
import java.io.PrintWriter
|
||||||
|
import java.io.StringWriter
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
import kotlin.coroutines.coroutineContext
|
||||||
|
|
||||||
|
inline fun <reified T> T.logger(name: String): KLogger {
|
||||||
|
return KotlinLogging.logger(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T> T.logger(): KLogger {
|
||||||
|
if (T::class.isCompanion) {
|
||||||
|
return KotlinLogging.logger(T::class.java.enclosingClass.simpleName)
|
||||||
|
}
|
||||||
|
return KotlinLogging.logger(T::class.java.simpleName)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun Exception.stackTraceToString(): String {
|
||||||
|
val exceptionWriter = StringWriter()
|
||||||
|
printStackTrace(PrintWriter(exceptionWriter))
|
||||||
|
return exceptionWriter.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun ignoreException(block: () -> Unit) {
|
||||||
|
try {
|
||||||
|
block()
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun ignoreExceptions(vararg blocks: () -> Unit) {
|
||||||
|
blocks.forEach { block ->
|
||||||
|
try {
|
||||||
|
block()
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun async(dispatcher: CoroutineDispatcher = Dispatchers.IO, action: suspend CoroutineScope.() -> Unit): Job {
|
||||||
|
return GlobalScope.launch(dispatcher) {
|
||||||
|
action()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun <T> Mutex.withReentrantLock(block: suspend () -> T): T {
|
||||||
|
val key = ReentrantMutexContextKey(this)
|
||||||
|
|
||||||
|
// call block directly when this mutex is already locked in the context
|
||||||
|
if (coroutineContext[key] != null) return block()
|
||||||
|
|
||||||
|
// otherwise add it to the context and lock the mutex
|
||||||
|
return withContext(ReentrantMutexContextElement(key)) {
|
||||||
|
withLock { block() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReentrantMutexContextElement(override val key: ReentrantMutexContextKey) : CoroutineContext.Element
|
||||||
|
data class ReentrantMutexContextKey(val mutex: Mutex) : CoroutineContext.Key<ReentrantMutexContextElement>
|
@ -236,7 +236,7 @@ class Desktop {
|
|||||||
throw new IOException("Path must not be null or empty.");
|
throw new IOException("Path must not be null or empty.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (OS.isMacOsX()) {
|
if (OS.INSTANCE.isMacOsX()) {
|
||||||
File directory = new File(path);
|
File directory = new File(path);
|
||||||
|
|
||||||
// Mac tries to open the .app rather than browsing it. Instead, pass a child with -R to select it in finder
|
// Mac tries to open the .app rather than browsing it. Instead, pass a child with -R to select it in finder
|
||||||
@ -285,7 +285,7 @@ class Desktop {
|
|||||||
* - CLI has thread/memory overhead
|
* - CLI has thread/memory overhead
|
||||||
*/
|
*/
|
||||||
private static boolean requireUnixLauncher() {
|
private static boolean requireUnixLauncher() {
|
||||||
return ((OS.isUnix() || OS.isLinux()) && (GtkCheck.isGtkLoaded && GtkCheck.isGtk3));
|
return ((OS.INSTANCE.isUnix() || OS.INSTANCE.isLinux()) && (GtkCheck.isGtkLoaded && GtkCheck.isGtk3));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
210
src/dorkbox/util/DomainUtils.kt
Normal file
210
src/dorkbox/util/DomainUtils.kt
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013 dorkbox, llc
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Tres Finocchiaro, QZ Industries, LLC
|
||||||
|
* Derivative code has been released as Apache 2.0, used with permission.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package dorkbox.util
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* And the effective_tld_names.dat is from mozilla (the following are all the same data)
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* https://mxr.mozilla.org/mozilla-central/source/netwerk/dns/effective_tld_names.dat?raw=1
|
||||||
|
* which is...
|
||||||
|
* https://publicsuffix.org/list/effective_tld_names.dat
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* also
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* https://publicsuffix.org/list/public_suffix_list.dat
|
||||||
|
*/
|
||||||
|
object DomainUtils {
|
||||||
|
private val exceptions = HashSet<String>()
|
||||||
|
private val suffixes = HashSet<String>()
|
||||||
|
|
||||||
|
fun init() {
|
||||||
|
// just here to load the class.
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
val tldFileName = "effective_tld_names.dat.txt"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parses the list from publicsuffix.org
|
||||||
|
* Copied from
|
||||||
|
* http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/cookie/PublicSuffixListParser.java
|
||||||
|
*
|
||||||
|
* new one at:
|
||||||
|
* http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/PublicSuffixDomainFilter.java
|
||||||
|
* and
|
||||||
|
* http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/psl/
|
||||||
|
*/
|
||||||
|
|
||||||
|
// now load this file into memory, so it's faster to process.
|
||||||
|
var file = File("blacklist", tldFileName)
|
||||||
|
val paths = LinkedList(Arrays.asList("NetRefDependencies", ".."))
|
||||||
|
|
||||||
|
while (!file.canRead() && !paths.isEmpty()) {
|
||||||
|
// for work in an IDE. Path can vary, so we work our way up
|
||||||
|
file = File(paths.removeFirst(), file.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
file = file.absoluteFile
|
||||||
|
if (!file.canRead()) {
|
||||||
|
throw RuntimeException("Unable to load the TLD list: $tldFileName")
|
||||||
|
}
|
||||||
|
|
||||||
|
FileUtil.read(file, object : FileUtil.Action {
|
||||||
|
override fun onLineRead(line: String) {
|
||||||
|
var line = line
|
||||||
|
|
||||||
|
// entire lines can also be commented using //
|
||||||
|
if (!line.isEmpty() && !line.startsWith("//")) {
|
||||||
|
|
||||||
|
if (line.startsWith(".")) {
|
||||||
|
line = line.substring(1) // A leading dot is optional
|
||||||
|
}
|
||||||
|
|
||||||
|
// An exclamation mark (!) at the start of a rule marks an exception
|
||||||
|
// to a previous wildcard rule
|
||||||
|
val isException = line.startsWith("!")
|
||||||
|
if (isException) {
|
||||||
|
line = line.substring(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isException) {
|
||||||
|
exceptions.add(line)
|
||||||
|
} else {
|
||||||
|
suffixes.add(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun finished() {}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the second level domain, from a fully qualified domain (ie: www.aa.com, or www.amazon.co.uk).
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This algorithm works from left to right parsing the domain string parameter
|
||||||
|
*
|
||||||
|
* @param domain a fully qualified domain (ie: www.aa.com, or www.amazon.co.uk)
|
||||||
|
*
|
||||||
|
* @return null (if there is no second level domain) or the SLD www.aa.com -> aa.com , or www.amazon.co.uk -> amazon.co.uk
|
||||||
|
*/
|
||||||
|
fun extractSLD(domain: String): String? {
|
||||||
|
var domain = domain
|
||||||
|
var last = domain
|
||||||
|
var anySLD = false
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (isTLD(domain)) {
|
||||||
|
return if (anySLD) {
|
||||||
|
last
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
anySLD = true
|
||||||
|
last = domain
|
||||||
|
|
||||||
|
val nextDot = domain.indexOf(".")
|
||||||
|
if (nextDot == -1) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
domain = domain.substring(nextDot + 1)
|
||||||
|
} while (domain.isNotEmpty())
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a domain that is without it's TLD at the end.
|
||||||
|
*
|
||||||
|
* @param domain domain a fully qualified domain or not, (ie: www.aa.com, or amazon.co.uk).
|
||||||
|
*
|
||||||
|
* @return a domain that is without it's TLD, ie: www.aa.com -> www.aa, or google.com -> google
|
||||||
|
*/
|
||||||
|
fun withoutTLD(domain: String): String? {
|
||||||
|
|
||||||
|
var index = 0
|
||||||
|
while (index != -1) {
|
||||||
|
index = domain.indexOf('.', index)
|
||||||
|
|
||||||
|
if (index != -1) {
|
||||||
|
if (isTLD(domain.substring(index))) {
|
||||||
|
return domain.substring(0, index)
|
||||||
|
}
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the domain is a TLD.
|
||||||
|
*/
|
||||||
|
fun isTLD(domain: String): Boolean {
|
||||||
|
var domain = domain
|
||||||
|
if (domain.startsWith(".")) {
|
||||||
|
domain = domain.substring(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// An exception rule takes priority over any other matching rule.
|
||||||
|
// Exceptions are ones that are not a TLD, but would match a pattern rule
|
||||||
|
// e.g. bl.uk is not a TLD, but the rule *.uk means it is. Hence there is an exception rule
|
||||||
|
// stating that bl.uk is not a TLD.
|
||||||
|
if (exceptions.contains(domain)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (suffixes.contains(domain)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try patterns. ie *.jp means that boo.jp is a TLD
|
||||||
|
val nextdot = domain.indexOf('.')
|
||||||
|
if (nextdot == -1) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
domain = "*" + domain.substring(nextdot)
|
||||||
|
|
||||||
|
return suffixes.contains(domain)
|
||||||
|
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// @JvmStatic
|
||||||
|
// fun main(args: Array<String>) {
|
||||||
|
// System.err.println("isTLD(espn.com) = " + isTLD("espn.com"))
|
||||||
|
// System.err.println("withoutTLD(com) = " + withoutTLD("com"))
|
||||||
|
// System.err.println("withoutTLD(chrome:extension) = " + withoutTLD(""))
|
||||||
|
// }
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
2058
src/dorkbox/util/FileUtil.kt
Normal file
2058
src/dorkbox/util/FileUtil.kt
Normal file
File diff suppressed because it is too large
Load Diff
@ -39,7 +39,7 @@ import dorkbox.os.OS;
|
|||||||
public
|
public
|
||||||
class FontUtil {
|
class FontUtil {
|
||||||
/** Default location where all the fonts are stored */
|
/** Default location where all the fonts are stored */
|
||||||
public static volatile String FONTS_LOCATION = OS.getProperty(FontUtil.class.getCanonicalName() + ".FONTS_LOCATION", "resources/fonts");
|
public static volatile String FONTS_LOCATION = OS.INSTANCE.getProperty(FontUtil.class.getCanonicalName() + ".FONTS_LOCATION", "resources/fonts");
|
||||||
|
|
||||||
|
|
||||||
/** All of the fonts in the {@link #FONTS_LOCATION} will be loaded by the Font manager */
|
/** All of the fonts in the {@link #FONTS_LOCATION} will be loaded by the Font manager */
|
||||||
|
@ -24,6 +24,8 @@ import java.io.OutputStream;
|
|||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
|
||||||
@SuppressWarnings({"unused", "Duplicates"})
|
@SuppressWarnings({"unused", "Duplicates"})
|
||||||
|
// deprecated. use kotlin.
|
||||||
|
@Deprecated
|
||||||
public
|
public
|
||||||
class IO {
|
class IO {
|
||||||
/**
|
/**
|
||||||
|
@ -145,13 +145,13 @@ class ImageUtil {
|
|||||||
|
|
||||||
// have to resize the file (and return the new path)
|
// have to resize the file (and return the new path)
|
||||||
|
|
||||||
String extension = FileUtil.getExtension(fileName);
|
String extension = FileUtil.INSTANCE.getExtension(fileName);
|
||||||
if (extension.isEmpty()) {
|
if (extension.isEmpty()) {
|
||||||
extension = "png"; // made up
|
extension = "png"; // made up
|
||||||
}
|
}
|
||||||
|
|
||||||
// now have to resize this file.
|
// now have to resize this file.
|
||||||
File newFile = new File(OS.TEMP_DIR, "temp_resize." + extension).getAbsoluteFile();
|
File newFile = new File(OS.INSTANCE.TEMP_DIR, "temp_resize." + extension).getAbsoluteFile();
|
||||||
Image image;
|
Image image;
|
||||||
|
|
||||||
// is file sitting on drive
|
// is file sitting on drive
|
||||||
|
@ -13,54 +13,34 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package dorkbox.util;
|
package dorkbox.util
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import org.tukaani.xz.LZMA2Options
|
||||||
import java.io.IOException;
|
import org.tukaani.xz.LZMAInputStream
|
||||||
import java.io.InputStream;
|
import org.tukaani.xz.LZMAOutputStream
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.IOException
|
||||||
import java.io.OutputStream;
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
import org.tukaani.xz.LZMA2Options;
|
object LZMA {
|
||||||
import org.tukaani.xz.LZMAInputStream;
|
|
||||||
import org.tukaani.xz.LZMAOutputStream;
|
|
||||||
|
|
||||||
public class LZMA {
|
|
||||||
// https://tukaani.org/xz/java.html
|
// https://tukaani.org/xz/java.html
|
||||||
|
@Throws(IOException::class)
|
||||||
public static final void encode(InputStream input, OutputStream output) throws IOException {
|
fun encode(input: InputStream, output: OutputStream) {
|
||||||
try (OutputStream compressionStream = new LZMAOutputStream(output, new LZMA2Options(3), true)) {
|
LZMAOutputStream(output, LZMA2Options(3), true).use { compressionStream ->
|
||||||
IO.copyStream(input, compressionStream);
|
input.copyTo(compressionStream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final ByteArrayOutputStream decode(InputStream input) throws IOException {
|
@Throws(IOException::class)
|
||||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(8192);
|
fun decode(input: InputStream): ByteArrayOutputStream {
|
||||||
|
val byteArrayOutputStream = ByteArrayOutputStream(8192)
|
||||||
try (LZMAInputStream compressedStream = new LZMAInputStream(input)) {
|
LZMAInputStream(input).use { compressedStream -> compressedStream.copyTo(byteArrayOutputStream) }
|
||||||
IO.copyStream(compressedStream, byteArrayOutputStream);
|
return byteArrayOutputStream
|
||||||
}
|
}
|
||||||
|
|
||||||
return byteArrayOutputStream;
|
@Throws(IOException::class)
|
||||||
}
|
fun decode(input: InputStream, output: OutputStream) {
|
||||||
|
LZMAInputStream(input).use { compressedStream -> compressedStream.copyTo(output) }
|
||||||
public static final void decode(InputStream input, OutputStream output) throws IOException {
|
|
||||||
try (LZMAInputStream compressedStream = new LZMAInputStream(input)) {
|
|
||||||
IO.copyStream(compressedStream, output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final Object clone() throws java.lang.CloneNotSupportedException {
|
|
||||||
throw new java.lang.CloneNotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private final void writeObject(ObjectOutputStream out) throws java.io.IOException {
|
|
||||||
throw new java.io.NotSerializableException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private final void readObject(ObjectInputStream in) throws java.io.IOException {
|
|
||||||
throw new java.io.NotSerializableException();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -578,7 +578,7 @@ class LocationResolver {
|
|||||||
}
|
}
|
||||||
String path = url.getPath();
|
String path = url.getPath();
|
||||||
|
|
||||||
if (OS.isWindows()) {
|
if (OS.INSTANCE.isWindows()) {
|
||||||
if (path.startsWith("/")) {
|
if (path.startsWith("/")) {
|
||||||
path = path.substring(1);
|
path = path.substring(1);
|
||||||
}
|
}
|
||||||
|
@ -35,10 +35,10 @@ class NativeLoader {
|
|||||||
File extractLibrary(final String sourceFileName, final String destinationDirectory, final String destinationName, String version) throws IOException {
|
File extractLibrary(final String sourceFileName, final String destinationDirectory, final String destinationName, String version) throws IOException {
|
||||||
try {
|
try {
|
||||||
String suffix;
|
String suffix;
|
||||||
if (OS.isLinux()) {
|
if (OS.INSTANCE.isLinux()) {
|
||||||
suffix = ".so";
|
suffix = ".so";
|
||||||
}
|
}
|
||||||
else if (OS.isWindows()) {
|
else if (OS.INSTANCE.isWindows()) {
|
||||||
suffix = ".dll";
|
suffix = ".dll";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -65,7 +65,7 @@ class ParallelProcessor<Task> {
|
|||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
ParallelProcessor() {
|
ParallelProcessor() {
|
||||||
this(-1, OS.getOptimumNumberOfThreads(), null);
|
this(-1, OS.INSTANCE.getOptimumNumberOfThreads(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -81,7 +81,7 @@ class ParallelProcessor<Task> {
|
|||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
ParallelProcessor(final int totalWorkload) {
|
ParallelProcessor(final int totalWorkload) {
|
||||||
this(totalWorkload, OS.getOptimumNumberOfThreads(), null);
|
this(totalWorkload, OS.INSTANCE.getOptimumNumberOfThreads(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -737,7 +737,7 @@ class Sys {
|
|||||||
builder.append(",");
|
builder.append(",");
|
||||||
}
|
}
|
||||||
if (i > inputOffset && lineLength > 0 && i % lineLength == 0) {
|
if (i > inputOffset && lineLength > 0 && i % lineLength == 0) {
|
||||||
builder.append(OS.LINE_SEPARATOR);
|
builder.append(OS.INSTANCE.LINE_SEPARATOR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
160
src/dorkbox/util/UrlDecoder.kt
Normal file
160
src/dorkbox/util/UrlDecoder.kt
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2005-2012, Paul Tuckey
|
||||||
|
* All rights reserved.
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed under the BSD License. Text as follows.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* - Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
* - Neither the name tuckey.org nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this
|
||||||
|
* software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* https://www.talisman.org/%7Eerlkonig/misc/lunatech%5Ewhat-every-webdev-must-know-about-url-encoding/
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright 2021 dorkbox, llc
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Tres Finocchiaro, QZ Industries, LLC
|
||||||
|
* Derivative code has been released as Apache 2.0, used with permission.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dorkbox.util
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException
|
||||||
|
import java.net.URISyntaxException
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
|
object URLDecoder {
|
||||||
|
private const val byte_0 = '0'.code.toByte()
|
||||||
|
private const val byte_1 = '1'.code.toByte()
|
||||||
|
private const val byte_2 = '2'.code.toByte()
|
||||||
|
private const val byte_3 = '3'.code.toByte()
|
||||||
|
private const val byte_4 = '4'.code.toByte()
|
||||||
|
private const val byte_5 = '5'.code.toByte()
|
||||||
|
private const val byte_6 = '6'.code.toByte()
|
||||||
|
private const val byte_7= '7'.code.toByte()
|
||||||
|
private const val byte_8 = '8'.code.toByte()
|
||||||
|
private const val byte_9 = '9'.code.toByte()
|
||||||
|
private const val byte_a = 'a'.code.toByte()
|
||||||
|
private const val byte_b = 'b'.code.toByte()
|
||||||
|
private const val byte_c = 'c'.code.toByte()
|
||||||
|
private const val byte_d = 'd'.code.toByte()
|
||||||
|
private const val byte_e = 'e'.code.toByte()
|
||||||
|
private const val byte_f = 'f'.code.toByte()
|
||||||
|
private const val byte_A = 'A'.code.toByte()
|
||||||
|
private const val byteB = 'B'.code.toByte()
|
||||||
|
private const val byteC = 'C'.code.toByte()
|
||||||
|
private const val byte_D= 'D'.code.toByte()
|
||||||
|
private const val byte_E= 'E'.code.toByte()
|
||||||
|
private const val byte_F = 'F'.code.toByte()
|
||||||
|
|
||||||
|
@Throws(URISyntaxException::class)
|
||||||
|
fun decodeURL(url: String, charset: Charset): String {
|
||||||
|
val queryPart = url.indexOf('?')
|
||||||
|
var query: String? = null
|
||||||
|
var path = url
|
||||||
|
if (queryPart != -1) {
|
||||||
|
query = url.substring(queryPart + 1)
|
||||||
|
path = url.substring(0, queryPart)
|
||||||
|
}
|
||||||
|
val decodedPath = decodePath(path, charset)
|
||||||
|
return if (query != null) decodedPath + '?' + decodeQuery(query, charset) else decodedPath
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(URISyntaxException::class)
|
||||||
|
fun decodePath(path: String, charset: Charset): String {
|
||||||
|
return decodeURLEncoded(path, false, charset)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(URISyntaxException::class)
|
||||||
|
fun decodeQuery(query: String, charset: Charset): String {
|
||||||
|
return decodeURLEncoded(query, true, charset)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(URISyntaxException::class)
|
||||||
|
fun decodeURLEncoded(part: String, query: Boolean, charset: Charset): String {
|
||||||
|
return try {
|
||||||
|
val ascii = part.toByteArray(Charsets.US_ASCII)
|
||||||
|
val decoded = ByteArray(ascii.size)
|
||||||
|
var j = 0
|
||||||
|
var i = 0
|
||||||
|
while (i < ascii.size) {
|
||||||
|
if (ascii[i] == '%'.code.toByte()) {
|
||||||
|
if (i + 2 >= ascii.size) throw URISyntaxException(part, "Invalid URL-encoded string at char $i")
|
||||||
|
// get the next two bytes
|
||||||
|
val first = ascii[++i]
|
||||||
|
val second = ascii[++i]
|
||||||
|
decoded[j] = (hexToByte(first) * 16 + hexToByte(second)).toByte()
|
||||||
|
} else if (query && ascii[i] == '+'.code.toByte()) decoded[j] = ' '.code.toByte() else decoded[j] = ascii[i]
|
||||||
|
i++
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
// now decode
|
||||||
|
String(decoded, 0, j, charset)
|
||||||
|
} catch (x: UnsupportedEncodingException) {
|
||||||
|
throw URISyntaxException(part, "Invalid encoding: $charset")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Throws(URISyntaxException::class)
|
||||||
|
private fun hexToByte(b: Byte): Byte {
|
||||||
|
when (b) {
|
||||||
|
byte_0 -> return 0
|
||||||
|
byte_1 -> return 1
|
||||||
|
byte_2 -> return 2
|
||||||
|
byte_3 -> return 3
|
||||||
|
byte_4 -> return 4
|
||||||
|
byte_5 -> return 5
|
||||||
|
byte_6 -> return 6
|
||||||
|
byte_7 -> return 7
|
||||||
|
byte_8 -> return 8
|
||||||
|
byte_9 -> return 9
|
||||||
|
byte_a, byte_A -> return 10
|
||||||
|
byte_b, byteB -> return 11
|
||||||
|
byte_c, byteC -> return 12
|
||||||
|
byte_d, byte_D -> return 13
|
||||||
|
byte_e, byte_E -> return 14
|
||||||
|
byte_f, byte_F -> return 15
|
||||||
|
}
|
||||||
|
throw URISyntaxException(b.toString(), "Invalid URL-encoded string")
|
||||||
|
}
|
||||||
|
}
|
248
src/dorkbox/util/UrlEncoder.kt
Normal file
248
src/dorkbox/util/UrlEncoder.kt
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2005-2012, Paul Tuckey
|
||||||
|
* All rights reserved.
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed under the BSD License. Text as follows.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* - Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
* - Neither the name tuckey.org nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this
|
||||||
|
* software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* https://www.talisman.org/%7Eerlkonig/misc/lunatech%5Ewhat-every-webdev-must-know-about-url-encoding/
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dorkbox.util
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL-encoding utility for each URL part according to the RFC specs
|
||||||
|
* see the rfc at http://www.ietf.org/rfc/rfc2396.txt
|
||||||
|
*
|
||||||
|
* @author stephane
|
||||||
|
*/
|
||||||
|
object URLEncoder {
|
||||||
|
/**
|
||||||
|
* mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
|
||||||
|
*/
|
||||||
|
val MARK = BitSet()
|
||||||
|
|
||||||
|
init {
|
||||||
|
MARK.set('-'.code)
|
||||||
|
MARK.set('_'.code)
|
||||||
|
MARK.set('.'.code)
|
||||||
|
MARK.set('!'.code)
|
||||||
|
MARK.set('~'.code)
|
||||||
|
MARK.set('*'.code)
|
||||||
|
MARK.set('\''.code)
|
||||||
|
MARK.set('('.code)
|
||||||
|
MARK.set(')'.code)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" |
|
||||||
|
* "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
|
||||||
|
*/
|
||||||
|
val LOW_ALPHA = BitSet()
|
||||||
|
|
||||||
|
init {
|
||||||
|
LOW_ALPHA.set('a'.code)
|
||||||
|
LOW_ALPHA.set('b'.code)
|
||||||
|
LOW_ALPHA.set('c'.code)
|
||||||
|
LOW_ALPHA.set('d'.code)
|
||||||
|
LOW_ALPHA.set('e'.code)
|
||||||
|
LOW_ALPHA.set('f'.code)
|
||||||
|
LOW_ALPHA.set('g'.code)
|
||||||
|
LOW_ALPHA.set('h'.code)
|
||||||
|
LOW_ALPHA.set('i'.code)
|
||||||
|
LOW_ALPHA.set('j'.code)
|
||||||
|
LOW_ALPHA.set('k'.code)
|
||||||
|
LOW_ALPHA.set('l'.code)
|
||||||
|
LOW_ALPHA.set('m'.code)
|
||||||
|
LOW_ALPHA.set('n'.code)
|
||||||
|
LOW_ALPHA.set('o'.code)
|
||||||
|
LOW_ALPHA.set('p'.code)
|
||||||
|
LOW_ALPHA.set('q'.code)
|
||||||
|
LOW_ALPHA.set('r'.code)
|
||||||
|
LOW_ALPHA.set('s'.code)
|
||||||
|
LOW_ALPHA.set('t'.code)
|
||||||
|
LOW_ALPHA.set('u'.code)
|
||||||
|
LOW_ALPHA.set('v'.code)
|
||||||
|
LOW_ALPHA.set('w'.code)
|
||||||
|
LOW_ALPHA.set('x'.code)
|
||||||
|
LOW_ALPHA.set('y'.code)
|
||||||
|
LOW_ALPHA.set('z'.code)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* upalpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" |
|
||||||
|
* "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
|
||||||
|
*/
|
||||||
|
val UP_ALPHA = BitSet()
|
||||||
|
|
||||||
|
init {
|
||||||
|
UP_ALPHA.set('A'.code)
|
||||||
|
UP_ALPHA.set('B'.code)
|
||||||
|
UP_ALPHA.set('C'.code)
|
||||||
|
UP_ALPHA.set('D'.code)
|
||||||
|
UP_ALPHA.set('E'.code)
|
||||||
|
UP_ALPHA.set('F'.code)
|
||||||
|
UP_ALPHA.set('G'.code)
|
||||||
|
UP_ALPHA.set('H'.code)
|
||||||
|
UP_ALPHA.set('I'.code)
|
||||||
|
UP_ALPHA.set('J'.code)
|
||||||
|
UP_ALPHA.set('K'.code)
|
||||||
|
UP_ALPHA.set('L'.code)
|
||||||
|
UP_ALPHA.set('M'.code)
|
||||||
|
UP_ALPHA.set('N'.code)
|
||||||
|
UP_ALPHA.set('O'.code)
|
||||||
|
UP_ALPHA.set('P'.code)
|
||||||
|
UP_ALPHA.set('Q'.code)
|
||||||
|
UP_ALPHA.set('R'.code)
|
||||||
|
UP_ALPHA.set('S'.code)
|
||||||
|
UP_ALPHA.set('T'.code)
|
||||||
|
UP_ALPHA.set('U'.code)
|
||||||
|
UP_ALPHA.set('V'.code)
|
||||||
|
UP_ALPHA.set('W'.code)
|
||||||
|
UP_ALPHA.set('X'.code)
|
||||||
|
UP_ALPHA.set('Y'.code)
|
||||||
|
UP_ALPHA.set('Z'.code)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* alpha = lowalpha | upalpha
|
||||||
|
*/
|
||||||
|
val ALPHA = BitSet()
|
||||||
|
|
||||||
|
init {
|
||||||
|
ALPHA.or(LOW_ALPHA)
|
||||||
|
ALPHA.or(UP_ALPHA)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
|
||||||
|
*/
|
||||||
|
val DIGIT = BitSet()
|
||||||
|
|
||||||
|
init {
|
||||||
|
DIGIT.set('0'.code)
|
||||||
|
DIGIT.set('1'.code)
|
||||||
|
DIGIT.set('2'.code)
|
||||||
|
DIGIT.set('3'.code)
|
||||||
|
DIGIT.set('4'.code)
|
||||||
|
DIGIT.set('5'.code)
|
||||||
|
DIGIT.set('6'.code)
|
||||||
|
DIGIT.set('7'.code)
|
||||||
|
DIGIT.set('8'.code)
|
||||||
|
DIGIT.set('9'.code)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* alphanum = alpha | digit
|
||||||
|
*/
|
||||||
|
val ALPHANUM = BitSet()
|
||||||
|
|
||||||
|
init {
|
||||||
|
ALPHANUM.or(ALPHA)
|
||||||
|
ALPHANUM.or(DIGIT)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unreserved = alphanum | mark
|
||||||
|
*/
|
||||||
|
val UNRESERVED = BitSet()
|
||||||
|
|
||||||
|
init {
|
||||||
|
UNRESERVED.or(ALPHANUM)
|
||||||
|
UNRESERVED.or(MARK)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pchar = unreserved | escaped | ":" | "@" | "&" | "=" | "+" | "$" | ","
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Note: we don't allow escaped here since we will escape it ourselves, so we don't want to allow them in the
|
||||||
|
* unescaped sequences
|
||||||
|
*/
|
||||||
|
val PCHAR = BitSet()
|
||||||
|
|
||||||
|
init {
|
||||||
|
PCHAR.or(UNRESERVED)
|
||||||
|
PCHAR.set(':'.code)
|
||||||
|
PCHAR.set('@'.code)
|
||||||
|
PCHAR.set('&'.code)
|
||||||
|
PCHAR.set('='.code)
|
||||||
|
PCHAR.set('+'.code)
|
||||||
|
PCHAR.set('$'.code)
|
||||||
|
PCHAR.set(','.code)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a string to be a valid path parameter URL, which means it can contain PCHAR* only (do not put the leading
|
||||||
|
* ";" or it will be escaped.
|
||||||
|
*
|
||||||
|
* @throws UnsupportedEncodingException
|
||||||
|
*/
|
||||||
|
@Throws(UnsupportedEncodingException::class)
|
||||||
|
fun encodePathParam(pathParam: String, charset: Charset): String {
|
||||||
|
return encodePathSegment(pathParam, charset)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a string to be a valid path segment URL, which means it can contain PCHAR* only (do not put path
|
||||||
|
* parameters or they will be escaped.
|
||||||
|
*
|
||||||
|
* @throws UnsupportedEncodingException
|
||||||
|
*/
|
||||||
|
@Throws(UnsupportedEncodingException::class)
|
||||||
|
fun encodePathSegment(pathSegment: String, charset: Charset): String {
|
||||||
|
// start at *3 for the worst case when everything is %encoded on one byte
|
||||||
|
val encoded = StringBuffer(pathSegment.length * 3)
|
||||||
|
val toEncode = pathSegment.toCharArray()
|
||||||
|
|
||||||
|
for (i in toEncode.indices) {
|
||||||
|
val c = toEncode[i]
|
||||||
|
if (PCHAR[c.code]) {
|
||||||
|
encoded.append(c)
|
||||||
|
} else {
|
||||||
|
val bytes = c.toString().toByteArray(charset)
|
||||||
|
for (j in bytes.indices) {
|
||||||
|
val b = bytes[j]
|
||||||
|
// make it unsigned (safe, since we only goto max 255, but makes conversion to hex easier)
|
||||||
|
val u8: Int = b.toInt() and 0xFF
|
||||||
|
encoded.append("%")
|
||||||
|
if (u8 < 16) encoded.append("0")
|
||||||
|
encoded.append(Integer.toHexString(u8))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return encoded.toString()
|
||||||
|
}
|
||||||
|
}
|
638
src/dorkbox/util/WebUtil.kt
Normal file
638
src/dorkbox/util/WebUtil.kt
Normal file
@ -0,0 +1,638 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 dorkbox, llc
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Tres Finocchiaro, QZ Industries, LLC
|
||||||
|
* Derivative code has been released as Apache 2.0, used with permission.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dorkbox.util
|
||||||
|
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.net.HttpURLConnection
|
||||||
|
import java.net.URL
|
||||||
|
import java.net.URLDecoder
|
||||||
|
import java.net.UnknownHostException
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
|
import java.util.regex.*
|
||||||
|
import javax.net.ssl.HostnameVerifier
|
||||||
|
import javax.net.ssl.HttpsURLConnection
|
||||||
|
import javax.net.ssl.SSLContext
|
||||||
|
import javax.net.ssl.TrustManager
|
||||||
|
import javax.net.ssl.X509TrustManager
|
||||||
|
|
||||||
|
object WebUtil {
|
||||||
|
private val SECOND_LEVEL_DOMAIN_PATTERN = Pattern.compile("^(https?:\\/\\/)?([\\dA-Za-z\\.-]+)\\.([a-z\\.]{2,6})([\\w \\.-]*)*$")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regular expression to match all IANA top-level domains.
|
||||||
|
* List accurate as of 2010/02/05. List taken from:
|
||||||
|
* http://data.iana.org/TLD/tlds-alpha-by-domain.txt
|
||||||
|
* This pattern is auto-generated by frameworks/base/common/tools/make-iana-tld-pattern.py
|
||||||
|
*/
|
||||||
|
@Volatile
|
||||||
|
private var TOP_LEVEL_DOMAIN_STR_FOR_WEB_URL = ("((aaa|aarp|abarth|abb|abbott|abbvie|abc|able|abogado|abudhabi|academy|accenture|accountant|accountants|aco|actor|adac|ads|adult|aeg|aero|aetna|afamilycompany|afl|africa|agakhan|agency|aig|airbus|airforce|airtel|akdn|alfaromeo|alibaba|alipay|allfinanz|allstate|ally|alsace|alstom|amazon|americanexpress|americanfamily|amex|amfam|amica|amsterdam|analytics|android|anquan|anz|aol|apartments|app|apple|aquarelle|arab|aramco|archi|army|arpa|art|arte|asda|asia|associates|athleta|attorney|auction|audi|audible|audio|auspost|author|auto|autos|avianca|aws|axa|azure|a[cdefgilmoqrstuwxz])"
|
||||||
|
+ "|(baby|baidu|banamex|bananarepublic|band|bank|bar|barcelona|barclaycard|barclays|barefoot|bargains|baseball|basketball|bauhaus|bayern|bbc|bbt|bbva|bcg|bcn|beats|beauty|beer|bentley|berlin|best|bestbuy|bet|bharti|bible|bid|bike|bing|bingo|bio|biz|black|blackfriday|blockbuster|blog|bloomberg|blue|bms|bmw|bnpparibas|boats|boehringer|bofa|bom|bond|boo|book|booking|bosch|bostik|boston|bot|boutique|box|bradesco|bridgestone|broadway|broker|brother|brussels|budapest|bugatti|build|builders|business|buy|buzz|bzh|b[abdefghijmnorstvwyz])"
|
||||||
|
+ "|(cab|cafe|cal|call|calvinklein|cam|camera|camp|cancerresearch|canon|capetown|capital|capitalone|car|caravan|cards|care|career|careers|cars|casa|case|caseih|cash|casino|cat|catering|catholic|cba|cbn|cbre|cbs|ceb|center|ceo|cern|cfa|cfd|chanel|channel|charity|chase|chat|cheap|chintai|christmas|chrome|church|cipriani|circle|cisco|citadel|citi|citic|city|cityeats|claims|cleaning|click|clinic|clinique|clothing|cloud|club|clubmed|coach|codes|coffee|college|cologne|com|comcast|commbank|community|company|compare|computer|comsec|condos|construction|consulting|contact|contractors|cooking|cookingchannel|cool|coop|corsica|country|coupon|coupons|courses|cpa|credit|creditcard|creditunion|cricket|crown|crs|cruise|cruises|csc|cuisinella|cymru|cyou|c[acdfghiklmnoruvwxyz])"
|
||||||
|
+ "|(dabur|dad|dance|data|date|dating|datsun|day|dclk|dds|deal|dealer|deals|degree|delivery|dell|deloitte|delta|democrat|dental|dentist|desi|design|dev|dhl|diamonds|diet|digital|direct|directory|discount|discover|dish|diy|dnp|docs|doctor|dog|domains|dot|download|drive|dtv|dubai|duck|dunlop|dupont|durban|dvag|dvr|d[ejkmoz])"
|
||||||
|
+ "|(earth|eat|eco|edeka|edu|education|email|emerck|energy|engineer|engineering|enterprises|epson|equipment|ericsson|erni|esq|estate|etisalat|eurovision|eus|events|exchange|expert|exposed|express|extraspace|e[cegrstu])"
|
||||||
|
+ "|(fage|fail|fairwinds|faith|family|fan|fans|farm|farmers|fashion|fast|fedex|feedback|ferrari|ferrero|fiat|fidelity|fido|film|final|finance|financial|fire|firestone|firmdale|fish|fishing|fit|fitness|flickr|flights|flir|florist|flowers|fly|foo|food|foodnetwork|football|ford|forex|forsale|forum|foundation|fox|free|fresenius|frl|frogans|frontdoor|frontier|ftr|fujitsu|fujixerox|fun|fund|furniture|futbol|fyi|f[ijkmor])"
|
||||||
|
+ "|(gal|gallery|gallo|gallup|game|games|gap|garden|gay|gbiz|gdn|gea|gent|genting|george|ggee|gift|gifts|gives|giving|glade|glass|gle|global|globo|gmail|gmbh|gmo|gmx|godaddy|gold|goldpoint|golf|goo|goodyear|goog|google|gop|got|gov|grainger|graphics|gratis|green|gripe|grocery|group|guardian|gucci|guge|guide|guitars|guru|g[abdefghilmnpqrstuwy])"
|
||||||
|
+ "|(hair|hamburg|hangout|haus|hbo|hdfc|hdfcbank|health|healthcare|help|helsinki|here|hermes|hgtv|hiphop|hisamitsu|hitachi|hiv|hkt|hockey|holdings|holiday|homedepot|homegoods|homes|homesense|honda|horse|hospital|host|hosting|hot|hoteles|hotels|hotmail|house|how|hsbc|hughes|hyatt|hyundai|h[kmnrtu])"
|
||||||
|
+ "|(ibm|icbc|ice|icu|ieee|ifm|ikano|imamat|imdb|immo|immobilien|inc|industries|infiniti|info|ing|ink|institute|insurance|insure|int|intel|international|intuit|investments|ipiranga|irish|ismaili|ist|istanbul|itau|itv|iveco|i[delmnoqrst])"
|
||||||
|
+ "|(jaguar|java|jcb|jcp|jeep|jetzt|jewelry|jio|jll|jmp|jnj|jobs|joburg|jot|joy|jpmorgan|jprs|juegos|juniper|j[emop])"
|
||||||
|
+ "|(kaufen|kddi|kerryhotels|kerrylogistics|kerryproperties|kfh|kia|kim|kinder|kindle|kitchen|kiwi|koeln|komatsu|kosher|kpmg|kpn|krd|kred|kuokgroup|kyoto|k[eghimnprwyz])"
|
||||||
|
+ "|(lacaixa|lamborghini|lamer|lancaster|lancia|land|landrover|lanxess|lasalle|lat|latino|latrobe|law|lawyer|lds|lease|leclerc|lefrak|legal|lego|lexus|lgbt|lidl|life|lifeinsurance|lifestyle|lighting|like|lilly|limited|limo|lincoln|linde|link|lipsy|live|living|lixil|llc|llp|loan|loans|locker|locus|loft|lol|london|lotte|lotto|love|lpl|lplfinancial|ltd|ltda|lundbeck|lupin|luxe|luxury|l[abcikrstuvy])"
|
||||||
|
+ "|(macys|madrid|maif|maison|makeup|man|management|mango|map|market|marketing|markets|marriott|marshalls|maserati|mattel|mba|mckinsey|med|media|meet|melbourne|meme|memorial|men|menu|merckmsd|miami|microsoft|mil|mini|mint|mit|mitsubishi|mlb|mls|mma|mobi|mobile|moda|moe|moi|mom|monash|money|monster|mormon|mortgage|moscow|moto|motorcycles|mov|movie|msd|mtn|mtr|museum|mutual|m[acdeghklmnopqrstuvwxyz])"
|
||||||
|
+ "|(nab|nagoya|name|nationwide|natura|navy|nba|nec|net|netbank|netflix|network|neustar|new|newholland|news|next|nextdirect|nexus|nfl|ngo|nhk|nico|nike|nikon|ninja|nissan|nissay|nokia|northwesternmutual|norton|now|nowruz|nowtv|nra|nrw|ntt|nyc|n[acefgilopruz])"
|
||||||
|
+ "|(obi|observer|off|office|okinawa|olayan|olayangroup|oldnavy|ollo|omega|one|ong|onl|online|onyourside|ooo|open|oracle|orange|org|organic|origins|osaka|otsuka|ott|ovh|om)"
|
||||||
|
+ "|(page|panasonic|paris|pars|partners|parts|party|passagens|pay|pccw|pet|pfizer|pharmacy|phd|philips|phone|photo|photography|photos|physio|pics|pictet|pictures|pid|pin|ping|pink|pioneer|pizza|place|play|playstation|plumbing|plus|pnc|pohl|poker|politie|porn|post|pramerica|praxi|press|prime|pro|prod|productions|prof|progressive|promo|properties|property|protection|pru|prudential|pub|pwc|p[aefghklmnrstwy])"
|
||||||
|
+ "|(qpon|quebec|quest|qvc|qa)"
|
||||||
|
+ "|(racing|radio|raid|read|realestate|realtor|realty|recipes|red|redstone|redumbrella|rehab|reise|reisen|reit|reliance|ren|rent|rentals|repair|report|republican|rest|restaurant|review|reviews|rexroth|rich|richardli|ricoh|ril|rio|rip|rmit|rocher|rocks|rodeo|rogers|room|rsvp|rugby|ruhr|run|rwe|ryukyu|r[eosuw])"
|
||||||
|
+ "|(saarland|safe|safety|sakura|sale|salon|samsclub|samsung|sandvik|sandvikcoromant|sanofi|sap|sarl|sas|save|saxo|sbi|sbs|sca|scb|schaeffler|schmidt|scholarships|school|schule|schwarz|science|scjohnson|scot|search|seat|secure|security|seek|select|sener|services|ses|seven|sew|sex|sexy|sfr|shangrila|sharp|shaw|shell|shia|shiksha|shoes|shop|shopping|shouji|show|showtime|shriram|silk|sina|singles|site|ski|skin|sky|skype|sling|smart|smile|sncf|soccer|social|softbank|software|sohu|solar|solutions|song|sony|soy|space|sport|spot|spreadbetting|srl|stada|staples|star|statebank|statefarm|stc|stcgroup|stockholm|storage|store|stream|studio|study|style|sucks|supplies|supply|support|surf|surgery|suzuki|swatch|swiftcover|swiss|sydney|systems|s[abcdeghijklmnorstuvxyz])"
|
||||||
|
+ "|(tab|taipei|talk|taobao|target|tatamotors|tatar|tattoo|tax|taxi|tci|tdk|team|tech|technology|tel|temasek|tennis|teva|thd|theater|theatre|tiaa|tickets|tienda|tiffany|tips|tires|tirol|tjmaxx|tjx|tkmaxx|tmall|today|tokyo|tools|top|toray|toshiba|total|tours|town|toyota|toys|trade|trading|training|travel|travelchannel|travelers|travelersinsurance|trust|trv|tube|tui|tunes|tushu|tvs|t[cdfghjklmnortvwz])"
|
||||||
|
+ "|(ubank|ubs|unicom|university|uno|uol|ups|u[agksyz])"
|
||||||
|
+ "|(vacations|vana|vanguard|vegas|ventures|verisign|versicherung|vet|viajes|video|vig|viking|villas|vin|vip|virgin|visa|vision|viva|vivo|vlaanderen|vodka|volkswagen|volvo|vote|voting|voto|voyage|vuelos|v[aceginu])"
|
||||||
|
+ "|(wales|walmart|walter|wang|wanggou|watch|watches|weather|weatherchannel|webcam|weber|website|wed|wedding|weibo|weir|whoswho|wien|wiki|williamhill|win|windows|wine|winners|wme|wolterskluwer|woodside|work|works|world|wow|wtc|wtf|w[fs])"
|
||||||
|
+ "|(xbox|xerox|xfinity|xihuan|xin|xn\\-\\-11b4c3d|xn\\-\\-1ck2e1b|xn\\-\\-1qqw23a|xn\\-\\-2scrj9c|xn\\-\\-30rr7y|xn\\-\\-3bst00m|xn\\-\\-3ds443g|xn\\-\\-3e0b707e|xn\\-\\-3hcrj9c|xn\\-\\-3oq18vl8pn36a|xn\\-\\-3pxu8k|xn\\-\\-42c2d9a|xn\\-\\-45br5cyl|xn\\-\\-45brj9c|xn\\-\\-45q11c|xn\\-\\-4gbrim|xn\\-\\-54b7fta0cc|xn\\-\\-55qw42g|xn\\-\\-55qx5d|xn\\-\\-5su34j936bgsg|xn\\-\\-5tzm5g|xn\\-\\-6frz82g|xn\\-\\-6qq986b3xl|xn\\-\\-80adxhks|xn\\-\\-80ao21a|xn\\-\\-80aqecdr1a|xn\\-\\-80asehdb|xn\\-\\-80aswg|xn\\-\\-8y0a063a|xn\\-\\-90a3ac|xn\\-\\-90ae|xn\\-\\-90ais|xn\\-\\-9dbq2a|xn\\-\\-9et52u|xn\\-\\-9krt00a|xn\\-\\-b4w605ferd|xn\\-\\-bck1b9a5dre4c|xn\\-\\-c1avg|xn\\-\\-c2br7g|xn\\-\\-cck2b3b|xn\\-\\-cckwcxetd|xn\\-\\-cg4bki|xn\\-\\-clchc0ea0b2g2a9gcd|xn\\-\\-czr694b|xn\\-\\-czrs0t|xn\\-\\-czru2d|xn\\-\\-d1acj3b|xn\\-\\-d1alf|xn\\-\\-e1a4c|xn\\-\\-eckvdtc9d|xn\\-\\-efvy88h|xn\\-\\-fct429k|xn\\-\\-fhbei|xn\\-\\-fiq228c5hs|xn\\-\\-fiq64b|xn\\-\\-fiqs8s|xn\\-\\-fiqz9s|xn\\-\\-fjq720a|xn\\-\\-flw351e|xn\\-\\-fpcrj9c3d|xn\\-\\-fzc2c9e2c|xn\\-\\-fzys8d69uvgm|xn\\-\\-g2xx48c|xn\\-\\-gckr3f0f|xn\\-\\-gecrj9c|xn\\-\\-gk3at1e|xn\\-\\-h2breg3eve|xn\\-\\-h2brj9c|xn\\-\\-h2brj9c8c|xn\\-\\-hxt814e|xn\\-\\-i1b6b1a6a2e|xn\\-\\-imr513n|xn\\-\\-io0a7i|xn\\-\\-j1aef|xn\\-\\-j1amh|xn\\-\\-j6w193g|xn\\-\\-jlq480n2rg|xn\\-\\-jlq61u9w7b|xn\\-\\-jvr189m|xn\\-\\-kcrx77d1x4a|xn\\-\\-kprw13d|xn\\-\\-kpry57d|xn\\-\\-kput3i|xn\\-\\-l1acc|xn\\-\\-lgbbat1ad8j|xn\\-\\-mgb9awbf|xn\\-\\-mgba3a3ejt|xn\\-\\-mgba3a4f16a|xn\\-\\-mgba7c0bbn0a|xn\\-\\-mgbaakc7dvf|xn\\-\\-mgbaam7a8h|xn\\-\\-mgbab2bd|xn\\-\\-mgbah1a3hjkrd|xn\\-\\-mgbai9azgqp6j|xn\\-\\-mgbayh7gpa|xn\\-\\-mgbbh1a|xn\\-\\-mgbbh1a71e|xn\\-\\-mgbc0a9azcg|xn\\-\\-mgbca7dzdo|xn\\-\\-mgbcpq6gpa1a|xn\\-\\-mgberp4a5d4ar|xn\\-\\-mgbgu82a|xn\\-\\-mgbi4ecexp|xn\\-\\-mgbpl2fh|xn\\-\\-mgbt3dhd|xn\\-\\-mgbtx2b|xn\\-\\-mgbx4cd0ab|xn\\-\\-mix891f|xn\\-\\-mk1bu44c|xn\\-\\-mxtq1m|xn\\-\\-ngbc5azd|xn\\-\\-ngbe9e0a|xn\\-\\-ngbrx|xn\\-\\-node|xn\\-\\-nqv7f|xn\\-\\-nqv7fs00ema|xn\\-\\-nyqy26a|xn\\-\\-o3cw4h|xn\\-\\-ogbpf8fl|xn\\-\\-otu796d|xn\\-\\-p1acf|xn\\-\\-p1ai|xn\\-\\-pgbs0dh|xn\\-\\-pssy2u|xn\\-\\-q7ce6a|xn\\-\\-q9jyb4c|xn\\-\\-qcka1pmc|xn\\-\\-qxa6a|xn\\-\\-qxam|xn\\-\\-rhqv96g|xn\\-\\-rovu88b|xn\\-\\-rvc1e0am3e|xn\\-\\-s9brj9c|xn\\-\\-ses554g|xn\\-\\-t60b56a|xn\\-\\-tckwe|xn\\-\\-tiq49xqyj|xn\\-\\-unup4y|xn\\-\\-vermgensberater\\-ctb|xn\\-\\-vermgensberatung\\-pwb|xn\\-\\-vhquv|xn\\-\\-vuq861b|xn\\-\\-w4r85el8fhu5dnra|xn\\-\\-w4rs40l|xn\\-\\-wgbh1c|xn\\-\\-wgbl6a|xn\\-\\-xhq521b|xn\\-\\-xkc2al3hye2a|xn\\-\\-xkc2dl3a5ee0h|xn\\-\\-y9a3aq|xn\\-\\-yfro4i67o|xn\\-\\-ygbi2ammx|xn\\-\\-zfr164b|xxx|xyz)"
|
||||||
|
+ "|(yachts|yahoo|yamaxun|yandex|yodobashi|yoga|yokohama|you|youtube|yun|y[et])"
|
||||||
|
+ "|(zappos|zara|zero|zip|zone|zuerich|z[amw])))")
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Good characters for Internationalized Resource Identifiers (IRI).
|
||||||
|
* This comprises most common used Unicode characters allowed in IRI
|
||||||
|
* as detailed in RFC 3987.
|
||||||
|
* Specifically, those two byte Unicode characters are not included.
|
||||||
|
*/
|
||||||
|
const val GOOD_IRI_CHAR = "a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the WEB_URL pattern as dirty, and will recompile it on its next usage
|
||||||
|
*/
|
||||||
|
@Volatile
|
||||||
|
private var MARK_URL_PATTERN_DIRTY = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regular expression pattern to match most part of RFC 3987
|
||||||
|
* Internationalized URLs, aka IRIs. Commonly used Unicode characters are
|
||||||
|
* added.
|
||||||
|
*/
|
||||||
|
@Volatile
|
||||||
|
private var WEB_URL = compileWebUrl()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the web URL mega-regex, and marks usages as dirty (so they are updated)
|
||||||
|
*/
|
||||||
|
fun updateWebUrlRegex(topLeveDomainUrls: String) {
|
||||||
|
TOP_LEVEL_DOMAIN_STR_FOR_WEB_URL = topLeveDomainUrls
|
||||||
|
MARK_URL_PATTERN_DIRTY = true // update the next time we use it.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun compileWebUrl(): Pattern {
|
||||||
|
return Pattern.compile(
|
||||||
|
"((?:(http|https|Http|Https|rtsp|Rtsp):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)"
|
||||||
|
+ "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_"
|
||||||
|
+ "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?"
|
||||||
|
+ "((?:(?:[" + GOOD_IRI_CHAR + "][" + GOOD_IRI_CHAR + "\\-]{0,64}\\.)+" // named host
|
||||||
|
+ TOP_LEVEL_DOMAIN_STR_FOR_WEB_URL
|
||||||
|
+ "|(?:(?:25[0-5]|2[0-4]" // or ip address
|
||||||
|
+ "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(?:25[0-5]|2[0-4][0-9]"
|
||||||
|
+ "|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1]"
|
||||||
|
+ "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}"
|
||||||
|
+ "|[1-9][0-9]|[0-9])))"
|
||||||
|
+ "(?:\\:\\d{1,5})?)" // plus option port number
|
||||||
|
+ "(\\/(?:(?:[a-zA-Z0-9\\;\\/\\?\\:\\@\\&\\=\\#\\~" // plus option query params
|
||||||
|
+ "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?"
|
||||||
|
+ "(?:\\b|$)")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only removes http?s:// and the path (if it's present) and www. (if it's present). Also removes *. (if it's present)
|
||||||
|
* ie:
|
||||||
|
* http://foo.com/index.php --> foo.com
|
||||||
|
* https://www.aa.foo.com/index.php --> aa.foo.com
|
||||||
|
* https://www.aa.foo.com/index&foo%bar --> aa.foo.com
|
||||||
|
* https://www.aa.foo.com%foobar --> aa.foo.com
|
||||||
|
*/
|
||||||
|
fun cleanupAndRemoveWwwAndPath(fullDomainName: String): String {
|
||||||
|
var start = fullDomainName.indexOf("://")
|
||||||
|
if (start == -1) {
|
||||||
|
start = 0
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
start += 3 // 3 is the length of ://
|
||||||
|
}
|
||||||
|
|
||||||
|
// get rid of the www. part if it exists.
|
||||||
|
val www = fullDomainName.indexOf("www.", start)
|
||||||
|
if (www > -1 && www <= 8) {
|
||||||
|
start = www + 4 // 4 is the length of www.
|
||||||
|
}
|
||||||
|
|
||||||
|
val star = fullDomainName.indexOf("*.", start)
|
||||||
|
if (star > -1) {
|
||||||
|
start = star + 2 // 2 is the length of *.
|
||||||
|
}
|
||||||
|
|
||||||
|
var end = fullDomainName.length
|
||||||
|
|
||||||
|
val slash = fullDomainName.indexOf("/", start + 3)
|
||||||
|
if (slash > -1 && slash < end) {
|
||||||
|
end = slash
|
||||||
|
}
|
||||||
|
|
||||||
|
val colon = fullDomainName.indexOf(":", start + 3)
|
||||||
|
if (colon > -1 && colon < end) {
|
||||||
|
end = colon
|
||||||
|
}
|
||||||
|
|
||||||
|
val percent = fullDomainName.indexOf("%", start)
|
||||||
|
if (percent > -1 && percent < end) {
|
||||||
|
end = percent
|
||||||
|
}
|
||||||
|
|
||||||
|
val ampersand = fullDomainName.indexOf("&", start)
|
||||||
|
if (ampersand > -1 && ampersand < end) {
|
||||||
|
end = ampersand
|
||||||
|
}
|
||||||
|
|
||||||
|
val question = fullDomainName.indexOf("?", start)
|
||||||
|
if (question > -1 && question < end) {
|
||||||
|
end = question
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return fullDomainName.substring(start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only removes http?s:// and www. (if it's present). Also removes *. (if it's present)
|
||||||
|
* ie:
|
||||||
|
* http://foo.com/index.php --> foo.com/index.php
|
||||||
|
* https://www.aa.foo.com/index.php --> aa.foo.com/index.php
|
||||||
|
* https://www.aa.foo.com/index&foo%bar --> aa.foo.com/index&foo%bar
|
||||||
|
* https://www.aa.foo.com%foobar --> aa.foo.com%foobar
|
||||||
|
*/
|
||||||
|
fun cleanupAndPreservePath(fullDomainName: String, removeQueryString: Boolean = true): String {
|
||||||
|
var start = fullDomainName.indexOf("://")
|
||||||
|
if (start == -1) {
|
||||||
|
start = 0
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
start += 3 // 3 is the length of ://
|
||||||
|
}
|
||||||
|
|
||||||
|
// get rid of the www. part if it exists.
|
||||||
|
val www = fullDomainName.indexOf("www.", start)
|
||||||
|
if (www > -1 && www <= 8) {
|
||||||
|
start = www + 4 // 4 is the length of www.
|
||||||
|
}
|
||||||
|
|
||||||
|
val star = fullDomainName.indexOf("*.", start)
|
||||||
|
if (star > -1) {
|
||||||
|
start = star + 2 // 2 is the length of *.
|
||||||
|
}
|
||||||
|
|
||||||
|
var end = if (removeQueryString) {
|
||||||
|
var end = fullDomainName.length
|
||||||
|
|
||||||
|
val percent = fullDomainName.indexOf("%", start)
|
||||||
|
if (percent > -1 && percent < end) {
|
||||||
|
end = percent
|
||||||
|
}
|
||||||
|
|
||||||
|
val ampersand = fullDomainName.indexOf("&", start)
|
||||||
|
if (ampersand > -1 && ampersand < end) {
|
||||||
|
end = ampersand
|
||||||
|
}
|
||||||
|
|
||||||
|
val question = fullDomainName.indexOf("?", start)
|
||||||
|
if (question > -1 && question < end) {
|
||||||
|
end = question
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
} else {
|
||||||
|
fullDomainName.length
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the last char is a /, remove it
|
||||||
|
if (end -1 >= 0 && fullDomainName[end - 1] == '/') {
|
||||||
|
end--
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullDomainName.substring(start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only removes www. (if it's present). Also removes *. (if it's present)
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ie:
|
||||||
|
* foo.com/index.php --> foo.com
|
||||||
|
* www.aa.foo.com/index.php --> aa.foo.com
|
||||||
|
* www.aa.foo.com/index&foo%bar --> aa.foo.com
|
||||||
|
* www.aa.foo.com%foobar --> aa.foo.com
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* NOTE: ONLY use this if you can GUARANTEE that there is no http?s://
|
||||||
|
*/
|
||||||
|
fun removeWww(fullDomainName: String?): String? {
|
||||||
|
if (fullDomainName == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// get rid of the www. part if it exists.
|
||||||
|
var start = fullDomainName.indexOf("www.")
|
||||||
|
if (start > -1) {
|
||||||
|
start += 4 // 4 is the length of www.
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
start = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
val star = fullDomainName.indexOf("*.", start)
|
||||||
|
if (star > -1) {
|
||||||
|
start = star + 2 // 2 is the length of *.
|
||||||
|
}
|
||||||
|
|
||||||
|
var end = fullDomainName.indexOf("/", start + 3)
|
||||||
|
if (end == -1) {
|
||||||
|
if (start == 0) {
|
||||||
|
// it was already clean.
|
||||||
|
return fullDomainName
|
||||||
|
}
|
||||||
|
|
||||||
|
end = fullDomainName.length
|
||||||
|
}
|
||||||
|
|
||||||
|
val percent = fullDomainName.indexOf("%", start)
|
||||||
|
if (percent > -1 && percent < end) {
|
||||||
|
end = percent
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullDomainName.substring(start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isValidUrl(url: String?): Boolean {
|
||||||
|
return if (url.isNullOrEmpty()) {
|
||||||
|
false // Don't even need to check, not a valid domain
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (MARK_URL_PATTERN_DIRTY) {
|
||||||
|
// race conditions don't matter, this just guarantees that it's updated.
|
||||||
|
WEB_URL = compileWebUrl()
|
||||||
|
MARK_URL_PATTERN_DIRTY = false
|
||||||
|
}
|
||||||
|
|
||||||
|
val m = WEB_URL.matcher(url)
|
||||||
|
m.matches()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isSubDomain(fullDomainName: String): Boolean {
|
||||||
|
var start = fullDomainName.indexOf("://")
|
||||||
|
if (start == -1) {
|
||||||
|
start = 0
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
start += 3
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullDomainName.contains("www.")) {
|
||||||
|
start += 4 // 4 is the length of www.
|
||||||
|
}
|
||||||
|
|
||||||
|
var end = fullDomainName.indexOf("/", start + 3)
|
||||||
|
if (end == -1) {
|
||||||
|
end = fullDomainName.length
|
||||||
|
}
|
||||||
|
|
||||||
|
val substring = fullDomainName.substring(start, end)
|
||||||
|
|
||||||
|
val dots = substring.count { it == '.' }
|
||||||
|
|
||||||
|
return dots > 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only remove http?s://www and the path (if it's present).
|
||||||
|
* Get the next level domain after cleanup if next level domain is not top level domain.
|
||||||
|
* ie:
|
||||||
|
* http://www.a.b.foo.com -> b.foo.com
|
||||||
|
* https://www.foo.com -> foo.com
|
||||||
|
* foo.com -> foo.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun cleanupAndGetNextLevelDomain(fullDomainName: String): String? {
|
||||||
|
var start = fullDomainName.indexOf("://")
|
||||||
|
if (start == -1) {
|
||||||
|
start = 0
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
start += 3
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullDomainName.contains("www.")) {
|
||||||
|
start += 4 // 4 is the length of www.
|
||||||
|
}
|
||||||
|
|
||||||
|
var end = fullDomainName.indexOf("/", start + 3)
|
||||||
|
if (end == -1) {
|
||||||
|
end = fullDomainName.length
|
||||||
|
}
|
||||||
|
|
||||||
|
var substring = fullDomainName.substring(start, end)
|
||||||
|
val last = substring
|
||||||
|
|
||||||
|
val nextDot = substring.indexOf(".")
|
||||||
|
if (nextDot == -1) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
substring = substring.substring(nextDot + 1)
|
||||||
|
|
||||||
|
if (DomainUtils.isTLD(substring)) {
|
||||||
|
substring = last
|
||||||
|
}
|
||||||
|
|
||||||
|
return substring
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getNextLevelDomain(fullDomainName: String): String? {
|
||||||
|
val nextDot = fullDomainName.indexOf(".")
|
||||||
|
if (nextDot == -1) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullDomainName.substring(nextDot + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only removes http?s:// and the path (if it's present).
|
||||||
|
* ie:
|
||||||
|
* http://foo.com/index.php --> foo.com
|
||||||
|
* https://www.aa.foo.com/index.php --> foo.com
|
||||||
|
*/
|
||||||
|
fun cleanupAndGetSecondLevelDomain(fullDomainName: String): String? {
|
||||||
|
// File URLs will return null at the extractSLD step, so this case is explicitly for logging purposes.
|
||||||
|
// We want to know when the returned value is null because it's a file, vs returning null for other reasons.
|
||||||
|
if (fullDomainName.startsWith("file://", true)){
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
var start = fullDomainName.indexOf("://")
|
||||||
|
if (start == -1) {
|
||||||
|
start = 0
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
start += 3
|
||||||
|
}
|
||||||
|
|
||||||
|
var end = fullDomainName.indexOf("/", start + 3)
|
||||||
|
if (end == -1) {
|
||||||
|
if (start == 0) {
|
||||||
|
// it was already clean.
|
||||||
|
return DomainUtils.extractSLD(fullDomainName)
|
||||||
|
}
|
||||||
|
|
||||||
|
end = fullDomainName.length
|
||||||
|
}
|
||||||
|
|
||||||
|
// for now, get the SLD as well
|
||||||
|
val substring= fullDomainName.substring(start, end)
|
||||||
|
return DomainUtils.extractSLD(substring)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the third level domain of google domains if it has one.
|
||||||
|
* ie:
|
||||||
|
* http://google.com/index.php -> google.com
|
||||||
|
* http://docs.google.com/index.php -> docs.google.com
|
||||||
|
* https://32.32.432.fdsa.docs.google.com/index.php -> docs.google.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun cleanupAndGetThirdLevelDomain(fullDomainName: String): String {
|
||||||
|
var cleanDomain = cleanupAndRemoveWwwAndPath(fullDomainName)
|
||||||
|
|
||||||
|
val periodCount = cleanDomain.count { it == '.'}
|
||||||
|
|
||||||
|
if (periodCount <= 2) {
|
||||||
|
return cleanDomain
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (x in periodCount downTo 3) {
|
||||||
|
val nextDot = cleanDomain.indexOf(".")
|
||||||
|
|
||||||
|
cleanDomain = cleanDomain.substring(nextDot + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleanDomain
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the last portion of the file uri, the file name itself.
|
||||||
|
* ie:
|
||||||
|
* file://Downloads/example.pdf -> example.pdf
|
||||||
|
* file:///media.jpg -> media.jpg
|
||||||
|
*/
|
||||||
|
fun cleanupFileUri(domain: String): String {
|
||||||
|
val lastSlashIndex = domain.lastIndexOf("/")
|
||||||
|
|
||||||
|
if (lastSlashIndex == -1) {
|
||||||
|
return domain
|
||||||
|
}
|
||||||
|
|
||||||
|
return domain.substring(lastSlashIndex + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun forceAcceptAllTlsCertificates() {
|
||||||
|
/*
|
||||||
|
* fix for
|
||||||
|
* Exception in thread "main" javax.net.ssl.SSLHandshakeException:
|
||||||
|
* sun.security.validator.ValidatorException:
|
||||||
|
* PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException:
|
||||||
|
* unable to find valid certification path to requested target
|
||||||
|
*/
|
||||||
|
val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
|
||||||
|
override fun getAcceptedIssuers(): Array<X509Certificate>? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun checkClientTrusted(certs: Array<X509Certificate>, authType: String) {}
|
||||||
|
|
||||||
|
override fun checkServerTrusted(certs: Array<X509Certificate>, authType: String) {}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
val sc = SSLContext.getInstance("SSL")
|
||||||
|
sc.init(null, trustAllCerts, java.security.SecureRandom())
|
||||||
|
HttpsURLConnection.setDefaultSSLSocketFactory(sc.socketFactory)
|
||||||
|
|
||||||
|
// Create all-trusting host name verifier
|
||||||
|
val allHostsValid = HostnameVerifier { hostname, session -> true }
|
||||||
|
|
||||||
|
// Install the all-trusting host verifier
|
||||||
|
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
println(cleanupAndPreservePath("https://www.youtube.com/watch?v=YP6EaIDlmEg&t=1s", removeQueryString = true))
|
||||||
|
println(cleanupAndPreservePath("https://www.khanacademy.org/", removeQueryString = true))
|
||||||
|
println(cleanupAndRemoveWwwAndPath("https://sat184.cloud1.tds.airast.org/student/V746/Pages/TestShell.aspx"))
|
||||||
|
println(cleanupAndRemoveWwwAndPath("https://sat184.cloud1.tds.airast.org/student/V746/Pages/TestShell.aspx"))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// println(WEB_URL.matcher("https://www.youtube.com/watch?v=WEVctuQTeaI").matches())
|
||||||
|
// println(WEB_URL.matcher("www.youtube.com/watch?v=WEVctuQTeaI").matches())
|
||||||
|
// println(WEB_URL.matcher("youtube.com/watch?v=WEVctuQTeaI").matches())
|
||||||
|
// println(WEB_URL.matcher("youtube.com").matches())
|
||||||
|
// println(WEB_URL.matcher("https://www.espn.com/nba/").matches())
|
||||||
|
// println(WEB_URL.matcher("https://www.espn.com/nba").matches())
|
||||||
|
// println(getNextLevelDomain("admin.regression.net-ref.com"))
|
||||||
|
// println(cleanupAndGetGoogleDomain("https://www.google.com/search?rlz=1CAZGSZ_enUS848&tbm=isch&q=pretty+backgrounds&chips=q:pretty+backgrounds,g_1:iphone:lJzZkCc6kg8%3D&usg=AI4_-kSfq6w5oVz33oUhcFfHeJC-MtmIww&sa=X&ved=0ahUKEwi0hP-Sk4riAhUUpJ4KHaWJDi0Q4lYIJigA&biw=1517&bih=695&dpr=0.9&safe=active&ssui=on"));
|
||||||
|
// println(cleanupAndRemoveWww("http://fasttmath.capousd.org:55880/fmng/loader/"))
|
||||||
|
// println(cleanupAndRemoveWww("http://fasttmath.capousd.org:55880/fmng/loader/"))
|
||||||
|
// println(cleanupAndRemoveWww("http://fasttmath.capousd.org:55880/fmng/loader/"))
|
||||||
|
// println(cleanupAndRemoveWww("https://clever.com/oauth/authorize?channel=clever-portal&client_id=8c54ced0462a3fe2da0a&confirmed=true&district_id=556cc0739496cf01000003f2" +
|
||||||
|
// "&redirect_uri=https%3A%2F%2Fapp.typingagent.com%2Fclever%2Findex%3Foauth%3Dtrue&response_type=code"))
|
||||||
|
// println(cleanupAndRemoveWww(
|
||||||
|
// "https://www.clever.com/oauth/authorize?channel=clever-portal&client_id=ae17f3b6f000d1bb4f2c&confirmed=true&district_id=556cc0739496cf01000003f2&redirect_uri=https%3A%2F%2Fwww" +
|
||||||
|
// ".khanacademy.org%2Flogin%2Fclever&response_type=code"))
|
||||||
|
// println(cleanupAndRemoveWww(cleanupAndRemoveWww("https://sat184.cloud1.tds.airast.org/student/V746/Pages/TestShell.aspx")))
|
||||||
|
//
|
||||||
|
// println(cleanupAndPreservePath("http://fasttmath.capousd.org:55880/fmng/loader/"))
|
||||||
|
// println(cleanupAndPreservePath(
|
||||||
|
// "https://www.clever.com/oauth/authorize?channel=clever-portal&client_id=ae17f3b6f000d1bb4f2c&confirmed=true&district_id=556cc0739496cf01000003f2&redirect_uri=https%3A%2F%2Fwww" +
|
||||||
|
// ".khanacademy.org%2Flogin%2Fclever&response_type=code"))
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the 'action' function when the scheme+domain+path(s) when it was successful. Runs the 'onError' function when it fails.
|
||||||
|
*/
|
||||||
|
suspend fun fetchData(scheme: String, domain: String, vararg paths: String,
|
||||||
|
onError: (String) ->Unit,
|
||||||
|
action: suspend (InputStream)->Unit) = withContext(Dispatchers.IO) {
|
||||||
|
val encodedPath = paths.joinToString(separator = "/") { URLEncoder.encodePathSegment(it, Charsets.UTF_8) }
|
||||||
|
var location = "scheme$domain/$encodedPath"
|
||||||
|
|
||||||
|
// logger.trace{ "Getting data: $location" }
|
||||||
|
|
||||||
|
// We DO want to support redirects, in case OLD code is running in the wild.
|
||||||
|
var base: URL
|
||||||
|
var next: URL
|
||||||
|
var visitedCount = 0
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
visitedCount += 1
|
||||||
|
if (visitedCount > 10) {
|
||||||
|
onError("Stuck in a loop for '$location' --- more than $visitedCount tries to get the domain '$location'")
|
||||||
|
return@withContext
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
base = URL(location)
|
||||||
|
with(base.openConnection() as HttpURLConnection) {
|
||||||
|
useCaches = false
|
||||||
|
instanceFollowRedirects = true
|
||||||
|
|
||||||
|
// if (logger.isTraceEnabled) {
|
||||||
|
// logger.trace { "Requesting URL : $url" }
|
||||||
|
// logger.trace { "Response Code : $responseCode" }
|
||||||
|
// }
|
||||||
|
|
||||||
|
when (responseCode) {
|
||||||
|
HttpURLConnection.HTTP_MOVED_PERM, HttpURLConnection.HTTP_MOVED_TEMP -> {
|
||||||
|
location = getHeaderField("Location")
|
||||||
|
// java.net.URLDecoder is only valid for query parameters/headers -- NOT FOR ACTUAL URLS!
|
||||||
|
location = URLDecoder.decode(location, Charsets.UTF_8)
|
||||||
|
|
||||||
|
// logger.trace { "Response to '$url' redirected to '$location'" }
|
||||||
|
|
||||||
|
next = URL(base, location) // Deal with relative URLs
|
||||||
|
location = next.toExternalForm()
|
||||||
|
|
||||||
|
// loop again with the new location
|
||||||
|
return@with
|
||||||
|
}
|
||||||
|
HttpURLConnection.HTTP_OK -> {
|
||||||
|
action(inputStream)
|
||||||
|
|
||||||
|
// done
|
||||||
|
return@withContext
|
||||||
|
}
|
||||||
|
HttpsURLConnection.HTTP_NOT_FOUND -> {
|
||||||
|
// if we are HTTPS, retry again as HTTP.
|
||||||
|
if (location.startsWith(scheme)) {
|
||||||
|
visitedCount = 0
|
||||||
|
|
||||||
|
location = if (scheme == "http://") {
|
||||||
|
"https://$domain/$encodedPath"
|
||||||
|
} else {
|
||||||
|
"http://$domain/$encodedPath"
|
||||||
|
}
|
||||||
|
|
||||||
|
// loop again with the new location
|
||||||
|
return@with
|
||||||
|
} else {
|
||||||
|
onError("Error '$responseCode' getting domain '$location' HTTPS option exhausted.")
|
||||||
|
|
||||||
|
// done
|
||||||
|
return@withContext
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
onError("Error '$responseCode' getting domain '$location'")
|
||||||
|
|
||||||
|
// done
|
||||||
|
return@withContext
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e: UnknownHostException) {
|
||||||
|
// TMI for what's going on. We just can't, so leave it at that.
|
||||||
|
onError("Failed to retrieve or write icon for domain: '${location}'")
|
||||||
|
return@withContext
|
||||||
|
}
|
||||||
|
catch (e: Exception) {
|
||||||
|
onError("Failed to retrieve or write icon for domain: '${location}'. ${e.message}")
|
||||||
|
return@withContext
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNREACHABLE_CODE")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
@ -30,7 +30,7 @@ class ConcurrentIterator<T> {
|
|||||||
/**
|
/**
|
||||||
* Specifies the load-factor for the IdentityMap used
|
* Specifies the load-factor for the IdentityMap used
|
||||||
*/
|
*/
|
||||||
public static volatile float LOAD_FACTOR = OS.getFloat(ConcurrentIterator.class.getCanonicalName() + ".LOAD_FACTOR", 0.8F);
|
public static volatile float LOAD_FACTOR = OS.INSTANCE.getFloat(ConcurrentIterator.class.getCanonicalName() + ".LOAD_FACTOR", 0.8F);
|
||||||
|
|
||||||
private static final AtomicInteger ID_COUNTER = new AtomicInteger();
|
private static final AtomicInteger ID_COUNTER = new AtomicInteger();
|
||||||
private final int ID = ID_COUNTER.getAndIncrement();
|
private final int ID = ID_COUNTER.getAndIncrement();
|
||||||
|
@ -43,7 +43,7 @@ class PropertiesProvider {
|
|||||||
throw new NullPointerException("propertiesFile");
|
throw new NullPointerException("propertiesFile");
|
||||||
}
|
}
|
||||||
|
|
||||||
propertiesFile = FileUtil.normalize(propertiesFile);
|
propertiesFile = FileUtil.INSTANCE.normalize(propertiesFile);
|
||||||
// make sure the parent dir exists...
|
// make sure the parent dir exists...
|
||||||
File parentFile = propertiesFile.getParentFile();
|
File parentFile = propertiesFile.getParentFile();
|
||||||
if (parentFile != null && !parentFile.exists()) {
|
if (parentFile != null && !parentFile.exists()) {
|
||||||
|
Loading…
Reference in New Issue
Block a user