gpt4 book ai didi

maven - 使用 map 的 map 作为 Maven 插件参数

转载 作者:行者123 更新时间:2023-12-01 11:24:37 25 4
gpt4 key购买 nike

是否可以使用 map map 作为 Maven 插件参数?例如

@Parameter
private Map<String, Map<String, String>> converters;

然后像使用它一样
<converters>
<json>
<indent>true</indent>
<strict>true</strict>
</json>
<yaml>
<stripComments>false</stripComments>
</yaml>
<converters>

如果我这样使用它, converters仅包含键 jsonyaml以 null 作为值。

我知道可以将复杂对象作为值,但是是否也可以像本例中那样将映射用于可变元素值?

最佳答案

这显然是 sisu.plexus 的限制。 Mojo API 内部使用的项目。如果你偷看 MapConverter 源,您会发现它首先尝试通过尝试将配置解释为字符串(调用 fromExpression )来获取 map 的值,当失败时,looks up the expected type of the value .但是此方法不检查 parameterized types ,这是我们这里的例子(因为 map 值的类型是 Map<String, String> )。我提交了the bug 498757在这个项目的 Bugzilla 上进行跟踪。

使用自定义包装对象

一种解决方法是不使用 Map<String, String>作为值,但使用自定义对象:

@Parameter
private Map<String, Converter> converters;

与类 Converter ,位于与 Mojo 相同的包中,即:

public class Converter {

@Parameter
private Map<String, String> properties;

@Override
public String toString() { return properties.toString(); } // to test

}

然后,您可以使用以下命令配置您的 Mojo:
<converters>
<json>
<properties>
<indent>true</indent>
<strict>true</strict>
</properties>
</json>
<yaml>
<properties>
<stripComments>false</stripComments>
</properties>
</yaml>
</converters>

此配置将正确注入(inject)内部映射中的值。它还保留了可变方面:对象仅作为内部映射的包装器引入。我用一个简单的测试魔力测试了这个

public void execute() throws MojoExecutionException, MojoFailureException {
getLog().info(converters.toString());
}

并且输出是预期的 {json={indent=true, strict=true}, yaml={stripComments=false}} .

使用自定义配置器

我还找到了保留 Map<String, Map<String, String>> 的方法通过使用自定义 ComponentConfigurator .

所以我们要修复 MapConverter通过继承它,问题是如何注册这个新的 FixedMapConverter .默认情况下,Maven 使用 BasicComponentConfigurator配置 Mojo,它依赖于 DefaultConverterLookup查找用于特定类的转换器。在这种情况下,我们希望为 Map 提供自定义转换。这将返回我们的固定版本。因此,我们需要扩展这个基本配置器并注册我们的新转换器。

import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.component.configurator.BasicComponentConfigurator;
import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
import org.codehaus.plexus.component.configurator.ConfigurationListener;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
import org.codehaus.plexus.configuration.PlexusConfiguration;

public class CustomBasicComponentConfigurator extends BasicComponentConfigurator {
@Override
public void configureComponent(final Object component, final PlexusConfiguration configuration,
final ExpressionEvaluator evaluator, final ClassRealm realm, final ConfigurationListener listener)
throws ComponentConfigurationException {
converterLookup.registerConverter(new FixedMapConverter());
super.configureComponent(component, configuration, evaluator, realm, listener);
}
}

然后我们需要告诉 Maven 使用这个新的配置器而不是基本的配置器。这是一个两步过程:
  • 在您的 Maven 插件中,创建一个文件 src/main/resources/META-INF/plexus/components.xml注册新组件:
    <?xml version="1.0" encoding="UTF-8"?>
    <component-set>
    <components>
    <component>
    <role>org.codehaus.plexus.component.configurator.ComponentConfigurator</role>
    <role-hint>custom-basic</role-hint>
    <implementation>package.to.CustomBasicComponentConfigurator</implementation>
    </component>
    </components>
    </component-set>

    注意几点:我们声明了一个带有提示 "custom-basic" 的新组件。 , 这将作为一个 id 来引用它和 <implementation>指的是我们配置器的全限定类名。
  • 告诉我们的 Mojo 将此配置器与 configurator 一起使用@Mojo 的属性注解:

    @Mojo(name = "test", configurator = "custom-basic")

    此处传递的配置器对应于 components.xml 中指定的角色提示。多于。

  • 有了这样的设置,你终于可以声明

    @Parameter
    private Map<String, Map<String, String>> converters;

    一切都将被正确注入(inject):Maven 将使用我们的自定义配置器,它将注册我们固定版本的 map 转换器并正确转换内部 map 。
    FixedMapConverter的完整代码(这几乎是复制粘贴 MapConverter 因为我们不能覆盖错误的方法):

    public class FixedMapConverter extends MapConverter {

    public Object fromConfiguration(final ConverterLookup lookup, final PlexusConfiguration configuration,
    final Class<?> type, final Type[] typeArguments, final Class<?> enclosingType, final ClassLoader loader,
    final ExpressionEvaluator evaluator, final ConfigurationListener listener)
    throws ComponentConfigurationException {
    final Object value = fromExpression(configuration, evaluator, type);
    if (null != value) {
    return value;
    }
    try {
    final Map<Object, Object> map = instantiateMap(configuration, type, loader);
    final Class<?> elementType = findElementType(typeArguments);
    if (Object.class == elementType || String.class == elementType) {
    for (int i = 0, size = configuration.getChildCount(); i < size; i++) {
    final PlexusConfiguration element = configuration.getChild(i);
    map.put(element.getName(), fromExpression(element, evaluator));
    }
    return map;
    }
    // handle maps with complex element types...
    final ConfigurationConverter converter = lookup.lookupConverterForType(elementType);
    for (int i = 0, size = configuration.getChildCount(); i < size; i++) {
    Object elementValue;
    final PlexusConfiguration element = configuration.getChild(i);
    try {
    elementValue = converter.fromConfiguration(lookup, element, elementType, enclosingType, //
    loader, evaluator, listener);
    }
    // TEMP: remove when http://jira.codehaus.org/browse/MSHADE-168
    // is fixed
    catch (final ComponentConfigurationException e) {
    elementValue = fromExpression(element, evaluator);

    Logs.warn("Map in " + enclosingType + " declares value type as: {} but saw: {} at runtime",
    elementType, null != elementValue ? elementValue.getClass() : null);
    }
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    map.put(element.getName(), elementValue);
    }
    return map;
    } catch (final ComponentConfigurationException e) {
    if (null == e.getFailedConfiguration()) {
    e.setFailedConfiguration(configuration);
    }
    throw e;
    }
    }

    @SuppressWarnings("unchecked")
    private Map<Object, Object> instantiateMap(final PlexusConfiguration configuration, final Class<?> type,
    final ClassLoader loader) throws ComponentConfigurationException {
    final Class<?> implType = getClassForImplementationHint(type, configuration, loader);
    if (null == implType || Modifier.isAbstract(implType.getModifiers())) {
    return new TreeMap<Object, Object>();
    }

    final Object impl = instantiateObject(implType);
    failIfNotTypeCompatible(impl, type, configuration);
    return (Map<Object, Object>) impl;
    }

    private static Class<?> findElementType( final Type[] typeArguments )
    {
    if ( null != typeArguments && typeArguments.length > 1 )
    {
    if ( typeArguments[1] instanceof Class<?> )
    {
    return (Class<?>) typeArguments[1];
    }
    // begin fix here
    if ( typeArguments[1] instanceof ParameterizedType )
    {
    return (Class<?>) ((ParameterizedType) typeArguments[1]).getRawType();
    }
    // end fix here
    }
    return Object.class;
    }

    }

    关于maven - 使用 map 的 map 作为 Maven 插件参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38628399/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com