Search code examples
javafreemarkerpreprocessor

Find freemarker source-linenumber from result-linenumber


C++ have a preprocessor. You can add Freemarker as a second preprocessor. For example:

<#function oh>
  <#return 'Oh!'>
</#function>
int main(){
  std::cout << "I am in line: __LINE__. ${oh()}" << std::endl;
  return 0;
}

The code is UTF8 and linefeed is CRLF (windows).

The output will be:

I am in line: 2. Oh!

(But its line 5)

I use this code to transform the ftl into cpp:

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;

// ...

@Override
public String load(final HttpServletRequest req) {
    String code = iftl.load(req);
    Configuration cfg = new Configuration();
    cfg.setDefaultEncoding("UTF-8");
    cfg.setLocale(Locale.US);
    cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
    try {
        Template template = new Template("x", new StringReader(code), cfg);
        StringWriter sw = new StringWriter();
        template.process(new LinkedHashMap<>(), sw);
        sw.close();
        return sw.toString();
    } catch (IOException | TemplateException e) {
        LOG.log(Level.SEVERE, "Could not parse freemaker-preprocessor.", e);
    }
    return code;
}

My problem: Line 2 is not correct! Line 5 is correct.

I need a function like this:

/** Takes a result-line-number and returns the source-line-number
  */
public int dest2src(int resultLineNumber, Template template) {
  // what to write here?
}
// dest2src(2, template)) = 5

Solution

  • The issue comes from white-space stripping, which removes lines that contain only directives. You can disable it like this:

    config.setWhitespaceStripping(false); 
    

    Then all line breaks will be preserved, so line numbers before and after processing will stay the same.