001: package net.sourceforge.squirrel_sql.plugins.i18n;
002:
003: import java.awt.event.ActionEvent;
004: import java.awt.event.ActionListener;
005: import java.io.BufferedReader;
006: import java.io.File;
007: import java.io.FileInputStream;
008: import java.io.FileOutputStream;
009: import java.io.FileReader;
010: import java.io.PrintWriter;
011: import java.util.ArrayList;
012: import java.util.Collections;
013: import java.util.Properties;
014: import java.util.prefs.Preferences;
015: import java.util.regex.Matcher;
016: import java.util.regex.Pattern;
017:
018: import javax.swing.JFileChooser;
019: import javax.swing.JOptionPane;
020:
021: import net.sourceforge.squirrel_sql.client.IApplication;
022: import net.sourceforge.squirrel_sql.fw.util.StringManager;
023: import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
024:
025: public class DevelopersController {
026: private static final StringManager s_stringMgr = StringManagerFactory
027: .getStringManager(DevelopersController.class);
028:
029: private DevelopersPanel _panel;
030: private IApplication _app;
031: private static final String PREF_KEY_SOURCE_DIR = "SquirrelSQL.i18n.sourceDir";
032: private static final String PREF_KEY_INCLUDE_TIMESTAMP = "SquirrelSQL.i18n.includeTimestamp";
033:
034: private static Preferences prefs = null;
035:
036: public DevelopersController(DevelopersPanel pnlDevelopers) {
037: _panel = pnlDevelopers;
038:
039: _panel.btnChooseSourceDir
040: .addActionListener(new ActionListener() {
041: public void actionPerformed(ActionEvent e) {
042: onChooseSourceDir();
043: }
044:
045: });
046:
047: _panel.btnAppendI18nInCode
048: .addActionListener(new ActionListener() {
049: public void actionPerformed(ActionEvent e) {
050: onAppendI18nInCode();
051: }
052: });
053:
054: prefs = Preferences.userRoot();
055: String sourceDir = prefs.get(PREF_KEY_SOURCE_DIR, null);
056: _panel.txtSourceDir.setText(sourceDir);
057: }
058:
059: private void onChooseSourceDir() {
060: String startDir = System.getProperties().getProperty(
061: "user.home");
062: if (_panel.txtSourceDir.getText() != null
063: && !"".equals(_panel.txtSourceDir.getText())) {
064: startDir = _panel.txtSourceDir.getText();
065: }
066: JFileChooser chooser = new JFileChooser(startDir);
067: chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
068: chooser.showOpenDialog(_app.getMainFrame());
069:
070: if (null != chooser.getSelectedFile()) {
071: _panel.txtSourceDir.setText(chooser.getSelectedFile()
072: .getPath());
073: }
074: }
075:
076: private void onAppendI18nInCode() {
077: File sourceDir = getSourceDir();
078:
079: if (null == sourceDir) {
080: return;
081: }
082:
083: appendProps(sourceDir);
084: // i18n[i18n.ParsingFinish=Parsing finished]
085: _app.getMessageHandler().showMessage(
086: s_stringMgr.getString("i18n.ParsingFinish"));
087: }
088:
089: private void appendProps(File sourceDir) {
090:
091: try {
092: File[] files = sourceDir.listFiles();
093:
094: ArrayList<String> newProps = new ArrayList<String>();
095: ArrayList<String> replaceProps = new ArrayList<String>();
096:
097: File i18nStringFile = new File(sourceDir,
098: "I18NStrings.properties");
099: Properties curProps = new Properties();
100: FileInputStream fis;
101:
102: if (i18nStringFile.exists()) {
103: fis = new FileInputStream(i18nStringFile);
104: curProps.load(fis);
105: fis.close();
106: }
107:
108: for (int i = 0; i < files.length; i++) {
109:
110: if (files[i].isDirectory()
111: && false == "CVS".equals(files[i].getName())) {
112: appendProps(files[i]);
113: } else if (files[i].getName().endsWith(".java")) {
114: int occurrences = 0;
115: StringBuffer code = new StringBuffer();
116: fis = new FileInputStream(files[i]);
117:
118: int buf = fis.read();
119: while (-1 != buf) {
120: code.append((char) buf);
121: buf = fis.read();
122: }
123:
124: fis.close();
125:
126: try {
127: occurrences = parseProps(code.toString(),
128: curProps, newProps, replaceProps);
129: if (occurrences > 0) {
130: int occurrencesFound = fixSourceFile(files[i]
131: .getAbsolutePath());
132: if (occurrences != occurrencesFound) {
133: Object[] params = new Object[] {
134: Integer.valueOf(occurrences),
135: Integer
136: .valueOf(occurrencesFound),
137: files[i].getPath() };
138:
139: // i18n[i18n.unequalOccurrences=Found {0} i18n comments but only {1} places
140: // to convert to s_stringMgr.getString() in file {2}]
141: String msg = s_stringMgr.getString(
142: "i18n.unequalOccurrences",
143: params);
144: _app.getMessageHandler()
145: .showErrorMessage(msg);
146: }
147: }
148: } catch (Exception e) {
149: Object[] params = new Object[] {
150: files[i].getPath(), e.toString() };
151: _app.getMessageHandler().showErrorMessage(
152: s_stringMgr.getString(
153: "i18n.failedToParse", params));
154: // i18n[i18n.failedToParse=Failed to parse {0}\n{1}]
155: continue;
156: }
157: }
158: }
159:
160: if (0 < newProps.size() || 0 < replaceProps.size()) {
161: FileOutputStream fos = new FileOutputStream(
162: i18nStringFile, true);
163: PrintWriter ps = new PrintWriter(fos);
164:
165: String includeTimestamp = prefs.get(
166: PREF_KEY_INCLUDE_TIMESTAMP, "true");
167: if (includeTimestamp.equals("true")) {
168: // No i18n, developers should write English props.
169: ps
170: .println("\n#\n#Missing/changed properties generated by I18n Plugin on "
171: + new java.util.Date() + "\n#");
172: }
173:
174: Collections.sort(newProps);
175: for (int j = 0; j < newProps.size(); j++) {
176: ps.println(newProps.get(j));
177: }
178:
179: for (int j = 0; j < replaceProps.size(); j++) {
180: // No i18n, developers should write English props.
181: ps.println();
182: ps
183: .println("# A T T E N T I O N: REPLACES SAME KEY ABOVE");
184: ps.println(replaceProps.get(j));
185: }
186:
187: ps.flush();
188: fos.flush();
189: ps.close();
190: fos.close();
191:
192: Object[] params = new Object[] {
193: Integer.valueOf(newProps.size()),
194: Integer.valueOf(replaceProps.size()),
195: i18nStringFile.getPath() };
196:
197: _app.getMessageHandler().showMessage(
198: s_stringMgr.getString("i18n.parseSuccess",
199: params));
200: // i18n[i18n.parseSuccess=Added {0} new and {1} replaced properties to {2}]
201: }
202:
203: } catch (Exception e) {
204: throw new RuntimeException(e);
205: }
206:
207: }
208:
209: private int parseProps(String code, Properties curProps,
210: ArrayList<String> newProps, ArrayList<String> replaceProps)
211: throws I18nParseException {
212: int occurrences = 0;
213: code = code.replace('\r', ' ');
214:
215: Pattern pat = Pattern.compile("//\\x20*i18n\\[(.*)");
216: Matcher m = pat.matcher(code);
217:
218: int[] propBounds = new int[] { 0, 0 };
219:
220: while (m.find(propBounds[1])) {
221: occurrences++;
222: propBounds[0] = m.start(m.groupCount());
223: String prop = getProp(code, propBounds);
224:
225: int equalsPos = prop.indexOf('=');
226: if (0 > equalsPos) {
227: throw new I18nParseException("Property " + prop
228: + " has no key.");
229: }
230: String key = prop.substring(0, equalsPos);
231: String val = prop.substring(equalsPos + 1).trim();
232:
233: if (curProps.containsKey(key)
234: && false == val.equals(I18nUtils
235: .normalizePropVal((String) curProps
236: .get(key)))) {
237: replaceProps.add(prop);
238: } else if (false == curProps.containsKey(key)) {
239: boolean found = false;
240: for (int i = 0; i < newProps.size(); i++) {
241: if (newProps.get(i).split("=")[0].startsWith(key
242: .split("=")[0])) {
243: found = true;
244: replaceProps.add(prop);
245: break;
246: }
247:
248: }
249:
250: if (false == found) {
251: newProps.add(prop);
252: }
253:
254: }
255: }
256: return occurrences;
257: }
258:
259: private String getProp(String code, int[] propBounds)
260: throws I18nParseException {
261: boolean isInComment = true;
262: boolean isABracket = false;
263: boolean isASlash = false;
264: boolean isInCommentBegin = false;
265:
266: StringBuffer ret = new StringBuffer();
267:
268: for (int i = propBounds[0]; i < code.length(); ++i) {
269: if (isInComment && isABracket && ']' != code.charAt(i)) {
270: if (isABracket) {
271: propBounds[1] = i;
272: return I18nUtils.normalizePropVal(ret.toString());
273: }
274: } else if (isInComment && ']' == code.charAt(i)) {
275: isABracket = !isABracket;
276: isASlash = false;
277: } else if (isInComment && '\n' == code.charAt(i)) {
278: isInComment = false;
279: isABracket = false;
280: isASlash = false;
281: } else if (false == isInComment && '\n' == code.charAt(i)) {
282: throw new I18nParseException("Property "
283: + ret.toString() + " does not end with ]");
284: } else if (false == isInComment && false == isASlash
285: && '/' == code.charAt(i)) {
286: isASlash = true;
287: isABracket = false;
288: } else if (false == isInComment && isASlash
289: && '/' == code.charAt(i)) {
290: isInComment = true;
291: isInCommentBegin = true;
292: isABracket = false;
293: isASlash = false;
294: }
295:
296: if (isInComment && false == isInCommentBegin
297: && false == isABracket) {
298: ret.append(code.charAt(i));
299: }
300:
301: isInCommentBegin = false;
302:
303: }
304:
305: if (ret.toString().length() > 50) {
306: ret.setLength(50);
307: }
308:
309: throw new I18nParseException("Property " + ret.toString()
310: + " does not end with ]");
311: }
312:
313: private File getSourceDir() {
314: String buf = _panel.txtSourceDir.getText();
315: if (null == buf || 0 == buf.trim().length()) {
316: String msg = s_stringMgr.getString("I18n.NoSourceDir");
317: // i18n[I18n.NoSourceDir=Please choose a source directory.]
318: JOptionPane.showMessageDialog(_app.getMainFrame(), msg);
319: return null;
320:
321: }
322:
323: File sourceDir = new File(buf);
324: if (false == sourceDir.isDirectory()) {
325: String msg = s_stringMgr.getString(
326: "I18n.SourceDirIsNotADirectory", sourceDir
327: .getPath());
328: // i18n[I18n.SourceDirIsNotADirectory=Source directory {0} is not a directory.]
329: JOptionPane.showMessageDialog(_app.getMainFrame(), msg);
330: }
331:
332: if (false == sourceDir.exists()) {
333: String msg = s_stringMgr.getString(
334: "I18n.SourceDirDoesNotExist", sourceDir.getPath());
335: // i18n[I18n.SourceDirDoesNotExist=Source directory {0} does not exist.]
336: JOptionPane.showMessageDialog(_app.getMainFrame(), msg);
337: return null;
338: }
339:
340: return sourceDir;
341: }
342:
343: private int fixSourceFile(String filename) throws Exception {
344: BufferedReader in = new BufferedReader(new FileReader(filename));
345: String nextLine = in.readLine();
346: String lineToPrint = nextLine;
347: int occurrencesReplaced = 0;
348: boolean writeFixFile = false;
349:
350: ArrayList<String> linesToPrint = new ArrayList<String>();
351:
352: Pattern pat = Pattern.compile("\\s*//\\s*i18n\\[(.*)");
353: Pattern commentLinePattern = Pattern.compile("\\s*//");
354: while (nextLine != null) {
355: Matcher m = pat.matcher(nextLine);
356: if (m.matches()) {
357: String[] parts = nextLine.split("\\[");
358: if (1 < parts.length) {
359: parts = parts[1].split("\\]");
360: if (0 < parts.length) {
361: parts = parts[0].split("=");
362: if (1 < parts.length) {
363: String key = parts[0];
364: String val = parts[1];
365:
366: // print the i18n comment
367: linesToPrint.add(nextLine);
368:
369: nextLine = in.readLine();
370: Matcher commentMatch = commentLinePattern
371: .matcher(nextLine);
372:
373: if (!commentMatch.matches()) {
374: String quotedVal = "\"" + val + "\"";
375: int indexOfQuotedVal = nextLine
376: .indexOf(quotedVal);
377:
378: lineToPrint = nextLine;
379: if (-1 < indexOfQuotedVal) {
380: // Rob: Removed replacement via RegExp because it needed several RegExp escapes in val.
381:
382: String stringManager = "s_stringMgr.getString(\""
383: + key + "\")";
384: lineToPrint = nextLine.substring(0,
385: indexOfQuotedVal)
386: + stringManager
387: + nextLine
388: .substring(indexOfQuotedVal
389: + quotedVal
390: .length());
391:
392: writeFixFile = true;
393: occurrencesReplaced++;
394: } else {
395: String stringManagerBegin = "s_stringMgr.getString(\""
396: + key + "\""; // No end bracket, params might follow
397: if (-1 < nextLine
398: .indexOf(stringManagerBegin)) {
399: // We see that the replacement was already done before so we can count this as replaced
400: occurrencesReplaced++;
401: }
402: }
403: } else {
404: lineToPrint = nextLine;
405: // here we've hit the second line of a multi-line i18n stanza
406: // Just skip it, we're not that sophisticated.
407: }
408: }
409: }
410: }
411:
412: }
413: linesToPrint.add(lineToPrint);
414: nextLine = in.readLine();
415: lineToPrint = nextLine;
416: }
417: in.close();
418:
419: if (writeFixFile) {
420: String outFileName = filename + ".fixed";
421: PrintWriter out = new PrintWriter(new FileOutputStream(
422: outFileName));
423: for (int i = 0; i < linesToPrint.size(); i++) {
424: out.println(linesToPrint.get(i));
425: }
426: out.flush();
427: out.close();
428: // i18n[i18n.wroteFixedFile=Wrote file {0}]
429: _app.getMessageHandler().showMessage(
430: s_stringMgr.getString("i18n.wroteFixedFile",
431: outFileName));
432: }
433:
434: return occurrencesReplaced;
435: }
436:
437: public void initialize(IApplication app) {
438: _app = app;
439: }
440:
441: public void uninitialize() {
442: Preferences.userRoot().put(PREF_KEY_SOURCE_DIR,
443: _panel.txtSourceDir.getText());
444: }
445:
446: }
|