Michael Ludwig avatar Michael Ludwig committed 47b260b

Add @Clone attribute for property declarations

Comments (0)

Files changed (2)

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

+/*
+ * Entreri, an entity-component framework in Java
+ *
+ * Copyright (c) 2012, Michael Ludwig
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ *     Redistributions of source code must retain the above copyright notice,
+ *         this list of conditions and the following disclaimer.
+ *     Redistributions in binary form must reproduce the above copyright notice,
+ *         this list of conditions and the following disclaimer in the
+ *         documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.lhkbob.entreri.property;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.lhkbob.entreri.Attribute;
+import com.lhkbob.entreri.Entity;
+import com.lhkbob.entreri.EntitySystem;
+import com.lhkbob.entreri.Factory;
+import com.lhkbob.entreri.PropertyFactory;
+
+/**
+ * <p>
+ * Clone is an attribute that can be applied to property declarations to change
+ * how the property's values are cloned when a Component or Entity are created
+ * from a template Component or Entity. At the moment it can be used to:
+ * <ol>
+ * <li>Copy values using Java's assignment semantics</li>
+ * <li>Clone objects using their clone() method if it exists</li>
+ * <li>Do not copy the value, and use the default value the factory normally
+ * would have assigned</li>
+ * </ol>
+ * <p>
+ * If these options are not sufficient, a custom {@link PropertyFactory} can be
+ * implemented and specified by using the {@link Factory} annotation.
+ * 
+ * @author Michael Ludwig
+ * 
+ */
+@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(com.lhkbob.entreri.Property, int, com.lhkbob.entreri.Property, int)}
+     * method is invoked in response to
+     * {@link Entity#add(com.lhkbob.entreri.Component)} or
+     * {@link EntitySystem#addEntity(Entity)}.
+     */
+    public static enum Policy {
+        /**
+         * Cloning policy that disables the clone action for the given property.
+         * The new component being cloned into gets the default value, just as
+         * if it was initialized via
+         * {@link Entity#add(com.lhkbob.entreri.TypeId)}.
+         */
+        DISABLE,
+        /**
+         * <p>
+         * Cloning policy that follows Java's assignment semantics, e.g.
+         * primitives are copied by value from the component being cloned to the
+         * new one, and references are copied but the actual instances are
+         * shared.
+         * <p>
+         * This is the default policy if the Clone attribute is not present on
+         * any of the property implementations provided in this package.
+         */
+        JAVA_DEFAULT,
+        /**
+         * <p>
+         * Cloning policy that attempts to invoke {@link Object#clone()} on
+         * cloned component's current value. If the value is null, null is
+         * assigned without throwing an NPE.
+         * <p>
+         * If the property type is a primitive data type, or is not
+         * {@link Cloneable}, this behaves like JAVA_DEFAULT.
+         */
+        INVOKE_CLONE
+    }
+
+    /**
+     * @return The cloning policy to use
+     */
+    Policy value();
+}

src/test/java/com/lhkbob/entreri/PropertyTest.java

+/*
+ * Entreri, an entity-component framework in Java
+ *
+ * Copyright (c) 2012, Michael Ludwig
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ *     Redistributions of source code must retain the above copyright notice,
+ *         this list of conditions and the following disclaimer.
+ *     Redistributions in binary form must reproduce the above copyright notice,
+ *         this list of conditions and the following disclaimer in the
+ *         documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.lhkbob.entreri;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import com.lhkbob.entreri.property.Clone;
+import com.lhkbob.entreri.property.Clone.Policy;
+import com.lhkbob.entreri.property.DoubleProperty;
+import com.lhkbob.entreri.property.DoubleProperty.DefaultDouble;
+import com.lhkbob.entreri.property.ObjectProperty;
+
+@SuppressWarnings({"unused", "rawtypes", "unchecked"})
+public class PropertyTest {
+    /*
+     * ObjectProperty fields for Attributes creation
+     */
+    private ObjectProperty<Object> objectPropertyNoPolicy;
+
+    @Clone(Policy.DISABLE)
+    private ObjectProperty<Object> objectPropertyDisabled;
+
+    @Clone(Policy.JAVA_DEFAULT)
+    private ObjectProperty<Object> objectPropertyDefault;
+
+    @Clone(Policy.INVOKE_CLONE)
+    private ObjectProperty<Object> objectPropertyInvoke;
+
+    /*
+     * DoubleProperty fields for Attributes creation
+     */
+    @DefaultDouble(1.0)
+    private DoubleProperty doublePropertyNoPolicy;
+
+    @DefaultDouble(2.0)
+    @Clone(Policy.DISABLE)
+    private DoubleProperty doublePropertyDisabled;
+
+    @Clone(Policy.JAVA_DEFAULT)
+    private DoubleProperty doublePropertyDefault;
+
+    @Clone(Policy.INVOKE_CLONE)
+    private DoubleProperty doublePropertyInvoke;
+
+    private Attributes createAttributes(String fieldName) throws Exception {
+        return new Attributes(getClass().getDeclaredField(fieldName));
+    }
+
+    @Test
+    public void testObjectPropertyCloneNoPolicy() throws Exception {
+        ObjectProperty.Factory factory = new ObjectProperty.Factory(createAttributes("objectPropertyNoPolicy"));
+
+        ObjectProperty p1 = factory.create();
+        ObjectProperty p2 = factory.create();
+
+        factory.setDefaultValue(p1, 0);
+        factory.setDefaultValue(p2, 0);
+
+        Object val = new Object();
+        p1.set(val, 0);
+
+        Assert.assertSame(val, p1.get(0));
+        Assert.assertNull(p2.get(0));
+
+        factory.clone(p1, 0, p2, 0);
+
+        Assert.assertSame(val, p1.get(0));
+        Assert.assertSame(val, p2.get(0));
+    }
+
+    @Test
+    public void testObjectPropertyCloneDisabled() throws Exception {
+        ObjectProperty.Factory factory = new ObjectProperty.Factory(createAttributes("objectPropertyDisabled"));
+
+        ObjectProperty p1 = factory.create();
+        ObjectProperty p2 = factory.create();
+
+        factory.setDefaultValue(p1, 0);
+        factory.setDefaultValue(p2, 0);
+
+        Object val = new Object();
+        p1.set(val, 0);
+
+        Assert.assertSame(val, p1.get(0));
+        Assert.assertNull(p2.get(0));
+
+        factory.clone(p1, 0, p2, 0);
+
+        Assert.assertSame(val, p1.get(0));
+        Assert.assertNull(p2.get(0));
+    }
+
+    @Test
+    public void testObjectPropertyCloneJavaDefault() throws Exception {
+        ObjectProperty.Factory factory = new ObjectProperty.Factory(createAttributes("objectPropertyDefault"));
+
+        ObjectProperty p1 = factory.create();
+        ObjectProperty p2 = factory.create();
+
+        factory.setDefaultValue(p1, 0);
+        factory.setDefaultValue(p2, 0);
+
+        Object val = new Object();
+        p1.set(val, 0);
+
+        Assert.assertSame(val, p1.get(0));
+        Assert.assertNull(p2.get(0));
+
+        factory.clone(p1, 0, p2, 0);
+
+        Assert.assertSame(val, p1.get(0));
+        Assert.assertSame(val, p2.get(0));
+    }
+
+    @Test
+    public void testObjectPropertyCloneInvoke() throws Exception {
+        ObjectProperty.Factory factory = new ObjectProperty.Factory(createAttributes("objectPropertyInvoke"));
+
+        ObjectProperty p1 = factory.create();
+        ObjectProperty p2 = factory.create();
+
+        factory.setDefaultValue(p1, 0);
+        factory.setDefaultValue(p2, 0);
+
+        CloneObject val = new CloneObject(5);
+        p1.set(val, 0);
+
+        Assert.assertSame(val, p1.get(0));
+        Assert.assertNull(p2.get(0));
+
+        factory.clone(p1, 0, p2, 0);
+
+        Assert.assertSame(val, p1.get(0));
+        Assert.assertNotSame(val, p2.get(0));
+        Assert.assertNotNull(p2.get(0));
+        Assert.assertEquals(5, ((CloneObject) p2.get(0)).foo);
+    }
+
+    @Test
+    public void testPrimitivePropertyCloneNoPolicy() throws Exception {
+        DoubleProperty.Factory factory = new DoubleProperty.Factory(createAttributes("doublePropertyNoPolicy"));
+
+        DoubleProperty p1 = factory.create();
+        DoubleProperty p2 = factory.create();
+
+        factory.setDefaultValue(p1, 0);
+        factory.setDefaultValue(p2, 0);
+
+        p1.set(4.0, 0);
+
+        Assert.assertEquals(4.0, p1.get(0), 0.0001);
+        Assert.assertEquals(1.0, p2.get(0), 0.0001);
+
+        factory.clone(p1, 0, p2, 0);
+
+        Assert.assertEquals(4.0, p1.get(0), 0.0001);
+        Assert.assertEquals(4.0, p2.get(0), 0.0001);
+    }
+
+    @Test
+    public void testPrimitivePropertyCloneDisabled() throws Exception {
+        DoubleProperty.Factory factory = new DoubleProperty.Factory(createAttributes("doublePropertyDisabled"));
+
+        DoubleProperty p1 = factory.create();
+        DoubleProperty p2 = factory.create();
+
+        factory.setDefaultValue(p1, 0);
+        factory.setDefaultValue(p2, 0);
+
+        p1.set(4.0, 0);
+
+        Assert.assertEquals(4.0, p1.get(0), 0.0001);
+        Assert.assertEquals(2.0, p2.get(0), 0.0001);
+
+        factory.clone(p1, 0, p2, 0);
+
+        Assert.assertEquals(4.0, p1.get(0), 0.0001);
+        Assert.assertEquals(2.0, p2.get(0), 0.0001);
+    }
+
+    @Test
+    public void testPrimitivePropertyCloneJavaDefault() throws Exception {
+        DoubleProperty.Factory factory = new DoubleProperty.Factory(createAttributes("doublePropertyDefault"));
+
+        DoubleProperty p1 = factory.create();
+        DoubleProperty p2 = factory.create();
+
+        factory.setDefaultValue(p1, 0);
+        factory.setDefaultValue(p2, 0);
+
+        p1.set(4.0, 0);
+
+        Assert.assertEquals(4.0, p1.get(0), 0.0001);
+        Assert.assertEquals(0.0, p2.get(0), 0.0001);
+
+        factory.clone(p1, 0, p2, 0);
+
+        Assert.assertEquals(4.0, p1.get(0), 0.0001);
+        Assert.assertEquals(4.0, p2.get(0), 0.0001);
+    }
+
+    @Test
+    public void testPrimitivePropertyCloneInvoke() throws Exception {
+        DoubleProperty.Factory factory = new DoubleProperty.Factory(createAttributes("doublePropertyInvoke"));
+
+        DoubleProperty p1 = factory.create();
+        DoubleProperty p2 = factory.create();
+
+        factory.setDefaultValue(p1, 0);
+        factory.setDefaultValue(p2, 0);
+
+        p1.set(4.0, 0);
+
+        Assert.assertEquals(4.0, p1.get(0), 0.0001);
+        Assert.assertEquals(0.0, p2.get(0), 0.0001);
+
+        factory.clone(p1, 0, p2, 0);
+
+        Assert.assertEquals(4.0, p1.get(0), 0.0001);
+        Assert.assertEquals(4.0, p2.get(0), 0.0001);
+    }
+
+    public static class CloneObject implements Cloneable {
+        public final int foo;
+
+        public CloneObject(int foo) {
+            this.foo = foo;
+        }
+
+        @Override
+        public CloneObject clone() {
+            return new CloneObject(foo);
+        }
+    }
+}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.