Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrank Schnicke2018-08-29 14:29:01 +0000
committerFrank Schnicke2018-08-30 14:13:56 +0000
commit3c36acc5adb4ff8255dc6b68080fcaad814d267d (patch)
tree95a0b83b945d28e08a264f8330660588435f2b63
parentad5285fcf3caf5a5173a15e2c4907179c6e6c00b (diff)
downloadbasyx-3c36acc5adb4ff8255dc6b68080fcaad814d267d.tar.gz
basyx-3c36acc5adb4ff8255dc6b68080fcaad814d267d.tar.xz
basyx-3c36acc5adb4ff8255dc6b68080fcaad814d267d.zip
Adds creation of properties based on annotations
* This allows to automatically generate submodels from their class description without the need for explicit creation of the property by the class maintainer Change-Id: I524f246c247fd6c7d4e934390023ebbd04509b3e Signed-off-by: Frank Schnicke <frank.schnicke@iese.fraunhofer.de>
-rw-r--r--sdks/java/basys.sdk/regression/org/eclipse/basyx/testsuite/support/backend/common/stubs/java/submodel/Stub1SubmodelType.java85
-rw-r--r--sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/provider/DescriptorGenerator.java110
-rw-r--r--sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/resources/basic/AbstractReflectionProperty.java28
-rw-r--r--sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/resources/basic/CollectionReflectionProperty.java69
-rw-r--r--sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/resources/basic/DataTypeMapping.java40
-rw-r--r--sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/resources/basic/MapReflectionProperty.java67
-rw-r--r--sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/resources/basic/SingleReflectionProperty.java35
7 files changed, 394 insertions, 40 deletions
diff --git a/sdks/java/basys.sdk/regression/org/eclipse/basyx/testsuite/support/backend/common/stubs/java/submodel/Stub1SubmodelType.java b/sdks/java/basys.sdk/regression/org/eclipse/basyx/testsuite/support/backend/common/stubs/java/submodel/Stub1SubmodelType.java
index dc8ea0490..95beec279 100644
--- a/sdks/java/basys.sdk/regression/org/eclipse/basyx/testsuite/support/backend/common/stubs/java/submodel/Stub1SubmodelType.java
+++ b/sdks/java/basys.sdk/regression/org/eclipse/basyx/testsuite/support/backend/common/stubs/java/submodel/Stub1SubmodelType.java
@@ -1,17 +1,19 @@
package org.eclipse.basyx.testsuite.support.backend.common.stubs.java.submodel;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
import org.eclipse.basyx.aas.api.annotation.AASOperation;
+import org.eclipse.basyx.aas.api.annotation.AASProperty;
import org.eclipse.basyx.aas.api.resources.basic.IAssetAdministrationShell;
+import org.eclipse.basyx.aas.impl.provider.DescriptorGenerator;
import org.eclipse.basyx.aas.impl.resources.basic.AssetAdministrationShell;
import org.eclipse.basyx.aas.impl.resources.basic.AssetKind;
-import org.eclipse.basyx.aas.impl.resources.basic.CollectionProperty;
import org.eclipse.basyx.aas.impl.resources.basic.DataType;
import org.eclipse.basyx.aas.impl.resources.basic.Operation;
import org.eclipse.basyx.aas.impl.resources.basic.ParameterType;
import org.eclipse.basyx.aas.impl.resources.basic.PropertyContainer;
-import org.eclipse.basyx.aas.impl.resources.basic.SingleProperty;
import org.eclipse.basyx.aas.impl.resources.basic.SubModel;
@@ -25,11 +27,24 @@ import org.eclipse.basyx.aas.impl.resources.basic.SubModel;
public class Stub1SubmodelType extends SubModel {
+
/**
* Property type with nested property values
*/
public class NestedPropertyType extends PropertyContainer {
+
+ /**
+ * Sub model property "samplePropertyA"
+ */
+ @AASProperty public int samplePropertyA = 4;
+
+ /**
+ * Sub model property "samplePropertyB"
+ */
+ @AASProperty public int samplePropertyB = 5;
+
+
/**
* Nested operation
*/
@@ -48,19 +63,6 @@ public class Stub1SubmodelType extends SubModel {
setDataType(DataType.REFERENCE);
parent.addProperty(this);
- // Register contained properties
- SingleProperty property4 = new SingleProperty(4);
- property4.setName("samplePropertyA");
- property4.setId("samplePropertyA");
- property4.setDataType(DataType.INTEGER);
- this.addProperty(property4);
-
- SingleProperty property5 = new SingleProperty(5);
- property5.setName("samplePropertyB");
- property5.setId("samplePropertyB");
- property5.setDataType(DataType.INTEGER);
- this.addProperty(property5);
-
// Create and add operation
Operation op = new Operation();
op.setAssetKind(AssetKind.INSTANCE);
@@ -72,9 +74,34 @@ public class Stub1SubmodelType extends SubModel {
op.setParameterTypes(pt);
op.setReturnDataType(DataType.INTEGER);
this.addOperation(op);
+
+ DescriptorGenerator.addProperties(this);
}
}
+ /**
+ * Sub model property "sampleProperty1"
+ */
+ @AASProperty public int sampleProperty1 = 2;
+
+
+ /**
+ * Sub model property "sampleProperty2"
+ */
+ @AASProperty public int sampleProperty2 = 3;
+
+
+ /**
+ * Sub model property "sampleProperty3" is nested property
+ */
+ @AASProperty public NestedPropertyType sampleProperty3 = null;
+
+
+ /**
+ * Sub model property "sampleProperty4"
+ */
+ @AASProperty public Collection<Integer> sampleProperty4 = new LinkedList<Integer>();
+
@@ -101,32 +128,10 @@ public class Stub1SubmodelType extends SubModel {
aas.setId("Stub1AAS");
aas.setName("Stub1AAS");
this.setParent(aas);
+
+ sampleProperty3 = new NestedPropertyType("sampleProperty3", this);
-
-
- // Create and add properties
- SingleProperty property1 = new SingleProperty(2);
- property1.setName("sampleProperty1");
- property1.setId("sampleProperty1");
- property1.setDataType(DataType.INTEGER);
- this.addProperty(property1);
-
- SingleProperty property2 = new SingleProperty(3);
- property2.setName("sampleProperty2");
- property2.setId("sampleProperty2");
- property2.setDataType(DataType.INTEGER);
- this.addProperty(property2);
-
- NestedPropertyType sampleProperty3 = new NestedPropertyType("sampleProperty3", this);
- this.addProperty(sampleProperty3);
-
- ArrayList<Object> collection = new ArrayList<>();
- collection.add(42);
- CollectionProperty sampleProperty4 = new CollectionProperty(collection);
- sampleProperty4.setName("sampleProperty4");
- sampleProperty4.setId("sampleProperty4");
- sampleProperty4.setDataType(DataType.COLLECTION);
- this.addProperty(sampleProperty4);
+ DescriptorGenerator.addProperties(this);
}
diff --git a/sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/provider/DescriptorGenerator.java b/sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/provider/DescriptorGenerator.java
new file mode 100644
index 000000000..f81eab872
--- /dev/null
+++ b/sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/provider/DescriptorGenerator.java
@@ -0,0 +1,110 @@
+package org.eclipse.basyx.aas.impl.provider;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.basyx.aas.api.annotation.AASProperty;
+import org.eclipse.basyx.aas.impl.resources.basic.CollectionReflectionProperty;
+import org.eclipse.basyx.aas.impl.resources.basic.DataType;
+import org.eclipse.basyx.aas.impl.resources.basic.DataTypeMapping;
+import org.eclipse.basyx.aas.impl.resources.basic.MapReflectionProperty;
+import org.eclipse.basyx.aas.impl.resources.basic.Property;
+import org.eclipse.basyx.aas.impl.resources.basic.PropertyContainer;
+import org.eclipse.basyx.aas.impl.resources.basic.SingleReflectionProperty;
+import org.eclipse.basyx.aas.impl.resources.basic.SubModel;
+
+/**
+ * Generates properties using the @AASProperty annotation
+ *
+ * @author schnicke
+ *
+ */
+public class DescriptorGenerator {
+ /**
+ * Get all fields that are tagged with AASProperty
+ */
+ private static Collection<Field> getAllFields(Class<?> cls) {
+ // Return value
+ Collection<Field> result = new LinkedList<>();
+ while (cls != null) {
+ try {
+ // Get fields
+ Field[] fields = cls.getDeclaredFields();
+
+ // Add fields to result
+ for (Field field : fields)
+ if (field.getAnnotation(AASProperty.class) != null)
+ result.add(field);
+ } catch (Exception e) {
+ // Do nothing
+ }
+ cls = cls.getSuperclass();
+ }
+
+ // Return result
+ return result;
+ }
+
+ public static void addProperties(SubModel sm) {
+ for (Property p : generateProperty(sm)) {
+ sm.addProperty(p);
+ }
+ }
+
+ public static void addProperties(PropertyContainer container) {
+ for (Property p : generateProperty(container)) {
+ container.addProperty(p);
+ }
+ }
+
+ public static List<Property> generateProperty(Object obj) {
+ List<Property> ret = new ArrayList<>();
+ Collection<Field> fields = getAllFields(obj.getClass());
+
+ // Iterate over the fields and assign the correct property type
+ // Uses reflection properties to allow modifying the standard values of the data
+ // in contrast to a standard property
+ for (Field f : fields) {
+ f.setAccessible(true);
+ Property p;
+ if (f.getType().isPrimitive() || isPrimitiveWrapper(f.getType()) || f.getType() == String.class) {
+ SingleReflectionProperty singleProp = new SingleReflectionProperty(f, obj);
+ DataType type = DataTypeMapping.map(obj.getClass());
+ singleProp.setDataType(type);
+ p = singleProp;
+ } else if (Collection.class.isAssignableFrom(f.getType())) {
+ CollectionReflectionProperty collectionProp = new CollectionReflectionProperty(f, obj);
+ collectionProp.setDataType(DataType.COLLECTION);
+ p = collectionProp;
+ } else if (Map.class.isAssignableFrom(f.getType())) {
+ MapReflectionProperty mapProp = new MapReflectionProperty(f, obj);
+ p = mapProp;
+ } else if (PropertyContainer.class.isAssignableFrom(f.getType())) {
+ try {
+ p = (Property) f.get(obj);
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ e.printStackTrace();
+ throw new RuntimeException("Reflection error with property " + f.getName() + " of type " + f.getType());
+ }
+ } else {
+ throw new RuntimeException("Unknown property type " + f.getType());
+ }
+
+ // Set name and id independent of the property type
+ String name = f.getName();
+ p.setName(name);
+ p.setId(name);
+ ret.add(p);
+
+ }
+ return ret;
+ }
+
+ private static boolean isPrimitiveWrapper(Class<?> c) {
+ return c == Integer.class|| c == Boolean.class || c == Double.class || c == Float.class || c == Character.class;
+ }
+}
diff --git a/sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/resources/basic/AbstractReflectionProperty.java b/sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/resources/basic/AbstractReflectionProperty.java
new file mode 100644
index 000000000..79952cd64
--- /dev/null
+++ b/sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/resources/basic/AbstractReflectionProperty.java
@@ -0,0 +1,28 @@
+package org.eclipse.basyx.aas.impl.resources.basic;
+
+import java.lang.reflect.Field;
+
+
+/**
+ * Base class for properties relying on reflections
+ * @author schnicke
+ *
+ */
+public abstract class AbstractReflectionProperty extends Property {
+ Field f;
+ Object o;
+
+ public AbstractReflectionProperty(Field f, Object o) {
+ super();
+ this.f = f;
+ this.o = o;
+ }
+
+ protected Field getField() {
+ return f;
+ }
+
+ protected Object getObject() {
+ return o;
+ }
+}
diff --git a/sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/resources/basic/CollectionReflectionProperty.java b/sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/resources/basic/CollectionReflectionProperty.java
new file mode 100644
index 000000000..dbad40aac
--- /dev/null
+++ b/sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/resources/basic/CollectionReflectionProperty.java
@@ -0,0 +1,69 @@
+package org.eclipse.basyx.aas.impl.resources.basic;
+
+import java.lang.reflect.Field;
+import java.util.Collection;
+
+import org.eclipse.basyx.aas.api.exception.ServerException;
+import org.eclipse.basyx.aas.api.exception.TypeMismatchException;
+import org.eclipse.basyx.aas.api.resources.basic.ICollectionProperty;
+
+/**
+ * Provides reflective access to a collection based on a property
+ *
+ * @author schnicke
+ *
+ */
+public class CollectionReflectionProperty extends AbstractReflectionProperty implements ICollectionProperty {
+
+ public CollectionReflectionProperty(Field f, Object o) {
+ super(f, o);
+ setCollection(true);
+ }
+
+ @Override
+ public Object get(Object objRef) {
+ return null; // TODO: ?
+ }
+
+ @Override
+ public void set(Collection<Object> collection) throws ServerException {
+ try {
+ f.set(getObject(), collection);
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ e.printStackTrace();
+ throw new ServerException("Reflection failed");
+ }
+ }
+
+ @Override
+ public void add(Object newValue) throws ServerException, TypeMismatchException {
+ getCollection().add(newValue);
+ }
+
+ @Override
+ public void remove(Object objectRef) throws ServerException {
+ getCollection().remove(objectRef);
+ }
+
+ @Override
+ public Collection<Object> getElements() throws ServerException {
+ return getCollection();
+ }
+
+ @Override
+ public int getElementCount() throws ServerException {
+ return getCollection().size();
+
+ }
+
+ @SuppressWarnings("unchecked")
+ private Collection<Object> getCollection() throws ServerException {
+ try {
+ return (Collection<Object>) f.get(getObject());
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ e.printStackTrace();
+ throw new ServerException("Reflection failed");
+ }
+ }
+
+}
diff --git a/sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/resources/basic/DataTypeMapping.java b/sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/resources/basic/DataTypeMapping.java
new file mode 100644
index 000000000..90dbc1b2a
--- /dev/null
+++ b/sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/resources/basic/DataTypeMapping.java
@@ -0,0 +1,40 @@
+package org.eclipse.basyx.aas.impl.resources.basic;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Maps a class to its corresponding DataType
+ *
+ * @author schnicke
+ *
+ */
+public class DataTypeMapping {
+ public static DataType map(Class<?> c) {
+ if (c.isPrimitive()) {
+ if (c == int.class || c == Integer.class) {
+ return DataType.INTEGER;
+ } else if (c == void.class || c == Void.class) {
+ return DataType.VOID;
+ } else if (c == boolean.class || c == Boolean.class) {
+ return DataType.BOOLEAN;
+ } else if (c == float.class || c == Float.class) {
+ return DataType.FLOAT;
+ } else if (c == double.class || c == Double.class) {
+ return DataType.DOUBLE;
+ } else if (c == char.class || c == Character.class) {
+ return DataType.CHARACTER;
+ } else {
+ throw new RuntimeException("Unsupported property type " + c);
+ }
+ } else if (c == String.class) {
+ return DataType.STRING;
+ } else if (Map.class.isAssignableFrom(c)) {
+ return DataType.MAP;
+ } else if (Collection.class.isAssignableFrom(c)) {
+ return DataType.COLLECTION;
+ } else {
+ return DataType.REFERENCE;
+ }
+ }
+}
diff --git a/sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/resources/basic/MapReflectionProperty.java b/sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/resources/basic/MapReflectionProperty.java
new file mode 100644
index 000000000..adfa4e299
--- /dev/null
+++ b/sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/resources/basic/MapReflectionProperty.java
@@ -0,0 +1,67 @@
+package org.eclipse.basyx.aas.impl.resources.basic;
+
+import java.lang.reflect.Field;
+import java.util.Collection;
+import java.util.Map;
+
+import org.eclipse.basyx.aas.api.exception.ServerException;
+import org.eclipse.basyx.aas.api.exception.TypeMismatchException;
+import org.eclipse.basyx.aas.api.resources.basic.IMapProperty;
+
+/**
+ * Provides reflective access to a map based on a property
+ * @author schnicke
+ *
+ */
+public class MapReflectionProperty extends AbstractReflectionProperty implements IMapProperty {
+
+ public MapReflectionProperty(Field f, Object o) {
+ super(f, o);
+ }
+
+ @Override
+ public Object getValue(Object key) throws TypeMismatchException, ServerException {
+ return getMap().get(key);
+ }
+
+ @Override
+ public void put(Object key, Object value) throws ServerException {
+ getMap().put(key, value);
+ }
+
+ @Override
+ public void set(Map<Object, Object> map) throws ServerException {
+ try {
+ f.set(getObject(), map);
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ e.printStackTrace();
+ throw new ServerException("Reflection failed");
+ }
+ }
+
+ @Override
+ public Collection<Object> getKeys() throws TypeMismatchException, ServerException {
+ return getMap().keySet();
+ }
+
+ @Override
+ public Integer getEntryCount() throws TypeMismatchException, ServerException {
+ return getMap().size();
+ }
+
+ @Override
+ public void remove(Object key) throws ServerException, TypeMismatchException {
+ getMap().remove(key);
+ }
+
+ @SuppressWarnings("unchecked")
+ private Map<Object, Object> getMap() throws ServerException {
+ try {
+ return (Map<Object, Object>) f.get(o);
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ e.printStackTrace();
+ throw new ServerException("Reflection failed");
+ }
+ }
+
+}
diff --git a/sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/resources/basic/SingleReflectionProperty.java b/sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/resources/basic/SingleReflectionProperty.java
new file mode 100644
index 000000000..acef18836
--- /dev/null
+++ b/sdks/java/basys.sdk/src/org/eclipse/basyx/aas/impl/resources/basic/SingleReflectionProperty.java
@@ -0,0 +1,35 @@
+package org.eclipse.basyx.aas.impl.resources.basic;
+
+import java.lang.reflect.Field;
+
+import org.eclipse.basyx.aas.api.exception.ServerException;
+
+/**
+ * Provides reflective access to a simple value based on a property
+ * @author schnicke
+ *
+ */
+public class SingleReflectionProperty extends SingleProperty{
+ private Field f;
+ private Object o;
+ public SingleReflectionProperty(Field f, Object o) {
+ super(null);
+ this.f = f;
+ this.o = o;
+ f.setAccessible(true);
+ }
+
+ @Override
+ public Object get() throws Exception {
+ return f.get(o);
+ }
+
+ @Override
+ public void set(Object newValue) throws ServerException {
+ try {
+ f.set(o, newValue);
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ }
+}

Back to the top