Added
Link Here
|
1 |
/******************************************************************************* |
2 |
* Copyright (c) 2004 Ben Konrath <ben@bagu.org> |
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 |
* Ben Konrath <ben@bagu.org> - initial implementation |
10 |
* Red Hat Incorporated - add ProfileVersionNumbers.CURRENT_VERSION |
11 |
*******************************************************************************/ |
12 |
|
13 |
package org.eclipse.jdt.core.formatter; |
14 |
|
15 |
import java.io.BufferedReader; |
16 |
import java.io.BufferedWriter; |
17 |
import java.io.File; |
18 |
import java.io.FileInputStream; |
19 |
import java.io.FileReader; |
20 |
import java.io.FileWriter; |
21 |
import java.io.IOException; |
22 |
import java.io.Writer; |
23 |
import java.text.MessageFormat; |
24 |
import java.util.ArrayList; |
25 |
import java.util.HashMap; |
26 |
import java.util.Map; |
27 |
import java.util.MissingResourceException; |
28 |
import java.util.ResourceBundle; |
29 |
|
30 |
import javax.xml.parsers.ParserConfigurationException; |
31 |
import javax.xml.parsers.SAXParser; |
32 |
import javax.xml.parsers.SAXParserFactory; |
33 |
|
34 |
import org.eclipse.core.runtime.IPlatformRunnable; |
35 |
import org.eclipse.core.runtime.Platform; |
36 |
import org.eclipse.jdt.core.ToolFactory; |
37 |
import org.eclipse.jdt.internal.core.util.Util; |
38 |
import org.eclipse.jface.text.BadLocationException; |
39 |
import org.eclipse.jface.text.Document; |
40 |
import org.eclipse.jface.text.IDocument; |
41 |
import org.eclipse.text.edits.TextEdit; |
42 |
import org.xml.sax.Attributes; |
43 |
import org.xml.sax.InputSource; |
44 |
import org.xml.sax.SAXException; |
45 |
import org.xml.sax.helpers.DefaultHandler; |
46 |
|
47 |
/** |
48 |
* Class that handles the org.eclipse.jdt.core.JavaCodeFormatter the |
49 |
* application. The map file reading code is based on code in ProfileStore.java |
50 |
* (org.eclipse.jdf.ui). |
51 |
* |
52 |
* There are a couple improvments that could be made: |
53 |
* 1. Add an import clean up option (requires stuff from org.eclipse.jdt.ui). |
54 |
* 2. Make a list of all the files first so that a file does not get formatted twice. |
55 |
* 3. Use a text based progress monitor for output. |
56 |
* 4. Allow using/updating older versions of the formatter config file. This requires |
57 |
* a largish refactor because ProfilerVersioner is tied to the |
58 |
* org.eclipse.jdt.internal.ui.preferences code which is tied to several UI plugins. |
59 |
* |
60 |
* @author Ben Konrath <ben@bagu.org> |
61 |
*/ |
62 |
public class CodeFormatterApplication implements IPlatformRunnable { |
63 |
|
64 |
/** |
65 |
* A SAX event handler to parse the config xml. |
66 |
*/ |
67 |
private final static class ConfigHandler extends DefaultHandler { |
68 |
|
69 |
/** |
70 |
* Identifiers for the XML file. |
71 |
*/ |
72 |
private final String XML_NODE_ROOT = "profiles"; //$NON-NLS-1$ |
73 |
|
74 |
private final String XML_NODE_PROFILE = "profile"; //$NON-NLS-1$ |
75 |
|
76 |
private final String XML_NODE_SETTING = "setting"; //$NON-NLS-1$ |
77 |
|
78 |
private final String XML_ATTRIBUTE_VERSION = "version"; //$NON-NLS-1$ |
79 |
|
80 |
private final String XML_ATTRIBUTE_ID = "id"; //$NON-NLS-1$ |
81 |
|
82 |
private final String XML_ATTRIBUTE_NAME = "name"; //$NON-NLS-1$ |
83 |
|
84 |
private final String XML_ATTRIBUTE_VALUE = "value"; //$NON-NLS-1$ |
85 |
|
86 |
private int fVersion; |
87 |
|
88 |
private String fName; |
89 |
|
90 |
private Map fSettings; |
91 |
|
92 |
public void startElement(String uri, String localName, String qName, |
93 |
Attributes attributes) throws SAXException { |
94 |
|
95 |
if (qName.equals(XML_NODE_SETTING)) { |
96 |
|
97 |
final String key = attributes.getValue(XML_ATTRIBUTE_ID); |
98 |
final String value = attributes.getValue(XML_ATTRIBUTE_VALUE); |
99 |
fSettings.put(key, value); |
100 |
|
101 |
} else if (qName.equals(XML_NODE_PROFILE)) { |
102 |
|
103 |
fName = attributes.getValue(XML_ATTRIBUTE_NAME); |
104 |
fSettings = new HashMap(200); |
105 |
|
106 |
} else if (qName.equals(XML_NODE_ROOT)) { |
107 |
|
108 |
try { |
109 |
fVersion = Integer.parseInt(attributes |
110 |
.getValue(XML_ATTRIBUTE_VERSION)); |
111 |
} catch (NumberFormatException ex) { |
112 |
throw new SAXException(ex); |
113 |
} |
114 |
|
115 |
} |
116 |
} |
117 |
|
118 |
public Map getSettings() { |
119 |
return fSettings; |
120 |
} |
121 |
|
122 |
public int getVersion() { |
123 |
return fVersion; |
124 |
} |
125 |
|
126 |
public String getName() { |
127 |
return fName; |
128 |
} |
129 |
|
130 |
} |
131 |
|
132 |
/** |
133 |
* Deals with the messages in the properties file (cut n' pasted from a |
134 |
* generated class). |
135 |
*/ |
136 |
private final static class FormatterAppMessages { |
137 |
private static final String BUNDLE_NAME = "org.eclipse.jdt.core.formatter.FormatterAppMessages";//$NON-NLS-1$ |
138 |
|
139 |
private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle |
140 |
.getBundle(BUNDLE_NAME); |
141 |
|
142 |
public static String getString(String key) { |
143 |
try { |
144 |
return RESOURCE_BUNDLE.getString(key); |
145 |
} catch (MissingResourceException e) { |
146 |
return '!' + key + '!'; |
147 |
} |
148 |
} |
149 |
|
150 |
public static String getFormattedString(String key, String arg) { |
151 |
return getFormattedString(key, new String[] { arg }); |
152 |
} |
153 |
|
154 |
public static String getFormattedString(String key, String[] args) { |
155 |
return MessageFormat.format(getString(key), args); |
156 |
} |
157 |
} |
158 |
|
159 |
/** |
160 |
* Read the xml config file and return a Map representing the options that |
161 |
* are in the specified config file. |
162 |
*/ |
163 |
public Map readConfig(String filename) { |
164 |
|
165 |
try { |
166 |
final FileInputStream reader = new FileInputStream(new File( |
167 |
filename)); |
168 |
final ConfigHandler handler = new ConfigHandler(); |
169 |
|
170 |
try { |
171 |
InputSource inputSource = new InputSource(reader); |
172 |
final SAXParserFactory factory = SAXParserFactory.newInstance(); |
173 |
final SAXParser parser = factory.newSAXParser(); |
174 |
parser.parse(inputSource, handler); |
175 |
if (handler.getVersion() != ProfileVersionNumbers.CURRENT_VERSION) |
176 |
return null; |
177 |
configName = handler.getName(); |
178 |
return handler.getSettings(); |
179 |
|
180 |
} finally { |
181 |
try { reader.close(); } catch (IOException e) { /* ignore */ } |
182 |
} |
183 |
|
184 |
} catch (IOException e) { |
185 |
Util.log(e, FormatterAppMessages |
186 |
.getString("ConfigFile.reading.error")); //$NON-NLS-1$ |
187 |
} catch (SAXException e) { |
188 |
Util.log(e, FormatterAppMessages |
189 |
.getString("ConfigFile.reading.error")); //$NON-NLS-1$ |
190 |
} catch (ParserConfigurationException e) { |
191 |
Util.log(e, FormatterAppMessages |
192 |
.getString("ConfigFile.reading.error")); //$NON-NLS-1$ |
193 |
} |
194 |
return null; |
195 |
} |
196 |
|
197 |
/** |
198 |
* Runs the Java code formatter application |
199 |
*/ |
200 |
public Object run(Object args) throws Exception { |
201 |
|
202 |
ArrayList fileList = processCommandLine((String[]) args); |
203 |
|
204 |
// nothing to do |
205 |
if (fileList == null || fileList.isEmpty()) |
206 |
return EXIT_OK; |
207 |
|
208 |
if (!quiet) { |
209 |
if (configName != null) |
210 |
System.out.println(FormatterAppMessages.getFormattedString("CommandLine.config.file", configName)); //$NON-NLS-1$ |
211 |
System.out.println(FormatterAppMessages.getString("CommandLine.start")); //$NON-NLS-1$ |
212 |
} |
213 |
|
214 |
// format the list of files and/or directories |
215 |
while (!fileList.isEmpty()) { |
216 |
File file = (File) fileList.remove(0); |
217 |
|
218 |
if (file.isDirectory()) |
219 |
formatDirTree(file); |
220 |
else |
221 |
formatFile(file); |
222 |
} |
223 |
|
224 |
if (!quiet) { |
225 |
System.out.println(FormatterAppMessages.getString("CommandLine.done")); //$NON-NLS-1$ |
226 |
} |
227 |
|
228 |
return EXIT_OK; |
229 |
} |
230 |
|
231 |
private void displayHelp(String message) { |
232 |
System.err.println(message); |
233 |
System.out.println(""); //$NON-NLS-1$ |
234 |
displayHelp(); |
235 |
} |
236 |
|
237 |
private String configName; |
238 |
|
239 |
/* |
240 |
* The output will look like this: |
241 |
* |
242 |
* "Usage: eclipse -application org.eclipse.jdt.core.JavaCodeFormatter [ OPTIONS ] <files> |
243 |
* <files> Java source files and/or directories to format. |
244 |
* Only files ending with .java will be formatted in the given directory. |
245 |
* OPTIONS: |
246 |
* -config <file> Use the formatting style from the specified config file. |
247 |
* This file must be an xml file that has been exported by Eclipse 3.0. |
248 |
* -help Display this message. |
249 |
* -quiet Only print error messages. |
250 |
* -verbose Be verbose about the formatting job. |
251 |
*/ |
252 |
private void displayHelp() { |
253 |
String binaryName = Platform.getOS().equals(Platform.OS_WIN32) ? "eclipse.exe" : "eclipse"; //$NON-NLS-1$ //$NON-NLS-2$ |
254 |
|
255 |
// this is UG-LY. is there a way to make this look nicer? |
256 |
System.out.println(FormatterAppMessages.getFormattedString("CommandLine.usage", //$NON-NLS-1$ |
257 |
binaryName + " -application org.eclipse.jdt.core.JavaCodeFormatter")); //$NON-NLS-1$ |
258 |
System.out.println(""); //$NON-NLS-1$ |
259 |
|
260 |
System.out.println(" " + FormatterAppMessages.getString("CommandLine.files") //$NON-NLS-1$ //$NON-NLS-2$ |
261 |
+ "\t" + FormatterAppMessages.getString("CommandLine.files.msg1")); //$NON-NLS-1$ //$NON-NLS-2$ |
262 |
System.out.println("\t\t" //$NON-NLS-1$ |
263 |
+ FormatterAppMessages.getFormattedString("CommandLine.files.msg2", ".java")); //$NON-NLS-1$ //$NON-NLS-2$ |
264 |
|
265 |
System.out.println(FormatterAppMessages.getString("CommandLine.options")); //$NON-NLS-1$ |
266 |
System.out.println(" " + FormatterAppMessages.getFormattedString("CommandLine.config", ARG_CONFIG) //$NON-NLS-1$ //$NON-NLS-2$ |
267 |
+ "\t" + FormatterAppMessages.getString("CommandLine.config.msg1")); //$NON-NLS-1$ //$NON-NLS-2$ |
268 |
System.out.println("\t\t\t" + FormatterAppMessages.getString("CommandLine.config.msg2")); //$NON-NLS-1$ //$NON-NLS-2$ |
269 |
System.out.println(" " + ARG_HELP + "\t\t" + FormatterAppMessages.getString("CommandLine.help")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
270 |
System.out.println(" " + ARG_QUIET + "\t\t" + FormatterAppMessages.getString("CommandLine.quiet")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
271 |
System.out.println(" " + ARG_VERBOSE +"\t\t" + FormatterAppMessages.getString("CommandLine.verbose")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
272 |
} |
273 |
|
274 |
private final String ARG_HELP = "-help"; //$NON-NLS-1$ |
275 |
private final String ARG_CONFIG = "-config"; //$NON-NLS-1$ |
276 |
private final String ARG_VERBOSE = "-verbose"; //$NON-NLS-1$ |
277 |
private final String ARG_QUIET = "-quiet"; //$NON-NLS-1$ |
278 |
private boolean verbose = false; |
279 |
private boolean quiet = false; |
280 |
|
281 |
private ArrayList processCommandLine(String[] argsArray) { |
282 |
|
283 |
ArrayList args = new ArrayList(); |
284 |
for (int i = 0; i < argsArray.length; i++) { |
285 |
args.add(argsArray[i]); |
286 |
} |
287 |
|
288 |
// look for flag-like args |
289 |
if (args.remove(ARG_HELP)) { |
290 |
displayHelp(); |
291 |
return null; |
292 |
} |
293 |
if (args.remove(ARG_VERBOSE)) |
294 |
verbose = true; |
295 |
if (args.remove(ARG_QUIET)) |
296 |
quiet = true; |
297 |
|
298 |
if (quiet && verbose) { |
299 |
displayHelp(FormatterAppMessages.getFormattedString |
300 |
("CommandLineError.quiet.verbose", new String[] {ARG_QUIET, ARG_VERBOSE})); //$NON-NLS-1$ |
301 |
return null; |
302 |
} |
303 |
args.remove("-pdelaunch"); //$NON-NLS-1$ |
304 |
|
305 |
// look for flag/param args |
306 |
int index = args.indexOf(ARG_CONFIG); |
307 |
if (index >= 0) { |
308 |
args.remove(index); |
309 |
String configFile = (String) args.remove(index); |
310 |
options = readConfig(configFile); |
311 |
if (options == null) { |
312 |
displayHelp(FormatterAppMessages |
313 |
.getFormattedString("CommandLineError.config", configFile)); //$NON-NLS-1$ |
314 |
return null; |
315 |
} |
316 |
} |
317 |
|
318 |
// only the files and directories should remain |
319 |
ArrayList fileList = new ArrayList(); |
320 |
while (!args.isEmpty()) { |
321 |
String fileName = (String) args.remove(0); |
322 |
File file = new File(fileName); |
323 |
if (file.exists()) { |
324 |
fileList.add(file); |
325 |
} else { |
326 |
displayHelp(FormatterAppMessages |
327 |
.getFormattedString("CommandLineError.file", fileName)); //$NON-NLS-1$ |
328 |
return null; |
329 |
} |
330 |
} |
331 |
|
332 |
if (fileList.isEmpty()) |
333 |
displayHelp(FormatterAppMessages.getString("CommandLineError.file.dir")); //$NON-NLS-1$ |
334 |
|
335 |
return fileList; |
336 |
} |
337 |
|
338 |
/** |
339 |
* Recursively format the Java source code that is contained in the |
340 |
* directory rooted at dir. |
341 |
*/ |
342 |
private void formatDirTree(File dir) { |
343 |
|
344 |
File[] files = dir.listFiles(); |
345 |
if (files == null) |
346 |
return; |
347 |
|
348 |
for (int i = 0; i < files.length; i++) { |
349 |
File file = files[i]; |
350 |
if (file.isDirectory()) { |
351 |
formatDirTree(file); |
352 |
} else if (file.getPath().endsWith(".java")) { //$NON-NLS-1$ |
353 |
formatFile(file); |
354 |
} |
355 |
} |
356 |
} |
357 |
|
358 |
// internal representation of configuration options in the xml file |
359 |
private Map options = null; |
360 |
|
361 |
/** |
362 |
* Format the given Java source file. |
363 |
*/ |
364 |
private void formatFile(File file) { |
365 |
|
366 |
IDocument doc = new Document(); |
367 |
try { |
368 |
// read the file |
369 |
final BufferedReader in = new BufferedReader(new FileReader(file)); |
370 |
if (verbose) { |
371 |
System.out.println(FormatterAppMessages.getFormattedString |
372 |
("CommandLine.formatting", file.getName())); //$NON-NLS-1$ |
373 |
} |
374 |
String line; |
375 |
String contents = ""; //$NON-NLS-1$ |
376 |
try { |
377 |
while ((line = in.readLine()) != null) |
378 |
contents = contents |
379 |
+ System.getProperty("line.separator") + line; //$NON-NLS-1$ |
380 |
} finally { |
381 |
try { in.close(); } catch (IOException e) { /* ignore */ } |
382 |
} |
383 |
|
384 |
// format the file (the meat and potatoes) |
385 |
doc.set(contents); |
386 |
TextEdit edit = ToolFactory.createCodeFormatter(options).format( |
387 |
CodeFormatter.K_COMPILATION_UNIT, doc.get(), 0, |
388 |
doc.getLength(), 0, null); |
389 |
if (edit != null) { |
390 |
edit.apply(doc); |
391 |
} else { |
392 |
System.err.println |
393 |
(FormatterAppMessages.getFormattedString("Edit.problem", file.getName())); //$NON-NLS-1$ |
394 |
return; |
395 |
} |
396 |
|
397 |
// write the file |
398 |
final Writer out = new BufferedWriter(new FileWriter(file, false)); |
399 |
try { |
400 |
out.write(doc.get()); |
401 |
out.flush(); |
402 |
} finally { |
403 |
try { out.close(); } catch (IOException e) { /* ignore */ } |
404 |
} |
405 |
|
406 |
} catch (IOException e) { |
407 |
String errorMessage = FormatterAppMessages.getString("Exception.io") + " " //$NON-NLS-1$ //$NON-NLS-2$ |
408 |
+ e.getLocalizedMessage(); |
409 |
Util.log(e, errorMessage); |
410 |
System.err.println(errorMessage); |
411 |
System.err.println(FormatterAppMessages.getString("Exception.skip")); //$NON-NLS-1$ |
412 |
|
413 |
} catch (BadLocationException e) { |
414 |
String errorMessage = FormatterAppMessages.getString("Exception.bad.location") + " " //$NON-NLS-1$ //$NON-NLS-2$ |
415 |
+ e.getLocalizedMessage(); |
416 |
Util.log(e, errorMessage); |
417 |
System.err.println(errorMessage); |
418 |
System.err.println(FormatterAppMessages.getString("Exception.skip")); //$NON-NLS-1$ |
419 |
} |
420 |
} |
421 |
|
422 |
} |