OS tools converted to kotlin, updated file, common + web utilities.

This commit is contained in:
Robinson 2022-03-02 23:44:31 +01:00
parent e5478201ab
commit edd5c4d2b1
No known key found for this signature in database
GPG Key ID: 8E7DB78588BD6F5C
31 changed files with 3936 additions and 2715 deletions

View File

@ -55,8 +55,8 @@ public class JnaClassUtils {
private static final String libName;
static {
if (OS.isMacOsX()) {
if (OS.javaVersion < 7) {
if (OS.INSTANCE.isMacOsX()) {
if (OS.INSTANCE.javaVersion < 7) {
libName = "JavaVM";
} else {
String javaLocation = System.getProperty("java.home");
@ -77,7 +77,7 @@ public class JnaClassUtils {
Map options = new HashMap();
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() {
@Override
public

View File

@ -37,7 +37,7 @@ class JnaHelper {
final Map<String, Object> options = new HashMap<String, Object>();
options.put(Library.OPTION_CLASSLOADER, clazz.getClassLoader());
if (OS.isWindows()) {
if (OS.INSTANCE.isWindows()) {
Set<Map.Entry<String, Object>> entries = W32APIOptions.DEFAULT_OPTIONS.entrySet();
for (Map.Entry<String, Object> entry : entries) {
options.put(entry.getKey(), entry.getValue());

View File

@ -45,7 +45,7 @@ class AppIndicator {
static {
boolean _isLoaded = false;
boolean shouldLoadAppIndicator = !(OS.isWindows() || OS.isMacOsX());
boolean shouldLoadAppIndicator = !(OS.INSTANCE.isWindows() || OS.INSTANCE.isMacOsX());
if (!shouldLoadAppIndicator) {
_isLoaded = true;
}

View File

@ -118,7 +118,7 @@ class GtkCheck {
*/
// 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 {
// Don't want to load the toolkit!!!
Class<?> toolkitClass = Class.forName("java.awt.Toolkit");

View File

@ -88,7 +88,7 @@ class GtkLoader {
int minor = 0;
int micro = 0;
boolean shouldLoadGtk = !(OS.isWindows() || OS.isMacOsX());
boolean shouldLoadGtk = !(OS.INSTANCE.isWindows() || OS.INSTANCE.isMacOsX());
if (!shouldLoadGtk) {
_isLoaded = true;
}

View File

@ -48,10 +48,10 @@ import dorkbox.util.MathUtil;
public
class GtkTheme {
/** 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. */
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
Rectangle getPixelTextHeight(String text) {
@ -279,7 +279,7 @@ class GtkTheme {
try {
File customSettings = new File("/usr/bin/startkde-custom");
if (customSettings.canRead()) {
List<String> lines = FileUtil.readLines(customSettings);
List<String> lines = FileUtil.INSTANCE.readLines(customSettings);
for (String line : lines) {
String str = "export GDK_SCALE=";
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");
if (mainFile.canRead()) {
List<String> lines = FileUtil.readLines(mainFile);
List<String> lines = FileUtil.INSTANCE.readLines(mainFile);
boolean found = false;
int index;
for (final String line : lines) {
@ -394,7 +394,7 @@ class GtkTheme {
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)
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) {
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)

View File

@ -37,7 +37,7 @@ import dorkbox.os.OS;
@SuppressWarnings("WeakerAccess")
public
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;

View File

@ -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
View 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
}
}

View File

@ -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
View 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
}

View File

@ -81,7 +81,7 @@ class OSUtil {
int[] getVersion() {
int[] version = new int[2];
if (!OS.isWindows()) {
if (!OS.INSTANCE.isWindows()) {
return version;
}
@ -179,7 +179,7 @@ class OSUtil {
class Unix {
public static
boolean isFreeBSD() {
if (!OS.isUnix()) {
if (!OS.INSTANCE.isUnix()) {
return false;
}
@ -207,7 +207,7 @@ class OSUtil {
return info;
}
if (!OS.isLinux()) {
if (!OS.INSTANCE.isLinux()) {
info = "";
return info;
}
@ -751,7 +751,7 @@ class OSUtil {
private static volatile Boolean isMATE = null;
public static
boolean isMATE() {
if (!OS.isLinux() && !OS.isUnix()) {
if (!OS.INSTANCE.isLinux() && !OS.INSTANCE.isUnix()) {
return false;
}
@ -772,7 +772,7 @@ class OSUtil {
private static volatile Boolean isGnome = null;
public static
boolean isGnome() {
if (!OS.isLinux() && !OS.isUnix()) {
if (!OS.INSTANCE.isLinux() && !OS.INSTANCE.isUnix()) {
return false;
}
@ -787,7 +787,7 @@ class OSUtil {
// ps x | grep 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
// ps a | grep gnome-shell
@ -814,7 +814,7 @@ class OSUtil {
return gnomeVersion;
}
if (!OS.isLinux() && !OS.isUnix()) {
if (!OS.INSTANCE.isLinux() && !OS.INSTANCE.isUnix()) {
return null;
}
@ -903,7 +903,7 @@ class OSUtil {
return getPlasmaVersionFull;
}
if (!OS.isLinux() && !OS.isUnix()) {
if (!OS.INSTANCE.isLinux() && !OS.INSTANCE.isUnix()) {
return null;
}
@ -932,7 +932,7 @@ class OSUtil {
private static volatile Boolean isXfce = null;
public static
boolean isXfce() {
if (!OS.isLinux() && !OS.isUnix()) {
if (!OS.INSTANCE.isLinux() && !OS.INSTANCE.isUnix()) {
return false;
}
@ -947,7 +947,7 @@ class OSUtil {
// ps x | grep 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
// ps a | grep gnome-shell
@ -977,7 +977,7 @@ class OSUtil {
return isNautilus;
}
if (!OS.isLinux() && !OS.isUnix()) {
if (!OS.INSTANCE.isLinux() && !OS.INSTANCE.isUnix()) {
isNautilus = false;
return false;
}
@ -1006,7 +1006,7 @@ class OSUtil {
public static
boolean isChromeOS() {
if (isChromeOS == null) {
if (!OS.isLinux()) {
if (!OS.INSTANCE.isLinux()) {
isChromeOS = false;
return false;
}
@ -1037,7 +1037,7 @@ class OSUtil {
*/
public static
String queryXfce(String channel, String property) {
if (!OS.isLinux() && !OS.isUnix()) {
if (!OS.INSTANCE.isLinux() && !OS.INSTANCE.isUnix()) {
return "";
}

View File

@ -23,6 +23,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Locale;
@ -62,7 +63,7 @@ class CacheUtil {
void clear() {
// deletes all of the files (recursively) in the specified location. If the directory is empty (no locked files), then the
// 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");
}
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
String hash = hashName(cacheName);
String extension = FileUtil.getExtension(cacheName);
String extension = FileUtil.INSTANCE.getExtension(cacheName);
if (extension.isEmpty()) {
extension = "cache";
}
@ -408,7 +409,7 @@ class CacheUtil {
private static
String hashName(final String name) {
// figure out the fileName
byte[] bytes = name.getBytes(OS.UTF_8);
byte[] bytes = name.getBytes(StandardCharsets.UTF_8);
MessageDigest digest = digestLocal.get();
digest.reset();

View 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>

View File

@ -236,7 +236,7 @@ class Desktop {
throw new IOException("Path must not be null or empty.");
}
if (OS.isMacOsX()) {
if (OS.INSTANCE.isMacOsX()) {
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
@ -285,7 +285,7 @@ class Desktop {
* - CLI has thread/memory overhead
*/
private static boolean requireUnixLauncher() {
return ((OS.isUnix() || OS.isLinux()) && (GtkCheck.isGtkLoaded && GtkCheck.isGtk3));
return ((OS.INSTANCE.isUnix() || OS.INSTANCE.isLinux()) && (GtkCheck.isGtkLoaded && GtkCheck.isGtk3));
}
/**

View 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

File diff suppressed because it is too large Load Diff

View File

@ -39,7 +39,7 @@ import dorkbox.os.OS;
public
class FontUtil {
/** 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 */

View File

@ -24,6 +24,8 @@ import java.io.OutputStream;
import javax.imageio.stream.ImageInputStream;
@SuppressWarnings({"unused", "Duplicates"})
// deprecated. use kotlin.
@Deprecated
public
class IO {
/**

View File

@ -145,13 +145,13 @@ class ImageUtil {
// have to resize the file (and return the new path)
String extension = FileUtil.getExtension(fileName);
String extension = FileUtil.INSTANCE.getExtension(fileName);
if (extension.isEmpty()) {
extension = "png"; // made up
}
// 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;
// is file sitting on drive

View File

@ -13,54 +13,34 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.util;
package dorkbox.util
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import org.tukaani.xz.LZMA2Options
import org.tukaani.xz.LZMAInputStream
import org.tukaani.xz.LZMAOutputStream
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import org.tukaani.xz.LZMA2Options;
import org.tukaani.xz.LZMAInputStream;
import org.tukaani.xz.LZMAOutputStream;
public class LZMA {
object LZMA {
// https://tukaani.org/xz/java.html
public static final void encode(InputStream input, OutputStream output) throws IOException {
try (OutputStream compressionStream = new LZMAOutputStream(output, new LZMA2Options(3), true)) {
IO.copyStream(input, compressionStream);
@Throws(IOException::class)
fun encode(input: InputStream, output: OutputStream) {
LZMAOutputStream(output, LZMA2Options(3), true).use { compressionStream ->
input.copyTo(compressionStream)
}
}
public static final ByteArrayOutputStream decode(InputStream input) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(8192);
try (LZMAInputStream compressedStream = new LZMAInputStream(input)) {
IO.copyStream(compressedStream, byteArrayOutputStream);
@Throws(IOException::class)
fun decode(input: InputStream): ByteArrayOutputStream {
val byteArrayOutputStream = ByteArrayOutputStream(8192)
LZMAInputStream(input).use { compressedStream -> compressedStream.copyTo(byteArrayOutputStream) }
return byteArrayOutputStream
}
return byteArrayOutputStream;
}
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();
@Throws(IOException::class)
fun decode(input: InputStream, output: OutputStream) {
LZMAInputStream(input).use { compressedStream -> compressedStream.copyTo(output) }
}
}

View File

@ -578,7 +578,7 @@ class LocationResolver {
}
String path = url.getPath();
if (OS.isWindows()) {
if (OS.INSTANCE.isWindows()) {
if (path.startsWith("/")) {
path = path.substring(1);
}

View File

@ -35,10 +35,10 @@ class NativeLoader {
File extractLibrary(final String sourceFileName, final String destinationDirectory, final String destinationName, String version) throws IOException {
try {
String suffix;
if (OS.isLinux()) {
if (OS.INSTANCE.isLinux()) {
suffix = ".so";
}
else if (OS.isWindows()) {
else if (OS.INSTANCE.isWindows()) {
suffix = ".dll";
}
else {

View File

@ -65,7 +65,7 @@ class ParallelProcessor<Task> {
*/
public
ParallelProcessor() {
this(-1, OS.getOptimumNumberOfThreads(), null);
this(-1, OS.INSTANCE.getOptimumNumberOfThreads(), null);
}
/**
@ -81,7 +81,7 @@ class ParallelProcessor<Task> {
*/
public
ParallelProcessor(final int totalWorkload) {
this(totalWorkload, OS.getOptimumNumberOfThreads(), null);
this(totalWorkload, OS.INSTANCE.getOptimumNumberOfThreads(), null);
}
/**

View File

@ -737,7 +737,7 @@ class Sys {
builder.append(",");
}
if (i > inputOffset && lineLength > 0 && i % lineLength == 0) {
builder.append(OS.LINE_SEPARATOR);
builder.append(OS.INSTANCE.LINE_SEPARATOR);
}
}

View 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")
}
}

View 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
View 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
}
}

View File

@ -30,7 +30,7 @@ class ConcurrentIterator<T> {
/**
* 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 final int ID = ID_COUNTER.getAndIncrement();

View File

@ -43,7 +43,7 @@ class PropertiesProvider {
throw new NullPointerException("propertiesFile");
}
propertiesFile = FileUtil.normalize(propertiesFile);
propertiesFile = FileUtil.INSTANCE.normalize(propertiesFile);
// make sure the parent dir exists...
File parentFile = propertiesFile.getParentFile();
if (parentFile != null && !parentFile.exists()) {