diff --git a/nb-configuration.xml b/nb-configuration.xml index 1787236af..34fd16624 100644 --- a/nb-configuration.xml +++ b/nb-configuration.xml @@ -37,6 +37,6 @@ WRAP_ALWAYS WRAP_ALWAYS WRAP_ALWAYS - JDK_16 + JDK_17 diff --git a/src/main/java/com/laytonsmith/PureUtilities/Common/ReflectionUtils.java b/src/main/java/com/laytonsmith/PureUtilities/Common/ReflectionUtils.java index 7542f1cc7..c9a54d453 100644 --- a/src/main/java/com/laytonsmith/PureUtilities/Common/ReflectionUtils.java +++ b/src/main/java/com/laytonsmith/PureUtilities/Common/ReflectionUtils.java @@ -7,8 +7,10 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -187,7 +189,7 @@ public static void set(Class clazz, Object instance, String variableName, Object * @param methodName The name of the method. * @return The invocation result, null if void. */ - public static Object invokeMethod(Class clazz, Object instance, String methodName) throws ReflectionException { + public static T invokeMethod(Class clazz, Object instance, String methodName) throws ReflectionException { return invokeMethod(clazz, instance, methodName, new Class[]{}, new Object[]{}); } @@ -202,7 +204,7 @@ public static Object invokeMethod(Class clazz, Object instance, String methodNam * @throws ReflectionException */ @SuppressWarnings({"ThrowableInstanceNotThrown", "ThrowableInstanceNeverThrown"}) - public static Object invokeMethod(Object instance, String methodName, Object... params) throws ReflectionException { + public static T invokeMethod(Object instance, String methodName, Object... params) throws ReflectionException { Class c = instance.getClass(); Class[] argTypes; { @@ -235,7 +237,7 @@ public static Object invokeMethod(Object instance, String methodName, Object... } } m.setAccessible(true); - return m.invoke(instance, params); + return (T) m.invoke(instance, params); } } catch(IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { throw new ReflectionException(ex); @@ -258,13 +260,13 @@ public static Object invokeMethod(Object instance, String methodName, Object... * @throws ReflectionException */ @SuppressWarnings({"ThrowableInstanceNotThrown", "ThrowableInstanceNeverThrown"}) - public static Object invokeMethod(Object instance, String methodName) throws ReflectionException { + public static T invokeMethod(Object instance, String methodName) throws ReflectionException { Class c = instance.getClass(); while(c != null) { for(Method m : c.getDeclaredMethods()) { - if(methodName.equals(m.getName())) { + if(methodName.equals(m.getName()) && m.getParameterCount() == 0) { try { - return m.invoke(instance); + return (T) m.invoke(instance); } catch(IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { throw new ReflectionException(ex); } @@ -288,12 +290,12 @@ public static Object invokeMethod(Object instance, String methodName) throws Ref * @param args The arguments. * @return The invocation result, null if void. */ - public static Object invokeMethod(Class clazz, Object instance, String methodName, Class[] argTypes, Object[] args) + public static T invokeMethod(Class clazz, Object instance, String methodName, Class[] argTypes, Object[] args) throws ReflectionException { try { Method m = clazz.getDeclaredMethod(methodName, argTypes); m.setAccessible(true); - return m.invoke(instance, args); + return (T) m.invoke(instance, args); } catch(InvocationTargetException | NoSuchMethodException | IllegalArgumentException | IllegalAccessException | SecurityException ex) { throw new ReflectionException(ex); @@ -462,9 +464,8 @@ public static void throwUncheckedException(Throwable t) { * * @param className the fully qualified name of the desired class. * @return the {@code Class} object for the class with the specified name. - * @throws LinkageError if the linkage fails - * @throws ExceptionInInitializerError if the initialization provoked by this method fails - * @throws ReflectionException if the class cannot be located + * @throws ReflectionException wraps a LinkageError if the linkage fails, ExceptionInInitializerError if the + * initialization provoked by this method fails, or ClassNotFoundException if the class is not found. */ public static Class forName(String className) { try { @@ -515,12 +516,11 @@ public static Class forName(String className) { * @param loader class loader from which the class must be loaded * @return class object representing the desired class * - * @throws LinkageError if the linkage fails - * @throws ExceptionInInitializerError if the initialization provoked by this method fails - * @throws ReflectionException if the class cannot be located by the specified class loader - * @throws SecurityException if a security manager is present, and the {@code loader} is {@code null}, and the + * @throws ReflectionException wraps a LinkageError if the linkage fails, ExceptionInInitializerError if the + * initialization provoked by this method fails, SecurityException if a security manager is present, + * and the {@code loader} is {@code null}, and the * caller's class loader is not {@code null}, and the caller does not have the - * {@link RuntimePermission}{@code ("getClassLoader")} + * {@link RuntimePermission}{@code ("getClassLoader")}, or ClassNotFoundException if the class is not found. * * @see java.lang.Class#forName(String, boolean, ClassLoader) * @see java.lang.ClassLoader @@ -533,4 +533,26 @@ public static Class forName(String name, boolean initialize, ClassLoader loader) } } + // Exceptions are expensive, so cache this. + private static Map classExistsMap = new HashMap<>(); + + /** + * Checks if a class exists, according to Class.forName(). This is cached. + * @param name The class name. + * @return True if the class exists. + */ + public static boolean classExists(String name) { + if(classExistsMap.containsKey(name)) { + return classExistsMap.get(name); + } + try { + Class.forName(name); + classExistsMap.put(name, Boolean.TRUE); + return true; + } catch(ClassNotFoundException ex) { + classExistsMap.put(name, Boolean.FALSE); + return false; + } + } + } diff --git a/src/main/java/com/laytonsmith/PureUtilities/Common/StreamUtils.java b/src/main/java/com/laytonsmith/PureUtilities/Common/StreamUtils.java index 736f02bef..9603fa88f 100644 --- a/src/main/java/com/laytonsmith/PureUtilities/Common/StreamUtils.java +++ b/src/main/java/com/laytonsmith/PureUtilities/Common/StreamUtils.java @@ -2,14 +2,13 @@ import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; 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.List; /** * Streams are hard sometimes. This class abstracts most of the functionality that is commonly used. @@ -44,7 +43,7 @@ public static void Copy(InputStream in, OutputStream out) throws IOException { public static String GetString(InputStream in) { try { return GetString(in, "UTF-8"); - } catch (UnsupportedEncodingException ex) { + } catch(UnsupportedEncodingException ex) { throw new Error(ex); } } @@ -76,7 +75,7 @@ public static String GetString(InputStream in, String encoding) throws Unsupport read = input.read(buffer, 0, buffer.length)) { output.append(buffer, 0, read); } - } catch (IOException ignore) { + } catch(IOException ignore) { } return output.toString(); @@ -84,8 +83,8 @@ public static String GetString(InputStream in, String encoding) throws Unsupport } /** - * Fully reads in a stream, as efficiently as possible, and returns a byte array. - * The input stream is not closed afterwards. + * Fully reads in a stream, as efficiently as possible, and returns a byte array. The input stream is not closed + * afterwards. * * @param in * @return @@ -93,12 +92,14 @@ public static String GetString(InputStream in, String encoding) throws Unsupport */ public static byte[] GetBytes(InputStream in) throws IOException { BufferedInputStream bis = new BufferedInputStream(in); - List bytes = new ArrayList<>(); - int i; - while((i = bis.read()) != -1) { - bytes.add(((byte) i)); + try(ByteArrayOutputStream out = new ByteArrayOutputStream()) { + int i; + byte[] buffer = new byte[8 * 1024]; + while((i = bis.read(buffer)) != -1) { + out.write(buffer, 0, i); + } + return out.toByteArray(); } - return ArrayUtils.unbox(bytes.toArray(new Byte[bytes.size()])); } /** @@ -110,7 +111,7 @@ public static byte[] GetBytes(InputStream in) throws IOException { public static InputStream GetInputStream(String contents) { try { return GetInputStream(contents, "UTF-8"); - } catch (UnsupportedEncodingException ex) { + } catch(UnsupportedEncodingException ex) { throw new Error(ex); } } @@ -146,7 +147,7 @@ public static InputStream GetInputStream(byte[] bytes) { public static PrintStream GetSystemOut() { try { return new PrintStream(System.out, true, "UTF-8"); - } catch (UnsupportedEncodingException ex) { + } catch(UnsupportedEncodingException ex) { throw new Error(ex); } } @@ -160,13 +161,14 @@ public static PrintStream GetSystemOut() { public static PrintStream GetSystemErr() { try { return new PrintStream(System.err, true, "UTF-8"); - } catch (UnsupportedEncodingException ex) { + } catch(UnsupportedEncodingException ex) { throw new Error(ex); } } /** * Gets a resource string with the specified encoding, relative to the class that is calling this method. + * * @param name The name of the resource. The name should follow the same naming conventions used by * {@link Class#getResource(java.lang.String)}. * @param encoding The encoding to use on the resource. @@ -185,6 +187,7 @@ public static final String GetResource(String name, String encoding) throws Unsu /** * Gets a resource as a UTF-8 encoded string, relative to the class that is calling this method. + * * @param name The name of the resource. The name should follow the same naming conventions used by * {@link Class#getResource(java.lang.String)}. * @return A string depiction of the specified resource. @@ -193,7 +196,7 @@ public static final String GetResource(String name, String encoding) throws Unsu public static final String GetResource(String name) throws IllegalArgumentException { try { return GetResource(name, "UTF-8"); - } catch (UnsupportedEncodingException ex) { + } catch(UnsupportedEncodingException ex) { throw new Error(ex); } } diff --git a/src/main/java/com/laytonsmith/PureUtilities/Common/StringUtils.java b/src/main/java/com/laytonsmith/PureUtilities/Common/StringUtils.java index 0057bd6b5..1f17720ce 100644 --- a/src/main/java/com/laytonsmith/PureUtilities/Common/StringUtils.java +++ b/src/main/java/com/laytonsmith/PureUtilities/Common/StringUtils.java @@ -37,9 +37,6 @@ public static String Join(Map map, String entryGlue, String elementGlue) { * @param entryGlue The glue to use between the key and value of each pair in the map * @param elementGlue The glue to use between each key-value element pairs in the map * @param lastElementGlue The glue for the last two elements - * @param glueForTwoItems If only two items are in the map, then this glue is used instead. If it is null, then - * lastElementGlue is used instead. - * @param empty If the map is completely empty, this string is simply returned. If null, an empty string is used. * @return The concatenated string */ public static String Join(Map map, String entryGlue, String elementGlue, String lastElementGlue) { @@ -53,7 +50,7 @@ public static String Join(Map map, String entryGlue, String elementGlue, String * @param entryGlue The glue to use between the key and value of each pair in the map * @param elementGlue The glue to use between each key-value element pairs in the map * @param lastElementGlue The glue for the last two elements - * @param glueForTwoItems If only two items are in the map, then this glue is used instead. If it is null, then + * @param elementGlueForTwoItems If only two items are in the map, then this glue is used instead. If it is null, then * lastElementGlue is used instead. * @return The concatenated string */ @@ -68,14 +65,14 @@ public static String Join(Map map, String entryGlue, String elementGlue, String * @param entryGlue The glue to use between the key and value of each pair in the map * @param elementGlue The glue to use between each key-value element pairs in the map * @param lastElementGlue The glue for the last two elements - * @param glueForTwoItems If only two items are in the map, then this glue is used instead. If it is null, then + * @param elementGlueForTwoItems If only two items are in the map, then this glue is used instead. If it is null, then * lastElementGlue is used instead. * @param empty If the map is completely empty, this string is simply returned. If null, an empty string is used. * @return The concatenated string */ public static String Join(Map map, String entryGlue, String elementGlue, String lastElementGlue, String elementGlueForTwoItems, String empty) { //Just create a list of glued together entries, then send it to the other Join method - List list = new ArrayList(); + List list = new ArrayList<>(); for(Object key : map.keySet()) { StringBuilder b = new StringBuilder(); b.append(key).append(entryGlue).append(map.get(key)); @@ -87,7 +84,8 @@ public static String Join(Map map, String entryGlue, String elementGlue, String /** * Joins a set together (using StringBuilder's {@link StringBuilder#append(Object)} method to "toString" the Object) * using the specified string for glue. - * @param list The set to concatenate + * @param The set type + * @param set The set to concatenate * @param glue The glue to use * @return The concatenated string */ @@ -97,13 +95,15 @@ public static String Join(Set set, String glue) { /** * Joins a set together, rendering each item with the custom renderer. - * @param set - * @param glue - * @param r + * @param The set type + * @param set The set to concatenate + * @param glue The glue to use + * @param renderer The item renderer. This renders each item in the set, one at a time. If null, toString will be + * used by default on each item. * @return */ - public static String Join(Set set, String glue, Renderer r) { - return Join(set, glue, null, null, null, r); + public static String Join(Set set, String glue, Renderer renderer) { + return Join(set, glue, null, null, null, renderer); } /** @@ -111,12 +111,13 @@ public static String Join(Set set, String glue, Renderer r) { * using the specified string for glue. If * lastGlue is null, it is the same as glue, but otherwise it is used to glue just the last two items together, * which is useful for sets that are being read by a human, to have a proper conjunction at the end. - * @param list The set to concatenate + * @param The set type + * @param set The set to concatenate * @param glue The glue to use * @param lastGlue The glue for the last two elements * @return The concatenated string */ - public static String Join(Set set, String glue, String lastGlue) { + public static String Join(Set set, String glue, String lastGlue) { return Join(set, glue, lastGlue, null, null); } @@ -125,14 +126,15 @@ public static String Join(Set set, String glue, String lastGlue) { * using the specified string for glue. * If lastGlue is null, it is the same as glue, but otherwise it is used to glue just the last two items together, * which is useful for sets that are being read by a human, to have a proper conjunction at the end. - * @param list The set to concatenate + * @param The set type + * @param set The set to concatenate * @param glue The glue to use * @param lastGlue The glue for the last two elements * @param glueForTwoItems If only two items are in the set, then this glue is used instead. If it is null, then * lastGlue is used instead. * @return The concatenated string */ - public static String Join(Set set, String glue, String lastGlue, String glueForTwoItems) { + public static String Join(Set set, String glue, String lastGlue, String glueForTwoItems) { return Join(set, glue, lastGlue, glueForTwoItems, null); } @@ -141,7 +143,8 @@ public static String Join(Set set, String glue, String lastGlue, String glueForT * using the specified string for glue. * If lastGlue is null, it is the same as glue, but otherwise it is used to glue just the last two items together, * which is useful for sets that are being read by a human, to have a proper conjunction at the end. - * @param list The set to concatenate + * @param The set type + * @param set The set to concatenate * @param glue The glue to use * @param lastGlue The glue for the last two elements * @param glueForTwoItems If only two items are in the set, then this glue is used instead. If it is null, then @@ -158,16 +161,20 @@ public static String Join(Set set, String glue, String lastGlue, String g * using the specified string for glue. * If lastGlue is null, it is the same as glue, but otherwise it is used to glue just the last two items together, * which is useful for sets that are being read by a human, to have a proper conjunction at the end. - * @param list The set to concatenate + * @param + * @param set The set to concatenate * @param glue The glue to use * @param lastGlue The glue for the last two elements * @param glueForTwoItems If only two items are in the set, then this glue is used instead. If it is null, then * lastGlue is used instead. * @param empty If the set is completely empty, this string is simply returned. If null, an empty string is used. + * @param renderer The item renderer. This renders each item in the set, one at a time. If null, toString will be + * used by default on each item. * @return The concatenated string */ - public static String Join(Set set, String glue, String lastGlue, String glueForTwoItems, String empty, Renderer renderer) { - final List list = new ArrayList(set); + public static String Join(Set set, String glue, String lastGlue, String glueForTwoItems, String empty, + Renderer renderer) { + final List list = new ArrayList<>(set); return doJoin(new ItemGetter() { @Override @@ -190,11 +197,12 @@ public boolean isEmpty() { /** * Joins an array together (using StringBuilder's {@link StringBuilder#append(Object)} method * to "toString" the Object) using the specified string for glue. + * @param The array type * @param list The array to concatenate * @param glue The glue to use * @return The concatenated string */ - public static String Join(Object[] list, String glue) { + public static String Join(T[] list, String glue) { return Join(list, glue, null, null, null); } @@ -203,12 +211,13 @@ public static String Join(Object[] list, String glue) { * to "toString" the Object) using the specified string for glue. * If lastGlue is null, it is the same as glue, but otherwise it is used to glue just the last two items together, * which is useful for lists that are being read by a human, to have a proper conjunction at the end. + * @param The array type * @param list The array to concatenate * @param glue The glue to use * @param lastGlue The glue for the last two elements * @return The concatenated string */ - public static String Join(Object[] list, String glue, String lastGlue) { + public static String Join(T[] list, String glue, String lastGlue) { return Join(list, glue, lastGlue, null, null); } @@ -217,6 +226,7 @@ public static String Join(Object[] list, String glue, String lastGlue) { * to "toString" the Object) using the specified string for glue. * If lastGlue is null, it is the same as glue, but otherwise it is used to glue just the last two items together, * which is useful for lists that are being read by a human, to have a proper conjunction at the end. + * @param The array type * @param list The array to concatenate * @param glue The glue to use * @param lastGlue The glue for the last two elements @@ -224,7 +234,7 @@ public static String Join(Object[] list, String glue, String lastGlue) { * lastGlue is used instead. * @return The concatenated string */ - public static String Join(Object[] list, String glue, String lastGlue, String glueForTwoItems) { + public static String Join(T[] list, String glue, String lastGlue, String glueForTwoItems) { return Join(list, glue, lastGlue, glueForTwoItems, null); } @@ -233,6 +243,7 @@ public static String Join(Object[] list, String glue, String lastGlue, String gl * to "toString" the Object) using the specified string for glue. * If lastGlue is null, it is the same as glue, but otherwise it is used to glue just the last two items together, * which is useful for lists that are being read by a human, to have a proper conjunction at the end. + * @param The array type * @param list The array to concatenate * @param glue The glue to use * @param lastGlue The glue for the last two elements @@ -241,7 +252,7 @@ public static String Join(Object[] list, String glue, String lastGlue, String gl * @param empty If the array is completely empty, this string is simply returned. If null, an empty string is used. * @return The concatenated string */ - public static String Join(Object[] list, String glue, String lastGlue, String glueForTwoItems, String empty) { + public static String Join(T[] list, String glue, String lastGlue, String glueForTwoItems, String empty) { return Join(list, glue, lastGlue, glueForTwoItems, empty, null); } @@ -344,6 +355,7 @@ public static String Join(final List list, String glue, String lastGlue, String * to "toString" the Object) using the specified string for glue. * If lastGlue is null, it is the same as glue, but otherwise it is used to glue just the last two items together, * which is useful for lists that are being read by a human, to have a proper conjunction at the end. + * @param The list type * @param list The list to concatenate * @param glue The glue to use * @param lastGlue The glue for the last two elements. If it is null, then glue is used instead. @@ -354,7 +366,8 @@ public static String Join(final List list, String glue, String lastGlue, String * used by default on each item. * @return The concatenated string */ - public static String Join(final List list, String glue, String lastGlue, String glueForTwoItems, String empty, Renderer renderer) { + public static String Join(final List list, String glue, String lastGlue, String glueForTwoItems, + String empty, Renderer renderer) { return doJoin(new ItemGetter() { @Override @@ -911,6 +924,7 @@ public static List lineSplit(String text, int len) { * * @param str The string to word wrap * @param wrapLength The max length of the line + * @return a line with newlines inserted, null if null input */ public static String lineWrap(String str, int wrapLength) { return lineWrap(str, wrapLength, "\n", true); diff --git a/src/main/java/com/laytonsmith/abstraction/MCEntity.java b/src/main/java/com/laytonsmith/abstraction/MCEntity.java index 89cde50c9..fd5b88c4e 100644 --- a/src/main/java/com/laytonsmith/abstraction/MCEntity.java +++ b/src/main/java/com/laytonsmith/abstraction/MCEntity.java @@ -108,4 +108,6 @@ public interface MCEntity extends MCMetadatable { int getFreezingTicks(); void setFreezingTicks(int ticks); + + int getEntityId(); } diff --git a/src/main/java/com/laytonsmith/abstraction/bukkit/entities/BukkitMCEntity.java b/src/main/java/com/laytonsmith/abstraction/bukkit/entities/BukkitMCEntity.java index 19f64b15f..0d3e2aee8 100644 --- a/src/main/java/com/laytonsmith/abstraction/bukkit/entities/BukkitMCEntity.java +++ b/src/main/java/com/laytonsmith/abstraction/bukkit/entities/BukkitMCEntity.java @@ -387,4 +387,10 @@ public boolean equals(Object obj) { public int hashCode() { return e.hashCode(); } + + @Override + public int getEntityId() { + return e.getEntityId(); + } + } diff --git a/src/main/java/com/laytonsmith/core/Static.java b/src/main/java/com/laytonsmith/core/Static.java index 597cbbf79..6cb190a5a 100644 --- a/src/main/java/com/laytonsmith/core/Static.java +++ b/src/main/java/com/laytonsmith/core/Static.java @@ -1487,9 +1487,18 @@ public static Construct getMSObject(Object object, Target t) { return CNull.NULL; } else if(object instanceof Boolean) { return CBoolean.get((boolean) object); - } else if((object instanceof Byte) || (object instanceof Short) || (object instanceof Integer) || (object instanceof Long)) { + } else if(object instanceof Byte b) { + // These are required due to unboxing + return new CInt(b.longValue(), t); + } else if(object instanceof Short s) { + return new CInt(s.longValue(), t); + } else if(object instanceof Integer i) { + return new CInt(i.longValue(), t); + } else if(object instanceof Long) { return new CInt((long) object, t); - } else if((object instanceof Float) || (object instanceof Double)) { + } else if(object instanceof Float f) { + return new CDouble(f.doubleValue(), t); + } else if(object instanceof Double) { return new CDouble((double) object, t); } else if(object instanceof Character) { return new CString((char) object, t); diff --git a/src/main/java/com/laytonsmith/core/constructs/CArray.java b/src/main/java/com/laytonsmith/core/constructs/CArray.java index 50a670803..3cf46553b 100644 --- a/src/main/java/com/laytonsmith/core/constructs/CArray.java +++ b/src/main/java/com/laytonsmith/core/constructs/CArray.java @@ -247,6 +247,10 @@ public final void push(Mixed c, Target t) { push(c, null, t); } + public final void push(String s) { + push(new CString(s, Target.UNKNOWN), Target.UNKNOWN); + } + /** * Pushes a new Construct onto the end of the array. If the index is specified, this works like a "insert" * operation, in that all values are shifted to the right, starting with the value at that index. If the array is @@ -396,6 +400,10 @@ public final void set(String index, float value) { set(index, new CDouble(value, Target.UNKNOWN), Target.UNKNOWN); } + public final void set(String index, int value) { + set(index, new CInt(value, Target.UNKNOWN), Target.UNKNOWN); + } + @Override public Mixed get(Mixed index, Target t) { if(!associativeMode) { diff --git a/src/main/java/com/laytonsmith/core/functions/EntityManagement.java b/src/main/java/com/laytonsmith/core/functions/EntityManagement.java index 7c10e5045..7ba973b69 100644 --- a/src/main/java/com/laytonsmith/core/functions/EntityManagement.java +++ b/src/main/java/com/laytonsmith/core/functions/EntityManagement.java @@ -5280,4 +5280,41 @@ public Boolean runAsync() { } } + + @api + public static class get_entity_transient_id extends EntityGetterFunction { + + @Override + public boolean isRestricted() { + return false; + } + + @Override + public Boolean runAsync() { + return false; + } + + @Override + public Mixed exec(Target t, Environment env, Mixed... args) throws ConfigRuntimeException { + MCEntity entity = Static.getEntity(args[0], t); + return new CInt(entity.getEntityId(), t); + } + + @Override + public String getName() { + return "get_entity_transient_id"; + } + + @Override + public String docs() { + return "int {uuid} Given a permanent entity uuid, returns the transient entity id. This should not" + + " be stored, as it is reset every restart, and is only useful for dealing with low level packets."; + } + + @Override + public Version since() { + return MSVersion.V3_3_5; + } + + } }