Commits

Michael Ludwig committed 74c2bc4

Resolve null values in ObjectProperty.
Improve attribute documentation from Component docs.
Improve performance in AbstractComponent.

Comments (0)

Files changed (12)

entreri-apt-tests/src/main/java/com.lhkbob.entreri.components/CustomProperty.java

 
 @Factory(CustomProperty.CustomFactoryWithAttributes.class)
 public class CustomProperty implements ShareableProperty<CustomProperty.Bletch> {
-    private final ObjectProperty<Bletch> property;
+    private final ObjectProperty property;
 
     public CustomProperty() {
-        property = new ObjectProperty<>();
+        property = new ObjectProperty();
     }
 
     @Override
     }
 
     public void set(int index, Bletch b) {
-        property.set(index, b);
+        get(index).value = b.value;
     }
 
     public Bletch get(int index) {
-        return property.get(index);
+        return (Bletch) property.get(index);
     }
 
     @Override
     public void get(int index, Bletch b) {
-        b.value = property.get(index).value;
+        b.value = get(index).value;
     }
 
     @Override
             b.value = (!attributes.hasAttribute(IntProperty.DefaultInt.class) ? 0
                                                                               : attributes
                                .getAttribute(IntProperty.DefaultInt.class).value());
-            property.set(index, b);
+            property.property.set(index, b);
         }
 
         @Override

entreri-apt-tests/src/main/java/com.lhkbob.entreri.components/NoFactoryProperty.java

 import com.lhkbob.entreri.property.Property;
 
 public class NoFactoryProperty implements Property {
-    private final ObjectProperty<Crass> property;
+    private final ObjectProperty property;
 
     public NoFactoryProperty() {
-        property = new ObjectProperty<Crass>();
+        property = new ObjectProperty();
     }
 
     public void set(int index, Crass b) {
     }
 
     public Crass get(int index) {
-        return property.get(index);
+        return (Crass) property.get(index);
     }
 
     @Override

entreri-core/src/main/java/com/lhkbob/entreri/Component.java

  * Logically a component definition is a set of named and typed properties, and a
  * method-based API to get and set the values of each property. Specific types of
  * component are defined by creating a sub-interface of Component. Using the {@link Named}
- * {@link SharedInstance}, and {@link com.lhkbob.entreri.property.Factory} annotations and
- * specific conventions the data properties of the component type are specified in the
- * sub-interface. A declaration model similar to the Java Bean model is used and is
- * outlined below:
+ * {@link SharedInstance}, {@link com.lhkbob.entreri.property.Factory Factory} and custom
+ * {@link com.lhkbob.entreri.property.Attribute Attribute} annotations defined by {@link
+ * com.lhkbob.entreri.property.Property Property} implementations, the data properties of
+ * the component type are specified in the sub-interface. A declaration model similar to
+ * the Java Bean model is used and is outlined below:
  * <p/>
  * <ol> <li>Non-void, zero-argument methods starting with 'get', 'is', and 'has' declare a
  * property. The property type is inspected from the return type of the method. The
  * defined in terms of primitives or types that have specialized Property implementations
  * that can pack and unpack an instance. The {@link SharedInstance} annotation can be
  * added to the getter method of a property to specify that the {@link
- * com.lhkbob.entreri.property.ShareableProperty} API should be leveraged by the generated
- * class.
+ * com.lhkbob.entreri.property.ShareableProperty ShareableProperty} API should be
+ * leveraged by the generated class.
  * <p/>
  * Additional attribute annotations can be added to the getter method to influence the
- * behavior of the {@link com.lhkbob.entreri.property.PropertyFactory} used for each
- * property in the component definition. Besides using the Factory annotation to specify
- * the factory type, a property implementation can be associated with a type with
- * canonical name <var>C</var> by adding the file META-INF/entreri/mapping/C to the
- * classpath, where its contents must be:
+ * behavior of the {@link com.lhkbob.entreri.property.PropertyFactory PropertyFactory}
+ * used for each property in the component definition. Besides using the Factory
+ * annotation to specify the factory type, a property implementation can be associated
+ * with a type with canonical name <var>C</var> by adding the file
+ * META-INF/entreri/mapping/C to the classpath, where its contents must be:
  * <pre>
  *     &lt;BINARY NAME OF PROPERTY&gt;
  * </pre>
  * where the value is suitable for passing into {@link Class#forName(String)}.
  * <p/>
+ * Attribute annotations provided by the default property implementations are outlined
+ * below: <ul> <li>{@link com.lhkbob.entreri.property.BooleanProperty.DefaultBoolean
+ *
+ * @author Michael Ludwig
+ * @DefaultBoolean} - set value for boolean properties</li> <li>{@link
+ * com.lhkbob.entreri.property.ByteProperty.DefaultByte @DefaultByte} - set value for byte
+ * properties</li> <li>{@link com.lhkbob.entreri.property.ShortProperty.DefaultShort
+ * @DefaultShort} - set value for short properties</li> <li>{@link
+ * com.lhkbob.entreri.property.IntProperty.DefaultInt @DefaultInt} - set value for int
+ * properties</li> <li>{@link com.lhkbob.entreri.property.LongProperty.DefaultLong
+ * @DefaultLong} - set value for long properties</li> <li>{@link
+ * com.lhkbob.entreri.property.FloatProperty.DefaultFloat @DefaultFloat} - set value for
+ * float properties</li> <li>{@link com.lhkbob.entreri.property.DoubleProperty.DefaultDouble
+ * @DefaultDouble} - set value for double properties</li> <li>{@link
+ * com.lhkbob.entreri.property.CharProperty.DefaultChar @DefaultChar} - set value for char
+ * properties</li> <li>{@link com.lhkbob.entreri.property.Clone @Clone} - specify clone
+ * policy used with entity or component templates</li> </ul> Note that there are no
+ * annotations that work with general object types. This is because the scope of that
+ * problem is intractable. The default ObjectProperty implementation assumes null values
+ * are allowed and that the default value is null.
+ * <p/>
  * The generated proxies will implement equals() and hashCode() based on their type and
  * the id of their owning entity. The {@link ComponentIterator} class creates flyweight
  * component instances whose identity changes as iteration proceeds; equals() and
  * component configurations because the JVM does not need to load each unique component
  * instance into the cache.
  * <p/>
- * Component implements both {@link com.lhkbob.entreri.Ownable} and {@link
- * com.lhkbob.entreri.Owner}. This can be used to create hierarchies of both components
- * and entities that share a lifetime. When a component is removed from an entity, all of
- * its owned objects are disowned. If any of them were entities or components, they are
- * also removed from the system.
- *
- * @author Michael Ludwig
+ * Component implements both {@link Ownable} and {@link Owner}. This can be used to create
+ * hierarchies of both components and entities that share a lifetime. When a component is
+ * removed from an entity, all of its owned objects are disowned. If any of them were
+ * entities or components, they are also removed from the system.
  */
 public interface Component extends Owner, Ownable {
     /**

entreri-core/src/main/java/com/lhkbob/entreri/impl/AbstractComponent.java

  * @param <T> The type of component the AbstractComponent is safely cast-able to
  */
 public abstract class AbstractComponent<T extends Component> implements Component {
+    /**
+     * The ComponentRepository managing this component and all of its property data
+     */
     protected final ComponentRepository<T> owner;
 
-    private int index;
+    /**
+     * The current index of the component. Subclasses must not modify directly, but should
+     * call setIndex(). This is provided to avoid the virtual getIndex() call.
+     */
+    protected int index;
     private int id;
 
     /**
         this.id = owner.getId(index);
     }
 
-    /**
-     * @return The repository that allocated this component
-     */
-    ComponentRepository<T> getRepository() {
-        return owner;
-    }
-
     @Override
     public boolean isAlive() {
         // we have to check the index of the Component because the ComponentRepository
     private String inspectProperty(Property p) {
         try {
             Method get = p.getClass().getMethod("get", int.class);
-            Object v = get.invoke(p, getIndex());
+            Object v = get.invoke(p, index);
 
             if (v != null) {
                 // strip out newlines

entreri-core/src/main/java/com/lhkbob/entreri/impl/ComponentFactoryProvider.java

         // time a property is accessed, and add any shared instance field declarations
         int property = 0;
         for (PropertyDeclaration s : spec.getProperties()) {
-            String fld = (use15 && s.getPropertyImplementation().equals(OBJECT_PROP_NAME)
-                          ? OBJECT_PROP_NAME + "<" + s.getType() + ">"
-                          : s.getPropertyImplementation());
-
-            sb.append("\tprivate final ").append(fld).append(' ')
-              .append(PROPERTY_FIELD_PREFIX).append(property).append(";\n");
+            sb.append("\tprivate final ").append(s.getPropertyImplementation())
+              .append(' ').append(PROPERTY_FIELD_PREFIX).append(property).append(";\n");
             if (s.isShared()) {
                 sb.append("\tprivate final ").append(s.getType()).append(' ')
                   .append(SHARED_FIELD_PREFIX).append(property).append(";\n");
           .append(REPO_FIELD_NAME).append(");\n");
         property = 0;
         for (PropertyDeclaration s : spec.getProperties()) {
-            String cast = (use15 && s.getPropertyImplementation().equals(OBJECT_PROP_NAME)
-                           ? OBJECT_PROP_NAME + "<" + s.getType() + ">"
-                           : s.getPropertyImplementation());
-
             sb.append("\t\t").append(PROPERTY_FIELD_PREFIX).append(property)
-              .append(" = (").append(cast).append(") ").append(REPO_FIELD_NAME)
-              .append('.').append(GET_PROPERTY_METHOD).append('(').append(property)
-              .append(");\n");
+              .append(" = (").append(s.getPropertyImplementation()).append(") ")
+              .append(REPO_FIELD_NAME).append('.').append(GET_PROPERTY_METHOD).append('(')
+              .append(property).append(");\n");
             if (s.isShared()) {
                 sb.append("\t\t").append(SHARED_FIELD_PREFIX).append(property)
                   .append(" = ").append(PROPERTY_FIELD_PREFIX).append(property)
         Map<String, List<PropertyDeclaration>> setters = new HashMap<>();
         property = 0;
         for (PropertyDeclaration s : spec.getProperties()) {
+            if (use15) {
+                sb.append("\n\t@Override");
+            }
             appendGetter(s, property, sb, use15);
             List<PropertyDeclaration> setterParams = setters.get(s.getSetterMethod());
             if (setterParams == null) {
 
         // implement all setters
         for (List<PropertyDeclaration> setter : setters.values()) {
+            if (use15) {
+                sb.append("\n\t@Override");
+            }
             appendSetter(setter, spec, sb);
         }
 
     private static final String OBJECT_PROP_NAME = ObjectProperty.class.getName();
 
     private static final String REPO_FIELD_NAME = "owner";
-    private static final String INDEX_FIELD_NAME = "getIndex()";
+    private static final String INDEX_FIELD_NAME = "index";
     private static final String GET_PROPERTY_METHOD = "getProperty";
     private static final String CREATE_SHARE_METHOD = "createShareableInstance";
     private static final String UPDATE_VERSION_METHOD = "incrementVersion";
               .append(index).append(");\n\t\treturn ").append(SHARED_FIELD_PREFIX)
               .append(index).append(";");
         } else {
-            if (forProperty.getPropertyImplementation().equals(OBJECT_PROP_NAME) &&
-                !useGenerics) {
+            if (forProperty.getPropertyImplementation().equals(OBJECT_PROP_NAME)) {
                 // special case where we allow ObjectProperty to have more permissive getters
                 // and setters to support any type under the sun, but that means we have
                 // to cast the object we get back

entreri-core/src/main/java/com/lhkbob/entreri/impl/ComponentIteratorImpl.java

             // putting one data into the required array
             required = Arrays.copyOf(required, required.length + 1);
 
-            if (data.getRepository().getMaxComponentIndex() <
-                primary.getRepository().getMaxComponentIndex()) {
+            if (data.owner.getMaxComponentIndex() <
+                primary.owner.getMaxComponentIndex()) {
                 // new primary
                 required[required.length - 1] = primary;
                 primary = data;
         boolean found;
         int entity;
         int component;
-        int count = primary.getRepository().getMaxComponentIndex();
+        int count = primary.owner.getMaxComponentIndex();
         while (index < count - 1) {
             index++; // always increment one
 
             found = true;
-            entity = primary.getRepository().getEntityIndex(index);
+            entity = primary.owner.getEntityIndex(index);
             if (entity != 0) {
                 // we have a possible entity candidate
                 primary.setIndex(index);
                 for (int i = 0; i < required.length; i++) {
-                    component = required[i].getRepository().getComponentIndex(entity);
+                    component = required[i].owner.getComponentIndex(entity);
                     if (component == 0) {
                         found = false;
                         break;
                     // we have satisfied all required components,
                     // so now set all optional requirements as well
                     for (int i = 0; i < optional.length; i++) {
-                        component = optional[i].getRepository().getComponentIndex(entity);
+                        component = optional[i].owner.getComponentIndex(entity);
                         optional[i].setIndex(component);
                     }
 

entreri-core/src/main/java/com/lhkbob/entreri/impl/ComponentRepository.java

     private final IntProperty componentIdProperty;
     private final IntProperty componentVersionProperty;
 
-    private final ObjectProperty<OwnerSupport> ownerDelegatesProperty;
+    private final ObjectProperty ownerDelegatesProperty;
 
     private int idSeq;
     private int versionSeq;
      * @return The OwnerSupport delegate for the component by the given index
      */
     public OwnerSupport getOwnerDelegate(int componentIndex) {
-        return ownerDelegatesProperty.get(componentIndex);
+        return (OwnerSupport) ownerDelegatesProperty.get(componentIndex);
     }
 
     /**

entreri-core/src/main/java/com/lhkbob/entreri/property/Clone.java

  */
 package com.lhkbob.entreri.property;
 
-import com.lhkbob.entreri.*;
+import com.lhkbob.entreri.Entity;
+import com.lhkbob.entreri.EntitySystem;
 
-import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
 
 /**
  * <p/>
  */
 @Attribute
 @Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.FIELD)
 public @interface Clone {
     /**
      * Policy is an enum describing a number of different behaviors performed by a {@link
-     * PropertyFactory} when its {@link PropertyFactory#clone(Property,
-     * int, Property, int)} method is invoked in response to {@link
-     * Entity#add(com.lhkbob.entreri.Component)} or {@link EntitySystem#addEntity(Entity)}.
+     * PropertyFactory} when its {@link PropertyFactory#clone(Property, int, Property,
+     * int)} method is invoked in response to {@link Entity#add(com.lhkbob.entreri.Component)}
+     * or {@link EntitySystem#addEntity(Entity)}.
      */
     public static enum Policy {
         /**

entreri-core/src/main/java/com/lhkbob/entreri/property/ObjectProperty.java

  * number of packed Object references for each property. Because it is not primitive data,
  * cache locality will suffer compared to the primitive property types, but it will allow
  * you to store arbitrary objects.
+ * <p/>
+ * However, ObjectProperty assumes that all component values can be null, and the default
+ * value is null. If this is not an acceptable contract then a custom property must be
+ * defined with a factory capable of constructing proper default instances.
  *
  * @author Michael Ludwig
  */
 @Factory(ObjectProperty.Factory.class)
-public final class ObjectProperty<T> implements Property {
+public final class ObjectProperty implements Property {
     private Object[] data;
 
     /**
     }
 
     /**
-     * Return a PropertyFactory that creates ObjectProperties with the given element size
-     * and default value.
-     *
-     * @param <T>
-     * @param dflt The default value assigned to each component and element
+     * Return a PropertyFactory that creates ObjectProperties using the given cloning
+     * policy.
      *
      * @return A PropertyFactory for ObjectProperty
      */
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    public static <T> PropertyFactory<ObjectProperty<T>> factory(T dflt) {
-        PropertyFactory superRaw = new Factory(dflt);
-        return superRaw;
+    public static PropertyFactory<ObjectProperty> factory(Clone.Policy policy) {
+        return new Factory(policy);
     }
 
     /**
      * @throws ArrayIndexOutOfBoundsException if the componentIndex is invalid
      */
     @SuppressWarnings("unchecked")
-    public T get(int componentIndex) {
-        return (T) data[componentIndex];
+    public Object get(int componentIndex) {
+        return data[componentIndex];
     }
 
     /**
      *
      * @throws ArrayIndexOutOfBoundsException if the componentIndex is invalid
      */
-    public void set(int componentIndex, T val) {
+    public void set(int componentIndex, Object val) {
         data[componentIndex] = val;
     }
 
      */
     @SuppressWarnings({ "rawtypes", "unchecked" })
     public static class Factory implements PropertyFactory<ObjectProperty> {
-        private final Object defaultValue;
         private final Clone.Policy policy;
 
         public Factory(Attributes attrs) {
-            defaultValue = null;
             policy = attrs.hasAttribute(Clone.class) ? attrs.getAttribute(Clone.class)
                                                             .value()
                                                      : Clone.Policy.JAVA_DEFAULT;
         }
 
-        public Factory(Object defaultValue) {
-            this.defaultValue = defaultValue;
-            policy = Clone.Policy.JAVA_DEFAULT;
+        public Factory(Clone.Policy policy) {
+            this.policy = policy;
         }
 
         @Override
 
         @Override
         public void setDefaultValue(ObjectProperty property, int index) {
-            property.set(index, defaultValue);
+            property.set(index, null);
         }
 
         @Override

entreri-core/src/test/java/com/lhkbob/entreri/PropertyFactoryTest.java

     /*
      * ObjectProperty fields for Attributes creation
      */
-    private ObjectProperty<Object> objectPropertyNoPolicy;
+    private ObjectProperty objectPropertyNoPolicy;
 
     @Clone(Policy.DISABLE)
-    private ObjectProperty<Object> objectPropertyDisabled;
+    private ObjectProperty objectPropertyDisabled;
 
     @Clone(Policy.JAVA_DEFAULT)
-    private ObjectProperty<Object> objectPropertyDefault;
+    private ObjectProperty objectPropertyDefault;
 
     @Clone(Policy.INVOKE_CLONE)
-    private ObjectProperty<Object> objectPropertyInvoke;
+    private ObjectProperty objectPropertyInvoke;
 
     /*
      * DoubleProperty fields for Attributes creation

entreri-core/src/test/java/com/lhkbob/entreri/components/CustomProperty.java

 
 @Factory(CustomProperty.CustomFactoryWithAttributes.class)
 public class CustomProperty implements ShareableProperty<CustomProperty.Bletch> {
-    private final ObjectProperty<Bletch> property;
+    private final ObjectProperty property;
 
     public CustomProperty() {
-        property = new ObjectProperty<Bletch>();
+        property = new ObjectProperty();
     }
 
     @Override
     }
 
     public void set(int index, Bletch b) {
-        property.set(index, b);
+        get(index).value = b.value;
     }
 
     public Bletch get(int index) {
-        return property.get(index);
+        return (Bletch) property.get(index);
     }
 
     @Override
     public void get(int index, Bletch b) {
-        b.value = property.get(index).value;
+        b.value = get(index).value;
     }
 
     @Override
             b.value = (!attributes.hasAttribute(IntProperty.DefaultInt.class) ? 0
                                                                               : attributes
                                .getAttribute(IntProperty.DefaultInt.class).value());
-            property.set(index, b);
+            property.property.set(index, b);
         }
 
         @Override

entreri-core/src/test/java/com/lhkbob/entreri/components/NoFactoryProperty.java

 import com.lhkbob.entreri.property.Property;
 
 public class NoFactoryProperty implements Property {
-    private final ObjectProperty<Crass> property;
+    private final ObjectProperty property;
 
     public NoFactoryProperty() {
-        property = new ObjectProperty<>();
+        property = new ObjectProperty();
     }
 
     public void set(int index, Crass b) {
     }
 
     public Crass get(int index) {
-        return property.get(index);
+        return (Crass) property.get(index);
     }
 
     @Override