KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > text > templates > TemplateTranslator


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jface.text.templates;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.Iterator JavaDoc;
15 import java.util.LinkedHashMap JavaDoc;
16 import java.util.List JavaDoc;
17 import java.util.Map JavaDoc;
18 import java.util.regex.Matcher JavaDoc;
19 import java.util.regex.Pattern JavaDoc;
20
21 /**
22  * The template translator translates a string into a template buffer. Regions marked as variables
23  * are translated into <code>TemplateVariable</code>s.
24  * <p>
25  * The EBNF grammar of a valid string is as follows:
26  * </p>
27  * <p>
28  * template := (text | escape)*. <br />
29  * text := character - dollar. <br />
30  * escape := dollar ('{' variable '}' | dollar). <br />
31  * dollar := '$'. <br />
32  * variable := identifier | identifier ':' type. <br />
33  * type := qualifiedname | qualifiedname '(' arguments ')'. <br />
34  * arguments := (qualifiedname ',')* qualifiedname. <br />
35  * qualifiedname := (identifier '.')* identifier. <br />
36  * </p>
37  * <p>
38  * Clients may only replace the <code>createVariable</code> method of this class.
39  * </p>
40  *
41  * @since 3.0
42  */

43 public class TemplateTranslator {
44     /**
45      * Precompiled regex pattern for qualified names.
46      * @since 3.3
47      */

48     private static final Pattern JavaDoc PARAM_PATTERN= Pattern.compile("(?:\\w++\\.)*\\w++"); //$NON-NLS-1$
49
/**
50      * Precompiled regex pattern for valid dollar escapes (dollar literals and variables) and
51      * (invalid) single dollars.
52      * @since 3.3
53      */

54     private static final Pattern JavaDoc ESCAPE_PATTERN= Pattern.compile("\\$\\$|\\$\\{\\s*+(\\w*+)\\s*+(?::\\s*+((?:\\w++\\.)*\\w++)\\s*+(?:\\(\\s*+((?:(?:\\w++\\.)*\\w++\\s*+,\\s*+)*(?:\\w++\\.)*\\w++)\\s*+\\))?\\s*+)?\\}|\\$"); //$NON-NLS-1$
55
/**
56      * @since 3.3
57      */

58     private final class VariableDescription {
59         final List JavaDoc fOffsets= new ArrayList JavaDoc(5);
60         final String JavaDoc fName;
61         TemplateVariableType fType;
62
63         VariableDescription(String JavaDoc name, TemplateVariableType type) {
64             fName= name;
65             fType= type;
66         }
67
68         void mergeType(TemplateVariableType type) throws TemplateException {
69             if (type == null)
70                 return;
71             if (fType == null)
72                 fType= type;
73             if (!type.equals(fType))
74                 fail(TextTemplateMessages.getFormattedString("TemplateTranslator.error.incompatible.type", fName)); //$NON-NLS-1$
75
}
76     }
77
78     /** Last translation error. */
79     private String JavaDoc fErrorMessage;
80     /**
81      * Used to ensure compatibility with subclasses overriding
82      * {@link #createVariable(String, String, int[])}.
83      * @since 3.3
84      */

85     private TemplateVariableType fCurrentType;
86
87     /**
88      * Returns an error message if an error occurred for the last translation, <code>null</code>
89      * otherwise.
90      *
91      * @return the error message if an error occurred during the most recent translation,
92      * <code>null</code> otherwise
93      */

94     public String JavaDoc getErrorMessage() {
95         return fErrorMessage;
96     }
97
98     /**
99      * Translates a template to a <code>TemplateBuffer</code>. <code>null</code> is returned if
100      * there was an error. <code>getErrorMessage()</code> retrieves the associated error message.
101      *
102      * @param template the template to translate.
103      * @return returns the template buffer corresponding to the string
104      * @see #getErrorMessage()
105      * @throws TemplateException if translation failed
106      */

107     public TemplateBuffer translate(Template template) throws TemplateException {
108         return parse(template.getPattern());
109     }
110
111     /**
112      * Translates a template string to <code>TemplateBuffer</code>. <code>null</code> is
113      * returned if there was an error. <code>getErrorMessage()</code> retrieves the associated
114      * error message.
115      *
116      * @param string the string to translate.
117      * @return returns the template buffer corresponding to the string
118      * @see #getErrorMessage()
119      * @throws TemplateException if translation failed
120      */

121     public TemplateBuffer translate(String JavaDoc string) throws TemplateException {
122         return parse(string);
123     }
124
125     /**
126      * Internal parser.
127      *
128      * @param string the string to parse
129      * @return the parsed <code>TemplateBuffer</code>
130      * @throws TemplateException if the string does not conform to the template format
131      */

132     private TemplateBuffer parse(String JavaDoc string) throws TemplateException {
133         
134         fErrorMessage= null;
135         final StringBuffer JavaDoc buffer= new StringBuffer JavaDoc(string.length());
136         final Matcher JavaDoc matcher= ESCAPE_PATTERN.matcher(string);
137         final Map JavaDoc variables= new LinkedHashMap JavaDoc();
138         
139         int complete= 0;
140         while (matcher.find()) {
141             // append any verbatim text
142
buffer.append(string.substring(complete, matcher.start()));
143
144             // check the escaped sequence
145
if ("$".equals(matcher.group())) { //$NON-NLS-1$
146
fail(TextTemplateMessages.getString("TemplateTranslator.error.incomplete.variable")); //$NON-NLS-1$
147
} else if ("$$".equals(matcher.group())) { //$NON-NLS-1$
148
// escaped $
149
buffer.append('$');
150             } else {
151                 // parse variable
152
String JavaDoc name= matcher.group(1);
153                 String JavaDoc typeName= matcher.group(2);
154                 String JavaDoc params= matcher.group(3);
155                 TemplateVariableType type= createType(typeName, params);
156                 
157                 updateOrCreateVariable(variables, name, type, buffer.length());
158                 
159                 buffer.append(name);
160             }
161             complete= matcher.end();
162         }
163         // append remaining verbatim text
164
buffer.append(string.substring(complete));
165
166         TemplateVariable[] vars= createVariables(variables);
167         return new TemplateBuffer(buffer.toString(), vars);
168     }
169
170     private TemplateVariableType createType(String JavaDoc typeName, String JavaDoc paramString) {
171         if (typeName == null)
172             return null;
173
174         if (paramString == null)
175             return new TemplateVariableType(typeName);
176
177         final Matcher JavaDoc matcher= PARAM_PATTERN.matcher(paramString);
178         List JavaDoc params= new ArrayList JavaDoc(5);
179         while (matcher.find())
180             params.add(matcher.group());
181
182         return new TemplateVariableType(typeName, (String JavaDoc[]) params.toArray(new String JavaDoc[params.size()]));
183     }
184
185     private void fail(String JavaDoc message) throws TemplateException {
186         fErrorMessage= message;
187         throw new TemplateException(message);
188     }
189
190     /**
191      * If there is no variable named <code>name</code>, a new variable with the given type, name
192      * and offset is created. If one exists, the offset is added to the variable and the type is
193      * merged with the existing type.
194      *
195      * @param variables the variables by variable name
196      * @param name the name of the variable
197      * @param type the variable type, <code>null</code> for not defined
198      * @param offset the buffer offset of the variable
199      * @throws TemplateException if merging the type fails
200      * @since 3.3
201      */

202     private void updateOrCreateVariable(Map JavaDoc variables, String JavaDoc name, TemplateVariableType type, int offset) throws TemplateException {
203         VariableDescription varDesc= (VariableDescription) variables.get(name);
204         if (varDesc == null) {
205             varDesc= new VariableDescription(name, type);
206             variables.put(name, varDesc);
207         } else {
208             varDesc.mergeType(type);
209         }
210         varDesc.fOffsets.add(new Integer JavaDoc(offset));
211     }
212
213     /**
214      * Creates proper {@link TemplateVariable}s from the variable descriptions.
215      *
216      * @param variables the variable descriptions by variable name
217      * @return the corresponding variables
218      * @since 3.3
219      */

220     private TemplateVariable[] createVariables(Map JavaDoc variables) {
221         TemplateVariable[] result= new TemplateVariable[variables.size()];
222         int idx= 0;
223         for (Iterator JavaDoc it= variables.values().iterator(); it.hasNext(); idx++) {
224             VariableDescription desc= (VariableDescription) it.next();
225             TemplateVariableType type= desc.fType == null ? new TemplateVariableType(desc.fName) : desc.fType;
226             int[] offsets= new int[desc.fOffsets.size()];
227             int i= 0;
228             for (Iterator JavaDoc intIt= desc.fOffsets.iterator(); intIt.hasNext(); i++) {
229                 Integer JavaDoc offset= (Integer JavaDoc) intIt.next();
230                 offsets[i]= offset.intValue();
231             }
232             fCurrentType= type;
233             /*
234              * Call the deprecated version of createVariable. When not overridden, it will delegate
235              * to the new version using fCurrentType.
236              */

237             TemplateVariable var= createVariable(type.getName(), desc.fName, offsets);
238             result[idx]= var;
239         }
240         fCurrentType= null; // avoid dangling reference
241
return result;
242     }
243     
244     /**
245      * Hook method to create new variables. Subclasses may override to supply their custom variable
246      * type.
247      * <p>
248      * Clients may replace this method.
249      * </p>
250      *
251      * @param type the type of the new variable.
252      * @param name the name of the new variable.
253      * @param offsets the offsets where the variable occurs in the template
254      * @return a new instance of <code>TemplateVariable</code>
255      * @deprecated as of 3.3 use {@link #createVariable(TemplateVariableType, String, int[])} instead
256      */

257     protected TemplateVariable createVariable(String JavaDoc type, String JavaDoc name, int[] offsets) {
258         return createVariable(fCurrentType, name, offsets);
259     }
260     
261     /**
262      * Hook method to create new variables. Subclasses may override to supply their custom variable
263      * type.
264      * <p>
265      * Clients may replace this method.
266      * </p>
267      *
268      * @param type the type of the new variable.
269      * @param name the name of the new variable.
270      * @param offsets the offsets where the variable occurs in the template
271      * @return a new instance of <code>TemplateVariable</code>
272      * @since 3.3
273      */

274     protected TemplateVariable createVariable(TemplateVariableType type, String JavaDoc name, int[] offsets) {
275         return new TemplateVariable(type, name, name, offsets);
276     }
277 }
278
Popular Tags