Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions checkstyle-for-deprecated-guice.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,18 @@
</module>
<module name="RegexpSingleline">
<property name="format" value=".*jakarta\.inject.*" />
<property name="fileExtensions" value="java"/>
<property name="message" value="The use of 'jakarta.inject' is forbidden in Rune model code." />
<property name="severity" value="error"/>
</module>
<module name="RegexpSingleline">
<property name="format" value="Licensed under the Apache License, Version 2\.0" />
<property name="fileExtensions" value="java,xml"/>
<property name="minimum" value="1" />
<property name="maximum" value="999" />
<property name="message" value="Missing Apache 2.0 license header. Run `mvn license:update-file-header` to fix." />
<property name="severity" value="error"/>
</module>
<module name="BeforeExecutionExclusionFileFilter">
<!-- Exclude any generated source folders -->
<property name="fileNamePattern" value=".*-gen[\\/].*$" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ private void buildSubstitutionLogicIndexes() {
}

public SubstitutionMap findSubstitutionMap(MapperConfig<?> config, AnnotatedMember member, ClassLoader classLoader) {
AnnotatedClass ac = getAnnotatedClassOrContent(config, member);
AnnotatedClass ac = RosettaXMLTypeConfigLookup.getAnnotatedClassOrContent(config, member);
RosettaDataType ann = ac.getAnnotation(RosettaDataType.class);

if (ann != null) {
Expand Down Expand Up @@ -294,7 +294,10 @@ public PropertyName findRootName(AnnotatedClass ac) {
// If the element name is specified in the XML configuration, use that.
// Otherwise, if the RosettaDataType annotation is present, use the name from that.
// Otherwise, use the default.
return getTypeXMLConfigurations(mapper.getSerializationConfig(), ac).stream()
return RosettaXMLTypeConfigLookup.getTypeXMLConfigurations(
rosettaXMLConfiguration,
mapper.getSerializationConfig(),
ac).stream()
.filter(t -> t.getXmlElementName().isPresent())
.map(t -> t.getXmlElementName().get())
.findFirst()
Expand Down Expand Up @@ -337,7 +340,7 @@ private boolean shouldUseDefaultPropertyName(MapperConfig<?> config, Annotated a

@Override
public void findAndAddVirtualProperties(MapperConfig<?> config, AnnotatedClass ac, List<BeanPropertyWriter> properties) {
getTypeXMLConfigurations(config, ac)
RosettaXMLTypeConfigLookup.getTypeXMLConfigurations(rosettaXMLConfiguration, config, ac)
.forEach(typeXMLConfiguration -> {
typeXMLConfiguration.getXmlAttributes().ifPresent(xmlAttributes -> {
// For each XML attribute in the configuration, add a virtual XML attribute.
Expand Down Expand Up @@ -495,37 +498,17 @@ public void findEnumAliases(MapperConfig<?> config, AnnotatedClass enumType,
}
}

private AnnotatedClass getEnclosingAnnotatedClass(MapperConfig<?> config, AnnotatedMember member) {
// TODO: see issue https://github.com/FasterXML/jackson-databind/issues/4141
return AnnotatedClassResolver.resolve(config, config.constructType(member.getDeclaringClass()), config);
}
private AnnotatedClass getAnnotatedClassOrContent(MapperConfig<?> config, AnnotatedMember m) {
JavaType t;
if (m instanceof AnnotatedMethod) {
AnnotatedMethod method = (AnnotatedMethod) m;
if (method.getParameterCount() == 1) {
// For setters
t = method.getParameterType(0);
} else {
t = method.getType();
}
} else {
t = m.getType();
}
if (t.getContentType() != null) {
t = t.getContentType();
}
return AnnotatedClassResolver.resolve(config, t, config);
}

private Optional<AttributeXMLConfiguration> getAttributeXMLConfiguration(MapperConfig<?> config, Annotated a) {
return Optional.of(a)
.filter(annotated -> annotated instanceof AnnotatedMember)
.map(annotated -> (AnnotatedMember) annotated)
.flatMap(annotatedMember ->
Optional.ofNullable(annotatedMember.getAnnotation(RosettaAttribute.class))
.flatMap(rosettaAttributeAnnotation ->
getTypeXMLConfigurations(config, getEnclosingAnnotatedClass(config, annotatedMember)).stream()
RosettaXMLTypeConfigLookup.getTypeXMLConfigurations(
rosettaXMLConfiguration,
config,
RosettaXMLTypeConfigLookup.getEnclosingAnnotatedClass(config, annotatedMember)).stream()
.filter(t -> t.getAttributes().isPresent())
.map(t -> t.getAttributes().get())
.filter(attrMap -> attrMap.containsKey(rosettaAttributeAnnotation.value()))
Expand All @@ -536,23 +519,6 @@ private Optional<AttributeXMLConfiguration> getAttributeXMLConfiguration(MapperC

}

private List<TypeXMLConfiguration> getTypeXMLConfigurations(MapperConfig<?> config, AnnotatedClass ac) {
List<TypeXMLConfiguration> result = new ArrayList<>();
Set<ModelSymbolId> visited = new HashSet<>();
RosettaDataType ann;
while ((ann = ac.getAnnotation(RosettaDataType.class)) != null) {
final ModelSymbolId modelSymbolId = createModelSymbolId(ac, ann.value());
if (visited.add(modelSymbolId)) {
rosettaXMLConfiguration.getConfigurationForType(modelSymbolId).ifPresent(result::add);
}

if (ac.getType().getSuperClass() == null) {
break;
}
ac = AnnotatedClassResolver.resolve(config, ac.getType().getSuperClass(), config);
}
return result;
}
private Optional<TypeXMLConfiguration> getEnumXMLConfigurations(MapperConfig<?> config, AnnotatedClass ac) {
RosettaEnum ann = ac.getAnnotation(RosettaEnum.class);
if (ann != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public void setupModule(SetupContext context) {
final SubstitutionMapLoader substitutionMapLoader = new SubstitutionMapLoader(classLoader);
context.addBeanSerializerModifier(new RosettaBeanSerializerModifier(substitutionMapLoader));
context.addBeanSerializerModifier(new XmlBeanSerializerModifier());
context.addBeanDeserializerModifier(new RosettaBeanDeserializerModifier(substitutionMapLoader));
context.addBeanDeserializerModifier(new RosettaBeanDeserializerModifier(substitutionMapLoader, rosettaXMLConfiguration));
context.addBeanDeserializerModifier(new XmlBeanDeserializerModifier(FromXmlParser.DEFAULT_UNNAMED_TEXT_PROPERTY));

context.insertAnnotationIntrospector(new RosettaXMLAnnotationIntrospector(mapper, rosettaXMLConfiguration, supportNativeEnumValue));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package com.regnosys.rosetta.common.serialisation.xml;

/*-
* ==============
* Rune Common
* ==============
* Copyright (C) 2018 - 2026 REGnosys
* ==============
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ==============
*/

import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
import com.fasterxml.jackson.databind.introspect.AnnotatedClassResolver;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.regnosys.rosetta.common.serialisation.xml.config.RosettaXMLConfiguration;
import com.regnosys.rosetta.common.serialisation.xml.config.TypeXMLConfiguration;
import com.rosetta.model.lib.ModelSymbolId;
import com.rosetta.model.lib.annotations.RosettaDataType;
import com.rosetta.util.DottedPath;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

public final class RosettaXMLTypeConfigLookup {

private RosettaXMLTypeConfigLookup() {
}

public static Optional<TypeXMLConfiguration> getTypeXMLConfiguration(RosettaXMLConfiguration rosettaXMLConfiguration,
MapperConfig<?> config,
BeanDescription beanDesc) {
return getTypeXMLConfigurations(rosettaXMLConfiguration, config, beanDesc.getClassInfo()).stream()
.findFirst();
}

public static List<TypeXMLConfiguration> getTypeXMLConfigurations(RosettaXMLConfiguration rosettaXMLConfiguration,
MapperConfig<?> config,
AnnotatedClass ac) {
List<TypeXMLConfiguration> result = new ArrayList<>();
Set<ModelSymbolId> visited = new HashSet<>();
RosettaDataType ann;
while ((ann = ac.getAnnotation(RosettaDataType.class)) != null) {
final ModelSymbolId modelSymbolId = createModelSymbolId(ac, ann.value());
if (visited.add(modelSymbolId)) {
rosettaXMLConfiguration.getConfigurationForType(modelSymbolId).ifPresent(result::add);
}

if (ac.getType().getSuperClass() == null) {
break;
}
ac = AnnotatedClassResolver.resolve(config, ac.getType().getSuperClass(), config);
}
return result;
}

public static AnnotatedClass getEnclosingAnnotatedClass(MapperConfig<?> config, AnnotatedMember member) {
// TODO: see issue https://github.com/FasterXML/jackson-databind/issues/4141
return AnnotatedClassResolver.resolve(config, config.constructType(member.getDeclaringClass()), config);
}

public static AnnotatedClass getAnnotatedClassOrContent(MapperConfig<?> config, AnnotatedMember member) {
JavaType t;
if (member instanceof AnnotatedMethod) {
AnnotatedMethod method = (AnnotatedMethod) member;
if (method.getParameterCount() == 1) {
// For setters
t = method.getParameterType(0);
} else {
t = method.getType();
}
} else {
t = member.getType();
}
if (t.getContentType() != null) {
t = t.getContentType();
}
return AnnotatedClassResolver.resolve(config, t, config);
}

private static ModelSymbolId createModelSymbolId(AnnotatedClass ac, String name) {
final String namespace = ac.getType().getRawClass().getPackage().getName();
return new ModelSymbolId(DottedPath.splitOnDots(namespace), name);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package com.regnosys.rosetta.common.serialisation.xml.config;

/*-
* ==============
* Rune Common
* ==============
* Copyright (C) 2018 - 2026 REGnosys
* ==============
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ==============
*/

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;

import java.util.Objects;
import java.util.Optional;

/**
* Immutable value type representing the {@code maxOccurs} bound of an XML content-model node.
*
* <p>Supports either a finite non-negative integer or the special "unbounded" value.
* Accepts JSON input as either a number ({@code 3}) or the string {@code "unbounded"}.</p>
*/
public final class OccursMax {

public static final String UNBOUNDED_TOKEN = "unbounded";

private static final OccursMax UNBOUNDED = new OccursMax(Optional.empty());

private final Optional<Integer> value;

private OccursMax(Optional<Integer> value) {
this.value = value;
}

public static OccursMax unbounded() {
return UNBOUNDED;
}

public static OccursMax of(int value) {
if (value < 0) {
throw new IllegalArgumentException("maxOccurs must be non-negative, was " + value);
}
return new OccursMax(Optional.of(value));
}

@JsonCreator
public static OccursMax fromJson(Object raw) {
if (raw == null) {
return null;
}
if (raw instanceof Number) {
return of(((Number) raw).intValue());
}
if (raw instanceof String) {
String text = ((String) raw).trim();
if (UNBOUNDED_TOKEN.equalsIgnoreCase(text)) {
return unbounded();
}
try {
return of(Integer.parseInt(text));
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid maxOccurs value: '" + raw + "'");
}
}
throw new IllegalArgumentException("Invalid maxOccurs value: " + raw);
}

@JsonValue
public Object toJson() {
return value.<Object>map(v -> v).orElse(UNBOUNDED_TOKEN);
}

public Optional<Integer> getValue() {
return value;
}

public boolean isUnbounded() {
return !value.isPresent();
}

/**
* Resolves this {@code maxOccurs} bound against the remaining input length.
* Returns the upper bound of how many times a node may be repeated.
*/
public int boundedValue(int inputRemaining) {
if (isUnbounded()) {
return inputRemaining;
}
return value.orElse(1);
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof OccursMax)) {
return false;
}
OccursMax other = (OccursMax) obj;
return Objects.equals(value, other.value);
}

@Override
public int hashCode() {
return Objects.hash(value);
}

@Override
public String toString() {
return value.map(String::valueOf).orElse(UNBOUNDED_TOKEN);
}
}
Loading
Loading