diff --git a/dataformat-core/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/core/util/AasUtils.java b/dataformat-core/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/core/util/AasUtils.java index b1f210bdd..22ffd8272 100644 --- a/dataformat-core/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/core/util/AasUtils.java +++ b/dataformat-core/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/core/util/AasUtils.java @@ -15,27 +15,20 @@ */ package org.eclipse.digitaltwin.aas4j.v3.dataformat.core.util; -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.internal.deserialization.EnumDeserializer; import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.internal.serialization.EnumSerializer; import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.internal.util.GetChildrenVisitor; import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.internal.util.GetIdentifierVisitor; -import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.internal.util.MostSpecificTypeTokenComparator; import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.internal.util.ReflectionHelper; import org.eclipse.digitaltwin.aas4j.v3.model.Environment; +import org.eclipse.digitaltwin.aas4j.v3.model.HasSemantics; import org.eclipse.digitaltwin.aas4j.v3.model.Identifiable; import org.eclipse.digitaltwin.aas4j.v3.model.Key; import org.eclipse.digitaltwin.aas4j.v3.model.KeyTypes; @@ -44,18 +37,11 @@ import org.eclipse.digitaltwin.aas4j.v3.model.ReferenceTypes; import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementList; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.reflect.TypeToken; /** * Provides utility functions related to AAS */ public class AasUtils { - - private static final Logger log = LoggerFactory.getLogger(AasUtils.class); - private static final Map REFERENCE_TYPE_REPRESENTATION = Map.of( ReferenceTypes.EXTERNAL_REFERENCE, "ExternalRef", ReferenceTypes.MODEL_REFERENCE, "ModelRef"); @@ -123,6 +109,8 @@ public static Reference toReference(Identifiable identifiable, Class referenceType, + Class keyType, boolean setReferredSemanticIdIfHasSemantics) { + Reference reference = toReference(identifiable, referenceType, keyType); + + return handleReferredSemanticId(identifiable, setReferredSemanticIdIfHasSemantics, reference); } + /** - * Gets the KeyElements type matching the provided Referable + * Creates a reference for an Identifiable instance * - * @param referable The referable to convert to KeyElements type - * @return the most specific KeyElements type representing the Referable, i.e. abstract types like SUBMODEL_ELEMENT - * or DATA_ELEMENT are never returned; null if there is no corresponding KeyElements type + * @param identifiable the identifiable to create the reference for + * @return a reference representing the identifiable */ - public static KeyTypes referableToKeyType(Referable referable) { - Class aasInterface = ReflectionHelper.getAasInterface(referable.getClass()); - if (aasInterface != null) { - return KeyTypes.valueOf(EnumDeserializer.deserializeEnumName(aasInterface.getSimpleName())); - } - return null; + public static Reference toReference(Identifiable identifiable) { + return toReference(identifiable, ReflectionHelper.getDefaultImplementation(Reference.class), ReflectionHelper.getDefaultImplementation(Key.class)); } /** - * Gets a Java interface representing the type provided by key. + * Creates a reference for an Identifiable instance * - * @param key The KeyElements type - * @return a Java interface representing the provided KeyElements type or null if no matching Class/interface could - * be found. It also returns abstract types like SUBMODEL_ELEMENT or DATA_ELEMENT + * @param identifiable the identifiable to create the reference for + * @param setReferredSemanticIdIfHasSemantics if the referredSemanticId should be set if the identifiable is of HasSemantics + * @return a reference representing the identifiable */ - private static Class keyTypeToClass(KeyTypes key) { - return Stream.concat(ReflectionHelper.INTERFACES.stream(), ReflectionHelper.INTERFACES_WITHOUT_DEFAULT_IMPLEMENTATION.stream()) - .filter(x -> x.getSimpleName().equals(EnumSerializer.serializeEnumName(key.name()))) - .findAny() - .orElse(null); + public static Reference toReference(Identifiable identifiable, boolean setReferredSemanticIdIfHasSemantics) { + Reference reference = toReference(identifiable); + + return handleReferredSemanticId(identifiable, setReferredSemanticIdIfHasSemantics, reference); } /** @@ -207,6 +196,27 @@ public static Reference toReference(Reference parent, Referable element, Class referenceType, + Class keyType, boolean setReferredSemanticIdIfHasSemantics) { + Reference reference = toReference(parent, element, referenceType, keyType); + return handleReferredSemanticId(element, setReferredSemanticIdIfHasSemantics, reference); + } + /** * Creates a reference for an element given a potential parent * @@ -224,6 +234,42 @@ public static Reference toReference(Reference parent, Referable element) { ReflectionHelper.getDefaultImplementation(Key.class)); } + /** + * Creates a reference for an element given a potential parent + * + * @param parent Reference to the parent. Can only be null when element is instance of Identifiable, otherwise + * result will always be null + * @param element the element to create a reference for + * @param setReferredSemanticIdIfHasSemantics if the referredSemanticId should be set if the identifiable is of HasSemantics + * + * @return A reference representing the element or null if either element is null or parent is null and element not + * an instance of Identifiable. In case element is an instance of Identifiable, the returned reference will only + * contain one key pointing directly to the element. + */ + public static Reference toReference(Reference parent, Referable element, boolean setReferredSemanticIdIfHasSemantics) { + return toReference(parent, + element, + ReflectionHelper.getDefaultImplementation(Reference.class), + ReflectionHelper.getDefaultImplementation(Key.class), + setReferredSemanticIdIfHasSemantics); + } + + /** + * Gets the KeyElements type matching the provided Referable + * + * @param referable The referable to convert to KeyElements type + * @return the most specific KeyElements type representing the Referable, i.e. abstract types like SUBMODEL_ELEMENT + * or DATA_ELEMENT are never returned; null if there is no corresponding KeyElements type + */ + public static KeyTypes referableToKeyType(Referable referable) { + Class aasInterface = ReflectionHelper.getAasInterface(referable.getClass()); + if (aasInterface != null) { + return KeyTypes.valueOf(EnumDeserializer.deserializeEnumName(aasInterface.getSimpleName())); + } + return null; + } + + /** * Checks if two references are refering to the same element ignoring referredSemanticId. * @@ -380,39 +426,13 @@ public static T resolve(Reference reference, Environment e return type.cast(current); } - /** - * Gets a list of all properties defined for a class implementing at least one AAS interface. - * - * @param type A class implementing at least one AAS interface. If it is does not implement any AAS interface the - * result will be an empty list - * @return a list of all properties defined in any of AAS interface implemented by type. If type does not implement - * any AAS interface an empty list is returned. - */ - private static List getAasProperties(Class type) { - Class aasType = ReflectionHelper.getAasInterface(type); - if (aasType == null) { - aasType = ReflectionHelper.INTERFACES_WITHOUT_DEFAULT_IMPLEMENTATION.stream() - .filter(x -> x.isAssignableFrom(type)) - .map(x -> TypeToken.of(x)) - .sorted(new MostSpecificTypeTokenComparator()) - .findFirst().get() - .getRawType(); - } - Set> types = new HashSet<>(); - if (aasType != null) { - types.add(aasType); - types.addAll(ReflectionHelper.getSuperTypes(aasType, true)); + private static Reference handleReferredSemanticId(Referable referable, + boolean setReferredSemanticIdIfHasSemantics, Reference reference) { + if (setReferredSemanticIdIfHasSemantics && referable instanceof HasSemantics) { + reference.setReferredSemanticId(((HasSemantics) referable).getSemanticId()); } - return types.stream() - .flatMap(x -> { - try { - return Stream.of(Introspector.getBeanInfo(x).getPropertyDescriptors()); - } catch (IntrospectionException ex) { - log.warn("error finding properties of class '{}'", type, ex); - } - return Stream.empty(); - }) - .sorted(Comparator.comparing(x -> x.getName())) - .collect(Collectors.toList()); + + return reference; } + } diff --git a/dataformat-core/src/test/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/core/util/AasUtilsTest.java b/dataformat-core/src/test/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/core/util/AasUtilsTest.java index 247888ff4..6ea6e25cc 100644 --- a/dataformat-core/src/test/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/core/util/AasUtilsTest.java +++ b/dataformat-core/src/test/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/core/util/AasUtilsTest.java @@ -16,11 +16,16 @@ */ package org.eclipse.digitaltwin.aas4j.v3.dataformat.core.util; -import junitparams.JUnitParamsRunner; +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; + import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.AASFull; +import org.eclipse.digitaltwin.aas4j.v3.model.DataTypeDefXsd; import org.eclipse.digitaltwin.aas4j.v3.model.Environment; import org.eclipse.digitaltwin.aas4j.v3.model.KeyTypes; import org.eclipse.digitaltwin.aas4j.v3.model.Operation; +import org.eclipse.digitaltwin.aas4j.v3.model.Property; import org.eclipse.digitaltwin.aas4j.v3.model.Referable; import org.eclipse.digitaltwin.aas4j.v3.model.Reference; import org.eclipse.digitaltwin.aas4j.v3.model.ReferenceTypes; @@ -39,10 +44,7 @@ import org.junit.Test; import org.junit.runner.RunWith; -import java.util.ArrayList; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import junitparams.JUnitParamsRunner; @RunWith(JUnitParamsRunner.class) public class AasUtilsTest { @@ -505,4 +507,61 @@ public void whenAsString_withReferredSemanticId_success() { String actual = AasUtils.asString(reference); Assert.assertEquals(expected, actual); } + + @Test + public void whenAsReferenceWithReferredSemanticId_IdentifiableWithSemanticId_success() { + Submodel submodel = AASFull.SUBMODEL_3; + Reference ref = AasUtils.toReference(submodel, true); + assertEquals(submodel.getSemanticId(), ref.getReferredSemanticId()); + } + + @Test + public void whenAsReferenceWithoutReferredSemanticId_IdentifiableWithSemanticId_success() { + Submodel submodel = AASFull.SUBMODEL_3; + Reference ref = AasUtils.toReference(submodel, false); + assertEquals(null, ref.getReferredSemanticId()); + } + + @Test + public void whenAsReferenceWithReferredSemanticId_PropertyWithSemanticId_success() { + Property prop = createPropertyWithSemanticId(); + Reference reference = new DefaultReference.Builder() + .type(ReferenceTypes.EXTERNAL_REFERENCE) + .keys(new DefaultKey.Builder() + .type(KeyTypes.GLOBAL_REFERENCE) + .value("bar") + .build()) + .build(); + Reference ref = AasUtils.toReference(reference, prop, true); + assertEquals(prop.getSemanticId(), ref.getReferredSemanticId()); + } + + @Test + public void whenAsReferenceWithoutReferredSemanticId_PropertyWithSemanticId_success() { + Property prop = createPropertyWithSemanticId(); + Reference reference = new DefaultReference.Builder() + .type(ReferenceTypes.EXTERNAL_REFERENCE) + .keys(new DefaultKey.Builder() + .type(KeyTypes.GLOBAL_REFERENCE) + .value("bar") + .build()) + .build(); + Reference ref = AasUtils.toReference(reference, prop, false); + assertEquals(null, ref.getReferredSemanticId()); + } + + private Property createPropertyWithSemanticId() { + return new DefaultProperty.Builder() + .idShort("ExampleProperty1") + .semanticId(new DefaultReference.Builder() + .keys(new DefaultKey.Builder() + .type(KeyTypes.GLOBAL_REFERENCE) + .value("http://acplt.org/Properties/ExampleProperty") + .build()) + .type(ReferenceTypes.EXTERNAL_REFERENCE) + .build()) + .value("http://acplt.org/ValueId/ExampleValueId") + .valueType(DataTypeDefXsd.STRING) + .build(); + } }