Skip to content
This repository was archived by the owner on Feb 15, 2024. It is now read-only.

Commit bc1ab17

Browse files
committed
working on deserialization, added functionality
* submodels are collected and added to AAS * LangString, ReferenceElement, RelationshipElement are properly handled * refactoring utility methods to handle custom subtypes (e.g. when cloning or creating new references) * updated MappingProvider to also accept classes with remaining generic type
1 parent 3dccc85 commit bc1ab17

36 files changed

Lines changed: 1412 additions & 301 deletions

dataformat-aml/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@
3333
<artifactId>jaxb-api</artifactId>
3434
<version>${jaxb.version}</version>
3535
</dependency>
36+
<dependency>
37+
<groupId>xerces</groupId>
38+
<artifactId>xercesImpl</artifactId>
39+
<version>2.12.1</version>
40+
</dependency>
3641
<dependency>
3742
<groupId>org.eclipse.persistence</groupId>
3843
<artifactId>org.eclipse.persistence.moxy</artifactId>
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright (c) 2021 Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e. V.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.adminshell.aas.v3.dataformat.aml.common;
17+
18+
import io.adminshell.aas.v3.dataformat.aml.serialization.naming.NamingStrategy;
19+
import io.adminshell.aas.v3.dataformat.mapping.Mapper;
20+
import io.adminshell.aas.v3.dataformat.mapping.MappingContext;
21+
import io.adminshell.aas.v3.dataformat.mapping.MappingProvider;
22+
import java.beans.PropertyDescriptor;
23+
24+
/**
25+
* Abstract base class for MappingContext used for mapping between AML and AAS
26+
*
27+
* @param <T>
28+
*/
29+
public class AbstractMappingContext<T extends Mapper> extends MappingContext<T> {
30+
31+
protected final NamingStrategy classNamingStrategy;
32+
protected final NamingStrategy propertyNamingStrategy;
33+
protected final PropertyDescriptor property;
34+
35+
protected AbstractMappingContext(MappingProvider<T> mappingProvider,
36+
NamingStrategy classNamingStrategy,
37+
NamingStrategy propertyNamingStrategy,
38+
PropertyDescriptor property) {
39+
super(mappingProvider);
40+
this.classNamingStrategy = classNamingStrategy;
41+
this.propertyNamingStrategy = propertyNamingStrategy;
42+
this.property = property;
43+
}
44+
45+
/**
46+
* Gets the class naming strategy. This is used to resolve naming
47+
* differences in class names between AML and AAS.
48+
*
49+
* @return the naming strategy
50+
*/
51+
public NamingStrategy getClassNamingStrategy() {
52+
return classNamingStrategy;
53+
}
54+
55+
/**
56+
* Gets the property naming strategy. This is used to resolve naming
57+
* differences in property namings between AML and AAS.
58+
*
59+
* @return
60+
*/
61+
public NamingStrategy getPropertyNamingStrategy() {
62+
return propertyNamingStrategy;
63+
}
64+
65+
/**
66+
* Gets the currently processed property if present, otherwise null
67+
*
68+
* @return the currently processed property if present, otherwise null
69+
*/
70+
public PropertyDescriptor getProperty() {
71+
return property;
72+
}
73+
74+
}

dataformat-aml/src/main/java/io/adminshell/aas/v3/dataformat/aml/deserialization/AasTypeFactory.java

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public <T> void useImplementation(Class<T> aasInterface, Class<? extends T> impl
5050
* Creates a new instance for a given aasInterface. If the
5151
*
5252
* @param <T> type to create
53-
* @param aasInterface class to find instantiate
53+
* @param aasInterfaceType class to find instantiate
5454
* @return an instance of aasInterface
5555
* @throws NoSuchMethodException
5656
* @throws InstantiationException
@@ -59,19 +59,34 @@ public <T> void useImplementation(Class<T> aasInterface, Class<? extends T> impl
5959
* concrete type to instantiate could be found
6060
* @throws InvocationTargetException
6161
*/
62-
public <T> T newInstance(Class<T> aasInterface) throws NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
63-
if (aasInterface == null) {
62+
public <T> T newInstance(Class<T> aasInterfaceType) throws NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
63+
Constructor<?> constructor = getImplementationType(aasInterfaceType).getConstructor();
64+
constructor.setAccessible(true);
65+
return (T) constructor.newInstance();
66+
}
67+
68+
/**
69+
* Gets the concrete implementation type to use for a given type. If there
70+
* is no explicit type mapping and the provided type is concrete, the type
71+
* itself is returned.
72+
*
73+
* @param <T> type information
74+
* @param aasInterfaceType the type to find a concrete implementation type
75+
* for
76+
* @throws IllegalArgumentException if there is no type mapping for the
77+
* provided type and the type is not concrete
78+
* @return a concrete implementation type for the given type
79+
*/
80+
public <T> Class<? extends T> getImplementationType(Class<T> aasInterfaceType) {
81+
if (aasInterfaceType == null) {
6482
throw new IllegalArgumentException("aasInterface must be non-null");
6583
}
66-
Class<?> classToInstantiate = aasInterface;
67-
if (typeMapping.containsKey(aasInterface)) {
68-
classToInstantiate = typeMapping.get(aasInterface);
84+
if (typeMapping.containsKey(aasInterfaceType)) {
85+
return (Class<? extends T>) typeMapping.get(aasInterfaceType);
6986
}
70-
if (classToInstantiate.isInterface()) {
71-
throw new IllegalArgumentException("could not resolve type for interface " + classToInstantiate.getName());
87+
if (aasInterfaceType.isInterface()) {
88+
throw new IllegalArgumentException("could not resolve type for interface " + aasInterfaceType.getName());
7289
}
73-
Constructor<?> constructor = classToInstantiate.getConstructor();
74-
constructor.setAccessible(true);
75-
return (T) constructor.newInstance();
90+
return aasInterfaceType;
7691
}
7792
}

dataformat-aml/src/main/java/io/adminshell/aas/v3/dataformat/aml/deserialization/Aml2AasMapper.java

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,24 @@
1818
import io.adminshell.aas.v3.dataformat.aml.deserialization.mappers.AssetAdministrationShellEnvironmentMapper;
1919
import io.adminshell.aas.v3.dataformat.aml.AmlDeserializationConfig;
2020
import io.adminshell.aas.v3.dataformat.aml.AmlDocumentInfo;
21+
import io.adminshell.aas.v3.dataformat.aml.deserialization.mappers.AssetAdministrationShellMapper;
22+
import io.adminshell.aas.v3.dataformat.aml.deserialization.mappers.EnumMapper;
23+
import io.adminshell.aas.v3.dataformat.aml.deserialization.mappers.IdentifiableMapper;
24+
import io.adminshell.aas.v3.dataformat.aml.deserialization.mappers.LangStringCollectionMapper;
25+
import io.adminshell.aas.v3.dataformat.aml.deserialization.mappers.ReferableMapper;
26+
import io.adminshell.aas.v3.dataformat.aml.deserialization.mappers.ReferenceElementMapper;
27+
import io.adminshell.aas.v3.dataformat.aml.deserialization.mappers.ReferenceMapper;
28+
import io.adminshell.aas.v3.dataformat.aml.deserialization.mappers.RelationshipElementMapper;
2129
import io.adminshell.aas.v3.dataformat.aml.model.caex.CAEXFile;
30+
import io.adminshell.aas.v3.dataformat.aml.serialization.naming.AbstractClassNamingStrategy;
31+
import io.adminshell.aas.v3.dataformat.aml.serialization.naming.NumberingClassNamingStrategy;
32+
import io.adminshell.aas.v3.dataformat.aml.serialization.naming.PropertyNamingStrategy;
2233
import io.adminshell.aas.v3.dataformat.mapping.MappingException;
2334
import io.adminshell.aas.v3.dataformat.mapping.MappingProvider;
2435
import io.adminshell.aas.v3.model.AssetAdministrationShellEnvironment;
36+
import io.adminshell.aas.v3.model.Identifiable;
37+
import io.adminshell.aas.v3.model.MultiLanguageProperty;
38+
import io.adminshell.aas.v3.model.Referable;
2539
import java.util.List;
2640

2741
/**
@@ -49,9 +63,24 @@ public AssetAdministrationShellEnvironment map(CAEXFile aml) throws MappingExcep
4963
AmlParser parser = new AmlParser(aml);
5064
MappingProvider mappingProvider = new MappingProvider(Mapper.class, new DefaultMapper(), new DefaultMapper());
5165
mappingProvider.register(new AssetAdministrationShellEnvironmentMapper());
52-
MappingContext context = new MappingContext(mappingProvider, config.getTypeFactory());
66+
mappingProvider.register(new EnumMapper());
67+
mappingProvider.register(new ReferenceMapper());
68+
mappingProvider.register(new AssetAdministrationShellMapper());
69+
mappingProvider.register(new LangStringCollectionMapper());
70+
mappingProvider.register(new RelationshipElementMapper());
71+
mappingProvider.register(new ReferenceElementMapper());
72+
mappingProvider.register(new ReferableMapper<Referable>());
73+
mappingProvider.register(new IdentifiableMapper<Identifiable>());
74+
AbstractClassNamingStrategy classNamingStrategy = new NumberingClassNamingStrategy();
75+
76+
PropertyNamingStrategy propertyNamingStrategy = new PropertyNamingStrategy();
77+
propertyNamingStrategy.registerCustomNaming(Referable.class, "descriptions", "description", true);
78+
propertyNamingStrategy.registerCustomNaming(MultiLanguageProperty.class, "values", "value", true);
79+
// propertyNamingStrategy.registerCustomNaming(Qualifier.class, x -> "qualifier:" + x.getType() + "=" + x.getValue(), false);
80+
MappingContext context = new MappingContext(mappingProvider, classNamingStrategy, propertyNamingStrategy, config.getTypeFactory());
5381
context.setDocumentInfo(AmlDocumentInfo.fromFile(aml));
54-
Object result = context.getMappingProvider().getMapper(AssetAdministrationShellEnvironment.class).map(parser, context);
82+
AssetAdministrationShellEnvironment result = (AssetAdministrationShellEnvironment) context.getMappingProvider().getMapper(AssetAdministrationShellEnvironment.class).map(parser, context);
83+
parser.resolveIdsToReferences(result);
5584
return (AssetAdministrationShellEnvironment) result;
5685
}
5786
}

dataformat-aml/src/main/java/io/adminshell/aas/v3/dataformat/aml/deserialization/AmlParser.java

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,30 @@
1717

1818
import io.adminshell.aas.v3.dataformat.aml.model.caex.CAEXFile;
1919
import io.adminshell.aas.v3.dataformat.aml.model.caex.CAEXObject;
20+
import io.adminshell.aas.v3.dataformat.core.util.AasUtils;
21+
import io.adminshell.aas.v3.dataformat.mapping.MappingException;
22+
import io.adminshell.aas.v3.model.AssetAdministrationShellEnvironment;
23+
import io.adminshell.aas.v3.model.Reference;
24+
import java.beans.PropertyDescriptor;
25+
import java.lang.reflect.InvocationTargetException;
26+
import java.util.ArrayList;
27+
import java.util.Collection;
28+
import java.util.HashMap;
29+
import java.util.List;
30+
import java.util.Map;
31+
import java.util.stream.Collectors;
2032

2133
/**
2234
* Wraps an AML file and provides a pointer to the current position within that
2335
* file
2436
*
25-
* @author jab
2637
*/
2738
public class AmlParser {
2839

2940
private final CAEXFile content;
3041
private CAEXObject current;
42+
private final Map<String, Reference> idToReference = new HashMap<>();
43+
private final Map<ObjectLocationByProperty, List<String>> idsToResolve = new HashMap<>();
3144

3245
public AmlParser(CAEXFile content) {
3346
this.content = content;
@@ -61,4 +74,107 @@ public void setCurrent(CAEXObject current) {
6174
this.current = current;
6275
}
6376

77+
/**
78+
* Saves mapping between AML-based ID and a AAS Reference that is needed
79+
* later on to resolve internalLinks within the AML document. This should
80+
* always be called when creating/reading a Referable from AML.
81+
*
82+
* @param amlId AML-based ID of the element/Referable
83+
* @param reference AAS reference to the Referable
84+
*/
85+
public void collectIdMapping(String amlId, Reference reference) {
86+
idToReference.put(amlId, reference);
87+
}
88+
89+
/**
90+
* Marks a property to contain an AAS Reference which at time of mapping may
91+
* not yet be available.
92+
*
93+
* @param parent parent AAS object that contains the property
94+
* @param property property that should contain the resolved reference(s)
95+
* later. Its type must be either Reference or collection of Reference
96+
* @param id AML-based ID of the targeted Referabe that should later be
97+
* resolved to a Reference
98+
*/
99+
public void resolveIdToReferenceLater(Object parent, PropertyDescriptor property, String id) {
100+
ObjectLocationByProperty location = new ObjectLocationByProperty(parent, property);
101+
if (!idsToResolve.containsKey(location)) {
102+
idsToResolve.put(location, new ArrayList<>());
103+
}
104+
idsToResolve.get(location).add(id);
105+
}
106+
107+
/**
108+
* Resolves properties containing References expressed through AML-based IDs
109+
* which have been previously makred by calling
110+
* resolveIdToReferenceLater(..)
111+
*
112+
* @param environment the environment to apply this resolution to
113+
* @throws MappingException when resolving fails, e.g. because cardinalities
114+
* do not match or AML IDs cannot be resolved
115+
*/
116+
public void resolveIdsToReferences(AssetAdministrationShellEnvironment environment) throws MappingException {
117+
for (Map.Entry<ObjectLocationByProperty, List<String>> entry : idsToResolve.entrySet()) {
118+
Object value = null;
119+
Class<?> acceptedType = null;
120+
List<String> idsToResolve = entry.getValue().stream().filter(x -> x != null).collect(Collectors.toList());
121+
if (!idsToResolve.isEmpty()) {
122+
if (Collection.class.isAssignableFrom(entry.getKey().property.getReadMethod().getReturnType())) {
123+
acceptedType = AasUtils.getCollectionContentType(entry.getKey().property.getReadMethod().getGenericReturnType());
124+
Collection<Reference> collection = new ArrayList<>();
125+
for (String id : idsToResolve) {
126+
if (!idToReference.containsKey(id)) {
127+
throw new MappingException(String.format("error adding resolved references for property %s - "
128+
+ "found unresolved AML id %s",
129+
entry.getKey().property.getName(),
130+
id));
131+
}
132+
collection.add(idToReference.get(id));
133+
}
134+
value = collection;
135+
} else {
136+
acceptedType = entry.getKey().property.getReadMethod().getReturnType();
137+
if (idsToResolve.size() > 1) {
138+
throw new MappingException(String.format("error adding resolved references for property %s - found %d ids but expected zero or one",
139+
entry.getKey().property.getName(),
140+
idsToResolve.size()));
141+
}
142+
if (!idToReference.containsKey(idsToResolve.get(0))) {
143+
throw new MappingException(String.format("error adding resolved references for property %s - "
144+
+ "found unresolved AML id %s",
145+
entry.getKey().property.getName(),
146+
idsToResolve.get(0)));
147+
}
148+
value = idToReference.get(idsToResolve.get(0));
149+
}
150+
}
151+
if (value != null) {
152+
if (!Reference.class.isAssignableFrom(acceptedType)) {
153+
throw new MappingException(String.format("error adding resolved references for property %s - expected type Reference or Collection<Reference> but found %s",
154+
entry.getKey().property.getName(),
155+
acceptedType));
156+
}
157+
}
158+
try {
159+
entry.getKey().property.getWriteMethod().invoke(entry.getKey().parent, value);
160+
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
161+
throw new MappingException("error resolving references", ex);
162+
}
163+
}
164+
}
165+
166+
private class ObjectLocationByProperty {
167+
168+
Object parent;
169+
PropertyDescriptor property;
170+
171+
private ObjectLocationByProperty(Object parent, PropertyDescriptor property) {
172+
this.parent = parent;
173+
this.property = property;
174+
}
175+
}
176+
177+
public Map<String, Reference> getIdToReference() {
178+
return idToReference;
179+
}
64180
}

0 commit comments

Comments
 (0)