001/* 002 * (C) Copyright 2006-20012 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Nuxeo - initial API and implementation 016 * 017 */ 018 019package org.nuxeo.template.fm; 020 021import java.util.ArrayList; 022import java.util.Iterator; 023import java.util.List; 024import java.util.ListIterator; 025import java.util.regex.Matcher; 026import java.util.regex.Pattern; 027 028import org.nuxeo.runtime.api.Framework; 029import org.nuxeo.template.api.TemplateInput; 030import org.nuxeo.template.api.TemplateProcessorService; 031 032/** 033 * Helper class used to extract variable names from a FreeMarker template. This is used to initialize the 034 * {@link TemplateInput} parameters. Extraction is for now simple and system may not detect all the cases, but user is 035 * able to add parameters from the UI. 036 * 037 * @author Tiry (tdelprat@nuxeo.com) 038 */ 039public class FreeMarkerVariableExtractor { 040 041 protected final static Pattern simpleVariableMatcher = Pattern.compile("\\$\\{([^\\}]*)\\}"); 042 043 protected final static String[] spliters = new String[] { ".", "?", "=", ">", "<", "!", " ", "[" }; 044 045 protected final static Pattern[] directiveMatchers = new Pattern[] { Pattern.compile("\\[\\#if\\s([^\\]]*)\\]"), 046 Pattern.compile("\\[\\#list\\s[\\d\\.\\.]*(.+)\\sas\\s([^\\]]*)\\]") }; 047 048 protected final static Pattern[] assignMatchers = new Pattern[] { Pattern.compile("\\[\\#assign\\s(.+)=.*\\]") }; 049 050 protected static final List<String> reservedContextKeywords = new ArrayList<String>(); 051 052 protected static final String[] freeMarkerVariableSuffix = { "_index", "_has_next" }; 053 054 protected static String extractVariableName(String match) { 055 056 String varName = match.trim(); 057 058 if (varName.startsWith("!")) { 059 varName = varName.substring(1); 060 } 061 062 while (varName.startsWith("(")) { 063 varName = varName.substring(1); 064 } 065 066 int idx = varName.indexOf("."); 067 if (idx > 1) { 068 varName = varName.substring(0, idx); 069 } 070 071 for (String spliter : spliters) { 072 idx = varName.indexOf(spliter); 073 if (idx > 1) { 074 varName = varName.substring(0, idx); 075 } 076 } 077 return varName; 078 } 079 080 public static void resetReservedContextKeywords() { 081 synchronized (reservedContextKeywords) { 082 reservedContextKeywords.clear(); 083 } 084 } 085 086 protected static List<String> getreservedContextKeywords() { 087 synchronized (reservedContextKeywords) { 088 if (reservedContextKeywords.size() == 0) { 089 TemplateProcessorService tps = Framework.getLocalService(TemplateProcessorService.class); 090 if (tps != null) { 091 reservedContextKeywords.addAll(tps.getReservedContextKeywords()); 092 } 093 } 094 } 095 return reservedContextKeywords; 096 } 097 098 public static List<String> extractVariables(String content) { 099 100 List<String> variables = new ArrayList<String>(); 101 102 List<String> blackListedVariables = new ArrayList<String>(); 103 104 if (content.length() > 10000) { 105 // split content in multilines 106 // otherwise some regexp won't capture everything 107 content = content.replaceAll("</", "\n</"); 108 } 109 110 Matcher matcher = simpleVariableMatcher.matcher(content); 111 112 matcher.matches(); 113 while (matcher.find()) { 114 if (matcher.groupCount() > 0) { 115 String v = extractVariableName(matcher.group(1)); 116 if (!variables.contains(v)) { 117 variables.add(v); 118 } 119 } 120 } 121 122 for (Pattern dPattern : directiveMatchers) { 123 Matcher dmatcher = dPattern.matcher(content); 124 dmatcher.matches(); 125 while (dmatcher.find()) { 126 if (dmatcher.groupCount() > 0) { 127 String v = extractVariableName(dmatcher.group(1)); 128 if (!variables.contains(v)) { 129 variables.add(v); 130 } 131 if (dmatcher.groupCount() > 1) { 132 String localVariable = extractVariableName(dmatcher.group(2)); 133 blackListedVariables.add(localVariable); 134 for (String suffix : freeMarkerVariableSuffix) { 135 blackListedVariables.add(localVariable + suffix); 136 } 137 } 138 } 139 } 140 } 141 142 for (Pattern dPattern : assignMatchers) { 143 Matcher dmatcher = dPattern.matcher(content); 144 dmatcher.matches(); 145 while (dmatcher.find()) { 146 if (dmatcher.groupCount() > 0) { 147 String v = extractVariableName(dmatcher.group(1)); 148 blackListedVariables.add(extractVariableName(v)); 149 } 150 } 151 } 152 153 // remove internal variables 154 for (String bVar : blackListedVariables) { 155 variables.remove(bVar); 156 } 157 158 // remove reserved variables that don't need specific bindings 159 for (String bVar : getreservedContextKeywords()) { 160 variables.remove(bVar); 161 } 162 163 // remove any non valid variable names 164 ListIterator<String> varIter = variables.listIterator(); 165 while (varIter.hasNext()) { 166 String var = varIter.next(); 167 if (var.contains("<") || var.contains(">")) { 168 varIter.remove(); 169 } else if (var.contains("\n")) { 170 varIter.set(var.replaceAll("\n", "").trim()); 171 } else if (var.startsWith(".")) { 172 // remove FM "Special Variables" 173 varIter.remove(); 174 } 175 } 176 177 return variables; 178 } 179 180}