Java FreeMarker:使用宏时保持标识

Java FreeMarker:使用宏时保持标识,java,code-generation,freemarker,Java,Code Generation,Freemarker,我正在使用FreeMarker模板引擎从Web服务的抽象描述生成一些php类。我的问题是,当我在FreeMarker模板中调用宏时,宏会在调用宏之前插入没有左边空白的文本 示例Template.ftl: <?php class ${class.name} { <@docAsComment class.doc/> <#list class.fields as field> $${field.name};

我正在使用FreeMarker模板引擎从Web服务的抽象描述生成一些php类。我的问题是,当我在FreeMarker模板中调用宏时,宏会在调用宏之前插入没有左边空白的文本

示例Template.ftl:

<?php
    class ${class.name} {
        <@docAsComment class.doc/>

        <#list class.fields as field>
        $${field.name};
        </#list>
        <#-- ... -->
    }
?>

<#macro docAsComment doc>
/*
<#if doc.title != "">
* ${doc.title}
</#if>
<#list doc.content as content>
<#if content != ""> * ${content}</#if>
</#list>
*/
</#macro>

/*
*${doc.title}
*${content}
*/
这将生成如下内容:

<?php
    class foo {
/*
 * foo
 * bar foo, bla
 */          

    $a;
    $b;
    }
?>
<@docAsComment class.doc 1/>
<#macro docAsComment doc indent=1>
   <#local spc>${""?left_pad(indent * 4)}</#local>
${spc}/*
<#if doc.title != "">
${spc}* ${doc.title}
</#if>
<#list doc.content as content>
<#if content != "">${spc} * ${content}</#if>
</#list>
${spc}*/
</#macro>


一种解决方案是,将前导空格作为参数提交给宏,但这只会使模板更不可读。有更好的解决方案吗?

似乎总是在代码生成中相同的缩进级别调用
docAsComment
。可以将缩进烘焙到宏中

如果注释的缩进是可变的,则必须传入缩进级别。我不理解你关于模板更难阅读的评论。它确实使宏变得更复杂一些

调用如下所示:

<?php
    class foo {
/*
 * foo
 * bar foo, bla
 */          

    $a;
    $b;
    }
?>
<@docAsComment class.doc 1/>
<#macro docAsComment doc indent=1>
   <#local spc>${""?left_pad(indent * 4)}</#local>
${spc}/*
<#if doc.title != "">
${spc}* ${doc.title}
</#if>
<#list doc.content as content>
<#if content != "">${spc} * ${content}</#if>
</#list>
${spc}*/
</#macro>

宏将更改为以下内容:

<?php
    class foo {
/*
 * foo
 * bar foo, bla
 */          

    $a;
    $b;
    }
?>
<@docAsComment class.doc 1/>
<#macro docAsComment doc indent=1>
   <#local spc>${""?left_pad(indent * 4)}</#local>
${spc}/*
<#if doc.title != "">
${spc}* ${doc.title}
</#if>
<#list doc.content as content>
<#if content != "">${spc} * ${content}</#if>
</#list>
${spc}*/
</#macro>

${“?左焊盘(缩进*4)}
${spc}/*
${spc}*${doc.title}
${spc}*${content}
${spc}*/
还不错,真的。通过缩进宏,可以使其更易于阅读:

<#macro docAsComment doc indent=1>
    <#local spc>${""?left_pad(indent * 4)}</#local>
    ${spc}/*<#lt>
    <#if doc.title != "">
        ${spc}* ${doc.title}<#lt>
    </#if>
    <#list doc.content as content>
        <#if content != "">${spc} * ${content}</#if><#lt>
    </#list>
    ${spc}*/<#lt>
</#macro>

${“?左焊盘(缩进*4)}
${spc}/*
${spc}*${doc.title}
${spc}*${content}
${spc}*/

这类问题(动态缩进)的通用解决方案是一个过滤器,它(基本)理解您生成的语言(PHP)并重新缩进代码。您可以将该过滤器实现为包装实际输出的
编写器
。如果它观察
{
}
/*
*/
标记的位置(我不确定),可能就足够了

另一个更容易实现的解决方案是,通过实现
TemplateDirectiveModel
来创建自定义FreeMarker指令,该指令通过在每一行的开头添加或删除作为参数指定给它的空格量,过滤嵌套内容中生成的输出。然后你可以做一些类似的事情:

<@indent spaces=4>
   ...
</@indent>

...

使用此选项将使模板更加复杂,但仍然不像在每行中插入缩进那样嘈杂。

今天,可以使用
。政府对此有如下说法:

对于具有nt指令的单行,可以禁用空白剥离(对于不修剪)

根据,在以前的版本中,只包含FTL标记的行会被修剪,但
和自定义指令除外(如
)。但在V2.3中,他们改变了这种行为,总是修剪这样的线条。因此,在使用宏时,可以将
放在行上以防止修剪,从而保持缩进

<#macro test>
...<#t>
</#macro>

Example:
   - <@test /><#nt>
您可以看到,在宏中,我定义了
,这是因为宏内部的新行不会被修剪,并且总是在
它所在的位置给出新行,因此在一部分中,我们修剪空白,在另一部分中,我们保留它

编辑:

值得一提的是,出于某种原因,此仅适用于一行。如果宏中有多行,它只保留第一行的缩进。到目前为止,我还没有找到解决这个问题的方法,但我为此创建了

例如:

<#macro test>
...
wow
</#macro>

Example:
   - <@test><#nt>

对于那些希望在导入的宏前面加上一些空格缩进的人,下面有一个类来执行这项工作:

public final static class IndentDirective
    implements TemplateDirectiveModel
{

  private static final String COUNT = "count";

  public void execute(Environment environment, Map parameters, TemplateModel[] templateModels,
      TemplateDirectiveBody body)
      throws TemplateException, IOException
  {
    Integer count = null;
    final Iterator iterator = parameters.entrySet().iterator();
    while (iterator.hasNext())
    {
      final Map.Entry entry = (Map.Entry) iterator.next();
      final String name = (String) entry.getKey();
      final TemplateModel value = (TemplateModel) entry.getValue();

      if (name.equals(COUNT) == true)
      {
        if (value instanceof TemplateNumberModel == false)
        {
          throw new TemplateModelException("The \"" + COUNT + "\" parameter " + "must be a number");
        }
        count = ((TemplateNumberModel) value).getAsNumber().intValue();
        if (count < 0)
        {
          throw new TemplateModelException("The \"" + COUNT + "\" parameter " + "cannot be negative");
        }
      }
      else
      {
        throw new TemplateModelException("Unsupported parameter '" + name + "'");
      }
    }
    if (count == null)
    {
      throw new TemplateModelException("The required \"" + COUNT + "\" parameter" + "is missing");
    }

    final String indentation = StringUtils.repeat(' ', count);
    final StringWriter writer = new StringWriter();
    body.render(writer);
    final String string = writer.toString();
    final String lineFeed = "\n";
    final boolean containsLineFeed = string.contains(lineFeed) == true;
    final String[] tokens = string.split(lineFeed);
    for (String token : tokens)
    {
      environment.getOut().write(indentation + token + (containsLineFeed == true ? lineFeed : ""));
    }
  }

}

谢谢你的回复!“使模板更难阅读”,我的意思几乎和你想的一样,它使宏更复杂,更不容易解释。提交缩进级别似乎是解决此问题的最合理的方法。您始终可以在宏上添加注释标题,以帮助澄清它的作用。您的代码段中有一个类型,它应该是:
..
${”“?left_pad(indent*4)}
..
@klingt.net谢谢!已修复。您建议如何将行拆分?应通过
TemplateDirectiveModel
而不是通过
#宏
实现
缩进
指令。如果您查看
TemplateDirectiveModel
API,您将看到如何实现它。是吗?:)然后你也可以做
,然后在上面使用正则表达式(通过
?替换
)来实现同样的效果。我想这就是你可能要做的。我喜欢RAD(纯FTL快速简便)。
TemplateDirectiveModel
最终会更清晰,但我的首选是避免将逻辑移到Java类中,直到1)功能的需求完全确定,2)在FTL中实现逻辑真的不可理解(在Java中会更清晰)。