From 78b0065651b40545f8ff288acf8756ee517b8d5a Mon Sep 17 00:00:00 2001 From: Dan Flettre Date: Thu, 13 Feb 2014 00:37:13 -0600 Subject: [PATCH] Added support for GWT -- including Reflection wrapper --- artemis-gwt/pom.xml | 58 ++ .../backends/artemis_backends_gwt.gwt.xml | 14 + .../utils/reflect/ArrayReflection.java | 44 + .../utils/reflect/ClassReflection.java | 188 ++++ .../artemis/utils/reflect/Constructor.java | 60 ++ .../emu/com/artemis/utils/reflect/Field.java | 138 +++ .../emu/com/artemis/utils/reflect/Method.java | 122 +++ .../backends/gwt/emu/java/util/BitSet.java | 917 ++++++++++++++++++ .../backends/gwt/emu/java/util/UUID.java | 112 +++ .../com/artemis/gwtref/ArtemisReflect.gwt.xml | 25 + .../artemis/gwtref/client/Constructor.java | 33 + .../java/com/artemis/gwtref/client/Field.java | 130 +++ .../gwtref/client/IReflectionCache.java | 42 + .../com/artemis/gwtref/client/Method.java | 161 +++ .../com/artemis/gwtref/client/Parameter.java | 46 + .../gwtref/client/ReflectionCache.java | 88 ++ .../java/com/artemis/gwtref/client/Test.java | 99 ++ .../java/com/artemis/gwtref/client/Type.java | 246 +++++ .../gwtref/gen/ReflectionCacheGenerator.java | 46 + .../gen/ReflectionCacheSourceCreator.java | 895 +++++++++++++++++ artemis/pom.xml | 18 + artemis/src/main/java/com/artemis.gwt.xml | 6 + .../com/artemis/BasicComponentMapper.java | 10 +- .../java/com/artemis/ComponentManager.java | 12 +- .../main/java/com/artemis/ComponentPool.java | 6 +- .../main/java/com/artemis/ComponentType.java | 9 +- artemis/src/main/java/com/artemis/Entity.java | 3 +- .../artemis/InvalidComponentException.java | 4 +- .../com/artemis/PackedComponentMapper.java | 13 +- artemis/src/main/java/com/artemis/World.java | 24 +- .../main/java/com/artemis/utils/FastMath.java | 9 +- .../main/java/com/artemis/utils/Utils.java | 28 - .../utils/reflect/ArrayReflection.java | 43 + .../utils/reflect/ClassReflection.java | 187 ++++ .../artemis/utils/reflect/Constructor.java | 64 ++ .../java/com/artemis/utils/reflect/Field.java | 151 +++ .../com/artemis/utils/reflect/Method.java | 118 +++ .../utils/reflect/ReflectionException.java | 42 + .../java/com/artemis/ComponentPoolTest.java | 2 +- pom.xml | 1 + 40 files changed, 4144 insertions(+), 70 deletions(-) create mode 100644 artemis-gwt/pom.xml create mode 100644 artemis-gwt/src/main/java/com/artemis/backends/artemis_backends_gwt.gwt.xml create mode 100644 artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/com/artemis/utils/reflect/ArrayReflection.java create mode 100644 artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/com/artemis/utils/reflect/ClassReflection.java create mode 100644 artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/com/artemis/utils/reflect/Constructor.java create mode 100644 artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/com/artemis/utils/reflect/Field.java create mode 100644 artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/com/artemis/utils/reflect/Method.java create mode 100644 artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/java/util/BitSet.java create mode 100644 artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/java/util/UUID.java create mode 100644 artemis-gwt/src/main/java/com/artemis/gwtref/ArtemisReflect.gwt.xml create mode 100644 artemis-gwt/src/main/java/com/artemis/gwtref/client/Constructor.java create mode 100644 artemis-gwt/src/main/java/com/artemis/gwtref/client/Field.java create mode 100644 artemis-gwt/src/main/java/com/artemis/gwtref/client/IReflectionCache.java create mode 100644 artemis-gwt/src/main/java/com/artemis/gwtref/client/Method.java create mode 100644 artemis-gwt/src/main/java/com/artemis/gwtref/client/Parameter.java create mode 100644 artemis-gwt/src/main/java/com/artemis/gwtref/client/ReflectionCache.java create mode 100644 artemis-gwt/src/main/java/com/artemis/gwtref/client/Test.java create mode 100644 artemis-gwt/src/main/java/com/artemis/gwtref/client/Type.java create mode 100644 artemis-gwt/src/main/java/com/artemis/gwtref/gen/ReflectionCacheGenerator.java create mode 100644 artemis-gwt/src/main/java/com/artemis/gwtref/gen/ReflectionCacheSourceCreator.java create mode 100644 artemis/src/main/java/com/artemis.gwt.xml create mode 100644 artemis/src/main/java/com/artemis/utils/reflect/ArrayReflection.java create mode 100644 artemis/src/main/java/com/artemis/utils/reflect/ClassReflection.java create mode 100644 artemis/src/main/java/com/artemis/utils/reflect/Constructor.java create mode 100644 artemis/src/main/java/com/artemis/utils/reflect/Field.java create mode 100644 artemis/src/main/java/com/artemis/utils/reflect/Method.java create mode 100644 artemis/src/main/java/com/artemis/utils/reflect/ReflectionException.java diff --git a/artemis-gwt/pom.xml b/artemis-gwt/pom.xml new file mode 100644 index 000000000..bd05bc41d --- /dev/null +++ b/artemis-gwt/pom.xml @@ -0,0 +1,58 @@ + + 4.0.0 + + net.onedaybeard.artemis + artemis-parent + 0.5.1-SNAPSHOT + + artemis-gwt + + + + + ${project.groupId} + artemis-odb + ${project.version} + + + + com.google.gwt + gwt-user + 2.5.1 + + + + com.google.gwt + gwt-dev + 2.5.1 + provided + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + generate-resources + + jar-no-fork + + + + + + + + \ No newline at end of file diff --git a/artemis-gwt/src/main/java/com/artemis/backends/artemis_backends_gwt.gwt.xml b/artemis-gwt/src/main/java/com/artemis/backends/artemis_backends_gwt.gwt.xml new file mode 100644 index 000000000..fbc16b72e --- /dev/null +++ b/artemis-gwt/src/main/java/com/artemis/backends/artemis_backends_gwt.gwt.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/com/artemis/utils/reflect/ArrayReflection.java b/artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/com/artemis/utils/reflect/ArrayReflection.java new file mode 100644 index 000000000..eef6082c4 --- /dev/null +++ b/artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/com/artemis/utils/reflect/ArrayReflection.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.artemis.utils.reflect; + +import com.artemis.gwtref.client.ReflectionCache; + +/** Utilities for Array reflection. + * @author nexsoftware */ +public final class ArrayReflection { + + /** Creates a new array with the specified component type and length. */ + static public Object newInstance (Class c, int size) { + return ReflectionCache.instance.newArray(c, size); + } + + /** Returns the length of the supplied array. */ + static public int getLength (Object array) { + return ReflectionCache.instance.getArrayLength(ReflectionCache.getType(array.getClass()), array); + } + + /** Returns the value of the indexed component in the supplied array. */ + static public Object get (Object array, int index) { + return ReflectionCache.instance.getArrayElement(ReflectionCache.getType(array.getClass()), array, index); + } + + /** Sets the value of the indexed component in the supplied array to the supplied value. */ + static public void set (Object array, int index, Object value) { + ReflectionCache.instance.setArrayElement(ReflectionCache.getType(array.getClass()), array, index, value); + } +} \ No newline at end of file diff --git a/artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/com/artemis/utils/reflect/ClassReflection.java b/artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/com/artemis/utils/reflect/ClassReflection.java new file mode 100644 index 000000000..261da392e --- /dev/null +++ b/artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/com/artemis/utils/reflect/ClassReflection.java @@ -0,0 +1,188 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.artemis.utils.reflect; + +import com.artemis.gwtref.client.ReflectionCache; +import com.artemis.gwtref.client.Type; +import com.artemis.utils.reflect.ReflectionException; + +/** Utilities for Class reflection. + * @author nexsoftware */ +public final class ClassReflection { + + /** Returns the Class object associated with the class or interface with the supplied string name. */ + static public Class forName (String name) throws ReflectionException { + try { + return ReflectionCache.forName(name).getClassOfType(); + } catch (ClassNotFoundException e) { + throw new ReflectionException("Class not found: " + name); + } + } + + /** Returns the simple name of the underlying class as supplied in the source code. */ + static public String getSimpleName (Class c) { + return c.getName(); + } + + /** Determines if the supplied Object is assignment-compatible with the object represented by supplied Class. */ + static public boolean isInstance (Class c, Object obj) { + return isAssignableFrom(c, obj.getClass()); + } + + /** Determines if the class or interface represented by first Class parameter is either the same as, or is a superclass or + * superinterface of, the class or interface represented by the second Class parameter. */ + static public boolean isAssignableFrom (Class c1, Class c2) { + Type c1Type = ReflectionCache.getType(c1); + Type c2Type = ReflectionCache.getType(c2); + return c1Type.isAssignableFrom(c2Type); + } + + /** Returns true if the class or interface represented by the supplied Class is a member class. */ + static public boolean isMemberClass (Class c) { + return ReflectionCache.getType(c).isMemberClass(); + } + + /** Returns true if the class or interface represented by the supplied Class is a static class. */ + static public boolean isStaticClass (Class c) { + return ReflectionCache.getType(c).isStatic(); + } + + /** Creates a new instance of the class represented by the supplied Class. */ + static public T newInstance (Class c) throws ReflectionException { + try { + return (T)ReflectionCache.getType(c).newInstance(); + } catch (NoSuchMethodException e) { + throw new ReflectionException("Could not use default constructor of " + c.getName(), e); + } + } + + /** Returns an array of {@link Constructor} containing the public constructors of the class represented by the supplied Class. */ + static public Constructor[] getConstructors (Class c) { + com.artemis.gwtref.client.Constructor[] constructors = ReflectionCache.getType(c).getConstructors(); + Constructor[] result = new Constructor[constructors.length]; + for (int i = 0, j = constructors.length; i < j; i++) { + result[i] = new Constructor(constructors[i]); + } + return result; + } + + /** Returns a {@link Constructor} that represents the public constructor for the supplied class which takes the supplied + * parameter types. */ + static public Constructor getConstructor (Class c, Class... parameterTypes) throws ReflectionException { + try { + return new Constructor(ReflectionCache.getType(c).getConstructor(parameterTypes)); + } catch (SecurityException e) { + throw new ReflectionException("Security violation while getting constructor for class: " + c.getName(), e); + } catch (NoSuchMethodException e) { + throw new ReflectionException("Constructor not found for class: " + c.getName(), e); + } + } + + /** Returns a {@link Constructor} that represents the constructor for the supplied class which takes the supplied parameter + * types. */ + static public Constructor getDeclaredConstructor (Class c, Class... parameterTypes) throws ReflectionException { + try { + return new Constructor(ReflectionCache.getType(c).getDeclaredConstructor(parameterTypes)); + } catch (SecurityException e) { + throw new ReflectionException("Security violation while getting constructor for class: " + c.getName(), e); + } catch (NoSuchMethodException e) { + throw new ReflectionException("Constructor not found for class: " + c.getName(), e); + } + } + + /** Returns an array of {@link Method} containing the public member methods of the class represented by the supplied Class. */ + static public Method[] getMethods (Class c) { + com.artemis.gwtref.client.Method[] methods = ReflectionCache.getType(c).getMethods(); + Method[] result = new Method[methods.length]; + for (int i = 0, j = methods.length; i < j; i++) { + result[i] = new Method(methods[i]); + } + return result; + } + + /** Returns a {@link Method} that represents the public member method for the supplied class which takes the supplied parameter + * types. */ + static public Method getMethod (Class c, String name, Class... parameterTypes) throws ReflectionException { + try { + return new Method(ReflectionCache.getType(c).getMethod(name, parameterTypes)); + } catch (SecurityException e) { + throw new ReflectionException("Security violation while getting method: " + name + ", for class: " + c.getName(), e); + } catch (NoSuchMethodException e) { + throw new ReflectionException("Method not found: " + name + ", for class: " + c.getName(), e); + } + } + + /** Returns an array of {@link Method} containing the methods declared by the class represented by the supplied Class. */ + static public Method[] getDeclaredMethods (Class c) { + com.artemis.gwtref.client.Method[] methods = ReflectionCache.getType(c).getDeclaredMethods(); + Method[] result = new Method[methods.length]; + for (int i = 0, j = methods.length; i < j; i++) { + result[i] = new Method(methods[i]); + } + return result; + } + + /** Returns a {@link Method} that represents the method declared by the supplied class which takes the supplied parameter types. */ + static public Method getDeclaredMethod (Class c, String name, Class... parameterTypes) throws ReflectionException { + try { + return new Method(ReflectionCache.getType(c).getMethod(name, parameterTypes)); + } catch (SecurityException e) { + throw new ReflectionException("Security violation while getting method: " + name + ", for class: " + c.getName(), e); + } catch (NoSuchMethodException e) { + throw new ReflectionException("Method not found: " + name + ", for class: " + c.getName(), e); + } + } + + /** Returns an array of {@link Field} containing the public fields of the class represented by the supplied Class. */ + static public Field[] getFields (Class c) { + com.artemis.gwtref.client.Field[] fields = ReflectionCache.getType(c).getFields(); + Field[] result = new Field[fields.length]; + for (int i = 0, j = fields.length; i < j; i++) { + result[i] = new Field(fields[i]); + } + return result; + } + + /** Returns a {@link Field} that represents the specified public member field for the supplied class. */ + static public Field getField (Class c, String name) throws ReflectionException { + try { + return new Field(ReflectionCache.getType(c).getField(name)); + } catch (SecurityException e) { + throw new ReflectionException("Security violation while getting field: " + name + ", for class: " + c.getName(), e); + } + } + + /** Returns an array of {@link Field} objects reflecting all the fields declared by the supplied class. */ + static public Field[] getDeclaredFields (Class c) { + com.artemis.gwtref.client.Field[] fields = ReflectionCache.getType(c).getDeclaredFields(); + Field[] result = new Field[fields.length]; + for (int i = 0, j = fields.length; i < j; i++) { + result[i] = new Field(fields[i]); + } + return result; + } + + /** Returns a {@link Field} that represents the specified declared field for the supplied class. */ + static public Field getDeclaredField (Class c, String name) throws ReflectionException { + try { + return new Field(ReflectionCache.getType(c).getField(name)); + } catch (SecurityException e) { + throw new ReflectionException("Security violation while getting field: " + name + ", for class: " + c.getName(), e); + } + } + +} diff --git a/artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/com/artemis/utils/reflect/Constructor.java b/artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/com/artemis/utils/reflect/Constructor.java new file mode 100644 index 000000000..c8af16736 --- /dev/null +++ b/artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/com/artemis/utils/reflect/Constructor.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.artemis.utils.reflect; + +import com.artemis.utils.reflect.ReflectionException; + +/** Provides information about, and access to, a single constructor for a Class. + * @author nexsoftware */ +public final class Constructor { + + private final com.artemis.gwtref.client.Constructor constructor; + + Constructor (com.artemis.gwtref.client.Constructor constructor) { + this.constructor = constructor; + } + + /** Returns an array of Class objects that represent the formal parameter types, in declaration order, of the constructor. */ + public Class[] getParameterTypes () { + return null; + } + + /** Returns the Class object representing the class or interface that declares the constructor. */ + public Class getDeclaringClass () { + return constructor.getEnclosingType(); + } + + public boolean isAccessible () { + return constructor.isPublic(); + } + + public void setAccessible (boolean accessible) { + // NOOP in GWT + } + + /** Uses the constructor to create and initialize a new instance of the constructor's declaring class, with the supplied + * initialization parameters. */ + public Object newInstance (Object... args) throws ReflectionException { + try { + return constructor.newInstance(args); + } catch (IllegalArgumentException e) { + throw new ReflectionException("Illegal argument(s) supplied to constructor for class: " + getDeclaringClass().getName(), + e); + } + } + +} diff --git a/artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/com/artemis/utils/reflect/Field.java b/artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/com/artemis/utils/reflect/Field.java new file mode 100644 index 000000000..58539fe0e --- /dev/null +++ b/artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/com/artemis/utils/reflect/Field.java @@ -0,0 +1,138 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.artemis.utils.reflect; + +import java.lang.annotation.Annotation; +import java.util.Arrays; + +import com.artemis.gwtref.client.ReflectionCache; +import com.artemis.gwtref.client.Type; +import com.artemis.utils.reflect.ReflectionException; + +/** Provides information about, and access to, a single field of a class or interface. + * @author nexsoftware */ +public final class Field { + + private final com.artemis.gwtref.client.Field field; + + Field (com.artemis.gwtref.client.Field field) { + this.field = field; + } + + /** Returns the name of the field. */ + public String getName () { + return field.getName(); + } + + /** Returns a Class object that identifies the declared type for the field. */ + public Class getType () { + return field.getType().getClassOfType(); + } + + /** Returns the Class object representing the class or interface that declares the field. */ + public Class getDeclaringClass () { + return field.getEnclosingType().getClassOfType(); + } + + public boolean isAccessible () { + return field.isPublic(); + } + + public void setAccessible (boolean accessible) { + // NOOP in GWT + } + + /** Return true if the field does not include any of the {@code private}, {@code protected}, or {@code public} modifiers. */ + public boolean isDefaultAccess () { + return !isPrivate() && !isProtected() && !isPublic(); + } + + /** Return true if the field includes the {@code final} modifier. */ + public boolean isFinal () { + return field.isFinal(); + } + + /** Return true if the field includes the {@code private} modifier. */ + public boolean isPrivate () { + return field.isPrivate(); + } + + /** Return true if the field includes the {@code protected} modifier. */ + public boolean isProtected () { + return field.isProtected(); + } + + /** Return true if the field includes the {@code public} modifier. */ + public boolean isPublic () { + return field.isPublic(); + } + + /** Return true if the field includes the {@code static} modifier. */ + public boolean isStatic () { + return field.isStatic(); + } + + /** Return true if the field includes the {@code transient} modifier. */ + public boolean isTransient () { + return field.isTransient(); + } + + /** Return true if the field includes the {@code volatile} modifier. */ + public boolean isVolatile () { + return field.isVolatile(); + } + + /** Return true if the field is a synthetic field. */ + public boolean isSynthetic () { + return field.isSynthetic(); + } + + /** If the type of the field is parameterized, returns the Class object representing the parameter type at the specified index, + * null otherwise. */ + public Class getElementType (int index) { + Type elementType = field.getElementType(index); + return elementType != null ? elementType.getClassOfType() : null; + } + + /** Returns the value of the field on the supplied object. */ + public Object get (Object obj) throws ReflectionException { + try { + return field.get(obj); + } catch (IllegalArgumentException e) { + throw new ReflectionException("Could not get " + getDeclaringClass() + "#" + getName() + ": " + e.getMessage(), e); + } catch (IllegalAccessException e) { + throw new ReflectionException("Illegal access to field " + getName() + ": " + e.getMessage(), e); + } + } + + /** Sets the value of the field on the supplied object. */ + public void set (Object obj, Object value) throws ReflectionException { + try { + field.set(obj, value); + } catch (IllegalArgumentException e) { + throw new ReflectionException("Could not set " + getDeclaringClass() + "#" + getName() + ": " + e.getMessage(), e); + } catch (IllegalAccessException e) { + throw new ReflectionException("Illegal access to field " + getName() + ": " + e.getMessage(), e); + } + } + + @SuppressWarnings("rawtypes") + public boolean hasAnnotation(Class annotationClass) { + return Arrays.asList(field.getAnnotationClasses()).contains(annotationClass.getName()); + } + +} diff --git a/artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/com/artemis/utils/reflect/Method.java b/artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/com/artemis/utils/reflect/Method.java new file mode 100644 index 000000000..7a72220aa --- /dev/null +++ b/artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/com/artemis/utils/reflect/Method.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.artemis.utils.reflect; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; + +import com.artemis.gwtref.client.Parameter; +import com.artemis.utils.reflect.ReflectionException; + +/** Provides information about, and access to, a single method on a class or interface. + * @author nexsoftware */ +public final class Method { + + private final com.artemis.gwtref.client.Method method; + + Method (com.artemis.gwtref.client.Method method) { + this.method = method; + } + + /** Returns the name of the method. */ + public String getName () { + return method.getName(); + } + + /** Returns a Class object that represents the formal return type of the method. */ + public Class getReturnType () { + return method.getReturnType(); + } + + /** Returns an array of Class objects that represent the formal parameter types, in declaration order, of the method. */ + public Class[] getParameterTypes () { + Parameter[] parameters = method.getParameters(); + Class[] parameterTypes = new Class[parameters.length]; + for (int i = 0, j = parameters.length; i < j; i++) { + parameterTypes[i] = parameters[i].getType(); + } + return parameterTypes; + } + + /** Returns the Class object representing the class or interface that declares the method. */ + public Class getDeclaringClass () { + return method.getEnclosingType(); + } + + public boolean isAccessible () { + return method.isPublic(); + } + + public void setAccessible (boolean accessible) { + // NOOP in GWT + } + + /** Return true if the method includes the {@code abstract} modifier. */ + public boolean isAbstract () { + return method.isAbstract(); + } + + /** Return true if the method does not include any of the {@code private}, {@code protected}, or {@code public} modifiers. */ + public boolean isDefaultAccess () { + return !isPrivate() && !isProtected() && !isPublic(); + } + + /** Return true if the method includes the {@code final} modifier. */ + public boolean isFinal () { + return method.isFinal(); + } + + /** Return true if the method includes the {@code private} modifier. */ + public boolean isPrivate () { + return method.isPrivate(); + } + + /** Return true if the method includes the {@code protected} modifier. */ + public boolean isProtected () { + return method.isProtected(); + } + + /** Return true if the method includes the {@code public} modifier. */ + public boolean isPublic () { + return method.isPublic(); + } + + /** Return true if the method includes the {@code native} modifier. */ + public boolean isNative () { + return method.isNative(); + } + + /** Return true if the method includes the {@code static} modifier. */ + public boolean isStatic () { + return method.isStatic(); + } + + /** Return true if the method takes a variable number of arguments. */ + public boolean isVarArgs () { + return method.isVarArgs(); + } + + /** Invokes the underlying method on the supplied object with the supplied parameters. */ + public Object invoke (Object obj, Object... args) throws ReflectionException { + try { + return method.invoke(obj, args); + } catch (IllegalArgumentException e) { + throw new ReflectionException("Illegal argument(s) supplied to method: " + getName(), e); + } + } + +} diff --git a/artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/java/util/BitSet.java b/artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/java/util/BitSet.java new file mode 100644 index 000000000..d5e3c73af --- /dev/null +++ b/artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/java/util/BitSet.java @@ -0,0 +1,917 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package java.util; + +import com.google.gwt.core.client.JavaScriptObject; +import com.google.gwt.core.client.JsArrayInteger; + +/** + * This implementation uses bit groups of size 32 to keep track of when bits are + * set to true or false. This implementation also uses the sparse nature of + * JavaScript arrays to speed up cases when very few bits are set in a large bit + * set. + * + * Since there is no speed advantage to pre-allocating array sizes in JavaScript + * the underlying array's length is shrunk to Sun's "logical length" whenever + * length() is called. This length is the index of the highest true bit, plus + * one, or zero if there are aren't any. This may cause the size() method to + * return a different size than in a true Java VM. + */ +public class BitSet { + // To speed up certain operations this class also uses the index properties + // of arrays as described in section 15.4 of "Standard ECMA-262" (June + // 1997), + // which can currently be found here: + // http://www.mozilla.org/js/language/E262.pdf + // + // 15.4 Array Objects + // Array objects give special treatment to a certain class of property + // names. + // A property name P (in the form of a string value) is an array index if + // and + // only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal + // to (2^32)-1. + + // checks the index range + private static void checkIndex(int bitIndex) { + // we only need to test for negatives, as there is no bit index too + // high. + if (bitIndex < 0) { + throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex); + } + } + + // checks to ensure indexes are not negative and not in reverse order + private static void checkRange(int fromIndex, int toIndex) { + if (fromIndex < 0) { + throw new IndexOutOfBoundsException("fromIndex < 0: " + fromIndex); + } + if (toIndex < 0) { + throw new IndexOutOfBoundsException("toIndex < 0: " + toIndex); + } + if (fromIndex > toIndex) { + throw new IndexOutOfBoundsException("fromIndex: " + fromIndex + + " > toIndex: " + toIndex); + } + } + + // converts from a bit index to a word index + private static int wordIndex(int bitIndex) { + // 32 bits per index + return bitIndex >>> 5; + } + + // converts from a word index to a bit index + private static int bitIndex(int wordIndex) { + // 1 word index for every 32 bit indexes + return wordIndex << 5; + } + + // gives the word offset for a bit index + private static int bitOffset(int bitIndex) { + return bitIndex & 0x1f; + } + + // + // none of the following static method perform any bounds checking + // + + // clears one bit + private static void clear(JsArrayInteger array, int bitIndex) { + int index = wordIndex(bitIndex); + int word = getWord(array, index); + if (word != 0) { + // mask the correct bit out + setWord(array, index, word & ~(1 << (bitOffset(bitIndex)))); + } + } + + // clones the JSArrayInteger array + private static native JsArrayInteger clone(JsArrayInteger array) /*-{ + return array.slice(0); + }-*/; + + // flips one bit + private static void flip(JsArrayInteger array, int bitIndex) { + // calculate index and offset + int index = wordIndex(bitIndex); + int offset = bitOffset(bitIndex); + + // figure out if the bit is on or off + int word = getWord(array, index); + if (((word >>> offset) & 1) == 1) { + // if on, turn it off + setWord(array, index, word & ~(1 << offset)); + } else { + // if off, turn it on + array.set(index, word | (1 << offset)); + } + }; + + // gets one bit + private static boolean get(JsArrayInteger array, int bitIndex) { + // retrieve the bits for the given index + int word = getWord(array, wordIndex(bitIndex)); + + // shift and mask the bit out + return ((word >>> (bitOffset(bitIndex))) & 1) == 1; + } + + // sets one bit to true + private static void set(JsArrayInteger array, int bitIndex) { + int index = wordIndex(bitIndex); + array.set(index, getWord(array, index) | (1 << (bitOffset(bitIndex)))); + } + + // sets all bits to true within the given range + private static void set(JsArrayInteger array, int fromIndex, int toIndex) { + int first = wordIndex(fromIndex); + int last = wordIndex(toIndex); + int startBit = bitOffset(fromIndex); + int endBit = bitOffset(toIndex); + + if (first == last) { + // set the bits in between first and last + maskInWord(array, first, startBit, endBit); + + } else { + // set the bits from fromIndex to the next 32 bit boundary + if (startBit != 0) { + maskInWord(array, first++, startBit, 32); + } + + // set the bits from the last 32 bit boundary to the toIndex + if (endBit != 0) { + maskInWord(array, last, 0, endBit); + } + + // + // set everything in between + // + for (int i = first; i < last; i++) { + array.set(i, 0xffffffff); + } + } + } + + // copies a subset of the array + private static native JsArrayInteger slice(JsArrayInteger array, + int fromIndex, int toIndex) /*-{ + return array.slice(fromIndex, toIndex); + }-*/; + + // trims the array to the minimum size it can without losing data + // returns index of the last element in the array, or -1 if empty + private static native int trimToSize(JsArrayInteger array) /*-{ + var length = array.length; + if (length === 0) { + return -1; + } + + // check if the last bit is false + var last = length - 1; + if (array[last] !== undefined) { + return last; + } + + // interleave property checks and linear index checks from the end + var biggestSeen = -1; + for (var property in array) { + + // test the index first + if (--last === -1) { + return -1; + } + if (array[last] !== undefined) { + return last; + } + + // now check the property + var number = property >>> 0; + if (String(number) == property && number !== 0xffffffff) { + if (number > biggestSeen) { + biggestSeen = number; + } + } + + } + array.length = biggestSeen + 1 + + return biggestSeen; + }-*/; + + // + // word methods use the literal index into the array, not the bit index + // + + // deletes an element from the array + private static native void deleteWord(JsArrayInteger array, int index) /*-{ + delete array[index]; + }-*/; + + // flips all bits stored at a certain index + private static void flipWord(JsArrayInteger array, int index) { + int word = getWord(array, index); + if (word == 0) { + array.set(index, 0xffffffff); + } else { + word = ~word; + setWord(array, index, word); + } + } + + // flips all bits stored at a certain index within the given range + private static void flipMaskedWord(JsArrayInteger array, int index, + int from, int to) { + if (from == to) { + return; + } + // get the bits + int word = getWord(array, index); + // adjust "to" so it will shift out those bits + to = 32 - to; + // create a mask and XOR it in + word ^= (((0xffffffff >>> from) << from) << to) >>> to; + ; + setWord(array, index, word); + } + + // returns all bits stored at a certain index + private static native int getWord(JsArrayInteger array, int index) /*-{ + // OR converts an undefined to 0 + return array[index] | 0; + }-*/; + + // sets all bits to true at a certain index within the given bit range + private static void maskInWord(JsArrayInteger array, int index, int from, + int to) { + // shifting by 32 is the same as shifting by 0, this check prevents that + // from happening in addition to the obvious avoidance of extra work + if (from != to) { + // adjust "to" so it will shift out those bits + to = 32 - to; + // create a mask and OR it in + int value = getWord(array, index); + value |= ((0xffffffff >>> from) << (from + to)) >>> to; + array.set(index, value); + } + }; + + // sets all bits to false at a certain index within the given bit range + private static void maskOutWord(JsArrayInteger array, int index, int from, + int to) { + int word = getWord(array, index); + // something only happens if word has bits set + if (word != 0) { + // create a mask + int mask; + if (from != 0) { + mask = 0xffffffff >>> (32 - from); + } else { + mask = 0; + } + // shifting by 32 is the same as shifting by 0 + if (to != 32) { + mask |= 0xffffffff << to; + } + + // mask it out + word &= mask; + setWord(array, index, word); + } + } + + private static native int nextSetWord(JsArrayInteger array, int index) /*-{ + // interleave property checks and linear "index" checks + var length = array.length; + var localMinimum = @java.lang.Integer::MAX_VALUE; + for (var property in array) { + + // test the index first + if (array[index] !== undefined) { + return index; + } + if (++index >= length) { + return -1; + } + + // now check the property + var number = property >>> 0; + if (String(number) == property && number !== 0xffffffff) { + if (number >= index && number < localMinimum) { + localMinimum = number; + } + } + } + + // if local minimum is what we started at, we found nothing + if (localMinimum === @java.lang.Integer::MAX_VALUE) { + return -1; + } + + return localMinimum; + }-*/; + + // sets all bits at a certain index to the given value + private static void setWord(JsArrayInteger array, int index, int value) { + // keep 0s out of the array + if (value == 0) { + deleteWord(array, index); + } else { + array.set(index, value); + } + } + + // sets the array length + private static native void setLengthWords(JsArrayInteger array, int length) /*-{ + array.length = length; + }-*/; + + // our array of bits + private JsArrayInteger array; + + public BitSet() { + // create a new array + array = JavaScriptObject.createArray().cast(); + } + + public BitSet(int nbits) { + this(); + + // throw an exception to be consistent + // but (do we want to be consistent?) + if (nbits < 0) { + throw new NegativeArraySizeException("nbits < 0: " + nbits); + } + + // even though the array's length is loosely kept to that of Sun's + // "logical + // length," this might help in some cases where code uses size() to fill + // in + // bits after constructing a BitSet, or after having one passed in as a + // parameter. + setLengthWords(array, wordIndex(nbits + 31)); + } + + private BitSet(JsArrayInteger array) { + this.array = array; + } + + public void and(BitSet set) { + // a & a is just a + if (this == set) { + return; + } + + // trim the second set to avoid extra work + trimToSize(set.array); + + // check if the length is longer than otherLength + int otherLength = set.array.length(); + if (array.length() > otherLength) { + // shrink the array, effectively ANDing those bits to false + setLengthWords(array, otherLength); + } + + // truth table + // + // case | a | b | a & b | change? + // 1 | false | false | false | a is already false + // 2 | false | true | false | a is already false + // 3 | true | false | false | set a to false + // 4 | true | true | true | a is already true + // + // we only need to change something in case 3, so iterate over set a + int index = 0; + while ((index = nextSetWord(array, index)) != -1) { + setWord(array, index, array.get(index) & getWord(set.array, index)); + index++; + } + } + + public void andNot(BitSet set) { + // a & !a is false + if (this == set) { + // all falses result in an empty BitSet + clear(); + return; + } + + // trim the second set to avoid extra work + trimToSize(array); + int length = array.length(); + + // truth table + // + // case | a | b | !b | a & !b | change? + // 1 | false | false | true | false | a is already false + // 2 | false | true | false | false | a is already false + // 3 | true | false | true | true | a is already true + // 4 | true | true | false | false | set a to false + // + // we only need to change something in case 4 + // whenever b is true, a should be false, so iterate over set b + int index = 0; + while ((index = nextSetWord(set.array, index)) != -1) { + setWord(array, index, getWord(array, index) & ~set.array.get(index)); + if (++index >= length) { + // nothing further will affect anything + break; + } + } + + } + + public native int cardinality() /*-{ + var count = 0; + var array = this.@java.util.BitSet::array; + for (var property in array) { + var number = property >>> 0; + if (String(number) == property && number !== 0xffffffff) { + count += @java.lang.Integer::bitCount(I)(array[number]); + } + } + return count; + }-*/; + + public void clear() { + // create a new array + array = JavaScriptObject.createArray().cast(); + } + + public void clear(int bitIndex) { + checkIndex(bitIndex); + clear(array, bitIndex); + } + + public void clear(int fromIndex, int toIndex) { + checkRange(fromIndex, toIndex); + + int length = length(); + if (fromIndex >= length) { + // nothing to do + return; + } + + // check to see if toIndex is greater than our array length + if (toIndex >= length) { + // truncate the array by setting it's length + int newLength = wordIndex(fromIndex + 31); + setLengthWords(array, newLength); + + // remove the extra bits off the end + if ((bitIndex(newLength)) - fromIndex != 0) { + maskOutWord(array, newLength - 1, bitOffset(fromIndex), 32); + } + + } else { + int first = wordIndex(fromIndex); + int last = wordIndex(toIndex); + int startBit = bitOffset(fromIndex); + int endBit = bitOffset(toIndex); + + if (first == last) { + // clear the bits in between first and last + maskOutWord(array, first, startBit, endBit); + + } else { + // clear the bits from fromIndex to the next 32 bit boundary + if (startBit != 0) { + maskOutWord(array, first++, startBit, 32); + } + + // clear the bits from the last 32 bit boundary to the toIndex + if (endBit != 0) { + maskOutWord(array, last, 0, endBit); + } + + // + // delete everything in between + // + for (int i = first; i < last; i++) { + deleteWord(array, i); + } + } + } + } + + public Object clone() { + return new BitSet(clone(array)); + } + + @Override + public boolean equals(Object obj) { + if (this != obj) { + + if (!(obj instanceof BitSet)) { + return false; + } + + BitSet other = (BitSet) obj; + + int last = trimToSize(array); + if (last != trimToSize(other.array)) { + return false; + } + + int index = 0; + while ((index = nextSetWord(array, index)) != -1) { + if (getWord(array, index) != getWord(other.array, index)) { + return false; + } + index++; + } + } + + return true; + } + + public void flip(int bitIndex) { + checkIndex(bitIndex); + flip(array, bitIndex); + } + + public void flip(int fromIndex, int toIndex) { + checkRange(fromIndex, toIndex); + + int length = length(); + + // if we are flipping bits beyond our length, we are setting them to + // true + if (fromIndex >= length) { + set(array, fromIndex, toIndex); + return; + } + + // check to see if toIndex is greater than our array length + if (toIndex >= length) { + set(array, length, toIndex); + toIndex = length; + } + + int first = wordIndex(fromIndex); + int last = wordIndex(toIndex); + int startBit = bitOffset(fromIndex); + int end = bitOffset(toIndex); + + if (first == last) { + // flip the bits in between first and last + flipMaskedWord(array, first, startBit, end); + + } else { + // clear the bits from fromIndex to the next 32 bit boundary + if (startBit != 0) { + flipMaskedWord(array, first++, startBit, 32); + } + + // clear the bits from the last 32 bit boundary to the toIndex + if (end != 0) { + flipMaskedWord(array, last, 0, end); + } + + // flip everything in between + for (int i = first; i < last; i++) { + flipWord(array, i); + } + } + } + + public boolean get(int bitIndex) { + checkIndex(bitIndex); + return get(array, bitIndex); + } + + public BitSet get(int fromIndex, int toIndex) { + checkRange(fromIndex, toIndex); + + // no need to go past our length + int length = length(); + if (toIndex >= length) { + toIndex = length(); + } + + // this is the bit shift offset for each group of bits + int rightShift = bitOffset(fromIndex); + + if (rightShift == 0) { + int subFrom = wordIndex(fromIndex); + int subTo = wordIndex(toIndex + 31); + JsArrayInteger subSet = slice(array, subFrom, subTo); + int leftOvers = bitOffset(toIndex); + if (leftOvers != 0) { + maskOutWord(subSet, subTo - subFrom - 1, leftOvers, 32); + } + return new BitSet(subSet); + } + + BitSet subSet = new BitSet(); + + int first = wordIndex(fromIndex); + int last = wordIndex(toIndex); + + if (first == last) { + // number of bits to cut from the end + int end = 32 - (bitOffset(toIndex)); + // raw bits + int word = getWord(array, first); + // shift out those bits + word = ((word << end) >>> end) >>> rightShift; + // set it + if (word != 0) { + subSet.set(0, word); + } + + } else { + // this will hold the newly packed bits + int current = 0; + + // this is the raw index into the sub set + int subIndex = 0; + + // fence post, carry over initial bits + int word = getWord(array, first++); + current = word >>> rightShift; + + // a left shift will be used to shift our bits to the top of + // "current" + int leftShift = 32 - rightShift; + + // loop through everything in the middle + for (int i = first; i <= last; i++) { + word = getWord(array, i); + + // shift out the bits from the top, OR them into current bits + current |= word << leftShift; + + // flush it out + if (current != 0) { + subSet.array.set(subIndex, current); + } + + // keep track of our index + subIndex++; + + // carry over the unused bits + current = word >>> rightShift; + } + + // fence post, flush out the extra bits, but don't go past the "end" + int end = 32 - (bitOffset(toIndex)); + current = (current << (rightShift + end)) >>> (rightShift + end); + if (current != 0) { + subSet.array.set(subIndex, current); + } + } + + return subSet; + } + + /** + * This hash is different than the one described in Sun's documentation. The + * described hash uses 64 bit integers and that's not practical in + * JavaScript. + */ + @Override + public int hashCode() { + // FNV constants + final int fnvOffset = 0x811c9dc5; + final int fnvPrime = 0x1000193; + + // initialize + final int last = trimToSize(array); + int hash = fnvOffset ^ last; + + // loop over the data + for (int i = 0; i <= last; i++) { + int value = getWord(array, i); + // hash one byte at a time using FNV1 + hash = (hash * fnvPrime) ^ (value & 0xff); + hash = (hash * fnvPrime) ^ ((value >>> 8) & 0xff); + hash = (hash * fnvPrime) ^ ((value >>> 16) & 0xff); + hash = (hash * fnvPrime) ^ (value >>> 24); + } + + return hash; + } + + public boolean intersects(BitSet set) { + int last = trimToSize(array); + + if (this == set) { + // if it has any bits then it intersects itself + return last != -1; + } + + int length = set.array.length(); + int index = 0; + while ((index = nextSetWord(array, index)) != -1) { + if ((array.get(index) & getWord(set.array, index)) != 0) { + return true; + } + if (++index >= length) { + // nothing further can intersect + break; + } + } + + return false; + } + + public boolean isEmpty() { + return length() == 0; + } + + public int length() { + int last = trimToSize(array); + if (last == -1) { + return 0; + } + + // compute the position of the leftmost bit's index + int offsets[] = { 16, 8, 4, 2, 1 }; + int bitMasks[] = { 0xffff0000, 0xff00, 0xf0, 0xc, 0x2 }; + int position = bitIndex(last) + 1; + int word = getWord(array, last); + for (int i = 0; i < offsets.length; i++) { + if ((word & bitMasks[i]) != 0) { + word >>>= offsets[i]; + position += offsets[i]; + } + } + return position; + } + + public int nextClearBit(int fromIndex) { + checkIndex(fromIndex); + int index = wordIndex(fromIndex); + + // special case for first index + int fromBit = fromIndex - (bitIndex(index)); + int word = getWord(array, index); + for (int i = fromBit; i < 32; i++) { + if ((word & (1 << i)) == 0) { + return (bitIndex(index)) + i; + } + } + + // loop through the rest + while (true) { + index++; + word = getWord(array, index); + if (word != 0xffffffff) { + return (bitIndex(index)) + Integer.numberOfTrailingZeros(~word); + } + } + } + + public int nextSetBit(int fromIndex) { + checkIndex(fromIndex); + + int index = wordIndex(fromIndex); + + // check the current word + int word = getWord(array, index); + if (word != 0) { + for (int i = bitOffset(fromIndex); i < 32; i++) { + if ((word & (1 << i)) != 0) { + return (bitIndex(index)) + i; + } + } + } + index++; + + // find the next set word + trimToSize(array); + index = nextSetWord(array, index); + if (index == -1) { + return -1; + } + + // return the next set bit + return (bitIndex(index)) + + Integer.numberOfTrailingZeros(array.get(index)); + }; + + public void or(BitSet set) { + // a | a is just a + if (this == set) { + return; + } + + // truth table + // + // case | a | b | a | b | change? + // 1 | false | false | false | a is already false + // 2 | false | true | true | set a to true + // 3 | true | false | true | a is already true + // 4 | true | true | true | a is already true + // + // we only need to change something in case 2 + // case 2 only happens when b is true, so iterate over set b + int index = 0; + while ((index = nextSetWord(set.array, index)) != -1) { + setWord(array, index, getWord(array, index) | set.array.get(index)); + index++; + } + } + + public void set(int bitIndex) { + checkIndex(bitIndex); + set(array, bitIndex); + } + + public void set(int bitIndex, boolean value) { + if (value == true) { + set(bitIndex); + } else { + clear(bitIndex); + } + } + + public void set(int fromIndex, int toIndex) { + checkRange(fromIndex, toIndex); + set(array, fromIndex, toIndex); + } + + public void set(int fromIndex, int toIndex, boolean value) { + if (value == true) { + set(fromIndex, toIndex); + } else { + clear(fromIndex, toIndex); + } + } + + public int size() { + // the number of bytes that can fit without using "more" memory + return bitIndex(array.length()); + } + + @Override + public String toString() { + // possibly faster if done in JavaScript and all numerical properties + // are + // put into an array and sorted + + int length = length(); + if (length == 0) { + // a "length" of 0 means there are no bits set to true + return "{}"; + } + + StringBuilder sb = new StringBuilder("{"); + + // at this point, there is at least one true bit, nextSetBit can not + // fail + int next = nextSetBit(0); + sb.append(next); + + // loop until nextSetBit returns -1 + while ((next = nextSetBit(next + 1)) != -1) { + sb.append(", "); + sb.append(next); + } + + sb.append("}"); + return sb.toString(); + } + + public void xor(BitSet set) { + // a ^ a is false + if (this == set) { + // this results in an empty BitSet + clear(); + return; + } + + // truth table + // + // case | a | b | a ^ b | change? + // 1 | false | false | false | a is already false + // 2 | false | true | true | set a to true + // 3 | true | false | true | a is already true + // 4 | true | true | false | set a to false + // + // we need to change something in cases 2 and 4 + // cases 2 and 4 only happen when b is true, so iterate over set b + int index = 0; + while ((index = nextSetWord(set.array, index)) != -1) { + setWord(array, index, getWord(array, index) ^ set.array.get(index)); + index++; + } + } + +} \ No newline at end of file diff --git a/artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/java/util/UUID.java b/artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/java/util/UUID.java new file mode 100644 index 000000000..13ba03a42 --- /dev/null +++ b/artemis-gwt/src/main/java/com/artemis/backends/gwt/emu/java/util/UUID.java @@ -0,0 +1,112 @@ +package java.util; + +import java.io.Serializable; + + +/** + * @author senk.christian@gmail.com + * + */ +public class UUID implements Serializable, Comparable { + + private static final long serialVersionUID = 7373345728974414241L; + private static final char[] CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray(); + + private String value; + + /** + * + */ + private UUID() {} + + /** + * @param uuidString + * @return + */ + public static UUID fromString(String uuidString) { + //TODO: Validation + + final UUID uuid = new UUID(); + uuid.value = uuidString; + + return uuid; + } + + /** + * @return + */ + public static UUID randomUUID() { + return fromString(generateUUIDString()); + } + + /** + * Generate a RFC4122, version 4 ID. Example: + * "92329D39-6F5C-4520-ABFC-AAB64544E172" + */ + private static String generateUUIDString() { + char[] uuid = new char[36]; + int r; + + // rfc4122 requires these characters + uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; + uuid[14] = '4'; + + // Fill in random data. At i==19 set the high bits of clock sequence as + // per rfc4122, sec. 4.1.5 + for (int i = 0; i < 36; i++) { + if (uuid[i] == 0) { + r = (int) (Math.random()*16); + uuid[i] = CHARS[(i == 19) ? (r & 0x3) | 0x8 : r & 0xf]; + } + } + return new String(uuid); + } + + /* (non-Javadoc) + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + @Override + public int compareTo(UUID arg0) { + return value.compareTo(arg0.value); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((value == null) ? 0 : value.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + UUID other = (UUID) obj; + if (value == null) { + if (other.value != null) + return false; + } else if (!value.equals(other.value)) + return false; + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return value; + } + +} \ No newline at end of file diff --git a/artemis-gwt/src/main/java/com/artemis/gwtref/ArtemisReflect.gwt.xml b/artemis-gwt/src/main/java/com/artemis/gwtref/ArtemisReflect.gwt.xml new file mode 100644 index 000000000..bed06a5cc --- /dev/null +++ b/artemis-gwt/src/main/java/com/artemis/gwtref/ArtemisReflect.gwt.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/artemis-gwt/src/main/java/com/artemis/gwtref/client/Constructor.java b/artemis-gwt/src/main/java/com/artemis/gwtref/client/Constructor.java new file mode 100644 index 000000000..a68cd60f2 --- /dev/null +++ b/artemis-gwt/src/main/java/com/artemis/gwtref/client/Constructor.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.artemis.gwtref.client; + +/** A constructor for the enclosing type. + * @author mzechner */ +public class Constructor extends Method { + Constructor (String name, Class enclosingType, Class returnType, Parameter[] parameters, boolean isAbstract, boolean isFinal, + boolean isStatic, boolean isDefaultAccess, boolean isPrivate, boolean isProtected, boolean isPublic, boolean isNative, + boolean isVarArgs, boolean isMethod, boolean isConstructor, int methodId) { + super(name, enclosingType, returnType, parameters, isAbstract, isFinal, isStatic, isDefaultAccess, isPrivate, isProtected, + isPublic, isNative, isVarArgs, isMethod, isConstructor, methodId); + } + + /** @return a new instance of the enclosing type of this constructor. */ + public Object newInstance (Object... params) { + return super.invoke(null, params); + } +} diff --git a/artemis-gwt/src/main/java/com/artemis/gwtref/client/Field.java b/artemis-gwt/src/main/java/com/artemis/gwtref/client/Field.java new file mode 100644 index 000000000..e5ea3bde8 --- /dev/null +++ b/artemis-gwt/src/main/java/com/artemis/gwtref/client/Field.java @@ -0,0 +1,130 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.artemis.gwtref.client; + +import java.util.Arrays; + +public class Field { + final String name; + final Class enclosingType; + final Class type; + final boolean isFinal; + final boolean isDefaultAccess; + final boolean isPrivate; + final boolean isProtected; + final boolean isPublic; + final boolean isStatic; + final boolean isTransient; + final boolean isVolatile; + final int getter; + final int setter; + final Class[] elementTypes; + final String[] annotationClasses; + + Field (String name, Class enclosingType, Class type, boolean isFinal, boolean isDefaultAccess, boolean isPrivate, + boolean isProtected, boolean isPublic, boolean isStatic, boolean isTransient, boolean isVolatile, int getter, int setter, + Class[] elementTypes, String[] annotationClasses) { + this.name = name; + this.enclosingType = enclosingType; + this.type = type; + this.isFinal = isFinal; + this.isDefaultAccess = isDefaultAccess; + this.isPrivate = isPrivate; + this.isProtected = isProtected; + this.isPublic = isPublic; + this.isStatic = isStatic; + this.isTransient = isTransient; + this.isVolatile = isVolatile; + this.getter = getter; + this.setter = setter; + this.elementTypes = elementTypes; + this.annotationClasses = annotationClasses; + } + + public String[] getAnnotationClasses() { + return annotationClasses; + } + + public Object get (Object obj) throws IllegalAccessException { + return ReflectionCache.instance.get(this, obj); + } + + public void set (Object obj, Object value) throws IllegalAccessException { + ReflectionCache.instance.set(this, obj, value); + } + + public Type getElementType (int index) { + if (elementTypes != null && index < elementTypes.length) return ReflectionCache.getType(elementTypes[index]); + return null; + } + + public String getName () { + return name; + } + + public Type getEnclosingType () { + return ReflectionCache.getType(enclosingType); + } + + public Type getType () { + return ReflectionCache.getType(type); + } + + public boolean isSynthetic () { + return false; + } + + public boolean isFinal () { + return isFinal; + } + + public boolean isDefaultAccess () { + return isDefaultAccess; + } + + public boolean isPrivate () { + return isPrivate; + } + + public boolean isProtected () { + return isProtected; + } + + public boolean isPublic () { + return isPublic; + } + + public boolean isStatic () { + return isStatic; + } + + public boolean isTransient () { + return isTransient; + } + + public boolean isVolatile () { + return isVolatile; + } + + @Override + public String toString () { + return "Field [name=" + name + ", enclosingType=" + enclosingType + ", type=" + type + ", isFinal=" + isFinal + + ", isDefaultAccess=" + isDefaultAccess + ", isPrivate=" + isPrivate + ", isProtected=" + isProtected + ", isPublic=" + + isPublic + ", isStatic=" + isStatic + ", isTransient=" + isTransient + ", isVolatile=" + isVolatile + ", getter=" + + getter + ", setter=" + setter + ", elementTypes=" + Arrays.toString(elementTypes) + "]"; + } +} diff --git a/artemis-gwt/src/main/java/com/artemis/gwtref/client/IReflectionCache.java b/artemis-gwt/src/main/java/com/artemis/gwtref/client/IReflectionCache.java new file mode 100644 index 000000000..a065e4730 --- /dev/null +++ b/artemis-gwt/src/main/java/com/artemis/gwtref/client/IReflectionCache.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.artemis.gwtref.client; + +import java.util.Collection; + +public interface IReflectionCache { + // Class level methods + public Collection getKnownTypes (); + + public Type forName (String name); + + public Object newArray (Class componentType, int size); + + public int getArrayLength (Type type, Object obj); + + public Object getArrayElement (Type type, Object obj, int i); + + public void setArrayElement (Type type, Object obj, int i, Object value); + + // Field Methods + public Object get (Field field, Object obj) throws IllegalAccessException; + + public void set (Field field, Object obj, Object value) throws IllegalAccessException; + + // Method Methods :p + public Object invoke (Method m, Object obj, Object[] params); +} diff --git a/artemis-gwt/src/main/java/com/artemis/gwtref/client/Method.java b/artemis-gwt/src/main/java/com/artemis/gwtref/client/Method.java new file mode 100644 index 000000000..450dfee18 --- /dev/null +++ b/artemis-gwt/src/main/java/com/artemis/gwtref/client/Method.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.artemis.gwtref.client; + +import java.util.Arrays; + +/** Describes a method of a {@link Type}. + * @author mzechner */ +public class Method { + private static final Parameter[] EMPTY_PARAMS = new Parameter[0]; + final String name; + final Class enclosingType; + final Class returnType; + final boolean isAbstract; + final boolean isFinal; + final boolean isStatic; + final boolean isNative; + final boolean isDefaultAccess; + final boolean isPrivate; + final boolean isProtected; + final boolean isPublic; + final boolean isVarArgs; + final boolean isMethod; + final boolean isConstructor; + final Parameter[] parameters; + final int methodId; + + public Method (String name, Class enclosingType, Class returnType, Parameter[] parameters, boolean isAbstract, + boolean isFinal, boolean isStatic, boolean isDefaultAccess, boolean isPrivate, boolean isProtected, boolean isPublic, + boolean isNative, boolean isVarArgs, boolean isMethod, boolean isConstructor, int methodId) { + this.name = name; + this.enclosingType = enclosingType; + this.parameters = parameters != null ? parameters : EMPTY_PARAMS; + this.returnType = returnType; + this.isAbstract = isAbstract; + this.isFinal = isFinal; + this.isStatic = isStatic; + this.isNative = isNative; + this.isDefaultAccess = isDefaultAccess; + this.isPrivate = isPrivate; + this.isProtected = isProtected; + this.isPublic = isPublic; + this.isVarArgs = isVarArgs; + this.isMethod = isMethod; + this.isConstructor = isConstructor; + this.methodId = methodId; + } + + /** @return the {@link Class} of the enclosing type. */ + public Class getEnclosingType () { + return enclosingType; + } + + /** @return the {@link Class} of the return type or null. */ + public Class getReturnType () { + return returnType; + } + + /** @return the list of parameters, can be a zero size array. */ + public Parameter[] getParameters () { + return parameters; + } + + /** @return the name of the method. */ + public String getName () { + return name; + } + + public boolean isAbstract () { + return isAbstract; + } + + public boolean isFinal () { + return isFinal; + } + + public boolean isDefaultAccess () { + return isDefaultAccess; + } + + public boolean isPrivate () { + return isPrivate; + } + + public boolean isProtected () { + return isProtected; + } + + public boolean isPublic () { + return isPublic; + } + + public boolean isNative () { + return isNative; + } + + public boolean isVarArgs () { + return isVarArgs; + } + + public boolean isStatic () { + return isStatic; + } + + public boolean isMethod () { + return isMethod; + } + + public boolean isConstructor () { + return isConstructor; + } + + /** Invokes the method on the given object. Ignores the object if this is a static method. Throws an IllegalArgumentException if + * the parameters do not match. + * @param obj the object to invoke the method on or null. + * @param params the parameters to pass to the method or null. + * @return the return value or null if the method does not return anything. */ + public Object invoke (Object obj, Object... params) { + if (parameters.length != (params != null ? params.length : 0)) throw new IllegalArgumentException("Parameter mismatch"); + + return ReflectionCache.instance.invoke(this, obj, params); + } + + boolean match (String name, Class... types) { + return this.name.equals(name) && match(types); + } + + boolean match (Class... types) { + if (types == null) return parameters.length == 0; + if (types.length != parameters.length) return false; + for (int i = 0; i < types.length; i++) { + Type t1 = ReflectionCache.instance.forName(parameters[i].getType().getName()); + Type t2 = ReflectionCache.instance.forName(types[i].getName()); + if (t1 != t2 && !t1.isAssignableFrom(t2)) return false; + } + return true; + } + + @Override + public String toString () { + return "Method [name=" + name + ", enclosingType=" + enclosingType + ", returnType=" + returnType + ", isAbstract=" + + isAbstract + ", isFinal=" + isFinal + ", isStatic=" + isStatic + ", isNative=" + isNative + ", isDefaultAccess=" + + isDefaultAccess + ", isPrivate=" + isPrivate + ", isProtected=" + isProtected + ", isPublic=" + isPublic + + ", isVarArgs=" + isVarArgs + ", isMethod=" + isMethod + ", isConstructor=" + isConstructor + ", parameters=" + + Arrays.toString(parameters) + "]"; + } +} diff --git a/artemis-gwt/src/main/java/com/artemis/gwtref/client/Parameter.java b/artemis-gwt/src/main/java/com/artemis/gwtref/client/Parameter.java new file mode 100644 index 000000000..dcdd92f1e --- /dev/null +++ b/artemis-gwt/src/main/java/com/artemis/gwtref/client/Parameter.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.artemis.gwtref.client; + +public class Parameter { + final String name; + final Class type; + final String jnsi; + + Parameter (String name, Class type, String jnsi) { + this.name = name; + this.type = type; + this.jnsi = jnsi; + } + + public String getName () { + return name; + } + + public Class getType () { + return type; + } + + public String getJnsi () { + return jnsi; + } + + @Override + public String toString () { + return "Parameter [name=" + name + ", type=" + type + ", jnsi=" + jnsi + "]"; + } +} diff --git a/artemis-gwt/src/main/java/com/artemis/gwtref/client/ReflectionCache.java b/artemis-gwt/src/main/java/com/artemis/gwtref/client/ReflectionCache.java new file mode 100644 index 000000000..7da33cf27 --- /dev/null +++ b/artemis-gwt/src/main/java/com/artemis/gwtref/client/ReflectionCache.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.artemis.gwtref.client; + +import java.util.Collection; + +import com.google.gwt.core.client.GWT; + +public class ReflectionCache { + public static IReflectionCache instance = GWT.create(IReflectionCache.class); + + public static Type forName (String name) throws ClassNotFoundException { + Type type = instance.forName(convert(name)); + if (type == null) { + throw new RuntimeException("Couldn't find Type for class '" + name + "'"); + } + return type; + } + + public static Type getType (Class clazz) { + if (clazz == null) return null; + Type type = instance.forName(convert(clazz.getName())); + if (type == null) { + throw new RuntimeException("Couldn't find Type for class '" + clazz.getName() + "'"); + } + return type; + } + + private static String convert (String className) { + if (className.startsWith("[")) { + int dimensions = 0; + char c = className.charAt(0); + String suffix = ""; + while (c == '[') { + dimensions++; + suffix += "[]"; + c = className.charAt(dimensions); + } + char t = className.charAt(dimensions); + switch (t) { + case 'Z': + return "boolean" + suffix; + case 'B': + return "byte" + suffix; + case 'C': + return "char" + suffix; + case 'L': + return className.substring(dimensions + 1, className.length() - 1).replace('$', '.') + suffix; + case 'D': + return "double" + suffix; + case 'F': + return "float" + suffix; + case 'I': + return "int" + suffix; + case 'J': + return "long" + suffix; + case 'S': + return "short" + suffix; + default: + throw new IllegalArgumentException("Couldn't transform '" + className + "' to qualified source name"); + } + } else { + return className.replace('$', '.'); + } + } + + public static Object newArray (Class componentType, int size) { + return instance.newArray(componentType, size); + } + + public static Collection getKnownTypes () { + return instance.getKnownTypes(); + } +} diff --git a/artemis-gwt/src/main/java/com/artemis/gwtref/client/Test.java b/artemis-gwt/src/main/java/com/artemis/gwtref/client/Test.java new file mode 100644 index 000000000..cd79b0bc2 --- /dev/null +++ b/artemis-gwt/src/main/java/com/artemis/gwtref/client/Test.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.artemis.gwtref.client; + +import com.google.gwt.core.client.EntryPoint; + +public class Test implements EntryPoint { + public static enum Enu { + Winter, Summer, Bleh; + } + + public static class A { + String text; + float numberf; + int numberi; + + public String getText () { + return text; + } + + public void setText (String text) { + this.text = text; + } + + public float getNumberf () { + return numberf; + } + + public void setNumberf (float numberf) { + this.numberf = numberf; + } + + public int getNumberi () { + return numberi; + } + + public void setNumberi (int numberi) { + this.numberi = numberi; + } + + public float getSum (float a, float b) { + return a + b; + } + } + + public static class B extends A { + String text = "This is a string"; + + public void testWithPackagePrivate (C c, int a) { + } + + public void testWidthPrivate (A c) { + } + + public void testVoid () { + } + + public native void test (A c) /*-{ + // this.@com.badlogic.gwtref.client.Test.B::testWidthPrivate(LC;)(c); + }-*/; + } + + public static class C { + } + + @Override + public void onModuleLoad () { + try { + Type ta = ReflectionCache.getType(A.class); + Type tb = ReflectionCache.getType(B.class); + B b = (B)tb.newInstance(); + for (Field f : tb.getFields()) + System.out.println(f); + for (Method m : tb.getMethods()) + System.out.println(m); + + tb.getDeclaredFields()[0].set(b, "Field of B"); + ta.getDeclaredFields()[0].set(b, "Field of A"); + System.out.println(ta.getMethod("getText").invoke(b)); + System.out.println(ta.getMethod("getSum", float.class, float.class).invoke(b, 1, 2)); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/artemis-gwt/src/main/java/com/artemis/gwtref/client/Type.java b/artemis-gwt/src/main/java/com/artemis/gwtref/client/Type.java new file mode 100644 index 000000000..202fee5f1 --- /dev/null +++ b/artemis-gwt/src/main/java/com/artemis/gwtref/client/Type.java @@ -0,0 +1,246 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.artemis.gwtref.client; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** Describes a type (equivalent to {@link Class}), providing methods to retrieve fields, constructors, methods and super + * interfaces of the type. Only types that are visible (public) can be described by this class. + * @author mzechner */ +public class Type { + private static final Field[] EMPTY_FIELDS = new Field[0]; + private static final Method[] EMPTY_METHODS = new Method[0]; + private static final Constructor[] EMPTY_CONSTRUCTORS = new Constructor[0]; + + String name; + int id; + Class clazz; + Class superClass; + Set assignables = new HashSet(); + boolean isAbstract; + boolean isInterface; + boolean isPrimitive; + boolean isEnum; + boolean isArray; + boolean isMemberClass; + boolean isStatic; + + Field[] fields = EMPTY_FIELDS; + Method[] methods = EMPTY_METHODS; + Constructor[] constructors = EMPTY_CONSTRUCTORS; + + Class componentType; + Object[] enumConstants; + + /** @return a new instance of this type created via the default constructor which must be public. */ + public Object newInstance () throws NoSuchMethodException { + return getConstructor().newInstance(); + } + + /** @return the fully qualified name of this type. */ + public String getName () { + return name; + } + + /** @return the {@link Class} of this type. */ + public Class getClassOfType () { + return clazz; + } + + /** @return the super class of this type or null */ + public Type getSuperclass () { + try { + return superClass == null ? null : ReflectionCache.forName(superClass.getName()); + } catch (ClassNotFoundException e) { + return null; + } + } + + /** @param otherType the other type + * @return whether this type is assignable to the other type. */ + public boolean isAssignableFrom (Type otherType) { + return otherType.assignables.contains(getClassOfType()); + } + + /** @param name the name of the field + * @return the public field of this type or one of its super interfaces with the given name or null. See + * {@link Class#getField(String)}. */ + public Field getField (String name) { + Type t = this; + while (t != null) { + Field[] declFields = t.getDeclaredFields(); + if (declFields != null) { + for (Field f : declFields) { + if (f.isPublic && f.name.equals(name)) return f; + } + } + t = t.getSuperclass(); + } + return null; + } + + /** @return an array containing all the public fields of this class and its super classes. See {@link Class#getFields()}. */ + public Field[] getFields () { + ArrayList allFields = new ArrayList(); + Type t = this; + while (t != null) { + Field[] declFields = t.getDeclaredFields(); + if (declFields != null) { + for (Field f : declFields) { + if (f.isPublic) allFields.add(f); + } + } + t = t.getSuperclass(); + } + return allFields.toArray(new Field[allFields.size()]); + } + + /** @return an array containing all the fields of this class, including private and protected fields. See + * {@link Class#getDeclaredFields()}. */ + public Field[] getDeclaredFields () { + return fields; + } + + /** @param name the name of the method + * @param parameterTypes the types of the parameters of the method + * @return the public method that matches the name and parameter types of this type or one of its super interfaces. + * @throws NoSuchMethodException */ + public Method getMethod (String name, Class... parameterTypes) throws NoSuchMethodException { + Type t = this; + while (t != null) { + Method[] declMethods = t.getDeclaredMethods(); + if (declMethods != null) { + for (Method m : declMethods) { + if (m.isPublic() && m.match(name, parameterTypes)) return m; + } + } + t = t.getSuperclass(); + } + throw new NoSuchMethodException(); + } + + /** s * @return an array containing all public methods of this class and its super classes. See {@link Class#getMethods()}. */ + public Method[] getMethods () { + ArrayList allMethods = new ArrayList(); + Type t = this; + while (t != null) { + Method[] declMethods = t.getDeclaredMethods(); + if (declMethods != null) { + for (Method m : declMethods) { + if (m.isPublic()) allMethods.add(m); + } + } + t = t.getSuperclass(); + } + return allMethods.toArray(new Method[allMethods.size()]); + } + + /** @return an array containing all methods of this class, including abstract, private and protected methods. See + * {@link Class#getDeclaredMethods()}. */ + public Method[] getDeclaredMethods () { + return methods; + } + + public Constructor[] getConstructors () { + return constructors; + } + + public Constructor getDeclaredConstructor (Class... parameterTypes) throws NoSuchMethodException { + return getConstructor(parameterTypes); + } + + public Constructor getConstructor (Class... parameterTypes) throws NoSuchMethodException { + if (constructors != null) { + for (Constructor c : constructors) { + if (c.isPublic() && c.match(parameterTypes)) return c; + } + } + throw new NoSuchMethodException(); + } + + public boolean isAbstract () { + return isAbstract; + } + + public boolean isInterface () { + return isInterface; + } + + public boolean isPrimitive () { + return isPrimitive; + } + + public boolean isEnum () { + return isEnum; + } + + public boolean isArray () { + return isArray; + } + + public boolean isMemberClass () { + return isMemberClass; + } + + public boolean isStatic () { + return isStatic; + } + + /** @return the class of the components if this is an array type or null. */ + public Class getComponentType () { + return componentType; + } + + /** @param obj an array object of this type. + * @return the length of the given array object. */ + public int getArrayLength (Object obj) { + return ReflectionCache.instance.getArrayLength(this, obj); + } + + /** @param obj an array object of this type. + * @param i the index of the element to retrieve. + * @return the element at position i in the array. */ + public Object getArrayElement (Object obj, int i) { + return ReflectionCache.instance.getArrayElement(this, obj, i); + } + + /** Sets the element i in the array object to value. + * @param obj an array object of this type. + * @param i the index of the element to set. + * @param value the element value. */ + public void setArrayElement (Object obj, int i, Object value) { + ReflectionCache.instance.setArrayElement(this, obj, i, value); + } + + /** @return the enumeration constants if this type is an enumeration or null. */ + public Object[] getEnumConstants () { + return enumConstants; + } + + @Override + public String toString () { + return "Type [name=" + name + ",\n clazz=" + clazz + ",\n superClass=" + superClass + ",\n assignables=" + assignables + + ",\n isAbstract=" + isAbstract + ",\n isInterface=" + isInterface + ",\n isPrimitive=" + isPrimitive + ",\n isEnum=" + + isEnum + ",\n isArray=" + isArray + ",\n isMemberClass=" + isMemberClass + ",\n isStatic=" + isStatic + ",\n fields=" + + Arrays.toString(fields) + ",\n methods=" + Arrays.toString(methods) + ",\n constructors=" + + Arrays.toString(constructors) + ",\n componentType=" + componentType + ",\n enumConstants=" + + Arrays.toString(enumConstants) + "]"; + } +} diff --git a/artemis-gwt/src/main/java/com/artemis/gwtref/gen/ReflectionCacheGenerator.java b/artemis-gwt/src/main/java/com/artemis/gwtref/gen/ReflectionCacheGenerator.java new file mode 100644 index 000000000..2458772e5 --- /dev/null +++ b/artemis-gwt/src/main/java/com/artemis/gwtref/gen/ReflectionCacheGenerator.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.artemis.gwtref.gen; + +import com.google.gwt.core.ext.Generator; +import com.google.gwt.core.ext.GeneratorContext; +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.TypeOracle; +import static com.google.gwt.core.ext.TreeLogger.*; + +public class ReflectionCacheGenerator extends Generator { + @Override + public String generate (TreeLogger logger, GeneratorContext context, String typeName) throws UnableToCompleteException { + TypeOracle oracle = context.getTypeOracle(); + assert (oracle != null); + JClassType type = oracle.findType(typeName); + if (type == null) { + logger.log(ERROR, "Couldn't find type '" + typeName + "'"); + throw new UnableToCompleteException(); + } + + if (type.isInterface() == null) { + logger.log(ERROR, "Type '" + typeName + "' must be an interface"); + throw new UnableToCompleteException(); + } + + ReflectionCacheSourceCreator source = new ReflectionCacheSourceCreator(logger, context, type); + return source.create(); + } +} diff --git a/artemis-gwt/src/main/java/com/artemis/gwtref/gen/ReflectionCacheSourceCreator.java b/artemis-gwt/src/main/java/com/artemis/gwtref/gen/ReflectionCacheSourceCreator.java new file mode 100644 index 000000000..b400ab98d --- /dev/null +++ b/artemis-gwt/src/main/java/com/artemis/gwtref/gen/ReflectionCacheSourceCreator.java @@ -0,0 +1,895 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.artemis.gwtref.gen; + +import java.io.PrintWriter; +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.gwt.core.ext.BadPropertyValueException; +import com.google.gwt.core.ext.ConfigurationProperty; +import com.google.gwt.core.ext.GeneratorContext; +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.TreeLogger.Type; +import com.google.gwt.core.ext.typeinfo.JAbstractMethod; +import com.google.gwt.core.ext.typeinfo.JArrayType; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.JConstructor; +import com.google.gwt.core.ext.typeinfo.JEnumConstant; +import com.google.gwt.core.ext.typeinfo.JEnumType; +import com.google.gwt.core.ext.typeinfo.JField; +import com.google.gwt.core.ext.typeinfo.JMethod; +import com.google.gwt.core.ext.typeinfo.JPackage; +import com.google.gwt.core.ext.typeinfo.JParameter; +import com.google.gwt.core.ext.typeinfo.JParameterizedType; +import com.google.gwt.core.ext.typeinfo.JPrimitiveType; +import com.google.gwt.core.ext.typeinfo.JType; +import com.google.gwt.core.ext.typeinfo.NotFoundException; +import com.google.gwt.core.ext.typeinfo.TypeOracle; +import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; +import com.google.gwt.user.rebind.SourceWriter; + +public class ReflectionCacheSourceCreator { + private static final List PRIMITIVE_TYPES = Collections.unmodifiableList(Arrays.asList(new String[] {"char", "int", + "long", "byte", "short", "float", "double", "boolean"})); + final TreeLogger logger; + final GeneratorContext context; + final JClassType type; + final String simpleName; + final String packageName; + SourceWriter sw; + StringBuffer source = new StringBuffer(); + List types = new ArrayList(); + List setterGetterStubs = new ArrayList(); + List methodStubs = new ArrayList(); + int nextId = 0; + + class SetterGetterStub { + int getter; + int setter; + String name; + String enclosingType; + String type; + boolean isStatic; + boolean isFinal; + boolean unused; + } + + class MethodStub { + String enclosingType; + String returnType; + List parameterTypes = new ArrayList(); + String jnsi; + int methodId; + boolean isStatic; + boolean isAbstract; + boolean isFinal; + boolean isNative; + boolean isConstructor; + boolean isMethod; + String name; + boolean unused; + } + + public ReflectionCacheSourceCreator (TreeLogger logger, GeneratorContext context, JClassType type) { + this.logger = logger; + this.context = context; + this.type = type; + this.packageName = type.getPackage().getName(); + this.simpleName = type.getSimpleSourceName() + "Generated"; + logger.log(Type.INFO, type.getQualifiedSourceName()); + } + + private int nextId () { + return nextId++; + } + + public String create () { + ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(packageName, simpleName); + composer.addImplementedInterface("com.artemis.gwtref.client.IReflectionCache"); + imports(composer); + PrintWriter printWriter = context.tryCreate(logger, packageName, simpleName); + if (printWriter == null) { + return packageName + "." + simpleName; + } + sw = composer.createSourceWriter(context, printWriter); + + generateLookups(); + + getKnownTypesC(); + forNameC(); + newArrayC(); + + getArrayLengthT(); + getArrayElementT(); + setArrayElementT(); + + getF(); + setF(); + + invokeM(); + + sw.commit(logger); + createProxy(type); + return packageName + "." + simpleName; + } + + private void createProxy (JClassType type) { + ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(type.getPackage().getName(), + type.getSimpleSourceName() + "Proxy"); + PrintWriter printWriter = context.tryCreate(logger, packageName, simpleName); + if (printWriter == null) { + return; + } + SourceWriter writer = composer.createSourceWriter(context, printWriter); + writer.commit(logger); + } + + private void generateLookups () { + p("Map types = new HashMap();"); + + TypeOracle typeOracle = context.getTypeOracle(); + JPackage[] packages = typeOracle.getPackages(); + + // gather all types from wanted packages + for (JPackage p : packages) { + for (JClassType t : p.getTypes()) { + gatherTypes(t.getErasedType(), types); + } + } + + gatherTypes(typeOracle.findType("java.util.List").getErasedType(), types); + gatherTypes(typeOracle.findType("java.util.ArrayList").getErasedType(), types); + gatherTypes(typeOracle.findType("java.util.HashMap").getErasedType(), types); + gatherTypes(typeOracle.findType("java.util.Map").getErasedType(), types); + gatherTypes(typeOracle.findType("java.lang.String").getErasedType(), types); + gatherTypes(typeOracle.findType("java.lang.Boolean").getErasedType(), types); + gatherTypes(typeOracle.findType("java.lang.Byte").getErasedType(), types); + gatherTypes(typeOracle.findType("java.lang.Long").getErasedType(), types); + gatherTypes(typeOracle.findType("java.lang.Character").getErasedType(), types); + gatherTypes(typeOracle.findType("java.lang.Short").getErasedType(), types); + gatherTypes(typeOracle.findType("java.lang.Integer").getErasedType(), types); + gatherTypes(typeOracle.findType("java.lang.Float").getErasedType(), types); + gatherTypes(typeOracle.findType("java.lang.CharSequence").getErasedType(), types); + gatherTypes(typeOracle.findType("java.lang.Double").getErasedType(), types); + gatherTypes(typeOracle.findType("java.lang.Object").getErasedType(), types); + + // sort the types so the generated output will be stable between runs + Collections.sort(types, new Comparator() { + public int compare (JType o1, JType o2) { + return o1.getQualifiedSourceName().compareTo(o2.getQualifiedSourceName()); + } + }); + + // generate Type lookup generator methods. + int id = 0; + for (JType t : types) { + String typeGen = createTypeGenerator(t); + p("private void c" + (id++) + "() {"); + p(typeGen); + p("}\n"); + } + + // generate constructor that calls all the type generators + // that populate the map. + p("public " + simpleName + "() {"); + for (int i = 0; i < id; i++) { + p("c" + i + "();"); + } + p("}"); + + // sort the stubs so the generated output will be stable between runs + Collections.sort(setterGetterStubs, new Comparator() { + @Override + public int compare (SetterGetterStub o1, SetterGetterStub o2) { + return new Integer(o1.setter).compareTo(o2.setter); + } + }); + + // generate field setters/getters + for (SetterGetterStub stub : setterGetterStubs) { + String stubSource = generateSetterGetterStub(stub); + if (stubSource.equals("")) stub.unused = true; + p(stubSource); + } + + // sort the stubs so the generated output will be stable between runs + Collections.sort(methodStubs, new Comparator() { + @Override + public int compare (MethodStub o1, MethodStub o2) { + return new Integer(o1.methodId).compareTo(o2.methodId); + } + }); + + // generate methods + for (MethodStub stub : methodStubs) { + String stubSource = generateMethodStub(stub); + if (stubSource.equals("")) stub.unused = true; + p(stubSource); + } + + logger.log(Type.INFO, types.size() + " types reflected"); + } + + private void out (String message, int nesting) { + for (int i = 0; i < nesting; i++) + System.out.print(" "); + System.out.println(message); + } + + int nesting = 0; + + private void gatherTypes (JType type, List types) { + nesting++; + // came here from a type that has no super class + if (type == null) { + nesting--; + return; + } + // package info + if (type.getQualifiedSourceName().contains("-")) { + nesting--; + return; + } + + // not visible + if (!isVisible(type)) { + nesting--; + return; + } + + // filter reflection scope based on configuration in gwt xml module + boolean keep = false; + String name = type.getQualifiedSourceName(); + try { + ConfigurationProperty prop; + keep |= !name.contains("."); + prop = context.getPropertyOracle().getConfigurationProperty("artemis.reflect.include"); + for (String s : prop.getValues()) + keep |= name.contains(s); + prop = context.getPropertyOracle().getConfigurationProperty("artemis.reflect.exclude"); + for (String s : prop.getValues()) + keep &= !name.equals(s); + } catch (BadPropertyValueException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (!keep) { + nesting--; + return; + } + + // already visited this type + if (types.contains(type.getErasedType())) { + nesting--; + return; + } + types.add(type.getErasedType()); + out(type.getErasedType().getQualifiedSourceName(), nesting); + + if (type instanceof JPrimitiveType) { + // nothing to do for a primitive type + nesting--; + return; + } else { + // gather fields + JClassType c = (JClassType)type; + JField[] fields = c.getFields(); + if (fields != null) { + for (JField field : fields) { + gatherTypes(field.getType().getErasedType(), types); + } + } + + // gather super types & interfaces + gatherTypes(c.getSuperclass(), types); + JClassType[] interfaces = c.getImplementedInterfaces(); + if (interfaces != null) { + for (JClassType i : interfaces) { + gatherTypes(i.getErasedType(), types); + } + } + + // gather method parameter & return types + JMethod[] methods = c.getMethods(); + if (methods != null) { + for (JMethod m : methods) { + gatherTypes(m.getReturnType().getErasedType(), types); + if (m.getParameterTypes() != null) { + for (JType p : m.getParameterTypes()) { + gatherTypes(p.getErasedType(), types); + } + } + } + } + + // gather inner classes + JClassType[] inner = c.getNestedTypes(); + if (inner != null) { + for (JClassType i : inner) { + gatherTypes(i.getErasedType(), types); + } + } + } + nesting--; + } + + private String generateMethodStub (MethodStub stub) { + buffer.setLength(0); + + if (stub.enclosingType == null) { + logger.log(Type.INFO, "method '" + stub.name + "' of invisible class is not invokable"); + return ""; + } + + if (stub.enclosingType.startsWith("java") || stub.enclosingType.contains("google")) { + logger.log(Type.INFO, "not emitting code for accessing method " + stub.name + " in class '" + stub.enclosingType + + ", either in java.* or GWT related class"); + return ""; + } + + if (stub.enclosingType.contains("[]")) { + logger.log(Type.INFO, "method '" + stub.name + "' of class '" + stub.enclosingType + + "' is not invokable because the class is an array type"); + return ""; + } + + for (int i = 0; i < stub.parameterTypes.size(); i++) { + String paramType = stub.parameterTypes.get(i); + if (paramType == null) { + logger.log(Type.INFO, "method '" + stub.name + "' of class '" + stub.enclosingType + + "' is not invokable because one of its argument types is not visible"); + return ""; + } else if (paramType.startsWith("long") || paramType.contains("java.lang.Long")) { + logger.log(Type.INFO, "method '" + stub.name + "' of class '" + stub.enclosingType + + " has long parameter, prohibited in JSNI"); + return ""; + } else { + stub.parameterTypes.set(i, paramType.replace(".class", "")); + } + } + if (stub.returnType == null) { + logger.log(Type.INFO, "method '" + stub.name + "' of class '" + stub.enclosingType + + "' is not invokable because its return type is not visible"); + return ""; + } + if (stub.returnType.startsWith("long") || stub.returnType.contains("java.lang.Long")) { + logger.log(Type.INFO, "method '" + stub.name + "' of class '" + stub.enclosingType + + " has long return type, prohibited in JSNI"); + return ""; + } + + stub.enclosingType = stub.enclosingType.replace(".class", ""); + stub.returnType = stub.returnType.replace(".class", ""); + + if (stub.isMethod) { + boolean isVoid = stub.returnType.equals("void"); + pbn("private native " + (isVoid ? "Object" : stub.returnType) + " m" + stub.methodId + "("); + if (!stub.isStatic) pbn(stub.enclosingType + " obj" + (stub.parameterTypes.size() > 0 ? ", " : "")); + int i = 0; + for (String paramType : stub.parameterTypes) { + pbn(paramType + " p" + i + (i < stub.parameterTypes.size() - 1 ? "," : "")); + i++; + } + pb(") /*-{"); + + if (!isVoid) pbn("return "); + if (stub.isStatic) + pbn("@" + stub.enclosingType + "::" + stub.name + "(" + stub.jnsi + ")("); + else + pbn("obj.@" + stub.enclosingType + "::" + stub.name + "(" + stub.jnsi + ")("); + + for (i = 0; i < stub.parameterTypes.size(); i++) { + pbn("p" + i + (i < stub.parameterTypes.size() - 1 ? ", " : "")); + } + pb(");"); + if (isVoid) pb("return null;"); + pb("}-*/;"); + } else { + pbn("private static " + stub.returnType + " m" + stub.methodId + "("); + int i = 0; + for (String paramType : stub.parameterTypes) { + pbn(paramType + " p" + i + (i < stub.parameterTypes.size() - 1 ? "," : "")); + i++; + } + pb(") {"); + + pbn("return new " + stub.returnType + "("); + for (i = 0; i < stub.parameterTypes.size(); i++) { + pbn("p" + i + (i < stub.parameterTypes.size() - 1 ? ", " : "")); + } + pb(");"); + + pb("}"); + } + + return buffer.toString(); + } + + private String generateSetterGetterStub (SetterGetterStub stub) { + buffer.setLength(0); + if (stub.enclosingType == null || stub.type == null) { + logger.log(Type.INFO, "field '" + stub.name + "' in class '" + stub.enclosingType + "' is not accessible as its type '" + + stub.type + "' is not public"); + return ""; + } + if (stub.enclosingType.startsWith("java") || stub.enclosingType.contains("google")) { + logger.log(Type.INFO, "not emitting code for accessing field " + stub.name + " in class '" + stub.enclosingType + + ", either in java.* or GWT related class"); + return ""; + } + + if (stub.type.startsWith("long") || stub.type.contains("java.lang.Long")) { + logger.log(Type.INFO, "not emitting code for accessing field " + stub.name + " in class '" + stub.enclosingType + + " as its of type long which can't be used with JSNI"); + return ""; + } + + stub.enclosingType = stub.enclosingType.replace(".class", ""); + stub.type = stub.type.replace(".class", ""); + + pb("// " + stub.enclosingType + "#" + stub.name); + pbn("private native " + stub.type + " g" + stub.getter + "(" + stub.enclosingType + " obj) /*-{"); + if (stub.isStatic) + pbn("return @" + stub.enclosingType + "::" + stub.name + ";"); + else + pbn("return obj.@" + stub.enclosingType + "::" + stub.name + ";"); + pb("}-*/;"); + + if (!stub.isFinal) { + pbn("private native void s" + stub.setter + "(" + stub.enclosingType + " obj, " + stub.type + " value) /*-{"); + if (stub.isStatic) + pbn("@" + stub.enclosingType + "::" + stub.name + " = value"); + else + pbn("obj.@" + stub.enclosingType + "::" + stub.name + " = value;"); + pb("}-*/;"); + } + + return buffer.toString(); + } + + private boolean isVisible (JType type) { + if (type == null) return false; + + if (type instanceof JClassType) { + if (type instanceof JArrayType) { + JType componentType = ((JArrayType)type).getComponentType(); + while (componentType instanceof JArrayType) { + componentType = ((JArrayType)componentType).getComponentType(); + } + if (componentType instanceof JClassType) { + return ((JClassType)componentType).isPublic(); + } + } else { + return ((JClassType)type).isPublic(); + } + } + return true; + } + + private Map typeNames2typeIds = new HashMap(); + + private String createTypeGenerator (JType t) { + buffer.setLength(0); + String varName = "t"; + if (t instanceof JPrimitiveType) varName = "p"; + int id = nextId(); + typeNames2typeIds.put(t.getErasedType().getQualifiedSourceName(), id); + pb("Type " + varName + " = new Type();"); + pb(varName + ".name = \"" + t.getErasedType().getQualifiedSourceName() + "\";"); + pb(varName + ".id = " + id + ";"); + pb(varName + ".clazz = " + t.getErasedType().getQualifiedSourceName() + ".class;"); + if (t instanceof JClassType) { + JClassType c = (JClassType)t; + if (isVisible(c.getSuperclass())) + pb(varName + ".superClass = " + c.getSuperclass().getErasedType().getQualifiedSourceName() + ".class;"); + if (c.getFlattenedSupertypeHierarchy() != null) { + pb("Set " + varName + "Assignables = new HashSet();"); + for (JType i : c.getFlattenedSupertypeHierarchy()) { + if (!isVisible(i)) continue; + pb(varName + "Assignables.add(" + i.getErasedType().getQualifiedSourceName() + ".class);"); + } + pb(varName + ".assignables = " + varName + "Assignables;"); + } + if (c.isInterface() != null) pb(varName + ".isInterface = true;"); + if (c.isEnum() != null) pb(varName + ".isEnum = true;"); + if (c.isArray() != null) pb(varName + ".isArray = true;"); + if (c.isMemberType()) pb(varName + ".isMemberClass = true;"); + pb(varName + ".isStatic = " + c.isStatic() + ";"); + pb(varName + ".isAbstract = " + c.isAbstract() + ";"); + + if (c.getFields() != null) { + pb(varName + ".fields = new Field[] {"); + for (JField f : c.getFields()) { + String enclosingType = getType(c); + String fieldType = getType(f.getType()); + int setter = nextId(); + int getter = nextId(); + String elementType = getElementTypes(f); + Annotation[] annotations = f.getAnnotations(); + StringBuilder annotationClasses = new StringBuilder("new String[]{"); + + for (int i = 0; i < annotations.length; i++) { + annotationClasses.append("\"").append(annotations[i].annotationType().getName()).append("\""); + if(i < annotations.length-1) { + annotationClasses.append(","); + } + } + annotationClasses.append("}"); + + pb("new Field(\"" + f.getName() + "\", " + enclosingType + ", " + fieldType + ", " + f.isFinal() + ", " + + f.isDefaultAccess() + ", " + f.isPrivate() + ", " + f.isProtected() + ", " + f.isPublic() + ", " + + f.isStatic() + ", " + f.isTransient() + ", " + f.isVolatile() + ", " + getter + ", " + setter + ", " + + elementType + ", " + annotationClasses.toString() + "), "); + + SetterGetterStub stub = new SetterGetterStub(); + stub.name = f.getName(); + stub.enclosingType = enclosingType; + stub.type = fieldType; + stub.isStatic = f.isStatic(); + stub.isFinal = f.isFinal(); + if (enclosingType != null && fieldType != null) { + stub.getter = getter; + stub.setter = setter; + } + setterGetterStubs.add(stub); + } + pb("};"); + } + + printMethods(c, varName, "Method", c.getMethods()); + if (!c.isAbstract() && (c.getEnclosingType() == null || c.isStatic())) { + printMethods(c, varName, "Constructor", c.getConstructors()); + } else { + logger.log(Type.INFO, c.getName() + " can't be instantiated. Constructors not generated"); + } + + if (c.isArray() != null) { + pb(varName + ".componentType = " + getType(c.isArray().getComponentType()) + ";"); + } + if (c.isEnum() != null) { + JEnumConstant[] enumConstants = c.isEnum().getEnumConstants(); + if (enumConstants != null) { + pb(varName + ".enumConstants = new Object[" + enumConstants.length + "];"); + for (int i = 0; i < enumConstants.length; i++) { + pb(varName + ".enumConstants[" + i + "] = " + c.getErasedType().getQualifiedSourceName() + "." + + enumConstants[i].getName() + ";"); + } + } + } + } else { + pb(varName + ".isPrimitive = true;"); + } + + pb("types.put(\"" + t.getErasedType().getQualifiedSourceName() + "\", " + varName + ");"); + return buffer.toString(); + } + + private void printMethods (JClassType c, String varName, String methodType, JAbstractMethod[] methodTypes) { + if (methodTypes != null) { + pb(varName + "." + methodType.toLowerCase() + "s = new " + methodType + "[] {"); + for (JAbstractMethod m : methodTypes) { + MethodStub stub = new MethodStub(); + stub.enclosingType = getType(c); + if (m.isMethod() != null) { + stub.isMethod = true; + stub.returnType = getType(m.isMethod().getReturnType()); + stub.isStatic = m.isMethod().isStatic(); + stub.isAbstract = m.isMethod().isAbstract(); + stub.isNative = m.isMethod().isAbstract(); + stub.isFinal = m.isMethod().isFinal(); + } else { + stub.isConstructor = true; + stub.returnType = stub.enclosingType; + } + stub.jnsi = ""; + stub.methodId = nextId(); + stub.name = m.getName(); + methodStubs.add(stub); + + pb("new " + methodType + "(\"" + m.getName() + "\", "); + pb(stub.enclosingType + ", "); + pb(stub.returnType + ", "); + + pb("new Parameter[] {"); + if (m.getParameters() != null) { + for (JParameter p : m.getParameters()) { + stub.parameterTypes.add(getType(p.getType())); + stub.jnsi += p.getType().getErasedType().getJNISignature(); + pb("new Parameter(\"" + p.getName() + "\", " + getType(p.getType()) + ", \"" + p.getType().getJNISignature() + + "\"), "); + } + } + pb("}, "); + + pb(stub.isAbstract + ", " + stub.isFinal + ", " + stub.isStatic + ", " + m.isDefaultAccess() + ", " + m.isPrivate() + + ", " + m.isProtected() + ", " + m.isPublic() + ", " + stub.isNative + ", " + m.isVarArgs() + ", " + + stub.isMethod + ", " + stub.isConstructor + ", " + stub.methodId + "),"); + } + pb("};"); + } + } + + private String getElementTypes (JField f) { + StringBuilder b = new StringBuilder(); + JParameterizedType params = f.getType().isParameterized(); + if (params != null) { + JClassType[] typeArgs = params.getTypeArgs(); + b.append("new Class[] {"); + for (JClassType typeArg : typeArgs) { + if (typeArg.isWildcard() != null) + b.append("Object.class"); + else if (!isVisible(typeArg)) + b.append("null"); + else if (typeArg.isClassOrInterface() != null) + b.append(typeArg.isClassOrInterface().getQualifiedSourceName()).append(".class"); + else if (typeArg.isParameterized() != null) + b.append(typeArg.isParameterized().getQualifiedBinaryName()).append(".class"); + else + b.append("null"); + b.append(", "); + } + b.append("}"); + return b.toString(); + } + return "null"; + } + + private String getType (JType type) { + if (!isVisible(type)) return null; + return type.getErasedType().getQualifiedSourceName() + ".class"; + } + + private void imports (ClassSourceFileComposerFactory composer) { + composer.addImport("java.security.AccessControlException"); + composer.addImport("java.util.*"); + composer.addImport("com.artemis.gwtref.client.*"); + } + + private void invokeM () { + p("public Object invoke(Method m, Object obj, Object[] params) {"); + SwitchedCodeBlock pc = new SwitchedCodeBlock("m.methodId"); + int subN = 0; + int nDispatch = 0; + + for (MethodStub stub : methodStubs) { + if (stub.enclosingType == null) continue; + if (stub.enclosingType.contains("[]")) continue; + if (stub.returnType == null) continue; + if (stub.unused) continue; + boolean paramsOk = true; + for (String paramType : stub.parameterTypes) { + if (paramType == null) { + paramsOk = false; + break; + } + } + + if (!paramsOk) continue; + + buffer.setLength(0); + pbn("return m" + stub.methodId + "("); + addParameters(stub); + pbn(");"); + pc.add(stub.methodId, buffer.toString()); + nDispatch++; + if (nDispatch > 1000) { + pc.print(); + pc = new SwitchedCodeBlock("m.methodId"); + subN++; + p(" return invoke" + subN + "(m, obj, params);"); + p("}"); + p("public Object invoke" + subN + "(Method m, Object obj, Object[] params) {"); + nDispatch = 0; + } + } + + pc.print(); + p(" throw new IllegalArgumentException(\"Missing method-stub \" + m.methodId + \" for method \" + m.name);"); + p("}"); + } + + private void addParameters (MethodStub stub) { + if (!stub.isStatic && !stub.isConstructor) + pbn("(" + stub.enclosingType + ")obj" + (stub.parameterTypes.size() > 0 ? "," : "")); + for (int i = 0; i < stub.parameterTypes.size(); i++) { + pbn(cast(stub.parameterTypes.get(i), "params[" + i + "]") + (i < stub.parameterTypes.size() - 1 ? ", " : "")); + } + } + + private String cast (String paramType, String arg) { + if (paramType.equals("byte") || paramType.equals("short") || paramType.equals("int") || paramType.equals("long") + || paramType.equals("float") || paramType.equals("double")) { + return "((Number)" + arg + ")." + paramType + "Value()"; + } else if (paramType.equals("boolean")) { + return "((Boolean)" + arg + ")." + paramType + "Value()"; + } else if (paramType.equals("char")) { + return "((Character)" + arg + ")." + paramType + "Value()"; + } else { + return "((" + paramType + ")" + arg + ")"; + } + } + + private void setF () { + p("public void set(Field field, Object obj, Object value) throws IllegalAccessException {"); + SwitchedCodeBlock pc = new SwitchedCodeBlock("field.setter"); + for (SetterGetterStub stub : setterGetterStubs) { + if (stub.enclosingType == null || stub.type == null || stub.isFinal || stub.unused) continue; + pc.add(stub.setter, "s" + stub.setter + "(" + cast(stub.enclosingType, "obj") + ", " + cast(stub.type, "value") + + "); return;"); + } + pc.print(); + p(" throw new IllegalArgumentException(\"Missing setter-stub \" + field.setter + \" for field \" + field.name);"); + p("}"); + } + + private void getF () { + p("public Object get(Field field, Object obj) throws IllegalAccessException {"); + SwitchedCodeBlock pc = new SwitchedCodeBlock("field.getter"); + for (SetterGetterStub stub : setterGetterStubs) { + if (stub.enclosingType == null || stub.type == null || stub.unused) continue; + pc.add(stub.getter, "return g" + stub.getter + "(" + cast(stub.enclosingType, "obj") + ");"); + } + pc.print(); + p(" throw new IllegalArgumentException(\"Missing getter-stub \" + field.getter + \" for field \" + field.name);"); + p("}"); + } + + private static boolean isInstantiableWithNewOperator (JClassType t) { + if (!t.isDefaultInstantiable() || t instanceof JArrayType || t instanceof JEnumType) return false; + try { + JConstructor constructor = t.getConstructor(new JType[0]); + return constructor != null && constructor.isPublic(); + } catch (NotFoundException e) { + return false; + } + } + + private void setArrayElementT () { + p("public void setArrayElement(Type type, Object obj, int i, Object value) {"); + SwitchedCodeBlock pc = new SwitchedCodeBlock("type.id"); + + for (String s : PRIMITIVE_TYPES) { + if (!typeNames2typeIds.containsKey(s + "[]")) continue; + pc.add(typeNames2typeIds.get(s + "[]"), "((" + s + "[])obj)[i] = " + cast(s, "value") + "; return;"); + } + + pc.print(); + p(" ((Object[])obj)[i] = value;"); + p("}"); + } + + private void getArrayElementT () { + p("public Object getArrayElement(Type type, Object obj, int i) {"); + SwitchedCodeBlock pc = new SwitchedCodeBlock("type.id"); + + for (String s : PRIMITIVE_TYPES) { + if (!typeNames2typeIds.containsKey(s + "[]")) continue; + pc.add(typeNames2typeIds.get(s + "[]"), "return ((" + s + "[])obj)[i];"); + } + + pc.print(); + p(" return ((Object[])obj)[i];"); + p("}"); + } + + private void getArrayLengthT () { + p("public int getArrayLength(Type type, Object obj) {"); + SwitchedCodeBlock pc = new SwitchedCodeBlock("type.id"); + + for (String s : PRIMITIVE_TYPES) { + if (!typeNames2typeIds.containsKey(s + "[]")) continue; + pc.add(typeNames2typeIds.get(s + "[]"), "return ((" + s + "[])obj).length;"); + } + + pc.print(); + p(" return ((Object[])obj).length;"); + p("}"); + } + + private void newArrayC () { + p("public Object newArray (Class componentType, int size) {"); + p(" Type t = forName(componentType.getName().replace('$', '.'));"); + p(" if (t != null) {"); + SwitchedCodeBlock pc = new SwitchedCodeBlock("t.id"); + for (JType type : types) { + if (type.getQualifiedSourceName().equals("void")) continue; + if (type.getQualifiedSourceName().endsWith("Void")) continue; + String arrayType = type.getErasedType().getQualifiedSourceName() + "[size]"; + if (arrayType.contains("[]")) { + arrayType = type.getErasedType().getQualifiedSourceName(); + arrayType = arrayType.replaceFirst("\\[\\]", "[size]") + "[]"; + } + pc.add(typeNames2typeIds.get(type.getQualifiedSourceName()), "return new " + arrayType + ";"); + } + pc.print(); + p(" }"); + p(" throw new RuntimeException(\"Couldn't create array with element type \" + componentType.getName());"); + p("}"); + } + + private void forNameC () { + p("public Type forName(String name) {"); + p(" return types.get(name);"); + p("}"); + } + + private void getKnownTypesC () { + p("public Collection getKnownTypes() {"); + p(" return types.values();"); + p("}"); + } + + void p (String line) { + sw.println(line); + source.append(line); + source.append("\n"); + } + + void pn (String line) { + sw.print(line); + source.append(line); + } + + StringBuffer buffer = new StringBuffer(); + + void pb (String line) { + buffer.append(line); + buffer.append("\n"); + } + + private void pbn (String line) { + buffer.append(line); + } + + class SwitchedCodeBlock { + private List blocks = new ArrayList(); + private final String switchStatement; + + SwitchedCodeBlock (String switchStatement) { + this.switchStatement = switchStatement; + } + + void add (int key, String codeBlock) { + KeyedCodeBlock b = new KeyedCodeBlock(); + b.key = key; + b.codeBlock = codeBlock; + blocks.add(b); + } + + void print () { + if (blocks.isEmpty()) return; + + p(" switch(" + switchStatement + ") {"); + for (KeyedCodeBlock b : blocks) { + p(" case " + b.key + ": " + b.codeBlock); + } + p("}"); + } + + class KeyedCodeBlock { + int key; + String codeBlock; + } + } +} diff --git a/artemis/pom.xml b/artemis/pom.xml index 30a1e412c..4895c72e3 100644 --- a/artemis/pom.xml +++ b/artemis/pom.xml @@ -22,4 +22,22 @@ + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + generate-resources + + jar-no-fork + + + + + + + diff --git a/artemis/src/main/java/com/artemis.gwt.xml b/artemis/src/main/java/com/artemis.gwt.xml new file mode 100644 index 000000000..f8352415f --- /dev/null +++ b/artemis/src/main/java/com/artemis.gwt.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/artemis/src/main/java/com/artemis/BasicComponentMapper.java b/artemis/src/main/java/com/artemis/BasicComponentMapper.java index 5ffa21a11..fc2897cc7 100644 --- a/artemis/src/main/java/com/artemis/BasicComponentMapper.java +++ b/artemis/src/main/java/com/artemis/BasicComponentMapper.java @@ -42,15 +42,17 @@ class BasicComponentMapper extends ComponentMapper { } - @Override + @SuppressWarnings("unchecked") + @Override public A get(Entity e) throws ArrayIndexOutOfBoundsException { - return classType.cast(components.get(e.getId())); + return (A) components.get(e.getId()); } - @Override + @SuppressWarnings("unchecked") + @Override public A getSafe(Entity e) { if(components.isIndexWithinBounds(e.getId())) { - return classType.cast(components.get(e.getId())); + return (A) components.get(e.getId()); } return null; } diff --git a/artemis/src/main/java/com/artemis/ComponentManager.java b/artemis/src/main/java/com/artemis/ComponentManager.java index 2f4a155a7..cdb99de78 100644 --- a/artemis/src/main/java/com/artemis/ComponentManager.java +++ b/artemis/src/main/java/com/artemis/ComponentManager.java @@ -3,6 +3,8 @@ import java.util.BitSet; import com.artemis.utils.Bag; +import com.artemis.utils.reflect.ClassReflection; +import com.artemis.utils.reflect.ReflectionException; /** @@ -61,10 +63,8 @@ protected T create(Entity owner, Class componentClass) case POOLED: try { return (T)pooledComponents.obtain((Class)componentClass); - } catch (InstantiationException e) { + } catch (ReflectionException e) { throw new InvalidComponentException(componentClass, "Unable to instantiate component.", e); - } catch (IllegalAccessException e) { - throw new InvalidComponentException(componentClass, "Missing public constructor.", e); } default: throw new InvalidComponentException(componentClass, " unknown component type: " + type.getTaxonomy()); @@ -83,11 +83,9 @@ protected BitSet getPackedComponentOwners(ComponentType type) private static T newInstance(Class componentClass) { try { - return componentClass.newInstance(); - } catch (InstantiationException e) { + return ClassReflection.newInstance(componentClass); + } catch (ReflectionException e) { throw new InvalidComponentException(componentClass, "Unable to instantiate component.", e); - } catch (IllegalAccessException e) { - throw new InvalidComponentException(componentClass, "Missing public constructor.", e); } } diff --git a/artemis/src/main/java/com/artemis/ComponentPool.java b/artemis/src/main/java/com/artemis/ComponentPool.java index 70646b952..0ac3da940 100644 --- a/artemis/src/main/java/com/artemis/ComponentPool.java +++ b/artemis/src/main/java/com/artemis/ComponentPool.java @@ -4,6 +4,8 @@ import java.util.Map; import com.artemis.utils.Bag; +import com.artemis.utils.reflect.ClassReflection; +import com.artemis.utils.reflect.ReflectionException; class ComponentPool { @@ -15,10 +17,10 @@ class ComponentPool { @SuppressWarnings("unchecked") T obtain(Class componentClass) - throws InstantiationException, IllegalAccessException { + throws ReflectionException { Pool pool = getPool(componentClass); - return (T)((pool.size() > 0) ? pool.obtain() : componentClass.newInstance()); + return (T)((pool.size() > 0) ? pool.obtain() : ClassReflection.newInstance(componentClass)); } void free(PooledComponent c) { diff --git a/artemis/src/main/java/com/artemis/ComponentType.java b/artemis/src/main/java/com/artemis/ComponentType.java index 08c0085fb..8a039cb04 100644 --- a/artemis/src/main/java/com/artemis/ComponentType.java +++ b/artemis/src/main/java/com/artemis/ComponentType.java @@ -3,6 +3,7 @@ import java.util.IdentityHashMap; import com.artemis.utils.Bag; +import com.artemis.utils.reflect.ClassReflection; /** @@ -42,13 +43,13 @@ enum Taxonomy { * @param type * the components class */ - private ComponentType(Class type) { + public ComponentType(Class type) { types.set(INDEX, this); index = INDEX++; this.type = type; - if (PackedComponent.class.isAssignableFrom(type)) { + if (ClassReflection.isAssignableFrom(PackedComponent.class, type)) { taxonomy = Taxonomy.PACKED; - } else if (PooledComponent.class.isAssignableFrom(type)) { + } else if (ClassReflection.isAssignableFrom(PooledComponent.class, type)) { taxonomy = Taxonomy.POOLED; } else { taxonomy = Taxonomy.BASIC; @@ -66,7 +67,7 @@ public int getIndex() { @Override public String toString() { - return "ComponentType["+type.getSimpleName()+"] ("+index+")"; + return "ComponentType["+ ClassReflection.getSimpleName(type) +"] ("+index+")"; } protected Taxonomy getTaxonomy() { diff --git a/artemis/src/main/java/com/artemis/Entity.java b/artemis/src/main/java/com/artemis/Entity.java index 60737ad4f..6855dd7ba 100644 --- a/artemis/src/main/java/com/artemis/Entity.java +++ b/artemis/src/main/java/com/artemis/Entity.java @@ -265,8 +265,9 @@ public Component getComponent(ComponentType type) { * * @return component that matches, or null if none is found */ + @SuppressWarnings("unchecked") public T getComponent(Class type) { - return type.cast(getComponent(ComponentType.getTypeFor(type))); + return (T)getComponent(ComponentType.getTypeFor(type)); } /** diff --git a/artemis/src/main/java/com/artemis/InvalidComponentException.java b/artemis/src/main/java/com/artemis/InvalidComponentException.java index 321d73ee0..5fdb2a1b3 100644 --- a/artemis/src/main/java/com/artemis/InvalidComponentException.java +++ b/artemis/src/main/java/com/artemis/InvalidComponentException.java @@ -1,5 +1,7 @@ package com.artemis; +import com.artemis.utils.reflect.ClassReflection; + @SuppressWarnings("serial") public class InvalidComponentException extends RuntimeException { @@ -16,7 +18,7 @@ public InvalidComponentException(Class componentClass, String string, Excepti } private static String message(Class componentClass, String string) { - return componentClass.getSimpleName() + ": " + string; + return ClassReflection.getSimpleName(componentClass) + ": " + string; } public Class getComponentClass() { diff --git a/artemis/src/main/java/com/artemis/PackedComponentMapper.java b/artemis/src/main/java/com/artemis/PackedComponentMapper.java index e86d3df39..568000a2f 100644 --- a/artemis/src/main/java/com/artemis/PackedComponentMapper.java +++ b/artemis/src/main/java/com/artemis/PackedComponentMapper.java @@ -1,6 +1,8 @@ package com.artemis; import java.util.BitSet; +import com.artemis.utils.reflect.ClassReflection; +import com.artemis.utils.reflect.ReflectionException; /** * High performance packed component retrieval from entities. Each instance @@ -45,10 +47,11 @@ static PackedComponentMapper create(Class type return new PackedComponentMapper(type, world); } - @Override @SuppressWarnings("unchecked") + @Override + @SuppressWarnings("unchecked") public A get(Entity e) throws ArrayIndexOutOfBoundsException { component.forEntity(e); - return (A)component; + return (A) component; } @Override @@ -83,11 +86,9 @@ public A getSafe(Entity e, boolean forceNewInstance) { private A newInstance(Class type) { try { - return classType.newInstance(); - } catch (InstantiationException e) { + return (A) ClassReflection.newInstance(classType); + } catch (ReflectionException e) { throw new InvalidComponentException(type, "Unable to instantiate component.", e); - } catch (IllegalAccessException e) { - throw new InvalidComponentException(type, "Missing public constructor or too restrictive access.", e); } } } diff --git a/artemis/src/main/java/com/artemis/World.java b/artemis/src/main/java/com/artemis/World.java index 3b773dea2..a6d9186ef 100644 --- a/artemis/src/main/java/com/artemis/World.java +++ b/artemis/src/main/java/com/artemis/World.java @@ -1,7 +1,5 @@ package com.artemis; -import java.lang.reflect.Field; -import java.lang.reflect.ParameterizedType; import java.util.ArrayList; import java.util.IdentityHashMap; import java.util.List; @@ -11,6 +9,8 @@ import com.artemis.annotations.Mapper; import com.artemis.utils.Bag; import com.artemis.utils.ImmutableBag; +import com.artemis.utils.reflect.ClassReflection; +import com.artemis.utils.reflect.Field; /** @@ -197,8 +197,9 @@ public final T setManager(T manager) { * * @return the manager */ - public T getManager(Class managerType) { - return managerType.cast(managers.get(managerType)); + @SuppressWarnings("unchecked") + public T getManager(Class managerType) { + return (T)managers.get(managerType); } /** @@ -443,8 +444,9 @@ private void notifyManagers(Performer performer, Entity e) { * * @return instance of the system in this world */ - public T getSystem(Class type) { - return type.cast(systems.get(type)); + @SuppressWarnings("unchecked") + public T getSystem(Class type) { + return (T)systems.get(type); } /** @@ -618,14 +620,10 @@ private static final class ComponentMapperInitHelper { public static void config(Object target, World world) throws RuntimeException { try { Class clazz = target.getClass(); - for (Field field : clazz.getDeclaredFields()) { - Mapper annotation = field.getAnnotation(Mapper.class); - if (annotation != null && Mapper.class.isAssignableFrom(Mapper.class)) { - ParameterizedType genericType = (ParameterizedType) field.getGenericType(); - + for (Field field : ClassReflection.getDeclaredFields(clazz)) { + if (field.hasAnnotation(Mapper.class)) { @SuppressWarnings("unchecked") - Class componentType = (Class) genericType.getActualTypeArguments()[0]; - + Class componentType = (Class) field.getElementType(0); field.setAccessible(true); field.set(target, world.getMapper(componentType)); } diff --git a/artemis/src/main/java/com/artemis/utils/FastMath.java b/artemis/src/main/java/com/artemis/utils/FastMath.java index 36f921569..aa961df88 100644 --- a/artemis/src/main/java/com/artemis/utils/FastMath.java +++ b/artemis/src/main/java/com/artemis/utils/FastMath.java @@ -54,9 +54,12 @@ public final static double atan(final double x) { } public final static double inverseSqrt(double x) { - final double xhalves = 0.5d * x; - x = Double.longBitsToDouble(0x5FE6EB50C7B537AAl - (Double.doubleToRawLongBits(x) >> 1)); - return x * (1.5d - xhalves * x * x); // more iterations possible + double xhalf = 0.5d * x; + long i = Double.doubleToLongBits(x); + i = 0x5fe6ec85e7de30daL - (i >> 1); + x = Double.longBitsToDouble(i); + x = x * (1.5d - xhalf * x * x); + return x; } public final static double sqrt(final double x) { diff --git a/artemis/src/main/java/com/artemis/utils/Utils.java b/artemis/src/main/java/com/artemis/utils/Utils.java index ff07ef172..a3ce56618 100644 --- a/artemis/src/main/java/com/artemis/utils/Utils.java +++ b/artemis/src/main/java/com/artemis/utils/Utils.java @@ -137,33 +137,5 @@ public static boolean collides(float x1, float y1, float radius1, float x2, floa return d < 0; } - public static String readFileContents(String file) { - InputStream is = Utils.class.getClassLoader().getResourceAsStream(file); - String contents = ""; - try { - if (is != null) { - Writer writer = new StringWriter(); - - char[] buffer = new char[1024]; - Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); - int n; - while ((n = reader.read(buffer)) != -1) { - writer.write(buffer, 0, n); - } - - contents = writer.toString(); - } - } catch (Exception e) { - e.printStackTrace(); - } finally { - try { - is.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - return contents; - } } diff --git a/artemis/src/main/java/com/artemis/utils/reflect/ArrayReflection.java b/artemis/src/main/java/com/artemis/utils/reflect/ArrayReflection.java new file mode 100644 index 000000000..028244c63 --- /dev/null +++ b/artemis/src/main/java/com/artemis/utils/reflect/ArrayReflection.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.artemis.utils.reflect; + +/** Utilities for Array reflection. + * @author nexsoftware */ +public final class ArrayReflection { + + /** Creates a new array with the specified component type and length. */ + static public Object newInstance (Class c, int size) { + return java.lang.reflect.Array.newInstance(c, size); + } + + /** Returns the length of the supplied array. */ + static public int getLength (Object array) { + return java.lang.reflect.Array.getLength(array); + } + + /** Returns the value of the indexed component in the supplied array. */ + static public Object get (Object array, int index) { + return java.lang.reflect.Array.get(array, index); + } + + /** Sets the value of the indexed component in the supplied array to the supplied value. */ + static public void set (Object array, int index, Object value) { + java.lang.reflect.Array.set(array, index, value); + } + +} \ No newline at end of file diff --git a/artemis/src/main/java/com/artemis/utils/reflect/ClassReflection.java b/artemis/src/main/java/com/artemis/utils/reflect/ClassReflection.java new file mode 100644 index 000000000..7ebe10216 --- /dev/null +++ b/artemis/src/main/java/com/artemis/utils/reflect/ClassReflection.java @@ -0,0 +1,187 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.artemis.utils.reflect; + +import java.lang.reflect.Modifier; + +/** Utilities for Class reflection. + * @author nexsoftware */ +public final class ClassReflection { + + /** Returns the Class object associated with the class or interface with the supplied string name. */ + static public Class forName (String name) throws ReflectionException { + try { + return Class.forName(name); + } catch (ClassNotFoundException e) { + throw new ReflectionException("Class not found: " + name, e); + } + } + + /** Returns the simple name of the underlying class as supplied in the source code. */ + static public String getSimpleName (Class c) { + return c.getSimpleName(); + } + + /** Determines if the supplied Object is assignment-compatible with the object represented by supplied Class. */ + static public boolean isInstance (Class c, Object obj) { + return c.isInstance(obj); + } + + /** Determines if the class or interface represented by first Class parameter is either the same as, or is a superclass or + * superinterface of, the class or interface represented by the second Class parameter. */ + static public boolean isAssignableFrom (Class c1, Class c2) { + return c1.isAssignableFrom(c2); + } + + /** Returns true if the class or interface represented by the supplied Class is a member class. */ + static public boolean isMemberClass (Class c) { + return c.isMemberClass(); + } + + /** Returns true if the class or interface represented by the supplied Class is a static class. */ + static public boolean isStaticClass (Class c) { + return Modifier.isStatic(c.getModifiers()); + } + + /** Creates a new instance of the class represented by the supplied Class. */ + static public T newInstance (Class c) throws ReflectionException { + try { + return c.newInstance(); + } catch (InstantiationException e) { + throw new ReflectionException("Could not instantiate instance of class: " + c.getName(), e); + } catch (IllegalAccessException e) { + throw new ReflectionException("Could not instantiate instance of class: " + c.getName(), e); + } + } + + /** Returns an array of {@link Constructor} containing the public constructors of the class represented by the supplied Class. */ + static public Constructor[] getConstructors (Class c) { + java.lang.reflect.Constructor[] constructors = c.getConstructors(); + Constructor[] result = new Constructor[constructors.length]; + for (int i = 0, j = constructors.length; i < j; i++) { + result[i] = new Constructor(constructors[i]); + } + return result; + } + + /** Returns a {@link Constructor} that represents the public constructor for the supplied class which takes the supplied parameter types. */ + static public Constructor getConstructor (Class c, Class... parameterTypes) throws ReflectionException { + try { + return new Constructor(c.getConstructor(parameterTypes)); + } catch (SecurityException e) { + throw new ReflectionException("Security violation occurred while getting constructor for class: '" + c.getName() + "'.", e); + } catch (NoSuchMethodException e) { + throw new ReflectionException("Constructor not found for class: " + c.getName(), e); + } + } + + /** Returns a {@link Constructor} that represents the constructor for the supplied class which takes the supplied parameter types. */ + static public Constructor getDeclaredConstructor (Class c, Class... parameterTypes) throws ReflectionException { + try { + return new Constructor(c.getDeclaredConstructor(parameterTypes)); + } catch (SecurityException e) { + throw new ReflectionException("Security violation while getting constructor for class: " + c.getName(), e); + } catch (NoSuchMethodException e) { + throw new ReflectionException("Constructor not found for class: " + c.getName(), e); + } + } + + /** Returns an array of {@link Method} containing the public member methods of the class represented by the supplied Class. */ + static public Method[] getMethods (Class c) { + java.lang.reflect.Method[] methods = c.getMethods(); + Method[] result = new Method[methods.length]; + for (int i = 0, j = methods.length; i < j; i++) { + result[i] = new Method(methods[i]); + } + return result; + } + + /** Returns a {@link Method} that represents the public member method for the supplied class which takes the supplied parameter types. */ + static public Method getMethod (Class c, String name, Class... parameterTypes) throws ReflectionException { + try { + return new Method(c.getMethod(name, parameterTypes)); + } catch (SecurityException e) { + throw new ReflectionException("Security violation while getting method: " + name + ", for class: " + c.getName(), e); + } catch (NoSuchMethodException e) { + throw new ReflectionException("Method not found: " + name + ", for class: " + c.getName(), e); + } + } + + /** Returns an array of {@link Method} containing the methods declared by the class represented by the supplied Class. */ + static public Method[] getDeclaredMethods (Class c) { + java.lang.reflect.Method[] methods = c.getDeclaredMethods(); + Method[] result = new Method[methods.length]; + for (int i = 0, j = methods.length; i < j; i++) { + result[i] = new Method(methods[i]); + } + return result; + } + + /** Returns a {@link Method} that represents the method declared by the supplied class which takes the supplied parameter types. */ + static public Method getDeclaredMethod (Class c, String name, Class... parameterTypes) throws ReflectionException { + try { + return new Method(c.getDeclaredMethod(name, parameterTypes)); + } catch (SecurityException e) { + throw new ReflectionException("Security violation while getting method: " + name + ", for class: " + c.getName(), e); + } catch (NoSuchMethodException e) { + throw new ReflectionException("Method not found: " + name + ", for class: " + c.getName(), e); + } + } + + /** Returns an array of {@link Field} containing the public fields of the class represented by the supplied Class. */ + static public Field[] getFields (Class c) { + java.lang.reflect.Field[] fields = c.getFields(); + Field[] result = new Field[fields.length]; + for (int i = 0, j = fields.length; i < j; i++) { + result[i] = new Field(fields[i]); + } + return result; + } + + /** Returns a {@link Field} that represents the specified public member field for the supplied class. */ + static public Field getField (Class c, String name) throws ReflectionException { + try { + return new Field(c.getField(name)); + } catch (SecurityException e) { + throw new ReflectionException("Security violation while getting field: " + name + ", for class: " + c.getName(), e); + } catch (NoSuchFieldException e) { + throw new ReflectionException("Field not found: " + name + ", for class: " + c.getName(), e); + } + } + + /** Returns an array of {@link Field} objects reflecting all the fields declared by the supplied class. */ + static public Field[] getDeclaredFields (Class c) { + java.lang.reflect.Field[] fields = c.getDeclaredFields(); + Field[] result = new Field[fields.length]; + for (int i = 0, j = fields.length; i < j; i++) { + result[i] = new Field(fields[i]); + } + return result; + } + + /** Returns a {@link Field} that represents the specified declared field for the supplied class. */ + static public Field getDeclaredField (Class c, String name) throws ReflectionException { + try { + return new Field(c.getDeclaredField(name)); + } catch (SecurityException e) { + throw new ReflectionException("Security violation while getting field: " + name + ", for class: " + c.getName(), e); + } catch (NoSuchFieldException e) { + throw new ReflectionException("Field not found: " + name + ", for class: " + c.getName(), e); + } + } + +} \ No newline at end of file diff --git a/artemis/src/main/java/com/artemis/utils/reflect/Constructor.java b/artemis/src/main/java/com/artemis/utils/reflect/Constructor.java new file mode 100644 index 000000000..a8ded6f49 --- /dev/null +++ b/artemis/src/main/java/com/artemis/utils/reflect/Constructor.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.artemis.utils.reflect; + +import java.lang.reflect.InvocationTargetException; + +/** Provides information about, and access to, a single constructor for a Class. + * @author nexsoftware */ +public final class Constructor { + + private final java.lang.reflect.Constructor constructor; + + Constructor (java.lang.reflect.Constructor constructor) { + this.constructor = constructor; + } + + /** Returns an array of Class objects that represent the formal parameter types, in declaration order, of the constructor. */ + public Class[] getParameterTypes () { + return constructor.getParameterTypes(); + } + + /** Returns the Class object representing the class or interface that declares the constructor. */ + public Class getDeclaringClass () { + return constructor.getDeclaringClass(); + } + + public boolean isAccessible () { + return constructor.isAccessible(); + } + + public void setAccessible (boolean accessible) { + constructor.setAccessible(accessible); + } + + /** Uses the constructor to create and initialize a new instance of the constructor's declaring class, with the supplied initialization parameters. */ + public Object newInstance (Object... args) throws ReflectionException { + try { + return constructor.newInstance(args); + } catch (IllegalArgumentException e) { + throw new ReflectionException("Illegal argument(s) supplied to constructor for class: " + getDeclaringClass().getName(), e); + } catch (InstantiationException e) { + throw new ReflectionException("Could not instantiate instance of class: " + getDeclaringClass().getName(), e); + } catch (IllegalAccessException e) { + throw new ReflectionException("Could not instantiate instance of class: " + getDeclaringClass().getName(), e); + } catch (InvocationTargetException e) { + throw new ReflectionException("Exception occurred in constructor for class: " + getDeclaringClass().getName(), e); + } + } + +} \ No newline at end of file diff --git a/artemis/src/main/java/com/artemis/utils/reflect/Field.java b/artemis/src/main/java/com/artemis/utils/reflect/Field.java new file mode 100644 index 000000000..d0fdddee3 --- /dev/null +++ b/artemis/src/main/java/com/artemis/utils/reflect/Field.java @@ -0,0 +1,151 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.artemis.utils.reflect; + +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +/** Provides information about, and access to, a single field of a class or interface. + * @author nexsoftware */ +public final class Field { + + private final java.lang.reflect.Field field; + + Field (java.lang.reflect.Field field) { + this.field = field; + } + + /** Returns the name of the field. */ + public String getName () { + return field.getName(); + } + + /** Returns a Class object that identifies the declared type for the field. */ + public Class getType () { + return field.getType(); + } + + /** Returns the Class object representing the class or interface that declares the field. */ + public Class getDeclaringClass () { + return field.getDeclaringClass(); + } + + public boolean isAccessible () { + return field.isAccessible(); + } + + public void setAccessible (boolean accessible) { + field.setAccessible(accessible); + } + + /** Return true if the field does not include any of the {@code private}, {@code protected}, or {@code public} modifiers. */ + public boolean isDefaultAccess () { + return !isPrivate() && !isProtected() && !isPublic(); + } + + /** Return true if the field includes the {@code final} modifier. */ + public boolean isFinal () { + return Modifier.isFinal(field.getModifiers()); + } + + /** Return true if the field includes the {@code private} modifier. */ + public boolean isPrivate () { + return Modifier.isPrivate(field.getModifiers()); + } + + /** Return true if the field includes the {@code protected} modifier. */ + public boolean isProtected () { + return Modifier.isProtected(field.getModifiers()); + } + + /** Return true if the field includes the {@code public} modifier. */ + public boolean isPublic () { + return Modifier.isPublic(field.getModifiers()); + } + + /** Return true if the field includes the {@code static} modifier. */ + public boolean isStatic () { + return Modifier.isStatic(field.getModifiers()); + } + + /** Return true if the field includes the {@code transient} modifier. */ + public boolean isTransient () { + return Modifier.isTransient(field.getModifiers()); + } + + /** Return true if the field includes the {@code volatile} modifier. */ + public boolean isVolatile () { + return Modifier.isVolatile(field.getModifiers()); + } + + /** Return true if the field is a synthetic field. */ + public boolean isSynthetic () { + return field.isSynthetic(); + } + + /** If the type of the field is parameterized, returns the Class object representing the parameter type at the specified index, + * null otherwise. */ + public Class getElementType (int index) { + Type genericType = field.getGenericType(); + if (genericType instanceof ParameterizedType) { + Type[] actualTypes = ((ParameterizedType)genericType).getActualTypeArguments(); + if (actualTypes.length - 1 >= index) { + Type actualType = actualTypes[index]; + if (actualType instanceof Class) + return (Class)actualType; + else if (actualType instanceof ParameterizedType) + return (Class)((ParameterizedType)actualType).getRawType(); + else if (actualType instanceof GenericArrayType) { + Type componentType = ((GenericArrayType)actualType).getGenericComponentType(); + if (componentType instanceof Class) return ArrayReflection.newInstance((Class)componentType, 0).getClass(); + } + } + } + return null; + } + + /** Returns the value of the field on the supplied object. */ + public Object get (Object obj) throws ReflectionException { + try { + return field.get(obj); + } catch (IllegalArgumentException e) { + throw new ReflectionException("Object is not an instance of " + getDeclaringClass(), e); + } catch (IllegalAccessException e) { + throw new ReflectionException("Illegal access to field: " + getName(), e); + } + } + + /** Sets the value of the field on the supplied object. */ + public void set (Object obj, Object value) throws ReflectionException { + try { + field.set(obj, value); + } catch (IllegalArgumentException e) { + throw new ReflectionException("Argument not valid for field: " + getName(), e); + } catch (IllegalAccessException e) { + throw new ReflectionException("Illegal access to field: " + getName(), e); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public boolean hasAnnotation(Class annotationClass) { + return field.getAnnotation(annotationClass) != null ? true: false; + } + + +} \ No newline at end of file diff --git a/artemis/src/main/java/com/artemis/utils/reflect/Method.java b/artemis/src/main/java/com/artemis/utils/reflect/Method.java new file mode 100644 index 000000000..080ae2751 --- /dev/null +++ b/artemis/src/main/java/com/artemis/utils/reflect/Method.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.artemis.utils.reflect; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; + +/** Provides information about, and access to, a single method on a class or interface. + * @author nexsoftware */ +public final class Method { + + private final java.lang.reflect.Method method; + + Method (java.lang.reflect.Method method) { + this.method = method; + } + + /** Returns the name of the method. */ + public String getName () { + return method.getName(); + } + + /** Returns a Class object that represents the formal return type of the method. */ + public Class getReturnType () { + return method.getReturnType(); + } + + /** Returns an array of Class objects that represent the formal parameter types, in declaration order, of the method. */ + public Class[] getParameterTypes () { + return method.getParameterTypes(); + } + + /** Returns the Class object representing the class or interface that declares the method. */ + public Class getDeclaringClass () { + return method.getDeclaringClass(); + } + + public boolean isAccessible () { + return method.isAccessible(); + } + + public void setAccessible (boolean accessible) { + method.setAccessible(accessible); + } + + /** Return true if the method includes the {@code abstract} modifier. */ + public boolean isAbstract () { + return Modifier.isAbstract(method.getModifiers()); + } + + /** Return true if the method does not include any of the {@code private}, {@code protected}, or {@code public} modifiers. */ + public boolean isDefaultAccess () { + return !isPrivate() && !isProtected() && !isPublic(); + } + + /** Return true if the method includes the {@code final} modifier. */ + public boolean isFinal () { + return Modifier.isFinal(method.getModifiers()); + } + + /** Return true if the method includes the {@code private} modifier. */ + public boolean isPrivate () { + return Modifier.isPrivate(method.getModifiers()); + } + + /** Return true if the method includes the {@code protected} modifier. */ + public boolean isProtected () { + return Modifier.isProtected(method.getModifiers()); + } + + /** Return true if the method includes the {@code public} modifier. */ + public boolean isPublic () { + return Modifier.isPublic(method.getModifiers()); + } + + /** Return true if the method includes the {@code native} modifier. */ + public boolean isNative () { + return Modifier.isNative(method.getModifiers()); + } + + /** Return true if the method includes the {@code static} modifier. */ + public boolean isStatic () { + return Modifier.isStatic(method.getModifiers()); + } + + /** Return true if the method takes a variable number of arguments. */ + public boolean isVarArgs () { + return method.isVarArgs(); + } + + /** Invokes the underlying method on the supplied object with the supplied parameters. */ + public Object invoke (Object obj, Object... args) throws ReflectionException { + try { + return method.invoke(obj, args); + } catch (IllegalArgumentException e) { + throw new ReflectionException("Illegal argument(s) supplied to method: " + getName(), e); + } catch (IllegalAccessException e) { + throw new ReflectionException("Illegal access to method: " + getName(), e); + } catch (InvocationTargetException e) { + throw new ReflectionException("Exception occurred in method: " + getName(), e); + } + } + +} \ No newline at end of file diff --git a/artemis/src/main/java/com/artemis/utils/reflect/ReflectionException.java b/artemis/src/main/java/com/artemis/utils/reflect/ReflectionException.java new file mode 100644 index 000000000..a92502e6f --- /dev/null +++ b/artemis/src/main/java/com/artemis/utils/reflect/ReflectionException.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.artemis.utils.reflect; + +/** Thrown when an exception occurs during reflection. + * @author nexsoftware + */ +public class ReflectionException extends Exception { + + private static final long serialVersionUID = -7146287043138864498L; + + public ReflectionException () { + super(); + } + + public ReflectionException (String message) { + super(message); + } + + public ReflectionException (Throwable cause) { + super(cause); + } + + public ReflectionException (String message, Throwable cause) { + super(message, cause); + } + +} \ No newline at end of file diff --git a/artemis/src/test/java/com/artemis/ComponentPoolTest.java b/artemis/src/test/java/com/artemis/ComponentPoolTest.java index ae81f3793..e8f31df09 100644 --- a/artemis/src/test/java/com/artemis/ComponentPoolTest.java +++ b/artemis/src/test/java/com/artemis/ComponentPoolTest.java @@ -19,7 +19,7 @@ public void reuse_pooled_components() throws Exception assertTrue(c1 == c1b); } - static final class SimplePooled extends PooledComponent { + public static final class SimplePooled extends PooledComponent { @Override public void reset() {} diff --git a/pom.xml b/pom.xml index 39366b109..3bb5df689 100644 --- a/pom.xml +++ b/pom.xml @@ -75,6 +75,7 @@ artemis-weaver artemis-maven artemis-test + artemis-gwt