2009-12-17 27 views
15

Tôi không chắc chắn rằng đây thậm chí là một vấn đề có thể giải quyết được, nhưng giả sử rằng tôi có một mẫu freemarker, tôi muốn có thể hỏi mẫu biến mà nó sử dụng.Làm thế nào tôi có thể nhìn thấy một mẫu freemarker để tìm ra những biến mà nó sử dụng?

Vì mục đích của mình, chúng ta có thể giả định rằng mẫu dấu vị trí của trình đánh dấu rất đơn giản - chỉ các mục "cấp cơ sở" (mẫu cho mẫu như vậy có thể là một Bản đồ đơn giản). Nói cách khác, tôi không cần xử lý các mẫu gọi cho cấu trúc lồng nhau, v.v.

+0

Bạn muốn in những gì giá trị mô hình có sẵn khi mẫu được xử lý? Hay bạn cần biến nào thực sự được sử dụng trong mẫu? Nói '$ {foo.bar}', bạn muốn biết rằng 'foo' sẽ được sử dụng?Bạn có cần biết trước khi mẫu được xử lý hoặc nó có thể được sau khi? – huynhjl

+0

Nếu $ {foo} và $ {bar} có trong mẫu, tôi muốn biết rằng "foo" và "bar" được sử dụng. – Jared

Trả lời

1

tôi đã cùng một công việc để có được danh sách các biến từ mẫu bên java và không tìm thấy bất kỳ phương pháp tốt để mà trừ sử dụng phản ánh. Tôi không chắc liệu có một cách tốt hơn để có được dữ liệu này hay không nhưng đây là cách tiếp cận của tôi:

public Set<String> referenceSet(Template template) throws TemplateModelException { 
    Set<String> result = new HashSet<>(); 
    TemplateElement rootTreeNode = template.getRootTreeNode(); 
    for (int i = 0; i < rootTreeNode.getChildCount(); i++) { 
     TemplateModel templateModel = rootTreeNode.getChildNodes().get(i); 
     if (!(templateModel instanceof StringModel)) { 
      continue; 
     } 
     Object wrappedObject = ((StringModel) templateModel).getWrappedObject(); 
     if (!"DollarVariable".equals(wrappedObject.getClass().getSimpleName())) { 
      continue; 
     } 

     try { 
      Object expression = getInternalState(wrappedObject, "expression"); 
      switch (expression.getClass().getSimpleName()) { 
       case "Identifier": 
        result.add(getInternalState(expression, "name").toString()); 
        break; 
       case "DefaultToExpression": 
        result.add(getInternalState(expression, "lho").toString()); 
        break; 
       case "BuiltinVariable": 
        break; 
       default: 
        throw new IllegalStateException("Unable to introspect variable"); 
      } 
     } catch (NoSuchFieldException | IllegalAccessException e) { 
      throw new TemplateModelException("Unable to reflect template model"); 
     } 
    } 
    return result; 
} 

private Object getInternalState(Object o, String fieldName) throws NoSuchFieldException, IllegalAccessException { 
    Field field = o.getClass().getDeclaredField(fieldName); 
    boolean wasAccessible = field.isAccessible(); 
    try { 
     field.setAccessible(true); 
     return field.get(o); 
    } finally { 
     field.setAccessible(wasAccessible); 
    } 
} 
dự án

mẫu mà tôi thực hiện để chứng minh mẫu mẫn có thể được tìm thấy trên github: https://github.com/SimY4/TemplatesPOC.git

+0

Điều này sử dụng các phương pháp được đánh dấu không được chấp nhận vì chúng được sử dụng nội bộ trong tương lai và có thể được thay thế hoàn toàn. Mã này cũng không làm việc cho tôi vào cuối năm 2016 với FreeMarker 2.3.25. –

+0

@MichaelOryl Xin chào. Tôi biết rằng phương pháp tiếp cận của tôi bằng cách sử dụng API không dùng nữa và cá nhân tôi sẽ không khuyên bạn sử dụng nó. Nhưng nó vẫn hoạt động cho tôi, ngay cả trong năm 2016 và freemarker 2.3.25. Bạn có thể xem mã làm việc tại https://github.com/SimY4/TemplatesPOC.git – SimY4

5

Đây có thể là muộn, nhưng trong trường hợp bất kỳ ai khác gặp sự cố này: bạn có thể sử dụng 'data_model' và 'globals' để kiểm tra mô hình - data_model sẽ chỉ chứa các giá trị do mô hình cung cấp trong khi các hình cầu cũng sẽ chứa bất kỳ biến nào được xác định trong mẫu. Bạn cần phải thêm vào trước các biến đặc biệt với một dấu chấm - vì vậy để truy cập globals, sử dụng $

Đối với các biến đặc biệt khác nhìn thấy http://freemarker.sourceforge.net/docs/ref_specvar.html

+1

Thực ra, tôi muốn lấy danh sách các biến trong mẫu trong mã Java của mình. Tôi muốn có thể làm điều gì đó như: Mẫu t; .... Danh sách = t.getReferenceVariables(); – Jared

+1

http://freemarker.org/docs/api/freemarker/template/Template.html#getRootTreeNode() cung cấp cho bạn một Hashtable của mẫu –

+1

Điều này không trả lời được câu hỏi. Để liệt kê nội dung của các biến, bạn cần phải làm nhiều hơn thế. –

-1

Execute sau regex trên mẫu {globals.}:

(?<=\$\{)([^\}]+)(?=\}) 
  • (? < = \ $ \ {) Trận mọi thứ tiếp theo $ {
  • ([^ \}] +) Tương ứng với một chuỗi không chứa}
  • (? = \}) Trận mọi thứ trước}
+0

Bạn chỉ có thể sử dụng một regex đơn giản để rút ra các biến '$ {whatever}' đơn giản. Điều này sẽ không tìm thấy những thứ được tham chiếu trong '<#if whatever>' hoặc cấu trúc vòng lặp. –

1

một cách khác để lấy các biến từ java. Đây chỉ cố gắng để xử lý mẫu và bắt InvalidReferenceException để tìm tất cả các biến trong một Freemarker-mẫu

/** 
* Find all the variables used in the Freemarker Template 
* @param templateName 
* @return 
*/ 
public Set<String> getTemplateVariables(String templateName) { 
    Template template = getTemplate(templateName); 
    StringWriter stringWriter = new StringWriter(); 
    Map<String, Object> dataModel = new HashMap<>(); 
    boolean exceptionCaught; 

    do { 
     exceptionCaught = false; 
     try { 
      template.process(dataModel, stringWriter); 
     } catch (InvalidReferenceException e) { 
      exceptionCaught = true; 
      dataModel.put(e.getBlamedExpressionString(), ""); 
     } catch (IOException | TemplateException e) { 
      throw new IllegalStateException("Failed to Load Template: " + templateName, e); 
     } 
    } while (exceptionCaught); 

    return dataModel.keySet(); 
} 

private Template getTemplate(String templateName) { 
    try { 
     return configuration.getTemplate(templateName); 
    } catch (IOException e) { 
     throw new IllegalStateException("Failed to Load Template: " + templateName, e); 
    } 
} 
+0

Đây là phương pháp duy nhất tôi thấy rằng A) hoạt động và B) không sử dụng các phương thức FreeMarker không dùng nữa. Bravo. –

+0

@MichaelOryl về việc sử dụng nó cho mẫu rất lớn? Bạn đã sẵn sàng hiển thị mẫu này nhiều lần dưới dạng các biến khác nhau trong nó chưa? – SimY4

0

tôi giải quyết này cho usecase rất đơn giản của tôi (chỉ sử dụng datastructure phẳng, không có làm tổ (ví dụ: $ { parent.child}), danh sách hoặc cụ thể hơn) với nhà cung cấp dữ liệu giả:

public class DummyDataProvider<K, V> extends HashMap<K, V> { 
    private static final long serialVersionUID = 1; 

    public final Set<String> variables = new HashSet<>(); 

    @SuppressWarnings("unchecked") 
    @Override 
    public V get(Object key) { 
     variables.add(key.toString()); 
     return (V) key; 
    } 
} 

bạn có thể cung cấp này để chế biến một mẫu và khi nó kết thúc Set variables chứa các biến của bạn.

Đây là cách tiếp cận rất đơn giản, chắc chắn cần cải tiến, nhưng bạn có ý tưởng.

+0

Thực ra tôi đã buộc phải sử dụng giải pháp dựa trên regex, bởi vì <#if ..> và <#elseif ..> là vô hình đối với loại DummyDataProvider này. Không phải là giải pháp rất sạch sẽ. Tôi thậm chí sẽ sử dụng động cơ mẫu khác nhau nếu có bất cứ điều gì để xem xét. HTTL đã yêu cầu các biến mẫu như là một công dân hạng nhất, nhưng bản thân HTTL đã chết và tài liệu của họ không phải là tốt nhất của giống ngoài kia ... –

0

Tôi gặp vấn đề tương tự và không có giải pháp nào được đăng có ý nghĩa với tôi. Những gì tôi đến là cắm triển khai tùy chỉnh TemplateExceptionHandler. Ví dụ:

final private List<String> missingReferences = Lists.newArrayList(); 
final Configuration cfg = new Configuration(Configuration.VERSION_2_3_23); 
cfg.setTemplateExceptionHandler(new TemplateExceptionHandler() { 

    @Override 
    public void handleTemplateException(TemplateException arg0, Environment arg1, Writer arg2) throws TemplateException { 
     if (arg0 instanceof InvalidReferenceException) { 
      missingReferences.add(arg0.getBlamedExpressionString()); 
      return; 
     } 
     throw arg0; 
    } 

} 

Template template = loadTemplate(cfg, templateId, templateText); 

StringWriter out = new StringWriter(); 

try { 
    template.process(templateData, out); 
} catch (TemplateException | IOException e) { 
     throw new RuntimeException("oops", e); 
} 

System.out.println("Missing references: " + missingReferences); 

đọc thêm ở đây: https://freemarker.apache.org/docs/pgui_config_errorhandling.html

Các vấn đề liên quan