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