Cleanup and minor javadoc fixes

master
Ondřej Hruška 10 years ago
parent e973aab384
commit 16956fa927
  1. 76
      src/mightypork/utils/Convert.java
  2. 18
      src/mightypork/utils/MapSort.java
  3. 54
      src/mightypork/utils/Reflect.java
  4. 37
      src/mightypork/utils/Support.java
  5. 11
      src/mightypork/utils/annotations/Alias.java
  6. 4
      src/mightypork/utils/annotations/FactoryMethod.java
  7. 2
      src/mightypork/utils/annotations/Stub.java
  8. 104
      src/mightypork/utils/config/SimpleConfig.java
  9. 28
      src/mightypork/utils/config/propmgr/Property.java
  10. 138
      src/mightypork/utils/config/propmgr/PropertyManager.java
  11. 44
      src/mightypork/utils/config/propmgr/PropertyStore.java
  12. 18
      src/mightypork/utils/config/propmgr/properties/BooleanProperty.java
  13. 18
      src/mightypork/utils/config/propmgr/properties/DoubleProperty.java
  14. 18
      src/mightypork/utils/config/propmgr/properties/IntegerProperty.java
  15. 18
      src/mightypork/utils/config/propmgr/properties/StringProperty.java
  16. 70
      src/mightypork/utils/config/propmgr/store/PropertyFile.java
  17. 114
      src/mightypork/utils/config/propmgr/store/SortedProperties.java
  18. 50
      src/mightypork/utils/eventbus/BusEvent.java
  19. 215
      src/mightypork/utils/eventbus/EventBus.java
  20. 93
      src/mightypork/utils/eventbus/EventChannel.java
  21. 40
      src/mightypork/utils/eventbus/clients/BusNode.java
  22. 24
      src/mightypork/utils/eventbus/clients/ClientHub.java
  23. 7
      src/mightypork/utils/eventbus/clients/ClientList.java
  24. 10
      src/mightypork/utils/eventbus/clients/DelegatingClient.java
  25. 40
      src/mightypork/utils/eventbus/clients/DelegatingList.java
  26. 6
      src/mightypork/utils/eventbus/clients/ToggleableClient.java
  27. 6
      src/mightypork/utils/eventbus/events/DestroyEvent.java
  28. 15
      src/mightypork/utils/eventbus/events/UpdateEvent.java
  29. 11
      src/mightypork/utils/eventbus/events/flags/DelayedEvent.java
  30. 9
      src/mightypork/utils/eventbus/events/flags/DirectEvent.java
  31. 4
      src/mightypork/utils/eventbus/events/flags/NonConsumableEvent.java
  32. 4
      src/mightypork/utils/eventbus/events/flags/NonRejectableEvent.java
  33. 9
      src/mightypork/utils/eventbus/events/flags/NotLoggedEvent.java
  34. 9
      src/mightypork/utils/eventbus/events/flags/SingleReceiverEvent.java
  35. 30
      src/mightypork/utils/exceptions/CorruptDataException.java
  36. 30
      src/mightypork/utils/exceptions/IllegalValueException.java
  37. 30
      src/mightypork/utils/exceptions/KeyAlreadyExistsException.java
  38. 25
      src/mightypork/utils/files/FileSuffixFilter.java
  39. 96
      src/mightypork/utils/files/FileTreeDiff.java
  40. 64
      src/mightypork/utils/files/FileUtil.java
  41. 16
      src/mightypork/utils/files/InstanceLock.java
  42. 52
      src/mightypork/utils/files/OsUtils.java
  43. 96
      src/mightypork/utils/files/WorkDir.java
  44. 65
      src/mightypork/utils/files/zip/ZipBuilder.java
  45. 92
      src/mightypork/utils/files/zip/ZipUtils.java
  46. 4
      src/mightypork/utils/interfaces/Destroyable.java
  47. 10
      src/mightypork/utils/interfaces/Enableable.java
  48. 8
      src/mightypork/utils/interfaces/Hideable.java
  49. 14
      src/mightypork/utils/interfaces/Pauseable.java
  50. 4
      src/mightypork/utils/interfaces/Pollable.java
  51. 6
      src/mightypork/utils/interfaces/Updateable.java
  52. 243
      src/mightypork/utils/ion/Ion.java
  53. 12
      src/mightypork/utils/ion/IonBinary.java
  54. 12
      src/mightypork/utils/ion/IonBundled.java
  55. 216
      src/mightypork/utils/ion/IonDataBundle.java
  56. 247
      src/mightypork/utils/ion/IonInput.java
  57. 34
      src/mightypork/utils/ion/IonMapWrapper.java
  58. 225
      src/mightypork/utils/ion/IonOutput.java
  59. 34
      src/mightypork/utils/ion/IonSequenceWrapper.java
  60. 18
      src/mightypork/utils/ion/IonizerBinary.java
  61. 18
      src/mightypork/utils/ion/IonizerBundled.java
  62. 172
      src/mightypork/utils/logging/Log.java
  63. 22
      src/mightypork/utils/logging/monitors/LogMonitor.java
  64. 4
      src/mightypork/utils/logging/monitors/LogMonitorStdout.java
  65. 62
      src/mightypork/utils/logging/writers/ArchivingLog.java
  66. 42
      src/mightypork/utils/logging/writers/LogWriter.java
  67. 81
      src/mightypork/utils/logging/writers/SimpleLog.java
  68. 2
      src/mightypork/utils/math/AlignX.java
  69. 2
      src/mightypork/utils/math/AlignY.java
  70. 181
      src/mightypork/utils/math/Calc.java
  71. 92
      src/mightypork/utils/math/Polar.java
  72. 129
      src/mightypork/utils/math/Range.java
  73. 93
      src/mightypork/utils/math/algo/Coord.java
  74. 37
      src/mightypork/utils/math/algo/Move.java
  75. 62
      src/mightypork/utils/math/algo/Moves.java
  76. 48
      src/mightypork/utils/math/algo/floodfill/FloodFill.java
  77. 4
      src/mightypork/utils/math/algo/pathfinding/Heuristic.java
  78. 133
      src/mightypork/utils/math/algo/pathfinding/PathFinder.java
  79. 33
      src/mightypork/utils/math/algo/pathfinding/PathFinderProxy.java
  80. 2
      src/mightypork/utils/math/algo/pathfinding/heuristics/DiagonalHeuristic.java
  81. 2
      src/mightypork/utils/math/algo/pathfinding/heuristics/ManhattanHeuristic.java
  82. 20
      src/mightypork/utils/math/angles/Angles.java
  83. 70
      src/mightypork/utils/math/angles/Deg.java
  84. 46
      src/mightypork/utils/math/angles/Rad.java
  85. 90
      src/mightypork/utils/math/animation/Animator.java
  86. 40
      src/mightypork/utils/math/animation/AnimatorBounce.java
  87. 34
      src/mightypork/utils/math/animation/AnimatorRewind.java
  88. 156
      src/mightypork/utils/math/animation/Easing.java
  89. 228
      src/mightypork/utils/math/animation/NumAnimated.java
  90. 31
      src/mightypork/utils/math/animation/NumAnimatedDeg.java
  91. 31
      src/mightypork/utils/math/animation/NumAnimatedRad.java
  92. 138
      src/mightypork/utils/math/animation/VectAnimated.java
  93. 136
      src/mightypork/utils/math/color/Color.java
  94. 27
      src/mightypork/utils/math/color/ColorAlphaAdjuster.java
  95. 33
      src/mightypork/utils/math/color/ColorHsb.java
  96. 27
      src/mightypork/utils/math/color/ColorRgb.java
  97. 15
      src/mightypork/utils/math/color/Grad.java
  98. 9
      src/mightypork/utils/math/color/GradH.java
  99. 9
      src/mightypork/utils/math/color/GradV.java
  100. 8
      src/mightypork/utils/math/color/pal/CGA.java
  101. Some files were not shown because too many files have changed in this diff Show More

@ -4,14 +4,14 @@ package mightypork.utils;
/** /**
* Utility for converting Object to data types; Can also convert strings to data * Utility for converting Object to data types; Can also convert strings to data
* types. * types.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class Convert { public class Convert {
/** /**
* Get INTEGER * Get INTEGER
* *
* @param o object * @param o object
* @param def default value * @param def default value
* @return integer * @return integer
@ -26,11 +26,11 @@ public class Convert {
} catch (final NumberFormatException e) {} } catch (final NumberFormatException e) {}
return def; return def;
} }
/** /**
* Get DOUBLE * Get DOUBLE
* *
* @param o object * @param o object
* @param def default value * @param def default value
* @return double * @return double
@ -45,11 +45,11 @@ public class Convert {
} catch (final NumberFormatException e) {} } catch (final NumberFormatException e) {}
return def; return def;
} }
/** /**
* Get FLOAT * Get FLOAT
* *
* @param o object * @param o object
* @param def default value * @param def default value
* @return float * @return float
@ -62,11 +62,11 @@ public class Convert {
} catch (final NumberFormatException e) {} } catch (final NumberFormatException e) {}
return def; return def;
} }
/** /**
* Get BOOLEAN * Get BOOLEAN
* *
* @param o object * @param o object
* @param def default value * @param def default value
* @return boolean * @return boolean
@ -76,7 +76,7 @@ public class Convert {
if (o == null) return def; if (o == null) return def;
if (o instanceof Boolean) return ((Boolean) o).booleanValue(); if (o instanceof Boolean) return ((Boolean) o).booleanValue();
if (o instanceof Number) return ((Number) o).intValue() != 0; if (o instanceof Number) return ((Number) o).intValue() != 0;
if (o instanceof String) { if (o instanceof String) {
final String s = ((String) o).trim().toLowerCase(); final String s = ((String) o).trim().toLowerCase();
if (s.equals("0")) return false; if (s.equals("0")) return false;
@ -85,26 +85,26 @@ public class Convert {
final double n = Double.parseDouble(s); final double n = Double.parseDouble(s);
return n != 0; return n != 0;
} catch (final NumberFormatException e) {} } catch (final NumberFormatException e) {}
if (s.equals("true")) return true; if (s.equals("true")) return true;
if (s.equals("yes")) return true; if (s.equals("yes")) return true;
if (s.equals("y")) return true; if (s.equals("y")) return true;
if (s.equals("a")) return true; if (s.equals("a")) return true;
if (s.equals("enabled")) return true; if (s.equals("enabled")) return true;
if (s.equals("false")) return false; if (s.equals("false")) return false;
if (s.equals("no")) return false; if (s.equals("no")) return false;
if (s.equals("n")) return false; if (s.equals("n")) return false;
if (s.equals("disabled")) return true; if (s.equals("disabled")) return true;
} }
return def; return def;
} }
/** /**
* Get STRING * Get STRING
* *
* @param o object * @param o object
* @param def default value * @param def default value
* @return String * @return String
@ -113,22 +113,22 @@ public class Convert {
{ {
if (o == null) return def; if (o == null) return def;
if (o instanceof String) return ((String) o); if (o instanceof String) return ((String) o);
if (o instanceof Float) return Support.str((float) o); if (o instanceof Float) return Support.str((float) o);
if (o instanceof Double) return Support.str((double) o); if (o instanceof Double) return Support.str((double) o);
if (o instanceof Class<?>) { if (o instanceof Class<?>) {
return Support.str(o); return Support.str(o);
} }
return o.toString(); return o.toString();
} }
/** /**
* Get INTEGER * Get INTEGER
* *
* @param o object * @param o object
* @return integer * @return integer
*/ */
@ -136,11 +136,11 @@ public class Convert {
{ {
return toInteger(o, 0); return toInteger(o, 0);
} }
/** /**
* Get DOUBLE * Get DOUBLE
* *
* @param o object * @param o object
* @return double * @return double
*/ */
@ -148,11 +148,11 @@ public class Convert {
{ {
return toDouble(o, 0d); return toDouble(o, 0d);
} }
/** /**
* Get FLOAT * Get FLOAT
* *
* @param o object * @param o object
* @return float * @return float
*/ */
@ -160,11 +160,11 @@ public class Convert {
{ {
return toFloat(o, 0f); return toFloat(o, 0f);
} }
/** /**
* Get BOOLEAN * Get BOOLEAN
* *
* @param o object * @param o object
* @return boolean * @return boolean
*/ */
@ -172,11 +172,11 @@ public class Convert {
{ {
return toBoolean(o, false); return toBoolean(o, false);
} }
/** /**
* Get STRING * Get STRING
* *
* @param o object * @param o object
* @return String * @return String
*/ */

@ -1,20 +1,25 @@
package mightypork.utils; package mightypork.utils;
import java.util.*; import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
/** /**
* Map sorting utils * Map sorting utils
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class MapSort { public class MapSort {
/** /**
* Sort a map by keys, maintaining key-value pairs, using natural order. * Sort a map by keys, maintaining key-value pairs, using natural order.
* *
* @param map map to be sorted * @param map map to be sorted
* @return linked hash map with sorted entries * @return linked hash map with sorted entries
*/ */
@ -27,7 +32,7 @@ public class MapSort {
/** /**
* Sort a map by keys, maintaining key-value pairs. * Sort a map by keys, maintaining key-value pairs.
* *
* @param map map to be sorted * @param map map to be sorted
* @param comparator a comparator, or null for natural ordering * @param comparator a comparator, or null for natural ordering
* @return linked hash map with sorted entries * @return linked hash map with sorted entries
@ -61,7 +66,7 @@ public class MapSort {
/** /**
* Sort a map by values, maintaining key-value pairs, using natural order. * Sort a map by values, maintaining key-value pairs, using natural order.
* *
* @param map map to be sorted * @param map map to be sorted
* @return linked hash map with sorted entries * @return linked hash map with sorted entries
*/ */
@ -74,7 +79,7 @@ public class MapSort {
/** /**
* Sort a map by values, maintaining key-value pairs. * Sort a map by values, maintaining key-value pairs.
* *
* @param map map to be sorted * @param map map to be sorted
* @param comparator a comparator, or null for natural ordering * @param comparator a comparator, or null for natural ordering
* @return linked hash map with sorted entries * @return linked hash map with sorted entries
@ -85,6 +90,7 @@ public class MapSort {
Collections.sort(entries, new Comparator<Map.Entry<K, V>>() { Collections.sort(entries, new Comparator<Map.Entry<K, V>>() {
@SuppressWarnings("unchecked")
@Override @Override
public int compare(Entry<K, V> o1, Entry<K, V> o2) public int compare(Entry<K, V> o1, Entry<K, V> o2)
{ {

@ -10,14 +10,14 @@ import java.lang.reflect.Type;
/** /**
* Miscelanous reflection-related utilities * Miscelanous reflection-related utilities
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class Reflect { public class Reflect {
/** /**
* Get annotation of given type from an object * Get annotation of given type from an object
* *
* @param tested the examined object * @param tested the examined object
* @param annotation annotation we want * @param annotation annotation we want
* @return the anotation on that object, or null * @return the anotation on that object, or null
@ -26,11 +26,11 @@ public class Reflect {
{ {
return tested.getClass().getAnnotation(annotation); return tested.getClass().getAnnotation(annotation);
} }
/** /**
* Check if an object has an annotation of given trype * Check if an object has an annotation of given trype
* *
* @param tested the examined object * @param tested the examined object
* @param annotation annotation we want * @param annotation annotation we want
* @return true if the annotation is present on the object * @return true if the annotation is present on the object
@ -39,48 +39,58 @@ public class Reflect {
{ {
return tested.getClass().isAnnotationPresent(annotation); return tested.getClass().isAnnotationPresent(annotation);
} }
/** /**
* Get generic parameters of a class * Get generic parameters of a class
* *
* @param clazz the examined class * @param clazz the examined class
* @return parameter types * @return parameter types
*/ */
public static Class<?>[] getGenericParameters(Class<?> clazz) public static Class<?>[] getGenericParameters(Class<?> clazz)
{ {
// BEHOLD, MAGIC! // BEHOLD, MAGIC!
final Type evtc = clazz.getGenericSuperclass(); final Type evtc = clazz.getGenericSuperclass();
if (evtc instanceof ParameterizedType) { if (evtc instanceof ParameterizedType) {
final Type[] types = ((ParameterizedType) evtc).getActualTypeArguments(); final Type[] types = ((ParameterizedType) evtc).getActualTypeArguments();
final Class<?>[] classes = new Class<?>[types.length]; final Class<?>[] classes = new Class<?>[types.length];
for (int i = 0; i < types.length; i++) { for (int i = 0; i < types.length; i++) {
classes[i] = (Class<?>) types[i]; classes[i] = (Class<?>) types[i];
} }
return classes; return classes;
} }
throw new RuntimeException(Support.str(clazz) + " is not generic."); throw new RuntimeException(Support.str(clazz) + " is not generic.");
} }
/**
* Get value of a public static final field. If the modifiers don't match,
* an exception is thrown.
*
* @param objClass the class
* @param fieldName field to retrieve
* @return the field value
* @throws ReflectiveOperationException if the field is not constant, or if
* the value could not be retrieved.
*/
public static Object getConstantFieldValue(Class<?> objClass, String fieldName) throws ReflectiveOperationException public static Object getConstantFieldValue(Class<?> objClass, String fieldName) throws ReflectiveOperationException
{ {
final Field fld = objClass.getDeclaredField(fieldName); final Field fld = objClass.getDeclaredField(fieldName);
final int modif = fld.getModifiers(); final int modif = fld.getModifiers();
if (!Modifier.isFinal(modif) || !Modifier.isStatic(modif)) { if (!Modifier.isFinal(modif) || !Modifier.isStatic(modif)) {
throw new RuntimeException("The " + fieldName + " field of " + Support.str(objClass) + " must be static and final!"); throw new ReflectiveOperationException("The " + fieldName + " field of " + Support.str(objClass) + " must be static and final!");
} }
fld.setAccessible(true); fld.setAccessible(true);
return fld.get(null); return fld.get(null);
} }
} }

@ -14,14 +14,14 @@ import mightypork.utils.annotations.Alias;
/** /**
* Miscelanous utilities * Miscelanous utilities
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public final class Support { public final class Support {
/** /**
* Create a new thread of the runnable, and start it. * Create a new thread of the runnable, and start it.
* *
* @param r runnable * @param r runnable
* @return the thread started * @return the thread started
*/ */
@ -35,7 +35,7 @@ public final class Support {
/** /**
* Pick first non-null option * Pick first non-null option
* *
* @param options options * @param options options
* @return the selected option * @return the selected option
*/ */
@ -51,7 +51,7 @@ public final class Support {
/** /**
* Get current time/date for given format. * Get current time/date for given format.
* *
* @param format format, according to {@link DateFormat}. * @param format format, according to {@link DateFormat}.
* @return the formatted time/date * @return the formatted time/date
*/ */
@ -64,7 +64,7 @@ public final class Support {
/** /**
* Parse array of vararg key, value pairs to a LinkedHashMap.<br> * Parse array of vararg key, value pairs to a LinkedHashMap.<br>
* Example: * Example:
* *
* <pre> * <pre>
* Object[] array = { * Object[] array = {
* &quot;one&quot;, 1, * &quot;one&quot;, 1,
@ -72,10 +72,10 @@ public final class Support {
* &quot;three&quot;, 9, * &quot;three&quot;, 9,
* &quot;four&quot;, 16 * &quot;four&quot;, 16
* }; * };
* *
* Map&lt;String, Integer&gt; args = parseVarArgs(array); * Map&lt;String, Integer&gt; args = parseVarArgs(array);
* </pre> * </pre>
* *
* @param args varargs * @param args varargs
* @return LinkedHashMap * @return LinkedHashMap
* @throws ClassCastException in case of incompatible type in the array * @throws ClassCastException in case of incompatible type in the array
@ -107,7 +107,7 @@ public final class Support {
/** /**
* Get if an Object is in array (using equals) * Get if an Object is in array (using equals)
* *
* @param needle checked Object * @param needle checked Object
* @param haystack array of Objects * @param haystack array of Objects
* @return is in array * @return is in array
@ -123,7 +123,7 @@ public final class Support {
/** /**
* Get if string is in array * Get if string is in array
* *
* @param needle checked string * @param needle checked string
* @param case_sensitive case sensitive comparision * @param case_sensitive case sensitive comparision
* @param haystack array of possible values * @param haystack array of possible values
@ -144,7 +144,7 @@ public final class Support {
/** /**
* Make enumeration iterable * Make enumeration iterable
* *
* @param enumeration enumeration * @param enumeration enumeration
* @return iterable wrapper * @return iterable wrapper
*/ */
@ -155,7 +155,7 @@ public final class Support {
/** /**
* Helper class for iterationg over an {@link Enumeration} * Helper class for iterationg over an {@link Enumeration}
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
* @param <T> target element type (will be cast) * @param <T> target element type (will be cast)
*/ */
@ -167,7 +167,8 @@ public final class Support {
/** /**
* @param enumeration the iterated enumeration * @param enumeration the iterated enumeration
*/ */
public IterableEnumerationWrapper(Enumeration<? extends T> enumeration) { public IterableEnumerationWrapper(Enumeration<? extends T> enumeration)
{
this.enumeration = enumeration; this.enumeration = enumeration;
} }
@ -205,9 +206,9 @@ public final class Support {
/** /**
* Convert a class to string, preserving name and outer class, but excluding * Convert a class to string, preserving name and outer class, but excluding
* path. * path.
* *
* @param cls * @param cls the class
* @return * @return class name
*/ */
public static String str(Class<?> cls) public static String str(Class<?> cls)
{ {
@ -236,7 +237,7 @@ public final class Support {
/** /**
* Convert double to string, remove the mess at the end. * Convert double to string, remove the mess at the end.
* *
* @param d double * @param d double
* @return string * @return string
*/ */
@ -253,7 +254,7 @@ public final class Support {
/** /**
* Convert float to string, remove the mess at the end. * Convert float to string, remove the mess at the end.
* *
* @param f float * @param f float
* @return string * @return string
*/ */
@ -270,7 +271,7 @@ public final class Support {
/** /**
* Convert object to string. If the object overrides toString(), it is * Convert object to string. If the object overrides toString(), it is
* caled. Otherwise it's class name is converted to string. * caled. Otherwise it's class name is converted to string.
* *
* @param o object * @param o object
* @return string representation * @return string representation
*/ */

@ -1,13 +1,18 @@
package mightypork.utils.annotations; package mightypork.utils.annotations;
import java.lang.annotation.*; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** /**
* Specify pretty name to be used when logging / converting class name to * Specify pretty name to be used when logging / converting class name to
* string. * string.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@ -15,6 +20,6 @@ import java.lang.annotation.*;
@Documented @Documented
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
public @interface Alias { public @interface Alias {
String name(); String name();
} }

@ -11,12 +11,12 @@ import java.lang.annotation.Target;
/** /**
* Marks a static factory method. This is a description annotation and has no * Marks a static factory method. This is a description annotation and has no
* other function. * other function.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
@Documented @Documented
public @interface FactoryMethod { public @interface FactoryMethod {
} }

@ -12,7 +12,7 @@ import java.lang.annotation.Target;
* Marked method can be safely overriden; it's left blank (or with default * Marked method can be safely overriden; it's left blank (or with default
* implementation) as a convenience.<br> * implementation) as a convenience.<br>
* This is a description annotation and has no other function. * This is a description annotation and has no other function.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
@Documented @Documented

@ -19,14 +19,14 @@ import mightypork.utils.logging.Log;
* empty lines and lines without "=" are ignored<br> * empty lines and lines without "=" are ignored<br>
* lines with "=" must have "key = value" format, or a warning is logged.<br> * lines with "=" must have "key = value" format, or a warning is logged.<br>
* use "NULL" to create empty value. * use "NULL" to create empty value.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class SimpleConfig { public class SimpleConfig {
/** /**
* Load list from file * Load list from file
* *
* @param file file * @param file file
* @return map of keys and values * @return map of keys and values
* @throws IOException * @throws IOException
@ -34,14 +34,14 @@ public class SimpleConfig {
public static List<String> listFromFile(File file) throws IOException public static List<String> listFromFile(File file) throws IOException
{ {
final String fileText = FileUtil.fileToString(file); final String fileText = FileUtil.fileToString(file);
return listFromString(fileText); return listFromString(fileText);
} }
/** /**
* Load map from file * Load map from file
* *
* @param file file * @param file file
* @return map of keys and values * @return map of keys and values
* @throws IOException * @throws IOException
@ -49,98 +49,98 @@ public class SimpleConfig {
public static Map<String, String> mapFromFile(File file) throws IOException public static Map<String, String> mapFromFile(File file) throws IOException
{ {
final String fileText = FileUtil.fileToString(file); final String fileText = FileUtil.fileToString(file);
return mapFromString(fileText); return mapFromString(fileText);
} }
/** /**
* Load list from string * Load list from string
* *
* @param text text of the file * @param text text of the file
* @return map of keys and values * @return map of keys and values
*/ */
public static List<String> listFromString(String text) public static List<String> listFromString(String text)
{ {
final List<String> list = new ArrayList<>(); final List<String> list = new ArrayList<>();
final String[] groupsLines = text.split("\n"); final String[] groupsLines = text.split("\n");
for (String s : groupsLines) { for (String s : groupsLines) {
// ignore invalid lines // ignore invalid lines
if (s.length() == 0) continue; if (s.length() == 0) continue;
if (s.startsWith("#") || s.startsWith("//")) continue; if (s.startsWith("#") || s.startsWith("//")) continue;
// NULL value // NULL value
if (s.equalsIgnoreCase("NULL")) s = null; if (s.equalsIgnoreCase("NULL")) s = null;
if (s != null) s = s.replace("\\n", "\n"); if (s != null) s = s.replace("\\n", "\n");
// save extracted key-value pair // save extracted key-value pair
list.add(s); list.add(s);
} }
return list; return list;
} }
/** /**
* Load map from string * Load map from string
* *
* @param text text of the file * @param text text of the file
* @return map of keys and values * @return map of keys and values
*/ */
public static Map<String, String> mapFromString(String text) public static Map<String, String> mapFromString(String text)
{ {
final LinkedHashMap<String, String> pairs = new LinkedHashMap<>(); final LinkedHashMap<String, String> pairs = new LinkedHashMap<>();
final String[] groupsLines = text.split("\n"); final String[] groupsLines = text.split("\n");
for (final String s : groupsLines) { for (final String s : groupsLines) {
// ignore invalid lines // ignore invalid lines
if (s.length() == 0) continue; if (s.length() == 0) continue;
if (s.startsWith("#") || s.startsWith("//")) continue; if (s.startsWith("#") || s.startsWith("//")) continue;
if (!s.contains("=")) continue; if (!s.contains("=")) continue;
// split and trim // split and trim
String[] parts = s.split("="); String[] parts = s.split("=");
for (int i = 0; i < parts.length; i++) { for (int i = 0; i < parts.length; i++) {
parts[i] = parts[i].trim(); parts[i] = parts[i].trim();
} }
// check if both parts are valid // check if both parts are valid
if (parts.length == 0) { if (parts.length == 0) {
Log.w("Bad line in config file: " + s); Log.w("Bad line in config file: " + s);
continue; continue;
} }
if (parts.length == 1) { if (parts.length == 1) {
parts = new String[] { parts[0], "" }; parts = new String[] { parts[0], "" };
} }
if (parts.length != 2) { if (parts.length != 2) {
Log.w("Bad line in config file: " + s); Log.w("Bad line in config file: " + s);
continue; continue;
} }
// NULL value // NULL value
if (parts[0].equalsIgnoreCase("NULL")) parts[0] = null; if (parts[0].equalsIgnoreCase("NULL")) parts[0] = null;
if (parts[1].equalsIgnoreCase("NULL")) parts[1] = null; if (parts[1].equalsIgnoreCase("NULL")) parts[1] = null;
if (parts[0] != null) parts[0] = parts[0].replace("\\n", "\n"); if (parts[0] != null) parts[0] = parts[0].replace("\\n", "\n");
if (parts[1] != null) parts[1] = parts[1].replace("\\n", "\n"); if (parts[1] != null) parts[1] = parts[1].replace("\\n", "\n");
// save extracted key-value pair // save extracted key-value pair
pairs.put(parts[0], parts[1]); pairs.put(parts[0], parts[1]);
} }
return pairs; return pairs;
} }
/** /**
* Save map to file * Save map to file
* *
* @param target * @param target
* @param data * @param data
* @param allowNulls allow nulls. * @param allowNulls allow nulls.
@ -149,38 +149,38 @@ public class SimpleConfig {
public static void mapToFile(File target, Map<String, String> data, boolean allowNulls) throws IOException public static void mapToFile(File target, Map<String, String> data, boolean allowNulls) throws IOException
{ {
final List<String> lines = new ArrayList<>(); final List<String> lines = new ArrayList<>();
for (final Entry<String, String> e : data.entrySet()) { for (final Entry<String, String> e : data.entrySet()) {
String key = e.getKey(); String key = e.getKey();
String value = e.getValue(); String value = e.getValue();
if (!allowNulls && (key == null || value == null || key.length() == 0 || value.length() == 0)) continue; if (!allowNulls && (key == null || value == null || key.length() == 0 || value.length() == 0)) continue;
if (key == null) key = "NULL"; if (key == null) key = "NULL";
if (value == null) value = "NULL"; if (value == null) value = "NULL";
key = key.replace("\n", "\\n"); key = key.replace("\n", "\\n");
value = value.replace("\n", "\\n"); value = value.replace("\n", "\\n");
lines.add(key + " = " + value); lines.add(key + " = " + value);
} }
String text = ""; // # File written by SimpleConfig String text = ""; // # File written by SimpleConfig
for (final String s : lines) { for (final String s : lines) {
if (text.length() > 0) text += "\n"; if (text.length() > 0) text += "\n";
text += s; text += s;
} }
FileUtil.stringToFile(target, text); FileUtil.stringToFile(target, text);
} }
/** /**
* Save list to file * Save list to file
* *
* @param target * @param target
* @param data * @param data
* @throws IOException * @throws IOException
@ -188,18 +188,18 @@ public class SimpleConfig {
public static void listToFile(File target, List<String> data) throws IOException public static void listToFile(File target, List<String> data) throws IOException
{ {
String text = ""; // # File written by SimpleConfig String text = ""; // # File written by SimpleConfig
for (String s : data) { for (String s : data) {
if (text.length() > 0) text += "\n"; if (text.length() > 0) text += "\n";
if (s == null) s = "NULL"; if (s == null) s = "NULL";
s = s.replace("\n", "\\n"); s = s.replace("\n", "\\n");
text += s; text += s;
} }
FileUtil.stringToFile(target, text); FileUtil.stringToFile(target, text);
} }
} }

@ -9,7 +9,7 @@ import mightypork.utils.annotations.Stub;
* Property entry for the {@link PropertyManager}.<br> * Property entry for the {@link PropertyManager}.<br>
* Extending this class can be used to add custom property types that are not * Extending this class can be used to add custom property types that are not
* supported by default. * supported by default.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
* @param <T> property type * @param <T> property type
*/ */
@ -24,26 +24,28 @@ public abstract class Property<T> {
/** /**
* Create a property without comment * Create a property without comment
* *
* @param key key in the config file * @param key key in the config file
* @param defaultValue defualt property value (used as fallback when * @param defaultValue defualt property value (used as fallback when
* parsing) * parsing)
*/ */
public Property(String key, T defaultValue) { public Property(String key, T defaultValue)
{
this(key, defaultValue, null); this(key, defaultValue, null);
} }
/** /**
* Create a property with a comment * Create a property with a comment
* *
* @param key key in the config file * @param key key in the config file
* @param defaultValue defualt property value (used as fallback when * @param defaultValue default property value, used as fallback when
* parsing) * parsing. Initially the value is assigned to defaultValue.
* @param comment optional property comment included above the property in * @param comment optional property comment included above the property in
* the config file. Can be null. * the config file. Can be null.
*/ */
public Property(String key, T defaultValue, String comment) { public Property(String key, T defaultValue, String comment)
{
this.comment = comment; this.comment = comment;
this.key = key; this.key = key;
this.value = defaultValue; this.value = defaultValue;
@ -54,7 +56,7 @@ public abstract class Property<T> {
/** /**
* Parse a string representation of the value into this property. If the * Parse a string representation of the value into this property. If the
* value cannot be decoded, use the default value instead. * value cannot be decoded, use the default value instead.
* *
* @param string property value as string * @param string property value as string
*/ */
public abstract void fromString(String string); public abstract void fromString(String string);
@ -62,7 +64,7 @@ public abstract class Property<T> {
/** /**
* Get property value as string (compatible with `fromString()) * Get property value as string (compatible with `fromString())
* *
* @return property value as string * @return property value as string
*/ */
@Override @Override
@ -75,7 +77,7 @@ public abstract class Property<T> {
/** /**
* Get the current property value * Get the current property value
* *
* @return the value * @return the value
*/ */
public T getValue() public T getValue()
@ -87,7 +89,7 @@ public abstract class Property<T> {
/** /**
* Set property value.<br> * Set property value.<br>
* Uses Object to allow setValue(Object) method in {@link PropertyManager} * Uses Object to allow setValue(Object) method in {@link PropertyManager}
* *
* @param value value to set. * @param value value to set.
* @throws ClassCastException in case of incompatible type. * @throws ClassCastException in case of incompatible type.
*/ */
@ -100,7 +102,7 @@ public abstract class Property<T> {
/** /**
* Get property comment. * Get property comment.
* *
* @return the comment text (can be null if no comment is defined) * @return the comment text (can be null if no comment is defined)
*/ */
public String getComment() public String getComment()
@ -111,7 +113,7 @@ public abstract class Property<T> {
/** /**
* Get property key * Get property key
* *
* @return property key * @return property key
*/ */
public String getKey() public String getKey()

@ -18,95 +18,97 @@ import mightypork.utils.logging.Log;
/** /**
* Property manager with advanced formatting and value checking. * Property manager with advanced formatting and value checking.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class PropertyManager { public class PropertyManager {
private final TreeMap<String, Property<?>> entries = new TreeMap<>(); private final TreeMap<String, Property<?>> entries = new TreeMap<>();
private final TreeMap<String, String> renameTable = new TreeMap<>(); private final TreeMap<String, String> renameTable = new TreeMap<>();
private PropertyStore props; private final PropertyStore props;
/** /**
* Create property manager from file path and a header comment.<br> * Create property manager from file path and a header comment.<br>
* This is the same as using a {@link PropertyFile} store. * This is the same as using a {@link PropertyFile} store.
* *
* @param file property file * @param file property file
* @param comment header comment. * @param comment header comment.
*/ */
public PropertyManager(File file, String comment) { public PropertyManager(File file, String comment)
{
this(new PropertyFile(file, comment)); this(new PropertyFile(file, comment));
} }
/** /**
* Create property manager based on provided {@link PropertyStore} * Create property manager based on provided {@link PropertyStore}
* *
* @param props a property store implementation backing this property * @param props a property store implementation backing this property
* manager * manager
*/ */
public PropertyManager(PropertyStore props) { public PropertyManager(PropertyStore props)
{
this.props = props; this.props = props;
} }
/** /**
* Load from file * Load from file
*/ */
public void load() public void load()
{ {
props.load(); props.load();
// rename keys (useful if keys change but value is to be kept) // rename keys (useful if keys change but value is to be kept)
for (final Entry<String, String> entry : renameTable.entrySet()) { for (final Entry<String, String> entry : renameTable.entrySet()) {
final String value = props.getProperty(entry.getKey()); final String value = props.getProperty(entry.getKey());
if (value == null) continue; if (value == null) continue;
final String oldKey = entry.getKey(); final String oldKey = entry.getKey();
final String newKey = entry.getValue(); final String newKey = entry.getValue();
props.removeProperty(oldKey); props.removeProperty(oldKey);
props.setProperty(newKey, value, entries.get(newKey).getComment()); props.setProperty(newKey, value, entries.get(newKey).getComment());
} }
for (final Property<?> entry : entries.values()) { for (final Property<?> entry : entries.values()) {
entry.fromString(props.getProperty(entry.getKey())); entry.fromString(props.getProperty(entry.getKey()));
} }
} }
public void save() public void save()
{ {
try { try {
final ArrayList<String> keyList = new ArrayList<>(); final ArrayList<String> keyList = new ArrayList<>();
// validate entries one by one, replace with default when needed // validate entries one by one, replace with default when needed
for (final Property<?> entry : entries.values()) { for (final Property<?> entry : entries.values()) {
keyList.add(entry.getKey()); keyList.add(entry.getKey());
props.setProperty(entry.getKey(), entry.toString(), entry.getComment()); props.setProperty(entry.getKey(), entry.toString(), entry.getComment());
} }
// removed unused props // removed unused props
for (final String key : props.keys()) { for (final String key : props.keys()) {
if (!keyList.contains(key)) { if (!keyList.contains(key)) {
props.removeProperty(key); props.removeProperty(key);
} }
} }
props.save(); props.save();
} catch (final IOException ioe) { } catch (final IOException ioe) {
ioe.printStackTrace(); ioe.printStackTrace();
} }
} }
/** /**
* Get a property entry (rarely used) * Get a property entry (rarely used)
* *
* @param k key * @param k key
* @return the entry * @return the entry
*/ */
@ -119,11 +121,11 @@ public class PropertyManager {
return null; return null;
} }
} }
/** /**
* Get boolean property * Get boolean property
* *
* @param k key * @param k key
* @return the boolean found, or false * @return the boolean found, or false
*/ */
@ -131,11 +133,11 @@ public class PropertyManager {
{ {
return Convert.toBoolean(getProperty(k).getValue()); return Convert.toBoolean(getProperty(k).getValue());
} }
/** /**
* Get numeric property * Get numeric property
* *
* @param k key * @param k key
* @return the int found, or null * @return the int found, or null
*/ */
@ -143,11 +145,11 @@ public class PropertyManager {
{ {
return Convert.toInteger(getProperty(k).getValue()); return Convert.toInteger(getProperty(k).getValue());
} }
/** /**
* Get numeric property as double * Get numeric property as double
* *
* @param k key * @param k key
* @return the double found, or null * @return the double found, or null
*/ */
@ -155,11 +157,11 @@ public class PropertyManager {
{ {
return Convert.toDouble(getProperty(k).getValue()); return Convert.toDouble(getProperty(k).getValue());
} }
/** /**
* Get string property * Get string property
* *
* @param k key * @param k key
* @return the string found, or null * @return the string found, or null
*/ */
@ -167,11 +169,11 @@ public class PropertyManager {
{ {
return Convert.toString(getProperty(k).getValue()); return Convert.toString(getProperty(k).getValue());
} }
/** /**
* Get arbitrary property. Make sure it's of the right type! * Get arbitrary property. Make sure it's of the right type!
* *
* @param k key * @param k key
* @return the prioperty found * @return the prioperty found
*/ */
@ -184,11 +186,11 @@ public class PropertyManager {
return null; return null;
} }
} }
/** /**
* Add a boolean property * Add a boolean property
* *
* @param k key * @param k key
* @param d default value * @param d default value
* @param comment the in-file comment * @param comment the in-file comment
@ -197,11 +199,11 @@ public class PropertyManager {
{ {
addProperty(new BooleanProperty(k, d, comment)); addProperty(new BooleanProperty(k, d, comment));
} }
/** /**
* Add a numeric property (double) * Add a numeric property (double)
* *
* @param k key * @param k key
* @param d default value * @param d default value
* @param comment the in-file comment * @param comment the in-file comment
@ -210,11 +212,11 @@ public class PropertyManager {
{ {
addProperty(new DoubleProperty(k, d, comment)); addProperty(new DoubleProperty(k, d, comment));
} }
/** /**
* Add a numeric property * Add a numeric property
* *
* @param k key * @param k key
* @param d default value * @param d default value
* @param comment the in-file comment * @param comment the in-file comment
@ -223,11 +225,11 @@ public class PropertyManager {
{ {
addProperty(new IntegerProperty(k, d, comment)); addProperty(new IntegerProperty(k, d, comment));
} }
/** /**
* Add a string property * Add a string property
* *
* @param k key * @param k key
* @param d default value * @param d default value
* @param comment the in-file comment * @param comment the in-file comment
@ -236,22 +238,22 @@ public class PropertyManager {
{ {
addProperty(new StringProperty(k, d, comment)); addProperty(new StringProperty(k, d, comment));
} }
/** /**
* Add a generic property (can be used with custom property types) * Add a generic property (can be used with custom property types)
* *
* @param prop property to add * @param prop property to add
*/ */
public <T> void addProperty(Property<T> prop) public <T> void addProperty(Property<T> prop)
{ {
entries.put(prop.getKey(), prop); entries.put(prop.getKey(), prop);
} }
/** /**
* Rename key before loading; value is preserved * Rename key before loading; value is preserved
* *
* @param oldKey old key * @param oldKey old key
* @param newKey new key * @param newKey new key
*/ */
@ -260,11 +262,11 @@ public class PropertyManager {
renameTable.put(oldKey, newKey); renameTable.put(oldKey, newKey);
return; return;
} }
/** /**
* Set value saved to certain key. * Set value saved to certain key.
* *
* @param key key * @param key key
* @param value the saved value * @param value the saved value
*/ */
@ -272,16 +274,16 @@ public class PropertyManager {
{ {
getProperty(key).setValue(value); getProperty(key).setValue(value);
} }
/** /**
* Set heading comment of the property store. * Set heading comment of the property store.
* *
* @param fileComment comment text (can be multi-line) * @param fileComment comment text (can be multi-line)
*/ */
public void setFileComment(String fileComment) public void setFileComment(String fileComment)
{ {
props.setComment(fileComment); props.setComment(fileComment);
} }
} }

@ -9,70 +9,70 @@ import java.util.Collection;
* Interface for a property store (used by {@link PropertyManager}).<br> * Interface for a property store (used by {@link PropertyManager}).<br>
* Due to this abstraction, different kind of property storage can be used, not * Due to this abstraction, different kind of property storage can be used, not
* only a file. * only a file.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public interface PropertyStore { public interface PropertyStore {
/** /**
* Set a header comment * Set a header comment
* *
* @param comment the comment text (can be multi-line) * @param comment the comment text (can be multi-line)
*/ */
void setComment(String comment); void setComment(String comment);
/** /**
* Load properties from the file / store. If the file does not exist or is * Load properties from the file / store. If the file does not exist or is
* inaccessible, nothing is loaded. * inaccessible, nothing is loaded.
*/ */
void load(); void load();
/** /**
* Save properties to the file / store. * Save properties to the file / store.
* *
* @throws IOException if the file cannot be created or written. * @throws IOException if the file cannot be created or written.
*/ */
void save() throws IOException; void save() throws IOException;
/** /**
* Get a property value * Get a property value
* *
* @param key property key * @param key property key
* @return value retrieved from the file, or null if none found. * @return value retrieved from the file, or null if none found.
*/ */
String getProperty(String key); String getProperty(String key);
/** /**
* Set a property value * Set a property value
* *
* @param key property key * @param key property key
* @param value property value to set * @param value property value to set
* @param comment property comment. Can be null. * @param comment property comment. Can be null.
*/ */
void setProperty(String key, String value, String comment); void setProperty(String key, String value, String comment);
/** /**
* Remove a property from the list. * Remove a property from the list.
* *
* @param key property key to remove * @param key property key to remove
*/ */
void removeProperty(String key); void removeProperty(String key);
/** /**
* Clear the property list * Clear the property list
*/ */
void clear(); void clear();
/** /**
* Get keys collection (can be used for iterating) * Get keys collection (can be used for iterating)
* *
* @return keys collection * @return keys collection
*/ */
public Collection<String> keys(); public Collection<String> keys();

@ -7,21 +7,23 @@ import mightypork.utils.config.propmgr.Property;
/** /**
* Boolean property * Boolean property
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class BooleanProperty extends Property<Boolean> { public class BooleanProperty extends Property<Boolean> {
public BooleanProperty(String key, Boolean defaultValue) { public BooleanProperty(String key, Boolean defaultValue)
{
super(key, defaultValue); super(key, defaultValue);
} }
public BooleanProperty(String key, Boolean defaultValue, String comment) { public BooleanProperty(String key, Boolean defaultValue, String comment)
{
super(key, defaultValue, comment); super(key, defaultValue, comment);
} }
@Override @Override
public void fromString(String string) public void fromString(String string)
{ {

@ -7,21 +7,23 @@ import mightypork.utils.config.propmgr.Property;
/** /**
* Double property * Double property
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class DoubleProperty extends Property<Double> { public class DoubleProperty extends Property<Double> {
public DoubleProperty(String key, Double defaultValue) { public DoubleProperty(String key, Double defaultValue)
{
super(key, defaultValue); super(key, defaultValue);
} }
public DoubleProperty(String key, Double defaultValue, String comment) { public DoubleProperty(String key, Double defaultValue, String comment)
{
super(key, defaultValue, comment); super(key, defaultValue, comment);
} }
@Override @Override
public void fromString(String string) public void fromString(String string)
{ {

@ -7,21 +7,23 @@ import mightypork.utils.config.propmgr.Property;
/** /**
* Integer property * Integer property
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class IntegerProperty extends Property<Integer> { public class IntegerProperty extends Property<Integer> {
public IntegerProperty(String key, Integer defaultValue) { public IntegerProperty(String key, Integer defaultValue)
{
super(key, defaultValue); super(key, defaultValue);
} }
public IntegerProperty(String key, Integer defaultValue, String comment) { public IntegerProperty(String key, Integer defaultValue, String comment)
{
super(key, defaultValue, comment); super(key, defaultValue, comment);
} }
@Override @Override
public void fromString(String string) public void fromString(String string)
{ {

@ -7,21 +7,23 @@ import mightypork.utils.config.propmgr.Property;
/** /**
* String property * String property
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class StringProperty extends Property<String> { public class StringProperty extends Property<String> {
public StringProperty(String key, String defaultValue) { public StringProperty(String key, String defaultValue)
{
super(key, defaultValue); super(key, defaultValue);
} }
public StringProperty(String key, String defaultValue, String comment) { public StringProperty(String key, String defaultValue, String comment)
{
super(key, defaultValue, comment); super(key, defaultValue, comment);
} }
@Override @Override
public void fromString(String string) public void fromString(String string)
{ {

@ -13,50 +13,52 @@ import mightypork.utils.config.propmgr.PropertyStore;
/** /**
* File based implementation utilizing {@link java.util.Properties}, hacked to * File based implementation utilizing {@link java.util.Properties}, hacked to
* support UTF-8. * support UTF-8.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class PropertyFile implements PropertyStore { public class PropertyFile implements PropertyStore {
private String comment; private String comment;
private File file; private final File file;
private SortedProperties props; private final SortedProperties props;
public PropertyFile(File file) { public PropertyFile(File file)
{
this.file = file; this.file = file;
this.comment = null; this.comment = null;
this.props = new SortedProperties(); this.props = new SortedProperties();
} }
public PropertyFile(File file, String comment) { public PropertyFile(File file, String comment)
{
this.file = file; this.file = file;
this.comment = comment; this.comment = comment;
this.props = new SortedProperties(); this.props = new SortedProperties();
} }
@Override @Override
public void setComment(String comment) public void setComment(String comment)
{ {
this.comment = comment; this.comment = comment;
} }
@Override @Override
public void load() public void load()
{ {
if (!file.exists()) return; if (!file.exists()) return;
try (FileInputStream in = new FileInputStream(file)) { try(FileInputStream in = new FileInputStream(file)) {
props.load(in); props.load(in);
} catch (IOException e) { } catch (final IOException e) {
// ignore // ignore
} }
} }
@Override @Override
public void save() throws IOException public void save() throws IOException
{ {
@ -65,55 +67,55 @@ public class PropertyFile implements PropertyStore {
throw new IOException("Cound not create config file."); throw new IOException("Cound not create config file.");
} }
} }
try (FileOutputStream out = new FileOutputStream(file)) { try(FileOutputStream out = new FileOutputStream(file)) {
props.store(out, comment); props.store(out, comment);
} }
} }
@Override @Override
public String getProperty(String key) public String getProperty(String key)
{ {
return props.getProperty(key); return props.getProperty(key);
} }
@Override @Override
public void setProperty(String key, String value, String comment) public void setProperty(String key, String value, String comment)
{ {
props.setProperty(key, value); props.setProperty(key, value);
props.setKeyComment(key, comment); props.setKeyComment(key, comment);
} }
@Override @Override
public void removeProperty(String key) public void removeProperty(String key)
{ {
props.remove(key); props.remove(key);
} }
@Override @Override
public void clear() public void clear()
{ {
props.clear(); props.clear();
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public Collection<String> keys() public Collection<String> keys()
{ {
// Set<String> keys = new HashSet<>(); // Set<String> keys = new HashSet<>();
// for (Object o : props.keySet()) { // for (Object o : props.keySet()) {
// keys.add((String) o); // keys.add((String) o);
// } // }
// return keys; // return keys;
// we know it is strings. // we know it is strings.
return (Collection<String>) (Collection<?>) props.keySet(); return (Collection<String>) (Collection<?>) props.keySet();
} }
} }

@ -1,7 +1,13 @@
package mightypork.utils.config.propmgr.store; package mightypork.utils.config.propmgr.store;
import java.io.*; import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Collections; import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Hashtable; import java.util.Hashtable;
@ -13,19 +19,19 @@ import java.util.Vector;
* Uses UTF-8 encoding and each property can have it's own comment.<br> * Uses UTF-8 encoding and each property can have it's own comment.<br>
* FIXME The quality of this class is dubious. It would probably be a good idea * FIXME The quality of this class is dubious. It would probably be a good idea
* to rewrite it without using {@link java.util.Properties} at all. * to rewrite it without using {@link java.util.Properties} at all.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
class SortedProperties extends java.util.Properties { class SortedProperties extends java.util.Properties {
/** Comments for individual keys */ /** Comments for individual keys */
private final Hashtable<String, String> keyComments = new Hashtable<>(); private final Hashtable<String, String> keyComments = new Hashtable<>();
private static void writeComments(BufferedWriter bw, String comm) throws IOException private static void writeComments(BufferedWriter bw, String comm) throws IOException
{ {
final String comments = comm.replace("\n\n", "\n \n"); final String comments = comm.replace("\n\n", "\n \n");
final int len = comments.length(); final int len = comments.length();
int current = 0; int current = 0;
int last = 0; int last = 0;
@ -38,7 +44,7 @@ class SortedProperties extends java.util.Properties {
if (last != current) { if (last != current) {
bw.write("# " + comments.substring(last, current)); bw.write("# " + comments.substring(last, current));
} }
if (c > '\u00ff') { if (c > '\u00ff') {
uu[2] = hexDigit(c, 12); uu[2] = hexDigit(c, 12);
uu[3] = hexDigit(c, 8); uu[3] = hexDigit(c, 8);
@ -58,13 +64,13 @@ class SortedProperties extends java.util.Properties {
if (last != current) { if (last != current) {
bw.write("# " + comments.substring(last, current)); bw.write("# " + comments.substring(last, current));
} }
bw.newLine(); bw.newLine();
bw.newLine(); bw.newLine();
bw.newLine(); bw.newLine();
} }
@SuppressWarnings({ "unchecked", "rawtypes" }) @SuppressWarnings({ "unchecked", "rawtypes" })
@Override @Override
public synchronized Enumeration keys() public synchronized Enumeration keys()
@ -77,8 +83,8 @@ class SortedProperties extends java.util.Properties {
Collections.sort(keyList); //sort! Collections.sort(keyList); //sort!
return keyList.elements(); return keyList.elements();
} }
private static String saveConvert(String theString, boolean escapeSpace, boolean escapeUnicode) private static String saveConvert(String theString, boolean escapeSpace, boolean escapeUnicode)
{ {
final int len = theString.length(); final int len = theString.length();
@ -87,10 +93,10 @@ class SortedProperties extends java.util.Properties {
bufLen = Integer.MAX_VALUE; bufLen = Integer.MAX_VALUE;
} }
final StringBuffer result = new StringBuffer(bufLen); final StringBuffer result = new StringBuffer(bufLen);
for (int x = 0; x < len; x++) { for (int x = 0; x < len; x++) {
final char ch = theString.charAt(x); final char ch = theString.charAt(x);
// Handle common case first, selecting largest block that // Handle common case first, selecting largest block that
// avoids the specials below // avoids the specials below
if ((ch > 61) && (ch < 127)) { if ((ch > 61) && (ch < 127)) {
@ -102,7 +108,7 @@ class SortedProperties extends java.util.Properties {
result.append(ch); result.append(ch);
continue; continue;
} }
switch (ch) { switch (ch) {
case ' ': case ' ':
if (x == 0 || escapeSpace) { if (x == 0 || escapeSpace) {
@ -110,27 +116,27 @@ class SortedProperties extends java.util.Properties {
} }
result.append(' '); result.append(' ');
break; break;
case '\t': case '\t':
result.append('\\'); result.append('\\');
result.append('t'); result.append('t');
break; break;
case '\n': case '\n':
result.append('\\'); result.append('\\');
result.append('n'); result.append('n');
break; break;
case '\r': case '\r':
result.append('\\'); result.append('\\');
result.append('r'); result.append('r');
break; break;
case '\f': case '\f':
result.append('\\'); result.append('\\');
result.append('f'); result.append('f');
break; break;
case '=': // Fall through case '=': // Fall through
case ':': // Fall through case ':': // Fall through
case '#': // Fall through case '#': // Fall through
@ -138,7 +144,7 @@ class SortedProperties extends java.util.Properties {
result.append('\\'); result.append('\\');
result.append(ch); result.append(ch);
break; break;
default: default:
if (((ch < 0x0020) || (ch > 0x007e)) & escapeUnicode) { if (((ch < 0x0020) || (ch > 0x007e)) & escapeUnicode) {
result.append('\\'); result.append('\\');
@ -152,14 +158,14 @@ class SortedProperties extends java.util.Properties {
} }
} }
} }
return result.toString(); return result.toString();
} }
/** /**
* Set additional comment to a key * Set additional comment to a key
* *
* @param key key for comment * @param key key for comment
* @param comment the comment * @param comment the comment
*/ */
@ -167,75 +173,75 @@ class SortedProperties extends java.util.Properties {
{ {
keyComments.put(key, comment); keyComments.put(key, comment);
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Override @Override
public void store(OutputStream out, String comments) throws IOException public void store(OutputStream out, String comments) throws IOException
{ {
final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out, "UTF-8")); final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
final boolean escUnicode = false; final boolean escUnicode = false;
boolean firstEntry = true; boolean firstEntry = true;
String lastSectionBeginning = ""; String lastSectionBeginning = "";
if (comments != null) { if (comments != null) {
writeComments(bw, comments); writeComments(bw, comments);
} }
synchronized (this) { synchronized (this) {
for (final Enumeration e = keys(); e.hasMoreElements();) { for (final Enumeration e = keys(); e.hasMoreElements();) {
boolean wasNewLine = false; boolean wasNewLine = false;
String key = (String) e.nextElement(); String key = (String) e.nextElement();
String val = (String) get(key); String val = (String) get(key);
key = saveConvert(key, true, escUnicode); key = saveConvert(key, true, escUnicode);
val = saveConvert(val, false, escUnicode); val = saveConvert(val, false, escUnicode);
// separate sections // separate sections
if (!lastSectionBeginning.equals(key.split("[.]")[0])) { if (!lastSectionBeginning.equals(key.split("[.]")[0])) {
if (!firstEntry) { if (!firstEntry) {
bw.newLine(); bw.newLine();
bw.newLine(); bw.newLine();
} }
wasNewLine = true; wasNewLine = true;
lastSectionBeginning = key.split("[.]")[0]; lastSectionBeginning = key.split("[.]")[0];
} }
if (keyComments.containsKey(key)) { if (keyComments.containsKey(key)) {
String cm = keyComments.get(key); String cm = keyComments.get(key);
cm = cm.replace("\r", "\n"); cm = cm.replace("\r", "\n");
cm = cm.replace("\r\n", "\n"); cm = cm.replace("\r\n", "\n");
cm = cm.replace("\n\n", "\n \n"); cm = cm.replace("\n\n", "\n \n");
final String[] cmlines = cm.split("\n"); final String[] cmlines = cm.split("\n");
// newline before comments // newline before comments
if (!wasNewLine && !firstEntry) { if (!wasNewLine && !firstEntry) {
bw.newLine(); bw.newLine();
} }
for (final String cmline : cmlines) { for (final String cmline : cmlines) {
bw.write("# " + cmline); bw.write("# " + cmline);
bw.newLine(); bw.newLine();
} }
} }
bw.write(key + " = " + val); bw.write(key + " = " + val);
bw.newLine(); bw.newLine();
firstEntry = false; firstEntry = false;
} }
} }
bw.flush(); bw.flush();
} }
private static String escapifyStr(String str) private static String escapifyStr(String str)
{ {
final StringBuilder result = new StringBuilder(); final StringBuilder result = new StringBuilder();
final int len = str.length(); final int len = str.length();
for (int x = 0; x < len; x++) { for (int x = 0; x < len; x++) {
final char ch = str.charAt(x); final char ch = str.charAt(x);
@ -243,7 +249,7 @@ class SortedProperties extends java.util.Properties {
result.append(ch); result.append(ch);
continue; continue;
} }
result.append('\\'); result.append('\\');
result.append('u'); result.append('u');
result.append(hexDigit(ch, 12)); result.append(hexDigit(ch, 12));
@ -253,26 +259,26 @@ class SortedProperties extends java.util.Properties {
} }
return result.toString(); return result.toString();
} }
private static char hexDigit(char ch, int offset) private static char hexDigit(char ch, int offset)
{ {
final int val = (ch >> offset) & 0xF; final int val = (ch >> offset) & 0xF;
if (val <= 9) { if (val <= 9) {
return (char) ('0' + val); return (char) ('0' + val);
} }
return (char) ('A' + val - 10); return (char) ('A' + val - 10);
} }
@Override @Override
public synchronized void load(InputStream is) throws IOException public synchronized void load(InputStream is) throws IOException
{ {
load(is, "utf-8"); load(is, "utf-8");
} }
public void load(InputStream is, String encoding) throws IOException public void load(InputStream is, String encoding) throws IOException
{ {
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
@ -282,18 +288,18 @@ class SortedProperties extends java.util.Properties {
if (temp < 0) { if (temp < 0) {
break; break;
} }
final char c = (char) temp; final char c = (char) temp;
sb.append(c); sb.append(c);
} }
// discard comments // discard comments
final String read = sb.toString().replaceAll("(#|;|//|--)[^\n]*\n", "\n"); final String read = sb.toString().replaceAll("(#|;|//|--)[^\n]*\n", "\n");
final String inputString = escapifyStr(read); final String inputString = escapifyStr(read);
final byte[] bs = inputString.getBytes("ISO-8859-1"); final byte[] bs = inputString.getBytes("ISO-8859-1");
final ByteArrayInputStream bais = new ByteArrayInputStream(bs); final ByteArrayInputStream bais = new ByteArrayInputStream(bs);
super.load(bais); super.load(bais);
} }
} }

@ -26,73 +26,73 @@ import mightypork.utils.eventbus.events.flags.SingleReceiverEvent;
* Default sending mode (if not changed by annotations) is <i>queued</i> with * Default sending mode (if not changed by annotations) is <i>queued</i> with
* zero delay. * zero delay.
* </p> * </p>
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
* @param <HANDLER> handler type * @param <HANDLER> handler type
*/ */
public abstract class BusEvent<HANDLER> { public abstract class BusEvent<HANDLER> {
private boolean consumed; private boolean consumed;
private boolean served; private boolean served;
/** /**
* Ask handler to handle this message. * Ask handler to handle this message.
* *
* @param handler handler instance * @param handler handler instance
*/ */
protected abstract void handleBy(HANDLER handler); protected abstract void handleBy(HANDLER handler);
/** /**
* Consume the event, so no other clients will receive it. * Consume the event, so no other clients will receive it.
* *
* @throws UnsupportedOperationException if the {@link NonConsumableEvent} * @throws UnsupportedOperationException if the {@link NonConsumableEvent}
* annotation is present. * annotation is present.
*/ */
public final void consume() public final void consume()
{ {
if (consumed) throw new IllegalStateException("Already consumed."); if (consumed) throw new IllegalStateException("Already consumed.");
if (getClass().isAnnotationPresent(NonConsumableEvent.class)) { if (getClass().isAnnotationPresent(NonConsumableEvent.class)) {
throw new UnsupportedOperationException("Not consumable."); throw new UnsupportedOperationException("Not consumable.");
} }
consumed = true; consumed = true;
} }
/** /**
* Deliver to a handler using the handleBy method. * Deliver to a handler using the handleBy method.
* *
* @param handler handler instance * @param handler handler instance
*/ */
final void deliverTo(HANDLER handler) final void deliverTo(HANDLER handler)
{ {
handleBy(handler); handleBy(handler);
if (!served) { if (!served) {
if (getClass().isAnnotationPresent(SingleReceiverEvent.class)) { if (getClass().isAnnotationPresent(SingleReceiverEvent.class)) {
consumed = true; consumed = true;
} }
served = true; served = true;
} }
} }
/** /**
* Check if the event is consumed. When an event is consumed, no other * Check if the event is consumed. When an event is consumed, no other
* clients will receive it. * clients will receive it.
* *
* @return true if consumed * @return true if consumed
*/ */
public final boolean isConsumed() public final boolean isConsumed()
{ {
return consumed; return consumed;
} }
/** /**
* @return true if the event was served to at least 1 client * @return true if the event was served to at least 1 client
*/ */
@ -100,8 +100,8 @@ public abstract class BusEvent<HANDLER> {
{ {
return served; return served;
} }
/** /**
* Clear "served" and "consumed" flags before dispatching. * Clear "served" and "consumed" flags before dispatching.
*/ */
@ -110,11 +110,11 @@ public abstract class BusEvent<HANDLER> {
served = false; served = false;
consumed = false; consumed = false;
} }
/** /**
* Called after all clients have received the event. * Called after all clients have received the event.
* *
* @param bus event bus instance * @param bus event bus instance
*/ */
public void onDispatchComplete(EventBus bus) public void onDispatchComplete(EventBus bus)

@ -22,75 +22,77 @@ import mightypork.utils.logging.Log;
/** /**
* An event bus, accommodating multiple EventChannels.<br> * An event bus, accommodating multiple EventChannels.<br>
* Channel will be created when an event of type is first encountered. * Channel will be created when an event of type is first encountered.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
final public class EventBus implements Destroyable { final public class EventBus implements Destroyable {
/** /**
* Queued event holder * Queued event holder
*/ */
private class DelayQueueEntry implements Delayed { private class DelayQueueEntry implements Delayed {
private final long due; private final long due;
private final BusEvent<?> evt; private final BusEvent<?> evt;
public DelayQueueEntry(double seconds, BusEvent<?> event) { public DelayQueueEntry(double seconds, BusEvent<?> event)
{
super(); super();
this.due = System.currentTimeMillis() + (long) (seconds * 1000); this.due = System.currentTimeMillis() + (long) (seconds * 1000);
this.evt = event; this.evt = event;
} }
@Override @Override
public int compareTo(Delayed o) public int compareTo(Delayed o)
{ {
return Long.valueOf(getDelay(TimeUnit.MILLISECONDS)).compareTo(o.getDelay(TimeUnit.MILLISECONDS)); return Long.valueOf(getDelay(TimeUnit.MILLISECONDS)).compareTo(o.getDelay(TimeUnit.MILLISECONDS));
} }
@Override @Override
public long getDelay(TimeUnit unit) public long getDelay(TimeUnit unit)
{ {
return unit.convert(due - System.currentTimeMillis(), TimeUnit.MILLISECONDS); return unit.convert(due - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
} }
public BusEvent<?> getEvent() public BusEvent<?> getEvent()
{ {
return evt; return evt;
} }
} }
/** /**
* Thread handling queued events * Thread handling queued events
*/ */
private class QueuePollingThread extends Thread { private class QueuePollingThread extends Thread {
public volatile boolean stopped = false; public volatile boolean stopped = false;
public QueuePollingThread() { public QueuePollingThread()
{
super("Queue Polling Thread"); super("Queue Polling Thread");
} }
@Override @Override
public void run() public void run()
{ {
DelayQueueEntry evt; DelayQueueEntry evt;
while (!stopped) { while (!stopped) {
evt = null; evt = null;
try { try {
evt = sendQueue.take(); evt = sendQueue.take();
} catch (final InterruptedException ignored) { } catch (final InterruptedException ignored) {
// //
} }
if (evt != null) { if (evt != null) {
try { try {
dispatch(evt.getEvent()); dispatch(evt.getEvent());
@ -100,46 +102,47 @@ final public class EventBus implements Destroyable {
} }
} }
} }
} }
static final String logMark = "(bus) "; static final String logMark = "(bus) ";
private static Class<?> getEventListenerClass(BusEvent<?> event) private static Class<?> getEventListenerClass(BusEvent<?> event)
{ {
return Reflect.getGenericParameters(event.getClass())[0]; return Reflect.getGenericParameters(event.getClass())[0];
} }
/** Log detailed messages (debug) */ /** Log detailed messages (debug) */
public boolean detailedLogging = false; public boolean detailedLogging = false;
/** Queue polling thread */ /** Queue polling thread */
private final QueuePollingThread busThread; private final QueuePollingThread busThread;
/** Registered clients */ /** Registered clients */
private final Set<Object> clients = Collections.newSetFromMap(new ConcurrentHashMap<Object, Boolean>()); private final Set<Object> clients = Collections.newSetFromMap(new ConcurrentHashMap<Object, Boolean>());
/** Whether the bus was destroyed */ /** Whether the bus was destroyed */
private boolean dead = false; private boolean dead = false;
/** Message channels */ /** Message channels */
private final Set<EventChannel<?, ?>> channels = Collections.newSetFromMap(new ConcurrentHashMap<EventChannel<?, ?>, Boolean>()); private final Set<EventChannel<?, ?>> channels = Collections.newSetFromMap(new ConcurrentHashMap<EventChannel<?, ?>, Boolean>());
/** Messages queued for delivery */ /** Messages queued for delivery */
private final DelayQueue<DelayQueueEntry> sendQueue = new DelayQueue<>(); private final DelayQueue<DelayQueueEntry> sendQueue = new DelayQueue<>();
/** /**
* Make a new bus and start it's queue thread. * Make a new bus and start it's queue thread.
*/ */
public EventBus() { public EventBus()
{
busThread = new QueuePollingThread(); busThread = new QueuePollingThread();
busThread.setDaemon(true); busThread.setDaemon(true);
busThread.start(); busThread.start();
} }
/** /**
* Halt bus thread and reject any future events. * Halt bus thread and reject any future events.
*/ */
@ -147,129 +150,129 @@ final public class EventBus implements Destroyable {
public void destroy() public void destroy()
{ {
assertLive(); assertLive();
busThread.stopped = true; busThread.stopped = true;
dead = true; dead = true;
} }
/** /**
* Send based on annotation * Send based on annotation
* *
* @param event event * @param event event
*/ */
public void send(BusEvent<?> event) public void send(BusEvent<?> event)
{ {
assertLive(); assertLive();
final DelayedEvent adelay = Reflect.getAnnotation(event, DelayedEvent.class); final DelayedEvent adelay = Reflect.getAnnotation(event, DelayedEvent.class);
if (adelay != null) { if (adelay != null) {
sendDelayed(event, adelay.delay()); sendDelayed(event, adelay.delay());
return; return;
} }
if (Reflect.hasAnnotation(event, DirectEvent.class)) { if (Reflect.hasAnnotation(event, DirectEvent.class)) {
sendDirect(event); sendDirect(event);
return; return;
} }
sendQueued(event); sendQueued(event);
} }
/** /**
* Add event to a queue * Add event to a queue
* *
* @param event event * @param event event
*/ */
public void sendQueued(BusEvent<?> event) public void sendQueued(BusEvent<?> event)
{ {
assertLive(); assertLive();
sendDelayed(event, 0); sendDelayed(event, 0);
} }
/** /**
* Add event to a queue, scheduled for given time. * Add event to a queue, scheduled for given time.
* *
* @param event event * @param event event
* @param delay delay before event is dispatched * @param delay delay before event is dispatched
*/ */
public void sendDelayed(BusEvent<?> event, double delay) public void sendDelayed(BusEvent<?> event, double delay)
{ {
assertLive(); assertLive();
final DelayQueueEntry dm = new DelayQueueEntry(delay, event); final DelayQueueEntry dm = new DelayQueueEntry(delay, event);
if (shallLog(event)) { if (shallLog(event)) {
Log.f3(logMark + "Qu [" + Support.str(event) + "]" + (delay == 0 ? "" : (", delay: " + delay + "s"))); Log.f3(logMark + "Qu [" + Support.str(event) + "]" + (delay == 0 ? "" : (", delay: " + delay + "s")));
} }
sendQueue.add(dm); sendQueue.add(dm);
} }
/** /**
* Send immediately.<br> * Send immediately.<br>
* Should be used for real-time events that require immediate response, such * Should be used for real-time events that require immediate response, such
* as timing events. * as timing events.
* *
* @param event event * @param event event
*/ */
public void sendDirect(BusEvent<?> event) public void sendDirect(BusEvent<?> event)
{ {
assertLive(); assertLive();
if (shallLog(event)) Log.f3(logMark + "Di [" + Support.str(event) + "]"); if (shallLog(event)) Log.f3(logMark + "Di [" + Support.str(event) + "]");
dispatch(event); dispatch(event);
} }
public void sendDirectToChildren(DelegatingClient delegatingClient, BusEvent<?> event) public void sendDirectToChildren(DelegatingClient delegatingClient, BusEvent<?> event)
{ {
assertLive(); assertLive();
if (shallLog(event)) Log.f3(logMark + "Di->sub [" + Support.str(event) + "]"); if (shallLog(event)) Log.f3(logMark + "Di->sub [" + Support.str(event) + "]");
doDispatch(delegatingClient.getChildClients(), event); doDispatch(delegatingClient.getChildClients(), event);
} }
/** /**
* Connect a client to the bus. The client will be connected to all current * Connect a client to the bus. The client will be connected to all current
* and future channels, until removed from the bus. * and future channels, until removed from the bus.
* *
* @param client the client * @param client the client
*/ */
public void subscribe(Object client) public void subscribe(Object client)
{ {
assertLive(); assertLive();
if (client == null) return; if (client == null) return;
clients.add(client); clients.add(client);
if (detailedLogging) Log.f3(logMark + "Client joined: " + Support.str(client)); if (detailedLogging) Log.f3(logMark + "Client joined: " + Support.str(client));
} }
/** /**
* Disconnect a client from the bus. * Disconnect a client from the bus.
* *
* @param client the client * @param client the client
*/ */
public void unsubscribe(Object client) public void unsubscribe(Object client)
{ {
assertLive(); assertLive();
clients.remove(client); clients.remove(client);
if (detailedLogging) Log.f3(logMark + "Client left: " + Support.str(client)); if (detailedLogging) Log.f3(logMark + "Client left: " + Support.str(client));
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private boolean addChannelForEvent(BusEvent<?> event) private boolean addChannelForEvent(BusEvent<?> event)
{ {
@ -277,99 +280,99 @@ final public class EventBus implements Destroyable {
if (detailedLogging) { if (detailedLogging) {
Log.f3(logMark + "Setting up channel for new event type: " + Support.str(event.getClass())); Log.f3(logMark + "Setting up channel for new event type: " + Support.str(event.getClass()));
} }
final Class<?> listener = getEventListenerClass(event); final Class<?> listener = getEventListenerClass(event);
final EventChannel<?, ?> ch = EventChannel.create(event.getClass(), listener); final EventChannel<?, ?> ch = EventChannel.create(event.getClass(), listener);
if (ch.canBroadcast(event)) { if (ch.canBroadcast(event)) {
channels.add(ch); channels.add(ch);
//channels.flush(); //channels.flush();
if (detailedLogging) { if (detailedLogging) {
Log.f3(logMark + "Created new channel: " + Support.str(event.getClass()) + " -> " + Support.str(listener)); Log.f3(logMark + "Created new channel: " + Support.str(event.getClass()) + " -> " + Support.str(listener));
} }
return true; return true;
} else { } else {
Log.w(logMark + "Could not create channel for event " + Support.str(event.getClass())); Log.w(logMark + "Could not create channel for event " + Support.str(event.getClass()));
} }
} catch (final Throwable t) { } catch (final Throwable t) {
Log.w(logMark + "Error while trying to add channel for event.", t); Log.w(logMark + "Error while trying to add channel for event.", t);
} }
return false; return false;
} }
/** /**
* Make sure the bus is not destroyed. * Make sure the bus is not destroyed.
* *
* @throws IllegalStateException if the bus is dead. * @throws IllegalStateException if the bus is dead.
*/ */
private void assertLive() throws IllegalStateException private void assertLive() throws IllegalStateException
{ {
if (dead) throw new IllegalStateException("EventBus is dead."); if (dead) throw new IllegalStateException("EventBus is dead.");
} }
/** /**
* Send immediately.<br> * Send immediately.<br>
* Should be used for real-time events that require immediate response, such * Should be used for real-time events that require immediate response, such
* as timing events. * as timing events.
* *
* @param event event * @param event event
*/ */
private synchronized void dispatch(BusEvent<?> event) private synchronized void dispatch(BusEvent<?> event)
{ {
assertLive(); assertLive();
doDispatch(clients, event); doDispatch(clients, event);
event.onDispatchComplete(this); event.onDispatchComplete(this);
} }
/** /**
* Send to a set of clients * Send to a set of clients
* *
* @param clients clients * @param clients clients
* @param event event * @param event event
*/ */
private synchronized void doDispatch(Collection<?> clients, BusEvent<?> event) private synchronized void doDispatch(Collection<?> clients, BusEvent<?> event)
{ {
boolean accepted = false; boolean accepted = false;
event.clearFlags(); event.clearFlags();
for (int i = 0; i < 2; i++) { // two tries. for (int i = 0; i < 2; i++) { // two tries.
for (final EventChannel<?, ?> b : channels) { for (final EventChannel<?, ?> b : channels) {
if (b.canBroadcast(event)) { if (b.canBroadcast(event)) {
accepted = true; accepted = true;
b.broadcast(event, clients); b.broadcast(event, clients);
} }
if (event.isConsumed()) break; if (event.isConsumed()) break;
} }
if (!accepted) if (addChannelForEvent(event)) continue; if (!accepted) if (addChannelForEvent(event)) continue;
break; break;
} }
if (!accepted) Log.e(logMark + "Not accepted by any channel: " + Support.str(event)); if (!accepted) Log.e(logMark + "Not accepted by any channel: " + Support.str(event));
if (!event.wasServed() && shallLog(event)) Log.w(logMark + "Not delivered: " + Support.str(event)); if (!event.wasServed() && shallLog(event)) Log.w(logMark + "Not delivered: " + Support.str(event));
} }
private boolean shallLog(BusEvent<?> event) private boolean shallLog(BusEvent<?> event)
{ {
if (!detailedLogging) return false; if (!detailedLogging) return false;
if (Reflect.hasAnnotation(event, NotLoggedEvent.class)) return false; if (Reflect.hasAnnotation(event, NotLoggedEvent.class)) return false;
return true; return true;
} }
} }

@ -14,52 +14,53 @@ import mightypork.utils.logging.Log;
/** /**
* Event delivery channel, module of {@link EventBus} * Event delivery channel, module of {@link EventBus}
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
* @param <EVENT> event type * @param <EVENT> event type
* @param <CLIENT> client (subscriber) type * @param <CLIENT> client (subscriber) type
*/ */
class EventChannel<EVENT extends BusEvent<CLIENT>, CLIENT> { class EventChannel<EVENT extends BusEvent<CLIENT>, CLIENT> {
private final Class<CLIENT> clientClass; private final Class<CLIENT> clientClass;
private final Class<EVENT> eventClass; private final Class<EVENT> eventClass;
/** /**
* Create a channel * Create a channel
* *
* @param eventClass event class * @param eventClass event class
* @param clientClass client class * @param clientClass client class
*/ */
public EventChannel(Class<EVENT> eventClass, Class<CLIENT> clientClass) { public EventChannel(Class<EVENT> eventClass, Class<CLIENT> clientClass)
{
if (eventClass == null || clientClass == null) { if (eventClass == null || clientClass == null) {
throw new NullPointerException("Null Event or Client class."); throw new NullPointerException("Null Event or Client class.");
} }
this.clientClass = clientClass; this.clientClass = clientClass;
this.eventClass = eventClass; this.eventClass = eventClass;
} }
/** /**
* Try to broadcast a event.<br> * Try to broadcast a event.<br>
* If event is of wrong type, <code>false</code> is returned. * If event is of wrong type, <code>false</code> is returned.
* *
* @param event a event to be sent * @param event a event to be sent
* @param clients collection of clients * @param clients collection of clients
*/ */
public void broadcast(BusEvent<?> event, Collection<?> clients) public void broadcast(BusEvent<?> event, Collection<?> clients)
{ {
if (!canBroadcast(event)) return; if (!canBroadcast(event)) return;
doBroadcast(eventClass.cast(event), clients, new HashSet<>()); doBroadcast(eventClass.cast(event), clients, new HashSet<>());
} }
/** /**
* Send the event * Send the event
* *
* @param event sent event * @param event sent event
* @param clients subscribing clients * @param clients subscribing clients
* @param processed clients already processed * @param processed clients already processed
@ -67,49 +68,49 @@ class EventChannel<EVENT extends BusEvent<CLIENT>, CLIENT> {
private void doBroadcast(final EVENT event, final Collection<?> clients, final Collection<Object> processed) private void doBroadcast(final EVENT event, final Collection<?> clients, final Collection<Object> processed)
{ {
for (final Object client : clients) { for (final Object client : clients) {
// exclude obvious non-clients // exclude obvious non-clients
if (!isClientValid(client)) { if (!isClientValid(client)) {
continue; continue;
} }
// avoid executing more times // avoid executing more times
if (processed.contains(client)) { if (processed.contains(client)) {
Log.w(EventBus.logMark + "Client already served: " + Support.str(client)); Log.w(EventBus.logMark + "Client already served: " + Support.str(client));
continue; continue;
} }
processed.add(client); processed.add(client);
final boolean must_deliver = Reflect.hasAnnotation(event, NonRejectableEvent.class); final boolean must_deliver = Reflect.hasAnnotation(event, NonRejectableEvent.class);
// opt-out // opt-out
if (client instanceof ToggleableClient) { if (client instanceof ToggleableClient) {
if (!must_deliver && !((ToggleableClient) client).isListening()) continue; if (!must_deliver && !((ToggleableClient) client).isListening()) continue;
} }
sendTo(client, event); sendTo(client, event);
if (event.isConsumed()) return; if (event.isConsumed()) return;
// pass on to delegated clients // pass on to delegated clients
if (client instanceof DelegatingClient) { if (client instanceof DelegatingClient) {
if (must_deliver || ((DelegatingClient) client).doesDelegate()) { if (must_deliver || ((DelegatingClient) client).doesDelegate()) {
final Collection<?> children = ((DelegatingClient) client).getChildClients(); final Collection<?> children = ((DelegatingClient) client).getChildClients();
if (children != null && children.size() > 0) { if (children != null && children.size() > 0) {
doBroadcast(event, children, processed); doBroadcast(event, children, processed);
} }
} }
} }
} }
} }
/** /**
* Send an event to a client. * Send an event to a client.
* *
* @param client target client * @param client target client
* @param event event to send * @param event event to send
*/ */
@ -120,11 +121,11 @@ class EventChannel<EVENT extends BusEvent<CLIENT>, CLIENT> {
((BusEvent<CLIENT>) event).deliverTo((CLIENT) client); ((BusEvent<CLIENT>) event).deliverTo((CLIENT) client);
} }
} }
/** /**
* Check if the given event can be broadcasted by this channel * Check if the given event can be broadcasted by this channel
* *
* @param event event object * @param event event object
* @return can be broadcasted * @return can be broadcasted
*/ */
@ -132,11 +133,11 @@ class EventChannel<EVENT extends BusEvent<CLIENT>, CLIENT> {
{ {
return event != null && eventClass.isInstance(event); return event != null && eventClass.isInstance(event);
} }
/** /**
* Create an instance for given types * Create an instance for given types
* *
* @param eventClass event class * @param eventClass event class
* @param clientClass client class * @param clientClass client class
* @return the broadcaster * @return the broadcaster
@ -145,11 +146,11 @@ class EventChannel<EVENT extends BusEvent<CLIENT>, CLIENT> {
{ {
return new EventChannel<>(eventClass, clientClass); return new EventChannel<>(eventClass, clientClass);
} }
/** /**
* Check if client is of channel type * Check if client is of channel type
* *
* @param client client * @param client client
* @return is of type * @return is of type
*/ */
@ -157,11 +158,11 @@ class EventChannel<EVENT extends BusEvent<CLIENT>, CLIENT> {
{ {
return clientClass.isInstance(client); return clientClass.isInstance(client);
} }
/** /**
* Check if the channel is compatible with given * Check if the channel is compatible with given
* *
* @param client client * @param client client
* @return is supported * @return is supported
*/ */
@ -169,8 +170,8 @@ class EventChannel<EVENT extends BusEvent<CLIENT>, CLIENT> {
{ {
return isClientOfChannelType(client) || (client instanceof DelegatingClient); return isClientOfChannelType(client) || (client instanceof DelegatingClient);
} }
@Override @Override
public int hashCode() public int hashCode()
{ {
@ -180,8 +181,8 @@ class EventChannel<EVENT extends BusEvent<CLIENT>, CLIENT> {
result = prime * result + ((eventClass == null) ? 0 : eventClass.hashCode()); result = prime * result + ((eventClass == null) ? 0 : eventClass.hashCode());
return result; return result;
} }
@Override @Override
public boolean equals(Object obj) public boolean equals(Object obj)
{ {
@ -197,8 +198,8 @@ class EventChannel<EVENT extends BusEvent<CLIENT>, CLIENT> {
} else if (!eventClass.equals(other.eventClass)) return false; } else if (!eventClass.equals(other.eventClass)) return false;
return true; return true;
} }
@Override @Override
public String toString() public String toString()
{ {

@ -11,40 +11,40 @@ import mightypork.utils.eventbus.EventBus;
/** /**
* Client that can be attached to the {@link EventBus}, or added as a child * Client that can be attached to the {@link EventBus}, or added as a child
* client to another {@link DelegatingClient} * client to another {@link DelegatingClient}
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public abstract class BusNode implements ClientHub { public abstract class BusNode implements ClientHub {
private final Set<Object> clients = new LinkedHashSet<>(); private final Set<Object> clients = new LinkedHashSet<>();
private boolean listening = true; private boolean listening = true;
private boolean delegating = true; private boolean delegating = true;
@Override @Override
public Collection<Object> getChildClients() public Collection<Object> getChildClients()
{ {
return clients; return clients;
} }
@Override @Override
public boolean doesDelegate() public boolean doesDelegate()
{ {
return delegating; return delegating;
} }
@Override @Override
public boolean isListening() public boolean isListening()
{ {
return listening; return listening;
} }
/** /**
* Add a child subscriber to the {@link EventBus}.<br> * Add a child subscriber to the {@link EventBus}.<br>
* *
* @param client * @param client
*/ */
@Override @Override
@ -52,11 +52,11 @@ public abstract class BusNode implements ClientHub {
{ {
clients.add(client); clients.add(client);
} }
/** /**
* Remove a child subscriber * Remove a child subscriber
* *
* @param client subscriber to remove * @param client subscriber to remove
*/ */
@Override @Override
@ -66,22 +66,22 @@ public abstract class BusNode implements ClientHub {
clients.remove(client); clients.remove(client);
} }
} }
/** /**
* Set whether events should be received. * Set whether events should be received.
* *
* @param listening receive events * @param listening receive events
*/ */
public void setListening(boolean listening) public void setListening(boolean listening)
{ {
this.listening = listening; this.listening = listening;
} }
/** /**
* Set whether events should be passed on to child nodes * Set whether events should be passed on to child nodes
* *
* @param delegating * @param delegating
*/ */
public void setDelegating(boolean delegating) public void setDelegating(boolean delegating)

@ -8,34 +8,34 @@ import mightypork.utils.eventbus.EventBus;
/** /**
* Common methods for client hubs (ie delegating vlient implementations) * Common methods for client hubs (ie delegating vlient implementations)
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public interface ClientHub extends DelegatingClient, ToggleableClient { public interface ClientHub extends DelegatingClient, ToggleableClient {
@Override @Override
public boolean doesDelegate(); public boolean doesDelegate();
@Override @Override
public Collection<Object> getChildClients(); public Collection<Object> getChildClients();
@Override @Override
public boolean isListening(); public boolean isListening();
/** /**
* Add a child subscriber to the {@link EventBus}.<br> * Add a child subscriber to the {@link EventBus}.<br>
* *
* @param client * @param client
*/ */
public void addChildClient(Object client); public void addChildClient(Object client);
/** /**
* Remove a child subscriber * Remove a child subscriber
* *
* @param client subscriber to remove * @param client subscriber to remove
*/ */
void removeChildClient(Object client); void removeChildClient(Object client);

@ -7,12 +7,13 @@ import java.util.ArrayList;
/** /**
* Array-list with varargs constructor, intended to wrap fre clients for * Array-list with varargs constructor, intended to wrap fre clients for
* delegating client. * delegating client.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class ClientList extends ArrayList<Object> { public class ClientList extends ArrayList<Object> {
public ClientList(Object... clients) { public ClientList(Object... clients)
{
for (final Object c : clients) { for (final Object c : clients) {
super.add(c); super.add(c);
} }

@ -8,20 +8,20 @@ import java.util.Collection;
* Client containing child clients. According to the contract, if the collection * Client containing child clients. According to the contract, if the collection
* of clients is ordered, the clients will be served in that order. In any case, * of clients is ordered, the clients will be served in that order. In any case,
* the {@link DelegatingClient} itself will be served beforehand. * the {@link DelegatingClient} itself will be served beforehand.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public interface DelegatingClient { public interface DelegatingClient {
/** /**
* @return collection of child clients. Can not be null. * @return collection of child clients. Can not be null.
*/ */
public Collection<?> getChildClients(); public Collection<?> getChildClients();
/** /**
* @return true if delegating is active * @return true if delegating is active
*/ */
public boolean doesDelegate(); public boolean doesDelegate();
} }

@ -8,59 +8,61 @@ import mightypork.utils.interfaces.Enableable;
/** /**
* List of clients, that can be used as a delegating client. * List of clients, that can be used as a delegating client.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class DelegatingList extends ClientList implements DelegatingClient, Enableable, ToggleableClient { public class DelegatingList extends ClientList implements DelegatingClient, Enableable, ToggleableClient {
private boolean enabled = true; private boolean enabled = true;
/** /**
* Delegating list with initial clients * Delegating list with initial clients
* *
* @param clients initial list members (clients) * @param clients initial list members (clients)
*/ */
public DelegatingList(Object... clients) { public DelegatingList(Object... clients)
{
super(clients); super(clients);
} }
/** /**
* Empty delegating list. * Empty delegating list.
*/ */
public DelegatingList() { public DelegatingList()
{
} }
@Override @Override
public Collection<?> getChildClients() public Collection<?> getChildClients()
{ {
return this; return this;
} }
@Override @Override
public boolean doesDelegate() public boolean doesDelegate()
{ {
return isEnabled(); return isEnabled();
} }
@Override @Override
public boolean isListening() public boolean isListening()
{ {
return isEnabled(); return isEnabled();
} }
@Override @Override
public void setEnabled(boolean yes) public void setEnabled(boolean yes)
{ {
enabled = yes; enabled = yes;
} }
@Override @Override
public boolean isEnabled() public boolean isEnabled()
{ {

@ -3,14 +3,14 @@ package mightypork.utils.eventbus.clients;
/** /**
* Client that can toggle receiving messages. * Client that can toggle receiving messages.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public interface ToggleableClient { public interface ToggleableClient {
/** /**
* @return true if the client wants to receive messages * @return true if the client wants to receive messages
*/ */
public boolean isListening(); public boolean isListening();
} }

@ -9,17 +9,17 @@ import mightypork.utils.interfaces.Destroyable;
/** /**
* Invoke destroy() method of all subscribers. Used to deinit a system. * Invoke destroy() method of all subscribers. Used to deinit a system.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
@DirectEvent @DirectEvent
@NonConsumableEvent @NonConsumableEvent
public class DestroyEvent extends BusEvent<Destroyable> { public class DestroyEvent extends BusEvent<Destroyable> {
@Override @Override
public void handleBy(Destroyable handler) public void handleBy(Destroyable handler)
{ {
handler.destroy(); handler.destroy();
} }
} }

@ -10,25 +10,26 @@ import mightypork.utils.interfaces.Updateable;
/** /**
* Delta timing update event. Not logged. * Delta timing update event. Not logged.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
@NotLoggedEvent @NotLoggedEvent
@DirectEvent @DirectEvent
@NonConsumableEvent @NonConsumableEvent
public class UpdateEvent extends BusEvent<Updateable> { public class UpdateEvent extends BusEvent<Updateable> {
private final double deltaTime; private final double deltaTime;
/** /**
* @param deltaTime time since last update (sec) * @param deltaTime time since last update (sec)
*/ */
public UpdateEvent(double deltaTime) { public UpdateEvent(double deltaTime)
{
this.deltaTime = deltaTime; this.deltaTime = deltaTime;
} }
@Override @Override
public void handleBy(Updateable handler) public void handleBy(Updateable handler)
{ {

@ -1,12 +1,17 @@
package mightypork.utils.eventbus.events.flags; package mightypork.utils.eventbus.events.flags;
import java.lang.annotation.*; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** /**
* Event that should be queued with given delay (default: 0); * Event that should be queued with given delay (default: 0);
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@ -14,7 +19,7 @@ import java.lang.annotation.*;
@Inherited @Inherited
@Documented @Documented
public @interface DelayedEvent { public @interface DelayedEvent {
/** /**
* @return event dispatch delay [seconds] * @return event dispatch delay [seconds]
*/ */

@ -1,12 +1,17 @@
package mightypork.utils.eventbus.events.flags; package mightypork.utils.eventbus.events.flags;
import java.lang.annotation.*; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** /**
* Event that should not be queued. * Event that should not be queued.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)

@ -10,12 +10,12 @@ import java.lang.annotation.Target;
/** /**
* Event that cannot be consumed * Event that cannot be consumed
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Documented @Documented
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
public @interface NonConsumableEvent { public @interface NonConsumableEvent {
} }

@ -10,12 +10,12 @@ import java.lang.annotation.Target;
/** /**
* Event that is forcibly delivered to all clients (bypass Toggleable etc) * Event that is forcibly delivered to all clients (bypass Toggleable etc)
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Documented @Documented
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
public @interface NonRejectableEvent { public @interface NonRejectableEvent {
} }

@ -1,13 +1,18 @@
package mightypork.utils.eventbus.events.flags; package mightypork.utils.eventbus.events.flags;
import java.lang.annotation.*; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** /**
* Event that's not worth logging, unless there was an error with it.<br> * Event that's not worth logging, unless there was an error with it.<br>
* Useful for common events that would otherwise clutter the log. * Useful for common events that would otherwise clutter the log.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)

@ -1,12 +1,17 @@
package mightypork.utils.eventbus.events.flags; package mightypork.utils.eventbus.events.flags;
import java.lang.annotation.*; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** /**
* Handled only by the first client, then discarded. * Handled only by the first client, then discarded.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)

@ -6,28 +6,32 @@ import java.io.IOException;
/** /**
* Thrown when data could not be read successfully. * Thrown when data could not be read successfully.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class CorruptDataException extends IOException { public class CorruptDataException extends IOException {
public CorruptDataException() { public CorruptDataException()
{
super(); super();
} }
public CorruptDataException(String message, Throwable cause) { public CorruptDataException(String message, Throwable cause)
{
super(message, cause); super(message, cause);
} }
public CorruptDataException(String message) { public CorruptDataException(String message)
{
super(message); super(message);
} }
public CorruptDataException(Throwable cause) { public CorruptDataException(Throwable cause)
{
super(cause); super(cause);
} }
} }

@ -4,27 +4,31 @@ package mightypork.utils.exceptions;
/** /**
* Thrown when a invalid value is given to a method, or found in a data object / * Thrown when a invalid value is given to a method, or found in a data object /
* file etc * file etc
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class IllegalValueException extends RuntimeException { public class IllegalValueException extends RuntimeException {
public IllegalValueException() { public IllegalValueException()
{
} }
public IllegalValueException(String message) { public IllegalValueException(String message)
{
super(message); super(message);
} }
public IllegalValueException(Throwable cause) { public IllegalValueException(Throwable cause)
{
super(cause); super(cause);
} }
public IllegalValueException(String message, Throwable cause) { public IllegalValueException(String message, Throwable cause)
{
super(message, cause); super(message, cause);
} }
} }

@ -3,28 +3,32 @@ package mightypork.utils.exceptions;
/** /**
* Thrown by a map-like class when the key specified is already taken. * Thrown by a map-like class when the key specified is already taken.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class KeyAlreadyExistsException extends RuntimeException { public class KeyAlreadyExistsException extends RuntimeException {
public KeyAlreadyExistsException() { public KeyAlreadyExistsException()
{
super(); super();
} }
public KeyAlreadyExistsException(String message, Throwable cause) { public KeyAlreadyExistsException(String message, Throwable cause)
{
super(message, cause); super(message, cause);
} }
public KeyAlreadyExistsException(String message) { public KeyAlreadyExistsException(String message)
{
super(message); super(message);
} }
public KeyAlreadyExistsException(Throwable cause) { public KeyAlreadyExistsException(Throwable cause)
{
super(cause); super(cause);
} }
} }

@ -7,39 +7,40 @@ import java.io.FileFilter;
/** /**
* File filter for certain suffixes * File filter for certain suffixes
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class FileSuffixFilter implements FileFilter { public class FileSuffixFilter implements FileFilter {
/** Array of allowed suffixes */ /** Array of allowed suffixes */
private String[] suffixes = null; private String[] suffixes = null;
/** /**
* Suffix filter * Suffix filter
* *
* @param suffixes var-args allowed suffixes, case insensitive * @param suffixes var-args allowed suffixes, case insensitive
*/ */
public FileSuffixFilter(String... suffixes) { public FileSuffixFilter(String... suffixes)
{
this.suffixes = suffixes; this.suffixes = suffixes;
} }
@Override @Override
public boolean accept(File pathname) public boolean accept(File pathname)
{ {
if (!pathname.isFile()) return false; if (!pathname.isFile()) return false;
final String fname = pathname.getName().toLowerCase().trim(); final String fname = pathname.getName().toLowerCase().trim();
for (final String suffix : suffixes) { for (final String suffix : suffixes) {
if (fname.endsWith(suffix.toLowerCase().trim())) { if (fname.endsWith(suffix.toLowerCase().trim())) {
return true; return true;
} }
} }
return false; return false;
} }
} }

@ -16,134 +16,138 @@ import mightypork.utils.logging.Log;
public class FileTreeDiff { public class FileTreeDiff {
private static final byte[] BUFFER = new byte[2048]; private static final byte[] BUFFER = new byte[2048];
private final Checksum ck1 = new Adler32(); private final Checksum ck1 = new Adler32();
private final Checksum ck2 = new Adler32(); private final Checksum ck2 = new Adler32();
private boolean logging = true; private boolean logging = true;
private final List<Tuple<File>> compared = new ArrayList<>(); private final List<Tuple<File>> compared = new ArrayList<>();
private final Comparator<File> fileFirstSorter = new Comparator<File>() { private final Comparator<File> fileFirstSorter = new Comparator<File>() {
@Override @Override
public int compare(File o1, File o2) public int compare(File o1, File o2)
{ {
if (!o1.isDirectory() && o2.isDirectory()) return -1; if (!o1.isDirectory() && o2.isDirectory()) return -1;
if (o1.isDirectory() && !o2.isDirectory()) return 1; if (o1.isDirectory() && !o2.isDirectory()) return 1;
return o1.getName().compareTo(o2.getName()); return o1.getName().compareTo(o2.getName());
} }
}; };
public void enableLogging(boolean state) public void enableLogging(boolean state)
{ {
logging = state; logging = state;
} }
public boolean areEqual(File dir1, File dir2) public boolean areEqual(File dir1, File dir2)
{ {
if (logging) Log.f3("Comparing directory trees:\n 1. " + dir1 + "\n 2. " + dir2); if (logging) Log.f3("Comparing directory trees:\n 1. " + dir1 + "\n 2. " + dir2);
try { try {
compared.clear(); compared.clear();
buildList(dir1, dir2); buildList(dir1, dir2);
calcChecksum(); calcChecksum();
if (logging) Log.f3("No difference found."); if (logging) Log.f3("No difference found.");
return true; return true;
} catch (final NotEqualException e) { } catch (final NotEqualException e) {
if (logging) Log.f3("Difference found:\n" + e.getMessage()); if (logging) Log.f3("Difference found:\n" + e.getMessage());
return false; return false;
} }
} }
private void calcChecksum() throws NotEqualException private void calcChecksum() throws NotEqualException
{ {
for (final Tuple<File> pair : compared) { for (final Tuple<File> pair : compared) {
ck1.reset(); ck1.reset();
ck2.reset(); ck2.reset();
try (FileInputStream in1 = new FileInputStream(pair.a); FileInputStream in2 = new FileInputStream(pair.b)) { try(FileInputStream in1 = new FileInputStream(pair.a);
FileInputStream in2 = new FileInputStream(pair.b)) {
try (CheckedInputStream cin1 = new CheckedInputStream(in1, ck1); CheckedInputStream cin2 = new CheckedInputStream(in2, ck2)) {
try(CheckedInputStream cin1 = new CheckedInputStream(in1, ck1);
CheckedInputStream cin2 = new CheckedInputStream(in2, ck2)) {
while (true) { while (true) {
final int read1 = cin1.read(BUFFER); final int read1 = cin1.read(BUFFER);
final int read2 = cin2.read(BUFFER); final int read2 = cin2.read(BUFFER);
if (read1 != read2 || ck1.getValue() != ck2.getValue()) { if (read1 != read2 || ck1.getValue() != ck2.getValue()) {
throw new NotEqualException("Bytes differ:\n" + pair.a + "\n" + pair.b); throw new NotEqualException("Bytes differ:\n" + pair.a + "\n" + pair.b);
} }
if (read1 == -1) break; if (read1 == -1) break;
} }
} }
} catch (final IOException e) { } catch (final IOException e) {
// ignore // ignore
} }
} }
} }
private void buildList(File f1, File f2) throws NotEqualException private void buildList(File f1, File f2) throws NotEqualException
{ {
if (f1.isDirectory() != f2.isDirectory()) throw new NotEqualException("isDirectory differs:\n" + f1 + "\n" + f2); if (f1.isDirectory() != f2.isDirectory()) throw new NotEqualException("isDirectory differs:\n" + f1 + "\n" + f2);
if (f1.isFile() && f2.isFile()) { if (f1.isFile() && f2.isFile()) {
if (f1.length() != f2.length()) throw new NotEqualException("Sizes differ:\n" + f1 + "\n" + f2); if (f1.length() != f2.length()) throw new NotEqualException("Sizes differ:\n" + f1 + "\n" + f2);
} }
if (f1.isDirectory()) { if (f1.isDirectory()) {
final File[] children1 = f1.listFiles(); final File[] children1 = f1.listFiles();
final File[] children2 = f2.listFiles(); final File[] children2 = f2.listFiles();
Arrays.sort(children1, fileFirstSorter); Arrays.sort(children1, fileFirstSorter);
Arrays.sort(children2, fileFirstSorter); Arrays.sort(children2, fileFirstSorter);
if (children1.length != children2.length) throw new NotEqualException("Child counts differ:\n" + f1 + "\n" + f2); if (children1.length != children2.length) throw new NotEqualException("Child counts differ:\n" + f1 + "\n" + f2);
for (int i = 0; i < children1.length; i++) { for (int i = 0; i < children1.length; i++) {
final File ch1 = children1[i]; final File ch1 = children1[i];
final File ch2 = children2[i]; final File ch2 = children2[i];
if (!ch1.getName().equals(ch2.getName())) throw new NotEqualException("Filenames differ:\n" + ch1 + "\n" + ch2); if (!ch1.getName().equals(ch2.getName())) throw new NotEqualException("Filenames differ:\n" + ch1 + "\n" + ch2);
buildList(ch1, ch2); buildList(ch1, ch2);
} }
} else { } else {
compared.add(new Tuple<>(f1, f2)); compared.add(new Tuple<>(f1, f2));
} }
} }
private class NotEqualException extends Exception { private class NotEqualException extends Exception {
public NotEqualException(String msg) { public NotEqualException(String msg)
{
super(msg); super(msg);
} }
} }
private class Tuple<T> { private class Tuple<T> {
public T a; public T a;
public T b; public T b;
public Tuple(T a, T b) { public Tuple(T a, T b)
{
this.a = a; this.a = a;
this.b = b; this.b = b;
} }
} }
} }

@ -1,7 +1,19 @@
package mightypork.utils.files; package mightypork.utils.files;
import java.io.*; import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -12,9 +24,10 @@ import mightypork.utils.string.validation.StringFilter;
public class FileUtil { public class FileUtil {
/** /**
* Copy directory recursively. * Copy directory recursively.
* *
* @param source source file * @param source source file
* @param target target file * @param target target file
* @throws IOException on error * @throws IOException on error
@ -27,7 +40,7 @@ public class FileUtil {
/** /**
* Copy directory recursively - advanced variant. * Copy directory recursively - advanced variant.
* *
* @param source source file * @param source source file
* @param target target file * @param target target file
* @param filter filter accepting only files and dirs to be copied * @param filter filter accepting only files and dirs to be copied
@ -61,7 +74,7 @@ public class FileUtil {
/** /**
* List directory recursively * List directory recursively
* *
* @param source source file * @param source source file
* @param filter filter accepting only files and dirs to be copied (or null) * @param filter filter accepting only files and dirs to be copied (or null)
* @param files list of the found files * @param files list of the found files
@ -87,7 +100,7 @@ public class FileUtil {
/** /**
* Copy file using streams. Make sure target directory exists! * Copy file using streams. Make sure target directory exists!
* *
* @param source source file * @param source source file
* @param target target file * @param target target file
* @throws IOException on error * @throws IOException on error
@ -95,7 +108,8 @@ public class FileUtil {
public static void copyFile(File source, File target) throws IOException public static void copyFile(File source, File target) throws IOException
{ {
try (InputStream in = new FileInputStream(source); OutputStream out = new FileOutputStream(target)) { try(InputStream in = new FileInputStream(source);
OutputStream out = new FileOutputStream(target)) {
copyStream(in, out); copyStream(in, out);
} }
@ -104,7 +118,7 @@ public class FileUtil {
/** /**
* Copy bytes from input to output stream, leaving out stream open * Copy bytes from input to output stream, leaving out stream open
* *
* @param in input stream * @param in input stream
* @param out output stream * @param out output stream
* @throws IOException on error * @throws IOException on error
@ -129,7 +143,7 @@ public class FileUtil {
/** /**
* Improved delete * Improved delete
* *
* @param path deleted path * @param path deleted path
* @param recursive recursive delete * @param recursive recursive delete
* @return success * @return success
@ -153,14 +167,14 @@ public class FileUtil {
/** /**
* Read entire file to a string. * Read entire file to a string.
* *
* @param file file * @param file file
* @return file contents * @return file contents
* @throws IOException * @throws IOException
*/ */
public static String fileToString(File file) throws IOException public static String fileToString(File file) throws IOException
{ {
try (FileInputStream fin = new FileInputStream(file)) { try(FileInputStream fin = new FileInputStream(file)) {
return streamToString(fin); return streamToString(fin);
} }
@ -169,7 +183,7 @@ public class FileUtil {
/** /**
* Get files in a folder (create folder if needed) * Get files in a folder (create folder if needed)
* *
* @param dir folder * @param dir folder
* @return list of files * @return list of files
*/ */
@ -181,7 +195,7 @@ public class FileUtil {
/** /**
* Get files in a folder (create folder if needed) * Get files in a folder (create folder if needed)
* *
* @param dir folder * @param dir folder
* @param filter file filter * @param filter file filter
* @return list of files * @return list of files
@ -202,7 +216,7 @@ public class FileUtil {
/** /**
* Remove extension. * Remove extension.
* *
* @param file file * @param file file
* @return filename without extension * @return filename without extension
*/ */
@ -226,7 +240,7 @@ public class FileUtil {
/** /**
* Remove extension. * Remove extension.
* *
* @param filename * @param filename
* @return filename and extension * @return filename and extension
*/ */
@ -253,7 +267,7 @@ public class FileUtil {
/** /**
* Read entire input stream to a string, and close it. * Read entire input stream to a string, and close it.
* *
* @param in input stream * @param in input stream
* @return file contents * @return file contents
*/ */
@ -265,7 +279,7 @@ public class FileUtil {
/** /**
* Read input stream to a string, and close it. * Read input stream to a string, and close it.
* *
* @param in input stream * @param in input stream
* @param lines max number of lines (-1 to disable limit) * @param lines max number of lines (-1 to disable limit)
* @return file contents * @return file contents
@ -327,9 +341,8 @@ public class FileUtil {
if (in != null) return in; if (in != null) return in;
try { try {
return new FileInputStream(new File(".", path)); return new FileInputStream(WorkDir.getFile(path));
} catch (final FileNotFoundException e) { } catch (final FileNotFoundException e) {
// error
Log.w("Could not open resource stream: " + path); Log.w("Could not open resource stream: " + path);
return null; return null;
} }
@ -339,20 +352,20 @@ public class FileUtil {
public static String getResourceAsString(String path) public static String getResourceAsString(String path)
{ {
return streamToString(FileUtil.class.getResourceAsStream(path)); return streamToString(getResource(path));
} }
/** /**
* Save string to file * Save string to file
* *
* @param file file * @param file file
* @param text string * @param text string
* @throws IOException on error * @throws IOException on error
*/ */
public static void stringToFile(File file, String text) throws IOException public static void stringToFile(File file, String text) throws IOException
{ {
try (PrintStream out = new PrintStream(new FileOutputStream(file), false, "UTF-8")) { try(PrintStream out = new PrintStream(new FileOutputStream(file), false, "UTF-8")) {
out.print(text); out.print(text);
@ -393,14 +406,15 @@ public class FileUtil {
/** /**
* Copy resource to file * Copy resource to file
* *
* @param resname resource name * @param resname resource name
* @param file out file * @param file out file
* @throws IOException * @throws IOException
*/ */
public static void resourceToFile(String resname, File file) throws IOException public static void resourceToFile(String resname, File file) throws IOException
{ {
try (InputStream in = FileUtil.getResource(resname); OutputStream out = new FileOutputStream(file)) { try(InputStream in = FileUtil.getResource(resname);
OutputStream out = new FileOutputStream(file)) {
FileUtil.copyStream(in, out); FileUtil.copyStream(in, out);
} }
@ -410,14 +424,14 @@ public class FileUtil {
/** /**
* Get resource as string, safely closing streams. * Get resource as string, safely closing streams.
* *
* @param resname resource name * @param resname resource name
* @return resource as string, empty string on failure * @return resource as string, empty string on failure
* @throws IOException on fail * @throws IOException on fail
*/ */
public static String resourceToString(String resname) throws IOException public static String resourceToString(String resname) throws IOException
{ {
try (InputStream in = FileUtil.getResource(resname)) { try(InputStream in = FileUtil.getResource(resname)) {
return streamToString(in); return streamToString(in);
} }
} }

@ -9,23 +9,23 @@ import java.nio.channels.FileLock;
/** /**
* Instance lock (avoid running twice) * Instance lock (avoid running twice)
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class InstanceLock { public class InstanceLock {
@SuppressWarnings("resource") @SuppressWarnings("resource")
public static boolean onFile(final File lockFile) public static boolean onFile(final File lockFile)
{ {
try { try {
lockFile.getParentFile().mkdirs(); lockFile.getParentFile().mkdirs();
final RandomAccessFile randomAccessFile = new RandomAccessFile(lockFile, "rw"); final RandomAccessFile randomAccessFile = new RandomAccessFile(lockFile, "rw");
final FileLock fileLock = randomAccessFile.getChannel().tryLock(); final FileLock fileLock = randomAccessFile.getChannel().tryLock();
if (fileLock != null) { if (fileLock != null) {
Runtime.getRuntime().addShutdownHook(new Thread() { Runtime.getRuntime().addShutdownHook(new Thread() {
@Override @Override
public void run() public void run()
{ {
@ -39,10 +39,10 @@ public class InstanceLock {
} }
} }
}); });
return true; return true;
} }
return false; return false;
} catch (final IOException e) { } catch (final IOException e) {
System.err.println("IO error while obtaining lock."); System.err.println("IO error while obtaining lock.");
@ -50,5 +50,5 @@ public class InstanceLock {
return false; return false;
} }
} }
} }

@ -5,90 +5,90 @@ import java.io.File;
public class OsUtils { public class OsUtils {
public static enum EnumOS public static enum EnumOS
{ {
linux, macos, solaris, unknown, windows; linux, macos, solaris, unknown, windows;
public boolean isLinux() public boolean isLinux()
{ {
return this == linux || this == solaris; return this == linux || this == solaris;
} }
public boolean isMac() public boolean isMac()
{ {
return this == macos; return this == macos;
} }
public boolean isWindows() public boolean isWindows()
{ {
return this == windows; return this == windows;
} }
} }
private static EnumOS cachedOs; private static EnumOS cachedOs;
public static File getHomeWorkDir(String dirname) public static File getHomeWorkDir(String dirname)
{ {
final String userhome = System.getProperty("user.home", "."); final String userhome = System.getProperty("user.home", ".");
File file; File file;
switch (getOs()) { switch (getOs()) {
case linux: case linux:
case solaris: case solaris:
file = new File(userhome, dirname + '/'); file = new File(userhome, dirname + '/');
break; break;
case windows: case windows:
final String appdata = System.getenv("APPDATA"); final String appdata = System.getenv("APPDATA");
if (appdata != null) { if (appdata != null) {
file = new File(appdata, dirname + '/'); file = new File(appdata, dirname + '/');
} else { } else {
file = new File(userhome, dirname + '/'); file = new File(userhome, dirname + '/');
} }
break; break;
case macos: case macos:
file = new File(userhome, "Library/Application Support/" + dirname); file = new File(userhome, "Library/Application Support/" + dirname);
break; break;
default: default:
file = new File(userhome, dirname + "/"); file = new File(userhome, dirname + "/");
break; break;
} }
return file; return file;
} }
public static EnumOS getOs() public static EnumOS getOs()
{ {
if (cachedOs != null) return cachedOs; if (cachedOs != null) return cachedOs;
final String s = System.getProperty("os.name").toLowerCase(); final String s = System.getProperty("os.name").toLowerCase();
if (s.contains("win")) { if (s.contains("win")) {
cachedOs = EnumOS.windows; cachedOs = EnumOS.windows;
} else if (s.contains("mac")) { } else if (s.contains("mac")) {
cachedOs = EnumOS.macos; cachedOs = EnumOS.macos;
} else if (s.contains("linux") || s.contains("unix")) { } else if (s.contains("linux") || s.contains("unix")) {
cachedOs = EnumOS.linux; cachedOs = EnumOS.linux;
} else if (s.contains("solaris") || s.contains("sunos")) { } else if (s.contains("solaris") || s.contains("sunos")) {
cachedOs = EnumOS.solaris; cachedOs = EnumOS.solaris;
} else { } else {
cachedOs = EnumOS.unknown; cachedOs = EnumOS.unknown;
} }
return cachedOs; return cachedOs;
} }
} }

@ -0,0 +1,96 @@
package mightypork.utils.files;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import mightypork.utils.logging.Log;
/**
* Working directory helper.
*
* @author Ondřej Hruška (MightyPork)
*/
public class WorkDir {
private static File baseDir = new File(".");
private static Map<String, String> namedPaths = new HashMap<>();
/**
* Initialize the workdir for the given root path
*
* @param workdir workdir root path
*/
public static void setBaseDir(File workdir)
{
WorkDir.baseDir = workdir;
}
/**
* Add a path alias (dir or file), relative to the workdir.
*
* @param alias path alias
* @param path path relative to workdir
*/
public static void addPath(String alias, String path)
{
namedPaths.put(alias, path);
}
/**
* Get workdir folder, create if not exists.
*
* @param path dir path relative to workdir
* @return dir file
*/
public static File getDir(String path)
{
if (namedPaths.containsKey(path)) path = namedPaths.get(path);
final File f = new File(baseDir, path);
if (!f.exists()) {
if (!f.mkdirs()) {
Log.w("Could not create a directory: " + f + " (path: " + path + ")");
}
}
return f;
}
/**
* Get workdir file, create parent if not exists.
*
* @param path dir path relative to workdir
* @return dir file
*/
public static File getFile(String path)
{
if (namedPaths.containsKey(path)) path = namedPaths.get(path);
final File f = new File(baseDir, path);
// create the parent dir
if (!f.getParent().equals(baseDir)) {
f.getParentFile().mkdirs();
}
return f;
}
/**
* @return the workdir File
*/
public static File getBaseDir()
{
return baseDir;
}
}

@ -16,31 +16,32 @@ import mightypork.utils.logging.Log;
/** /**
* Class for building a zip file * Class for building a zip file
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class ZipBuilder { public class ZipBuilder {
private final ZipOutputStream out; private final ZipOutputStream out;
private final HashSet<String> included = new HashSet<>(); private final HashSet<String> included = new HashSet<>();
/** /**
* @param target target zip file * @param target target zip file
* @throws IOException if the file is directory or cannot be created * @throws IOException if the file is directory or cannot be created
*/ */
public ZipBuilder(File target) throws IOException { public ZipBuilder(File target) throws IOException
{
if (!target.getParentFile().mkdirs()) throw new IOException("Could not create output directory."); if (!target.getParentFile().mkdirs()) throw new IOException("Could not create output directory.");
final FileOutputStream dest = new FileOutputStream(target); final FileOutputStream dest = new FileOutputStream(target);
out = new ZipOutputStream(new BufferedOutputStream(dest)); out = new ZipOutputStream(new BufferedOutputStream(dest));
} }
/** /**
* Add stream to a path * Add stream to a path
* *
* @param path path * @param path path
* @param in stream * @param in stream
* @throws IOException * @throws IOException
@ -53,16 +54,16 @@ public class ZipBuilder {
return; // ignore return; // ignore
} }
included.add(path); included.add(path);
out.putNextEntry(new ZipEntry(path)); out.putNextEntry(new ZipEntry(path));
FileUtil.copyStream(in, out); FileUtil.copyStream(in, out);
} }
/** /**
* Add string as a file * Add string as a file
* *
* @param path path * @param path path
* @param text text to write * @param text text to write
* @throws IOException * @throws IOException
@ -72,18 +73,18 @@ public class ZipBuilder {
path = preparePath(path); path = preparePath(path);
if (included.contains(path)) return; // ignore if (included.contains(path)) return; // ignore
included.add(path); included.add(path);
out.putNextEntry(new ZipEntry(path)); out.putNextEntry(new ZipEntry(path));
try (InputStream in = FileUtil.stringToStream(text)) { try(InputStream in = FileUtil.stringToStream(text)) {
FileUtil.copyStream(in, out); FileUtil.copyStream(in, out);
} }
} }
/** /**
* Add resource obtained via FileUtils.getResource() * Add resource obtained via FileUtils.getResource()
* *
* @param path path * @param path path
* @param resPath resource path * @param resPath resource path
* @throws IOException * @throws IOException
@ -93,34 +94,34 @@ public class ZipBuilder {
path = preparePath(path); path = preparePath(path);
if (included.contains(path)) return; // ignore if (included.contains(path)) return; // ignore
included.add(path); included.add(path);
out.putNextEntry(new ZipEntry(path)); out.putNextEntry(new ZipEntry(path));
try (InputStream in = FileUtil.getResource(resPath)) { try(InputStream in = FileUtil.getResource(resPath)) {
FileUtil.copyStream(in, out); FileUtil.copyStream(in, out);
} }
} }
/** /**
* Normalize path * Normalize path
* *
* @param path original path * @param path original path
* @return normalized path * @return normalized path
*/ */
private static String preparePath(String path) private static String preparePath(String path)
{ {
path = path.replace("\\", "/"); path = path.replace("\\", "/");
if (path.charAt(0) == '/') path = path.substring(1); if (path.charAt(0) == '/') path = path.substring(1);
return path; return path;
} }
/** /**
* Close the zip stream * Close the zip stream
* *
* @throws IOException * @throws IOException
*/ */
public void close() throws IOException public void close() throws IOException

@ -1,7 +1,12 @@
package mightypork.utils.files.zip; package mightypork.utils.files.zip;
import java.io.*; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.List; import java.util.List;
@ -15,17 +20,17 @@ import mightypork.utils.string.validation.StringFilter;
/** /**
* Utilities for manipulating zip files * Utilities for manipulating zip files
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class ZipUtils { public class ZipUtils {
private static final int BUFFER_SIZE = 2048; private static final int BUFFER_SIZE = 2048;
/** /**
* Extract zip file to target directory * Extract zip file to target directory
* *
* @param file zip file * @param file zip file
* @param outputDir target directory * @param outputDir target directory
* @param filter string filter (will be used to test entry names (paths)) * @param filter string filter (will be used to test entry names (paths))
@ -34,15 +39,15 @@ public class ZipUtils {
*/ */
public static List<String> extractZip(File file, File outputDir, StringFilter filter) throws IOException public static List<String> extractZip(File file, File outputDir, StringFilter filter) throws IOException
{ {
try (ZipFile zip = new ZipFile(file)) { try(ZipFile zip = new ZipFile(file)) {
return extractZip(zip, outputDir, filter); return extractZip(zip, outputDir, filter);
} }
} }
/** /**
* Extract zip file to target directory * Extract zip file to target directory
* *
* @param zip open zip file * @param zip open zip file
* @param outputDir target directory * @param outputDir target directory
* @param filter string filter (will be used to test entry names (paths)) * @param filter string filter (will be used to test entry names (paths))
@ -52,53 +57,53 @@ public class ZipUtils {
public static List<String> extractZip(ZipFile zip, File outputDir, StringFilter filter) throws IOException public static List<String> extractZip(ZipFile zip, File outputDir, StringFilter filter) throws IOException
{ {
final ArrayList<String> files = new ArrayList<>(); final ArrayList<String> files = new ArrayList<>();
if (!outputDir.mkdirs()) throw new IOException("Could not create output directory."); if (!outputDir.mkdirs()) throw new IOException("Could not create output directory.");
final Enumeration<? extends ZipEntry> zipFileEntries = zip.entries(); final Enumeration<? extends ZipEntry> zipFileEntries = zip.entries();
// process each entry // process each entry
while (zipFileEntries.hasMoreElements()) { while (zipFileEntries.hasMoreElements()) {
final ZipEntry entry = zipFileEntries.nextElement(); final ZipEntry entry = zipFileEntries.nextElement();
// parse filename and path // parse filename and path
final String entryPath = entry.getName(); final String entryPath = entry.getName();
final File destFile = new File(outputDir, entryPath); final File destFile = new File(outputDir, entryPath);
final File destinationParent = destFile.getParentFile(); final File destinationParent = destFile.getParentFile();
if (entry.isDirectory() || (filter != null && !filter.isValid(entryPath))) continue; if (entry.isDirectory() || (filter != null && !filter.isValid(entryPath))) continue;
// make sure directories exist // make sure directories exist
if (!destinationParent.mkdirs()) throw new IOException("Could not create directory."); if (!destinationParent.mkdirs()) throw new IOException("Could not create directory.");
if (!entry.isDirectory()) { if (!entry.isDirectory()) {
extractZipEntry(zip, entry, destFile); extractZipEntry(zip, entry, destFile);
files.add(entryPath); files.add(entryPath);
} }
} }
return files; return files;
} }
/** /**
* Read zip entries and add their paths to a list * Read zip entries and add their paths to a list
* *
* @param zipFile open zip file * @param zipFile open zip file
* @return list of entry names * @return list of entry names
* @throws IOException on error * @throws IOException on error
*/ */
public static List<String> listZip(File zipFile) throws IOException public static List<String> listZip(File zipFile) throws IOException
{ {
try (ZipFile zip = new ZipFile(zipFile)) { try(ZipFile zip = new ZipFile(zipFile)) {
return listZip(zip); return listZip(zip);
} }
} }
/** /**
* Read zip entries and add their paths to a list * Read zip entries and add their paths to a list
* *
* @param zip open zip file * @param zip open zip file
* @return list of entry names * @return list of entry names
* @throws IOException on error * @throws IOException on error
@ -106,25 +111,25 @@ public class ZipUtils {
public static List<String> listZip(ZipFile zip) throws IOException public static List<String> listZip(ZipFile zip) throws IOException
{ {
final ArrayList<String> files = new ArrayList<>(); final ArrayList<String> files = new ArrayList<>();
final Enumeration<? extends ZipEntry> zipFileEntries = zip.entries(); final Enumeration<? extends ZipEntry> zipFileEntries = zip.entries();
// process each entry // process each entry
while (zipFileEntries.hasMoreElements()) { while (zipFileEntries.hasMoreElements()) {
final ZipEntry entry = zipFileEntries.nextElement(); final ZipEntry entry = zipFileEntries.nextElement();
if (!entry.isDirectory()) { if (!entry.isDirectory()) {
files.add(entry.getName()); files.add(entry.getName());
} }
} }
return files; return files;
} }
/** /**
* Extract one zip entry to target file * Extract one zip entry to target file
* *
* @param zip open zip file * @param zip open zip file
* @param entry entry from the zip file * @param entry entry from the zip file
* @param destFile destination file ((NOT directory!) * @param destFile destination file ((NOT directory!)
@ -133,17 +138,20 @@ public class ZipUtils {
public static void extractZipEntry(ZipFile zip, ZipEntry entry, File destFile) throws IOException public static void extractZipEntry(ZipFile zip, ZipEntry entry, File destFile) throws IOException
{ {
if (!destFile.getParentFile().mkdirs()) throw new IOException("Could not create output directory."); if (!destFile.getParentFile().mkdirs()) throw new IOException("Could not create output directory.");
try (InputStream in = zip.getInputStream(entry); BufferedInputStream is = new BufferedInputStream(in); FileOutputStream fos = new FileOutputStream(destFile); BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER_SIZE)) { try(InputStream in = zip.getInputStream(entry);
BufferedInputStream is = new BufferedInputStream(in);
FileOutputStream fos = new FileOutputStream(destFile);
BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER_SIZE)) {
FileUtil.copyStream(is, dest); FileUtil.copyStream(is, dest);
} }
} }
/** /**
* Load zip entry to String * Load zip entry to String
* *
* @param zip open zip file * @param zip open zip file
* @param entry entry from the zip file * @param entry entry from the zip file
* @return loaded string * @return loaded string
@ -164,16 +172,16 @@ public class ZipUtils {
} }
} }
} }
public static boolean entryExists(File selectedFile, String string) public static boolean entryExists(File selectedFile, String string)
{ {
try (ZipFile zf = new ZipFile(selectedFile)) { try(ZipFile zf = new ZipFile(selectedFile)) {
return zf.getEntry(string) != null; return zf.getEntry(string) != null;
} catch (final IOException | RuntimeException e) { } catch (final IOException | RuntimeException e) {
Log.w("Error reading zip.", e); Log.w("Error reading zip.", e);
return false; return false;
} }
} }
} }

@ -3,11 +3,11 @@ package mightypork.utils.interfaces;
/** /**
* Object that can be destroyed (free resources etc) * Object that can be destroyed (free resources etc)
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public interface Destroyable { public interface Destroyable {
/** /**
* Destroy this object * Destroy this object
*/ */

@ -5,19 +5,19 @@ package mightypork.utils.interfaces;
* Can be enabled or disabled.<br> * Can be enabled or disabled.<br>
* Implementations should take appropriate action (ie. stop listening to events, * Implementations should take appropriate action (ie. stop listening to events,
* updating etc.) * updating etc.)
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public interface Enableable { public interface Enableable {
/** /**
* Change enabled state * Change enabled state
* *
* @param yes enabled * @param yes enabled
*/ */
public void setEnabled(boolean yes); public void setEnabled(boolean yes);
/** /**
* @return true if enabled * @return true if enabled
*/ */

@ -3,13 +3,13 @@ package mightypork.utils.interfaces;
/** /**
* Element that can be hidden or visible * Element that can be hidden or visible
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public interface Hideable { public interface Hideable {
void setVisible(boolean yes); void setVisible(boolean yes);
boolean isVisible(); boolean isVisible();
} }

@ -3,26 +3,26 @@ package mightypork.utils.interfaces;
/** /**
* Can be paused & resumed * Can be paused & resumed
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public interface Pauseable { public interface Pauseable {
/** /**
* Pause operation * Pause operation
*/ */
public void pause(); public void pause();
/** /**
* Resume operation * Resume operation
*/ */
public void resume(); public void resume();
/** /**
* @return paused state * @return paused state
*/ */
public boolean isPaused(); public boolean isPaused();
} }

@ -3,11 +3,11 @@ package mightypork.utils.interfaces;
/** /**
* Can be asked to update it's state * Can be asked to update it's state
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public interface Pollable { public interface Pollable {
/** /**
* Update internal state * Update internal state
*/ */

@ -3,14 +3,14 @@ package mightypork.utils.interfaces;
/** /**
* Uses delta timing * Uses delta timing
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public interface Updateable { public interface Updateable {
/** /**
* Update item state based on elapsed time * Update item state based on elapsed time
* *
* @param delta time elapsed since last update, in seconds * @param delta time elapsed since last update, in seconds
*/ */
public void update(double delta); public void update(double delta);

@ -1,7 +1,12 @@
package mightypork.utils.ion; package mightypork.utils.ion;
import java.io.*; import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -13,17 +18,17 @@ import mightypork.utils.Support;
/** /**
* Universal data storage system (main API class) * Universal data storage system (main API class)
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class Ion { public class Ion {
private static final int RESERVED_LOW = 0; private static final int RESERVED_LOW = 0;
private static final int RESERVED_HIGH = 49; private static final int RESERVED_HIGH = 49;
private static final int RANGE_LOW = 0; private static final int RANGE_LOW = 0;
private static final int RANGE_HIGH = 255; private static final int RANGE_HIGH = 255;
// marks for object saving // marks for object saving
/** Null mark */ /** Null mark */
static final int NULL = 0; static final int NULL = 0;
@ -79,31 +84,31 @@ public class Ion {
public static final int MAP = 25; public static final int MAP = 25;
/** Array of arbitrary objects */ /** Array of arbitrary objects */
public static final int OBJECT_ARRAY = 26; public static final int OBJECT_ARRAY = 26;
/** Ionizables<Mark, Class> */ /** Ionizables<Mark, Class> */
private static Map<Integer, Class<?>> markToClass = new HashMap<>(); private static Map<Integer, Class<?>> markToClass = new HashMap<>();
private static Map<Class<?>, Integer> classToMark = new HashMap<>(); private static Map<Class<?>, Integer> classToMark = new HashMap<>();
private static Map<Class<?>, IonizerBinary<?>> ionizersBinary = new HashMap<>(); private static Map<Class<?>, IonizerBinary<?>> ionizersBinary = new HashMap<>();
private static Map<Class<?>, IonizerBundled<?>> ionizersBundled = new HashMap<>(); private static Map<Class<?>, IonizerBundled<?>> ionizersBundled = new HashMap<>();
private static boolean reservedMarkChecking; private static boolean reservedMarkChecking;
static { static {
reservedMarkChecking = false; reservedMarkChecking = false;
// register built-ins // register built-ins
register(ION_BUNDLE, IonDataBundle.class); register(ION_BUNDLE, IonDataBundle.class);
register(SEQUENCE_WRAPPER, IonSequenceWrapper.class); register(SEQUENCE_WRAPPER, IonSequenceWrapper.class);
register(MAP_WRAPPER, IonMapWrapper.class); register(MAP_WRAPPER, IonMapWrapper.class);
reservedMarkChecking = true; reservedMarkChecking = true;
} }
/** /**
* Register a type for writing/loading. * Register a type for writing/loading.
* *
* @param mark binary ION mark * @param mark binary ION mark
* @param objClass class of the registered object * @param objClass class of the registered object
*/ */
@ -114,16 +119,16 @@ public class Ion {
throw new IllegalArgumentException("Cannot register directly: " + Support.str(objClass)); throw new IllegalArgumentException("Cannot register directly: " + Support.str(objClass));
} }
} }
assertHasImplicitConstructor(objClass); assertHasImplicitConstructor(objClass);
registerUsingMark(mark, objClass); registerUsingMark(mark, objClass);
} }
/** /**
* Try to register a type using a static final ION_MARK int field. * Try to register a type using a static final ION_MARK int field.
* *
* @param objClass type class * @param objClass type class
*/ */
public static void register(Class<?> objClass) public static void register(Class<?> objClass)
@ -133,66 +138,66 @@ public class Ion {
throw new IllegalArgumentException("Cannot register directly: " + Support.str(objClass)); throw new IllegalArgumentException("Cannot register directly: " + Support.str(objClass));
} }
} }
assertHasImplicitConstructor(objClass); assertHasImplicitConstructor(objClass);
registerUsingConstant(objClass); registerUsingConstant(objClass);
} }
private static void registerUsingMark(int mark, Class<?> objClass) private static void registerUsingMark(int mark, Class<?> objClass)
{ {
assertMarkAvailable(mark, objClass); assertMarkAvailable(mark, objClass);
markToClass.put(mark, objClass); markToClass.put(mark, objClass);
classToMark.put(objClass, mark); classToMark.put(objClass, mark);
} }
public static void registerUsingConstant(Class<?> objClass) public static void registerUsingConstant(Class<?> objClass)
{ {
try { try {
final int mark = ((Number) Reflect.getConstantFieldValue(objClass, "ION_MARK")).intValue(); final int mark = ((Number) Reflect.getConstantFieldValue(objClass, "ION_MARK")).intValue();
registerUsingMark(mark, objClass); registerUsingMark(mark, objClass);
} catch (final Exception e) { } catch (final Exception e) {
throw new RuntimeException("Could not register " + Support.str(objClass) + " using an ION_MARK field.", e); throw new RuntimeException("Could not register " + Support.str(objClass) + " using an ION_MARK field.", e);
} }
} }
/** /**
* Register new binary ionizer. * Register new binary ionizer.
* *
* @param mark binary ION mark * @param mark binary ION mark
* @param ionizer ionizer * @param ionizer ionizer
*/ */
public static void registerIndirect(int mark, IonizerBinary<?> ionizer) public static void registerIndirect(int mark, IonizerBinary<?> ionizer)
{ {
final Class<?> objClass = Reflect.getGenericParameters(ionizer.getClass())[0]; final Class<?> objClass = Reflect.getGenericParameters(ionizer.getClass())[0];
registerUsingMark(mark, objClass); registerUsingMark(mark, objClass);
ionizersBinary.put(objClass, ionizer); ionizersBinary.put(objClass, ionizer);
} }
/** /**
* Register new bundled ionizer. * Register new bundled ionizer.
* *
* @param mark binary ION mark * @param mark binary ION mark
* @param ionizer ionizer * @param ionizer ionizer
*/ */
public static void registerIndirect(int mark, IonizerBundled<?> ionizer) public static void registerIndirect(int mark, IonizerBundled<?> ionizer)
{ {
final Class<?> objClass = Reflect.getGenericParameters(ionizer.getClass())[0]; final Class<?> objClass = Reflect.getGenericParameters(ionizer.getClass())[0];
registerUsingMark(mark, objClass); registerUsingMark(mark, objClass);
ionizersBundled.put(objClass, ionizer); ionizersBundled.put(objClass, ionizer);
} }
private static void assertHasImplicitConstructor(Class<?> objClass) private static void assertHasImplicitConstructor(Class<?> objClass)
{ {
try { try {
@ -201,28 +206,28 @@ public class Ion {
throw new RuntimeException("Class " + objClass + " doesn't have an implicit constructor."); throw new RuntimeException("Class " + objClass + " doesn't have an implicit constructor.");
} }
} }
private static void assertMarkAvailable(int mark, Class<?> objClass) private static void assertMarkAvailable(int mark, Class<?> objClass)
{ {
// negative marks are allowed. // negative marks are allowed.
if (mark > RANGE_HIGH) throw new IllegalArgumentException("Mark must be < 256."); if (mark > RANGE_HIGH) throw new IllegalArgumentException("Mark must be < 256.");
if (mark < RANGE_LOW) throw new IllegalArgumentException("Mark must be positive."); if (mark < RANGE_LOW) throw new IllegalArgumentException("Mark must be positive.");
if (reservedMarkChecking && isMarkReserved(mark)) { if (reservedMarkChecking && isMarkReserved(mark)) {
throw new IllegalArgumentException("Marks " + RESERVED_LOW + ".." + RESERVED_HIGH + " are reserved."); throw new IllegalArgumentException("Marks " + RESERVED_LOW + ".." + RESERVED_HIGH + " are reserved.");
} }
if (markToClass.containsKey(mark)) { if (markToClass.containsKey(mark)) {
throw new IllegalArgumentException("Mark " + mark + " is already in use."); throw new IllegalArgumentException("Mark " + mark + " is already in use.");
} }
if (classToMark.containsKey(objClass)) { if (classToMark.containsKey(objClass)) {
throw new IllegalArgumentException(Support.str(objClass) + " is already registered."); throw new IllegalArgumentException(Support.str(objClass) + " is already registered.");
} }
} }
/** /**
* Load binary from file and cast. * Load binary from file and cast.
*/ */
@ -230,19 +235,19 @@ public class Ion {
{ {
return fromFile(new File(path)); return fromFile(new File(path));
} }
/** /**
* Load binary from file and cast. * Load binary from file and cast.
*/ */
public static <T> T fromFile(File file) throws IOException public static <T> T fromFile(File file) throws IOException
{ {
try (InputStream in = new FileInputStream(file)) { try(InputStream in = new FileInputStream(file)) {
return fromStream(in); return fromStream(in);
} }
} }
/** /**
* Write binary to file with mark. * Write binary to file with mark.
*/ */
@ -250,49 +255,49 @@ public class Ion {
{ {
toFile(new File(path), obj); toFile(new File(path), obj);
} }
/** /**
* Write binary to file with mark. * Write binary to file with mark.
*/ */
public static void toFile(File file, Object obj) throws IOException public static void toFile(File file, Object obj) throws IOException
{ {
try (OutputStream out = new FileOutputStream(file)) { try(OutputStream out = new FileOutputStream(file)) {
toStream(out, obj); toStream(out, obj);
out.flush(); out.flush();
} catch (final Exception e) { } catch (final Exception e) {
throw new IOException("Error writing to ION file.", e); throw new IOException("Error writing to ION file.", e);
} }
} }
/** /**
* Load object from stream based on mark, try to cast. * Load object from stream based on mark, try to cast.
*/ */
public static <T> T fromStream(InputStream in) throws IOException public static <T> T fromStream(InputStream in) throws IOException
{ {
try (final IonInput inp = new IonInput(in)) { try(final IonInput inp = new IonInput(in)) {
return (T) inp.readObject(); return (T) inp.readObject();
} }
} }
/** /**
* Write object to output with a mark. * Write object to output with a mark.
*/ */
public static void toStream(OutputStream out, Object obj) throws IOException public static void toStream(OutputStream out, Object obj) throws IOException
{ {
try (IonOutput iout = new IonOutput(out)) { try(IonOutput iout = new IonOutput(out)) {
iout.writeObject(obj); iout.writeObject(obj);
} }
} }
/** /**
* Get ion input * Get ion input
* *
* @param path file path to read * @param path file path to read
* @return input * @return input
* @throws IOException * @throws IOException
@ -301,11 +306,11 @@ public class Ion {
{ {
return getInput(new File(path)); return getInput(new File(path));
} }
/** /**
* Get ion input * Get ion input
* *
* @param file file to read * @param file file to read
* @return input * @return input
* @throws IOException * @throws IOException
@ -315,11 +320,11 @@ public class Ion {
{ {
return new IonInput(new FileInputStream(file)); return new IonInput(new FileInputStream(file));
} }
/** /**
* Get ion output * Get ion output
* *
* @param path file path to write * @param path file path to write
* @return output * @return output
* @throws IOException * @throws IOException
@ -328,11 +333,11 @@ public class Ion {
{ {
return getOutput(new File(path)); return getOutput(new File(path));
} }
/** /**
* Get ion output * Get ion output
* *
* @param file file to write * @param file file to write
* @return output * @return output
* @throws IOException * @throws IOException
@ -342,8 +347,8 @@ public class Ion {
{ {
return new IonOutput(new FileOutputStream(file)); return new IonOutput(new FileOutputStream(file));
} }
/** /**
* Create new bundle and write the object to it. * Create new bundle and write the object to it.
*/ */
@ -353,12 +358,12 @@ public class Ion {
content.save(ib); content.save(ib);
return ib; return ib;
} }
/** /**
* Try to unwrap an object from bundle. The object class must have implicit * Try to unwrap an object from bundle. The object class must have implicit
* accessible constructor. * accessible constructor.
* *
* @param bundle unwrapped bundle * @param bundle unwrapped bundle
* @param objClass class of desired object * @param objClass class of desired object
* @return the object unwrapped * @return the object unwrapped
@ -374,56 +379,56 @@ public class Ion {
throw new IOException("Could not instantiate " + Support.str(objClass) + "."); throw new IOException("Could not instantiate " + Support.str(objClass) + ".");
} }
} }
static Class<?> getClassForMark(int mark) static Class<?> getClassForMark(int mark)
{ {
return markToClass.get(mark); return markToClass.get(mark);
} }
static IonizerBinary<?> getIonizerBinaryForClass(Class<?> clz) static IonizerBinary<?> getIonizerBinaryForClass(Class<?> clz)
{ {
return ionizersBinary.get(clz); return ionizersBinary.get(clz);
} }
static IonizerBundled<?> getIonizerBundledForClass(Class<?> clz) static IonizerBundled<?> getIonizerBundledForClass(Class<?> clz)
{ {
return ionizersBundled.get(clz); return ionizersBundled.get(clz);
} }
public static int getMark(Object object) public static int getMark(Object object)
{ {
assertRegistered(object); assertRegistered(object);
return classToMark.get(object.getClass()); return classToMark.get(object.getClass());
} }
/** /**
* @return true if the mark is for a registered {@link IonBinary} object * @return true if the mark is for a registered {@link IonBinary} object
*/ */
static boolean isMarkForBinary(int mark) static boolean isMarkForBinary(int mark)
{ {
if (!markToClass.containsKey(mark)) return false; if (!markToClass.containsKey(mark)) return false;
return IonBinary.class.isAssignableFrom(markToClass.get(mark)); return IonBinary.class.isAssignableFrom(markToClass.get(mark));
} }
/** /**
* @return true if the mark is for a registered {@link IonBinary} object * @return true if the mark is for a registered {@link IonBinary} object
*/ */
static boolean isMarkForBundled(int mark) static boolean isMarkForBundled(int mark)
{ {
if (!markToClass.containsKey(mark)) return false; if (!markToClass.containsKey(mark)) return false;
return IonBundled.class.isAssignableFrom(markToClass.get(mark)); return IonBundled.class.isAssignableFrom(markToClass.get(mark));
} }
/** /**
* @return true if the mark is for a registered indirectly saved binary * @return true if the mark is for a registered indirectly saved binary
* object * object
@ -431,11 +436,11 @@ public class Ion {
static boolean isMarkForIndirectBinary(int mark) static boolean isMarkForIndirectBinary(int mark)
{ {
if (!markToClass.containsKey(mark)) return false; if (!markToClass.containsKey(mark)) return false;
return ionizersBinary.containsKey(markToClass.get(mark)); return ionizersBinary.containsKey(markToClass.get(mark));
} }
/** /**
* @return true if the mark is for a registered indirectly saved bundled * @return true if the mark is for a registered indirectly saved bundled
* object * object
@ -443,11 +448,11 @@ public class Ion {
static boolean isMarkForIndirectBundled(int mark) static boolean isMarkForIndirectBundled(int mark)
{ {
if (!markToClass.containsKey(mark)) return false; if (!markToClass.containsKey(mark)) return false;
return ionizersBundled.containsKey(markToClass.get(mark)); return ionizersBundled.containsKey(markToClass.get(mark));
} }
/** /**
* @return true if the mark is reserved for internal use * @return true if the mark is reserved for internal use
*/ */
@ -455,8 +460,8 @@ public class Ion {
{ {
return mark >= RESERVED_LOW && mark <= RESERVED_HIGH; return mark >= RESERVED_LOW && mark <= RESERVED_HIGH;
} }
/** /**
* @return true if the mark is for a registered {@link IonBinary} object * @return true if the mark is for a registered {@link IonBinary} object
*/ */
@ -464,14 +469,14 @@ public class Ion {
{ {
final Class<?> clazz = object.getClass(); final Class<?> clazz = object.getClass();
if (classToMark.containsKey(clazz)) return true; if (classToMark.containsKey(clazz)) return true;
return false; return false;
} }
/** /**
* Make sure object is registered in the table. * Make sure object is registered in the table.
* *
* @throws IOException if not registered or class mismatch * @throws IOException if not registered or class mismatch
*/ */
static void assertRegistered(Object obj) static void assertRegistered(Object obj)
@ -480,8 +485,8 @@ public class Ion {
throw new RuntimeException("Type not registered: " + Support.str(obj.getClass())); throw new RuntimeException("Type not registered: " + Support.str(obj.getClass()));
} }
} }
/** /**
* For get all external registered types - just like if the class was * For get all external registered types - just like if the class was
* freshly loaded. Can be used for unit tests. * freshly loaded. Can be used for unit tests.
@ -489,31 +494,31 @@ public class Ion {
public static void reset() public static void reset()
{ {
final List<Integer> toRemove = new ArrayList<>(); final List<Integer> toRemove = new ArrayList<>();
// remove direct // remove direct
for (final Integer mark : markToClass.keySet()) { for (final Integer mark : markToClass.keySet()) {
if (!isMarkReserved(mark)) { if (!isMarkReserved(mark)) {
toRemove.add(mark); toRemove.add(mark);
} }
} }
for (final int i : toRemove) { for (final int i : toRemove) {
final Class<?> clz = markToClass.remove(i); final Class<?> clz = markToClass.remove(i);
classToMark.remove(clz); classToMark.remove(clz);
ionizersBinary.remove(clz); ionizersBinary.remove(clz);
ionizersBundled.remove(clz); ionizersBundled.remove(clz);
} }
} }
public static boolean isObjectIndirectBundled(Object obj) public static boolean isObjectIndirectBundled(Object obj)
{ {
return ionizersBundled.containsKey(obj.getClass()); return ionizersBundled.containsKey(obj.getClass());
} }
public static boolean isObjectIndirectBinary(Object obj) public static boolean isObjectIndirectBinary(Object obj)
{ {
return ionizersBinary.containsKey(obj.getClass()); return ionizersBinary.containsKey(obj.getClass());

@ -7,24 +7,24 @@ import java.io.IOException;
/** /**
* Binary ion object. If a class implements both binary and bundled, then binary * Binary ion object. If a class implements both binary and bundled, then binary
* will be preferred by both IonInput and IonOutput. * will be preferred by both IonInput and IonOutput.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public interface IonBinary { public interface IonBinary {
/** /**
* Load data from the input stream. * Load data from the input stream.
* *
* @param in input stream * @param in input stream
* @throws IOException * @throws IOException
*/ */
void load(IonInput in) throws IOException; void load(IonInput in) throws IOException;
/** /**
* Store data to output stream (in such way that the load method will later * Store data to output stream (in such way that the load method will later
* be able to read it). * be able to read it).
* *
* @param out Output stream * @param out Output stream
* @throws IOException * @throws IOException
*/ */

@ -4,22 +4,22 @@ package mightypork.utils.ion;
/** /**
* Bundled ion object. If a class implements both binary and bundled, then * Bundled ion object. If a class implements both binary and bundled, then
* binary will be preferred by both IonInput and IonOutput. * binary will be preferred by both IonInput and IonOutput.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public interface IonBundled { public interface IonBundled {
/** /**
* Load this object from the data bundle * Load this object from the data bundle
* *
* @param in bundle to load from * @param in bundle to load from
*/ */
void load(IonDataBundle in); void load(IonDataBundle in);
/** /**
* Save this object to the data bundle * Save this object to the data bundle
* *
* @param out bundle to save into * @param out bundle to save into
*/ */
void save(IonDataBundle out); void save(IonDataBundle out);

@ -12,34 +12,34 @@ import java.util.Map;
/** /**
* Ion data bundle - simplified Map with facilities for storing maps and * Ion data bundle - simplified Map with facilities for storing maps and
* sequences. * sequences.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class IonDataBundle implements IonBinary { public class IonDataBundle implements IonBinary {
private final Map<String, Object> backingMap = new HashMap<>(); private final Map<String, Object> backingMap = new HashMap<>();
/** /**
* Clear & fill a provided bundle with elements from a bundle value * Clear & fill a provided bundle with elements from a bundle value
* *
* @param key key * @param key key
* @param filled bundle to fill * @param filled bundle to fill
*/ */
public void loadBundle(String key, IonDataBundle filled) public void loadBundle(String key, IonDataBundle filled)
{ {
if (!containsKey(key)) return; if (!containsKey(key)) return;
final IonDataBundle ib = get(key, new IonDataBundle()); final IonDataBundle ib = get(key, new IonDataBundle());
filled.clear(); filled.clear();
filled.putAll(ib); filled.putAll(ib);
} }
/** /**
* Check if a key is used in the bundle * Check if a key is used in the bundle
* *
* @param key key to check * @param key key to check
* @return true if this key is used in the bundle * @return true if this key is used in the bundle
*/ */
@ -47,11 +47,11 @@ public class IonDataBundle implements IonBinary {
{ {
return backingMap.containsKey(key); return backingMap.containsKey(key);
} }
/** /**
* Check if a value is contained in the bundle * Check if a value is contained in the bundle
* *
* @param value value to check * @param value value to check
* @return true if this value is contained in the bundle * @return true if this value is contained in the bundle
*/ */
@ -59,11 +59,11 @@ public class IonDataBundle implements IonBinary {
{ {
return backingMap.containsValue(value); return backingMap.containsValue(value);
} }
/** /**
* Get a map value * Get a map value
* *
* @param key key * @param key key
* @return a new Map with elements from that value * @return a new Map with elements from that value
*/ */
@ -71,11 +71,11 @@ public class IonDataBundle implements IonBinary {
{ {
return loadMap(key, new LinkedHashMap<K, V>()); return loadMap(key, new LinkedHashMap<K, V>());
} }
/** /**
* Clear & fill the provided map with elements from a map value * Clear & fill the provided map with elements from a map value
* *
* @param key key * @param key key
* @param filled Map to fill * @param filled Map to fill
*/ */
@ -87,11 +87,11 @@ public class IonDataBundle implements IonBinary {
imw.fill(filled); imw.fill(filled);
return filled; return filled;
} }
/** /**
* Get a sequence value * Get a sequence value
* *
* @param key key * @param key key
* @return a new Collection with elements from that value * @return a new Collection with elements from that value
*/ */
@ -99,11 +99,11 @@ public class IonDataBundle implements IonBinary {
{ {
return loadSequence(key, new ArrayList<E>()); return loadSequence(key, new ArrayList<E>());
} }
/** /**
* Clear & fill the provided Collection with elements from a sequence value * Clear & fill the provided Collection with elements from a sequence value
* *
* @param key key * @param key key
* @param filled Collection to fill * @param filled Collection to fill
* @return the filled collection * @return the filled collection
@ -114,15 +114,15 @@ public class IonDataBundle implements IonBinary {
if (isw == null) throw new RuntimeException("No such key: " + key); if (isw == null) throw new RuntimeException("No such key: " + key);
filled.clear(); filled.clear();
isw.fill(filled); isw.fill(filled);
return filled; return filled;
} }
/** /**
* Load a bundled object from a bundle value.<br> * Load a bundled object from a bundle value.<br>
* The object does not have to be registered. * The object does not have to be registered.
* *
* @param key key * @param key key
* @param loaded loaded object * @param loaded loaded object
* @return the loaded object * @return the loaded object
@ -131,17 +131,17 @@ public class IonDataBundle implements IonBinary {
{ {
final IonDataBundle bu = get(key, null); final IonDataBundle bu = get(key, null);
if (bu == null) throw new RuntimeException("No such key: " + key); if (bu == null) throw new RuntimeException("No such key: " + key);
loaded.load(bu); loaded.load(bu);
return loaded; return loaded;
} }
/** /**
* Save a bundled object to a bundle value.<br> * Save a bundled object to a bundle value.<br>
* The object does not have to be registered. * The object does not have to be registered.
* *
* @param key key * @param key key
* @param saved saved object * @param saved saved object
*/ */
@ -151,11 +151,11 @@ public class IonDataBundle implements IonBinary {
saved.save(bu); saved.save(bu);
put(key, bu); put(key, bu);
} }
/** /**
* Get value, or fallback (if none found of with bad type). * Get value, or fallback (if none found of with bad type).
* *
* @param key * @param key
* @param fallback value * @param fallback value
* @return value * @return value
@ -170,11 +170,11 @@ public class IonDataBundle implements IonBinary {
return fallback; return fallback;
} }
} }
/** /**
* Get value, or null (if none found of with bad type). * Get value, or null (if none found of with bad type).
* *
* @param key * @param key
* @return value * @return value
*/ */
@ -182,127 +182,127 @@ public class IonDataBundle implements IonBinary {
{ {
return get(key, (T) null); return get(key, (T) null);
} }
public void put(String key, Object value) public void put(String key, Object value)
{ {
if (key == null || value == null) return; if (key == null || value == null) return;
if (!Ion.isRegistered(value)) throw new IllegalArgumentException("Cannot add to bundle, not registered: " + value); if (!Ion.isRegistered(value)) throw new IllegalArgumentException("Cannot add to bundle, not registered: " + value);
backingMap.put(key, value); backingMap.put(key, value);
} }
public void put(String key, boolean value) public void put(String key, boolean value)
{ {
backingMap.put(key, value); backingMap.put(key, value);
} }
public void put(String key, byte value) public void put(String key, byte value)
{ {
backingMap.put(key, value); backingMap.put(key, value);
} }
public void put(String key, char value) public void put(String key, char value)
{ {
backingMap.put(key, value); backingMap.put(key, value);
} }
public void put(String key, short value) public void put(String key, short value)
{ {
backingMap.put(key, value); backingMap.put(key, value);
} }
public void put(String key, int value) public void put(String key, int value)
{ {
backingMap.put(key, value); backingMap.put(key, value);
} }
public void put(String key, long value) public void put(String key, long value)
{ {
backingMap.put(key, value); backingMap.put(key, value);
} }
public void put(String key, double value) public void put(String key, double value)
{ {
backingMap.put(key, value); backingMap.put(key, value);
} }
public void put(String key, float value) public void put(String key, float value)
{ {
backingMap.put(key, value); backingMap.put(key, value);
} }
public void put(String key, String value) public void put(String key, String value)
{ {
backingMap.put(key, value); backingMap.put(key, value);
} }
public void put(String key, boolean[] value) public void put(String key, boolean[] value)
{ {
backingMap.put(key, value); backingMap.put(key, value);
} }
public void put(String key, char[] value) public void put(String key, char[] value)
{ {
backingMap.put(key, value); backingMap.put(key, value);
} }
public void put(String key, short[] value) public void put(String key, short[] value)
{ {
backingMap.put(key, value); backingMap.put(key, value);
} }
public void put(String key, int[] value) public void put(String key, int[] value)
{ {
backingMap.put(key, value); backingMap.put(key, value);
} }
public void put(String key, long[] value) public void put(String key, long[] value)
{ {
backingMap.put(key, value); backingMap.put(key, value);
} }
public void put(String key, double[] value) public void put(String key, double[] value)
{ {
backingMap.put(key, value); backingMap.put(key, value);
} }
public void put(String key, float[] value) public void put(String key, float[] value)
{ {
backingMap.put(key, value); backingMap.put(key, value);
} }
public void put(String key, String[] value) public void put(String key, String[] value)
{ {
backingMap.put(key, value); backingMap.put(key, value);
} }
public void put(String key, Object[] value) public void put(String key, Object[] value)
{ {
backingMap.put(key, value); backingMap.put(key, value);
} }
/** /**
* Put a sequence to the bundle. * Put a sequence to the bundle.
* *
* @param key key * @param key key
* @param c value (Collection) * @param c value (Collection)
*/ */
@ -311,11 +311,11 @@ public class IonDataBundle implements IonBinary {
{ {
backingMap.put(key, new IonSequenceWrapper(c)); backingMap.put(key, new IonSequenceWrapper(c));
} }
/** /**
* Put a map to the bundle. * Put a map to the bundle.
* *
* @param key key * @param key key
* @param m value (Map) * @param m value (Map)
*/ */
@ -324,44 +324,44 @@ public class IonDataBundle implements IonBinary {
{ {
backingMap.put(key, new IonMapWrapper(m)); backingMap.put(key, new IonMapWrapper(m));
} }
@Override @Override
public void load(IonInput in) throws IOException public void load(IonInput in) throws IOException
{ {
in.readMap(backingMap); in.readMap(backingMap);
} }
@Override @Override
public void save(IonOutput out) throws IOException public void save(IonOutput out) throws IOException
{ {
out.writeMap(backingMap); out.writeMap(backingMap);
} }
/** /**
* Get number of elements in the bundle * Get number of elements in the bundle
* *
* @return size * @return size
*/ */
public int size() public int size()
{ {
return backingMap.size(); return backingMap.size();
} }
/** /**
* Check whether the bundle is empty * Check whether the bundle is empty
* *
* @return true if empty * @return true if empty
*/ */
public boolean isEmpty() public boolean isEmpty()
{ {
return backingMap.isEmpty(); return backingMap.isEmpty();
} }
/** /**
* Remove all elements * Remove all elements
*/ */
@ -369,11 +369,11 @@ public class IonDataBundle implements IonBinary {
{ {
backingMap.clear(); backingMap.clear();
} }
/** /**
* Remove a value by key * Remove a value by key
* *
* @param key key to remove * @param key key to remove
* @return the removed object * @return the removed object
*/ */
@ -381,26 +381,26 @@ public class IonDataBundle implements IonBinary {
{ {
return backingMap.remove(key); return backingMap.remove(key);
} }
/** /**
* Put all from another bundle * Put all from another bundle
* *
* @param anotherBundle another bundle * @param anotherBundle another bundle
*/ */
public void putAll(IonDataBundle anotherBundle) public void putAll(IonDataBundle anotherBundle)
{ {
backingMap.putAll(anotherBundle.backingMap); backingMap.putAll(anotherBundle.backingMap);
} }
@Override @Override
public String toString() public String toString()
{ {
return backingMap.toString(); return backingMap.toString();
} }
@Override @Override
public int hashCode() public int hashCode()
{ {
@ -409,8 +409,8 @@ public class IonDataBundle implements IonBinary {
result = prime * result + ((backingMap == null) ? 0 : backingMap.hashCode()); result = prime * result + ((backingMap == null) ? 0 : backingMap.hashCode());
return result; return result;
} }
@Override @Override
public boolean equals(Object obj) public boolean equals(Object obj)
{ {

@ -1,7 +1,14 @@
package mightypork.utils.ion; package mightypork.utils.ion;
import java.io.*; import java.io.Closeable;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
@ -12,31 +19,33 @@ import mightypork.utils.exceptions.CorruptDataException;
/** /**
* Ion input stream * Ion input stream
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class IonInput implements Closeable { public class IonInput implements Closeable {
private final DataInput in; private final DataInput in;
private final InputStream stream; private final InputStream stream;
@SuppressWarnings("resource") @SuppressWarnings("resource")
public IonInput(File inFile) throws FileNotFoundException { public IonInput(File inFile) throws FileNotFoundException
{
this(new FileInputStream(inFile)); this(new FileInputStream(inFile));
} }
public IonInput(InputStream in) { public IonInput(InputStream in)
{
this.stream = in; this.stream = in;
this.in = new DataInputStream(in); this.in = new DataInputStream(in);
} }
/** /**
* Read int 0-255. Suitable when the int was written using * Read int 0-255. Suitable when the int was written using
* <code>writeIntByte()</code> method. * <code>writeIntByte()</code> method.
* *
* @return int * @return int
* @throws IOException * @throws IOException
*/ */
@ -44,12 +53,12 @@ public class IonInput implements Closeable {
{ {
return in.readUnsignedByte(); return in.readUnsignedByte();
} }
/** /**
* Read an int 0-65535. Suitable when the int was written using * Read an int 0-65535. Suitable when the int was written using
* <code>writeIntShort()</code> method. * <code>writeIntShort()</code> method.
* *
* @return int * @return int
* @throws IOException * @throws IOException
*/ */
@ -57,62 +66,62 @@ public class IonInput implements Closeable {
{ {
return in.readUnsignedShort(); return in.readUnsignedShort();
} }
public boolean readBoolean() throws IOException public boolean readBoolean() throws IOException
{ {
return in.readBoolean(); return in.readBoolean();
} }
public byte readByte() throws IOException public byte readByte() throws IOException
{ {
return in.readByte(); return in.readByte();
} }
public short readShort() throws IOException public short readShort() throws IOException
{ {
return in.readShort(); return in.readShort();
} }
public char readChar() throws IOException public char readChar() throws IOException
{ {
return in.readChar(); return in.readChar();
} }
public int readInt() throws IOException public int readInt() throws IOException
{ {
return in.readInt(); return in.readInt();
} }
public long readLong() throws IOException public long readLong() throws IOException
{ {
return in.readLong(); return in.readLong();
} }
public float readFloat() throws IOException public float readFloat() throws IOException
{ {
return in.readFloat(); return in.readFloat();
} }
public double readDouble() throws IOException public double readDouble() throws IOException
{ {
return in.readDouble(); return in.readDouble();
} }
public String readString() throws IOException public String readString() throws IOException
{ {
return in.readUTF(); return in.readUTF();
} }
public boolean[] readBooleans() throws IOException public boolean[] readBooleans() throws IOException
{ {
final int length = readLength(); final int length = readLength();
@ -122,8 +131,8 @@ public class IonInput implements Closeable {
} }
return arr; return arr;
} }
public byte[] readBytes() throws IOException public byte[] readBytes() throws IOException
{ {
final int length = readLength(); final int length = readLength();
@ -133,8 +142,8 @@ public class IonInput implements Closeable {
} }
return arr; return arr;
} }
public char[] readChars() throws IOException public char[] readChars() throws IOException
{ {
final int length = readLength(); final int length = readLength();
@ -144,8 +153,8 @@ public class IonInput implements Closeable {
} }
return arr; return arr;
} }
public short[] readShorts() throws IOException public short[] readShorts() throws IOException
{ {
final int length = readLength(); final int length = readLength();
@ -155,8 +164,8 @@ public class IonInput implements Closeable {
} }
return arr; return arr;
} }
public int[] readInts() throws IOException public int[] readInts() throws IOException
{ {
final int length = readLength(); final int length = readLength();
@ -166,8 +175,8 @@ public class IonInput implements Closeable {
} }
return arr; return arr;
} }
public long[] readLongs() throws IOException public long[] readLongs() throws IOException
{ {
final int length = readLength(); final int length = readLength();
@ -177,8 +186,8 @@ public class IonInput implements Closeable {
} }
return arr; return arr;
} }
public float[] readFloats() throws IOException public float[] readFloats() throws IOException
{ {
final int length = readLength(); final int length = readLength();
@ -188,8 +197,8 @@ public class IonInput implements Closeable {
} }
return arr; return arr;
} }
public double[] readDoubles() throws IOException public double[] readDoubles() throws IOException
{ {
final int length = readLength(); final int length = readLength();
@ -199,8 +208,8 @@ public class IonInput implements Closeable {
} }
return arr; return arr;
} }
public String[] readStrings() throws IOException public String[] readStrings() throws IOException
{ {
final int length = readLength(); final int length = readLength();
@ -210,8 +219,8 @@ public class IonInput implements Closeable {
} }
return arr; return arr;
} }
public Object[] readObjects() throws IOException public Object[] readObjects() throws IOException
{ {
final int length = readLength(); final int length = readLength();
@ -221,8 +230,8 @@ public class IonInput implements Closeable {
} }
return arr; return arr;
} }
/** /**
* Read bundle without a mark * Read bundle without a mark
*/ */
@ -232,8 +241,8 @@ public class IonInput implements Closeable {
ib.load(this); ib.load(this);
return ib; return ib;
} }
/** /**
* Read bundle without a mark, load into a provided one * Read bundle without a mark, load into a provided one
*/ */
@ -242,20 +251,20 @@ public class IonInput implements Closeable {
filled.clear(); filled.clear();
filled.load(this); filled.load(this);
} }
private int readMark() throws IOException private int readMark() throws IOException
{ {
return readIntByte(); return readIntByte();
} }
private int readLength() throws IOException private int readLength() throws IOException
{ {
return readInt(); return readInt();
} }
/** /**
* <p> * <p>
* Read object based on mark; if null mark is found, returns default value. * Read object based on mark; if null mark is found, returns default value.
@ -264,7 +273,7 @@ public class IonInput implements Closeable {
* If, however, an object of invalid or different type is found, an * If, however, an object of invalid or different type is found, an
* exception will be thrown. * exception will be thrown.
* </p> * </p>
* *
* @param def default value. * @param def default value.
* @return the loaded object * @return the loaded object
* @throws CorruptDataException * @throws CorruptDataException
@ -279,130 +288,130 @@ public class IonInput implements Closeable {
throw new CorruptDataException("Could not load object.", e); throw new CorruptDataException("Could not load object.", e);
} }
} }
/** /**
* Read single object, preceded by a mark. * Read single object, preceded by a mark.
* *
* @return the loaded object * @return the loaded object
* @throws IOException * @throws IOException
*/ */
public Object readObject() throws IOException public Object readObject() throws IOException
{ {
final int mark = readMark(); final int mark = readMark();
try { try {
if (Ion.isMarkForBinary(mark)) { if (Ion.isMarkForBinary(mark)) {
IonBinary loaded; IonBinary loaded;
loaded = (IonBinary) Ion.getClassForMark(mark).newInstance(); loaded = (IonBinary) Ion.getClassForMark(mark).newInstance();
loaded.load(this); loaded.load(this);
return loaded; return loaded;
} }
if (Ion.isMarkForBundled(mark)) { if (Ion.isMarkForBundled(mark)) {
IonBundled loaded; IonBundled loaded;
loaded = (IonBundled) Ion.getClassForMark(mark).newInstance(); loaded = (IonBundled) Ion.getClassForMark(mark).newInstance();
final IonDataBundle ib = readBundle(); final IonDataBundle ib = readBundle();
loaded.load(ib); loaded.load(ib);
return loaded; return loaded;
} }
if (Ion.isMarkForIndirectBundled(mark)) { if (Ion.isMarkForIndirectBundled(mark)) {
final IonizerBundled<?> ionizer = Ion.getIonizerBundledForClass(Ion.getClassForMark(mark)); final IonizerBundled<?> ionizer = Ion.getIonizerBundledForClass(Ion.getClassForMark(mark));
return ionizer.load(readBundle()); return ionizer.load(readBundle());
} }
if (Ion.isMarkForIndirectBinary(mark)) { if (Ion.isMarkForIndirectBinary(mark)) {
final IonizerBinary<?> ionizer = Ion.getIonizerBinaryForClass(Ion.getClassForMark(mark)); final IonizerBinary<?> ionizer = Ion.getIonizerBinaryForClass(Ion.getClassForMark(mark));
return ionizer.load(this); return ionizer.load(this);
} }
} catch (final Exception e) { } catch (final Exception e) {
throw new RuntimeException("Could not load object for mark: " + mark, e); throw new RuntimeException("Could not load object for mark: " + mark, e);
} }
switch (mark) { switch (mark) {
case Ion.NULL: case Ion.NULL:
return null; return null;
case Ion.BOOLEAN: case Ion.BOOLEAN:
return readBoolean(); return readBoolean();
case Ion.BYTE: case Ion.BYTE:
return readByte(); return readByte();
case Ion.CHAR: case Ion.CHAR:
return readChar(); return readChar();
case Ion.SHORT: case Ion.SHORT:
return readShort(); return readShort();
case Ion.INT: case Ion.INT:
return readInt(); return readInt();
case Ion.LONG: case Ion.LONG:
return readLong(); return readLong();
case Ion.FLOAT: case Ion.FLOAT:
return readFloat(); return readFloat();
case Ion.DOUBLE: case Ion.DOUBLE:
return readDouble(); return readDouble();
case Ion.STRING: case Ion.STRING:
return readString(); return readString();
case Ion.BOOLEAN_ARRAY: case Ion.BOOLEAN_ARRAY:
return readBooleans(); return readBooleans();
case Ion.BYTE_ARRAY: case Ion.BYTE_ARRAY:
return readBytes(); return readBytes();
case Ion.CHAR_ARRAY: case Ion.CHAR_ARRAY:
return readChars(); return readChars();
case Ion.SHORT_ARRAY: case Ion.SHORT_ARRAY:
return readShorts(); return readShorts();
case Ion.INT_ARRAY: case Ion.INT_ARRAY:
return readInts(); return readInts();
case Ion.LONG_ARRAY: case Ion.LONG_ARRAY:
return readLongs(); return readLongs();
case Ion.FLOAT_ARRAY: case Ion.FLOAT_ARRAY:
return readFloats(); return readFloats();
case Ion.DOUBLE_ARRAY: case Ion.DOUBLE_ARRAY:
return readDoubles(); return readDoubles();
case Ion.STRING_ARRAY: case Ion.STRING_ARRAY:
return readStrings(); return readStrings();
case Ion.OBJECT_ARRAY: case Ion.OBJECT_ARRAY:
return readObjects(); return readObjects();
case Ion.MAP: case Ion.MAP:
return readMap(); return readMap();
case Ion.SEQUENCE: case Ion.SEQUENCE:
return readSequence(); return readSequence();
default: default:
throw new CorruptDataException("Invalid mark: " + mark); throw new CorruptDataException("Invalid mark: " + mark);
} }
} }
/** /**
* Reads mark and returns true if the mark is ENTRY, false if the mark is * Reads mark and returns true if the mark is ENTRY, false if the mark is
* END. Throws an exception otherwise. * END. Throws an exception otherwise.
* *
* @return mark was ENTRY * @return mark was ENTRY
* @throws IOException when the mark is neither ENTRY or END. * @throws IOException when the mark is neither ENTRY or END.
*/ */
@ -411,14 +420,14 @@ public class IonInput implements Closeable {
final int mark = readMark(); final int mark = readMark();
if (mark == Ion.ENTRY) return true; if (mark == Ion.ENTRY) return true;
if (mark == Ion.END) return false; if (mark == Ion.END) return false;
throw new CorruptDataException("Unexpected mark in sequence: " + mark); throw new CorruptDataException("Unexpected mark in sequence: " + mark);
} }
/** /**
* Read a sequence of elements into an ArrayList * Read a sequence of elements into an ArrayList
* *
* @return the collection * @return the collection
* @throws IOException * @throws IOException
*/ */
@ -426,11 +435,11 @@ public class IonInput implements Closeable {
{ {
return readSequence(new ArrayList<T>()); return readSequence(new ArrayList<T>());
} }
/** /**
* Load entries into a collection. The collection is cleaned first. * Load entries into a collection. The collection is cleaned first.
* *
* @param filled collection to populate * @param filled collection to populate
* @return the collection * @return the collection
* @throws IOException * @throws IOException
@ -448,11 +457,11 @@ public class IonInput implements Closeable {
throw new CorruptDataException("Unexpected element type in sequence.", e); throw new CorruptDataException("Unexpected element type in sequence.", e);
} }
} }
/** /**
* Read element pairs into a HashMap * Read element pairs into a HashMap
* *
* @return the map * @return the map
* @throws IOException * @throws IOException
*/ */
@ -460,11 +469,11 @@ public class IonInput implements Closeable {
{ {
return readMap(new HashMap<K, V>()); return readMap(new HashMap<K, V>());
} }
/** /**
* Load data into a map. The map is cleaned first. * Load data into a map. The map is cleaned first.
* *
* @param filled filled map * @param filled filled map
* @return the map * @return the map
* @throws IOException * @throws IOException
@ -477,7 +486,7 @@ public class IonInput implements Closeable {
while (hasNextEntry()) { while (hasNextEntry()) {
final K key = (K) readObject(); final K key = (K) readObject();
final V value = (V) readObject(); final V value = (V) readObject();
filled.put(key, value); filled.put(key, value);
} }
return filled; return filled;
@ -485,8 +494,8 @@ public class IonInput implements Closeable {
throw new CorruptDataException("Unexpected element type in map.", e); throw new CorruptDataException("Unexpected element type in map.", e);
} }
} }
@Override @Override
public void close() throws IOException public void close() throws IOException
{ {

@ -8,45 +8,47 @@ import java.util.Map;
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
class IonMapWrapper implements IonBinary { class IonMapWrapper implements IonBinary {
private final Map map; private final Map map;
public IonMapWrapper() { public IonMapWrapper()
{
map = new LinkedHashMap<>(); map = new LinkedHashMap<>();
} }
public IonMapWrapper(Map saved) { public IonMapWrapper(Map saved)
{
map = saved; map = saved;
} }
@Override @Override
public void load(IonInput in) throws IOException public void load(IonInput in) throws IOException
{ {
map.clear(); map.clear();
in.readMap(map); in.readMap(map);
} }
@Override @Override
public void save(IonOutput out) throws IOException public void save(IonOutput out) throws IOException
{ {
out.writeMap(map); out.writeMap(map);
} }
public void fill(Map o) public void fill(Map o)
{ {
o.clear(); o.clear();
o.putAll(map); o.putAll(map);
} }
public Map getMap() public Map getMap()
{ {
return map; return map;
} }
} }

@ -1,7 +1,14 @@
package mightypork.utils.ion; package mightypork.utils.ion;
import java.io.*; import java.io.Closeable;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -9,99 +16,101 @@ import java.util.Map.Entry;
/** /**
* Ion output stream * Ion output stream
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class IonOutput implements Closeable { public class IonOutput implements Closeable {
private final DataOutput out; private final DataOutput out;
private final OutputStream stream; private final OutputStream stream;
@SuppressWarnings("resource") @SuppressWarnings("resource")
public IonOutput(File outFile) throws FileNotFoundException { public IonOutput(File outFile) throws FileNotFoundException
{
this(new FileOutputStream(outFile)); this(new FileOutputStream(outFile));
} }
public IonOutput(OutputStream out) { public IonOutput(OutputStream out)
{
this.stream = out; this.stream = out;
this.out = new DataOutputStream(out); this.out = new DataOutputStream(out);
} }
public void writeBoolean(boolean a) throws IOException public void writeBoolean(boolean a) throws IOException
{ {
out.writeBoolean(a); out.writeBoolean(a);
} }
public void writeByte(int a) throws IOException public void writeByte(int a) throws IOException
{ {
out.writeByte(a); out.writeByte(a);
} }
public void writeShort(int a) throws IOException public void writeShort(int a) throws IOException
{ {
out.writeShort(a); out.writeShort(a);
} }
public void writeChar(int a) throws IOException public void writeChar(int a) throws IOException
{ {
out.writeChar(a); out.writeChar(a);
} }
public void writeInt(int a) throws IOException public void writeInt(int a) throws IOException
{ {
out.writeInt(a); out.writeInt(a);
} }
public void writeIntShort(int a) throws IOException public void writeIntShort(int a) throws IOException
{ {
out.writeShort(a); out.writeShort(a);
} }
public void writeIntByte(int a) throws IOException public void writeIntByte(int a) throws IOException
{ {
out.writeByte(a); out.writeByte(a);
} }
public void writeLong(long a) throws IOException public void writeLong(long a) throws IOException
{ {
out.writeLong(a); out.writeLong(a);
} }
public void writeFloat(float a) throws IOException public void writeFloat(float a) throws IOException
{ {
out.writeFloat(a); out.writeFloat(a);
} }
public void writeDouble(double a) throws IOException public void writeDouble(double a) throws IOException
{ {
out.writeDouble(a); out.writeDouble(a);
} }
public void writeBytes(String a) throws IOException public void writeBytes(String a) throws IOException
{ {
out.writeBytes(a); out.writeBytes(a);
} }
public void writeString(String a) throws IOException public void writeString(String a) throws IOException
{ {
out.writeUTF(a); out.writeUTF(a);
} }
public void writeBooleans(boolean[] arr) throws IOException public void writeBooleans(boolean[] arr) throws IOException
{ {
writeLength(arr.length); writeLength(arr.length);
@ -109,8 +118,8 @@ public class IonOutput implements Closeable {
out.writeBoolean(a); out.writeBoolean(a);
} }
} }
public void writeBytes(byte[] arr) throws IOException public void writeBytes(byte[] arr) throws IOException
{ {
writeLength(arr.length); writeLength(arr.length);
@ -118,8 +127,8 @@ public class IonOutput implements Closeable {
out.writeByte(a); out.writeByte(a);
} }
} }
public void writeChars(char[] arr) throws IOException public void writeChars(char[] arr) throws IOException
{ {
writeLength(arr.length); writeLength(arr.length);
@ -127,8 +136,8 @@ public class IonOutput implements Closeable {
out.writeChar(a); out.writeChar(a);
} }
} }
public void writeShorts(short[] arr) throws IOException public void writeShorts(short[] arr) throws IOException
{ {
writeLength(arr.length); writeLength(arr.length);
@ -136,8 +145,8 @@ public class IonOutput implements Closeable {
out.writeShort(a); out.writeShort(a);
} }
} }
public void writeInts(int[] arr) throws IOException public void writeInts(int[] arr) throws IOException
{ {
writeLength(arr.length); writeLength(arr.length);
@ -145,8 +154,8 @@ public class IonOutput implements Closeable {
out.writeInt(a); out.writeInt(a);
} }
} }
public void writeLongs(long[] arr) throws IOException public void writeLongs(long[] arr) throws IOException
{ {
writeLength(arr.length); writeLength(arr.length);
@ -154,8 +163,8 @@ public class IonOutput implements Closeable {
out.writeLong(a); out.writeLong(a);
} }
} }
public void writeFloats(float[] arr) throws IOException public void writeFloats(float[] arr) throws IOException
{ {
writeLength(arr.length); writeLength(arr.length);
@ -163,8 +172,8 @@ public class IonOutput implements Closeable {
out.writeFloat(a); out.writeFloat(a);
} }
} }
public void writeDoubles(double[] arr) throws IOException public void writeDoubles(double[] arr) throws IOException
{ {
writeLength(arr.length); writeLength(arr.length);
@ -172,8 +181,8 @@ public class IonOutput implements Closeable {
out.writeDouble(a); out.writeDouble(a);
} }
} }
public void writeStrings(String[] arr) throws IOException public void writeStrings(String[] arr) throws IOException
{ {
writeLength(arr.length); writeLength(arr.length);
@ -181,8 +190,8 @@ public class IonOutput implements Closeable {
out.writeUTF(a); out.writeUTF(a);
} }
} }
/** /**
* Write a bundle without a mark * Write a bundle without a mark
*/ */
@ -190,11 +199,11 @@ public class IonOutput implements Closeable {
{ {
bundle.save(this); bundle.save(this);
} }
/** /**
* Write array of objects. Works with all that is supported by writeObject() * Write array of objects. Works with all that is supported by writeObject()
* *
* @param arr array to write * @param arr array to write
* @throws IOException on IO error or on invalid object type. * @throws IOException on IO error or on invalid object type.
*/ */
@ -205,8 +214,8 @@ public class IonOutput implements Closeable {
writeObject(a); writeObject(a);
} }
} }
public <T> void writeSequence(Collection<T> sequence) throws IOException public <T> void writeSequence(Collection<T> sequence) throws IOException
{ {
for (final T element : sequence) { for (final T element : sequence) {
@ -215,51 +224,51 @@ public class IonOutput implements Closeable {
} }
endSequence(); endSequence();
} }
public <K, V> void writeMap(Map<K, V> map) throws IOException public <K, V> void writeMap(Map<K, V> map) throws IOException
{ {
for (final Entry<K, V> e : map.entrySet()) { for (final Entry<K, V> e : map.entrySet()) {
if (e.getValue() == null) { if (e.getValue() == null) {
continue; continue;
} }
startEntry(); startEntry();
writeObject(e.getKey()); writeObject(e.getKey());
writeObject(e.getValue()); writeObject(e.getValue());
} }
endSequence(); endSequence();
} }
public void endSequence() throws IOException public void endSequence() throws IOException
{ {
writeMark(Ion.END); writeMark(Ion.END);
} }
public void startEntry() throws IOException public void startEntry() throws IOException
{ {
writeMark(Ion.ENTRY); writeMark(Ion.ENTRY);
} }
private void writeMark(int mark) throws IOException private void writeMark(int mark) throws IOException
{ {
writeIntByte(mark); writeIntByte(mark);
} }
private void writeLength(int length) throws IOException private void writeLength(int length) throws IOException
{ {
writeInt(length); writeInt(length);
} }
/** /**
* Write an object. Supported are built-in types and types registered to * Write an object. Supported are built-in types and types registered to
* Ion. * Ion.
* *
* @param obj obj to write * @param obj obj to write
* @throws IOException on IO error or invalid object type. * @throws IOException on IO error or invalid object type.
*/ */
@ -269,177 +278,177 @@ public class IonOutput implements Closeable {
writeMark(Ion.NULL); writeMark(Ion.NULL);
return; return;
} }
if (obj instanceof IonBinary) { if (obj instanceof IonBinary) {
final IonBinary iObj = (IonBinary) obj; final IonBinary iObj = (IonBinary) obj;
writeMark(Ion.getMark(obj)); writeMark(Ion.getMark(obj));
iObj.save(this); iObj.save(this);
return; return;
} }
if (obj instanceof IonBundled) { if (obj instanceof IonBundled) {
final IonBundled iObj = (IonBundled) obj; final IonBundled iObj = (IonBundled) obj;
writeMark(Ion.getMark(obj)); writeMark(Ion.getMark(obj));
final IonDataBundle bundle = new IonDataBundle(); final IonDataBundle bundle = new IonDataBundle();
iObj.save(bundle); iObj.save(bundle);
writeBundle(bundle); writeBundle(bundle);
return; return;
} }
if (Ion.isObjectIndirectBundled(obj)) { if (Ion.isObjectIndirectBundled(obj)) {
final IonizerBundled<?> ionizer = Ion.getIonizerBundledForClass(obj.getClass()); final IonizerBundled<?> ionizer = Ion.getIonizerBundledForClass(obj.getClass());
writeMark(Ion.getMark(obj)); writeMark(Ion.getMark(obj));
final IonDataBundle bundle = new IonDataBundle(); final IonDataBundle bundle = new IonDataBundle();
ionizer._save(obj, bundle); ionizer._save(obj, bundle);
writeBundle(bundle); writeBundle(bundle);
return; return;
} }
if (Ion.isObjectIndirectBinary(obj)) { if (Ion.isObjectIndirectBinary(obj)) {
final IonizerBinary<?> ionizer = Ion.getIonizerBinaryForClass(obj.getClass()); final IonizerBinary<?> ionizer = Ion.getIonizerBinaryForClass(obj.getClass());
writeMark(Ion.getMark(obj)); writeMark(Ion.getMark(obj));
ionizer._save(obj, this); ionizer._save(obj, this);
return; return;
} }
if (obj instanceof Map) { if (obj instanceof Map) {
writeMark(Ion.MAP); writeMark(Ion.MAP);
writeMap((Map<?, ?>) obj); writeMap((Map<?, ?>) obj);
return; return;
} }
if (obj instanceof Collection) { if (obj instanceof Collection) {
writeMark(Ion.SEQUENCE); writeMark(Ion.SEQUENCE);
writeSequence((Collection<?>) obj); writeSequence((Collection<?>) obj);
return; return;
} }
if (obj instanceof Boolean) { if (obj instanceof Boolean) {
writeMark(Ion.BOOLEAN); writeMark(Ion.BOOLEAN);
writeBoolean((Boolean) obj); writeBoolean((Boolean) obj);
return; return;
} }
if (obj instanceof Byte) { if (obj instanceof Byte) {
writeMark(Ion.BYTE); writeMark(Ion.BYTE);
writeByte((Byte) obj); writeByte((Byte) obj);
return; return;
} }
if (obj instanceof Character) { if (obj instanceof Character) {
writeMark(Ion.CHAR); writeMark(Ion.CHAR);
writeChar((Character) obj); writeChar((Character) obj);
return; return;
} }
if (obj instanceof Short) { if (obj instanceof Short) {
writeMark(Ion.SHORT); writeMark(Ion.SHORT);
writeShort((Short) obj); writeShort((Short) obj);
return; return;
} }
if (obj instanceof Integer) { if (obj instanceof Integer) {
writeMark(Ion.INT); writeMark(Ion.INT);
writeInt((Integer) obj); writeInt((Integer) obj);
return; return;
} }
if (obj instanceof Long) { if (obj instanceof Long) {
writeMark(Ion.LONG); writeMark(Ion.LONG);
writeLong((Long) obj); writeLong((Long) obj);
return; return;
} }
if (obj instanceof Float) { if (obj instanceof Float) {
writeMark(Ion.FLOAT); writeMark(Ion.FLOAT);
writeFloat((Float) obj); writeFloat((Float) obj);
return; return;
} }
if (obj instanceof Double) { if (obj instanceof Double) {
writeMark(Ion.DOUBLE); writeMark(Ion.DOUBLE);
writeDouble((Double) obj); writeDouble((Double) obj);
return; return;
} }
if (obj instanceof String) { if (obj instanceof String) {
writeMark(Ion.STRING); writeMark(Ion.STRING);
writeString((String) obj); writeString((String) obj);
return; return;
} }
if (obj instanceof boolean[]) { if (obj instanceof boolean[]) {
writeMark(Ion.BOOLEAN_ARRAY); writeMark(Ion.BOOLEAN_ARRAY);
writeBooleans((boolean[]) obj); writeBooleans((boolean[]) obj);
return; return;
} }
if (obj instanceof byte[]) { if (obj instanceof byte[]) {
writeMark(Ion.BYTE_ARRAY); writeMark(Ion.BYTE_ARRAY);
writeBytes((byte[]) obj); writeBytes((byte[]) obj);
return; return;
} }
if (obj instanceof char[]) { if (obj instanceof char[]) {
writeMark(Ion.CHAR_ARRAY); writeMark(Ion.CHAR_ARRAY);
writeChars((char[]) obj); writeChars((char[]) obj);
return; return;
} }
if (obj instanceof short[]) { if (obj instanceof short[]) {
writeMark(Ion.SHORT_ARRAY); writeMark(Ion.SHORT_ARRAY);
writeShorts((short[]) obj); writeShorts((short[]) obj);
return; return;
} }
if (obj instanceof int[]) { if (obj instanceof int[]) {
writeMark(Ion.INT_ARRAY); writeMark(Ion.INT_ARRAY);
writeInts((int[]) obj); writeInts((int[]) obj);
return; return;
} }
if (obj instanceof long[]) { if (obj instanceof long[]) {
writeMark(Ion.LONG_ARRAY); writeMark(Ion.LONG_ARRAY);
writeLongs((long[]) obj); writeLongs((long[]) obj);
return; return;
} }
if (obj instanceof float[]) { if (obj instanceof float[]) {
writeMark(Ion.FLOAT_ARRAY); writeMark(Ion.FLOAT_ARRAY);
writeFloats((float[]) obj); writeFloats((float[]) obj);
return; return;
} }
if (obj instanceof double[]) { if (obj instanceof double[]) {
writeMark(Ion.DOUBLE_ARRAY); writeMark(Ion.DOUBLE_ARRAY);
writeDoubles((double[]) obj); writeDoubles((double[]) obj);
return; return;
} }
if (obj instanceof String[]) { if (obj instanceof String[]) {
writeMark(Ion.STRING_ARRAY); writeMark(Ion.STRING_ARRAY);
writeStrings((String[]) obj); writeStrings((String[]) obj);
return; return;
} }
if (obj instanceof Object[]) { if (obj instanceof Object[]) {
writeMark(Ion.OBJECT_ARRAY); writeMark(Ion.OBJECT_ARRAY);
writeObjects((Object[]) obj); writeObjects((Object[]) obj);
return; return;
} }
throw new IOException("Object " + obj + " could not be be written to stream."); throw new IOException("Object " + obj + " could not be be written to stream.");
} }
@Override @Override
public void close() throws IOException public void close() throws IOException
{ {

@ -8,45 +8,47 @@ import java.util.Collection;
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
class IonSequenceWrapper implements IonBinary { class IonSequenceWrapper implements IonBinary {
private Collection collection = new ArrayList(); private Collection collection = new ArrayList();
public IonSequenceWrapper() { public IonSequenceWrapper()
{
collection = new ArrayList(); collection = new ArrayList();
} }
public IonSequenceWrapper(Collection saved) { public IonSequenceWrapper(Collection saved)
{
collection = saved; collection = saved;
} }
@Override @Override
public void load(IonInput in) throws IOException public void load(IonInput in) throws IOException
{ {
collection.clear(); collection.clear();
in.readSequence(collection); in.readSequence(collection);
} }
@Override @Override
public void save(IonOutput out) throws IOException public void save(IonOutput out) throws IOException
{ {
out.writeSequence(collection); out.writeSequence(collection);
} }
public void fill(Collection o) public void fill(Collection o)
{ {
o.clear(); o.clear();
o.addAll(collection); o.addAll(collection);
} }
public Collection getSequence() public Collection getSequence()
{ {
return collection; return collection;
} }
} }

@ -7,35 +7,35 @@ import java.io.IOException;
/** /**
* External ionizer using a IonOutput / IonInput - can be used if the data type * External ionizer using a IonOutput / IonInput - can be used if the data type
* cannot be modified to implement the proper interface * cannot be modified to implement the proper interface
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
* @param <T> * @param <T>
*/ */
public abstract class IonizerBinary<T> { public abstract class IonizerBinary<T> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final void _save(Object object, IonOutput out) throws IOException final void _save(Object object, IonOutput out) throws IOException
{ {
save((T) object, out); save((T) object, out);
} }
/** /**
* Save an object to ion output * Save an object to ion output
* *
* @param object object to save * @param object object to save
* @param out ion output * @param out ion output
* @throws IOException * @throws IOException
*/ */
public abstract void save(T object, IonOutput out) throws IOException; public abstract void save(T object, IonOutput out) throws IOException;
/** /**
* Load an object from ion input * Load an object from ion input
* *
* @param in ion input * @param in ion input
* @return the loaded object * @return the loaded object
*/ */
public abstract T load(IonInput in) throws IOException; public abstract T load(IonInput in) throws IOException;
} }

@ -4,34 +4,34 @@ package mightypork.utils.ion;
/** /**
* External ionizer using a data bundle - can be used if the data type cannot be * External ionizer using a data bundle - can be used if the data type cannot be
* modified to implement the proper interface * modified to implement the proper interface
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
* @param <T> * @param <T>
*/ */
public abstract class IonizerBundled<T> { public abstract class IonizerBundled<T> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final void _save(Object object, IonDataBundle out) final void _save(Object object, IonDataBundle out)
{ {
save((T) object, out); save((T) object, out);
} }
/** /**
* Save an object to data bundle * Save an object to data bundle
* *
* @param object object to save * @param object object to save
* @param out bundle to save to * @param out bundle to save to
*/ */
public abstract void save(T object, IonDataBundle out); public abstract void save(T object, IonDataBundle out);
/** /**
* Load an object from a bundle * Load an object from a bundle
* *
* @param in bundle to load from * @param in bundle to load from
* @return the loaded object * @return the loaded object
*/ */
public abstract T load(IonDataBundle in); public abstract T load(IonDataBundle in);
} }

@ -18,23 +18,23 @@ import mightypork.utils.string.StringUtil;
/** /**
* A log. * A log.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class Log { public class Log {
private static LogWriter main = null; private static LogWriter main = null;
private static boolean enabled = true; private static boolean enabled = true;
private static final LogMonitorStdout sysoMonitor = new LogMonitorStdout(); private static final LogMonitorStdout sysoMonitor = new LogMonitorStdout();
private static final long start_ms = System.currentTimeMillis(); private static final long start_ms = System.currentTimeMillis();
private static HashMap<String, SimpleLog> logs = new HashMap<>(); private static HashMap<String, SimpleLog> logs = new HashMap<>();
/** /**
* Create a logger. If another with the name already exists, it'll be * Create a logger. If another with the name already exists, it'll be
* retrieved instead of creating a new one. * retrieved instead of creating a new one.
* *
* @param logName log name (used for filename, should be application-unique) * @param logName log name (used for filename, should be application-unique)
* @param logFile log file; old logs will be kept here too. * @param logFile log file; old logs will be kept here too.
* @param oldLogsCount number of old logs to keep, -1 infinite, 0 none. * @param oldLogsCount number of old logs to keep, -1 infinite, 0 none.
@ -44,20 +44,20 @@ public class Log {
public static synchronized LogWriter create(String logName, File logFile, int oldLogsCount) public static synchronized LogWriter create(String logName, File logFile, int oldLogsCount)
{ {
if (logs.containsKey(logName)) return logs.get(logName); if (logs.containsKey(logName)) return logs.get(logName);
final ArchivingLog log = new ArchivingLog(logName, logFile, oldLogsCount); final ArchivingLog log = new ArchivingLog(logName, logFile, oldLogsCount);
log.init(); log.init();
logs.put(logName, log); logs.put(logName, log);
return log; return log;
} }
/** /**
* Create a logger. If another with the name already exists, it'll be * Create a logger. If another with the name already exists, it'll be
* retrieved instead of creating a new one. * retrieved instead of creating a new one.
* *
* @param logName log name (used for filename, must be application-unique) * @param logName log name (used for filename, must be application-unique)
* @param logFile log file; old logs will be kept here too. * @param logFile log file; old logs will be kept here too.
* @return the created Log instance * @return the created Log instance
@ -66,53 +66,53 @@ public class Log {
public static synchronized LogWriter create(String logName, File logFile) public static synchronized LogWriter create(String logName, File logFile)
{ {
if (logs.containsKey(logName)) return logs.get(logName); if (logs.containsKey(logName)) return logs.get(logName);
final SimpleLog log = new SimpleLog(logName, logFile); final SimpleLog log = new SimpleLog(logName, logFile);
log.init(); log.init();
logs.put(logName, log); logs.put(logName, log);
return log; return log;
} }
public static void setMainLogger(LogWriter log) public static void setMainLogger(LogWriter log)
{ {
main = log; main = log;
} }
public static LogWriter getMainLogger() public static LogWriter getMainLogger()
{ {
return main; return main;
} }
public static void addMonitor(LogMonitor mon) public static void addMonitor(LogMonitor mon)
{ {
assertInited(); assertInited();
main.addMonitor(mon); main.addMonitor(mon);
} }
public static void removeMonitor(LogMonitor mon) public static void removeMonitor(LogMonitor mon)
{ {
assertInited(); assertInited();
main.removeMonitor(mon); main.removeMonitor(mon);
} }
private static void assertInited() private static void assertInited()
{ {
if (main == null) throw new IllegalStateException("Main logger not initialized."); if (main == null) throw new IllegalStateException("Main logger not initialized.");
} }
/** /**
* Log a message * Log a message
* *
* @param level message level * @param level message level
* @param msg message text * @param msg message text
*/ */
@ -120,17 +120,17 @@ public class Log {
{ {
if (enabled) { if (enabled) {
sysoMonitor.onMessageLogged(level, formatMessage(level, msg, null, start_ms)); sysoMonitor.onMessageLogged(level, formatMessage(level, msg, null, start_ms));
if (main != null) { if (main != null) {
main.log(level, msg); main.log(level, msg);
} }
} }
} }
/** /**
* Log a message * Log a message
* *
* @param level message level * @param level message level
* @param msg message text * @param msg message text
* @param t thrown exception * @param t thrown exception
@ -139,83 +139,83 @@ public class Log {
{ {
if (enabled) { if (enabled) {
sysoMonitor.onMessageLogged(level, formatMessage(level, msg, t, start_ms)); sysoMonitor.onMessageLogged(level, formatMessage(level, msg, t, start_ms));
if (main != null) { if (main != null) {
main.log(level, msg, t); main.log(level, msg, t);
} }
} }
} }
/** /**
* Log FINE message * Log FINE message
* *
* @param msg message * @param msg message
*/ */
public static void f1(String msg) public static void f1(String msg)
{ {
log(Level.FINE, msg); log(Level.FINE, msg);
} }
/** /**
* Log FINER message * Log FINER message
* *
* @param msg message * @param msg message
*/ */
public static void f2(String msg) public static void f2(String msg)
{ {
log(Level.FINER, msg); log(Level.FINER, msg);
} }
/** /**
* Log FINEST message * Log FINEST message
* *
* @param msg message * @param msg message
*/ */
public static void f3(String msg) public static void f3(String msg)
{ {
log(Level.FINEST, msg); log(Level.FINEST, msg);
} }
/** /**
* Log INFO message * Log INFO message
* *
* @param msg message * @param msg message
*/ */
public static void i(String msg) public static void i(String msg)
{ {
log(Level.INFO, msg); log(Level.INFO, msg);
} }
/** /**
* Log WARNING message (less severe than ERROR) * Log WARNING message (less severe than ERROR)
* *
* @param msg message * @param msg message
*/ */
public static void w(String msg) public static void w(String msg)
{ {
log(Level.WARNING, msg); log(Level.WARNING, msg);
} }
/** /**
* Log ERROR message * Log ERROR message
* *
* @param msg message * @param msg message
*/ */
public static void e(String msg) public static void e(String msg)
{ {
log(Level.SEVERE, msg); log(Level.SEVERE, msg);
} }
/** /**
* Log warning message with exception * Log warning message with exception
* *
* @param msg message * @param msg message
* @param thrown thrown exception * @param thrown thrown exception
*/ */
@ -223,22 +223,22 @@ public class Log {
{ {
log(Level.WARNING, msg, thrown); log(Level.WARNING, msg, thrown);
} }
/** /**
* Log exception thrown as warning * Log exception thrown as warning
* *
* @param thrown thrown exception * @param thrown thrown exception
*/ */
public static void w(Throwable thrown) public static void w(Throwable thrown)
{ {
log(Level.WARNING, null, thrown); log(Level.WARNING, null, thrown);
} }
/** /**
* Log error message * Log error message
* *
* @param msg message * @param msg message
* @param thrown thrown exception * @param thrown thrown exception
*/ */
@ -246,42 +246,42 @@ public class Log {
{ {
log(Level.SEVERE, msg, thrown); log(Level.SEVERE, msg, thrown);
} }
/** /**
* Log exception thrown as error * Log exception thrown as error
* *
* @param thrown thrown exception * @param thrown thrown exception
*/ */
public static void e(Throwable thrown) public static void e(Throwable thrown)
{ {
log(Level.SEVERE, null, thrown); log(Level.SEVERE, null, thrown);
} }
public static void enable(boolean flag) public static void enable(boolean flag)
{ {
enabled = flag; enabled = flag;
} }
public static void setSysoutLevel(Level level) public static void setSysoutLevel(Level level)
{ {
sysoMonitor.setLevel(level); sysoMonitor.setLevel(level);
} }
public static void setLevel(Level level) public static void setLevel(Level level)
{ {
assertInited(); assertInited();
main.setLevel(level); main.setLevel(level);
} }
/** /**
* Get stack trace from throwable * Get stack trace from throwable
* *
* @param t * @param t
* @return trace * @return trace
*/ */
@ -294,31 +294,31 @@ public class Log {
sw.flush(); sw.flush();
return sw.toString(); return sw.toString();
} }
public static String formatMessage(Level level, String message, Throwable throwable, long start_ms) public static String formatMessage(Level level, String message, Throwable throwable, long start_ms)
{ {
if (message == null) message = ""; if (message == null) message = "";
final String nl = System.getProperty("line.separator"); final String nl = System.getProperty("line.separator");
if (message.length() > 0) { if (message.length() > 0) {
if (message.equals("\n")) { if (message.equals("\n")) {
return nl; return nl;
} }
if (message.charAt(0) == '\n') { if (message.charAt(0) == '\n') {
message = nl + message.substring(1); message = nl + message.substring(1);
} }
} }
final long time_ms = (System.currentTimeMillis() - start_ms); final long time_ms = (System.currentTimeMillis() - start_ms);
final double time_s = time_ms / 1000D; final double time_s = time_ms / 1000D;
final String time = String.format("%6.2f ", time_s); final String time = String.format("%6.2f ", time_s);
final String time_blank = StringUtil.repeat(" ", time.length()); final String time_blank = StringUtil.repeat(" ", time.length());
String prefix = "[ ? ]"; String prefix = "[ ? ]";
if (level == Level.FINE) { if (level == Level.FINE) {
prefix = "[ # ] "; prefix = "[ # ] ";
} else if (level == Level.FINER) { } else if (level == Level.FINER) {
@ -332,13 +332,13 @@ public class Log {
} else if (level == Level.WARNING) { } else if (level == Level.WARNING) {
prefix = "[!W!] "; prefix = "[!W!] ";
} }
message = time + prefix + message.replaceAll("\n", nl + time_blank + prefix) + nl; message = time + prefix + message.replaceAll("\n", nl + time_blank + prefix) + nl;
if (throwable != null) { if (throwable != null) {
message += getStackTrace(throwable); message += getStackTrace(throwable);
} }
return message; return message;
} }
} }

@ -5,32 +5,32 @@ import java.util.logging.Level;
public abstract class LogMonitor { public abstract class LogMonitor {
private boolean enabled = true; private boolean enabled = true;
private Level accepted = Level.ALL; private Level accepted = Level.ALL;
public void onMessageLogged(Level level, String message) public void onMessageLogged(Level level, String message)
{ {
if (!enabled) return; if (!enabled) return;
if (accepted.intValue() > level.intValue()) return; if (accepted.intValue() > level.intValue()) return;
logMessage(level, message); logMessage(level, message);
} }
protected abstract void logMessage(Level level, String message); protected abstract void logMessage(Level level, String message);
public void setLevel(Level level) public void setLevel(Level level)
{ {
this.accepted = level; this.accepted = level;
} }
public void enable(boolean flag) public void enable(boolean flag)
{ {
this.enabled = flag; this.enabled = flag;
} }
} }

@ -5,7 +5,7 @@ import java.util.logging.Level;
public class LogMonitorStdout extends LogMonitor { public class LogMonitorStdout extends LogMonitor {
@Override @Override
protected void logMessage(Level level, String message) protected void logMessage(Level level, String message)
{ {
@ -15,5 +15,5 @@ public class LogMonitorStdout extends LogMonitor {
System.out.print(message); System.out.print(message);
} }
} }
} }

@ -15,100 +15,102 @@ import mightypork.utils.string.StringUtil;
/** /**
* Logger that cleans directory & archives old logs * Logger that cleans directory & archives old logs
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
* @copy (c) 2014 * @copy (c) 2014
*/ */
public class ArchivingLog extends SimpleLog { public class ArchivingLog extends SimpleLog {
/** Number of old logs to keep */ /** Number of old logs to keep */
private final int logs_to_keep; private final int logs_to_keep;
/** /**
* Log * Log
* *
* @param name log name * @param name log name
* @param file log file (in log directory) * @param file log file (in log directory)
* @param oldLogCount number of old log files to keep: -1 all, 0 none. * @param oldLogCount number of old log files to keep: -1 all, 0 none.
*/ */
public ArchivingLog(String name, File file, int oldLogCount) { public ArchivingLog(String name, File file, int oldLogCount)
{
super(name, file); super(name, file);
this.logs_to_keep = oldLogCount; this.logs_to_keep = oldLogCount;
} }
/** /**
* Log, not keeping 5 last log files (default); * Log, not keeping 5 last log files (default);
* *
* @param name log name * @param name log name
* @param file log file (in log directory) * @param file log file (in log directory)
*/ */
public ArchivingLog(String name, File file) { public ArchivingLog(String name, File file)
{
super(name, file); super(name, file);
this.logs_to_keep = 5; this.logs_to_keep = 5;
} }
@Override @Override
public void init() public void init()
{ {
cleanLoggingDirectory(); cleanLoggingDirectory();
super.init(); super.init();
} }
private void cleanLoggingDirectory() private void cleanLoggingDirectory()
{ {
if (logs_to_keep == 0) return; // overwrite if (logs_to_keep == 0) return; // overwrite
final File log_file = getFile(); final File log_file = getFile();
final File log_dir = log_file.getParentFile(); final File log_dir = log_file.getParentFile();
final String fname = FileUtil.getBasename(log_file.toString()); final String fname = FileUtil.getBasename(log_file.toString());
// move old file // move old file
for (final File f : FileUtil.listDirectory(log_dir)) { for (final File f : FileUtil.listDirectory(log_dir)) {
if (!f.isFile()) continue; if (!f.isFile()) continue;
if (f.equals(getFile())) { if (f.equals(getFile())) {
final Date d = new Date(f.lastModified()); final Date d = new Date(f.lastModified());
final String fbase = fname + '_' + (new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss")).format(d); final String fbase = fname + '_' + (new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss")).format(d);
final String suff = "." + getSuffix(); final String suff = "." + getSuffix();
String cntStr = ""; String cntStr = "";
File f2; File f2;
for (int cnt = 0; (f2 = new File(log_dir, fbase + cntStr + suff)).exists(); cntStr = "_" + (++cnt)) {} for (int cnt = 0; (f2 = new File(log_dir, fbase + cntStr + suff)).exists(); cntStr = "_" + (++cnt)) {}
if (!f.renameTo(f2)) throw new RuntimeException("Could not move log file."); if (!f.renameTo(f2)) throw new RuntimeException("Could not move log file.");
} }
} }
if (logs_to_keep == -1) return; // keep all if (logs_to_keep == -1) return; // keep all
final List<File> oldLogs = FileUtil.listDirectory(log_dir, new FileFilter() { final List<File> oldLogs = FileUtil.listDirectory(log_dir, new FileFilter() {
@Override @Override
public boolean accept(File f) public boolean accept(File f)
{ {
if (f.isDirectory()) return false; if (f.isDirectory()) return false;
if (!f.getName().endsWith(getSuffix())) return false; if (!f.getName().endsWith(getSuffix())) return false;
if (!f.getName().startsWith(fname)) return false; if (!f.getName().startsWith(fname)) return false;
return true; return true;
} }
}); });
Collections.sort(oldLogs, new Comparator<File>() { Collections.sort(oldLogs, new Comparator<File>() {
@Override @Override
public int compare(File o1, File o2) public int compare(File o1, File o2)
{ {
return o1.getName().compareTo(o2.getName()); return o1.getName().compareTo(o2.getName());
} }
}); });
// playing with fireee // playing with fireee
for (int i = 0; i < oldLogs.size() - logs_to_keep; i++) { for (int i = 0; i < oldLogs.size() - logs_to_keep; i++) {
if (!oldLogs.get(i).delete()) { if (!oldLogs.get(i).delete()) {
@ -116,8 +118,8 @@ public class ArchivingLog extends SimpleLog {
} }
} }
} }
/** /**
* @return log filename suffix * @return log filename suffix
*/ */

@ -8,65 +8,65 @@ import mightypork.utils.logging.monitors.LogMonitor;
/** /**
* Log interface * Log interface
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public interface LogWriter { public interface LogWriter {
/** /**
* Prepare logs for logging * Prepare logs for logging
*/ */
void init(); void init();
/** /**
* Add log monitor * Add log monitor
* *
* @param mon monitor * @param mon monitor
*/ */
void addMonitor(LogMonitor mon); void addMonitor(LogMonitor mon);
/** /**
* Remove a monitor * Remove a monitor
* *
* @param removed monitor to remove * @param removed monitor to remove
*/ */
void removeMonitor(LogMonitor removed); void removeMonitor(LogMonitor removed);
/** /**
* Set logging level * Set logging level
* *
* @param level * @param level
*/ */
void setLevel(Level level); void setLevel(Level level);
/** /**
* Enable logging. * Enable logging.
* *
* @param flag do enable logging * @param flag do enable logging
*/ */
void enable(boolean flag); void enable(boolean flag);
/** /**
* Log a message * Log a message
* *
* @param level message level * @param level message level
* @param msg message text * @param msg message text
*/ */
void log(Level level, String msg); void log(Level level, String msg);
/** /**
* Log a message * Log a message
* *
* @param level message level * @param level message level
* @param msg message text * @param msg message text
* @param t thrown exception * @param t thrown exception
*/ */
void log(Level level, String msg, Throwable t); void log(Level level, String msg, Throwable t);
} }

@ -17,75 +17,76 @@ import mightypork.utils.logging.monitors.LogMonitor;
/** /**
* Basic logger * Basic logger
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class SimpleLog implements LogWriter { public class SimpleLog implements LogWriter {
/** /**
* Log file formatter. * Log file formatter.
*/ */
class LogFormatter extends Formatter { class LogFormatter extends Formatter {
@Override @Override
public String format(LogRecord record) public String format(LogRecord record)
{ {
return Log.formatMessage(record.getLevel(), record.getMessage(), record.getThrown(), started_ms); return Log.formatMessage(record.getLevel(), record.getMessage(), record.getThrown(), started_ms);
} }
} }
/** Log file */ /** Log file */
private final File file; private final File file;
/** Log name */ /** Log name */
private final String name; private final String name;
/** Logger instance. */ /** Logger instance. */
private Logger logger; private Logger logger;
private boolean enabled = true; private boolean enabled = true;
private final HashSet<LogMonitor> monitors = new HashSet<>(); private final HashSet<LogMonitor> monitors = new HashSet<>();
private final long started_ms; private final long started_ms;
public SimpleLog(String name, File file) { public SimpleLog(String name, File file)
{
this.name = name; this.name = name;
this.file = file; this.file = file;
this.started_ms = System.currentTimeMillis(); this.started_ms = System.currentTimeMillis();
} }
@Override @Override
public void init() public void init()
{ {
logger = Logger.getLogger(getName()); logger = Logger.getLogger(getName());
FileHandler handler = null; FileHandler handler = null;
try { try {
handler = new FileHandler(getFile().getPath()); handler = new FileHandler(getFile().getPath());
} catch (final Throwable t) { } catch (final Throwable t) {
throw new RuntimeException("Failed to init log.", t); throw new RuntimeException("Failed to init log.", t);
} }
handler.setFormatter(new LogFormatter()); handler.setFormatter(new LogFormatter());
logger.addHandler(handler); logger.addHandler(handler);
logger.setUseParentHandlers(false); logger.setUseParentHandlers(false);
logger.setLevel(Level.ALL); logger.setLevel(Level.ALL);
printHeader(); printHeader();
} }
protected void printHeader() protected void printHeader()
{ {
final String stamp = (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(new Date()); final String stamp = (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(new Date());
log(Level.INFO, "Logger \"" + getName() + "\" initialized.\n" + stamp); log(Level.INFO, "Logger \"" + getName() + "\" initialized.\n" + stamp);
} }
/** /**
* Add log monitor * Add log monitor
* *
* @param mon monitor * @param mon monitor
*/ */
@Override @Override
@ -93,11 +94,11 @@ public class SimpleLog implements LogWriter {
{ {
monitors.add(mon); monitors.add(mon);
} }
/** /**
* Remove a monitor * Remove a monitor
* *
* @param removed monitor to remove * @param removed monitor to remove
*/ */
@Override @Override
@ -105,61 +106,61 @@ public class SimpleLog implements LogWriter {
{ {
monitors.remove(removed); monitors.remove(removed);
} }
@Override @Override
public void setLevel(Level level) public void setLevel(Level level)
{ {
logger.setLevel(level); logger.setLevel(level);
} }
@Override @Override
public void enable(boolean flag) public void enable(boolean flag)
{ {
enabled = flag; enabled = flag;
} }
public File getFile() public File getFile()
{ {
return file; return file;
} }
public String getName() public String getName()
{ {
return name; return name;
} }
@Override @Override
public void log(Level level, String msg) public void log(Level level, String msg)
{ {
if (enabled) { if (enabled) {
logger.log(level, msg); logger.log(level, msg);
final String fmt = Log.formatMessage(level, msg, null, started_ms); final String fmt = Log.formatMessage(level, msg, null, started_ms);
for (final LogMonitor mon : monitors) { for (final LogMonitor mon : monitors) {
mon.onMessageLogged(level, fmt); mon.onMessageLogged(level, fmt);
} }
} }
} }
@Override @Override
public void log(Level level, String msg, Throwable t) public void log(Level level, String msg, Throwable t)
{ {
if (enabled) { if (enabled) {
logger.log(level, msg, t); logger.log(level, msg, t);
final String fmt = Log.formatMessage(level, msg, t, started_ms); final String fmt = Log.formatMessage(level, msg, t, started_ms);
for (final LogMonitor mon : monitors) { for (final LogMonitor mon : monitors) {
mon.onMessageLogged(level, fmt); mon.onMessageLogged(level, fmt);
} }
} }
} }
} }

@ -3,7 +3,7 @@ package mightypork.utils.math;
/** /**
* Horizontal align sides * Horizontal align sides
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public enum AlignX public enum AlignX

@ -3,7 +3,7 @@ package mightypork.utils.math;
/** /**
* Vertical align sides * Vertical align sides
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public enum AlignY public enum AlignY

@ -13,22 +13,23 @@ import mightypork.utils.math.constraints.vect.Vect;
/** /**
* Math utils * Math utils
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public final class Calc { public final class Calc {
private Calc() { private Calc()
{
// not instantiable // not instantiable
} }
/** Square root of two */ /** Square root of two */
public static final double SQ2 = 1.41421356237; public static final double SQ2 = 1.41421356237;
/** /**
* Get distance from 2D line to 2D point [X,Y] * Get distance from 2D line to 2D point [X,Y]
* *
* @param lineDirVec line directional vector * @param lineDirVec line directional vector
* @param linePoint point of line * @param linePoint point of line
* @param point point coordinate * @param point point coordinate
@ -39,56 +40,56 @@ public final class Calc {
// line point L[lx,ly] // line point L[lx,ly]
final double lx = linePoint.x(); final double lx = linePoint.x();
final double ly = linePoint.y(); final double ly = linePoint.y();
// line equation ax+by+c=0 // line equation ax+by+c=0
final double a = -lineDirVec.y(); final double a = -lineDirVec.y();
final double b = lineDirVec.x(); final double b = lineDirVec.x();
final double c = -a * lx - b * ly; final double c = -a * lx - b * ly;
// checked point P[x,y] // checked point P[x,y]
final double x = point.x(); final double x = point.x();
final double y = point.y(); final double y = point.y();
// distance // distance
return Math.abs(a * x + b * y + c) / Math.sqrt(a * a + b * b); return Math.abs(a * x + b * y + c) / Math.sqrt(a * a + b * b);
} }
public static final Random rand = new Random(); public static final Random rand = new Random();
public static double sphereSurface(double radius) public static double sphereSurface(double radius)
{ {
return 4D * Math.PI * square(radius); return 4D * Math.PI * square(radius);
} }
public static double sphereVolume(double radius) public static double sphereVolume(double radius)
{ {
return (4D / 3D) * Math.PI * cube(radius); return (4D / 3D) * Math.PI * cube(radius);
} }
public static double sphereRadius(double volume) public static double sphereRadius(double volume)
{ {
return Math.cbrt((3D * volume) / (4 * Math.PI)); return Math.cbrt((3D * volume) / (4 * Math.PI));
} }
public static double circleSurface(double radius) public static double circleSurface(double radius)
{ {
return Math.PI * square(radius); return Math.PI * square(radius);
} }
public static double circleRadius(double surface) public static double circleRadius(double surface)
{ {
return Math.sqrt(surface / Math.PI); return Math.sqrt(surface / Math.PI);
} }
/** /**
* Safe equals that works with nulls * Safe equals that works with nulls
* *
* @param a * @param a
* @param b * @param b
* @return are equal * @return are equal
@ -97,11 +98,11 @@ public final class Calc {
{ {
return a == null ? b == null : a.equals(b); return a == null ? b == null : a.equals(b);
} }
/** /**
* Clamp integer * Clamp integer
* *
* @param number * @param number
* @param min * @param min
* @param max * @param max
@ -111,11 +112,11 @@ public final class Calc {
{ {
return number < min ? min : number > max ? max : number; return number < min ? min : number > max ? max : number;
} }
/** /**
* Clamp double * Clamp double
* *
* @param number * @param number
* @param min * @param min
* @param max * @param max
@ -125,17 +126,17 @@ public final class Calc {
{ {
return number < min ? min : number > max ? max : number; return number < min ? min : number > max ? max : number;
} }
public static boolean isInRange(double number, double left, double right) public static boolean isInRange(double number, double left, double right)
{ {
return number >= left && number <= right; return number >= left && number <= right;
} }
/** /**
* Get number from A to B at delta time (A -> B) * Get number from A to B at delta time (A -> B)
* *
* @param from * @param from
* @param to * @param to
* @param elapsed progress ratio 0..1 * @param elapsed progress ratio 0..1
@ -146,11 +147,11 @@ public final class Calc {
{ {
return from + (to - from) * easing.get(elapsed); return from + (to - from) * easing.get(elapsed);
} }
/** /**
* Get angle [degrees] from A to B at delta time (tween A to B) * Get angle [degrees] from A to B at delta time (tween A to B)
* *
* @param from * @param from
* @param to * @param to
* @param elapsed progress ratio 0..1 * @param elapsed progress ratio 0..1
@ -161,11 +162,11 @@ public final class Calc {
{ {
return Deg.norm(from - Deg.delta(to, from) * easing.get(elapsed)); return Deg.norm(from - Deg.delta(to, from) * easing.get(elapsed));
} }
/** /**
* Get angle [radians] from A to B at delta time (tween A to B) * Get angle [radians] from A to B at delta time (tween A to B)
* *
* @param from * @param from
* @param to * @param to
* @param elapsed progress ratio 0..1 * @param elapsed progress ratio 0..1
@ -176,8 +177,8 @@ public final class Calc {
{ {
return Rad.norm(from - Rad.delta(to, from) * easing.get(elapsed)); return Rad.norm(from - Rad.delta(to, from) * easing.get(elapsed));
} }
public static double max(double... numbers) public static double max(double... numbers)
{ {
double highest = numbers[0]; double highest = numbers[0];
@ -186,8 +187,8 @@ public final class Calc {
} }
return highest; return highest;
} }
public static int max(int... numbers) public static int max(int... numbers)
{ {
int highest = numbers[0]; int highest = numbers[0];
@ -196,8 +197,8 @@ public final class Calc {
} }
return highest; return highest;
} }
public static double min(double... numbers) public static double min(double... numbers)
{ {
double lowest = numbers[0]; double lowest = numbers[0];
@ -206,8 +207,8 @@ public final class Calc {
} }
return lowest; return lowest;
} }
public static int min(int... numbers) public static int min(int... numbers)
{ {
int lowest = numbers[0]; int lowest = numbers[0];
@ -216,11 +217,11 @@ public final class Calc {
} }
return lowest; return lowest;
} }
/** /**
* Split comma separated list of integers. * Split comma separated list of integers.
* *
* @param list String containing the list. * @param list String containing the list.
* @param delimiter delimiter character * @param delimiter delimiter character
* @return array of integers or null. * @return array of integers or null.
@ -230,24 +231,24 @@ public final class Calc {
if (list == null) { if (list == null) {
return null; return null;
} }
final String[] parts = list.split(Character.toString(delimiter)); final String[] parts = list.split(Character.toString(delimiter));
final ArrayList<Integer> intList = new ArrayList<>(); final ArrayList<Integer> intList = new ArrayList<>();
for (final String part : parts) { for (final String part : parts) {
try { try {
intList.add(Integer.parseInt(part.trim())); intList.add(Integer.parseInt(part.trim()));
} catch (final NumberFormatException e) {} } catch (final NumberFormatException e) {}
} }
return intList; return intList;
} }
/** /**
* Pick random element from a given list. * Pick random element from a given list.
* *
* @param list list of choices * @param list list of choices
* @return picked element * @return picked element
*/ */
@ -255,11 +256,11 @@ public final class Calc {
{ {
return pick(rand, list); return pick(rand, list);
} }
/** /**
* Pick random element from a given list. * Pick random element from a given list.
* *
* @param rand RNG * @param rand RNG
* @param list list of choices * @param list list of choices
* @return picked element * @return picked element
@ -269,11 +270,11 @@ public final class Calc {
if (list.size() == 0) return null; if (list.size() == 0) return null;
return list.get(rand.nextInt(list.size())); return list.get(rand.nextInt(list.size()));
} }
/** /**
* Take a square * Take a square
* *
* @param a value * @param a value
* @return value squared * @return value squared
*/ */
@ -281,11 +282,11 @@ public final class Calc {
{ {
return a * a; return a * a;
} }
/** /**
* Take a cube * Take a cube
* *
* @param a value * @param a value
* @return value cubed * @return value cubed
*/ */
@ -293,8 +294,8 @@ public final class Calc {
{ {
return a * a * a; return a * a * a;
} }
/** /**
* @param d number * @param d number
* @return fractional part * @return fractional part
@ -303,11 +304,11 @@ public final class Calc {
{ {
return d - Math.floor(d); return d - Math.floor(d);
} }
/** /**
* Make sure value is within array length. * Make sure value is within array length.
* *
* @param index tested index * @param index tested index
* @param length array length * @param length array length
* @throws IndexOutOfBoundsException if the index is not in range. * @throws IndexOutOfBoundsException if the index is not in range.
@ -318,11 +319,11 @@ public final class Calc {
throw new IndexOutOfBoundsException(); throw new IndexOutOfBoundsException();
} }
} }
/** /**
* Get distance of two coordinates in 2D plane * Get distance of two coordinates in 2D plane
* *
* @param x1 first coordinate X * @param x1 first coordinate X
* @param y1 first coordinate y * @param y1 first coordinate y
* @param x2 second coordinate X * @param x2 second coordinate X
@ -333,24 +334,24 @@ public final class Calc {
{ {
return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)); return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
} }
public static int randInt(Random rand, int low, int high) public static int randInt(Random rand, int low, int high)
{ {
final int range = Math.abs(high - low) + 1; final int range = Math.abs(high - low) + 1;
return low + rand.nextInt(range); return low + rand.nextInt(range);
} }
public static int randInt(int low, int high) public static int randInt(int low, int high)
{ {
return randInt(rand, low, high); return randInt(rand, low, high);
} }
/** /**
* Get ordinal version of numbers (1 = 1st, 5 = 5th etc.) * Get ordinal version of numbers (1 = 1st, 5 = 5th etc.)
* *
* @param number number * @param number number
* @return ordinal, string * @return ordinal, string
*/ */
@ -363,11 +364,11 @@ public final class Calc {
} }
return number + "th"; return number + "th";
} }
/** /**
* Format number with thousands separated. * Format number with thousands separated.
* *
* @param number number * @param number number
* @param thousandSep * @param thousandSep
* @return string * @return string
@ -377,18 +378,18 @@ public final class Calc {
final String num = String.valueOf(number); final String num = String.valueOf(number);
final String dot = String.valueOf(thousandSep); final String dot = String.valueOf(thousandSep);
String out = ""; String out = "";
int cnt = 1; int cnt = 1;
for (int i = num.length() - 1; i >= 0; i--) { for (int i = num.length() - 1; i >= 0; i--) {
out = num.charAt(i) + out; out = num.charAt(i) + out;
if (cnt % 3 == 0 && i > 0) out = dot + out; if (cnt % 3 == 0 && i > 0) out = dot + out;
cnt++; cnt++;
} }
return out; return out;
} }
public static int countBits(byte b) public static int countBits(byte b)
{ {
int c = 0; int c = 0;

@ -6,44 +6,46 @@ import mightypork.utils.math.constraints.vect.Vect;
/** /**
* Polar coordinate * Polar coordinate
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class Polar { public class Polar {
/** angle in radians */ /** angle in radians */
private double angle = 0; private double angle = 0;
/** distance in units */ /** distance in units */
private double radius = 0; private double radius = 0;
private Vect coord = null; private Vect coord = null;
/** /**
* Create a polar * Create a polar
* *
* @param angle angle in RAD * @param angle angle in RAD
* @param distance distance from origin * @param distance distance from origin
*/ */
public Polar(double angle, double distance) { public Polar(double angle, double distance)
{
this(angle, false, distance); this(angle, false, distance);
} }
/** /**
* Create a polar * Create a polar
* *
* @param angle angle * @param angle angle
* @param deg angle is in DEG * @param deg angle is in DEG
* @param distance radius * @param distance radius
*/ */
public Polar(double angle, boolean deg, double distance) { public Polar(double angle, boolean deg, double distance)
{
this.radius = distance; this.radius = distance;
this.angle = deg ? Math.toRadians(angle) : angle; this.angle = deg ? Math.toRadians(angle) : angle;
} }
/** /**
* @return angle in RAD * @return angle in RAD
*/ */
@ -51,8 +53,8 @@ public class Polar {
{ {
return angle; return angle;
} }
/** /**
* @return angle in DEG * @return angle in DEG
*/ */
@ -60,8 +62,8 @@ public class Polar {
{ {
return Math.toDegrees(angle); return Math.toDegrees(angle);
} }
/** /**
* @param angle angle in RAD * @param angle angle in RAD
*/ */
@ -69,8 +71,8 @@ public class Polar {
{ {
this.angle = angle; this.angle = angle;
} }
/** /**
* @param angle angle in DEG * @param angle angle in DEG
*/ */
@ -78,8 +80,8 @@ public class Polar {
{ {
this.angle = Math.toRadians(angle); this.angle = Math.toRadians(angle);
} }
/** /**
* @return radius * @return radius
*/ */
@ -87,8 +89,8 @@ public class Polar {
{ {
return radius; return radius;
} }
/** /**
* @param r radius * @param r radius
*/ */
@ -96,24 +98,24 @@ public class Polar {
{ {
this.radius = r; this.radius = r;
} }
/** /**
* Make polar from coord * Make polar from coord
* *
* @param coord coord * @param coord coord
* @return polar * @return polar
*/ */
public static Polar fromCoord(Vect coord) public static Polar fromCoord(Vect coord)
{ {
return Polar.fromCoord(coord.x(), coord.y()); return Polar.fromCoord(coord.x(), coord.y());
} }
/** /**
* Make polar from coords * Make polar from coords
* *
* @param x x coord * @param x x coord
* @param y y coord * @param y y coord
* @return polar * @return polar
@ -122,46 +124,46 @@ public class Polar {
{ {
final double a = Math.atan2(y, x); final double a = Math.atan2(y, x);
final double r = Math.sqrt(x * x + y * y); final double r = Math.sqrt(x * x + y * y);
return new Polar(a, r); return new Polar(a, r);
} }
/** /**
* Get coord from polar * Get coord from polar
* *
* @return coord * @return coord
*/ */
public Vect toCoord() public Vect toCoord()
{ {
// lazy init // lazy init
if (coord == null) coord = new Vect() { if (coord == null) coord = new Vect() {
@Override @Override
public double x() public double x()
{ {
return radius * Math.cos(angle); return radius * Math.cos(angle);
} }
@Override @Override
public double y() public double y()
{ {
return radius * Math.sin(angle); return radius * Math.sin(angle);
} }
}; };
return coord; return coord;
} }
@Override @Override
public String toString() public String toString()
{ {
return "Polar(" + angle + "rad, " + radius + ")"; return "Polar(" + angle + "rad, " + radius + ")";
} }
@Override @Override
public int hashCode() public int hashCode()
{ {
@ -174,8 +176,8 @@ public class Polar {
result = prime * result + (int) (temp ^ (temp >>> 32)); result = prime * result + (int) (temp ^ (temp >>> 32));
return result; return result;
} }
@Override @Override
public boolean equals(Object obj) public boolean equals(Object obj)
{ {

@ -6,82 +6,85 @@ import java.util.Random;
/** /**
* Numeric range, able to generate random numbers and give min/max values. * Numeric range, able to generate random numbers and give min/max values.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class Range { public class Range {
public static Range make(double low, double high) public static Range make(double low, double high)
{ {
return new Range(low, high); return new Range(low, high);
} }
private double min = 0; private double min = 0;
private double max = 1; private double max = 1;
/** /**
* Implicit range constructor 0-1 * Implicit range constructor 0-1
*/ */
public Range() { public Range()
{
} }
/** /**
* Create new range * Create new range
* *
* @param min min number * @param min min number
* @param max max number * @param max max number
*/ */
public Range(double min, double max) { public Range(double min, double max)
{
this.min = min; this.min = min;
this.max = max; this.max = max;
norm(); norm();
} }
/** /**
* Create new range * Create new range
* *
* @param minmax min = max number * @param minmax min = max number
*/ */
public Range(double minmax) { public Range(double minmax)
{
this.min = minmax; this.min = minmax;
this.max = minmax; this.max = minmax;
} }
public static Range fromString(String string) public static Range fromString(String string)
{ {
try { try {
String s = string.trim(); String s = string.trim();
// drop whitespace // drop whitespace
s = s.replaceAll("\\s", ""); s = s.replaceAll("\\s", "");
// drop brackets // drop brackets
s = s.replaceAll("[\\(\\[\\{\\)\\]\\}]", ""); s = s.replaceAll("[\\(\\[\\{\\)\\]\\}]", "");
// norm separators // norm separators
s = s.replaceAll("[:;]", "|").replace("..", "|"); s = s.replaceAll("[:;]", "|").replace("..", "|");
// norm floating point // norm floating point
s = s.replaceAll("[,]", "."); s = s.replaceAll("[,]", ".");
// dash to pipe, if not a minus sign // dash to pipe, if not a minus sign
s = s.replaceAll("([0-9])\\s?[\\-]", "$1|"); s = s.replaceAll("([0-9])\\s?[\\-]", "$1|");
final String[] parts = s.split("[|]"); final String[] parts = s.split("[|]");
if (parts.length >= 1) { if (parts.length >= 1) {
final double low = Double.parseDouble(parts[0].trim()); final double low = Double.parseDouble(parts[0].trim());
if (parts.length == 2) { if (parts.length == 2) {
final double high = Double.parseDouble(parts[1].trim()); final double high = Double.parseDouble(parts[1].trim());
return Range.make(low, high); return Range.make(low, high);
} }
return Range.make(low, low); return Range.make(low, low);
} }
} catch (final RuntimeException e) { } catch (final RuntimeException e) {
@ -89,15 +92,15 @@ public class Range {
} }
return null; return null;
} }
@Override @Override
public String toString() public String toString()
{ {
return String.format("(%f : %f)", getMin(), getMax()); return String.format("(%f : %f)", getMin(), getMax());
} }
/** /**
* Make sure min is <= max * Make sure min is <= max
*/ */
@ -109,33 +112,33 @@ public class Range {
max = t; max = t;
} }
} }
/** /**
* Get random integer from range * Get random integer from range
* *
* @return random int * @return random int
*/ */
public int randInt() public int randInt()
{ {
return randInt(Calc.rand); return randInt(Calc.rand);
} }
/** /**
* Get random double from this range * Get random double from this range
* *
* @return random double * @return random double
*/ */
public double randDouble() public double randDouble()
{ {
return randDouble(Calc.rand); return randDouble(Calc.rand);
} }
/** /**
* Get random integer from range * Get random integer from range
* *
* @param rand RNG * @param rand RNG
* @return random int * @return random int
*/ */
@ -143,11 +146,11 @@ public class Range {
{ {
return Calc.randInt(rand, (int) Math.round(min), (int) Math.round(min)); return Calc.randInt(rand, (int) Math.round(min), (int) Math.round(min));
} }
/** /**
* Get random double from this range * Get random double from this range
* *
* @param rand RNG * @param rand RNG
* @return random double * @return random double
*/ */
@ -155,33 +158,33 @@ public class Range {
{ {
return min + rand.nextDouble() * (max - min); return min + rand.nextDouble() * (max - min);
} }
/** /**
* Get min * Get min
* *
* @return min number * @return min number
*/ */
public double getMin() public double getMin()
{ {
return min; return min;
} }
/** /**
* Get max * Get max
* *
* @return max number * @return max number
*/ */
public double getMax() public double getMax()
{ {
return max; return max;
} }
/** /**
* Set min * Set min
* *
* @param min min value * @param min min value
*/ */
public void setMin(double min) public void setMin(double min)
@ -189,11 +192,11 @@ public class Range {
this.min = min; this.min = min;
norm(); norm();
} }
/** /**
* Set max * Set max
* *
* @param max max value * @param max max value
*/ */
public void setMax(double max) public void setMax(double max)
@ -201,22 +204,22 @@ public class Range {
this.max = max; this.max = max;
norm(); norm();
} }
/** /**
* Get identical copy * Get identical copy
* *
* @return copy * @return copy
*/ */
public Range copy() public Range copy()
{ {
return new Range(min, max); return new Range(min, max);
} }
/** /**
* Set to value of other range * Set to value of other range
* *
* @param other copied range * @param other copied range
*/ */
public void setTo(Range other) public void setTo(Range other)
@ -226,11 +229,11 @@ public class Range {
max = other.max; max = other.max;
norm(); norm();
} }
/** /**
* Set to min-max values * Set to min-max values
* *
* @param min min value * @param min min value
* @param max max value * @param max max value
*/ */

@ -9,63 +9,66 @@ import mightypork.utils.math.constraints.vect.VectConst;
/** /**
* Very simple integer coordinate * Very simple integer coordinate
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class Coord { public class Coord {
public int x; public int x;
public int y; public int y;
@FactoryMethod @FactoryMethod
public static Coord make(int x, int y) public static Coord make(int x, int y)
{ {
return new Coord(x, y); return new Coord(x, y);
} }
@FactoryMethod @FactoryMethod
public static Coord make(Coord other) public static Coord make(Coord other)
{ {
return new Coord(other); return new Coord(other);
} }
@FactoryMethod @FactoryMethod
public static Coord zero() public static Coord zero()
{ {
return make(0, 0); return make(0, 0);
} }
public Coord() { public Coord()
{
// for ion // for ion
} }
public Coord(int x, int y) { public Coord(int x, int y)
{
super(); super();
this.x = x; this.x = x;
this.y = y; this.y = y;
} }
public Coord(Coord other) { public Coord(Coord other)
{
this.x = other.x; this.x = other.x;
this.y = other.y; this.y = other.y;
} }
public Coord add(int addX, int addY) public Coord add(int addX, int addY)
{ {
return new Coord(x + addX, y + addY); return new Coord(x + addX, y + addY);
} }
/** /**
* Add other coord in a copy * Add other coord in a copy
* *
* @param added * @param added
* @return changed copy * @return changed copy
*/ */
@ -73,36 +76,36 @@ public class Coord {
{ {
return add(added.x, added.y); return add(added.x, added.y);
} }
public Coord add(Move added) public Coord add(Move added)
{ {
return add(added.x(), added.y()); return add(added.x(), added.y());
} }
public Coord copy() public Coord copy()
{ {
return make(this); return make(this);
} }
public void setTo(int x, int y) public void setTo(int x, int y)
{ {
this.x = x; this.x = x;
this.y = y; this.y = y;
} }
public void setTo(Coord pos) public void setTo(Coord pos)
{ {
setTo(pos.x, pos.y); setTo(pos.x, pos.y);
} }
/** /**
* Check if coord is in a range (inclusive) * Check if coord is in a range (inclusive)
* *
* @param x0 range min x * @param x0 range min x
* @param y0 range min y * @param y0 range min y
* @param x1 range max x * @param x1 range max x
@ -113,27 +116,27 @@ public class Coord {
{ {
return !(x < x0 || x > x1 || y < y0 || y > y1); return !(x < x0 || x > x1 || y < y0 || y > y1);
} }
public double dist(Coord coord) public double dist(Coord coord)
{ {
return Calc.dist(x, y, coord.x, coord.y); return Calc.dist(x, y, coord.x, coord.y);
} }
public VectConst toVect() public VectConst toVect()
{ {
return Vect.make(x, y); return Vect.make(x, y);
} }
@Override @Override
public String toString() public String toString()
{ {
return "Coord(" + x + "," + y + ")"; return "Coord(" + x + "," + y + ")";
} }
@Override @Override
public int hashCode() public int hashCode()
{ {
@ -143,8 +146,8 @@ public class Coord {
result = prime * result + y; result = prime * result + y;
return result; return result;
} }
@Override @Override
public boolean equals(Object obj) public boolean equals(Object obj)
{ {
@ -156,8 +159,8 @@ public class Coord {
if (y != other.y) return false; if (y != other.y) return false;
return true; return true;
} }
public static Coord fromVect(Vect vect) public static Coord fromVect(Vect vect)
{ {
return make((int) Math.floor(vect.x()), (int) Math.floor(vect.y())); return make((int) Math.floor(vect.x()), (int) Math.floor(vect.y()));

@ -4,60 +4,61 @@ package mightypork.utils.math.algo;
/** /**
* Path step.<br> * Path step.<br>
* Must be binary in order to be saveable in lists. * Must be binary in order to be saveable in lists.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class Move { public class Move {
public static final Move NORTH = new Move(0, -1); public static final Move NORTH = new Move(0, -1);
public static final Move SOUTH = new Move(0, 1); public static final Move SOUTH = new Move(0, 1);
public static final Move EAST = new Move(1, 0); public static final Move EAST = new Move(1, 0);
public static final Move WEST = new Move(-1, 0); public static final Move WEST = new Move(-1, 0);
public static final Move NONE = new Move(0, 0); public static final Move NONE = new Move(0, 0);
public static Move make(int x, int y) public static Move make(int x, int y)
{ {
x = x < 0 ? -1 : x > 0 ? 1 : 0; x = x < 0 ? -1 : x > 0 ? 1 : 0;
y = y < 0 ? -1 : y > 0 ? 1 : 0; y = y < 0 ? -1 : y > 0 ? 1 : 0;
if (y == -1 && x == 0) return NORTH; if (y == -1 && x == 0) return NORTH;
if (y == 1 && x == 0) return SOUTH; if (y == 1 && x == 0) return SOUTH;
if (x == -1 && y == 0) return WEST; if (x == -1 && y == 0) return WEST;
if (x == 1 && y == 0) return EAST; if (x == 1 && y == 0) return EAST;
if (x == 0 && y == 0) return NONE; if (x == 0 && y == 0) return NONE;
return new Move(x, y); return new Move(x, y);
} }
private final byte x; private final byte x;
private final byte y; private final byte y;
public Move(int x, int y) { public Move(int x, int y)
{
this.x = (byte) (x < 0 ? -1 : x > 0 ? 1 : 0); this.x = (byte) (x < 0 ? -1 : x > 0 ? 1 : 0);
this.y = (byte) (y < 0 ? -1 : y > 0 ? 1 : 0); this.y = (byte) (y < 0 ? -1 : y > 0 ? 1 : 0);
} }
public int x() public int x()
{ {
return x; return x;
} }
public int y() public int y()
{ {
return y; return y;
} }
public Coord toCoord() public Coord toCoord()
{ {
return Coord.make(x, y); return Coord.make(x, y);
} }
@Override @Override
public String toString() public String toString()
{ {

@ -11,11 +11,11 @@ import mightypork.utils.math.Calc;
/** /**
* Move lists, bit masks and other utilities * Move lists, bit masks and other utilities
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class Moves { public class Moves {
public static final byte BIT_NW = (byte) 0b10000000; public static final byte BIT_NW = (byte) 0b10000000;
public static final byte BIT_N = (byte) 0b01000000; public static final byte BIT_N = (byte) 0b01000000;
public static final byte BIT_NE = (byte) 0b00100000; public static final byte BIT_NE = (byte) 0b00100000;
@ -24,15 +24,15 @@ public class Moves {
public static final byte BIT_S = (byte) 0b00000100; public static final byte BIT_S = (byte) 0b00000100;
public static final byte BIT_SW = (byte) 0b00000010; public static final byte BIT_SW = (byte) 0b00000010;
public static final byte BIT_W = (byte) 0b00000001; public static final byte BIT_W = (byte) 0b00000001;
public static final byte BITS_CARDINAL = BIT_N | BIT_S | BIT_E | BIT_W; public static final byte BITS_CARDINAL = BIT_N | BIT_S | BIT_E | BIT_W;
public static final byte BITS_DIAGONAL = BIT_NE | BIT_NW | BIT_SE | BIT_SW; public static final byte BITS_DIAGONAL = BIT_NE | BIT_NW | BIT_SE | BIT_SW;
public static final byte BITS_NW_CORNER = BIT_W | BIT_NW | BIT_N; public static final byte BITS_NW_CORNER = BIT_W | BIT_NW | BIT_N;
public static final byte BITS_NE_CORNER = BIT_E | BIT_NE | BIT_N; public static final byte BITS_NE_CORNER = BIT_E | BIT_NE | BIT_N;
public static final byte BITS_SW_CORNER = BIT_W | BIT_SW | BIT_S; public static final byte BITS_SW_CORNER = BIT_W | BIT_SW | BIT_S;
public static final byte BITS_SE_CORNER = BIT_E | BIT_SE | BIT_S; public static final byte BITS_SE_CORNER = BIT_E | BIT_SE | BIT_S;
public static final Move NW = Move.make(-1, -1); public static final Move NW = Move.make(-1, -1);
public static final Move N = Move.make(0, -1); public static final Move N = Move.make(0, -1);
public static final Move NE = Move.make(1, -1); public static final Move NE = Move.make(1, -1);
@ -41,32 +41,32 @@ public class Moves {
public static final Move S = Move.make(0, 1); public static final Move S = Move.make(0, 1);
public static final Move SW = Move.make(-1, 1); public static final Move SW = Move.make(-1, 1);
public static final Move W = Move.make(-1, 0); public static final Move W = Move.make(-1, 0);
//@formatter:off //@formatter:off
/** All sides, in the order of bits. */ /** All sides, in the order of bits. */
public final static List<Move> ALL_SIDES = Collections.unmodifiableList(Arrays.asList( public final static List<Move> ALL_SIDES = Collections.unmodifiableList(Arrays.asList(
NW, NW,
N, N,
NE, NE,
E, E,
SE, SE,
S, S,
SW, SW,
W W
)); ));
public final static List<Move> CARDINAL_SIDES = Collections.unmodifiableList(Arrays.asList( public final static List<Move> CARDINAL_SIDES = Collections.unmodifiableList(Arrays.asList(
N, N,
E, E,
S, S,
W W
)); ));
//@formatter:on //@formatter:on
/** /**
* Get element from all sides * Get element from all sides
* *
* @param i side index * @param i side index
* @return the side coord * @return the side coord
*/ */
@ -74,20 +74,20 @@ public class Moves {
{ {
return ALL_SIDES.get(i); return ALL_SIDES.get(i);
} }
public static byte getBit(int i) public static byte getBit(int i)
{ {
return (byte) (1 << (7 - i)); return (byte) (1 << (7 - i));
} }
public static Move randomCardinal() public static Move randomCardinal()
{ {
return Calc.pick(CARDINAL_SIDES); return Calc.pick(CARDINAL_SIDES);
} }
public static Move randomCardinal(Random rand) public static Move randomCardinal(Random rand)
{ {
return Calc.pick(rand, CARDINAL_SIDES); return Calc.pick(rand, CARDINAL_SIDES);

@ -11,33 +11,33 @@ import mightypork.utils.math.algo.Move;
public abstract class FloodFill { public abstract class FloodFill {
public abstract boolean canEnter(Coord pos); public abstract boolean canEnter(Coord pos);
public abstract boolean canSpreadFrom(Coord pos); public abstract boolean canSpreadFrom(Coord pos);
public abstract List<Move> getSpreadSides(); public abstract List<Move> getSpreadSides();
/** /**
* Get the max distance filled form start point. Use -1 for unlimited range. * Get the max distance filled form start point. Use -1 for unlimited range.
* *
* @return max distance * @return max distance
*/ */
public abstract double getMaxDistance(); public abstract double getMaxDistance();
/** /**
* @return true if start should be spread no matter what * @return true if start should be spread no matter what
*/ */
public abstract boolean forceSpreadStart(); public abstract boolean forceSpreadStart();
/** /**
* Fill an area * Fill an area
* *
* @param start start point * @param start start point
* @param foundNodes collection to put filled coords in * @param foundNodes collection to put filled coords in
* @return true if fill was successful; false if max range was reached. * @return true if fill was successful; false if max range was reached.
@ -45,32 +45,32 @@ public abstract class FloodFill {
public final boolean fill(Coord start, Collection<Coord> foundNodes) public final boolean fill(Coord start, Collection<Coord> foundNodes)
{ {
final Queue<Coord> activeNodes = new LinkedList<>(); final Queue<Coord> activeNodes = new LinkedList<>();
final double maxDist = getMaxDistance(); final double maxDist = getMaxDistance();
activeNodes.add(start); activeNodes.add(start);
boolean forceSpreadNext = forceSpreadStart(); boolean forceSpreadNext = forceSpreadStart();
boolean limitReached = false; boolean limitReached = false;
while (!activeNodes.isEmpty()) { while (!activeNodes.isEmpty()) {
final Coord current = activeNodes.poll(); final Coord current = activeNodes.poll();
foundNodes.add(current); foundNodes.add(current);
if (!canSpreadFrom(current) && !forceSpreadNext) continue; if (!canSpreadFrom(current) && !forceSpreadNext) continue;
forceSpreadNext = false; forceSpreadNext = false;
for (final Move spr : getSpreadSides()) { for (final Move spr : getSpreadSides()) {
final Coord next = current.add(spr); final Coord next = current.add(spr);
if (activeNodes.contains(next) || foundNodes.contains(next)) continue; if (activeNodes.contains(next) || foundNodes.contains(next)) continue;
if (next.dist(start) > maxDist) { if (next.dist(start) > maxDist) {
limitReached = true; limitReached = true;
continue; continue;
} }
if (canEnter(next)) { if (canEnter(next)) {
activeNodes.add(next); activeNodes.add(next);
} else { } else {
@ -78,7 +78,7 @@ public abstract class FloodFill {
} }
} }
} }
return !limitReached; return !limitReached;
} }
} }

@ -5,10 +5,10 @@ import mightypork.utils.math.algo.Coord;
public abstract class Heuristic { public abstract class Heuristic {
/** /**
* Get tile cost (estimate of how many tiles remain to the target) * Get tile cost (estimate of how many tiles remain to the target)
* *
* @param pos current pos * @param pos current pos
* @param target target pos * @param target target pos
* @return estimated number of tiles * @return estimated number of tiles

@ -15,34 +15,34 @@ import mightypork.utils.math.algo.pathfinding.heuristics.ManhattanHeuristic;
/** /**
* A* pathfinder * A* pathfinder
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public abstract class PathFinder { public abstract class PathFinder {
private static final FComparator F_COMPARATOR = new FComparator(); private static final FComparator F_COMPARATOR = new FComparator();
public static final Heuristic CORNER_HEURISTIC = new ManhattanHeuristic(); public static final Heuristic CORNER_HEURISTIC = new ManhattanHeuristic();
public static final Heuristic DIAGONAL_HEURISTIC = new DiagonalHeuristic(); public static final Heuristic DIAGONAL_HEURISTIC = new DiagonalHeuristic();
private boolean ignoreStart; private boolean ignoreStart;
private boolean ignoreEnd; private boolean ignoreEnd;
public List<Move> findPathRelative(Coord start, Coord end) public List<Move> findPathRelative(Coord start, Coord end)
{ {
return findPathRelative(start, end, ignoreStart, ignoreEnd); return findPathRelative(start, end, ignoreStart, ignoreEnd);
} }
public List<Move> findPathRelative(Coord start, Coord end, boolean ignoreStart, boolean ignoreEnd) public List<Move> findPathRelative(Coord start, Coord end, boolean ignoreStart, boolean ignoreEnd)
{ {
final List<Coord> path = findPath(start, end, ignoreStart, ignoreEnd); final List<Coord> path = findPath(start, end, ignoreStart, ignoreEnd);
if (path == null) return null; if (path == null) return null;
final List<Move> out = new ArrayList<>(); final List<Move> out = new ArrayList<>();
final Coord current = start.copy(); final Coord current = start.copy();
for (final Coord c : path) { for (final Coord c : path) {
if (c.equals(current)) continue; if (c.equals(current)) continue;
@ -50,24 +50,24 @@ public abstract class PathFinder {
current.x = c.x; current.x = c.x;
current.y = c.y; current.y = c.y;
} }
return out; return out;
} }
public List<Coord> findPath(Coord start, Coord end) public List<Coord> findPath(Coord start, Coord end)
{ {
return findPath(start, end, ignoreStart, ignoreEnd); return findPath(start, end, ignoreStart, ignoreEnd);
} }
public List<Coord> findPath(Coord start, Coord end, boolean ignoreStart, boolean ignoreEnd) public List<Coord> findPath(Coord start, Coord end, boolean ignoreStart, boolean ignoreEnd)
{ {
final LinkedList<Node> open = new LinkedList<>(); final LinkedList<Node> open = new LinkedList<>();
final LinkedList<Node> closed = new LinkedList<>(); final LinkedList<Node> closed = new LinkedList<>();
final Heuristic heuristic = getHeuristic(); final Heuristic heuristic = getHeuristic();
// add first node // add first node
{ {
final Node n = new Node(start); final Node n = new Node(start);
@ -75,37 +75,37 @@ public abstract class PathFinder {
n.g_cost = 0; n.g_cost = 0;
open.add(n); open.add(n);
} }
Node current = null; Node current = null;
while (true) { while (true) {
current = open.poll(); current = open.poll();
if (current == null) { if (current == null) {
break; break;
} }
closed.add(current); closed.add(current);
if (current.pos.equals(end)) { if (current.pos.equals(end)) {
break; break;
} }
for (final Move go : getWalkSides()) { for (final Move go : getWalkSides()) {
final Coord c = current.pos.add(go); final Coord c = current.pos.add(go);
if (!isAccessible(c) && !(c.equals(end) && ignoreEnd) && !(c.equals(start) && ignoreStart)) continue; if (!isAccessible(c) && !(c.equals(end) && ignoreEnd) && !(c.equals(start) && ignoreStart)) continue;
final Node a = new Node(c); final Node a = new Node(c);
a.g_cost = current.g_cost + getCost(c, a.pos); a.g_cost = current.g_cost + getCost(c, a.pos);
a.h_cost = (int) (heuristic.getCost(a.pos, end) * getMinCost()); a.h_cost = (int) (heuristic.getCost(a.pos, end) * getMinCost());
a.parent = current; a.parent = current;
if (!closed.contains(a)) { if (!closed.contains(a)) {
if (open.contains(a)) { if (open.contains(a)) {
boolean needSort = false; boolean needSort = false;
// find where it is // find where it is
for (final Node n : open) { for (final Node n : open) {
if (n.pos.equals(a.pos)) { // found it if (n.pos.equals(a.pos)) { // found it
@ -117,51 +117,52 @@ public abstract class PathFinder {
break; break;
} }
} }
if (needSort) Collections.sort(open, F_COMPARATOR); if (needSort) Collections.sort(open, F_COMPARATOR);
} else { } else {
open.add(a); open.add(a);
} }
} }
} }
} }
if (current == null) { if (current == null) {
return null; // no path found return null; // no path found
} }
final LinkedList<Coord> path = new LinkedList<>(); final LinkedList<Coord> path = new LinkedList<>();
// extract path elements // extract path elements
while (current != null) { while (current != null) {
path.addFirst(current.pos); path.addFirst(current.pos);
current = current.parent; current = current.parent;
} }
return path; return path;
} }
private static class Node { private static class Node {
Coord pos; Coord pos;
int g_cost; // to get there int g_cost; // to get there
int h_cost; // to target int h_cost; // to target
Node parent; Node parent;
public Node(Coord pos) { public Node(Coord pos)
{
this.pos = pos; this.pos = pos;
} }
int fCost() int fCost()
{ {
return g_cost + h_cost; return g_cost + h_cost;
} }
@Override @Override
public int hashCode() public int hashCode()
{ {
@ -170,8 +171,8 @@ public abstract class PathFinder {
result = prime * result + ((pos == null) ? 0 : pos.hashCode()); result = prime * result + ((pos == null) ? 0 : pos.hashCode());
return result; return result;
} }
@Override @Override
public boolean equals(Object obj) public boolean equals(Object obj)
{ {
@ -184,63 +185,63 @@ public abstract class PathFinder {
} else if (!pos.equals(other.pos)) return false; } else if (!pos.equals(other.pos)) return false;
return true; return true;
} }
@Override @Override
public String toString() public String toString()
{ {
return "N " + pos + ", G =" + g_cost + ", H = " + h_cost; return "N " + pos + ", G =" + g_cost + ", H = " + h_cost;
} }
} }
private static class FComparator implements Comparator<Node> { private static class FComparator implements Comparator<Node> {
@Override @Override
public int compare(Node n1, Node n2) public int compare(Node n1, Node n2)
{ {
return n1.fCost() - n2.fCost(); return n1.fCost() - n2.fCost();
} }
} }
public void setIgnoreEnd(boolean ignoreEnd) public void setIgnoreEnd(boolean ignoreEnd)
{ {
this.ignoreEnd = ignoreEnd; this.ignoreEnd = ignoreEnd;
} }
public void setIgnoreStart(boolean ignoreStart) public void setIgnoreStart(boolean ignoreStart)
{ {
this.ignoreStart = ignoreStart; this.ignoreStart = ignoreStart;
} }
/** /**
* @return used heuristic * @return used heuristic
*/ */
protected abstract Heuristic getHeuristic(); protected abstract Heuristic getHeuristic();
protected abstract List<Move> getWalkSides(); protected abstract List<Move> getWalkSides();
/** /**
* @param pos tile pos * @param pos tile pos
* @return true if the tile is walkable * @return true if the tile is walkable
*/ */
public abstract boolean isAccessible(Coord pos); public abstract boolean isAccessible(Coord pos);
/** /**
* Cost of walking onto a tile. It's useful to use ie. 10 for basic step. * Cost of walking onto a tile. It's useful to use ie. 10 for basic step.
* *
* @param from last tile * @param from last tile
* @param to current tile * @param to current tile
* @return cost * @return cost
*/ */
protected abstract int getCost(Coord from, Coord to); protected abstract int getCost(Coord from, Coord to);
/** /**
* @return lowest cost. Used to multiply heuristics. * @return lowest cost. Used to multiply heuristics.
*/ */

@ -10,51 +10,52 @@ import mightypork.utils.math.algo.Move;
/** /**
* Pathfinder proxy. Can be used to override individual methods but keep the * Pathfinder proxy. Can be used to override individual methods but keep the
* rest as is. * rest as is.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class PathFinderProxy extends PathFinder { public class PathFinderProxy extends PathFinder {
private final PathFinder source; private final PathFinder source;
public PathFinderProxy(PathFinder other) { public PathFinderProxy(PathFinder other)
{
this.source = other; this.source = other;
} }
@Override @Override
public boolean isAccessible(Coord pos) public boolean isAccessible(Coord pos)
{ {
return source.isAccessible(pos); return source.isAccessible(pos);
} }
@Override @Override
public int getCost(Coord from, Coord to) public int getCost(Coord from, Coord to)
{ {
return source.getCost(from, to); return source.getCost(from, to);
} }
@Override @Override
public int getMinCost() public int getMinCost()
{ {
return source.getMinCost(); return source.getMinCost();
} }
@Override @Override
protected Heuristic getHeuristic() protected Heuristic getHeuristic()
{ {
return source.getHeuristic(); return source.getHeuristic();
} }
@Override @Override
protected List<Move> getWalkSides() protected List<Move> getWalkSides()
{ {
return source.getWalkSides(); return source.getWalkSides();
} }
} }

@ -6,7 +6,7 @@ import mightypork.utils.math.algo.pathfinding.Heuristic;
public class DiagonalHeuristic extends Heuristic { public class DiagonalHeuristic extends Heuristic {
@Override @Override
public double getCost(Coord pos, Coord target) public double getCost(Coord pos, Coord target)
{ {

@ -6,7 +6,7 @@ import mightypork.utils.math.algo.pathfinding.Heuristic;
public class ManhattanHeuristic extends Heuristic { public class ManhattanHeuristic extends Heuristic {
@Override @Override
public double getCost(Coord pos, Coord target) public double getCost(Coord pos, Coord target)
{ {

@ -3,14 +3,14 @@ package mightypork.utils.math.angles;
/** /**
* Common angles functionality * Common angles functionality
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
class Angles { class Angles {
/** /**
* Delta of two angles (positive or negative - positive is CCW) * Delta of two angles (positive or negative - positive is CCW)
* *
* @param alpha first angle * @param alpha first angle
* @param beta second angle * @param beta second angle
* @param fullAngle value of full angle * @param fullAngle value of full angle
@ -22,14 +22,14 @@ class Angles {
alpha = norm(alpha + fullAngle / 2D, fullAngle); alpha = norm(alpha + fullAngle / 2D, fullAngle);
beta = norm(beta + fullAngle / 2D, fullAngle); beta = norm(beta + fullAngle / 2D, fullAngle);
} }
return beta - alpha; return beta - alpha;
} }
/** /**
* Difference of two angles (same as delta, but always positive) * Difference of two angles (same as delta, but always positive)
* *
* @param alpha first angle * @param alpha first angle
* @param beta second angle * @param beta second angle
* @param fullAngle value of full angle * @param fullAngle value of full angle
@ -39,11 +39,11 @@ class Angles {
{ {
return Math.abs(delta(alpha, beta, fullAngle)); return Math.abs(delta(alpha, beta, fullAngle));
} }
/** /**
* Normalize angle to 0-full range * Normalize angle to 0-full range
* *
* @param angle angle * @param angle angle
* @param fullAngle full angle * @param fullAngle full angle
* @return angle normalized * @return angle normalized

@ -3,11 +3,11 @@ package mightypork.utils.math.angles;
/** /**
* Angle calculations for degrees. * Angle calculations for degrees.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class Deg { public class Deg {
/** 180° in degrees */ /** 180° in degrees */
public static final double a180 = 180; public static final double a180 = 180;
/** 270° in degrees */ /** 270° in degrees */
@ -18,11 +18,11 @@ public class Deg {
public static final double a45 = 45; public static final double a45 = 45;
/** 90° in degrees */ /** 90° in degrees */
public static final double a90 = 90; public static final double a90 = 90;
/** /**
* Subtract two angles alpha - beta * Subtract two angles alpha - beta
* *
* @param alpha first angle * @param alpha first angle
* @param beta second angle * @param beta second angle
* @return (alpha - beta) in degrees * @return (alpha - beta) in degrees
@ -31,11 +31,11 @@ public class Deg {
{ {
return Angles.delta(alpha, beta, a360); return Angles.delta(alpha, beta, a360);
} }
/** /**
* Difference of two angles (absolute value of delta) * Difference of two angles (absolute value of delta)
* *
* @param alpha first angle * @param alpha first angle
* @param beta second angle * @param beta second angle
* @return difference in radians * @return difference in radians
@ -44,11 +44,11 @@ public class Deg {
{ {
return Angles.diff(alpha, beta, a360); return Angles.diff(alpha, beta, a360);
} }
/** /**
* Cosinus in degrees * Cosinus in degrees
* *
* @param deg angle in degrees * @param deg angle in degrees
* @return cosinus * @return cosinus
*/ */
@ -56,11 +56,11 @@ public class Deg {
{ {
return Math.cos(toRad(deg)); return Math.cos(toRad(deg));
} }
/** /**
* Sinus in degrees * Sinus in degrees
* *
* @param deg angle in degrees * @param deg angle in degrees
* @return sinus * @return sinus
*/ */
@ -68,11 +68,11 @@ public class Deg {
{ {
return Math.sin(toRad(deg)); return Math.sin(toRad(deg));
} }
/** /**
* Tangents in degrees * Tangents in degrees
* *
* @param deg angle in degrees * @param deg angle in degrees
* @return tangents * @return tangents
*/ */
@ -80,11 +80,11 @@ public class Deg {
{ {
return Math.tan(toRad(deg)); return Math.tan(toRad(deg));
} }
/** /**
* Angle normalized to 0-360 range * Angle normalized to 0-360 range
* *
* @param angle angle to normalize * @param angle angle to normalize
* @return normalized angle * @return normalized angle
*/ */
@ -92,11 +92,11 @@ public class Deg {
{ {
return Angles.norm(angle, a360); return Angles.norm(angle, a360);
} }
/** /**
* Convert to radians * Convert to radians
* *
* @param deg degrees * @param deg degrees
* @return radians * @return radians
*/ */
@ -104,11 +104,11 @@ public class Deg {
{ {
return Math.toRadians(deg); return Math.toRadians(deg);
} }
/** /**
* Round angle to 0,45,90,135... * Round angle to 0,45,90,135...
* *
* @param deg angle in deg. to round * @param deg angle in deg. to round
* @param increment rounding increment (45 - round to 0,45,90...) * @param increment rounding increment (45 - round to 0,45,90...)
* @return rounded * @return rounded
@ -123,11 +123,11 @@ public class Deg {
if (a == 360) a = 0; if (a == 360) a = 0;
return (int) Math.round(a); return (int) Math.round(a);
} }
/** /**
* Round angle to 0,15,30,45,60,75,90... * Round angle to 0,15,30,45,60,75,90...
* *
* @param deg angle in deg to round * @param deg angle in deg to round
* @return rounded * @return rounded
*/ */
@ -135,11 +135,11 @@ public class Deg {
{ {
return roundToIncrement(deg, 15); return roundToIncrement(deg, 15);
} }
/** /**
* Round angle to 0,45,90,135... * Round angle to 0,45,90,135...
* *
* @param deg angle in deg. to round * @param deg angle in deg. to round
* @return rounded * @return rounded
*/ */
@ -147,11 +147,11 @@ public class Deg {
{ {
return roundToIncrement(deg, 45); return roundToIncrement(deg, 45);
} }
/** /**
* Round angle to 0,90,180,270 * Round angle to 0,90,180,270
* *
* @param deg angle in deg. to round * @param deg angle in deg. to round
* @return rounded * @return rounded
*/ */

@ -3,11 +3,11 @@ package mightypork.utils.math.angles;
/** /**
* Angle calculations for radians. * Angle calculations for radians.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class Rad { public class Rad {
/** 180° in radians */ /** 180° in radians */
public static final double a180 = Math.PI; public static final double a180 = Math.PI;
/** 270° in radians */ /** 270° in radians */
@ -18,11 +18,11 @@ public class Rad {
public static final double a45 = Math.PI / 4D; public static final double a45 = Math.PI / 4D;
/** 90° in radians */ /** 90° in radians */
public static final double a90 = Math.PI / 2D; public static final double a90 = Math.PI / 2D;
/** /**
* Subtract two angles alpha - beta * Subtract two angles alpha - beta
* *
* @param alpha first angle * @param alpha first angle
* @param beta second angle * @param beta second angle
* @return (alpha - beta) in radians * @return (alpha - beta) in radians
@ -31,11 +31,11 @@ public class Rad {
{ {
return Angles.delta(alpha, beta, a360); return Angles.delta(alpha, beta, a360);
} }
/** /**
* Difference of two angles (absolute value of delta) * Difference of two angles (absolute value of delta)
* *
* @param alpha first angle * @param alpha first angle
* @param beta second angle * @param beta second angle
* @return difference in radians * @return difference in radians
@ -44,11 +44,11 @@ public class Rad {
{ {
return Angles.delta(alpha, beta, a360); return Angles.delta(alpha, beta, a360);
} }
/** /**
* Cos * Cos
* *
* @param rad angle in rads * @param rad angle in rads
* @return cos * @return cos
*/ */
@ -56,11 +56,11 @@ public class Rad {
{ {
return Math.cos(rad); return Math.cos(rad);
} }
/** /**
* Sin * Sin
* *
* @param rad angle in rads * @param rad angle in rads
* @return sin * @return sin
*/ */
@ -68,11 +68,11 @@ public class Rad {
{ {
return Math.sin(rad); return Math.sin(rad);
} }
/** /**
* Tan * Tan
* *
* @param rad angle in rads * @param rad angle in rads
* @return tan * @return tan
*/ */
@ -80,11 +80,11 @@ public class Rad {
{ {
return Math.tan(rad); return Math.tan(rad);
} }
/** /**
* Angle normalized to 0-2*PI range * Angle normalized to 0-2*PI range
* *
* @param angle angle to normalize * @param angle angle to normalize
* @return normalized angle * @return normalized angle
*/ */
@ -92,11 +92,11 @@ public class Rad {
{ {
return Angles.norm(angle, a360); return Angles.norm(angle, a360);
} }
/** /**
* Convert to degrees * Convert to degrees
* *
* @param rad radians * @param rad radians
* @return degrees * @return degrees
*/ */

@ -10,124 +10,128 @@ import mightypork.utils.math.constraints.num.NumBound;
public abstract class Animator implements NumBound, Updateable, Pauseable { public abstract class Animator implements NumBound, Updateable, Pauseable {
private final NumAnimated numAnim; private final NumAnimated numAnim;
private final Num animatorValue; private final Num animatorValue;
private final double highValue; private final double highValue;
private final double lowValue; private final double lowValue;
public Animator(double period) { public Animator(double period)
{
this(0, 1, period, Easing.LINEAR); this(0, 1, period, Easing.LINEAR);
} }
public Animator(double start, double end, double period) { public Animator(double start, double end, double period)
{
this(start, end, period, Easing.LINEAR); this(start, end, period, Easing.LINEAR);
} }
public Animator(double period, Easing easing) { public Animator(double period, Easing easing)
{
this(0, 1, period, easing); this(0, 1, period, easing);
} }
public Animator(double start, double end, double period, Easing easing) { public Animator(double start, double end, double period, Easing easing)
{
numAnim = new NumAnimated(0, easing); numAnim = new NumAnimated(0, easing);
numAnim.setDefaultDuration(period); numAnim.setDefaultDuration(period);
this.lowValue = start; this.lowValue = start;
this.highValue = end; this.highValue = end;
this.animatorValue = numAnim.mul(end - start).add(start); this.animatorValue = numAnim.mul(end - start).add(start);
} }
@Override @Override
public void pause() public void pause()
{ {
numAnim.pause(); numAnim.pause();
} }
public void start() public void start()
{ {
resume(); resume();
} }
@Override @Override
public void resume() public void resume()
{ {
numAnim.resume(); numAnim.resume();
} }
@Override @Override
public boolean isPaused() public boolean isPaused()
{ {
return numAnim.isPaused(); return numAnim.isPaused();
} }
public void reset() public void reset()
{ {
numAnim.reset(); numAnim.reset();
} }
public void restart() public void restart()
{ {
reset(); reset();
resume(); resume();
} }
public void setDuration(double secs) public void setDuration(double secs)
{ {
numAnim.setDefaultDuration(secs); numAnim.setDefaultDuration(secs);
} }
public double getDuration() public double getDuration()
{ {
return numAnim.getDefaultDuration(); return numAnim.getDefaultDuration();
} }
@Override @Override
public Num getNum() public Num getNum()
{ {
return animatorValue; return animatorValue;
} }
public double getValue() public double getValue()
{ {
return animatorValue.value(); return animatorValue.value();
} }
@Override @Override
public void update(double delta) public void update(double delta)
{ {
numAnim.update(delta); numAnim.update(delta);
if (numAnim.isFinished()) nextCycle(numAnim); if (numAnim.isFinished()) nextCycle(numAnim);
} }
@Stub @Stub
protected abstract void nextCycle(NumAnimated anim); protected abstract void nextCycle(NumAnimated anim);
public void setProgress(double value) public void setProgress(double value)
{ {
final double target = numAnim.getEnd(); final double target = numAnim.getEnd();
numAnim.setTo(Calc.clamp(value, lowValue, highValue)); numAnim.setTo(Calc.clamp(value, lowValue, highValue));
numAnim.animate((target < value ? highValue : lowValue), target, numAnim.getDefaultDuration()); numAnim.animate((target < value ? highValue : lowValue), target, numAnim.getDefaultDuration());
} }
public double getProgress() public double getProgress()
{ {
return numAnim.value(); return numAnim.value();

@ -3,34 +3,38 @@ package mightypork.utils.math.animation;
/** /**
* Animator that upon reaching max, animates back down and then up again * Animator that upon reaching max, animates back down and then up again
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class AnimatorBounce extends Animator { public class AnimatorBounce extends Animator {
private boolean wasUp = false; private boolean wasUp = false;
public AnimatorBounce(double start, double end, double period, Easing easing) { public AnimatorBounce(double start, double end, double period, Easing easing)
{
super(start, end, period, easing); super(start, end, period, easing);
} }
public AnimatorBounce(double start, double end, double period) { public AnimatorBounce(double start, double end, double period)
{
super(start, end, period); super(start, end, period);
} }
public AnimatorBounce(double period, Easing easing) { public AnimatorBounce(double period, Easing easing)
{
super(period, easing); super(period, easing);
} }
public AnimatorBounce(double period) { public AnimatorBounce(double period)
{
super(period); super(period);
} }
@Override @Override
protected void nextCycle(NumAnimated anim) protected void nextCycle(NumAnimated anim)
{ {
@ -39,8 +43,8 @@ public class AnimatorBounce extends Animator {
} else { } else {
anim.fadeIn(); anim.fadeIn();
} }
wasUp = !wasUp; wasUp = !wasUp;
} }
} }

@ -4,36 +4,40 @@ package mightypork.utils.math.animation;
/** /**
* Animator that upon reaching top, jumps straight to zero and continues another * Animator that upon reaching top, jumps straight to zero and continues another
* cycle. * cycle.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class AnimatorRewind extends Animator { public class AnimatorRewind extends Animator {
public AnimatorRewind(double start, double end, double period, Easing easing) { public AnimatorRewind(double start, double end, double period, Easing easing)
{
super(start, end, period, easing); super(start, end, period, easing);
} }
public AnimatorRewind(double start, double end, double period) { public AnimatorRewind(double start, double end, double period)
{
super(start, end, period); super(start, end, period);
} }
public AnimatorRewind(double period, Easing easing) { public AnimatorRewind(double period, Easing easing)
{
super(period, easing); super(period, easing);
} }
public AnimatorRewind(double period) { public AnimatorRewind(double period)
{
super(period); super(period);
} }
@Override @Override
protected void nextCycle(NumAnimated anim) protected void nextCycle(NumAnimated anim)
{ {
anim.reset(); anim.reset();
anim.fadeIn(); anim.fadeIn();
} }
} }

@ -3,23 +3,23 @@ package mightypork.utils.math.animation;
/** /**
* EasingFunction function. * EasingFunction function.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public abstract class Easing { public abstract class Easing {
/** /**
* Get value at time t. * Get value at time t.
* *
* @param t time parameter (t = 1..1) * @param t time parameter (t = 1..1)
* @return value at given t (0..1, can exceed if needed) * @return value at given t (0..1, can exceed if needed)
*/ */
public abstract double get(double t); public abstract double get(double t);
/** /**
* Reverse an easing (factory method) * Reverse an easing (factory method)
* *
* @param original original easing * @param original original easing
* @return reversed easing * @return reversed easing
*/ */
@ -27,11 +27,11 @@ public abstract class Easing {
{ {
return new Reverse(original); return new Reverse(original);
} }
/** /**
* Combine two easings (factory method) * Combine two easings (factory method)
* *
* @param in initial easing * @param in initial easing
* @param out terminal easing * @param out terminal easing
* @return product * @return product
@ -40,12 +40,12 @@ public abstract class Easing {
{ {
return new Composite(in, out); return new Composite(in, out);
} }
/** /**
* Create "bilinear" easing - compose of straight and reverse. (factory * Create "bilinear" easing - compose of straight and reverse. (factory
* method) * method)
* *
* @param in initial easing * @param in initial easing
* @return product * @return product
*/ */
@ -53,55 +53,57 @@ public abstract class Easing {
{ {
return combine(in, reverse(in)); return combine(in, reverse(in));
} }
/** /**
* Reverse EasingFunction * Reverse EasingFunction
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
private static class Reverse extends Easing { private static class Reverse extends Easing {
private final Easing ea; private final Easing ea;
/** /**
* @param in Easing to reverse * @param in Easing to reverse
*/ */
public Reverse(Easing in) { public Reverse(Easing in)
{
this.ea = in; this.ea = in;
} }
@Override @Override
public double get(double t) public double get(double t)
{ {
return 1 - ea.get(1 - t); return 1 - ea.get(1 - t);
} }
} }
/** /**
* Composite EasingFunction (0-0.5 EasingFunction A, 0.5-1 EasingFunction B) * Composite EasingFunction (0-0.5 EasingFunction A, 0.5-1 EasingFunction B)
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
private static class Composite extends Easing { private static class Composite extends Easing {
private final Easing in; private final Easing in;
private final Easing out; private final Easing out;
/** /**
* Create a composite EasingFunction * Create a composite EasingFunction
* *
* @param in initial EasingFunction * @param in initial EasingFunction
* @param out terminal EasingFunction * @param out terminal EasingFunction
*/ */
public Composite(Easing in, Easing out) { public Composite(Easing in, Easing out)
{
this.in = in; this.in = in;
this.out = out; this.out = out;
} }
@Override @Override
public double get(double t) public double get(double t)
{ {
@ -109,172 +111,172 @@ public abstract class Easing {
return 0.5 + out.get(2 * t - 1) * 0.5; return 0.5 + out.get(2 * t - 1) * 0.5;
} }
} }
/** No easing; At t=0.5 goes high. */ /** No easing; At t=0.5 goes high. */
public static final Easing NONE = new Easing() { public static final Easing NONE = new Easing() {
@Override @Override
public double get(double t) public double get(double t)
{ {
return (t < 0.5 ? 0 : 1); return (t < 0.5 ? 0 : 1);
} }
}; };
/** Linear (y=t) easing */ /** Linear (y=t) easing */
public static final Easing LINEAR = new Easing() { public static final Easing LINEAR = new Easing() {
@Override @Override
public double get(double t) public double get(double t)
{ {
return t; return t;
} }
}; };
/** Quadratic (y=t^2) easing in */ /** Quadratic (y=t^2) easing in */
public static final Easing QUADRATIC_IN = new Easing() { public static final Easing QUADRATIC_IN = new Easing() {
@Override @Override
public double get(double t) public double get(double t)
{ {
return t * t; return t * t;
} }
}; };
/** Quadratic (y=t^2) easing out */ /** Quadratic (y=t^2) easing out */
public static final Easing QUADRATIC_OUT = reverse(QUADRATIC_IN); public static final Easing QUADRATIC_OUT = reverse(QUADRATIC_IN);
/** Quadratic (y=t^2) easing both */ /** Quadratic (y=t^2) easing both */
public static final Easing QUADRATIC_BOTH = inOut(QUADRATIC_IN); public static final Easing QUADRATIC_BOTH = inOut(QUADRATIC_IN);
/** Cubic (y=t^3) easing in */ /** Cubic (y=t^3) easing in */
public static final Easing CUBIC_IN = new Easing() { public static final Easing CUBIC_IN = new Easing() {
@Override @Override
public double get(double t) public double get(double t)
{ {
return t * t * t; return t * t * t;
} }
}; };
/** Cubic (y=t^3) easing out */ /** Cubic (y=t^3) easing out */
public static final Easing CUBIC_OUT = reverse(CUBIC_IN); public static final Easing CUBIC_OUT = reverse(CUBIC_IN);
/** Cubic (y=t^3) easing both */ /** Cubic (y=t^3) easing both */
public static final Easing CUBIC_BOTH = inOut(CUBIC_IN); public static final Easing CUBIC_BOTH = inOut(CUBIC_IN);
/** Quartic (y=t^4) easing in */ /** Quartic (y=t^4) easing in */
public static final Easing QUARTIC_IN = new Easing() { public static final Easing QUARTIC_IN = new Easing() {
@Override @Override
public double get(double t) public double get(double t)
{ {
return t * t * t * t; return t * t * t * t;
} }
}; };
/** Quartic (y=t^4) easing out */ /** Quartic (y=t^4) easing out */
public static final Easing QUARTIC_OUT = reverse(QUADRATIC_IN); public static final Easing QUARTIC_OUT = reverse(QUADRATIC_IN);
/** Quartic (y=t^4) easing both */ /** Quartic (y=t^4) easing both */
public static final Easing QUARTIC_BOTH = inOut(QUADRATIC_IN); public static final Easing QUARTIC_BOTH = inOut(QUADRATIC_IN);
/** Quintic (y=t^5) easing in */ /** Quintic (y=t^5) easing in */
public static final Easing QUINTIC_IN = new Easing() { public static final Easing QUINTIC_IN = new Easing() {
@Override @Override
public double get(double t) public double get(double t)
{ {
return t * t * t * t * t; return t * t * t * t * t;
} }
}; };
/** Quintic (y=t^5) easing out */ /** Quintic (y=t^5) easing out */
public static final Easing QUINTIC_OUT = reverse(QUINTIC_IN); public static final Easing QUINTIC_OUT = reverse(QUINTIC_IN);
/** Quintic (y=t^5) easing both */ /** Quintic (y=t^5) easing both */
public static final Easing QUINTIC_BOTH = inOut(QUINTIC_IN); public static final Easing QUINTIC_BOTH = inOut(QUINTIC_IN);
/** Sine easing in */ /** Sine easing in */
public static final Easing SINE_IN = new Easing() { public static final Easing SINE_IN = new Easing() {
@Override @Override
public double get(double t) public double get(double t)
{ {
return 1 - Math.cos(t * (Math.PI / 2)); return 1 - Math.cos(t * (Math.PI / 2));
} }
}; };
/** Sine easing out */ /** Sine easing out */
public static final Easing SINE_OUT = reverse(SINE_IN); public static final Easing SINE_OUT = reverse(SINE_IN);
/** Sine easing both */ /** Sine easing both */
public static final Easing SINE_BOTH = inOut(SINE_IN); public static final Easing SINE_BOTH = inOut(SINE_IN);
/** Exponential easing in */ /** Exponential easing in */
public static final Easing EXPO_IN = new Easing() { public static final Easing EXPO_IN = new Easing() {
@Override @Override
public double get(double t) public double get(double t)
{ {
return Math.pow(2, 10 * (t - 1)); return Math.pow(2, 10 * (t - 1));
} }
}; };
/** Exponential easing out */ /** Exponential easing out */
public static final Easing EXPO_OUT = reverse(EXPO_IN); public static final Easing EXPO_OUT = reverse(EXPO_IN);
/** Exponential easing both */ /** Exponential easing both */
public static final Easing EXPO_BOTH = inOut(EXPO_IN); public static final Easing EXPO_BOTH = inOut(EXPO_IN);
/** Circular easing in */ /** Circular easing in */
public static final Easing CIRC_IN = new Easing() { public static final Easing CIRC_IN = new Easing() {
@Override @Override
public double get(double t) public double get(double t)
{ {
return 1 - Math.sqrt(1 - t * t); return 1 - Math.sqrt(1 - t * t);
} }
}; };
/** Circular easing out */ /** Circular easing out */
public static final Easing CIRC_OUT = reverse(CIRC_IN); public static final Easing CIRC_OUT = reverse(CIRC_IN);
/** Circular easing both */ /** Circular easing both */
public static final Easing CIRC_BOTH = inOut(CIRC_IN); public static final Easing CIRC_BOTH = inOut(CIRC_IN);
/** Bounce easing in */ /** Bounce easing in */
public static final Easing BOUNCE_OUT = new Easing() { public static final Easing BOUNCE_OUT = new Easing() {
@Override @Override
public double get(double t) public double get(double t)
{ {
if (t < (1 / 2.75f)) { if (t < (1 / 2.75f)) {
return (7.5625f * t * t); return (7.5625f * t * t);
} else if (t < (2 / 2.75f)) { } else if (t < (2 / 2.75f)) {
t -= (1.5f / 2.75f); t -= (1.5f / 2.75f);
return (7.5625f * t * t + 0.75f); return (7.5625f * t * t + 0.75f);
} else if (t < (2.5 / 2.75)) { } else if (t < (2.5 / 2.75)) {
t -= (2.25f / 2.75f); t -= (2.25f / 2.75f);
return (7.5625f * t * t + 0.9375f); return (7.5625f * t * t + 0.9375f);
} else { } else {
t -= (2.625f / 2.75f); t -= (2.625f / 2.75f);
return (7.5625f * t * t + 0.984375f); return (7.5625f * t * t + 0.984375f);
} }
} }
}; };
/** Bounce easing out */ /** Bounce easing out */
public static final Easing BOUNCE_IN = reverse(BOUNCE_OUT); public static final Easing BOUNCE_IN = reverse(BOUNCE_OUT);
/** Bounce easing both */ /** Bounce easing both */
public static final Easing BOUNCE_BOTH = inOut(BOUNCE_IN); public static final Easing BOUNCE_BOTH = inOut(BOUNCE_IN);
/** Back easing in */ /** Back easing in */
public static final Easing BACK_IN = new Easing() { public static final Easing BACK_IN = new Easing() {
@Override @Override
public double get(double t) public double get(double t)
{ {
@ -282,31 +284,31 @@ public abstract class Easing {
return t * t * ((s + 1) * t - s); return t * t * ((s + 1) * t - s);
} }
}; };
/** Back easing out */ /** Back easing out */
public static final Easing BACK_OUT = reverse(BACK_IN); public static final Easing BACK_OUT = reverse(BACK_IN);
/** Back easing both */ /** Back easing both */
public static final Easing BACK_BOTH = inOut(BACK_IN); public static final Easing BACK_BOTH = inOut(BACK_IN);
/** Elastic easing in */ /** Elastic easing in */
public static final Easing ELASTIC_IN = new Easing() { public static final Easing ELASTIC_IN = new Easing() {
@Override @Override
public double get(double t) public double get(double t)
{ {
if (t == 0) return 0; if (t == 0) return 0;
if (t == 1) return 1; if (t == 1) return 1;
final double p = .3f; final double p = .3f;
final double s = p / 4; final double s = p / 4;
return -(Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); return -(Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
} }
}; };
/** Elastic easing out */ /** Elastic easing out */
public static final Easing ELASTIC_OUT = reverse(ELASTIC_IN); public static final Easing ELASTIC_OUT = reverse(ELASTIC_IN);
/** Elastic easing both */ /** Elastic easing both */
public static final Easing ELASTIC_BOTH = inOut(ELASTIC_IN); public static final Easing ELASTIC_BOTH = inOut(ELASTIC_IN);
} }

@ -11,109 +11,115 @@ import mightypork.utils.math.constraints.num.var.NumMutable;
* Double which supports delta timing.<br> * Double which supports delta timing.<br>
* When both in and out easings are set differently, then they'll be used for * When both in and out easings are set differently, then they'll be used for
* fade-in and fade-out respectively. Otherwise both use the same. * fade-in and fade-out respectively. Otherwise both use the same.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class NumAnimated extends NumMutable implements Updateable, Pauseable { public class NumAnimated extends NumMutable implements Updateable, Pauseable {
/** target double */ /** target double */
protected double to = 0; protected double to = 0;
/** last tick double */ /** last tick double */
protected double from = 0; protected double from = 0;
/** how long the transition should last */ /** how long the transition should last */
protected double duration = 0; protected double duration = 0;
/** current anim time */ /** current anim time */
protected double elapsedTime = 0; protected double elapsedTime = 0;
/** True if this animator is paused */ /** True if this animator is paused */
protected boolean paused = false; protected boolean paused = false;
/** Easing fn */ /** Easing fn */
protected Easing easingCurrent = Easing.LINEAR; protected Easing easingCurrent = Easing.LINEAR;
protected Easing easingOut = Easing.LINEAR; protected Easing easingOut = Easing.LINEAR;
protected Easing easingIn = Easing.LINEAR; protected Easing easingIn = Easing.LINEAR;
/** Default duration (seconds) */ /** Default duration (seconds) */
private double defaultDuration = 1; private double defaultDuration = 1;
/** /**
* With linear easing * With linear easing
* *
* @param value initial value * @param value initial value
*/ */
public NumAnimated(double value) { public NumAnimated(double value)
{
setTo(value); setTo(value);
} }
/** /**
* Create animator with easing * Create animator with easing
* *
* @param value initial value * @param value initial value
* @param easing easing function * @param easing easing function
*/ */
public NumAnimated(double value, Easing easing) { public NumAnimated(double value, Easing easing)
{
this(value); this(value);
setEasing(easing); setEasing(easing);
} }
/** /**
* Create animator with easing * Create animator with easing
* *
* @param value initial value * @param value initial value
* @param easingIn easing function (fade in) * @param easingIn easing function (fade in)
* @param easingOut easing function (fade out) * @param easingOut easing function (fade out)
*/ */
public NumAnimated(double value, Easing easingIn, Easing easingOut) { public NumAnimated(double value, Easing easingIn, Easing easingOut)
{
this(value); this(value);
setEasing(easingIn, easingOut); setEasing(easingIn, easingOut);
} }
/** /**
* Create animator with easing * Create animator with easing
* *
* @param value initial value * @param value initial value
* @param easing easing function * @param easing easing function
* @param defaultDuration default fade duration * @param defaultDuration default fade duration
*/ */
public NumAnimated(double value, Easing easing, double defaultDuration) { public NumAnimated(double value, Easing easing, double defaultDuration)
{
this(value); this(value);
setEasing(easing); setEasing(easing);
setDefaultDuration(defaultDuration); setDefaultDuration(defaultDuration);
} }
/** /**
* Create animator with easing * Create animator with easing
* *
* @param value initial value * @param value initial value
* @param easingIn easing function (fade in) * @param easingIn easing function (fade in)
* @param easingOut easing function (fade out) * @param easingOut easing function (fade out)
* @param defaultDuration default fade duration * @param defaultDuration default fade duration
*/ */
public NumAnimated(double value, Easing easingIn, Easing easingOut, double defaultDuration) { public NumAnimated(double value, Easing easingIn, Easing easingOut, double defaultDuration)
{
this(value); this(value);
setEasing(easingIn, easingOut); setEasing(easingIn, easingOut);
setDefaultDuration(defaultDuration); setDefaultDuration(defaultDuration);
} }
/** /**
* Create as copy of another * Create as copy of another
* *
* @param other other animator * @param other other animator
*/ */
public NumAnimated(NumAnimated other) { public NumAnimated(NumAnimated other)
{
setTo(other); setTo(other);
} }
/** /**
* @param easing easing function * @param easing easing function
*/ */
@ -121,8 +127,8 @@ public class NumAnimated extends NumMutable implements Updateable, Pauseable {
{ {
this.easingCurrent = this.easingIn = this.easingOut = easing; this.easingCurrent = this.easingIn = this.easingOut = easing;
} }
/** /**
* @param easingIn easing for fade in * @param easingIn easing for fade in
* @param easingOut easing for fade out * @param easingOut easing for fade out
@ -133,30 +139,30 @@ public class NumAnimated extends NumMutable implements Updateable, Pauseable {
this.easingOut = easingOut; this.easingOut = easingOut;
this.easingCurrent = easingIn; this.easingCurrent = easingIn;
} }
/** /**
* Get start value * Get start value
* *
* @return number * @return number
*/ */
public double getStart() public double getStart()
{ {
return from; return from;
} }
/** /**
* Get end value * Get end value
* *
* @return number * @return number
*/ */
public double getEnd() public double getEnd()
{ {
return to; return to;
} }
/** /**
* @return current animation duration (seconds) * @return current animation duration (seconds)
*/ */
@ -164,8 +170,8 @@ public class NumAnimated extends NumMutable implements Updateable, Pauseable {
{ {
return duration; return duration;
} }
/** /**
* @return elapsed time in current animation (seconds) * @return elapsed time in current animation (seconds)
*/ */
@ -173,8 +179,8 @@ public class NumAnimated extends NumMutable implements Updateable, Pauseable {
{ {
return elapsedTime; return elapsedTime;
} }
/** /**
* @return default animation duration (seconds) * @return default animation duration (seconds)
*/ */
@ -182,8 +188,8 @@ public class NumAnimated extends NumMutable implements Updateable, Pauseable {
{ {
return defaultDuration; return defaultDuration;
} }
/** /**
* @param defaultDuration default animation duration (seconds) * @param defaultDuration default animation duration (seconds)
*/ */
@ -191,11 +197,11 @@ public class NumAnimated extends NumMutable implements Updateable, Pauseable {
{ {
this.defaultDuration = defaultDuration; this.defaultDuration = defaultDuration;
} }
/** /**
* Get value at delta time * Get value at delta time
* *
* @return the value * @return the value
*/ */
@Override @Override
@ -204,11 +210,11 @@ public class NumAnimated extends NumMutable implements Updateable, Pauseable {
if (duration == 0) return to; if (duration == 0) return to;
return Calc.interpolate(from, to, (elapsedTime / duration), easingCurrent); return Calc.interpolate(from, to, (elapsedTime / duration), easingCurrent);
} }
/** /**
* Get how much of the animation is already finished * Get how much of the animation is already finished
* *
* @return completion ratio (0 to 1) * @return completion ratio (0 to 1)
*/ */
public double getProgress() public double getProgress()
@ -216,13 +222,13 @@ public class NumAnimated extends NumMutable implements Updateable, Pauseable {
if (duration == 0) return 1; if (duration == 0) return 1;
return elapsedTime / duration; return elapsedTime / duration;
} }
@Override @Override
public void update(double delta) public void update(double delta)
{ {
if (paused || isFinished()) return; if (paused || isFinished()) return;
elapsedTime = Calc.clamp(elapsedTime + delta, 0, duration); elapsedTime = Calc.clamp(elapsedTime + delta, 0, duration);
if (isFinished()) { if (isFinished()) {
duration = 0; duration = 0;
@ -230,22 +236,22 @@ public class NumAnimated extends NumMutable implements Updateable, Pauseable {
from = to; from = to;
} }
} }
/** /**
* Get if animation is finished * Get if animation is finished
* *
* @return is finished * @return is finished
*/ */
public boolean isFinished() public boolean isFinished()
{ {
return duration == 0 || elapsedTime >= duration; return duration == 0 || elapsedTime >= duration;
} }
/** /**
* Set to a value (without animation) * Set to a value (without animation)
* *
* @param value * @param value
*/ */
@Override @Override
@ -255,11 +261,11 @@ public class NumAnimated extends NumMutable implements Updateable, Pauseable {
elapsedTime = 0; elapsedTime = 0;
duration = 0; duration = 0;
} }
/** /**
* Copy other * Copy other
* *
* @param other * @param other
*/ */
public void setTo(NumAnimated other) public void setTo(NumAnimated other)
@ -274,11 +280,11 @@ public class NumAnimated extends NumMutable implements Updateable, Pauseable {
this.easingOut = other.easingOut; this.easingOut = other.easingOut;
this.defaultDuration = other.defaultDuration; this.defaultDuration = other.defaultDuration;
} }
/** /**
* Animate between two states, start from current value (if it's in between) * Animate between two states, start from current value (if it's in between)
* *
* @param from start value * @param from start value
* @param to target state * @param to target state
* @param time animation time (secs) * @param time animation time (secs)
@ -286,45 +292,45 @@ public class NumAnimated extends NumMutable implements Updateable, Pauseable {
public void animate(double from, double to, double time) public void animate(double from, double to, double time)
{ {
final double current = value(); final double current = value();
this.from = from; this.from = from;
this.to = to; this.to = to;
final double progress = getProgressFromValue(current); final double progress = getProgressFromValue(current);
this.from = (progress > 0 ? current : from); this.from = (progress > 0 ? current : from);
this.duration = time * (1 - progress); this.duration = time * (1 - progress);
this.elapsedTime = 0; this.elapsedTime = 0;
} }
/** /**
* Get progress already elapsed based on current value.<br> * Get progress already elapsed based on current value.<br>
* Used to resume animation from current point in fading etc. * Used to resume animation from current point in fading etc.
* *
* @param value current value * @param value current value
* @return progress ratio 0-1 * @return progress ratio 0-1
*/ */
protected double getProgressFromValue(double value) protected double getProgressFromValue(double value)
{ {
double p = 0; double p = 0;
if (from == to) return 0; if (from == to) return 0;
if (value >= from && value <= to) { // up if (value >= from && value <= to) { // up
p = ((value - from) / (to - from)); p = ((value - from) / (to - from));
} else if (value >= to && value <= from) { // down } else if (value >= to && value <= from) { // down
p = ((from - value) / (from - to)); p = ((from - value) / (from - to));
} }
return p; return p;
} }
/** /**
* Animate to a value from current value * Animate to a value from current value
* *
* @param to target state * @param to target state
* @param duration animation duration (speeds) * @param duration animation duration (speeds)
*/ */
@ -335,11 +341,11 @@ public class NumAnimated extends NumMutable implements Updateable, Pauseable {
this.duration = duration; this.duration = duration;
this.elapsedTime = 0; this.elapsedTime = 0;
} }
/** /**
* Animate 0 to 1 * Animate 0 to 1
* *
* @param time animation time (secs) * @param time animation time (secs)
*/ */
public void fadeIn(double time) public void fadeIn(double time)
@ -347,11 +353,11 @@ public class NumAnimated extends NumMutable implements Updateable, Pauseable {
easingCurrent = easingIn; easingCurrent = easingIn;
animate(0, 1, time); animate(0, 1, time);
} }
/** /**
* Animate 1 to 0 * Animate 1 to 0
* *
* @param time animation time (secs) * @param time animation time (secs)
*/ */
public void fadeOut(double time) public void fadeOut(double time)
@ -359,8 +365,8 @@ public class NumAnimated extends NumMutable implements Updateable, Pauseable {
easingCurrent = easingOut; easingCurrent = easingOut;
animate(1, 0, time); animate(1, 0, time);
} }
/** /**
* Animate 0 to 1 with default duration * Animate 0 to 1 with default duration
*/ */
@ -369,8 +375,8 @@ public class NumAnimated extends NumMutable implements Updateable, Pauseable {
easingCurrent = easingIn; easingCurrent = easingIn;
animate(0, 1, defaultDuration); animate(0, 1, defaultDuration);
} }
/** /**
* Animate 1 to 0 with default duration * Animate 1 to 0 with default duration
*/ */
@ -379,11 +385,11 @@ public class NumAnimated extends NumMutable implements Updateable, Pauseable {
easingCurrent = easingOut; easingCurrent = easingOut;
animate(1, 0, defaultDuration); animate(1, 0, defaultDuration);
} }
/** /**
* Make a copy * Make a copy
* *
* @return copy * @return copy
*/ */
@Override @Override
@ -391,15 +397,15 @@ public class NumAnimated extends NumMutable implements Updateable, Pauseable {
{ {
return new NumAnimated(this); return new NumAnimated(this);
} }
@Override @Override
public String toString() public String toString()
{ {
return "Animation(" + from + " -> " + to + ", t=" + duration + "s, elapsed=" + elapsedTime + "s)"; return "Animation(" + from + " -> " + to + ", t=" + duration + "s, elapsed=" + elapsedTime + "s)";
} }
/** /**
* Set to zero and stop animation * Set to zero and stop animation
*/ */
@ -410,8 +416,8 @@ public class NumAnimated extends NumMutable implements Updateable, Pauseable {
duration = 0; duration = 0;
paused = false; paused = false;
} }
/** /**
* Stop animation, keep current value * Stop animation, keep current value
*/ */
@ -421,29 +427,29 @@ public class NumAnimated extends NumMutable implements Updateable, Pauseable {
elapsedTime = 0; elapsedTime = 0;
duration = 0; duration = 0;
} }
@Override @Override
public void pause() public void pause()
{ {
paused = true; paused = true;
} }
@Override @Override
public void resume() public void resume()
{ {
paused = false; paused = false;
} }
@Override @Override
public boolean isPaused() public boolean isPaused()
{ {
return paused; return paused;
} }
public boolean isInProgress() public boolean isInProgress()
{ {
return !isFinished() && !isPaused(); return !isFinished() && !isPaused();

@ -7,34 +7,37 @@ import mightypork.utils.math.angles.Deg;
/** /**
* Degree animator * Degree animator
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class NumAnimatedDeg extends NumAnimated { public class NumAnimatedDeg extends NumAnimated {
public NumAnimatedDeg(NumAnimated other) { public NumAnimatedDeg(NumAnimated other)
{
super(other); super(other);
} }
public NumAnimatedDeg(double value) { public NumAnimatedDeg(double value)
{
super(value); super(value);
} }
public NumAnimatedDeg(double value, Easing easing) { public NumAnimatedDeg(double value, Easing easing)
{
super(value, easing); super(value, easing);
} }
@Override @Override
public double value() public double value()
{ {
if (duration == 0) return Deg.norm(to); if (duration == 0) return Deg.norm(to);
return Calc.interpolateDeg(from, to, (elapsedTime / duration), easingCurrent); return Calc.interpolateDeg(from, to, (elapsedTime / duration), easingCurrent);
} }
@Override @Override
protected double getProgressFromValue(double value) protected double getProgressFromValue(double value)
{ {
@ -43,7 +46,7 @@ public class NumAnimatedDeg extends NumAnimated {
final double partial = Deg.diff(from, value); final double partial = Deg.diff(from, value);
return partial / whole; return partial / whole;
} }
return 0; return 0;
} }
} }

@ -7,34 +7,37 @@ import mightypork.utils.math.angles.Rad;
/** /**
* Radians animator * Radians animator
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class NumAnimatedRad extends NumAnimated { public class NumAnimatedRad extends NumAnimated {
public NumAnimatedRad(NumAnimated other) { public NumAnimatedRad(NumAnimated other)
{
super(other); super(other);
} }
public NumAnimatedRad(double value) { public NumAnimatedRad(double value)
{
super(value); super(value);
} }
public NumAnimatedRad(double value, Easing easing) { public NumAnimatedRad(double value, Easing easing)
{
super(value, easing); super(value, easing);
} }
@Override @Override
public double value() public double value()
{ {
if (duration == 0) return Rad.norm(to); if (duration == 0) return Rad.norm(to);
return Calc.interpolateRad(from, to, (elapsedTime / duration), easingCurrent); return Calc.interpolateRad(from, to, (elapsedTime / duration), easingCurrent);
} }
@Override @Override
protected double getProgressFromValue(double value) protected double getProgressFromValue(double value)
{ {
@ -43,7 +46,7 @@ public class NumAnimatedRad extends NumAnimated {
final double partial = Rad.diff(from, value); final double partial = Rad.diff(from, value);
return partial / whole; return partial / whole;
} }
return 0; return 0;
} }
} }

@ -10,64 +10,66 @@ import mightypork.utils.math.constraints.vect.var.VectMutable;
/** /**
* 3D coordinated with support for transitions, mutable. * 3D coordinated with support for transitions, mutable.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class VectAnimated extends VectMutable implements Pauseable, Updateable { public class VectAnimated extends VectMutable implements Pauseable, Updateable {
private final NumAnimated x, y, z; private final NumAnimated x, y, z;
private double defaultDuration = 0.5; private double defaultDuration = 0.5;
/** /**
* Create an animated vector; This way different easing / settings can be * Create an animated vector; This way different easing / settings can be
* specified for each coordinate. * specified for each coordinate.
* *
* @param x x animator * @param x x animator
* @param y y animator * @param y y animator
* @param z z animator * @param z z animator
*/ */
public VectAnimated(NumAnimated x, NumAnimated y, NumAnimated z) { public VectAnimated(NumAnimated x, NumAnimated y, NumAnimated z)
{
this.x = x; this.x = x;
this.y = y; this.y = y;
this.z = z; this.z = z;
} }
/** /**
* Create an animated vector * Create an animated vector
* *
* @param start initial positioon * @param start initial positioon
* @param easing animation easing * @param easing animation easing
*/ */
public VectAnimated(Vect start, Easing easing) { public VectAnimated(Vect start, Easing easing)
{
x = new NumAnimated(start.x(), easing); x = new NumAnimated(start.x(), easing);
y = new NumAnimated(start.y(), easing); y = new NumAnimated(start.y(), easing);
z = new NumAnimated(start.z(), easing); z = new NumAnimated(start.z(), easing);
} }
@Override @Override
public double x() public double x()
{ {
return x.value(); return x.value();
} }
@Override @Override
public double y() public double y()
{ {
return y.value(); return y.value();
} }
@Override @Override
public double z() public double z()
{ {
return z.value(); return z.value();
} }
@Override @Override
public void setTo(double x, double y, double z) public void setTo(double x, double y, double z)
{ {
@ -75,35 +77,35 @@ public class VectAnimated extends VectMutable implements Pauseable, Updateable {
setY(y); setY(y);
setZ(z); setZ(z);
} }
@Override @Override
public void setX(double x) public void setX(double x)
{ {
this.x.setTo(x); this.x.setTo(x);
} }
@Override @Override
public void setY(double y) public void setY(double y)
{ {
this.y.setTo(y); this.y.setTo(y);
} }
@Override @Override
public void setZ(double z) public void setZ(double z)
{ {
this.z.setTo(z); this.z.setTo(z);
} }
public void add(Vect offset, double duration) public void add(Vect offset, double duration)
{ {
animate(this.add(offset), duration); animate(this.add(offset), duration);
} }
public VectAnimated animate(double x, double y, double z, double duration) public VectAnimated animate(double x, double y, double z, double duration)
{ {
this.x.animate(x, duration); this.x.animate(x, duration);
@ -111,15 +113,15 @@ public class VectAnimated extends VectMutable implements Pauseable, Updateable {
this.z.animate(z, duration); this.z.animate(z, duration);
return this; return this;
} }
public VectAnimated animate(Vect target, double duration) public VectAnimated animate(Vect target, double duration)
{ {
animate(target.x(), target.y(), target.z(), duration); animate(target.x(), target.y(), target.z(), duration);
return this; return this;
} }
public VectAnimated animate(double x, double y, double z) public VectAnimated animate(double x, double y, double z)
{ {
this.x.animate(x, defaultDuration); this.x.animate(x, defaultDuration);
@ -127,15 +129,15 @@ public class VectAnimated extends VectMutable implements Pauseable, Updateable {
this.z.animate(z, defaultDuration); this.z.animate(z, defaultDuration);
return this; return this;
} }
public VectAnimated animate(Vect target) public VectAnimated animate(Vect target)
{ {
animate(target.x(), target.y(), target.z()); animate(target.x(), target.y(), target.z());
return this; return this;
} }
/** /**
* @return the default duration (seconds) * @return the default duration (seconds)
*/ */
@ -143,19 +145,19 @@ public class VectAnimated extends VectMutable implements Pauseable, Updateable {
{ {
return defaultDuration; return defaultDuration;
} }
/** /**
* Set default animation duration (when changed without using animate()) * Set default animation duration (when changed without using animate())
* *
* @param defaultDuration default duration (seconds) * @param defaultDuration default duration (seconds)
*/ */
public void setDefaultDuration(double defaultDuration) public void setDefaultDuration(double defaultDuration)
{ {
this.defaultDuration = defaultDuration; this.defaultDuration = defaultDuration;
} }
@Override @Override
public void update(double delta) public void update(double delta)
{ {
@ -163,8 +165,8 @@ public class VectAnimated extends VectMutable implements Pauseable, Updateable {
y.update(delta); y.update(delta);
z.update(delta); z.update(delta);
} }
@Override @Override
public void pause() public void pause()
{ {
@ -172,8 +174,8 @@ public class VectAnimated extends VectMutable implements Pauseable, Updateable {
y.pause(); y.pause();
z.pause(); z.pause();
} }
@Override @Override
public void resume() public void resume()
{ {
@ -181,15 +183,15 @@ public class VectAnimated extends VectMutable implements Pauseable, Updateable {
y.resume(); y.resume();
z.resume(); z.resume();
} }
@Override @Override
public boolean isPaused() public boolean isPaused()
{ {
return x.isPaused(); // BÚNO return x.isPaused(); // BÚNO
} }
/** /**
* @return true if the animation is finished * @return true if the animation is finished
*/ */
@ -197,8 +199,8 @@ public class VectAnimated extends VectMutable implements Pauseable, Updateable {
{ {
return x.isFinished(); // BÚNO return x.isFinished(); // BÚNO
} }
/** /**
* @return current animation duration * @return current animation duration
*/ */
@ -206,8 +208,8 @@ public class VectAnimated extends VectMutable implements Pauseable, Updateable {
{ {
return x.getDuration(); // BÚNO return x.getDuration(); // BÚNO
} }
/** /**
* @return elapsed time since the start of the animation * @return elapsed time since the start of the animation
*/ */
@ -215,8 +217,8 @@ public class VectAnimated extends VectMutable implements Pauseable, Updateable {
{ {
return x.getElapsed(); // BÚNO return x.getElapsed(); // BÚNO
} }
/** /**
* @return animation progress (elapsed / duration) * @return animation progress (elapsed / duration)
*/ */
@ -224,11 +226,11 @@ public class VectAnimated extends VectMutable implements Pauseable, Updateable {
{ {
return x.getProgress(); // BÚNO return x.getProgress(); // BÚNO
} }
/** /**
* Set easing for all three coordinates * Set easing for all three coordinates
* *
* @param easing * @param easing
*/ */
public void setEasing(Easing easing) public void setEasing(Easing easing)
@ -237,12 +239,12 @@ public class VectAnimated extends VectMutable implements Pauseable, Updateable {
y.setEasing(easing); y.setEasing(easing);
z.setEasing(easing); z.setEasing(easing);
} }
/** /**
* Create an animated vector; This way different easing / settings can be * Create an animated vector; This way different easing / settings can be
* specified for each coordinate. * specified for each coordinate.
* *
* @param x x animator * @param x x animator
* @param y y animator * @param y y animator
* @param z z animator * @param z z animator
@ -253,11 +255,11 @@ public class VectAnimated extends VectMutable implements Pauseable, Updateable {
{ {
return new VectAnimated(x, y, z); return new VectAnimated(x, y, z);
} }
/** /**
* Create an animated vector * Create an animated vector
* *
* @param start initial positioon * @param start initial positioon
* @param easing animation easing * @param easing animation easing
* @return animated mutable vector * @return animated mutable vector
@ -267,11 +269,11 @@ public class VectAnimated extends VectMutable implements Pauseable, Updateable {
{ {
return new VectAnimated(start, easing); return new VectAnimated(start, easing);
} }
/** /**
* Create an animated vector, initialized at 0,0,0 * Create an animated vector, initialized at 0,0,0
* *
* @param easing animation easing * @param easing animation easing
* @return animated mutable vector * @return animated mutable vector
*/ */
@ -280,5 +282,5 @@ public class VectAnimated extends VectMutable implements Pauseable, Updateable {
{ {
return new VectAnimated(Vect.ZERO, easing); return new VectAnimated(Vect.ZERO, easing);
} }
} }

@ -12,15 +12,15 @@ import mightypork.utils.math.constraints.num.Num;
/** /**
* Color.<br> * Color.<br>
* All values are 0-1 * All values are 0-1
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public abstract class Color { public abstract class Color {
private static final Stack<Num> alphaStack = new Stack<>(); private static final Stack<Num> alphaStack = new Stack<>();
private static volatile boolean alphaStackEnabled = true; private static volatile boolean alphaStackEnabled = true;
@FactoryMethod @FactoryMethod
public static final Color fromHex(int rgb_hex) public static final Color fromHex(int rgb_hex)
{ {
@ -29,146 +29,146 @@ public abstract class Color {
final int ri = (rgb_hex >> 16) & 0xff; final int ri = (rgb_hex >> 16) & 0xff;
return rgb(ri / 255D, gi / 255D, bi / 255D); return rgb(ri / 255D, gi / 255D, bi / 255D);
} }
@FactoryMethod @FactoryMethod
public static final Color rgb(double r, double g, double b) public static final Color rgb(double r, double g, double b)
{ {
return rgba(Num.make(r), Num.make(g), Num.make(b), Num.ONE); return rgba(Num.make(r), Num.make(g), Num.make(b), Num.ONE);
} }
@FactoryMethod @FactoryMethod
public static final Color rgba(double r, double g, double b, double a) public static final Color rgba(double r, double g, double b, double a)
{ {
return rgba(Num.make(r), Num.make(g), Num.make(b), Num.make(a)); return rgba(Num.make(r), Num.make(g), Num.make(b), Num.make(a));
} }
@FactoryMethod @FactoryMethod
public static final Color rgb(Num r, Num g, Num b) public static final Color rgb(Num r, Num g, Num b)
{ {
return rgba(r, g, b, Num.ONE); return rgba(r, g, b, Num.ONE);
} }
@FactoryMethod @FactoryMethod
public static final Color rgba(Num r, Num g, Num b, Num a) public static final Color rgba(Num r, Num g, Num b, Num a)
{ {
return new ColorRgb(r, g, b, a); return new ColorRgb(r, g, b, a);
} }
@FactoryMethod @FactoryMethod
public static final Color hsb(double h, double s, double b) public static final Color hsb(double h, double s, double b)
{ {
return hsba(Num.make(h), Num.make(s), Num.make(b), Num.ONE); return hsba(Num.make(h), Num.make(s), Num.make(b), Num.ONE);
} }
@FactoryMethod @FactoryMethod
public static final Color hsba(double h, double s, double b, double a) public static final Color hsba(double h, double s, double b, double a)
{ {
return hsba(Num.make(h), Num.make(s), Num.make(b), Num.make(a)); return hsba(Num.make(h), Num.make(s), Num.make(b), Num.make(a));
} }
@FactoryMethod @FactoryMethod
public static final Color hsb(Num h, Num s, Num b) public static final Color hsb(Num h, Num s, Num b)
{ {
return hsba(h, s, b, Num.ONE); return hsba(h, s, b, Num.ONE);
} }
@FactoryMethod @FactoryMethod
public static final Color hsba(Num h, Num s, Num b, Num a) public static final Color hsba(Num h, Num s, Num b, Num a)
{ {
return new ColorHsb(h, s, b, a); return new ColorHsb(h, s, b, a);
} }
@FactoryMethod @FactoryMethod
public static final Color light(double a) public static final Color light(double a)
{ {
return light(Num.make(a)); return light(Num.make(a));
} }
@FactoryMethod @FactoryMethod
public static final Color light(Num a) public static final Color light(Num a)
{ {
return rgba(Num.ONE, Num.ONE, Num.ONE, a); return rgba(Num.ONE, Num.ONE, Num.ONE, a);
} }
@FactoryMethod @FactoryMethod
public static final Color dark(double a) public static final Color dark(double a)
{ {
return dark(Num.make(a)); return dark(Num.make(a));
} }
@FactoryMethod @FactoryMethod
public static final Color dark(Num a) public static final Color dark(Num a)
{ {
return rgba(Num.ZERO, Num.ZERO, Num.ZERO, a); return rgba(Num.ZERO, Num.ZERO, Num.ZERO, a);
} }
protected static final double clamp(Num n) protected static final double clamp(Num n)
{ {
return Calc.clamp(n.value(), 0, 1); return Calc.clamp(n.value(), 0, 1);
} }
protected static final double clamp(double n) protected static final double clamp(double n)
{ {
return Calc.clamp(n, 0, 1); return Calc.clamp(n, 0, 1);
} }
/** /**
* @return red 0-1 * @return red 0-1
*/ */
public abstract double r(); public abstract double r();
/** /**
* @return green 0-1 * @return green 0-1
*/ */
public abstract double g(); public abstract double g();
/** /**
* @return blue 0-1 * @return blue 0-1
*/ */
public abstract double b(); public abstract double b();
/** /**
* @return alpha 0-1 * @return alpha 0-1
*/ */
public final double a() public final double a()
{ {
double alpha = rawAlpha(); double alpha = rawAlpha();
if (alphaStackEnabled) { if (alphaStackEnabled) {
for (final Num n : alphaStack) { for (final Num n : alphaStack) {
alpha *= clamp(n.value()); alpha *= clamp(n.value());
} }
} }
return clamp(alpha); return clamp(alpha);
} }
/** /**
* @return alpha 0-1, before multiplying with the global alpha value. * @return alpha 0-1, before multiplying with the global alpha value.
*/ */
protected abstract double rawAlpha(); protected abstract double rawAlpha();
/** /**
* <p> * <p>
* Push alpha multiplier on the stack (can be animated or whatever you * Push alpha multiplier on the stack (can be animated or whatever you
@ -180,7 +180,7 @@ public abstract class Color {
* multiplier value should not exceed the range 0..1, otherwise it will be * multiplier value should not exceed the range 0..1, otherwise it will be
* clamped to it. * clamped to it.
* </p> * </p>
* *
* @param alpha alpha multiplier * @param alpha alpha multiplier
*/ */
public static void pushAlpha(Num alpha) public static void pushAlpha(Num alpha)
@ -188,15 +188,15 @@ public abstract class Color {
if (!alphaStackEnabled) { if (!alphaStackEnabled) {
return; return;
} }
alphaStack.push(alpha); alphaStack.push(alpha);
} }
/** /**
* Remove a pushed alpha multiplier from the stack. If there's no remaining * Remove a pushed alpha multiplier from the stack. If there's no remaining
* multiplier on the stack, an exception is raised. * multiplier on the stack, an exception is raised.
* *
* @throws EmptyStackException if the stack is empty * @throws EmptyStackException if the stack is empty
*/ */
public static void popAlpha() public static void popAlpha()
@ -204,27 +204,27 @@ public abstract class Color {
if (!alphaStackEnabled) { if (!alphaStackEnabled) {
return; return;
} }
if (alphaStack.isEmpty()) { if (alphaStack.isEmpty()) {
throw new EmptyStackException(); throw new EmptyStackException();
} }
alphaStack.pop(); alphaStack.pop();
} }
/** /**
* Enable alpha stack. When disabled, pushAlpha() and popAlpha() have no * Enable alpha stack. When disabled, pushAlpha() and popAlpha() have no
* effect. * effect.
* *
* @param yes * @param yes
*/ */
public static void enableAlphaStack(boolean yes) public static void enableAlphaStack(boolean yes)
{ {
alphaStackEnabled = yes; alphaStackEnabled = yes;
} }
/** /**
* @return true if alpha stack is enabled. * @return true if alpha stack is enabled.
*/ */
@ -232,20 +232,20 @@ public abstract class Color {
{ {
return alphaStackEnabled; return alphaStackEnabled;
} }
public Color withAlpha(double multiplier) public Color withAlpha(double multiplier)
{ {
return new ColorAlphaAdjuster(this, Num.make(multiplier)); return new ColorAlphaAdjuster(this, Num.make(multiplier));
} }
public Color withAlpha(Num multiplier) public Color withAlpha(Num multiplier)
{ {
return new ColorAlphaAdjuster(this, multiplier); return new ColorAlphaAdjuster(this, multiplier);
} }
@Override @Override
public int hashCode() public int hashCode()
{ {
@ -262,15 +262,15 @@ public abstract class Color {
result = prime * result + (int) (temp ^ (temp >>> 32)); result = prime * result + (int) (temp ^ (temp >>> 32));
return result; return result;
} }
@Override @Override
public boolean equals(Object obj) public boolean equals(Object obj)
{ {
if (this == obj) return true; if (this == obj) return true;
if (obj == null) return false; if (obj == null) return false;
if (!(obj instanceof Color)) return false; if (!(obj instanceof Color)) return false;
Color other = (Color) obj; final Color other = (Color) obj;
if (Double.doubleToLongBits(b()) != Double.doubleToLongBits(other.b())) return false; if (Double.doubleToLongBits(b()) != Double.doubleToLongBits(other.b())) return false;
if (Double.doubleToLongBits(g()) != Double.doubleToLongBits(other.g())) return false; if (Double.doubleToLongBits(g()) != Double.doubleToLongBits(other.g())) return false;
if (Double.doubleToLongBits(r()) != Double.doubleToLongBits(other.r())) return false; if (Double.doubleToLongBits(r()) != Double.doubleToLongBits(other.r())) return false;

@ -5,42 +5,43 @@ import mightypork.utils.math.constraints.num.Num;
public class ColorAlphaAdjuster extends Color { public class ColorAlphaAdjuster extends Color {
private final Color source; private final Color source;
private final Num alphaAdjust; private final Num alphaAdjust;
public ColorAlphaAdjuster(Color source, Num alphaMul) { public ColorAlphaAdjuster(Color source, Num alphaMul)
{
this.source = source; this.source = source;
this.alphaAdjust = alphaMul; this.alphaAdjust = alphaMul;
} }
@Override @Override
public double r() public double r()
{ {
return source.r(); return source.r();
} }
@Override @Override
public double g() public double g()
{ {
return source.g(); return source.g();
} }
@Override @Override
public double b() public double b()
{ {
return source.b(); return source.b();
} }
@Override @Override
protected double rawAlpha() protected double rawAlpha()
{ {
return source.rawAlpha() * alphaAdjust.value(); return source.rawAlpha() * alphaAdjust.value();
} }
} }

@ -5,57 +5,58 @@ import mightypork.utils.math.constraints.num.Num;
public class ColorHsb extends Color { public class ColorHsb extends Color {
private final Num h; private final Num h;
private final Num s; private final Num s;
private final Num b; private final Num b;
private final Num a; private final Num a;
public ColorHsb(Num h, Num s, Num b, Num a) { public ColorHsb(Num h, Num s, Num b, Num a)
{
this.h = h; this.h = h;
this.s = s; this.s = s;
this.b = b; this.b = b;
this.a = a; this.a = a;
} }
private double[] asRgb() private double[] asRgb()
{ {
final int hex = java.awt.Color.HSBtoRGB((float) clamp(h), (float) clamp(s), (float) clamp(b)); final int hex = java.awt.Color.HSBtoRGB((float) clamp(h), (float) clamp(s), (float) clamp(b));
final int bi = hex & 0xff; final int bi = hex & 0xff;
final int gi = (hex >> 8) & 0xff; final int gi = (hex >> 8) & 0xff;
final int ri = (hex >> 16) & 0xff; final int ri = (hex >> 16) & 0xff;
return new double[] { ri / 255D, gi / 255D, bi / 255D, clamp(a) }; return new double[] { ri / 255D, gi / 255D, bi / 255D, clamp(a) };
} }
@Override @Override
public double r() public double r()
{ {
return asRgb()[0]; return asRgb()[0];
} }
@Override @Override
public double g() public double g()
{ {
return asRgb()[1]; return asRgb()[1];
} }
@Override @Override
public double b() public double b()
{ {
return asRgb()[2]; return asRgb()[2];
} }
@Override @Override
protected double rawAlpha() protected double rawAlpha()
{ {
return asRgb()[3]; return asRgb()[3];
} }
} }

@ -5,46 +5,47 @@ import mightypork.utils.math.constraints.num.Num;
public class ColorRgb extends Color { public class ColorRgb extends Color {
private final Num r; private final Num r;
private final Num g; private final Num g;
private final Num b; private final Num b;
private final Num a; private final Num a;
public ColorRgb(Num r, Num g, Num b, Num a) { public ColorRgb(Num r, Num g, Num b, Num a)
{
this.r = r; this.r = r;
this.g = g; this.g = g;
this.b = b; this.b = b;
this.a = a; this.a = a;
} }
@Override @Override
public double r() public double r()
{ {
return clamp(r); return clamp(r);
} }
@Override @Override
public double g() public double g()
{ {
return clamp(g); return clamp(g);
} }
@Override @Override
public double b() public double b()
{ {
return clamp(b); return clamp(b);
} }
@Override @Override
protected double rawAlpha() protected double rawAlpha()
{ {
return clamp(a); return clamp(a);
} }
} }

@ -1,27 +1,26 @@
package mightypork.utils.math.color; package mightypork.utils.math.color;
/** /**
* Linear gradient (each corner can have different color) * Linear gradient (each corner can have different color)
* *
* @author MightyPork * @author MightyPork
*/ */
public class Grad { public class Grad {
public final Color leftTop, rightTop, rightBottom, leftBottom; public final Color leftTop, rightTop, rightBottom, leftBottom;
/** /**
* Create a gradient * Create a gradient
* *
* @param leftTop left top color * @param leftTop left top color
* @param rightTop right top color * @param rightTop right top color
* @param rightBottom right bottom color * @param rightBottom right bottom color
* @param leftBottom left bottom color * @param leftBottom left bottom color
*/ */
public Grad(Color leftTop, Color rightTop, Color rightBottom, Color leftBottom) { public Grad(Color leftTop, Color rightTop, Color rightBottom, Color leftBottom)
{
this.leftTop = leftTop; this.leftTop = leftTop;
this.rightTop = rightTop; this.rightTop = rightTop;
this.rightBottom = rightBottom; this.rightBottom = rightBottom;

@ -1,16 +1,15 @@
package mightypork.utils.math.color; package mightypork.utils.math.color;
/** /**
* Linear horizontal gradient * Linear horizontal gradient
* *
* @author MightyPork * @author MightyPork
*/ */
public class GradH extends Grad { public class GradH extends Grad {
public GradH(Color left, Color right) { public GradH(Color left, Color right)
{
super(left, right, right, left); super(left, right, right, left);
} }
} }

@ -1,16 +1,15 @@
package mightypork.utils.math.color; package mightypork.utils.math.color;
/** /**
* Linear vertical gradient * Linear vertical gradient
* *
* @author MightyPork * @author MightyPork
*/ */
public class GradV extends Grad { public class GradV extends Grad {
public GradV(Color top, Color bottom) { public GradV(Color top, Color bottom)
{
super(top, top, bottom, bottom); super(top, top, bottom, bottom);
} }
} }

@ -6,16 +6,16 @@ import mightypork.utils.math.color.Color;
/** /**
* CGA palette * CGA palette
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public interface CGA { public interface CGA {
Color BLACK = Color.fromHex(0x000000); Color BLACK = Color.fromHex(0x000000);
Color GRAY_DARK = Color.fromHex(0x686868); Color GRAY_DARK = Color.fromHex(0x686868);
Color GRAY_LIGHT = Color.fromHex(0xB8B8B8); Color GRAY_LIGHT = Color.fromHex(0xB8B8B8);
Color WHITE = Color.fromHex(0xFFFFFF); Color WHITE = Color.fromHex(0xFFFFFF);
Color RED_DARK = Color.fromHex(0xC41F0C); Color RED_DARK = Color.fromHex(0xC41F0C);
Color RED_LIGHT = Color.fromHex(0xFF706A); Color RED_LIGHT = Color.fromHex(0xFF706A);
Color MAGENTA_DARK = Color.fromHex(0xC12BB6); Color MAGENTA_DARK = Color.fromHex(0xC12BB6);
@ -26,7 +26,7 @@ public interface CGA {
Color CYAN_LIGHT = Color.fromHex(0x23FCFE); Color CYAN_LIGHT = Color.fromHex(0x23FCFE);
Color GREEN_DARK = Color.fromHex(0x00B41D); Color GREEN_DARK = Color.fromHex(0x00B41D);
Color GREEN_LIGHT = Color.fromHex(0x39FA6F); Color GREEN_LIGHT = Color.fromHex(0x39FA6F);
Color YELLOW = Color.fromHex(0xFFFD72); Color YELLOW = Color.fromHex(0xFFFD72);
Color BROWN = Color.fromHex(0xC16A14); Color BROWN = Color.fromHex(0xC16A14);
} }

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save