Skip to content

Commit

Permalink
Merge pull request #310 from eclipse-aas4j/frankschnicke/semanticId
Browse files Browse the repository at this point in the history
Adds setting of referredSemanticId
  • Loading branch information
mjacoby authored Nov 19, 2024
2 parents 0b73774 + b92237c commit 935535e
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<ReferenceTypes, String> REFERENCE_TYPE_REPRESENTATION = Map.of(
ReferenceTypes.EXTERNAL_REFERENCE, "ExternalRef",
ReferenceTypes.MODEL_REFERENCE, "ModelRef");
Expand Down Expand Up @@ -123,6 +109,8 @@ public static Reference toReference(Identifiable identifiable, Class<? extends R
try {
Reference reference = referenceType.getConstructor().newInstance();
reference.setType(ReferenceTypes.MODEL_REFERENCE);


Key key = keyType.getConstructor().newInstance();
key.setType(referableToKeyType(identifiable));
key.setValue(identifiable.getId());
Expand All @@ -134,42 +122,43 @@ public static Reference toReference(Identifiable identifiable, Class<? extends R
}

/**
* Creates a reference for an Identifiable instance
* Creates a reference for an Identifiable instance using provided implementation types for reference and key
*
* @param identifiable the identifiable to create the reference for
* @param referenceType implementation type of Reference interface
* @param keyType implementation type of Key interface
* @param setReferredSemanticIdIfHasSemantics if the referredSemanticId should be set if the identifiable is of HasSemantics
* @return a reference representing the identifiable
*/
public static Reference toReference(Identifiable identifiable) {
return toReference(identifiable, ReflectionHelper.getDefaultImplementation(Reference.class), ReflectionHelper.getDefaultImplementation(Key.class));
public static Reference toReference(Identifiable identifiable, Class<? extends Reference> referenceType,
Class<? extends Key> 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);
}

/**
Expand Down Expand Up @@ -207,6 +196,27 @@ public static Reference toReference(Reference parent, Referable element, Class<?
}
}

/**
* Creates a reference for an element given a potential parent using provided implementation types for reference and
* key
*
* @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 referenceType implementation type of Reference interface
* @param keyType implementation type of Key interface
* @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, Class<? extends Reference> referenceType,
Class<? extends Key> 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
*
Expand All @@ -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.
*
Expand Down Expand Up @@ -380,39 +426,13 @@ public static <T extends Referable> 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<PropertyDescriptor> 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<Class<?>> 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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand Down Expand Up @@ -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();
}
}

0 comments on commit 935535e

Please sign in to comment.