1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package net.sf.commonclipse;
18
19 import java.text.MessageFormat;
20 import java.util.HashMap;
21 import java.util.Iterator;
22 import java.util.Map;
23 import java.util.regex.Pattern;
24
25 import org.eclipse.core.resources.IFile;
26 import org.eclipse.core.resources.IResource;
27 import org.eclipse.core.resources.ResourcesPlugin;
28 import org.eclipse.core.runtime.IProgressMonitor;
29 import org.eclipse.core.runtime.IStatus;
30 import org.eclipse.core.runtime.Preferences;
31 import org.eclipse.jdt.core.Flags;
32 import org.eclipse.jdt.core.IBuffer;
33 import org.eclipse.jdt.core.ICompilationUnit;
34 import org.eclipse.jdt.core.IField;
35 import org.eclipse.jdt.core.IJavaElement;
36 import org.eclipse.jdt.core.IMethod;
37 import org.eclipse.jdt.core.ISourceReference;
38 import org.eclipse.jdt.core.IType;
39 import org.eclipse.jdt.core.ITypeHierarchy;
40 import org.eclipse.jdt.core.JavaCore;
41 import org.eclipse.jdt.core.JavaModelException;
42 import org.eclipse.jdt.core.ToolFactory;
43 import org.eclipse.jdt.core.formatter.CodeFormatter;
44 import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
45 import org.eclipse.jface.dialogs.MessageDialog;
46 import org.eclipse.jface.dialogs.ProgressMonitorDialog;
47 import org.eclipse.jface.text.BadLocationException;
48 import org.eclipse.jface.text.Document;
49 import org.eclipse.swt.widgets.Shell;
50 import org.eclipse.text.edits.MalformedTreeException;
51 import org.eclipse.text.edits.TextEdit;
52
53
54 /***
55 * @author fgiust
56 * @version $Revision: 1.7 $ ($Author: fgiust $)
57 */
58 public abstract class Generator
59 {
60
61 /***
62 * line separator.
63 */
64 private static final String LINE_SEPARATOR = System.getProperty("line.separator");
65
66 /***
67 * Generates the appropriate method in <code>type</code>.
68 * @param type IType
69 * @param shell Shell
70 */
71 public void generate(IType type, Shell shell)
72 {
73
74 ICompilationUnit cu = (ICompilationUnit) type.getAncestor(IJavaElement.COMPILATION_UNIT);
75
76
77 IResource resource;
78 try
79 {
80 resource = cu.getCorrespondingResource();
81 }
82 catch (JavaModelException e)
83 {
84 MessageDialog.openError(shell, CCMessages.getString("Generator.errortitle"), e.getMessage());
85 return;
86 }
87 if (resource != null && resource.getResourceAttributes().isReadOnly())
88 {
89 IStatus status = ResourcesPlugin.getWorkspace().validateEdit(new IFile[]{(IFile) resource}, shell);
90
91 if (!status.isOK())
92 {
93 MessageDialog.openError(shell, CCMessages.getString("Generator.errortitle"), CCMessages
94 .getString("Generator.readonly"));
95 return;
96 }
97 }
98
99 resource = null;
100
101 if (!validate(type, shell))
102 {
103 return;
104 }
105
106 ProgressMonitorDialog progressDialog = new ProgressMonitorDialog(shell);
107 progressDialog.open();
108 try
109 {
110
111 IProgressMonitor monitor = progressDialog.getProgressMonitor();
112 generateMethod(type, cu, shell, monitor);
113 }
114 catch (JavaModelException ex)
115 {
116 MessageDialog.openError(shell, CCMessages.getString("Generator.errortitle"), ex.getMessage());
117 }
118
119 progressDialog.close();
120 }
121
122 /***
123 * Checks if a corresponding method already exists and prompt the user for replacing it.
124 * @param type IType
125 * @param shell Shell
126 * @return <code>true</code> if the method doesn't exists or the user has choosen to overwrite it
127 */
128 protected boolean validate(IType type, Shell shell)
129 {
130
131 IMethod method = getExistingMethod(type);
132
133 if (method != null && method.exists())
134 {
135 boolean dontAsk = CCPluginPreferences.getPreferences().dontAskOnOverwrite();
136
137 if (dontAsk
138 || MessageDialog.openConfirm(shell, CCPlugin.PLUGIN_NAME, MessageFormat.format(CCMessages
139 .getString("Generator.methodexists"),
140 new Object[]{getMethodName(), type.getElementName()})))
141 {
142 try
143 {
144 method.delete(true, null);
145 return true;
146 }
147 catch (JavaModelException e)
148 {
149 MessageDialog.openError(shell, CCMessages.getString("Generator.errortitle"),
150 MessageFormat.format(CCMessages.getString("Generator.unabletodelete"),
151 new Object[]{getMethodName(), e.getMessage()}));
152 }
153 }
154 return false;
155 }
156
157 return true;
158 }
159
160 /***
161 * Generates the method by:
162 * <ul>
163 * <li>call createMethod</li>
164 * <li>format the given method</li>
165 * <li>add it to type</li>
166 * <li>call addImports</li>
167 * </ul>.
168 * @param type IType
169 * @param cu compilation unit
170 * @param shell Shell for messages
171 * @param monitor progress monitor, updated during processing
172 * @throws JavaModelException any exception in method generation
173 */
174 public void generateMethod(IType type, ICompilationUnit cu, Shell shell, IProgressMonitor monitor)
175 throws JavaModelException
176 {
177 String className = type.getElementName();
178
179 String title = MessageFormat.format(CCMessages.getString("Generator.generating"),
180 new Object[]{className});
181 monitor.beginTask(title, 100);
182 monitor.worked(10);
183
184 monitor.setTaskName(title + CCMessages.getString("Generator.parsing"));
185 String src = createMethod(type);
186 monitor.worked(30);
187
188 monitor.setTaskName(title + CCMessages.getString("Generator.formatting"));
189 Document document = new Document(src);
190
191 TextEdit text = ToolFactory.createCodeFormatter(null).format(
192 CodeFormatter.K_UNKNOWN,
193 src,
194 0,
195 src.length(),
196 getIndentUsed(type, cu) + 1,
197 null);
198
199 try
200 {
201 text.apply(document);
202 }
203 catch (MalformedTreeException ex)
204 {
205 MessageDialog.openError(shell, CCMessages.getString("Generator.errortitle"), ex.getMessage());
206 }
207 catch (BadLocationException ex)
208 {
209 MessageDialog.openError(shell, CCMessages.getString("Generator.errortitle"), ex.getMessage());
210 }
211
212 monitor.worked(20);
213
214 monitor.setTaskName(title + CCMessages.getString("Generator.adding"));
215 type.createMethod(document.get() + LINE_SEPARATOR, null, false, null);
216
217 monitor.worked(20);
218
219 monitor.setTaskName(title + CCMessages.getString("Generator.imports"));
220 addImports(type);
221 monitor.worked(20);
222
223 monitor.done();
224 }
225
226 /***
227 * Evaluates the indention used by a Java element.
228 * @param elem Java element
229 * @param cu compilation unit
230 * @return indentation level
231 * @throws JavaModelException model exception when trying to access source
232 */
233 public int getIndentUsed(IJavaElement elem, ICompilationUnit cu) throws JavaModelException
234 {
235 if (elem instanceof ISourceReference)
236 {
237
238 if (cu != null)
239 {
240 IBuffer buf = cu.getBuffer();
241 int offset = ((ISourceReference) elem).getSourceRange().getOffset();
242 int i = offset;
243
244 while (i > 0 && !isLineDelimiterChar(buf.getChar(i - 1)))
245 {
246 i--;
247 }
248
249 return computeIndent(buf.getText(i, offset - i));
250 }
251 }
252 return 0;
253 }
254
255 /***
256 * Line delimiter chars are '\n' and '\r'.
257 * @param ch char
258 * @return <code>true</code> if ch is '\n' or '\r'
259 */
260 private boolean isLineDelimiterChar(char ch)
261 {
262 return ch == '\n' || ch == '\r';
263 }
264
265 /***
266 * Returns the indent of the given string.
267 * @param line the text line
268 * @return indent level
269 */
270 public int computeIndent(String line)
271 {
272 Preferences preferences = JavaCore.getPlugin().getPluginPreferences();
273 int tabWidth = preferences.getInt(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE);
274
275 int result = 0;
276 int blanks = 0;
277 int size = line.length();
278 for (int i = 0; i < size; i++)
279 {
280 char c = line.charAt(i);
281 if (c == '\t')
282 {
283 result++;
284 blanks = 0;
285 }
286 else if (Character.isWhitespace(c) && !(c == '\n' || c == '\r'))
287 {
288 blanks++;
289 if (blanks == tabWidth)
290 {
291 result++;
292 blanks = 0;
293 }
294 }
295 else
296 {
297 return result;
298 }
299 }
300 return result;
301 }
302
303 /***
304 * Returns the generated method name.
305 * @return String method name
306 */
307 protected abstract String getMethodName();
308
309 /***
310 * Creates the method for the ITYPE type.
311 * @param type Itype
312 * @return Method String
313 * @throws JavaModelException exception in creating method
314 */
315 protected abstract String createMethod(IType type) throws JavaModelException;
316
317 /***
318 * Returns the existing method.
319 * @param type IType
320 * @return IMethod
321 */
322 protected abstract IMethod getExistingMethod(IType type);
323
324 /***
325 * Adds required imports to type.
326 * @param type IType
327 * @throws JavaModelException exception in adding imports
328 */
329 protected abstract void addImports(IType type) throws JavaModelException;
330
331 /***
332 * Iterates on fields and call getFieldString() on any match not in the configurable excluded list.
333 * @param type IType
334 * @return String
335 * @throws JavaModelException exception in analyzing fields
336 */
337 protected String buildAppenderList(IType type) throws JavaModelException
338 {
339
340 Map fields = buildFieldMap(type);
341
342
343 StringBuffer buffer = new StringBuffer();
344 Iterator fieldsIterator = fields.keySet().iterator();
345
346 while (fieldsIterator.hasNext())
347 {
348 String fieldName = (String) fieldsIterator.next();
349
350
351 if (!isExcluded(fieldName))
352 {
353 buffer.append(getFieldAppender(fieldName, fieldName));
354 }
355 }
356
357 return buffer.toString();
358
359 }
360
361 /***
362 * Returns a Set containing all the names of fields visible by this type.
363 * @param type IType
364 * @return Map containg field names - IField objects
365 * @throws JavaModelException exception in analyzing type
366 */
367 protected Map buildFieldMap(IType type) throws JavaModelException
368 {
369 Map fieldNames = new HashMap();
370
371 IField[] fields = type.getFields();
372
373 for (int j = 0; j < fields.length; j++)
374 {
375 IField field = fields[j];
376 int flags = field.getFlags();
377
378 if (!Flags.isStatic(flags))
379 {
380 fieldNames.put(field.getElementName(), field);
381 }
382 }
383
384
385 ITypeHierarchy hierarchy = type.newSupertypeHierarchy(null);
386 IType[] types = hierarchy.getSupertypes(type);
387
388 for (int j = 0; j < types.length; j++)
389 {
390 IField[] superFields = types[j].getFields();
391
392 for (int x = 0; x < superFields.length; x++)
393 {
394 IField field = superFields[x];
395 int flags = field.getFlags();
396
397
398 if (!Flags.isStatic(flags) && !Flags.isPrivate(flags))
399 {
400 fieldNames.put(field.getElementName(), field);
401 }
402 }
403 }
404 return fieldNames;
405 }
406
407 /***
408 * Checks if a given field should be excluded from generated method.
409 * @param fieldName field/property name
410 * @return <code>true</code> if the field should not be included in generathed method
411 */
412 protected boolean isExcluded(String fieldName)
413 {
414 Pattern exclusion = CCPluginPreferences.getPreferences().getExcludedFielsPattern();
415 return exclusion.matcher(fieldName).matches();
416 }
417
418 /***
419 * get the "append" statement for a field.
420 * @param fieldName name of the field
421 * @param accessor can be different for fieldname
422 * @return String
423 */
424 protected abstract String getFieldAppender(String fieldName, String accessor);
425
426 }