Merged utilities from common + bootstrap into single project.
This commit is contained in:
parent
ffa0304f9e
commit
a7548cad84
@ -21,5 +21,6 @@
|
|||||||
<orderEntry type="library" name="bouncyCastle bcpkix-jdk15on" level="application" />
|
<orderEntry type="library" name="bouncyCastle bcpkix-jdk15on" level="application" />
|
||||||
<orderEntry type="library" name="reflectasm" level="application" />
|
<orderEntry type="library" name="reflectasm" level="application" />
|
||||||
<orderEntry type="library" name="lz4_xxhash" level="application" />
|
<orderEntry type="library" name="lz4_xxhash" level="application" />
|
||||||
|
<orderEntry type="library" name="lzma-java" level="application" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
364
src/dorkbox/exit/Exit.java
Normal file
364
src/dorkbox/exit/Exit.java
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
/*
|
||||||
|
* 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.exit;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import dorkbox.util.OS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The EXIT system uses ERRORS to exit the application. We must make sure NOT to swallow them higher up! -- so don't catch "throwable"!
|
||||||
|
*/
|
||||||
|
public final class Exit {
|
||||||
|
/**
|
||||||
|
* Used to set the data that will be
|
||||||
|
* 1) used for relaunch,
|
||||||
|
* 2) used to display an error message on exit
|
||||||
|
*/
|
||||||
|
private static native void setExitError(String data);
|
||||||
|
|
||||||
|
private static native boolean isNative0();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if we are native.
|
||||||
|
* Used for determining how exit's are handled.
|
||||||
|
*/
|
||||||
|
public static final boolean isNative() {
|
||||||
|
try {
|
||||||
|
return isNative0();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MIN/MAX values are -128 - 127, because of parent process limitations (anything over 127 will be converted to negative)
|
||||||
|
private static int UNDEFINED = -1; // must match C source (also anything < 0)
|
||||||
|
private static int NORMAL = 0; // must match C source
|
||||||
|
private static int FAILED_CONFIG = 1; // when we are trying to configure something, and it fails. Generally this is for required configurations
|
||||||
|
private static int FAILED_INIT = 2;
|
||||||
|
private static int FAILED_SECURITY = 3;
|
||||||
|
|
||||||
|
// must match C source!!
|
||||||
|
private static int RESERVED = 100; // must match C source
|
||||||
|
private static List<Integer> RESERVED_LIST = new ArrayList<Integer>(2);
|
||||||
|
|
||||||
|
private static int SAVED_IN_LOG_FILE = 101; // must match C source
|
||||||
|
private static int RESTART = 102; // must match C source
|
||||||
|
private static int UPGRADE_EXECUTABLE = 103; // must match C source
|
||||||
|
|
||||||
|
static {
|
||||||
|
RESERVED_LIST.add(new Integer(SAVED_IN_LOG_FILE));
|
||||||
|
RESERVED_LIST.add(new Integer(RESTART));
|
||||||
|
RESERVED_LIST.add(new Integer(UPGRADE_EXECUTABLE));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// so, it is important to observe, that WHILE the application is starting (Launcher.java),
|
||||||
|
// throwing exceptions is ACCEPTABLE. Afterwards, we MUST rely on the blocking structure to notify
|
||||||
|
// the main thread, so it can return normally.
|
||||||
|
|
||||||
|
|
||||||
|
private static AtomicInteger exitValue = new AtomicInteger(NORMAL);
|
||||||
|
private static ExitBase exitError = null;
|
||||||
|
private Exit() {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// This is what tells us that we are DONE launching, and have moved onto the bootstrap.
|
||||||
|
public static int getExitValue() {
|
||||||
|
_setExitData(exitError);
|
||||||
|
|
||||||
|
int exit = exitValue.get();
|
||||||
|
if (exit > RESERVED) {
|
||||||
|
if (RESERVED_LIST.contains(exitError)) {
|
||||||
|
return exit;
|
||||||
|
} else {
|
||||||
|
setExitError("You cannot use any number greater than 99");
|
||||||
|
return RESERVED;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only used on the inside of a thrown exception by the main thread
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* sets the exit data (ONLY IF there isn't already an error set in the system) then gets the return code
|
||||||
|
*/
|
||||||
|
public static int getExitDuringException(ExitBase exitError) {
|
||||||
|
if (Exit.exitError != null) {
|
||||||
|
_setExitData(Exit.exitError);
|
||||||
|
} else {
|
||||||
|
_setExitData(exitError);
|
||||||
|
}
|
||||||
|
|
||||||
|
// just in case.
|
||||||
|
int exit = exitValue.get();
|
||||||
|
if (exit > RESERVED) {
|
||||||
|
if (RESERVED_LIST.contains(exitError)) {
|
||||||
|
return exit;
|
||||||
|
} else {
|
||||||
|
setExitError("You cannot use any number greater than 99");
|
||||||
|
return RESERVED;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Writes a message to the log file
|
||||||
|
*/
|
||||||
|
public static void writeToLogFile(String title, String message) {
|
||||||
|
try {
|
||||||
|
Writer output = new BufferedWriter(new FileWriter("error.log", true));
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (message != null) {
|
||||||
|
// FileWriter always assumes default encoding is OK
|
||||||
|
if (title != null) {
|
||||||
|
output.write(title);
|
||||||
|
output.write(OS.LINE_SEPARATOR + " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
output.write(new java.util.Date() + OS.LINE_SEPARATOR + " ");
|
||||||
|
output.write(message);
|
||||||
|
output.write(OS.LINE_SEPARATOR);
|
||||||
|
} else {
|
||||||
|
output.write("Execption thrown! Unknown error.");
|
||||||
|
}
|
||||||
|
output.write(OS.LINE_SEPARATOR);
|
||||||
|
} finally {
|
||||||
|
output.close();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called by the uncaught exception hander.
|
||||||
|
*/
|
||||||
|
public static void handleUncaughtException(Throwable error) {
|
||||||
|
// only manage this if it's not on purpose!
|
||||||
|
if (!(error instanceof ExitBase)) {
|
||||||
|
// Not always undefined, although sometimes it is.
|
||||||
|
String message = error.getMessage();
|
||||||
|
if (message == null) {
|
||||||
|
message = Exit.getStackStrace(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
Exit.writeToLogFile("Uncaught Exception: " + error.getClass(), message);
|
||||||
|
|
||||||
|
// if we are launching, then throw the error. If we FINISHED launching, trip the block
|
||||||
|
// if we are closing (happens with a LEGIT error), then do nothing, since we are already
|
||||||
|
// handling it.
|
||||||
|
_throwIfLaunching(new ExitError(Exit.UNDEFINED,
|
||||||
|
"Abnormal termination from an uncaught exception! See log file.)"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undefined/unknown exit, and the info has been written to the log file.
|
||||||
|
* @param string
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static int Undefined(Throwable e) {
|
||||||
|
// Not always undefined, although sometimes it is.
|
||||||
|
String message = e.getMessage();
|
||||||
|
if (message == null) {
|
||||||
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(8196);
|
||||||
|
PrintStream printStream = new PrintStream(byteArrayOutputStream);
|
||||||
|
e.printStackTrace(printStream);
|
||||||
|
message = byteArrayOutputStream.toString();
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
message = e.getClass().toString() + " : " + message;
|
||||||
|
|
||||||
|
// undefined exit! The launcher will restart the JVM in this case
|
||||||
|
Exit.writeToLogFile("Error", message);
|
||||||
|
|
||||||
|
// will actually return the ACTUAL exit error, if there is one.
|
||||||
|
return Generic(Exit.UNDEFINED, "Undefined exception! Saved in log file.", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exit normally (from the launcher).
|
||||||
|
*/
|
||||||
|
public static int Normal() {
|
||||||
|
return Generic(Exit.NORMAL, "Normal exit called.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* when we are trying to configure something, and it fails. Generally this is for required configurations
|
||||||
|
*/
|
||||||
|
public static int FailedConfiguration(String errorMessage, Throwable throwable) {
|
||||||
|
return Generic(Exit.FAILED_CONFIG, "FailedConfiguration called: " + errorMessage, throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* when we are trying to configure something, and it fails. Generally this is for required configurations
|
||||||
|
*/
|
||||||
|
public static int FailedInitialization(Throwable throwable) {
|
||||||
|
return Generic(Exit.FAILED_INIT, "FailedInitialization called: " + throwable.getMessage(), throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int FailedConfiguration(String errorMessage) {
|
||||||
|
return Generic(Exit.FAILED_CONFIG, "FailedConfiguration called: " + errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int FailedInitialization(String errorMessage, Throwable throwable) {
|
||||||
|
return Generic(Exit.FAILED_INIT, "FailedInitialization called: " + errorMessage, throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int FailedInitialization(String errorMessage) {
|
||||||
|
return Generic(Exit.FAILED_INIT, "FailedInitialization called: " + errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int FailedSecurity(Throwable throwable) {
|
||||||
|
return Generic(Exit.FAILED_SECURITY, "FailedSecurity called: " + throwable.getMessage(), throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int FailedSecurity(String errorMessage, Throwable throwable) {
|
||||||
|
return Generic(Exit.FAILED_SECURITY, "FailedSecurity called: " + errorMessage, throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int FailedSecurity(String errorMessage) {
|
||||||
|
return Generic(Exit.FAILED_SECURITY, "FailedSecurity called: " + errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static int Generic(int exitCode, String errorMessage, Throwable throwable) {
|
||||||
|
return Generic(exitCode, errorMessage + OS.LINE_SEPARATOR + throwable.getClass() + OS.LINE_SEPARATOR + throwable.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int Generic(int exitCode, String errorMessage) {
|
||||||
|
// if we are launching, then throw the error. If we FINISHED launching, trip the block
|
||||||
|
_throwIfLaunching(new ExitError(exitCode, errorMessage));
|
||||||
|
return exitCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int Generic(int exitCode, String errorTitle, String errorMessage) {
|
||||||
|
// if we are launching, then throw the error. If we FINISHED launching, trip the block
|
||||||
|
_throwIfLaunching(new ExitError(exitCode, errorTitle, errorMessage));
|
||||||
|
return exitCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* restart the application with the current arguments.
|
||||||
|
* If we need to modify launch args, use the ini file. (or create a new one)
|
||||||
|
*/
|
||||||
|
public static void Restart() {
|
||||||
|
_throwIfLaunching(new ExitRestart(Exit.RESTART));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify that we want to upgrade the launcher executable. Other types of upgrade should use
|
||||||
|
* restart, where the launcher will automatically detect and upgrade the components in place.
|
||||||
|
*/
|
||||||
|
public static void UpgradeExectuable() {
|
||||||
|
_throwIfLaunching(new ExitRestart(Exit.UPGRADE_EXECUTABLE));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void _setExitData(ExitBase e) {
|
||||||
|
if (e == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
exitValue.set(e.getExitCode());
|
||||||
|
|
||||||
|
// exitData is passed to the launcher.
|
||||||
|
if (e.getMessage() != null) {
|
||||||
|
String message = e.getMessage();
|
||||||
|
if (isNative()) {
|
||||||
|
if (e.getTitle() != null) {
|
||||||
|
// can set the title if we want to. Normally it's just the program name.
|
||||||
|
setExitError("<title>" + e.getTitle() + "</title>" + OS.LINE_SEPARATOR + message);
|
||||||
|
} else {
|
||||||
|
setExitError(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _setExitCode(int exitCode) {
|
||||||
|
exitValue.set(exitCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// throw error if we are still launcher, otherwise set and notify the block
|
||||||
|
static void _throwIfLaunching(ExitBase exitError) {
|
||||||
|
// save the error. It's not always passed up the chain.
|
||||||
|
if (exitError != null && Exit.exitError == null) {
|
||||||
|
Exit.exitError = exitError;
|
||||||
|
}
|
||||||
|
|
||||||
|
// throw the error. This makes sure we exit/quit the thread we are currently in.
|
||||||
|
// we will end up in our "uncaught exception handler"!!
|
||||||
|
if (exitError != null) {
|
||||||
|
throw exitError;
|
||||||
|
} else {
|
||||||
|
throw new ExitError(Exit.FAILED_INIT, "Unable to have a null errorMessage!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility method to get the stack trace from an exception, and convert it to a string.
|
||||||
|
*/
|
||||||
|
public static String getStackStrace(Throwable throwable) {
|
||||||
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(8196);
|
||||||
|
PrintStream printStream = new PrintStream(byteArrayOutputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
|
||||||
|
String message = byteArrayOutputStream.toString();
|
||||||
|
|
||||||
|
printStream.flush();
|
||||||
|
printStream.close();
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Object clone() throws java.lang.CloneNotSupportedException {
|
||||||
|
throw new java.lang.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();
|
||||||
|
}
|
||||||
|
}
|
69
src/dorkbox/exit/ExitBase.java
Normal file
69
src/dorkbox/exit/ExitBase.java
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* 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.exit;
|
||||||
|
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
|
||||||
|
//package scope, as we don't want to accidentally let someone "catch" this error.
|
||||||
|
class ExitBase extends Error {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 546657685093303326L;
|
||||||
|
|
||||||
|
private final String message;
|
||||||
|
private final int exitCode;
|
||||||
|
private final String title;
|
||||||
|
|
||||||
|
ExitBase(int exitCode) {
|
||||||
|
this(exitCode, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExitBase(int exitCode, String message) {
|
||||||
|
this(exitCode, null, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExitBase(int exitCode, String title, String message) {
|
||||||
|
this.exitCode = exitCode;
|
||||||
|
this.title = title;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final String getTitle() {
|
||||||
|
return this.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String getMessage() {
|
||||||
|
return this.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int getExitCode() {
|
||||||
|
return this.exitCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Object clone() throws java.lang.CloneNotSupportedException {
|
||||||
|
throw new java.lang.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();
|
||||||
|
}
|
||||||
|
}
|
34
src/dorkbox/exit/ExitError.java
Normal file
34
src/dorkbox/exit/ExitError.java
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* 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.exit;
|
||||||
|
|
||||||
|
public final class ExitError extends ExitBase {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 4291020084877555138L;
|
||||||
|
|
||||||
|
|
||||||
|
public ExitError(int exitCode) {
|
||||||
|
super(exitCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExitError(int exitCode, String message) {
|
||||||
|
super(exitCode, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExitError(int exitCode, String title, String message) {
|
||||||
|
super(exitCode, title, message);
|
||||||
|
}
|
||||||
|
}
|
25
src/dorkbox/exit/ExitRestart.java
Normal file
25
src/dorkbox/exit/ExitRestart.java
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* 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.exit;
|
||||||
|
|
||||||
|
public final class ExitRestart extends ExitBase {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -3569173102400803538L;
|
||||||
|
|
||||||
|
public ExitRestart(int exitCode) {
|
||||||
|
super(exitCode);
|
||||||
|
}
|
||||||
|
}
|
98
src/dorkbox/urlHandler/BoxHandler.java
Normal file
98
src/dorkbox/urlHandler/BoxHandler.java
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* 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.urlHandler;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.net.URLStreamHandler;
|
||||||
|
|
||||||
|
public
|
||||||
|
class BoxHandler extends URLStreamHandler {
|
||||||
|
//
|
||||||
|
// This is also in the (ClassLoader project) Node!!!, but I didn't want to force a dependency just because of this.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// The following must ALL be valid URI symbols, defined by RFC 3986: http://tools.ietf.org/html/rfc3986#section-2
|
||||||
|
//
|
||||||
|
// ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=.
|
||||||
|
//
|
||||||
|
// Any other character needs to be encoded with the percent-encoding (%hh). Each part of the URI has further restrictions about
|
||||||
|
// what characters need to be represented by an percent-encoded word.
|
||||||
|
|
||||||
|
/** This is exclusively used to identify if a resource we are requesting is inside of a jar that was already parsed */
|
||||||
|
static final char jarUrlSeperator = '*';
|
||||||
|
static final char jarPathToken = '/';
|
||||||
|
static final char packageToken = '.';
|
||||||
|
|
||||||
|
static final String protocol = "box";
|
||||||
|
|
||||||
|
static final String protocolFull = protocol + ":/";
|
||||||
|
static final int protocolLength = protocolFull.length();
|
||||||
|
|
||||||
|
|
||||||
|
public BoxHandler() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected URLConnection openConnection(URL url) throws IOException {
|
||||||
|
return new BoxURLConnection(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes sure that when creating paths, etc, from this URL, that we also make sure to add a token, so
|
||||||
|
* our classloader knows where to find the resource.
|
||||||
|
*
|
||||||
|
* This absolutely MUST not end in special characters. it must be the letters/numbers or a "/". NOTHING ELSE.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected String toExternalForm(URL url) {
|
||||||
|
// ONLY append jarUrlSeperator if we haven't already done so!
|
||||||
|
String externalForm = super.toExternalForm(url);
|
||||||
|
|
||||||
|
char jarurlseperator = jarUrlSeperator;
|
||||||
|
|
||||||
|
if (externalForm.indexOf(jarurlseperator) == -1) {
|
||||||
|
int length = externalForm.length();
|
||||||
|
StringBuilder stringBuilder = new StringBuilder(length + 1);
|
||||||
|
stringBuilder.append(externalForm);
|
||||||
|
if (length > 1 && externalForm.charAt(length-1) == jarPathToken) {
|
||||||
|
stringBuilder.insert(length, jarurlseperator);
|
||||||
|
} else {
|
||||||
|
stringBuilder.append(jarurlseperator);
|
||||||
|
}
|
||||||
|
return stringBuilder.toString();
|
||||||
|
} else {
|
||||||
|
// we've already modified it, don't do it again.
|
||||||
|
return externalForm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Object clone() throws java.lang.CloneNotSupportedException {
|
||||||
|
throw new java.lang.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();
|
||||||
|
}
|
||||||
|
}
|
60
src/dorkbox/urlHandler/BoxHandlerFactory.java
Normal file
60
src/dorkbox/urlHandler/BoxHandlerFactory.java
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* 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.urlHandler;
|
||||||
|
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.net.URLStreamHandler;
|
||||||
|
import java.net.URLStreamHandlerFactory;
|
||||||
|
|
||||||
|
public
|
||||||
|
class BoxHandlerFactory implements URLStreamHandlerFactory {
|
||||||
|
private final BoxHandler transparentJar;
|
||||||
|
|
||||||
|
public
|
||||||
|
BoxHandlerFactory(BoxHandler transparentJar) {
|
||||||
|
this.transparentJar = transparentJar;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
URLStreamHandler createURLStreamHandler(String protocol) {
|
||||||
|
// transparent jar handler.
|
||||||
|
if (BoxHandler.protocol.equals(protocol)) {
|
||||||
|
return this.transparentJar;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// use the default URLStreamHandlers
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final
|
||||||
|
Object clone() throws java.lang.CloneNotSupportedException {
|
||||||
|
throw new java.lang.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();
|
||||||
|
}
|
||||||
|
}
|
153
src/dorkbox/urlHandler/BoxURLConnection.java
Normal file
153
src/dorkbox/urlHandler/BoxURLConnection.java
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* 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.urlHandler;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A 'Box' URL is nothing like a JAR/ZIP, HOWEVER, it appears as though it is a jar/zip file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public
|
||||||
|
class BoxURLConnection extends URLConnection {
|
||||||
|
|
||||||
|
public
|
||||||
|
BoxURLConnection(URL url) {
|
||||||
|
super(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the base name of the url. This will be the internal container (inside the main jar file) that actually contains our resource.
|
||||||
|
* This will be empty for class files.
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
String getContainerName() {
|
||||||
|
String spec = this.url.getPath();
|
||||||
|
int separator = spec.indexOf(BoxHandler.jarUrlSeperator);
|
||||||
|
int length = spec.length();
|
||||||
|
|
||||||
|
if (separator > 0 && separator != length) {
|
||||||
|
if (spec.charAt(0) == '/') {
|
||||||
|
if (spec.charAt(separator - 1) == '/') {
|
||||||
|
String substring = spec.substring(1, separator - 1);
|
||||||
|
return substring;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
String substring = spec.substring(1, separator);
|
||||||
|
return substring;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (spec.charAt(separator - 1) == '/') {
|
||||||
|
String substring = spec.substring(0, separator - 1);
|
||||||
|
return substring;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
String substring = spec.substring(0, separator);
|
||||||
|
return substring;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the name of the entry that is nested inside an internal resource. This would be the name of a file, where the base URL would
|
||||||
|
* be the internal resource container.
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
String getResourceName() {
|
||||||
|
String spec = this.url.getPath();
|
||||||
|
int separator = spec.indexOf(BoxHandler.jarUrlSeperator);
|
||||||
|
|
||||||
|
if (separator > -1 && separator != spec.length()) {
|
||||||
|
if (spec.charAt(separator + 1) == '/') {
|
||||||
|
return spec.substring(separator + 2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return spec.substring(separator + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void connect() throws IOException {
|
||||||
|
this.connected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
int getContentLength() {
|
||||||
|
// if we are inside our box file, this will return -1, so inputstreams will be used (which they have to be...)
|
||||||
|
// if we return anything other than -1, then our box resource will try to be opened like a file (which we don't want)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
long getLastModified() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the resources stream, if applicable. You cannot load classes using this method
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
InputStream getInputStream() throws IOException {
|
||||||
|
String path = this.url.getPath();
|
||||||
|
|
||||||
|
int length = BoxHandler.protocolLength;
|
||||||
|
StringBuilder stringBuilder = new StringBuilder(path.length() + length);
|
||||||
|
stringBuilder.append(BoxHandler.protocolFull);
|
||||||
|
if (path.charAt(0) == '/') {
|
||||||
|
stringBuilder.deleteCharAt(length - 1);
|
||||||
|
}
|
||||||
|
stringBuilder.append(path);
|
||||||
|
|
||||||
|
InputStream is = getClass().getClassLoader()
|
||||||
|
.getResourceAsStream(stringBuilder.toString());
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final
|
||||||
|
Object clone() throws java.lang.CloneNotSupportedException {
|
||||||
|
throw new java.lang.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();
|
||||||
|
}
|
||||||
|
}
|
660
src/dorkbox/util/Base64Fast.java
Normal file
660
src/dorkbox/util/Base64Fast.java
Normal file
@ -0,0 +1,660 @@
|
|||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/** A very fast and memory efficient class to encode and decode to and from BASE64 in full accordance
|
||||||
|
* with RFC 2045.<br><br>
|
||||||
|
* On Windows XP sp1 with 1.4.2_04 and later ;), this encoder and decoder is about 10 times faster
|
||||||
|
* on small arrays (10 - 1000 bytes) and 2-3 times as fast on larger arrays (10000 - 1000000 bytes)
|
||||||
|
* compared to <code>sun.misc.Encoder()/Decoder()</code>.<br><br>
|
||||||
|
*
|
||||||
|
* On byte arrays the encoder is about 20% faster than Jakarta Commons Base64 Codec for encode and
|
||||||
|
* about 50% faster for decoding large arrays. This implementation is about twice as fast on very small
|
||||||
|
* arrays (< 30 bytes). If source/destination is a <code>String</code> this
|
||||||
|
* version is about three times as fast due to the fact that the Commons Codec result has to be recoded
|
||||||
|
* to a <code>String</code> from <code>byte[]</code>, which is very expensive.<br><br>
|
||||||
|
*
|
||||||
|
* This encode/decode algorithm doesn't create any temporary arrays as many other codecs do, it only
|
||||||
|
* allocates the resulting array. This produces less garbage and it is possible to handle arrays twice
|
||||||
|
* as large as algorithms that create a temporary array. (E.g. Jakarta Commons Codec). It is unknown
|
||||||
|
* whether Sun's <code>sun.misc.Encoder()/Decoder()</code> produce temporary arrays but since performance
|
||||||
|
* is quite low it probably does.<br><br>
|
||||||
|
*
|
||||||
|
* The encoder produces the same output as the Sun one except that the Sun's encoder appends
|
||||||
|
* a trailing line separator if the last character isn't a pad. Unclear why but it only adds to the
|
||||||
|
* length and is probably a side effect. Both are in conformance with RFC 2045 though.<br>
|
||||||
|
* Commons codec seem to always add a trailing line separator.<br><br>
|
||||||
|
*
|
||||||
|
* <b>Note!</b>
|
||||||
|
* The encode/decode method pairs (types) come in three versions with the <b>exact</b> same algorithm and
|
||||||
|
* thus a lot of code redundancy. This is to not create any temporary arrays for transcoding to/from different
|
||||||
|
* format types. The methods not used can simply be commented out.<br><br>
|
||||||
|
*
|
||||||
|
* There is also a "fast" version of all decode methods that works the same way as the normal ones, but
|
||||||
|
* has a few demands on the decoded input. Normally though, these fast versions should be used if the source if
|
||||||
|
* the input is known and it hasn't bee tampered with.<br><br>
|
||||||
|
*
|
||||||
|
* If you find the code useful or you find a bug, please send me a note at base64 @ miginfocom . com.
|
||||||
|
*
|
||||||
|
* License (BSD):
|
||||||
|
* ==============
|
||||||
|
*
|
||||||
|
* Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (base64 @ miginfocom . com)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 of the MiG InfoCom AB 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.
|
||||||
|
*
|
||||||
|
* @version 2.2
|
||||||
|
* @author Mikael Grev
|
||||||
|
* Date: 2004-aug-02
|
||||||
|
* Time: 11:31:11
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class Base64Fast
|
||||||
|
{
|
||||||
|
private static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
|
||||||
|
private static final int[] IA = new int[256];
|
||||||
|
static {
|
||||||
|
Arrays.fill(IA, -1);
|
||||||
|
for (int i = 0, iS = CA.length; i < iS; i++) {
|
||||||
|
IA[CA[i]] = i;
|
||||||
|
}
|
||||||
|
IA['='] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ****************************************************************************************
|
||||||
|
// * char[] version
|
||||||
|
// ****************************************************************************************
|
||||||
|
|
||||||
|
/** Encodes a raw byte array into a BASE64 <code>char[]</code> representation in accordance with RFC 2045.
|
||||||
|
* @param sArr The bytes to convert. If <code>null</code> or length 0 an empty array will be returned.
|
||||||
|
* @param lineSep Optional "\r\n" after 76 characters, unless end of file.<br>
|
||||||
|
* No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a
|
||||||
|
* little faster.
|
||||||
|
* @return A BASE64 encoded array. Never <code>null</code>.
|
||||||
|
*/
|
||||||
|
public final static char[] encodeToChar(byte[] sArr, boolean lineSep)
|
||||||
|
{
|
||||||
|
sArr = sArr != null ? sArr : new byte[0];
|
||||||
|
|
||||||
|
// Check special case
|
||||||
|
int sLen = sArr.length;
|
||||||
|
if (sLen == 0) {
|
||||||
|
return new char[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
int eLen = sLen / 3 * 3; // Length of even 24-bits.
|
||||||
|
int cCnt = (sLen - 1) / 3 + 1 << 2; // Returned character count
|
||||||
|
int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array
|
||||||
|
char[] dArr = new char[dLen];
|
||||||
|
|
||||||
|
// Encode even 24-bits
|
||||||
|
for (int s = 0, d = 0, cc = 0; s < eLen;) {
|
||||||
|
// Copy next three bytes into lower 24 bits of int, paying attension to sign.
|
||||||
|
int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | sArr[s++] & 0xff;
|
||||||
|
|
||||||
|
// Encode the int into four chars
|
||||||
|
dArr[d++] = CA[i >>> 18 & 0x3f];
|
||||||
|
dArr[d++] = CA[i >>> 12 & 0x3f];
|
||||||
|
dArr[d++] = CA[i >>> 6 & 0x3f];
|
||||||
|
dArr[d++] = CA[i & 0x3f];
|
||||||
|
|
||||||
|
// Add optional line separator
|
||||||
|
if (lineSep && ++cc == 19 && d < dLen - 2) {
|
||||||
|
dArr[d++] = '\r';
|
||||||
|
dArr[d++] = '\n';
|
||||||
|
cc = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pad and encode last bits if source isn't even 24 bits.
|
||||||
|
int left = sLen - eLen; // 0 - 2.
|
||||||
|
if (left > 0) {
|
||||||
|
// Prepare the int
|
||||||
|
int i = (sArr[eLen] & 0xff) << 10 | (left == 2 ? (sArr[sLen - 1] & 0xff) << 2 : 0);
|
||||||
|
|
||||||
|
// Set last four chars
|
||||||
|
dArr[dLen - 4] = CA[i >> 12];
|
||||||
|
dArr[dLen - 3] = CA[i >>> 6 & 0x3f];
|
||||||
|
dArr[dLen - 2] = left == 2 ? CA[i & 0x3f] : '=';
|
||||||
|
dArr[dLen - 1] = '=';
|
||||||
|
}
|
||||||
|
return dArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Decodes a BASE64 encoded char array. All illegal characters will be ignored and can handle both arrays with
|
||||||
|
* and without line separators.
|
||||||
|
* @param sArr The source array. <code>null</code> or length 0 will return an empty array.
|
||||||
|
* @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters
|
||||||
|
* (including '=') isn't divideable by 4. (I.e. definitely corrupted).
|
||||||
|
*/
|
||||||
|
public final static byte[] decode(char[] sArr)
|
||||||
|
{
|
||||||
|
sArr = sArr != null ? sArr : new char[0];
|
||||||
|
|
||||||
|
// Check special case
|
||||||
|
int sLen = sArr.length;
|
||||||
|
if (sLen == 0) {
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count illegal characters (including '\r', '\n') to know what size the returned array will be,
|
||||||
|
// so we don't have to reallocate & copy it later.
|
||||||
|
int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)
|
||||||
|
for (int i = 0; i < sLen; i++) {
|
||||||
|
if (IA[sArr[i]] < 0) {
|
||||||
|
sepCnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045.
|
||||||
|
if ((sLen - sepCnt) % 4 != 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pad = 0;
|
||||||
|
for (int i = sLen; i > 1 && IA[sArr[--i]] <= 0;) {
|
||||||
|
if (sArr[i] == '=') {
|
||||||
|
pad++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = ((sLen - sepCnt) * 6 >> 3) - pad;
|
||||||
|
|
||||||
|
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||||
|
|
||||||
|
for (int s = 0, d = 0; d < len;) {
|
||||||
|
// Assemble three bytes into an int from four "valid" characters.
|
||||||
|
int i = 0;
|
||||||
|
for (int j = 0; j < 4; j++) { // j only increased if a valid char was found.
|
||||||
|
int c = IA[sArr[s++]];
|
||||||
|
if (c >= 0) {
|
||||||
|
i |= c << 18 - j * 6;
|
||||||
|
} else {
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add the bytes
|
||||||
|
dArr[d++] = (byte) (i >> 16);
|
||||||
|
if (d < len) {
|
||||||
|
dArr[d++]= (byte) (i >> 8);
|
||||||
|
if (d < len) {
|
||||||
|
dArr[d++] = (byte) i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Decodes a BASE64 encoded char array that is known to be resonably well formatted. The method is about twice as
|
||||||
|
* fast as {@link #decode(char[])}. The preconditions are:<br>
|
||||||
|
* + The array must have a line length of 76 chars OR no line separators at all (one line).<br>
|
||||||
|
* + Line separator must be "\r\n", as specified in RFC 2045
|
||||||
|
* + The array must not contain illegal characters within the encoded string<br>
|
||||||
|
* + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
|
||||||
|
* @param sArr The source array. Length 0 will return an empty array. <code>null</code> will throw an exception.
|
||||||
|
* @return The decoded array of bytes. May be of length 0.
|
||||||
|
*/
|
||||||
|
public final static byte[] decodeFast(char[] sArr)
|
||||||
|
{
|
||||||
|
// Check special case
|
||||||
|
int sLen = sArr.length;
|
||||||
|
if (sLen == 0) {
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
int sIx = 0, eIx = sLen - 1; // Start and end index after trimming.
|
||||||
|
|
||||||
|
// Trim illegal chars from start
|
||||||
|
while (sIx < eIx && IA[sArr[sIx]] < 0) {
|
||||||
|
sIx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim illegal chars from end
|
||||||
|
while (eIx > 0 && IA[sArr[eIx]] < 0) {
|
||||||
|
eIx--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the padding count (=) (0, 1 or 2)
|
||||||
|
int pad = sArr[eIx] == '=' ? sArr[eIx - 1] == '=' ? 2 : 1 : 0; // Count '=' at end.
|
||||||
|
int cCnt = eIx - sIx + 1; // Content count including possible separators
|
||||||
|
int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0;
|
||||||
|
|
||||||
|
int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
|
||||||
|
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||||
|
|
||||||
|
// Decode all but the last 0 - 2 bytes.
|
||||||
|
int d = 0;
|
||||||
|
for (int cc = 0, eLen = len / 3 * 3; d < eLen;) {
|
||||||
|
// Assemble three bytes into an int from four "valid" characters.
|
||||||
|
int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]];
|
||||||
|
|
||||||
|
// Add the bytes
|
||||||
|
dArr[d++] = (byte) (i >> 16);
|
||||||
|
dArr[d++] = (byte) (i >> 8);
|
||||||
|
dArr[d++] = (byte) i;
|
||||||
|
|
||||||
|
// If line separator, jump over it.
|
||||||
|
if (sepCnt > 0 && ++cc == 19) {
|
||||||
|
sIx += 2;
|
||||||
|
cc = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d < len) {
|
||||||
|
// Decode last 1-3 bytes (incl '=') into 1-3 bytes
|
||||||
|
int i = 0;
|
||||||
|
for (int j = 0; sIx <= eIx - pad; j++) {
|
||||||
|
i |= IA[sArr[sIx++]] << 18 - j * 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int r = 16; d < len; r -= 8) {
|
||||||
|
dArr[d++] = (byte) (i >> r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ****************************************************************************************
|
||||||
|
// * byte[] version
|
||||||
|
// ****************************************************************************************
|
||||||
|
|
||||||
|
/** Encodes a raw byte array into a BASE64 <code>byte[]</code> representation i accordance with RFC 2045.
|
||||||
|
* @param sArr The bytes to convert. If <code>null</code> or length 0 an empty array will be returned.
|
||||||
|
* @param lineSep Optional "\r\n" after 76 characters, unless end of file.<br>
|
||||||
|
* No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a
|
||||||
|
* little faster.
|
||||||
|
* @return A BASE64 encoded array. Never <code>null</code>.
|
||||||
|
*/
|
||||||
|
public final static byte[] encodeToByte(byte[] sArr, boolean lineSep)
|
||||||
|
{
|
||||||
|
return encodeToByte(sArr, 0, sArr != null ? sArr.length : 0, lineSep);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Encodes a raw byte array into a BASE64 <code>byte[]</code> representation i accordance with RFC 2045.
|
||||||
|
* @param sArr The bytes to convert. If <code>null</code> an empty array will be returned.
|
||||||
|
* @param sOff The starting position in the bytes to convert.
|
||||||
|
* @param sLen The number of bytes to convert. If 0 an empty array will be returned.
|
||||||
|
* @param lineSep Optional "\r\n" after 76 characters, unless end of file.<br>
|
||||||
|
* No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a
|
||||||
|
* little faster.
|
||||||
|
* @return A BASE64 encoded array. Never <code>null</code>.
|
||||||
|
*/
|
||||||
|
public final static byte[] encodeToByte(byte[] sArr, int sOff, int sLen, boolean lineSep)
|
||||||
|
{
|
||||||
|
// Check special case
|
||||||
|
if (sArr == null || sLen == 0) {
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
int eLen = sLen / 3 * 3; // Length of even 24-bits.
|
||||||
|
int cCnt = (sLen - 1) / 3 + 1 << 2; // Returned character count
|
||||||
|
int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array
|
||||||
|
byte[] dArr = new byte[dLen];
|
||||||
|
|
||||||
|
// Encode even 24-bits
|
||||||
|
for (int s = sOff, d = 0, cc = 0; s < sOff + eLen;) {
|
||||||
|
// Copy next three bytes into lower 24 bits of int, paying attension to sign.
|
||||||
|
int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | sArr[s++] & 0xff;
|
||||||
|
|
||||||
|
// Encode the int into four chars
|
||||||
|
dArr[d++] = (byte) CA[i >>> 18 & 0x3f];
|
||||||
|
dArr[d++] = (byte) CA[i >>> 12 & 0x3f];
|
||||||
|
dArr[d++] = (byte) CA[i >>> 6 & 0x3f];
|
||||||
|
dArr[d++] = (byte) CA[i & 0x3f];
|
||||||
|
|
||||||
|
// Add optional line separator
|
||||||
|
if (lineSep && ++cc == 19 && d < dLen - 2) {
|
||||||
|
dArr[d++] = '\r';
|
||||||
|
dArr[d++] = '\n';
|
||||||
|
cc = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pad and encode last bits if source isn't an even 24 bits.
|
||||||
|
int left = sLen - eLen; // 0 - 2.
|
||||||
|
if (left > 0) {
|
||||||
|
// Prepare the int
|
||||||
|
int i = (sArr[sOff + eLen] & 0xff) << 10 | (left == 2 ? (sArr[sOff + sLen - 1] & 0xff) << 2 : 0);
|
||||||
|
|
||||||
|
// Set last four chars
|
||||||
|
dArr[dLen - 4] = (byte) CA[i >> 12];
|
||||||
|
dArr[dLen - 3] = (byte) CA[i >>> 6 & 0x3f];
|
||||||
|
dArr[dLen - 2] = left == 2 ? (byte) CA[i & 0x3f] : (byte) '=';
|
||||||
|
dArr[dLen - 1] = '=';
|
||||||
|
}
|
||||||
|
return dArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with
|
||||||
|
* and without line separators.
|
||||||
|
* @param sArr The source array. Length 0 will return an empty array. <code>null</code> will throw an exception.
|
||||||
|
* @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters
|
||||||
|
* (including '=') isn't divideable by 4. (I.e. definitely corrupted).
|
||||||
|
*/
|
||||||
|
public final static byte[] decode(byte[] sArr)
|
||||||
|
{
|
||||||
|
return decode(sArr, 0, sArr.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with
|
||||||
|
* and without line separators.
|
||||||
|
* @param sArr The source array. <code>null</code> will throw an exception.
|
||||||
|
* @param sOff The starting position in the source array.
|
||||||
|
* @param sLen The number of bytes to decode from the source array. Length 0 will return an empty array.
|
||||||
|
* @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters
|
||||||
|
* (including '=') isn't divideable by 4. (I.e. definitely corrupted).
|
||||||
|
*/
|
||||||
|
public final static byte[] decode(byte[] sArr, int sOff, int sLen)
|
||||||
|
{
|
||||||
|
// Count illegal characters (including '\r', '\n') to know what size the returned array will be,
|
||||||
|
// so we don't have to reallocate & copy it later.
|
||||||
|
int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)
|
||||||
|
for (int i = 0; i < sLen; i++) {
|
||||||
|
if (IA[sArr[sOff + i] & 0xff] < 0) {
|
||||||
|
sepCnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045.
|
||||||
|
if ((sLen - sepCnt) % 4 != 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pad = 0;
|
||||||
|
for (int i = sLen; i > 1 && IA[sArr[sOff + --i] & 0xff] <= 0;) {
|
||||||
|
if (sArr[sOff + i] == '=') {
|
||||||
|
pad++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = ((sLen - sepCnt) * 6 >> 3) - pad;
|
||||||
|
|
||||||
|
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||||
|
|
||||||
|
for (int s = 0, d = 0; d < len;) {
|
||||||
|
// Assemble three bytes into an int from four "valid" characters.
|
||||||
|
int i = 0;
|
||||||
|
for (int j = 0; j < 4; j++) { // j only increased if a valid char was found.
|
||||||
|
int c = IA[sArr[sOff + s++] & 0xff];
|
||||||
|
if (c >= 0) {
|
||||||
|
i |= c << 18 - j * 6;
|
||||||
|
} else {
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the bytes
|
||||||
|
dArr[d++] = (byte) (i >> 16);
|
||||||
|
if (d < len) {
|
||||||
|
dArr[d++]= (byte) (i >> 8);
|
||||||
|
if (d < len) {
|
||||||
|
dArr[d++] = (byte) i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Decodes a BASE64 encoded byte array that is known to be resonably well formatted. The method is about twice as
|
||||||
|
* fast as {@link #decode(byte[])}. The preconditions are:<br>
|
||||||
|
* + The array must have a line length of 76 chars OR no line separators at all (one line).<br>
|
||||||
|
* + Line separator must be "\r\n", as specified in RFC 2045
|
||||||
|
* + The array must not contain illegal characters within the encoded string<br>
|
||||||
|
* + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
|
||||||
|
* @param sArr The source array. Length 0 will return an empty array. <code>null</code> will throw an exception.
|
||||||
|
* @return The decoded array of bytes. May be of length 0.
|
||||||
|
*/
|
||||||
|
public final static byte[] decodeFast(byte[] sArr)
|
||||||
|
{
|
||||||
|
// Check special case
|
||||||
|
int sLen = sArr.length;
|
||||||
|
if (sLen == 0) {
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
int sIx = 0, eIx = sLen - 1; // Start and end index after trimming.
|
||||||
|
|
||||||
|
// Trim illegal chars from start
|
||||||
|
while (sIx < eIx && IA[sArr[sIx] & 0xff] < 0) {
|
||||||
|
sIx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim illegal chars from end
|
||||||
|
while (eIx > 0 && IA[sArr[eIx] & 0xff] < 0) {
|
||||||
|
eIx--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the padding count (=) (0, 1 or 2)
|
||||||
|
int pad = sArr[eIx] == '=' ? sArr[eIx - 1] == '=' ? 2 : 1 : 0; // Count '=' at end.
|
||||||
|
int cCnt = eIx - sIx + 1; // Content count including possible separators
|
||||||
|
int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0;
|
||||||
|
|
||||||
|
int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
|
||||||
|
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||||
|
|
||||||
|
// Decode all but the last 0 - 2 bytes.
|
||||||
|
int d = 0;
|
||||||
|
for (int cc = 0, eLen = len / 3 * 3; d < eLen;) {
|
||||||
|
// Assemble three bytes into an int from four "valid" characters.
|
||||||
|
int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]];
|
||||||
|
|
||||||
|
// Add the bytes
|
||||||
|
dArr[d++] = (byte) (i >> 16);
|
||||||
|
dArr[d++] = (byte) (i >> 8);
|
||||||
|
dArr[d++] = (byte) i;
|
||||||
|
|
||||||
|
// If line separator, jump over it.
|
||||||
|
if (sepCnt > 0 && ++cc == 19) {
|
||||||
|
sIx += 2;
|
||||||
|
cc = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d < len) {
|
||||||
|
// Decode last 1-3 bytes (incl '=') into 1-3 bytes
|
||||||
|
int i = 0;
|
||||||
|
for (int j = 0; sIx <= eIx - pad; j++) {
|
||||||
|
i |= IA[sArr[sIx++]] << 18 - j * 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int r = 16; d < len; r -= 8) {
|
||||||
|
dArr[d++] = (byte) (i >> r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ****************************************************************************************
|
||||||
|
// * String version
|
||||||
|
// ****************************************************************************************
|
||||||
|
|
||||||
|
/** Encodes a raw byte array into a BASE64 <code>String</code> representation i accordance with RFC 2045.
|
||||||
|
* @param sArr The bytes to convert. If <code>null</code> or length 0 an empty array will be returned.
|
||||||
|
* @param lineSep Optional "\r\n" after 76 characters, unless end of file.<br>
|
||||||
|
* No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a
|
||||||
|
* little faster.
|
||||||
|
* @return A BASE64 encoded array. Never <code>null</code>.
|
||||||
|
*/
|
||||||
|
public final static String encodeToString(byte[] sArr, boolean lineSep)
|
||||||
|
{
|
||||||
|
// Reuse char[] since we can't create a String incrementally anyway and StringBuffer/Builder would be slower.
|
||||||
|
return new String(encodeToChar(sArr, lineSep));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Decodes a BASE64 encoded <code>String</code>. All illegal characters will be ignored and can handle both strings with
|
||||||
|
* and without line separators.<br>
|
||||||
|
* <b>Note!</b> It can be up to about 2x the speed to call <code>decode(str.toCharArray())</code> instead. That
|
||||||
|
* will create a temporary array though. This version will use <code>str.charAt(i)</code> to iterate the string.
|
||||||
|
* @param str The source string. <code>null</code> or length 0 will return an empty array.
|
||||||
|
* @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters
|
||||||
|
* (including '=') isn't divideable by 4. (I.e. definitely corrupted).
|
||||||
|
*/
|
||||||
|
public final static byte[] decode(String str)
|
||||||
|
{
|
||||||
|
str = str != null ? str : "";
|
||||||
|
|
||||||
|
// Check special case
|
||||||
|
int sLen = str.length();
|
||||||
|
if (sLen == 0) {
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count illegal characters (including '\r', '\n') to know what size the returned array will be,
|
||||||
|
// so we don't have to reallocate & copy it later.
|
||||||
|
int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)
|
||||||
|
for (int i = 0; i < sLen; i++) {
|
||||||
|
if (IA[str.charAt(i)] < 0) {
|
||||||
|
sepCnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045.
|
||||||
|
if ((sLen - sepCnt) % 4 != 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count '=' at end
|
||||||
|
int pad = 0;
|
||||||
|
for (int i = sLen; i > 1 && IA[str.charAt(--i)] <= 0;) {
|
||||||
|
if (str.charAt(i) == '=') {
|
||||||
|
pad++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = ((sLen - sepCnt) * 6 >> 3) - pad;
|
||||||
|
|
||||||
|
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||||
|
|
||||||
|
for (int s = 0, d = 0; d < len;) {
|
||||||
|
// Assemble three bytes into an int from four "valid" characters.
|
||||||
|
int i = 0;
|
||||||
|
for (int j = 0; j < 4; j++) { // j only increased if a valid char was found.
|
||||||
|
int c = IA[str.charAt(s++)];
|
||||||
|
if (c >= 0) {
|
||||||
|
i |= c << 18 - j * 6;
|
||||||
|
} else {
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add the bytes
|
||||||
|
dArr[d++] = (byte) (i >> 16);
|
||||||
|
if (d < len) {
|
||||||
|
dArr[d++]= (byte) (i >> 8);
|
||||||
|
if (d < len) {
|
||||||
|
dArr[d++] = (byte) i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Decodes a BASE64 encoded string that is known to be reasonably well formatted. The method is about twice as
|
||||||
|
* fast as {@link #decode(String)}. The preconditions are:<br>
|
||||||
|
* + The array must have a line length of 76 chars OR no line separators at all (one line).<br>
|
||||||
|
* + Line separator must be "\r\n", as specified in RFC 2045
|
||||||
|
* + The array must not contain illegal characters within the encoded string<br>
|
||||||
|
* + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
|
||||||
|
* @param s The source string. Length 0 will return an empty array. <code>null</code> will throw an exception.
|
||||||
|
* @return The decoded array of bytes. May be of length 0.
|
||||||
|
*/
|
||||||
|
public final static byte[] decodeFast(String s)
|
||||||
|
{
|
||||||
|
// Check special case
|
||||||
|
int sLen = s.length();
|
||||||
|
if (sLen == 0) {
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
int sIx = 0, eIx = sLen - 1; // Start and end index after trimming.
|
||||||
|
|
||||||
|
// Trim illegal chars from start
|
||||||
|
while (sIx < eIx && IA[s.charAt(sIx) & 0xff] < 0) {
|
||||||
|
sIx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim illegal chars from end
|
||||||
|
while (eIx > 0 && IA[s.charAt(eIx) & 0xff] < 0) {
|
||||||
|
eIx--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the padding count (=) (0, 1 or 2)
|
||||||
|
int pad = s.charAt(eIx) == '=' ? s.charAt(eIx - 1) == '=' ? 2 : 1 : 0; // Count '=' at end.
|
||||||
|
int cCnt = eIx - sIx + 1; // Content count including possible separators
|
||||||
|
int sepCnt = sLen > 76 ? (s.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0;
|
||||||
|
|
||||||
|
int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
|
||||||
|
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||||
|
|
||||||
|
// Decode all but the last 0 - 2 bytes.
|
||||||
|
int d = 0;
|
||||||
|
for (int cc = 0, eLen = len / 3 * 3; d < eLen;) {
|
||||||
|
// Assemble three bytes into an int from four "valid" characters.
|
||||||
|
int i = IA[s.charAt(sIx++)] << 18 | IA[s.charAt(sIx++)] << 12 | IA[s.charAt(sIx++)] << 6 | IA[s.charAt(sIx++)];
|
||||||
|
|
||||||
|
// Add the bytes
|
||||||
|
dArr[d++] = (byte) (i >> 16);
|
||||||
|
dArr[d++] = (byte) (i >> 8);
|
||||||
|
dArr[d++] = (byte) i;
|
||||||
|
|
||||||
|
// If line separator, jump over it.
|
||||||
|
if (sepCnt > 0 && ++cc == 19) {
|
||||||
|
sIx += 2;
|
||||||
|
cc = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d < len) {
|
||||||
|
// Decode last 1-3 bytes (incl '=') into 1-3 bytes
|
||||||
|
int i = 0;
|
||||||
|
for (int j = 0; sIx <= eIx - pad; j++) {
|
||||||
|
i |= IA[s.charAt(sIx++)] << 18 - j * 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int r = 16; d < len; r -= 8) {
|
||||||
|
dArr[d++] = (byte) (i >> r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dArr;
|
||||||
|
}
|
||||||
|
}
|
49
src/dorkbox/util/ClassResolver.java
Normal file
49
src/dorkbox/util/ClassResolver.java
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 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.util;
|
||||||
|
|
||||||
|
public abstract class ClassResolver {
|
||||||
|
/**
|
||||||
|
* A helper class to get the call context. It subclasses SecurityManager to make getClassContext() accessible. An instance of
|
||||||
|
* CallerResolver only needs to be created, not installed as an actual security manager.
|
||||||
|
*/
|
||||||
|
private static final class CallerResolver extends SecurityManager {
|
||||||
|
@Override
|
||||||
|
protected Class<?>[] getClassContext() {
|
||||||
|
return super.getClassContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int CALL_CONTEXT_OFFSET = 3; // may need to change if this class is redesigned
|
||||||
|
private static final CallerResolver CALLER_RESOLVER;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
// This can fail if the current SecurityManager does not allow
|
||||||
|
// RuntimePermission ("createSecurityManager"):
|
||||||
|
CALLER_RESOLVER = new CallerResolver();
|
||||||
|
} catch (SecurityException se) {
|
||||||
|
throw new RuntimeException("ClassLoaderResolver: could not create CallerResolver: " + se);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indexes into the current method call context with a given offset.
|
||||||
|
*/
|
||||||
|
public static Class<?> getCallerClass(final int callerOffset) {
|
||||||
|
return CALLER_RESOLVER.getClassContext()[CALL_CONTEXT_OFFSET + callerOffset];
|
||||||
|
}
|
||||||
|
}
|
111
src/dorkbox/util/LZMA.java
Normal file
111
src/dorkbox/util/LZMA.java
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import lzma.sdk.lzma.Decoder;
|
||||||
|
import lzma.sdk.lzma.Encoder;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
public class LZMA {
|
||||||
|
// LZMA (Java) 4.61 2008-11-23
|
||||||
|
// http://jponge.github.com/lzma-java/
|
||||||
|
|
||||||
|
public static final void encode(long fileSize, InputStream input, OutputStream output) throws IOException {
|
||||||
|
try {
|
||||||
|
final Encoder encoder = new Encoder();
|
||||||
|
|
||||||
|
if (!encoder.setDictionarySize(1 << 23)) {
|
||||||
|
throw new RuntimeException("Incorrect dictionary size");
|
||||||
|
}
|
||||||
|
if (!encoder.setNumFastBytes(273)) {
|
||||||
|
throw new RuntimeException("Incorrect -fb value");
|
||||||
|
}
|
||||||
|
if (!encoder.setMatchFinder(1)) {
|
||||||
|
throw new RuntimeException("Incorrect -mf value");
|
||||||
|
}
|
||||||
|
if (!encoder.setLcLpPb(3, 0, 2)) {
|
||||||
|
throw new RuntimeException("Incorrect -lc or -lp or -pb value");
|
||||||
|
}
|
||||||
|
encoder.setEndMarkerMode(false);
|
||||||
|
encoder.writeCoderProperties(output);
|
||||||
|
|
||||||
|
for (int ii = 0; ii < 8; ii++) {
|
||||||
|
output.write((int)(fileSize >>> 8 * ii) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
encoder.code(input, output, -1, -1, null);
|
||||||
|
} finally {
|
||||||
|
input.close();
|
||||||
|
output.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final ByteArrayOutputStream decode(InputStream input) throws IOException {
|
||||||
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(8192);
|
||||||
|
decode(input, byteArrayOutputStream);
|
||||||
|
return byteArrayOutputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final void decode(InputStream input, OutputStream output) throws IOException {
|
||||||
|
try {
|
||||||
|
int propertiesSize = 5;
|
||||||
|
byte[] properties = new byte[propertiesSize];
|
||||||
|
if (input.read(properties, 0, propertiesSize) != propertiesSize) {
|
||||||
|
throw new IOException("input .lzma file is too short");
|
||||||
|
}
|
||||||
|
|
||||||
|
Decoder decoder = new Decoder();
|
||||||
|
if (!decoder.setDecoderProperties(properties)) {
|
||||||
|
throw new IOException("Incorrect stream properties");
|
||||||
|
}
|
||||||
|
|
||||||
|
long outSize = 0;
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
int v = input.read();
|
||||||
|
if (v < 0) {
|
||||||
|
throw new IOException("Can't read stream size");
|
||||||
|
}
|
||||||
|
outSize |= (long)v << 8 * i;
|
||||||
|
}
|
||||||
|
if (!decoder.code(input, output, outSize)) {
|
||||||
|
throw new IOException("Error in data stream");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
output.flush();
|
||||||
|
input.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
}
|
584
src/dorkbox/util/LocationResolver.java
Normal file
584
src/dorkbox/util/LocationResolver.java
Normal file
@ -0,0 +1,584 @@
|
|||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.security.CodeSource;
|
||||||
|
import java.security.ProtectionDomain;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Vector;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience methods for working with resource/file/class locations
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
class LocationResolver {
|
||||||
|
private static final Pattern SLASH_PATTERN = Pattern.compile("\\\\");
|
||||||
|
|
||||||
|
private static
|
||||||
|
void log(String message) {
|
||||||
|
System.err.println(prefix() + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes the path. fixes %20 as spaces (in winxp at least). Converts \ -> / (windows slash -> unix slash)
|
||||||
|
*
|
||||||
|
* @return a string pointing to the cleaned path
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private static
|
||||||
|
String normalizePath(String path) throws IOException {
|
||||||
|
// make sure the slashes are in unix format.
|
||||||
|
path = SLASH_PATTERN.matcher(path)
|
||||||
|
.replaceAll("/");
|
||||||
|
|
||||||
|
// Can have %20 as spaces (in winxp at least). need to convert to proper path from URL
|
||||||
|
return URLDecoder.decode(path, "UTF-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the location of the currently loaded jar, or possibly null if it was compiled on the fly
|
||||||
|
*/
|
||||||
|
public static
|
||||||
|
File get() {
|
||||||
|
return get(LocationResolver.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the location that this classfile was loaded from, or possibly null if the class was compiled on the fly
|
||||||
|
*/
|
||||||
|
public static
|
||||||
|
File get(Class<?> clazz) {
|
||||||
|
// Get the location of this class
|
||||||
|
ProtectionDomain pDomain = clazz.getProtectionDomain();
|
||||||
|
CodeSource cSource = pDomain.getCodeSource();
|
||||||
|
|
||||||
|
// file:/X:/workspace/XYZ/classes/ when it's in ide/flat
|
||||||
|
// jar:/X:/workspace/XYZ/jarname.jar when it's jar
|
||||||
|
URL loc = cSource.getLocation();
|
||||||
|
|
||||||
|
// we don't always have a protection domain (for example, when we compile classes on the fly, from memory)
|
||||||
|
if (loc == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can have %20 as spaces (in winxp at least). need to convert to proper path from URL
|
||||||
|
try {
|
||||||
|
File file = new File(normalizePath(loc.getFile())).getAbsoluteFile()
|
||||||
|
.getCanonicalFile();
|
||||||
|
return file;
|
||||||
|
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new RuntimeException("Unable to decode file path!", e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Unable to get canonical file path!", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a URL of a given resourceName. If the resourceName is a directory, the returned URL will be the URL for the directory.
|
||||||
|
* </p>
|
||||||
|
* This method searches the disk first (via new {@link File#File(String)}, then by {@link ClassLoader#getResource(String)}, then by
|
||||||
|
* {@link ClassLoader#getSystemResource(String)}.
|
||||||
|
*
|
||||||
|
* @param resourceName the resource name to search for
|
||||||
|
*
|
||||||
|
* @return the URL for that given resource name
|
||||||
|
*/
|
||||||
|
public static
|
||||||
|
URL getResource(String resourceName) {
|
||||||
|
try {
|
||||||
|
resourceName = normalizePath(resourceName);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
URL resource = null;
|
||||||
|
|
||||||
|
// 1) maybe it's on disk? priority is disk
|
||||||
|
File file = new File(resourceName);
|
||||||
|
if (file.canRead()) {
|
||||||
|
try {
|
||||||
|
resource = file.toURI()
|
||||||
|
.toURL();
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) is it in the context classloader
|
||||||
|
if (resource == null) {
|
||||||
|
resource = Thread.currentThread()
|
||||||
|
.getContextClassLoader()
|
||||||
|
.getResource(resourceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) is it in the system classloader
|
||||||
|
if (resource == null) {
|
||||||
|
// maybe it's in the system classloader?
|
||||||
|
resource = ClassLoader.getSystemResource(resourceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4) look for it, and log the output (so we can find or debug it)
|
||||||
|
if (resource == null) {
|
||||||
|
try {
|
||||||
|
searchResource(resourceName);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves an enumeration of URLs of a given resourceName. If the resourceName is a directory, the returned list will be the URLs
|
||||||
|
* of the contents of that directory. The first URL will always be the directory URL, as returned by {@link #getResource(String)}.
|
||||||
|
* </p>
|
||||||
|
* This method searches the disk first (via new {@link File#File(String)}, then by {@link ClassLoader#getResources(String)}, then by
|
||||||
|
* {@link ClassLoader#getSystemResources(String)}.
|
||||||
|
*
|
||||||
|
* @param resourceName the resource name to search for
|
||||||
|
*
|
||||||
|
* @return the enumeration of URLs for that given resource name
|
||||||
|
*/
|
||||||
|
public static
|
||||||
|
Enumeration<URL> getResources(String resourceName) {
|
||||||
|
try {
|
||||||
|
resourceName = normalizePath(resourceName);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
Enumeration<URL> resources = null;
|
||||||
|
try {
|
||||||
|
// 1) maybe it's on disk? priority is disk
|
||||||
|
File file = new File(resourceName);
|
||||||
|
if (file.canRead()) {
|
||||||
|
ArrayDeque<URL> urlList = new ArrayDeque<URL>(4);
|
||||||
|
// add self always
|
||||||
|
urlList.add(file.toURI()
|
||||||
|
.toURL());
|
||||||
|
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
// add urls of all children
|
||||||
|
File[] files = file.listFiles();
|
||||||
|
if (files != null) {
|
||||||
|
for (int i = 0, n = files.length; i < n; i++) {
|
||||||
|
urlList.add(files[i].toURI()
|
||||||
|
.toURL());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resources = new Vector<URL>(urlList).elements();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) is it in the context classloader
|
||||||
|
if (resources == null) {
|
||||||
|
resources = Thread.currentThread()
|
||||||
|
.getContextClassLoader()
|
||||||
|
.getResources(resourceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) is it in the system classloader
|
||||||
|
if (resources == null) {
|
||||||
|
// maybe it's in the system classloader?
|
||||||
|
resources = ClassLoader.getSystemResources(resourceName);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4) look for it, and log the output (so we can find or debug it)
|
||||||
|
if (resources == null) {
|
||||||
|
searchResource(resourceName);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the resource as a stream.
|
||||||
|
* <p>
|
||||||
|
* 1) checks the disk in the relative location to the executing app<br/>
|
||||||
|
* 2) Checks the current thread context classloader <br/>
|
||||||
|
* 3) Checks the Classloader system resource
|
||||||
|
*
|
||||||
|
* @param resourceName the name, including path information (Only valid '\' as the path separator)
|
||||||
|
*
|
||||||
|
* @return the resource stream, if it could be found, otherwise null.
|
||||||
|
*/
|
||||||
|
public static
|
||||||
|
InputStream getResourceAsStream(String resourceName) {
|
||||||
|
try {
|
||||||
|
resourceName = normalizePath(resourceName);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream resourceAsStream = null;
|
||||||
|
|
||||||
|
// 1) maybe it's on disk? priority is disk
|
||||||
|
if (new File(resourceName).canRead()) {
|
||||||
|
try {
|
||||||
|
resourceAsStream = new FileInputStream(resourceName);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
// shouldn't happen, but if there is something wonky...
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) is it in the context classloader
|
||||||
|
if (resourceAsStream == null) {
|
||||||
|
resourceAsStream = Thread.currentThread()
|
||||||
|
.getContextClassLoader()
|
||||||
|
.getResourceAsStream(resourceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) is it in the system classloader
|
||||||
|
if (resourceAsStream == null) {
|
||||||
|
// maybe it's in the system classloader?
|
||||||
|
resourceAsStream = ClassLoader.getSystemResourceAsStream(resourceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 4) look for it, and log the output (so we can find or debug it)
|
||||||
|
if (resourceAsStream == null) {
|
||||||
|
try {
|
||||||
|
searchResource(resourceName);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resourceAsStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// via RIVEN at JGO. CC0 as far as I can tell.
|
||||||
|
public static
|
||||||
|
void searchResource(String path) throws IOException {
|
||||||
|
try {
|
||||||
|
path = normalizePath(path);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Root> roots = new ArrayList<Root>();
|
||||||
|
|
||||||
|
ClassLoader contextClassLoader = Thread.currentThread()
|
||||||
|
.getContextClassLoader();
|
||||||
|
|
||||||
|
if (contextClassLoader instanceof URLClassLoader) {
|
||||||
|
URL[] urLs = ((URLClassLoader) contextClassLoader).getURLs();
|
||||||
|
for (URL url : urLs) {
|
||||||
|
roots.add(new Root(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
System.err.println();
|
||||||
|
log("SEARCHING: \"" + path + "\"");
|
||||||
|
|
||||||
|
for (int attempt = 1; attempt <= 6; attempt++) {
|
||||||
|
for (Root root : roots) {
|
||||||
|
if (root.search(path, attempt)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log("FAILED: failed to find anything like");
|
||||||
|
log(" \"" + path + "\"");
|
||||||
|
log(" in all classpath entries:");
|
||||||
|
|
||||||
|
for (Root root : roots) {
|
||||||
|
final File entry = root.entry;
|
||||||
|
if (entry != null) {
|
||||||
|
log(" \"" + entry.getAbsolutePath() + "\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IOException("Unable to search for '" + path + "' in the context classloader of type '" + contextClassLoader.getClass() +
|
||||||
|
"'. Please report this issue with as many specific details as possible (OS, Java version, application version");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("Duplicates")
|
||||||
|
private static
|
||||||
|
class Root {
|
||||||
|
final File entry;
|
||||||
|
final List<String> resources = new ArrayList<String>();
|
||||||
|
|
||||||
|
public
|
||||||
|
Root(URL entry) throws IOException {
|
||||||
|
this.entry = visitRoot(entry, resources);
|
||||||
|
}
|
||||||
|
|
||||||
|
public
|
||||||
|
boolean search(String path, int attempt) {
|
||||||
|
try {
|
||||||
|
path = normalizePath(path);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (attempt) {
|
||||||
|
case 1: {
|
||||||
|
for (String resource : resources) {
|
||||||
|
if (path.equals(resource)) {
|
||||||
|
log("SUCCESS: found resource \"" + path + "\" in root: " + entry);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
case 2: {
|
||||||
|
for (String resource : resources) {
|
||||||
|
if (path.toLowerCase()
|
||||||
|
.equals(resource.toLowerCase())) {
|
||||||
|
log("FOUND: similarly named resource:");
|
||||||
|
log(" \"" + resource + "\"");
|
||||||
|
log(" in classpath entry:");
|
||||||
|
log(" \"" + entry + "\"");
|
||||||
|
log(" for access use:");
|
||||||
|
log(" getResourceAsStream(\"/" + resource + "\");");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
case 3: {
|
||||||
|
for (String resource : resources) {
|
||||||
|
String r1 = path;
|
||||||
|
String r2 = resource;
|
||||||
|
|
||||||
|
if (r1.contains("/")) {
|
||||||
|
r1 = r1.substring(r1.lastIndexOf('/') + 1);
|
||||||
|
}
|
||||||
|
if (r2.contains("/")) {
|
||||||
|
r2 = r2.substring(r2.lastIndexOf('/') + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r1.equals(r2)) {
|
||||||
|
log("FOUND: mislocated resource:");
|
||||||
|
log(" \"" + resource + "\"");
|
||||||
|
log(" in classpath entry:");
|
||||||
|
log(" \"" + entry + "\"");
|
||||||
|
log(" for access use:");
|
||||||
|
log(" getResourceAsStream(\"/" + resource + "\");");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
case 4: {
|
||||||
|
for (String resource : resources) {
|
||||||
|
String r1 = path.toLowerCase();
|
||||||
|
String r2 = resource.toLowerCase();
|
||||||
|
|
||||||
|
if (r1.contains("/")) {
|
||||||
|
r1 = r1.substring(r1.lastIndexOf('/') + 1);
|
||||||
|
}
|
||||||
|
if (r2.contains("/")) {
|
||||||
|
r2 = r2.substring(r2.lastIndexOf('/') + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r1.equals(r2)) {
|
||||||
|
log("FOUND: mislocated, similarly named resource:");
|
||||||
|
log(" \"" + resource + "\"");
|
||||||
|
log(" in classpath entry:");
|
||||||
|
log(" \"" + entry + "\"");
|
||||||
|
log(" for access use:");
|
||||||
|
log(" getResourceAsStream(\"/" + resource + "\");");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
case 5: {
|
||||||
|
for (String resource : resources) {
|
||||||
|
String r1 = path;
|
||||||
|
String r2 = resource;
|
||||||
|
|
||||||
|
if (r1.contains("/")) {
|
||||||
|
r1 = r1.substring(r1.lastIndexOf('/') + 1);
|
||||||
|
}
|
||||||
|
if (r2.contains("/")) {
|
||||||
|
r2 = r2.substring(r2.lastIndexOf('/') + 1);
|
||||||
|
}
|
||||||
|
if (r1.contains(".")) {
|
||||||
|
r1 = r1.substring(0, r1.lastIndexOf('.'));
|
||||||
|
}
|
||||||
|
if (r2.contains(".")) {
|
||||||
|
r2 = r2.substring(0, r2.lastIndexOf('.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r1.equals(r2)) {
|
||||||
|
log("FOUND: resource with different extension:");
|
||||||
|
log(" \"" + resource + "\"");
|
||||||
|
log(" in classpath entry:");
|
||||||
|
log(" \"" + entry + "\"");
|
||||||
|
log(" for access use:");
|
||||||
|
log(" getResourceAsStream(\"/" + resource + "\");");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
case 6: {
|
||||||
|
for (String resource : resources) {
|
||||||
|
String r1 = path.toLowerCase();
|
||||||
|
String r2 = resource.toLowerCase();
|
||||||
|
|
||||||
|
if (r1.contains("/")) {
|
||||||
|
r1 = r1.substring(r1.lastIndexOf('/') + 1);
|
||||||
|
}
|
||||||
|
if (r2.contains("/")) {
|
||||||
|
r2 = r2.substring(r2.lastIndexOf('/') + 1);
|
||||||
|
}
|
||||||
|
if (r1.contains(".")) {
|
||||||
|
r1 = r1.substring(0, r1.lastIndexOf('.'));
|
||||||
|
}
|
||||||
|
if (r2.contains(".")) {
|
||||||
|
r2 = r2.substring(0, r2.lastIndexOf('.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r1.equals(r2)) {
|
||||||
|
log("FOUND: similarly named resource with different extension:");
|
||||||
|
log(" \"" + resource + "\"");
|
||||||
|
log(" in classpath entry:");
|
||||||
|
log(" \"" + entry + "\"");
|
||||||
|
log(" for access use:");
|
||||||
|
log(" getResourceAsStream(\"/" + resource + "\");");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static
|
||||||
|
File visitRoot(URL url, List<String> resources) throws IOException {
|
||||||
|
if (!url.getProtocol()
|
||||||
|
.equals("file")) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
String path = url.getPath();
|
||||||
|
|
||||||
|
if (OS.isWindows()) {
|
||||||
|
if (path.startsWith("/")) {
|
||||||
|
path = path.substring(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File root = new File(path);
|
||||||
|
if (!root.exists()) {
|
||||||
|
log("failed to find classpath entry in filesystem: " + path);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root.isDirectory()) {
|
||||||
|
visitDir(normalizePath(root.getAbsolutePath()), root, resources);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final String s = root.getName()
|
||||||
|
.toLowerCase();
|
||||||
|
|
||||||
|
if (s.endsWith(".zip")) {
|
||||||
|
visitZip(root, resources);
|
||||||
|
}
|
||||||
|
else if (s.endsWith(".jar")) {
|
||||||
|
visitZip(root, resources);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log("unknown classpath entry type: " + path);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static
|
||||||
|
void visitDir(String root, File dir, Collection<String> out) {
|
||||||
|
final File[] files = dir.listFiles();
|
||||||
|
if (files != null) {
|
||||||
|
for (File file : files) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
visitDir(root, file, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
out.add(file.getAbsolutePath()
|
||||||
|
.replace('\\', '/')
|
||||||
|
.substring(root.length() + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static
|
||||||
|
void visitZip(File jar, Collection<String> out) throws IOException {
|
||||||
|
ZipInputStream zis = new ZipInputStream(new FileInputStream(jar));
|
||||||
|
while (true) {
|
||||||
|
ZipEntry entry = zis.getNextEntry();
|
||||||
|
if (entry == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
out.add(entry.getName()
|
||||||
|
.replace('\\', '/'));
|
||||||
|
}
|
||||||
|
zis.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static
|
||||||
|
String prefix() {
|
||||||
|
return "[" + LocationResolver.class.getSimpleName() + "] ";
|
||||||
|
}
|
||||||
|
}
|
316
src/dorkbox/util/OS.java
Normal file
316
src/dorkbox/util/OS.java
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||||
|
public
|
||||||
|
class OS {
|
||||||
|
public static final String LINE_SEPARATOR = System.getProperty("line.separator");
|
||||||
|
public static final String LINE_SEPARATOR_UNIX = "\n";
|
||||||
|
public static final String LINE_SEPARATOR_WINDOWS = "\r\n";
|
||||||
|
public static final Charset US_ASCII = Charset.forName("US-ASCII");
|
||||||
|
public static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||||
|
public static final Charset UTF_16LE = Charset.forName("UTF-16LE");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The currently running java version as a NUMBER. For example, "Java version 1.7u45", and converts it into 7
|
||||||
|
*/
|
||||||
|
public static final int javaVersion = _getJavaVersion();
|
||||||
|
|
||||||
|
|
||||||
|
private static final OsType osType;
|
||||||
|
private static final String originalTimeZone = TimeZone.getDefault()
|
||||||
|
.getID();
|
||||||
|
|
||||||
|
static {
|
||||||
|
/**
|
||||||
|
* By default, the timer resolution in some operating systems are not particularly high-resolution (ie: 'Thread.sleep(1)' will not
|
||||||
|
* really sleep for 1ms, but will really sleep for 16ms). This forces the JVM to use high resolution timers. This is USUALLY
|
||||||
|
* necessary on Windows.
|
||||||
|
*/
|
||||||
|
Thread timerAccuracyThread = new Thread(new Runnable() {
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
//noinspection InfiniteLoopStatement
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(Long.MAX_VALUE);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, "ForceHighResTimer");
|
||||||
|
timerAccuracyThread.setDaemon(true);
|
||||||
|
timerAccuracyThread.start();
|
||||||
|
|
||||||
|
|
||||||
|
String osName = System.getProperty("os.name");
|
||||||
|
String osArch = System.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 or not
|
||||||
|
boolean isAndroid;
|
||||||
|
try {
|
||||||
|
Class.forName("android.app.Activity");
|
||||||
|
isAndroid = true;
|
||||||
|
} catch (ClassNotFoundException e) { isAndroid = false; }
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 java version as a NUMBER. For example, "Java version 1.7u45", and converts it into 7
|
||||||
|
*/
|
||||||
|
private static
|
||||||
|
int _getJavaVersion() {
|
||||||
|
String fullJavaVersion = System.getProperty("java.version");
|
||||||
|
|
||||||
|
char versionChar;
|
||||||
|
if (fullJavaVersion.startsWith("1.")) {
|
||||||
|
versionChar = fullJavaVersion.charAt(2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
versionChar = fullJavaVersion.charAt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (versionChar) {
|
||||||
|
case '4':
|
||||||
|
return 4;
|
||||||
|
case '5':
|
||||||
|
return 5;
|
||||||
|
case '6':
|
||||||
|
return 6;
|
||||||
|
case '7':
|
||||||
|
return 7;
|
||||||
|
case '8':
|
||||||
|
return 8;
|
||||||
|
case '9':
|
||||||
|
return 9;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final
|
||||||
|
Object clone() throws java.lang.CloneNotSupportedException {
|
||||||
|
throw new java.lang.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();
|
||||||
|
}
|
||||||
|
}
|
136
src/dorkbox/util/OsType.java
Normal file
136
src/dorkbox/util/OsType.java
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
40
src/dorkbox/util/Property.java
Normal file
40
src/dorkbox/util/Property.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation to track system properties across the entire code-base. System properties are values that can be changed programmatically,
|
||||||
|
* via a "System.getProperty()", or via arguments.
|
||||||
|
* <br>
|
||||||
|
* Loading arguments is left for the end-user application. Using an annotation detector to load/save properties is recommended.
|
||||||
|
* <br>
|
||||||
|
* For example (if implemented): -Ddorkbox.Args.Debug=true
|
||||||
|
*/
|
||||||
|
@Retention(value = RetentionPolicy.RUNTIME)
|
||||||
|
@Target(value = {ElementType.FIELD})
|
||||||
|
public
|
||||||
|
@interface Property {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used to mark a unique value for the property, in case the object name is used elsewhere, is generic, or is repeated.
|
||||||
|
*/
|
||||||
|
String alias() default "";
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user