mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 04:38:00 +00:00
init2
This commit is contained in:
17
src/main/java/net/slightlymagic/braids/LICENSE.txt
Normal file
17
src/main/java/net/slightlymagic/braids/LICENSE.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
The files in this directory and in all subdirectories of it (the "Files") are
|
||||
Copyright 2011 Braids Cabal-Conjurer. They are available under either Forge's
|
||||
main license (the GNU Public License; see LICENSE.txt in Forge's top directory)
|
||||
or under the Apache License, as explained below.
|
||||
|
||||
The Files are additionally licensed under the Apache License, Version 2.0 (the
|
||||
"Apache License"); you may not use the files in this directory except in
|
||||
compliance with one of its two licenses. You may obtain a copy of the Apache
|
||||
License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the Apache License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the Apache License for the specific language governing permissions and
|
||||
limitations under the Apache License.
|
||||
@@ -0,0 +1,8 @@
|
||||
package net.slightlymagic.braids.util;
|
||||
|
||||
/**
|
||||
* Like Runnable, but it can throw any Exception.
|
||||
*/
|
||||
public interface ClumsyRunnable {
|
||||
public void run() throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package net.slightlymagic.braids.util;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Acts as both immutable Iterator and Iterable; remove method always throws
|
||||
* exception.
|
||||
*/
|
||||
public class ImmutableIterableFrom<T> implements Iterable<T>, Iterator<T> {
|
||||
private Iterator<T> iterator;
|
||||
|
||||
/**
|
||||
* Wrap an iterable so that it cannot be changed via
|
||||
* the remove method.
|
||||
*
|
||||
* @param iterable the iterable to wrap
|
||||
*/
|
||||
public ImmutableIterableFrom(Iterable<T> iterable) {
|
||||
this.iterator = iterable.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap an iterator so that its container cannot be changed via
|
||||
* the remove method.
|
||||
*
|
||||
* @param iterator the iterator to wrap
|
||||
*/
|
||||
public ImmutableIterableFrom(Iterator<T> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class acts as both an Iterable and an Iterator.
|
||||
*/
|
||||
public Iterator<T> iterator() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns hasNext from the wrapped [object's] iterator.
|
||||
*/
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns next from the wrapped [object's] iterator.
|
||||
*/
|
||||
public T next() {
|
||||
return iterator.next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Never succeeeds.
|
||||
* @throws UnsupportedOperationException always.
|
||||
*/
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package net.slightlymagic.braids.util;
|
||||
|
||||
/**
|
||||
* This exception indicates the particular method (or part of a method) being
|
||||
* called has not been implemented; getting this exception is generally
|
||||
* considered a programming error.
|
||||
*
|
||||
* Throwing this exception does not necessarily mean the method will be
|
||||
* implemented at any point in the future.
|
||||
*/
|
||||
public class NotImplementedError extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = -6714022569997781370L;
|
||||
|
||||
/**
|
||||
* No-arg constructor; this usually means the entire method or block from
|
||||
* which it is thrown has not been implemented.
|
||||
*/
|
||||
public NotImplementedError() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates what has not been implemented.
|
||||
*
|
||||
* @param message indicates what exactly has not been implemented.
|
||||
* May include information about future plans to implement the described
|
||||
* section of code.
|
||||
*/
|
||||
public NotImplementedError(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like the no-arg constructor, but with a cause parameter.
|
||||
*
|
||||
* @param cause the exception that caused this one to be thrown
|
||||
*
|
||||
* @see #NotImplementedError()
|
||||
*/
|
||||
public NotImplementedError(final Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like the String constructor, but with a cause parameter.
|
||||
*
|
||||
* @param message indicates what exactly has not been implemented.
|
||||
* May include information about future plans to implement the described
|
||||
* section of code.
|
||||
*
|
||||
* @param cause the exception that caused this one to be thrown
|
||||
*
|
||||
* @see #NotImplementedError(String)
|
||||
*/
|
||||
public NotImplementedError(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
267
src/main/java/net/slightlymagic/braids/util/UtilFunctions.java
Normal file
267
src/main/java/net/slightlymagic/braids/util/UtilFunctions.java
Normal file
@@ -0,0 +1,267 @@
|
||||
package net.slightlymagic.braids.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Some general-purpose functions.
|
||||
*/
|
||||
public final class UtilFunctions {
|
||||
/**
|
||||
* Do not instantiate.
|
||||
*/
|
||||
private UtilFunctions() {;}
|
||||
|
||||
|
||||
/**
|
||||
* Throws a NullPointerException if param is null.
|
||||
*
|
||||
* @param paramName the name of the parameter; may be null
|
||||
* @param param the parameter to test
|
||||
*/
|
||||
public static void checkNotNull(String paramName, Object param) {
|
||||
if (param != null) return;
|
||||
|
||||
NullPointerException exn = null;
|
||||
|
||||
if (paramName != null) {
|
||||
exn = new NullPointerException(paramName + " must not be null");
|
||||
}
|
||||
else {
|
||||
//
|
||||
exn = new NullPointerException();
|
||||
}
|
||||
|
||||
// Doctor the exception to appear to come from the caller.
|
||||
StackTraceElement[] trace = exn.getStackTrace();
|
||||
int len = getSliceLength(trace, 1);
|
||||
exn.setStackTrace(slice(new StackTraceElement[len], trace, 1));
|
||||
throw exn;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create an array from the (rest of) an iterator's output;
|
||||
* this function is horribly inefficient.
|
||||
*
|
||||
* Please, only use it on small iterators.
|
||||
*
|
||||
* @param iter the iterator to traverse
|
||||
*
|
||||
* @return an array of (the rest of) the iterator's values
|
||||
*/
|
||||
public static <T> T[] iteratorToArray(Iterator<T> iter) {
|
||||
ArrayList<T> list = new ArrayList<T>();
|
||||
|
||||
T item;
|
||||
while (iter.hasNext()) {
|
||||
item = iter.next();
|
||||
list.add(item);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
T[] result = (T[]) list.toArray();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the rightmost portion of an array, Python-style.
|
||||
*
|
||||
* @param <T> (inferred automatically)
|
||||
*
|
||||
* @param srcArray the array to copy (shallowly)
|
||||
*
|
||||
* @param startIndex if positive, the index (from the left) at which to
|
||||
* start copying; if negative, we treat this as the index from the right.
|
||||
* For example, calling this with startIndex = -2 returns the last two
|
||||
* items in the array, if it has that many.
|
||||
*
|
||||
* @return a shallow copy of array starting at startIndex; this may return
|
||||
* an empty array if the startIndex is out of bounds.
|
||||
*/
|
||||
public static <T extends Object> T[] slice(T[] dstArray, T[] srcArray,
|
||||
int startIndex)
|
||||
{
|
||||
if (startIndex < 0) {
|
||||
startIndex = srcArray.length + startIndex;
|
||||
if (startIndex < 0) startIndex = 0;
|
||||
}
|
||||
|
||||
if (dstArray == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
if (srcArray == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
int resultLength = getSliceLength(srcArray, startIndex);
|
||||
|
||||
if (dstArray.length != resultLength) {
|
||||
throw new ArrayIndexOutOfBoundsException(
|
||||
"First parameter must have length " + resultLength + ", but length is " + dstArray.length + ".");
|
||||
}
|
||||
|
||||
int srcIx = startIndex;
|
||||
|
||||
for (int dstIx = 0;
|
||||
dstIx < resultLength && srcIx < srcArray.length;
|
||||
dstIx++, srcIx++)
|
||||
{
|
||||
dstArray[dstIx] = srcArray[srcIx];
|
||||
}
|
||||
|
||||
return dstArray;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a slice's length in preparation for taking a slice.
|
||||
*
|
||||
* I do not like the fact that I have to use this function, but
|
||||
* Java left me with little choice.
|
||||
*
|
||||
* @see #slice(Object[], Object[], int)
|
||||
*
|
||||
* @return the length of the array that would result from calling
|
||||
* slice(Object[], Object[], int) with the given srcArray and
|
||||
* startIndex.
|
||||
*/
|
||||
public static <T> int getSliceLength(T[] srcArray, int startIndex) {
|
||||
if (startIndex < 0) {
|
||||
startIndex = srcArray.length + startIndex;
|
||||
if (startIndex < 0) startIndex = 0;
|
||||
}
|
||||
|
||||
int resultLength = srcArray.length - startIndex;
|
||||
return resultLength;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles the boilerplate null and isinstance check for an equals method.
|
||||
*
|
||||
* Example:
|
||||
* <pre>
|
||||
* public boolean equals(Object obj) {
|
||||
* MyClassName that = checkNullOrNotInstance(this, obj);
|
||||
* if (that == null) {
|
||||
* return false;
|
||||
* }
|
||||
* //...
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param goodInstance a non-null instance of type T; looks neater than
|
||||
* passing in goodInstance.getClass()
|
||||
*
|
||||
* @param obj the object to test
|
||||
*
|
||||
* @return null if obj is null or not an instance of goodInstance's class;
|
||||
* otherwise, we return obj cast to goodInstance's type
|
||||
*/
|
||||
public static <T> T checkNullOrNotInstance(T goodInstance, Object obj) {
|
||||
if (goodInstance == null) {
|
||||
throw new NullPointerException("first parameter must not be null");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<T> classT = (Class<T>) goodInstance.getClass();
|
||||
|
||||
boolean viable = true;
|
||||
|
||||
if (obj == null) viable = false;
|
||||
else if (!(classT.isInstance(obj))) viable = false;
|
||||
|
||||
if (viable) {
|
||||
return classT.cast(obj);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Safely converts an object to a String.
|
||||
*
|
||||
* @param obj to convert; may be null
|
||||
*
|
||||
* @return "null" if obj is null, obj.toString() otherwise
|
||||
*/
|
||||
public static String safeToString(Object obj) {
|
||||
if (obj == null) {
|
||||
return "null";
|
||||
}
|
||||
else {
|
||||
return obj.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove nulls and duplicate items from the list.
|
||||
*
|
||||
* This may change the list's ordering. It uses the items' equals methods to
|
||||
* determine equality.
|
||||
*
|
||||
* Advantages over HashSet: This consumes no unnecessary heap-memory, nor
|
||||
* does it require objects to implement hashCode. It is OK if
|
||||
* (o1.equals(o2) does not imply o1.hashCode() == o2.hashCode()).
|
||||
*
|
||||
* Advantages over TreeSet: This does not require a comparator.
|
||||
*
|
||||
* Disadvantages over HashSet and TreeSet: This runs in O(n*n) time.
|
||||
*
|
||||
* @param list the list to modify; this is fastest with ArrayList.
|
||||
*/
|
||||
public static <T> void smartRemoveDuplicatesAndNulls(List<T> list)
|
||||
{
|
||||
// Get rid of pesky leading nulls.
|
||||
smartRemoveDuplicatesAndNullsHelper(list, 0, null);
|
||||
|
||||
for (int earlierIx = 0; earlierIx < list.size(); earlierIx++)
|
||||
{
|
||||
for (int laterIx = earlierIx + 1; laterIx < list.size(); laterIx++)
|
||||
{
|
||||
T itemAtEarlierIx = list.get(earlierIx);
|
||||
|
||||
smartRemoveDuplicatesAndNullsHelper(list, laterIx,
|
||||
itemAtEarlierIx);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for smartRemoveDuplicatesAndNulls that is subject to
|
||||
* change; if you call this directly, you do so at your own risk!
|
||||
*
|
||||
* @param list the list to modify; if all items from startIx to the end
|
||||
* are either null or equal to objSeenPreviously, then we truncate the
|
||||
* list just before startIx.
|
||||
*
|
||||
* @param startIx the index to examine; we only move items within the range
|
||||
* of [startIx, list.size()-1].
|
||||
*
|
||||
* @param objSeenPreviously the object with which to compare list[startIx];
|
||||
* may be null.
|
||||
*/
|
||||
public static <T> void smartRemoveDuplicatesAndNullsHelper(
|
||||
List<T> list, int startIx, T objSeenPreviously)
|
||||
{
|
||||
while (startIx < list.size() &&
|
||||
(list.get(startIx) == null ||
|
||||
list.get(startIx) == objSeenPreviously ||
|
||||
list.get(startIx).equals(objSeenPreviously)))
|
||||
{
|
||||
int lastItemIx = list.size()-1;
|
||||
|
||||
// Overwrite the item at laterIx with the one at the end,
|
||||
// then delete the one at the end.
|
||||
list.set(startIx, list.get(lastItemIx));
|
||||
list.remove(lastItemIx);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/** Licensed under both the GPL and the Apache 2.0 License. */
|
||||
package net.slightlymagic.braids.util.generator;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import com.google.code.jyield.Generator;
|
||||
import com.google.code.jyield.Yieldable;
|
||||
|
||||
/**
|
||||
* This is a generator over all of the non-directories residing in a given
|
||||
* starting directory and all subdirectories of it that do NOT start with a
|
||||
* dot; this prevents the code from descending into .svn directories.
|
||||
*
|
||||
* For documentation on Java-Yield and its generators, see
|
||||
* {@link http://code.google.com/p/java-yield/}
|
||||
*/
|
||||
public class FindNonDirectoriesSkipDotDirectoriesGenerator implements Generator<File> {
|
||||
private File startDir;
|
||||
|
||||
/**
|
||||
* Create a generator at a given starting directory.
|
||||
*
|
||||
* One can invoke this generator more than once by calling its generate
|
||||
* method.
|
||||
*
|
||||
* @param startDir the directory to start in; we ignore this directory's
|
||||
* name, so if it starts with a dot, we treat it as if it didn't.
|
||||
*/
|
||||
public FindNonDirectoriesSkipDotDirectoriesGenerator(File startDir) {
|
||||
this.startDir = startDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard generate method.
|
||||
*
|
||||
* <p>Yields results to the given Yieldable. Convert Generator instances to
|
||||
* Iterables with YieldUtils.toIterable.</p>
|
||||
*
|
||||
* See {@link https://java-yield.googlecode.com/hg/docs/com/google/code/jyield/YieldUtils.html#toIterable(com.google.code.jyield.Generator)}
|
||||
*/
|
||||
public void generate(Yieldable<File> yy) {
|
||||
String[] list = startDir.list();
|
||||
|
||||
for (String filename : list) {
|
||||
File entry = new File(startDir, filename);
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
if (!filename.startsWith(".")) {
|
||||
FindNonDirectoriesSkipDotDirectoriesGenerator child = new FindNonDirectoriesSkipDotDirectoriesGenerator(entry);
|
||||
child.generate(yy);
|
||||
child = null;
|
||||
}
|
||||
// else do nothing, because it's a dot directory
|
||||
}
|
||||
else {
|
||||
// Use this instead of a return statement.
|
||||
yy.yield(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package net.slightlymagic.braids.util.generator;
|
||||
|
||||
import com.google.code.jyield.Generator;
|
||||
import com.google.code.jyield.Yieldable;
|
||||
|
||||
/**
|
||||
* Creates a Generator from an array; generators are a handy
|
||||
* substitute for passing around and creating temporary
|
||||
* lists, collections, and arrays.
|
||||
*
|
||||
* @see http://code.google.com/p/jyield/
|
||||
*/
|
||||
public class GeneratorFromArray<T> implements Generator<T> {
|
||||
private T[] array;
|
||||
|
||||
/**
|
||||
* Create a Generator from an array
|
||||
*
|
||||
* @param array from which to generate items
|
||||
*/
|
||||
public GeneratorFromArray(T[] array) {
|
||||
this.array = array;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Submits all of the array's elements to the yieldable.
|
||||
*
|
||||
* @param yy the yieldable which receives the elements
|
||||
*/
|
||||
public void generate(Yieldable<T> yy) {
|
||||
for (T item : array) {
|
||||
yy.yield(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
/** Licensed under both the GPL and the Apache 2.0 License. */
|
||||
package net.slightlymagic.braids.util.generator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import net.slightlymagic.braids.util.lambda.Lambda1;
|
||||
|
||||
import com.google.code.jyield.Generator;
|
||||
import com.google.code.jyield.YieldUtils;
|
||||
import com.google.code.jyield.Yieldable;
|
||||
|
||||
/**
|
||||
* For documentation on Java-Yield and its generators, see
|
||||
* {@link http://code.google.com/p/java-yield/}
|
||||
*/
|
||||
public final class GeneratorFunctions {
|
||||
|
||||
/**
|
||||
* Do not instantiate.
|
||||
*/
|
||||
private GeneratorFunctions() {
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate the number of items in this generator by traversing all of its
|
||||
* elements.
|
||||
*
|
||||
* Note this only works on a generator that can be reinstantiated once it
|
||||
* has been traversed. This is only an estimate, because a generator's size
|
||||
* may vary been traversals. This is especially true if the generator
|
||||
* relies on external resources, such as a file system.
|
||||
*
|
||||
* If you call this on an infinite generator, this method will never
|
||||
* return.
|
||||
*
|
||||
* @return the estimated number of items provided by this generator
|
||||
*/
|
||||
public static <T> long estimateSize(Generator<T> gen) {
|
||||
long result = 0;
|
||||
for (@SuppressWarnings("unused") T ignored : YieldUtils.toIterable(gen))
|
||||
{
|
||||
result++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Highly efficient means of filtering a long or infinite sequence.
|
||||
*
|
||||
* @param <T> any type
|
||||
*
|
||||
* @param predicate a Lambda (function) whose apply method takes an object
|
||||
* of type <T> and returns a Boolean. If it returns false or null, the
|
||||
* item from the inputGenerator is not yielded by this Generator;
|
||||
* if predicate.apply returns true, then this Generator <i>does</i>
|
||||
* yield the value.
|
||||
*
|
||||
* @param inputGenerator the sequence upon which we operate
|
||||
*
|
||||
* @return a generator which produces a subset <= the inputGenerator
|
||||
*/
|
||||
public static <T> Generator<T> filterGenerator(
|
||||
final Lambda1<Boolean,T> predicate, final Generator<T> inputGenerator)
|
||||
{
|
||||
Generator<T> result = new Generator<T>() {
|
||||
|
||||
@Override
|
||||
public void generate(final Yieldable<T> outputYield) {
|
||||
|
||||
Yieldable<T> inputYield = new Yieldable<T>() {
|
||||
Boolean pResult;
|
||||
|
||||
@Override
|
||||
public void yield(T input) {
|
||||
pResult = predicate.apply(input);
|
||||
if (pResult != null && pResult) {
|
||||
outputYield.yield(input);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
inputGenerator.generate(inputYield);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Highly efficient means of applying a transform to a long or infinite
|
||||
* sequence.
|
||||
*
|
||||
* @param <T> any type
|
||||
*
|
||||
* @param transform a Lambda (function) whose apply method takes an object
|
||||
* of type <T> and returns an object of the same type. This transforms
|
||||
* the values from the inputGenerator into this Generator.
|
||||
*
|
||||
* @param inputGenerator the sequence upon which we operate
|
||||
*
|
||||
* @return a generator that yields transform.apply's return value for
|
||||
* each item in the inputGenerator
|
||||
*/
|
||||
public static <T> Generator<T> transformGenerator(
|
||||
final Lambda1<T,T> transform, final Generator<T> inputGenerator)
|
||||
{
|
||||
Generator<T> result = new Generator<T>() {
|
||||
|
||||
@Override
|
||||
public void generate(final Yieldable<T> outputYield) {
|
||||
|
||||
Yieldable<T> inputYield = new Yieldable<T>() {
|
||||
@Override
|
||||
public void yield(T input) {
|
||||
outputYield.yield(transform.apply(input));
|
||||
}
|
||||
};
|
||||
|
||||
inputGenerator.generate(inputYield);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces a generator to be completely evaluated into a temporary data
|
||||
* structure, then returns the generator over that same structure.
|
||||
*
|
||||
* This effectively returns the same Generator, but it is a faster one.
|
||||
* This trades away heap space for reduced CPU intensity. This is
|
||||
* particuarly helpful if you know that a Generator is going to be
|
||||
* totally evaluated more than once in the near future.
|
||||
*
|
||||
* @param <T> inferred automatically
|
||||
*
|
||||
* @param unevaluated a Generator of T instances
|
||||
*
|
||||
* @return the equivalent Generator, except that the result's generate
|
||||
* method can be invoked multiple times for fast results.
|
||||
*/
|
||||
public static <T> Generator<T> solidify(Generator<T> unevaluated) {
|
||||
ArrayList<T> solidTmp = YieldUtils.toArrayList(unevaluated);
|
||||
solidTmp.trimToSize();
|
||||
return YieldUtils.toGenerator(solidTmp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select an item at random from a Generator; this causes the entire
|
||||
* Generator to be evaluated once, but only once.
|
||||
*
|
||||
* @param generator
|
||||
* the generator from which to select a random item
|
||||
*
|
||||
* @return an item chosen at random from the generator; this may be null, if
|
||||
* the generator contains null items.
|
||||
*
|
||||
* @throws NoSuchElementException
|
||||
* if the generator has no contents
|
||||
*/
|
||||
public static <T> T selectRandom(Generator<T> generator)
|
||||
throws NoSuchElementException
|
||||
{
|
||||
/*
|
||||
* This algorithm requires some explanation. Each time we encounter a
|
||||
* new item from the generator, we determine via random chance if the
|
||||
* item is the one we select. At the end of each iteration, we have a
|
||||
* candidate, and we have a count of the number of items encountered so
|
||||
* far. Each iteration has a 1/n chance of replacing the candidate with
|
||||
* the current item, where n is the number of items encountered so far.
|
||||
* This allows us to randomly select an item from the generated contents
|
||||
* with an equal distribution; and we don't have to count the number of
|
||||
* items first!
|
||||
*/
|
||||
|
||||
int n = 0;
|
||||
T candidate = null;
|
||||
|
||||
for (T item : YieldUtils.toIterable(generator)) {
|
||||
n++;
|
||||
int rand = (int) (Math.random() * n);
|
||||
// At this point, 0 <= rand < n.
|
||||
rand++; // Now, 1 <= rand <= n.
|
||||
|
||||
if (rand == 1) {
|
||||
// We rolled a 1 on an n-sided die. We have a new candidate!
|
||||
// Note that on the first iteration, this always happens,
|
||||
// because n = 1.
|
||||
candidate = item;
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
// There were no items in the generator!
|
||||
throw new NoSuchElementException("generator is empty");
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Utilities to be used with Java-Yield Generators.
|
||||
*
|
||||
* Files in this package and all subpackages are Copyright 2011 Braids Cabal-Conjurer.
|
||||
* They are licensed under both the GPL and the Apache 2.0 license.
|
||||
*
|
||||
* For documentation on Java-Yield and its generators, see
|
||||
* {@link http://code.google.com/p/java-yield/} .
|
||||
*/
|
||||
package net.slightlymagic.braids.util.generator;
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package net.slightlymagic.braids.util.lambda;
|
||||
|
||||
import net.slightlymagic.braids.util.UtilFunctions;
|
||||
import static net.slightlymagic.braids.util.UtilFunctions.checkNotNull;
|
||||
|
||||
/**
|
||||
* This embodies a promise to invoke a certain method at a later time; the
|
||||
* FrozenCall remembers the arguments to use and the return type.
|
||||
*
|
||||
* @param <T> the return type of apply
|
||||
*
|
||||
* @see Thunk
|
||||
*/
|
||||
public class FrozenCall<T> implements Thunk<T> {
|
||||
private Lambda<T> proc;
|
||||
private Object[] args;
|
||||
|
||||
public FrozenCall(Lambda<T> proc, Object[] args) {
|
||||
checkNotNull("proc", proc);
|
||||
checkNotNull("args", args);
|
||||
|
||||
this.proc = proc;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public T apply() {
|
||||
return proc.apply(args);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
FrozenCall<T> that = UtilFunctions.checkNullOrNotInstance(this, obj);
|
||||
if (that == null) return false;
|
||||
else if (!this.proc.equals(that.proc)) return false;
|
||||
else if (this.args.length != that.args.length) return false;
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
if (this.args[i] == null && that.args[i] != null) return false;
|
||||
else if (!this.args[i].equals(that.args[i])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package net.slightlymagic.braids.util.lambda;
|
||||
|
||||
public interface Lambda<R> {
|
||||
public abstract R apply(Object[] args);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package net.slightlymagic.braids.util.lambda;
|
||||
|
||||
public abstract class Lambda1<R,A1> implements Lambda<R> {
|
||||
|
||||
public abstract R apply(A1 arg1);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
//TODO @Override
|
||||
public R apply(Object[] args) {
|
||||
return apply((A1) args[0]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package net.slightlymagic.braids.util.lambda;
|
||||
|
||||
public abstract class Lambda2<R,A1,A2> implements Lambda<R> {
|
||||
|
||||
public abstract R apply(A1 arg1, A2 arg2);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
//TODO @Override
|
||||
public R apply(Object[] args) {
|
||||
return apply((A1) args[0], (A2) args[1]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package net.slightlymagic.braids.util.lambda;
|
||||
|
||||
public abstract class Lambda3<R,A1,A2,A3> implements Lambda<R> {
|
||||
|
||||
public abstract R apply(A1 arg1, A2 arg2, A3 arg3);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
//TODO @Override
|
||||
public R apply(Object[] args) {
|
||||
return apply((A1) args[0], (A2) args[1], (A3) args[2]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package net.slightlymagic.braids.util.lambda;
|
||||
|
||||
/**
|
||||
* Do not try to instantiate this class, because you cannot; this is simply
|
||||
* a marker for the null literal.
|
||||
*/
|
||||
public final class Null {
|
||||
private Null() {;}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package net.slightlymagic.braids.util.lambda;
|
||||
|
||||
public interface Thunk<T> {
|
||||
public abstract T apply();
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
/** Forge Card Game */
|
||||
package net.slightlymagic.braids.util.lambda;
|
||||
@@ -0,0 +1,2 @@
|
||||
/** Forge Card Game */
|
||||
package net.slightlymagic.braids.util;
|
||||
@@ -0,0 +1,528 @@
|
||||
package net.slightlymagic.braids.util.progress_monitor;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.esotericsoftware.minlog.Log;
|
||||
|
||||
/**
|
||||
* This base class also acts as a "null" progress monitor; it doesn't display
|
||||
* anything when updated.
|
||||
*
|
||||
* Absolute times are measured in seconds, in congruence with ProgressMonitor.
|
||||
*
|
||||
* @see net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor
|
||||
*/
|
||||
public class BaseProgressMonitor implements BraidsProgressMonitor {
|
||||
private int numPhases;
|
||||
private int currentPhase;
|
||||
private long totalUnitsThisPhase;
|
||||
private long unitsCompletedSoFarThisPhase;
|
||||
private float minUIUpdateIntervalSec;
|
||||
private long lastUIUpdateTime;
|
||||
private long phaseOneStartTime;
|
||||
private long currentPhaseStartTime;
|
||||
private float currentPhaseExponent;
|
||||
private long[] phaseDurationHistorySecList;
|
||||
private float[] phaseWeights;
|
||||
|
||||
public final int SECONDS_PER_MINUTE = 60;
|
||||
public final int SECONDS_PER_HOUR = 60 * SECONDS_PER_MINUTE;
|
||||
public final int SECONDS_PER_DAY = 24 * SECONDS_PER_HOUR;
|
||||
|
||||
|
||||
/**
|
||||
* Convenience for
|
||||
* BaseProgressMonitor(numPhases, totalUnitsFirstPhase, 2.0f, null).
|
||||
*
|
||||
* @see #BaseProgressMonitor(int,long,float,float[])
|
||||
*/
|
||||
public BaseProgressMonitor(int numPhases, long totalUnitsFirstPhase) {
|
||||
this(numPhases, totalUnitsFirstPhase, 2.0f, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience for
|
||||
* BaseProgressMonitor(numPhases, totalUnitsFirstPhase,
|
||||
* minUIUpdateIntervalSec, null).
|
||||
*
|
||||
* @see #BaseProgressMonitor(int,long,float,float[])
|
||||
*/
|
||||
public BaseProgressMonitor(int numPhases, long totalUnitsFirstPhase,
|
||||
float minUIUpdateIntervalSec)
|
||||
{
|
||||
this(numPhases, totalUnitsFirstPhase, minUIUpdateIntervalSec, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes fields and starts the timers.
|
||||
*
|
||||
* @param numPhases the total number of phases we will monitor
|
||||
*
|
||||
* @param totalUnitsFirstPhase how many units to expect in phase 1
|
||||
*
|
||||
* @param minUIUpdateIntervalSec the approximate interval at which we
|
||||
* update the user interface, in seconds
|
||||
*
|
||||
* @param phaseWeights may be null; if not null, this indicates the
|
||||
* relative weight of each phase in terms of time to complete all phases.
|
||||
* Index 0 of this array indicates phase 1's weight, index 1 indicates
|
||||
* the weight of phase 2, and so on. If null, all phases are considered to
|
||||
* take an equal amount of time to complete, which is equivalent to setting
|
||||
* all phase weights to 1.0f. For example, if there are two phases, and
|
||||
* the phase weights are set to {2.0f, 1.0f}, then the methods that compute
|
||||
* the final ETA (Estimated Time of Arrival or completion) will assume that
|
||||
* phase 2 takes half as long as phase 1. In other words, the operation
|
||||
* will spend 67% of its time in phase 1, and 33% of its time in phase 2.
|
||||
*/
|
||||
public BaseProgressMonitor(int numPhases, long totalUnitsFirstPhase,
|
||||
float minUIUpdateIntervalSec, float[] phaseWeights)
|
||||
{
|
||||
this.numPhases = numPhases;
|
||||
this.currentPhase = 1;
|
||||
this.unitsCompletedSoFarThisPhase = 0L;
|
||||
this.minUIUpdateIntervalSec = minUIUpdateIntervalSec;
|
||||
this.lastUIUpdateTime = 0L;
|
||||
this.phaseOneStartTime = new Date().getTime()/1000;
|
||||
this.currentPhaseStartTime = this.phaseOneStartTime;
|
||||
this.currentPhaseExponent = 1;
|
||||
this.phaseDurationHistorySecList = new long[numPhases];
|
||||
|
||||
if (phaseWeights == null) {
|
||||
this.phaseWeights = new float[numPhases];
|
||||
for (int ix = 0; ix < numPhases; ix++) {
|
||||
this.phaseWeights[ix] = 1.0f;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.phaseWeights = phaseWeights;
|
||||
}
|
||||
|
||||
setTotalUnitsThisPhase(totalUnitsFirstPhase);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing.
|
||||
*/
|
||||
public void dispose() {
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor#getNumPhases()
|
||||
*/
|
||||
public int getNumPhases() {
|
||||
return this.numPhases;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor#getMinUpdateIntervalSec()
|
||||
*/
|
||||
public float getMinUpdateIntervalSec() {
|
||||
return this.minUIUpdateIntervalSec;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor#getCurrentPhase()
|
||||
*/
|
||||
public int getCurrentPhase() {
|
||||
return this.currentPhase;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor#getUnitsCompletedSoFarThisPhase()
|
||||
*/
|
||||
public long getUnitsCompletedSoFarThisPhase() {
|
||||
return this.unitsCompletedSoFarThisPhase;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor#getTotalUnitsThisPhase()
|
||||
*/
|
||||
public long getTotalUnitsThisPhase() {
|
||||
return this.totalUnitsThisPhase;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor#getLastUIUpdateTime()
|
||||
*/
|
||||
public long getLastUIUpdateTime() {
|
||||
return this.lastUIUpdateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor#getPhaseOneStartTime()
|
||||
*/
|
||||
public long getPhaseOneStartTime() {
|
||||
return this.phaseOneStartTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor#getCurrentPhaseStartTime()
|
||||
*/
|
||||
public long getCurrentPhaseStartTime() {
|
||||
return this.currentPhaseStartTime;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor#setMinUpdateIntervalSec(float)
|
||||
*/
|
||||
public void setMinUpdateIntervalSec(float value) {
|
||||
this.minUIUpdateIntervalSec = value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor#setTotalUnitsThisPhase(long)
|
||||
*/
|
||||
public void setTotalUnitsThisPhase(long value) {
|
||||
this.totalUnitsThisPhase = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor#getPercentCompleteOfThisPhaseAsString()
|
||||
*/
|
||||
public String getPercentCompleteOfThisPhaseAsString() {
|
||||
|
||||
Float percent = getPercentCompleteOfThisPhaseAsFloat();
|
||||
|
||||
if (percent != null) {
|
||||
return Integer.toString((int) (float) percent);
|
||||
}
|
||||
else {
|
||||
return "??";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor#getTotalPercentCompleteAsString()
|
||||
*/
|
||||
public String getTotalPercentCompleteAsString() {
|
||||
Float percent = getTotalPercentCompleteAsFloat();
|
||||
|
||||
if (percent == null) {
|
||||
return "??";
|
||||
}
|
||||
else {
|
||||
return Integer.toString((int) (float) percent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convenience for getRelativeETAAsString(false), meaning to compute the
|
||||
* value for the end of the last phase.
|
||||
*
|
||||
* @see #getRelativeETAAsString(boolean)
|
||||
*/
|
||||
public String getRelativeETAAsString() {
|
||||
return getRelativeETAAsString(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor#getRelativeETAAsString(boolean)
|
||||
*/
|
||||
public String getRelativeETAAsString(boolean thisPhaseOnly) {
|
||||
|
||||
Integer etaSec = getRelativeETASec(thisPhaseOnly);
|
||||
|
||||
if (etaSec == null) {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
String result = "";
|
||||
if (etaSec > SECONDS_PER_DAY) {
|
||||
result += Integer.toString(etaSec / SECONDS_PER_DAY);
|
||||
result += " da, ";
|
||||
etaSec %= SECONDS_PER_DAY; // Shave off the portion recorded.
|
||||
}
|
||||
if (result.length() > 0 || etaSec > SECONDS_PER_HOUR) {
|
||||
result += Integer.toString(etaSec / SECONDS_PER_HOUR);
|
||||
result += " hr, ";
|
||||
etaSec %= SECONDS_PER_HOUR; // Shave off the portion recorded.
|
||||
}
|
||||
if (result.length() > 0 || etaSec > SECONDS_PER_MINUTE) {
|
||||
result += Integer.toString(etaSec / SECONDS_PER_MINUTE);
|
||||
result += " min, ";
|
||||
etaSec %= SECONDS_PER_MINUTE; // Shave off the portion recorded.
|
||||
}
|
||||
|
||||
result += Integer.toString(etaSec);
|
||||
result += " sec";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience for getAbsoluteETAAsLocalTimeString(false), meaning to
|
||||
* compute the value for the end of the last phase.
|
||||
*
|
||||
* @see #getAbsoluteETAAsLocalTimeString(boolean)
|
||||
*/
|
||||
public String getAbsoluteETAAsLocalTimeString() {
|
||||
return getAbsoluteETAAsLocalTimeString(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor#getAbsoluteETAAsLocalTimeString(boolean)
|
||||
*/
|
||||
public String getAbsoluteETAAsLocalTimeString(boolean thisPhaseOnly) {
|
||||
Long etaTime = getAbsoluteETATime(thisPhaseOnly);
|
||||
|
||||
if (etaTime == null) {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
return (new Date(etaTime*1000).toString());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor#incrementUnitsCompletedThisPhase(long)
|
||||
*/
|
||||
public void incrementUnitsCompletedThisPhase(long numUnits) {
|
||||
this.unitsCompletedSoFarThisPhase += numUnits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must call this immediately after updating the UI, to
|
||||
* preserve the integrity of the shouldUpdateUI method.
|
||||
*/
|
||||
protected void justUpdatedUI() {
|
||||
this.lastUIUpdateTime = new Date().getTime()/1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor#shouldUpdateUI()
|
||||
*/
|
||||
public boolean shouldUpdateUI() {
|
||||
|
||||
doctorStartTimes();
|
||||
long nowTime = (new Date().getTime()/1000);
|
||||
|
||||
if (nowTime - this.lastUIUpdateTime >= this.minUIUpdateIntervalSec ||
|
||||
(this.getUnitsCompletedSoFarThisPhase() ==
|
||||
this.getTotalUnitsThisPhase()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor#markCurrentPhaseAsComplete(long)
|
||||
*/
|
||||
public void markCurrentPhaseAsComplete(long totalUnitsNextPhase) {
|
||||
|
||||
if ((this.currentPhase > this.numPhases)) {
|
||||
String message = "The phase just completed (";
|
||||
message += this.currentPhase;
|
||||
message += ") is greater than the total number ";
|
||||
message += "of anticipated phases (";
|
||||
message += this.numPhases;
|
||||
message += "); the latter is probably incorrect.";
|
||||
|
||||
Log.warn(message);
|
||||
}
|
||||
|
||||
this.currentPhase += 1;
|
||||
this.unitsCompletedSoFarThisPhase = 0;
|
||||
setTotalUnitsThisPhase(totalUnitsNextPhase);
|
||||
this.currentPhaseExponent = 1;
|
||||
|
||||
long nowTime = (new Date().getTime()/1000);
|
||||
long durationOfThisPhaseSec = nowTime - this.currentPhaseStartTime;
|
||||
if (durationOfThisPhaseSec < 0) {
|
||||
durationOfThisPhaseSec = 0;
|
||||
}
|
||||
|
||||
if (0 <= currentPhase-2 && currentPhase-2 < phaseDurationHistorySecList.length) {
|
||||
this.phaseDurationHistorySecList[currentPhase-2] = durationOfThisPhaseSec;
|
||||
}
|
||||
this.currentPhaseStartTime = nowTime;
|
||||
|
||||
if (this.currentPhase >= this.numPhases) {
|
||||
String message = "Actual individual phase durations: [";
|
||||
for (int ix = 0 ; ix < phaseDurationHistorySecList.length ; ix++) {
|
||||
message += phaseDurationHistorySecList[ix] + ", ";
|
||||
}
|
||||
|
||||
Log.info(message + ']');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor#sendMessage(java.lang.String)
|
||||
*/
|
||||
public void sendMessage(String message) {
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor#setCurrentPhaseAsExponential(float)
|
||||
*/
|
||||
public void setCurrentPhaseAsExponential(float value) {
|
||||
this.currentPhaseExponent = value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor#getCurrentPhaseExponent()
|
||||
*/
|
||||
public float getCurrentPhaseExponent() {
|
||||
return this.currentPhaseExponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return number in range [0.0, 100.0] or null.
|
||||
*/
|
||||
protected Float getPercentCompleteOfThisPhaseAsFloat() {
|
||||
if (this.totalUnitsThisPhase < 1 ||
|
||||
this.unitsCompletedSoFarThisPhase > this.totalUnitsThisPhase) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
float ratio = ((float) (this.unitsCompletedSoFarThisPhase)) /
|
||||
((float) this.totalUnitsThisPhase);
|
||||
|
||||
ratio = (float) Math.pow(ratio, this.getCurrentPhaseExponent());
|
||||
|
||||
return (ratio * 100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns number in range [0.0, 100.0] or null.
|
||||
*/
|
||||
protected Float getTotalPercentCompleteAsFloat() {
|
||||
long totalPoints = 0;
|
||||
for (float weight : this.phaseWeights) {
|
||||
totalPoints += weight * 100;
|
||||
}
|
||||
|
||||
Float percentThisPhase = getPercentCompleteOfThisPhaseAsFloat();
|
||||
|
||||
if (percentThisPhase == null) {
|
||||
// If we can't know the percentage for this phase, use a
|
||||
// conservative estimate.
|
||||
percentThisPhase = 0.0f;
|
||||
}
|
||||
|
||||
long pointsSoFar = 0;
|
||||
for (int ix = 0; ix < this.currentPhase-1; ix++) {
|
||||
// We get full points for (all the phases completed prior to this one.
|
||||
pointsSoFar += phaseWeights[ix] * 100;
|
||||
}
|
||||
|
||||
pointsSoFar += percentThisPhase * this.phaseWeights[this.currentPhase-1];
|
||||
|
||||
if (totalPoints <= 0.0 || pointsSoFar > totalPoints) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return (100.0f * pointsSoFar) / totalPoints;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convenience for getRelativeETASec(false), meaning to compute the value
|
||||
* for the end of the last phase.
|
||||
*
|
||||
* @see #getRelativeETASec(boolean)
|
||||
*/
|
||||
protected Integer getRelativeETASec() {
|
||||
return getRelativeETASec(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return estimated seconds until completion for either thisPhaseOnly
|
||||
* or for the entire operation. May return null if unknown.
|
||||
*/
|
||||
protected Integer getRelativeETASec(boolean thisPhaseOnly) {
|
||||
|
||||
Long absoluteETATime = getAbsoluteETATime(thisPhaseOnly);
|
||||
if (absoluteETATime == null) {
|
||||
return null;
|
||||
}
|
||||
return (int) (absoluteETATime - (new Date().getTime()/1000));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convenience for getAbsoluteETATime(false), meaning to compute the value
|
||||
* for the end of all phases.
|
||||
*
|
||||
* @see #getAbsoluteETATime(boolean)
|
||||
*/
|
||||
protected Long getAbsoluteETATime() {
|
||||
return getAbsoluteETATime(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the estimated time (in absolute seconds) at which thisPhaseOnly
|
||||
* or the entire operation will be completed. May return null if (unknown.
|
||||
*/
|
||||
protected Long getAbsoluteETATime(boolean thisPhaseOnly) {
|
||||
doctorStartTimes();
|
||||
|
||||
// If we're in the last phase, the overall ETA is the same as the ETA
|
||||
// for (this particular phase.
|
||||
if (this.getCurrentPhase() >= this.getNumPhases()) {
|
||||
thisPhaseOnly = true;
|
||||
}
|
||||
|
||||
Float percentDone = null;
|
||||
long startTime = 0L;
|
||||
|
||||
if (thisPhaseOnly) {
|
||||
percentDone = getPercentCompleteOfThisPhaseAsFloat();
|
||||
startTime = this.currentPhaseStartTime;
|
||||
}
|
||||
else {
|
||||
percentDone = getTotalPercentCompleteAsFloat();
|
||||
startTime = this.phaseOneStartTime;
|
||||
}
|
||||
|
||||
if (percentDone == null || percentDone <= 0.001) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Elapsed time is to percent done as total time is to total done =>
|
||||
// elapsed/percentDone == totalTime/100.0 =>
|
||||
long totalTime = (long) (100.0f * ((new Date().getTime()/1000) - startTime) / percentDone);
|
||||
|
||||
return totalTime + startTime;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Repair the start times in case the system clock has been moved
|
||||
* backwards.
|
||||
*/
|
||||
protected void doctorStartTimes() {
|
||||
|
||||
long nowTime = (new Date().getTime()/1000);
|
||||
|
||||
if (this.lastUIUpdateTime > nowTime) {
|
||||
this.lastUIUpdateTime = 0;
|
||||
}
|
||||
|
||||
if (this.phaseOneStartTime > nowTime) {
|
||||
this.phaseOneStartTime = nowTime;
|
||||
}
|
||||
|
||||
if (this.currentPhaseStartTime > nowTime) {
|
||||
this.currentPhaseStartTime = nowTime;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package net.slightlymagic.braids.util.progress_monitor;
|
||||
|
||||
/**
|
||||
* Interface for a progress monitor that can have multiple phases
|
||||
* and periodically update its UI.
|
||||
*
|
||||
* All times must be in seconds; absolute times are measured in seconds since
|
||||
* 01 Jan 1970 00:00:00 UTC (GMT) a la (new Date().getTime()/1000).
|
||||
*/
|
||||
public interface BraidsProgressMonitor {
|
||||
|
||||
/**
|
||||
* Destroy this progress monitor, making it no longer usable and/or
|
||||
* visible.
|
||||
*/
|
||||
public void dispose();
|
||||
|
||||
/**
|
||||
* @return the total number of phases monitored by this object.
|
||||
*/
|
||||
public abstract int getNumPhases();
|
||||
|
||||
/**
|
||||
* @return the approximate minimum interval in seconds at which the UI
|
||||
* should be updated.
|
||||
*/
|
||||
public abstract float getMinUpdateIntervalSec();
|
||||
|
||||
/**
|
||||
* @return the current phase number; this is never less than 1 (one).
|
||||
*/
|
||||
public abstract int getCurrentPhase();
|
||||
|
||||
/**
|
||||
* @return the number of units (an intentionally vague amount) completed
|
||||
* so far in the current phase.
|
||||
*/
|
||||
public abstract long getUnitsCompletedSoFarThisPhase();
|
||||
|
||||
/**
|
||||
* @return the total units we expect to process in this phase
|
||||
*/
|
||||
public abstract long getTotalUnitsThisPhase();
|
||||
|
||||
/**
|
||||
* @return the time in absolute seconds since the UI was last updated
|
||||
*/
|
||||
public abstract long getLastUIUpdateTime();
|
||||
|
||||
/**
|
||||
* @return the time in absolute seconds at which the first phase started
|
||||
*/
|
||||
public abstract long getPhaseOneStartTime();
|
||||
|
||||
/**
|
||||
* @return the time in absolute seconds at which the current phase started
|
||||
*/
|
||||
public abstract long getCurrentPhaseStartTime();
|
||||
|
||||
/**
|
||||
* @param value
|
||||
* the approximate time in relative seconds at which the UI
|
||||
* should be updated periodically
|
||||
*/
|
||||
public abstract void setMinUpdateIntervalSec(float value);
|
||||
|
||||
/**
|
||||
* @param value the total number of units expected to processed in this
|
||||
* phase
|
||||
*/
|
||||
public abstract void setTotalUnitsThisPhase(long value);
|
||||
|
||||
/**
|
||||
* Resulting string does not contain a percent sign.
|
||||
*
|
||||
* @return the percentage completion of this phase as a String with no
|
||||
* percent sign.
|
||||
*/
|
||||
public abstract String getPercentCompleteOfThisPhaseAsString();
|
||||
|
||||
/**
|
||||
* Resulting string does not contain a percent sign.
|
||||
*
|
||||
* @return the percentage completion at this point, taking into account all
|
||||
* phases and phase-weights, as a String with no percent sign.
|
||||
*/
|
||||
public abstract String getTotalPercentCompleteAsString();
|
||||
|
||||
/**
|
||||
* May return "unknown"
|
||||
*/
|
||||
public abstract String getRelativeETAAsString(boolean thisPhaseOnly);
|
||||
|
||||
/**
|
||||
* May return "unknown"
|
||||
*/
|
||||
public abstract String getAbsoluteETAAsLocalTimeString(boolean thisPhaseOnly);
|
||||
|
||||
/**
|
||||
* Note this will NOT advance the phase.
|
||||
* To do that, use markCurrentPhaseAsComplete().
|
||||
*/
|
||||
public abstract void incrementUnitsCompletedThisPhase(long numUnits);
|
||||
|
||||
/**
|
||||
* Returns a boolean, whether or not to display the updated information.
|
||||
* This throttles the update so it doesn't refresh so fast that it is
|
||||
* unreadable. Implementers should call this method from their own
|
||||
* incrementUnitsCompletedThisPhase method.
|
||||
*
|
||||
* If we have just reached 100% for (the current phase, we return true,
|
||||
* even if it would otherwise be too soon to update the UI.
|
||||
*/
|
||||
public abstract boolean shouldUpdateUI();
|
||||
|
||||
/**
|
||||
* This is the only way to advance the phase number.
|
||||
* It automatically "starts the clock" for the next phase.
|
||||
*
|
||||
* @param totalUnitsNextPhase if unknown, use zero (0), and be sure to call
|
||||
* setTotalUnitsThisPhase() soon after.
|
||||
*/
|
||||
public abstract void markCurrentPhaseAsComplete(long totalUnitsNextPhase);
|
||||
|
||||
/**
|
||||
* Attempt to display a message to the user; not all implementations
|
||||
* support this.
|
||||
*
|
||||
* If they do not, they may silently ignore this call.
|
||||
*
|
||||
* @param message the message to display
|
||||
*/
|
||||
public abstract void sendMessage(String message);
|
||||
|
||||
|
||||
/**
|
||||
* Mark the current phase as having an exponential rate; such phases
|
||||
* reach their totalUnits slower and slower as they process more units.
|
||||
*
|
||||
* By default, a phase is considered to be linear, meaning this value is
|
||||
* 1.0f.
|
||||
*
|
||||
* @param value usually less than 1.0f; often determined empirically.
|
||||
*/
|
||||
public abstract void setCurrentPhaseAsExponential(float value);
|
||||
|
||||
/**
|
||||
* @return the exponent for this phase
|
||||
*/
|
||||
public abstract float getCurrentPhaseExponent();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package net.slightlymagic.braids.util.progress_monitor;
|
||||
|
||||
public class StderrProgressMonitor extends BaseProgressMonitor {
|
||||
|
||||
/**
|
||||
* @see BaseProgressMonitor(int,long)
|
||||
*/
|
||||
public StderrProgressMonitor(int numPhases, long totalUnitsFirstPhase) {
|
||||
this(numPhases, totalUnitsFirstPhase, 2.0f, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BaseProgressMonitor(int,long,float)
|
||||
*/
|
||||
public StderrProgressMonitor(int numPhases, long totalUnitsFirstPhase,
|
||||
float minUpdateIntervalSec)
|
||||
{
|
||||
this(numPhases, totalUnitsFirstPhase, minUpdateIntervalSec, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BaseProgressMonitor(int,long,float,float[])
|
||||
*/
|
||||
public StderrProgressMonitor(int numPhases, long totalUnitsFirstPhase,
|
||||
float minUpdateIntervalSec, float[] phaseWeights)
|
||||
{
|
||||
super(numPhases, totalUnitsFirstPhase, minUpdateIntervalSec, phaseWeights);
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* @see BaseProgressMonitor#incrementUnitsCompletedThisPhase(long)
|
||||
*/
|
||||
public void incrementUnitsCompletedThisPhase(long numUnits) {
|
||||
super.incrementUnitsCompletedThisPhase(numUnits);
|
||||
|
||||
if (shouldUpdateUI()) {
|
||||
|
||||
if ((getNumPhases() > 1)) {
|
||||
printUpdate(
|
||||
"Phase " + getCurrentPhase() + ": " +
|
||||
getUnitsCompletedSoFarThisPhase() + " units processed. " +
|
||||
"Overall: " + getTotalPercentCompleteAsString() + "% complete, " +
|
||||
"ETA in " + getRelativeETAAsString() + "."
|
||||
);
|
||||
}
|
||||
else {
|
||||
printUpdate(
|
||||
"Overall: " +
|
||||
getUnitsCompletedSoFarThisPhase() + " units processed " +
|
||||
"(" + getTotalPercentCompleteAsString() + "%); " +
|
||||
"ETA in " + getRelativeETAAsString() + "."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a message to stderr, overwriting the current estimate; calls
|
||||
* from outside this class should provide a newline character at the
|
||||
* end of the messsage.
|
||||
*
|
||||
* @param message the message to display
|
||||
*/
|
||||
public void printUpdate(String message) {
|
||||
|
||||
while (message.length() < 79) {
|
||||
message += ' ';
|
||||
}
|
||||
|
||||
System.err.print("\r");
|
||||
System.err.print(message);
|
||||
|
||||
if (message.length() > 79) {
|
||||
System.err.print("\n");
|
||||
}
|
||||
|
||||
justUpdatedUI();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
/** Forge Card Game */
|
||||
package net.slightlymagic.braids.util.progress_monitor;
|
||||
Reference in New Issue
Block a user