
io.micronaut.inject.annotation.DefaultAnnotationMetadata Maven / Gradle / Ivy
/*
* Copyright 2017-2018 original authors
*
* 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.
*/
package io.micronaut.inject.annotation;
import io.micronaut.core.annotation.*;
import io.micronaut.core.annotation.AnnotationValue;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.core.convert.TypeConverter;
import io.micronaut.core.convert.value.ConvertibleValues;
import io.micronaut.core.reflect.ClassLoadingReporter;
import io.micronaut.core.reflect.ClassUtils;
import io.micronaut.core.type.Argument;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.StringUtils;
import io.micronaut.core.value.OptionalValues;
import javax.annotation.Nullable;
import java.lang.annotation.Annotation;
import java.lang.annotation.Repeatable;
import java.lang.reflect.Array;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* Default implementation of {@link AnnotationMetadata}.
*
*
* NOTE: Although required to be public This is an internal class and should not be referenced directly in user code
*
*
* @author Graeme Rocher
* @since 1.0
*/
@Internal
public class DefaultAnnotationMetadata extends AbstractAnnotationMetadata implements AnnotationMetadata, Cloneable {
static {
ConversionService.SHARED.addConverter(io.micronaut.core.annotation.AnnotationValue.class, Annotation.class, (TypeConverter) (object, targetType, context) -> {
Optional annotationClass = ClassUtils.forName(object.getAnnotationName(), targetType.getClassLoader());
return annotationClass.map(aClass -> AnnotationMetadataSupport.buildAnnotation(aClass, ConvertibleValues.of(object.getValues())));
});
ConversionService.SHARED.addConverter(io.micronaut.core.annotation.AnnotationValue[].class, Object[].class, (TypeConverter) (object, targetType, context) -> {
List result = new ArrayList();
Class annotationClass = null;
for (io.micronaut.core.annotation.AnnotationValue annotationValue : object) {
if (annotationClass == null) {
// all annotations will be on the same type
Optional aClass = ClassUtils.forName(annotationValue.getAnnotationName(), targetType.getClassLoader());
if (!aClass.isPresent()) {
break;
}
annotationClass = aClass.get();
}
Annotation annotation = AnnotationMetadataSupport.buildAnnotation(annotationClass, ConvertibleValues.of(annotationValue.getValues()));
result.add(annotation);
}
if (!result.isEmpty()) {
return Optional.of(result.toArray((Object[]) Array.newInstance(annotationClass, result.size())));
}
return Optional.empty();
});
}
Map> declaredAnnotations;
Map> allAnnotations;
Map> declaredStereotypes;
Map> allStereotypes;
Map> annotationsByStereotype;
private Map annotationValuesByType = new ConcurrentHashMap<>(2);
// should not be used in any of the read methods
// The following fields are used only at compile time, and
private Map> annotationDefaultValues;
private Map repeated = null;
/**
* Constructs empty annotation metadata.
*/
@Internal
protected DefaultAnnotationMetadata() {
}
/**
* This constructor is designed to be used by compile time produced subclasses.
*
* @param declaredAnnotations The directly declared annotations
* @param declaredStereotypes The directly declared stereotypes
* @param allStereotypes All of the stereotypes
* @param allAnnotations All of the annotations
* @param annotationsByStereotype The annotations by stereotype
*/
@Internal
@UsedByGeneratedCode
public DefaultAnnotationMetadata(
@Nullable Map> declaredAnnotations,
@Nullable Map> declaredStereotypes,
@Nullable Map> allStereotypes,
@Nullable Map> allAnnotations,
@Nullable Map> annotationsByStereotype) {
super(declaredAnnotations, allAnnotations);
this.declaredAnnotations = declaredAnnotations;
this.declaredStereotypes = declaredStereotypes;
this.allStereotypes = allStereotypes;
this.allAnnotations = allAnnotations;
this.annotationsByStereotype = annotationsByStereotype;
if (ClassLoadingReporter.isReportingEnabled()) {
if (allAnnotations != null) {
for (String annotationName : allAnnotations.keySet()) {
ClassUtils.forName(annotationName, DefaultAnnotationMetadata.class.getClassLoader()).ifPresent(ClassLoadingReporter::reportPresent);
}
}
}
}
@Override
public Optional getValue(Class extends Annotation> annotation, String member, Class requiredType) {
final Repeatable repeatable = annotation.getAnnotation(Repeatable.class);
final boolean isRepeatable = repeatable != null;
if (isRepeatable) {
List extends AnnotationValue extends Annotation>> values = getAnnotationValuesByType(annotation);
if (!values.isEmpty()) {
return values.iterator().next().get(member, requiredType);
} else {
return Optional.empty();
}
} else {
return getValue(annotation.getName(), member, requiredType);
}
}
@Override
public Optional getValue(String annotation, String member, Argument requiredType) {
Optional resolved = Optional.empty();
if (allAnnotations != null && StringUtils.isNotEmpty(annotation)) {
Map values = allAnnotations.get(annotation);
if (values != null) {
resolved = ConversionService.SHARED.convert(
values.get(member), requiredType
);
} else if (allStereotypes != null) {
values = allStereotypes.get(annotation);
if (values != null) {
resolved = ConversionService.SHARED.convert(
values.get(member), requiredType
);
}
}
}
if (!resolved.isPresent()) {
if (hasStereotype(annotation)) {
return getDefaultValue(annotation, member, requiredType);
}
}
return resolved;
}
@Override
public Optional getDefaultValue(String annotation, String member, Class requiredType) {
Map defaultValues = AnnotationMetadataSupport.getDefaultValues(annotation);
if (defaultValues.containsKey(member)) {
return ConversionService.SHARED.convert(defaultValues.get(member), requiredType);
}
return Optional.empty();
}
@SuppressWarnings("unchecked")
@Override
public List> getAnnotationValuesByType(Class annotationType) {
if (annotationType != null) {
List> results = annotationValuesByType.get(annotationType);
if (results == null) {
results = resolveAnnotationValuesByType(annotationType, allAnnotations, allStereotypes);
if (results != null) {
return results;
}
results = Collections.emptyList();
annotationValuesByType.put(annotationType, results);
}
return results;
}
return Collections.emptyList();
}
@Override
public List> getDeclaredAnnotationValuesByType(Class annotationType) {
if (annotationType != null) {
Map> sourceAnnotations = this.declaredAnnotations;
Map> sourceStereotypes = this.declaredStereotypes;
List> results = resolveAnnotationValuesByType(annotationType, sourceAnnotations, sourceStereotypes);
if (results != null) {
return results;
}
}
return Collections.emptyList();
}
@SuppressWarnings("unchecked")
@Override
public T[] synthesizeAnnotationsByType(Class annotationClass) {
if (annotationClass != null) {
List> values = getAnnotationValuesByType(annotationClass);
return values.stream()
.map(entries -> AnnotationMetadataSupport.buildAnnotation(annotationClass, entries.getConvertibleValues()))
.toArray(value -> (T[]) Array.newInstance(annotationClass, value));
}
//noinspection unchecked
return (T[]) AnnotationUtil.ZERO_ANNOTATIONS;
}
@Override
public T[] synthesizeDeclaredAnnotationsByType(Class annotationClass) {
if (annotationClass != null) {
List> values = getAnnotationValuesByType(annotationClass);
return values.stream()
.map(entries -> AnnotationMetadataSupport.buildAnnotation(annotationClass, entries.getConvertibleValues()))
.toArray(value -> (T[]) Array.newInstance(annotationClass, value));
}
//noinspection unchecked
return (T[]) AnnotationUtil.ZERO_ANNOTATIONS;
}
@Override
public boolean isEmpty() {
return allAnnotations == null || allAnnotations.isEmpty();
}
@Override
public boolean hasDeclaredAnnotation(String annotation) {
return declaredAnnotations != null && StringUtils.isNotEmpty(annotation) && declaredAnnotations.containsKey(annotation);
}
@Override
public boolean hasAnnotation(String annotation) {
return hasDeclaredAnnotation(annotation) || (allAnnotations != null && StringUtils.isNotEmpty(annotation) && allAnnotations.keySet().contains(annotation));
}
@Override
public boolean hasStereotype(String annotation) {
return hasAnnotation(annotation) || (allStereotypes != null && StringUtils.isNotEmpty(annotation) && allStereotypes.keySet().contains(annotation));
}
@Override
public boolean hasDeclaredStereotype(String annotation) {
return hasDeclaredAnnotation(annotation) || (declaredStereotypes != null && StringUtils.isNotEmpty(annotation) && declaredStereotypes.containsKey(annotation));
}
@Override
public List getAnnotationNamesByStereotype(String stereotype) {
if (annotationsByStereotype != null) {
List annotations = annotationsByStereotype.get(stereotype);
if (annotations != null) {
return Collections.unmodifiableList(annotations);
}
}
if (allAnnotations != null && allAnnotations.containsKey(stereotype)) {
return StringUtils.internListOf(stereotype);
}
if (declaredAnnotations != null && declaredAnnotations.containsKey(stereotype)) {
return StringUtils.internListOf(stereotype);
}
return Collections.emptyList();
}
@Override
public Set getAnnotationNames() {
if (allAnnotations != null) {
return allAnnotations.keySet();
}
return Collections.emptySet();
}
@Override
public Set getDeclaredAnnotationNames() {
if (declaredAnnotations != null) {
return declaredAnnotations.keySet();
}
return Collections.emptySet();
}
@Override
public List getDeclaredAnnotationNamesByStereotype(String stereotype) {
if (annotationsByStereotype != null) {
List annotations = annotationsByStereotype.get(stereotype);
if (annotations != null) {
annotations = new ArrayList<>(annotations);
if (declaredAnnotations != null) {
annotations.removeIf(s -> !declaredAnnotations.containsKey(s));
return Collections.unmodifiableList(annotations);
} else {
// no declared
return Collections.emptyList();
}
}
}
if (declaredAnnotations != null && declaredAnnotations.containsKey(stereotype)) {
return StringUtils.internListOf(stereotype);
}
return Collections.emptyList();
}
@Override
public Optional> getAnnotationType(String name) {
return AnnotationMetadataSupport.getAnnotationType(name);
}
@SuppressWarnings("Duplicates")
@Override
public Optional> findAnnotation(String annotation) {
if (allAnnotations != null && StringUtils.isNotEmpty(annotation)) {
Map values = allAnnotations.get(annotation);
if (values != null) {
return Optional.of(new AnnotationValue<>(annotation, values, AnnotationMetadataSupport.getDefaultValues(annotation)));
} else if (allStereotypes != null) {
values = allStereotypes.get(annotation);
if (values != null) {
return Optional.of(new AnnotationValue<>(annotation, values, AnnotationMetadataSupport.getDefaultValues(annotation)));
}
}
}
return Optional.empty();
}
@SuppressWarnings("Duplicates")
@Override
public Optional> findDeclaredAnnotation(String annotation) {
if (declaredAnnotations != null && StringUtils.isNotEmpty(annotation)) {
Map values = declaredAnnotations.get(annotation);
if (values != null) {
return Optional.of(new AnnotationValue<>(annotation, values, AnnotationMetadataSupport.getDefaultValues(annotation)));
} else if (declaredStereotypes != null) {
values = declaredStereotypes.get(annotation);
if (values != null) {
return Optional.of(new AnnotationValue<>(annotation, values, AnnotationMetadataSupport.getDefaultValues(annotation)));
}
}
}
return Optional.empty();
}
@Override
public OptionalValues getValues(String annotation, Class valueType) {
if (allAnnotations != null && StringUtils.isNotEmpty(annotation)) {
Map values = allAnnotations.get(annotation);
if (values != null) {
return OptionalValues.of(valueType, values);
} else {
values = allStereotypes.get(annotation);
if (values != null) {
return OptionalValues.of(valueType, values);
}
}
}
return OptionalValues.empty();
}
@Override
public Optional getDefaultValue(String annotation, String member, Argument requiredType) {
// Note this method should never reference the "annotationDefaultValues" field, which is used only at compile time
Map defaultValues = AnnotationMetadataSupport.getDefaultValues(annotation);
if (defaultValues.containsKey(member)) {
return ConversionService.SHARED.convert(defaultValues.get(member), requiredType);
}
return Optional.empty();
}
@Override
public AnnotationMetadata clone() {
return new DefaultAnnotationMetadata(
declaredAnnotations != null ? new HashMap<>(declaredAnnotations) : null,
declaredStereotypes != null ? new HashMap<>(declaredStereotypes) : null,
allStereotypes != null ? new HashMap<>(allStereotypes) : null,
allAnnotations != null ? new HashMap<>(allAnnotations) : null,
annotationsByStereotype != null ? new HashMap<>(annotationsByStereotype) : null
);
}
/**
* Adds an annotation and its member values, if the annotation already exists the data will be merged with existing
* values replaced.
*
* @param annotation The annotation
* @param values The values
*/
@SuppressWarnings("WeakerAccess")
protected final void addAnnotation(String annotation, Map values) {
if (annotation != null) {
String repeatedName = getRepeatedName(annotation);
if (repeatedName != null) {
Object v = values.get(AnnotationMetadata.VALUE_MEMBER);
if (v instanceof io.micronaut.core.annotation.AnnotationValue[]) {
io.micronaut.core.annotation.AnnotationValue[] avs = (io.micronaut.core.annotation.AnnotationValue[]) v;
for (io.micronaut.core.annotation.AnnotationValue av : avs) {
addRepeatable(annotation, av);
}
} else if (v instanceof Iterable) {
Iterable i = (Iterable) v;
for (Object o : i) {
if (o instanceof io.micronaut.core.annotation.AnnotationValue) {
addRepeatable(annotation, ((io.micronaut.core.annotation.AnnotationValue) o));
}
}
}
} else {
Map> allAnnotations = getAllAnnotations();
addAnnotation(annotation, values, null, allAnnotations, false);
}
}
}
/**
* Adds an annotation directly declared on the element and its member values, if the annotation already exists the
* data will be merged with existing values replaced.
*
* @param annotation The annotation
* @param values The values
*/
protected final void addDefaultAnnotationValues(String annotation, Map values) {
if (annotation != null) {
Map> annotationDefaults = this.annotationDefaultValues;
if (annotationDefaults == null) {
this.annotationDefaultValues = new HashMap<>();
annotationDefaults = this.annotationDefaultValues;
}
putValues(annotation, values, annotationDefaults);
}
}
/**
* Returns whether annotation defaults are registered for the give annotation. Used by generated byte code. DO NOT REMOVE.
*
* @param annotation The annotation name
* @return True if defaults have already been registered
*/
@SuppressWarnings("unused")
@Internal
@UsedByGeneratedCode
protected static boolean areAnnotationDefaultsRegistered(String annotation) {
return AnnotationMetadataSupport.hasDefaultValues(annotation);
}
/**
* Registers annotation default values. Used by generated byte code. DO NOT REMOVE.
*
* @param annotation The annotation name
* @param defaultValues The default values
*/
@SuppressWarnings("unused")
@Internal
@UsedByGeneratedCode
protected static void registerAnnotationDefaults(String annotation, Map defaultValues) {
AnnotationMetadataSupport.registerDefaultValues(annotation, defaultValues);
}
/**
* Registers annotation default values. Used by generated byte code. DO NOT REMOVE.
*
* @param annotation The annotation name
* @param defaultValues The default values
*/
@SuppressWarnings("unused")
@Internal
@UsedByGeneratedCode
protected static void registerAnnotationDefaults(AnnotationClassValue> annotation, Map defaultValues) {
AnnotationMetadataSupport.registerDefaultValues(annotation, defaultValues);
}
/**
* Adds a repeatable annotation value. If a value already exists will be added
*
* @param annotationName The annotation name
* @param annotationValue The annotation value
*/
protected final void addRepeatable(String annotationName, io.micronaut.core.annotation.AnnotationValue annotationValue) {
if (StringUtils.isNotEmpty(annotationName) && annotationValue != null) {
Map> allAnnotations = getAllAnnotations();
addRepeatableInternal(annotationName, annotationValue, allAnnotations);
}
}
/**
* Adds a repeatable stereotype value. If a value already exists will be added
*
* @param parents The parent annotations
* @param stereotype The annotation name
* @param annotationValue The annotation value
*/
protected void addRepeatableStereotype(List parents, String stereotype, io.micronaut.core.annotation.AnnotationValue annotationValue) {
Map> allStereotypes = getAllStereotypes();
List annotationList = getAnnotationsByStereotypeInternal(stereotype);
for (String parentAnnotation : parents) {
if (!annotationList.contains(parentAnnotation)) {
annotationList.add(parentAnnotation);
}
}
addRepeatableInternal(stereotype, annotationValue, allStereotypes);
}
/**
* Adds a repeatable declared stereotype value. If a value already exists will be added
*
* @param parents The parent annotations
* @param stereotype The annotation name
* @param annotationValue The annotation value
*/
protected void addDeclaredRepeatableStereotype(List parents, String stereotype, io.micronaut.core.annotation.AnnotationValue annotationValue) {
Map> declaredStereotypes = getDeclaredStereotypesInternal();
List annotationList = getAnnotationsByStereotypeInternal(stereotype);
for (String parentAnnotation : parents) {
if (!annotationList.contains(parentAnnotation)) {
annotationList.add(parentAnnotation);
}
}
addRepeatableInternal(stereotype, annotationValue, declaredStereotypes);
addRepeatableInternal(stereotype, annotationValue, getAllStereotypes());
}
/**
* Adds a repeatable annotation value. If a value already exists will be added
*
* @param annotationName The annotation name
* @param annotationValue The annotation value
*/
protected final void addDeclaredRepeatable(String annotationName, io.micronaut.core.annotation.AnnotationValue annotationValue) {
if (StringUtils.isNotEmpty(annotationName) && annotationValue != null) {
Map> allAnnotations = getDeclaredAnnotationsInternal();
addRepeatableInternal(annotationName, annotationValue, allAnnotations);
addRepeatable(annotationName, annotationValue);
}
}
/**
* Adds a stereotype and its member values, if the annotation already exists the data will be merged with existing
* values replaced.
*
* @param parentAnnotations The parent annotations
* @param stereotype The annotation
* @param values The values
*/
@SuppressWarnings("WeakerAccess")
protected final void addStereotype(List parentAnnotations, String stereotype, Map values) {
if (stereotype != null) {
String repeatedName = getRepeatedName(stereotype);
if (repeatedName != null) {
Object v = values.get(AnnotationMetadata.VALUE_MEMBER);
if (v instanceof io.micronaut.core.annotation.AnnotationValue[]) {
io.micronaut.core.annotation.AnnotationValue[] avs = (io.micronaut.core.annotation.AnnotationValue[]) v;
for (io.micronaut.core.annotation.AnnotationValue av : avs) {
addRepeatableStereotype(parentAnnotations, stereotype, av);
}
} else if (v instanceof Iterable) {
Iterable i = (Iterable) v;
for (Object o : i) {
if (o instanceof io.micronaut.core.annotation.AnnotationValue) {
addRepeatableStereotype(parentAnnotations, stereotype, (io.micronaut.core.annotation.AnnotationValue) o);
}
}
}
} else {
Map> allStereotypes = getAllStereotypes();
List annotationList = getAnnotationsByStereotypeInternal(stereotype);
for (String parentAnnotation : parentAnnotations) {
if (!annotationList.contains(parentAnnotation)) {
annotationList.add(parentAnnotation);
}
}
// add to stereotypes
addAnnotation(
stereotype,
values,
null,
allStereotypes,
false
);
}
}
}
/**
* Adds a stereotype and its member values, if the annotation already exists the data will be merged with existing
* values replaced.
*
* @param parentAnnotations The parent annotations
* @param stereotype The annotation
* @param values The values
*/
@SuppressWarnings("WeakerAccess")
protected final void addDeclaredStereotype(List parentAnnotations, String stereotype, Map values) {
if (stereotype != null) {
String repeatedName = getRepeatedName(stereotype);
if (repeatedName != null) {
Object v = values.get(AnnotationMetadata.VALUE_MEMBER);
if (v instanceof io.micronaut.core.annotation.AnnotationValue[]) {
io.micronaut.core.annotation.AnnotationValue[] avs = (io.micronaut.core.annotation.AnnotationValue[]) v;
for (io.micronaut.core.annotation.AnnotationValue av : avs) {
addDeclaredRepeatableStereotype(parentAnnotations, stereotype, av);
}
} else if (v instanceof Iterable) {
Iterable i = (Iterable) v;
for (Object o : i) {
if (o instanceof io.micronaut.core.annotation.AnnotationValue) {
addDeclaredRepeatableStereotype(parentAnnotations, stereotype, (io.micronaut.core.annotation.AnnotationValue) o);
}
}
}
} else {
Map> declaredStereotypes = getDeclaredStereotypesInternal();
Map> allStereotypes = getAllStereotypes();
List annotationList = getAnnotationsByStereotypeInternal(stereotype);
for (String parentAnnotation : parentAnnotations) {
if (!annotationList.contains(parentAnnotation)) {
annotationList.add(parentAnnotation);
}
}
addAnnotation(
stereotype,
values,
declaredStereotypes,
allStereotypes,
true
);
}
}
}
/**
* Adds an annotation directly declared on the element and its member values, if the annotation already exists the
* data will be merged with existing values replaced.
*
* @param annotation The annotation
* @param values The values
*/
protected void addDeclaredAnnotation(String annotation, Map values) {
if (annotation != null) {
String repeatedName = getRepeatedName(annotation);
if (repeatedName != null) {
Object v = values.get(AnnotationMetadata.VALUE_MEMBER);
if (v instanceof io.micronaut.core.annotation.AnnotationValue[]) {
io.micronaut.core.annotation.AnnotationValue[] avs = (io.micronaut.core.annotation.AnnotationValue[]) v;
for (io.micronaut.core.annotation.AnnotationValue av : avs) {
addDeclaredRepeatable(annotation, av);
}
} else if (v instanceof Iterable) {
Iterable i = (Iterable) v;
for (Object o : i) {
if (o instanceof io.micronaut.core.annotation.AnnotationValue) {
addDeclaredRepeatable(annotation, ((io.micronaut.core.annotation.AnnotationValue) o));
}
}
}
} else {
Map> declaredAnnotations = getDeclaredAnnotationsInternal();
Map> allAnnotations = getAllAnnotations();
addAnnotation(annotation, values, declaredAnnotations, allAnnotations, true);
}
}
}
/**
* Dump the values.
*/
@SuppressWarnings("unused")
@Internal
void dump() {
System.out.println("declaredAnnotations = " + declaredAnnotations);
System.out.println("declaredStereotypes = " + declaredStereotypes);
System.out.println("allAnnotations = " + allAnnotations);
System.out.println("allStereotypes = " + allStereotypes);
System.out.println("annotationsByStereotype = " + annotationsByStereotype);
}
private List> resolveAnnotationValuesByType(Class annotationType, Map> sourceAnnotations, Map> sourceStereotypes) {
Repeatable repeatable = annotationType.getAnnotation(Repeatable.class);
if (repeatable != null) {
Class extends Annotation> repeatableType = repeatable.value();
if (hasStereotype(repeatableType)) {
List> results = new ArrayList<>();
if (sourceAnnotations != null) {
Map values = sourceAnnotations.get(repeatableType.getName());
addAnnotationValuesFromData(results, values);
}
if (sourceStereotypes != null) {
Map values = sourceStereotypes.get(repeatableType.getName());
addAnnotationValuesFromData(results, values);
}
return results;
}
}
return null;
}
private void addAnnotation(String annotation,
Map values,
Map> declaredAnnotations, Map> allAnnotations,
boolean isDeclared) {
if (isDeclared && declaredAnnotations != null) {
putValues(annotation, values, declaredAnnotations);
}
putValues(annotation, values, allAnnotations);
}
private void putValues(String annotation, Map values, Map> currentAnnotationValues) {
Map existing = currentAnnotationValues.get(annotation);
boolean hasValues = CollectionUtils.isNotEmpty(values);
if (existing != null && hasValues) {
if (existing.isEmpty()) {
existing = new LinkedHashMap<>();
currentAnnotationValues.put(annotation, existing);
}
for (CharSequence key : values.keySet()) {
if (!existing.containsKey(key)) {
existing.put(key, values.get(key));
}
}
} else {
if (!hasValues) {
existing = existing == null ? Collections.emptyMap() : existing;
} else {
existing = new LinkedHashMap<>(values.size());
existing.putAll(values);
}
currentAnnotationValues.put(annotation, existing);
}
}
private ConvertibleValues
© 2015 - 2025 Weber Informatics LLC | Privacy Policy