From ff4ad07a2e5ac97ed8631c7ee58f3524277c58c8 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Wed, 1 Jun 2016 15:01:30 +0100 Subject: [PATCH] JAL-1424 utility to check resource bundle labels used in code (and some fixes arising) --- resources/lang/Messages.properties | 5 +- src/jalview/fts/core/GFTSPanel.java | 4 +- src/jalview/gui/AlignFrame.java | 2 +- src/jalview/gui/TextColourChooser.java | 5 +- src/jalview/jbgui/GPreferences.java | 2 +- utils/MessageBundleChecker.java | 308 ++++++++++++++++++++++++++++++++ 6 files changed, 318 insertions(+), 8 deletions(-) create mode 100644 utils/MessageBundleChecker.java diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index 8dac5c6..a64e79d 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -1288,7 +1288,6 @@ exception.fts_server_unreachable = Jalview is unable to reach the {0} server. \n label.nw_mapping = Needleman & Wunsch Alignment label.sifts_mapping = SIFTs Mapping label.mapping_method = Sequence \u27f7 Structure mapping method -label.mapping_method = Sequence \u27f7 Structure mapping method status.waiting_for_user_to_select_output_file = Waiting for user to select {0} file. status.cancelled_image_export_operation = Cancelled {0} export operation. info.error_creating_file = Error creating {0} file. @@ -1300,7 +1299,7 @@ label.couldnt_run_groovy_script = Failed to run Groovy script label.uniprot_sequence_fetcher = UniProt Sequence Fetcher action.next_page= >> action.prev_page= << -label.next_page_tooltop=Next Page -label.prev_page_tooltop=Previous Page +label.next_page_tooltip=Next Page +label.prev_page_tooltip=Previous Page exception.bad_request=Bad request. There is a problem with your input. exception.service_not_available=Service not available. The server is being updated, try again later. diff --git a/src/jalview/fts/core/GFTSPanel.java b/src/jalview/fts/core/GFTSPanel.java index 4dc61f0..ee71407 100644 --- a/src/jalview/fts/core/GFTSPanel.java +++ b/src/jalview/fts/core/GFTSPanel.java @@ -348,7 +348,7 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI }); btn_next_page.setEnabled(false); btn_next_page.setToolTipText(MessageManager - .getString("label.next_page_tooltop")); + .getString("label.next_page_tooltip")); btn_next_page.setFont(new java.awt.Font("Verdana", 0, 12)); btn_next_page.setText(MessageManager.getString("action.next_page")); btn_next_page.addActionListener(new java.awt.event.ActionListener() @@ -373,7 +373,7 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI btn_prev_page.setEnabled(false); btn_prev_page.setToolTipText(MessageManager - .getString("label.prev_page_tooltop")); + .getString("label.prev_page_tooltip")); btn_prev_page.setFont(new java.awt.Font("Verdana", 0, 12)); btn_prev_page.setText(MessageManager.getString("action.prev_page")); btn_prev_page.addActionListener(new java.awt.event.ActionListener() diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index 818c150..f59e560 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -4941,7 +4941,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, .getString("label.error_when_translating_sequences_submit_bug_report"); final String errorTitle = MessageManager .getString("label.implementation_error") - + MessageManager.getString("translation_failed"); + + MessageManager.getString("label.translation_failed"); JOptionPane.showMessageDialog(Desktop.desktop, msg, errorTitle, JOptionPane.ERROR_MESSAGE); return; diff --git a/src/jalview/gui/TextColourChooser.java b/src/jalview/gui/TextColourChooser.java index 6bac6df..f1c6768 100644 --- a/src/jalview/gui/TextColourChooser.java +++ b/src/jalview/gui/TextColourChooser.java @@ -72,7 +72,7 @@ public class TextColourChooser final JPanel col2 = new JPanel(); col2.setPreferredSize(new Dimension(40, 20)); col2.setBorder(BorderFactory.createEtchedBorder()); - col2.setToolTipText(MessageManager.getString("label.ligth_colour")); + col2.setToolTipText(MessageManager.getString("label.light_colour")); col2.setBackground(new Color(original2)); final JPanel bigpanel = new JPanel(new BorderLayout()); JPanel panel = new JPanel(); @@ -89,6 +89,7 @@ public class TextColourChooser col1.addMouseListener(new MouseAdapter() { + @Override public void mousePressed(MouseEvent e) { Color col = JColorChooser.showDialog(bigpanel, @@ -104,6 +105,7 @@ public class TextColourChooser col2.addMouseListener(new MouseAdapter() { + @Override public void mousePressed(MouseEvent e) { Color col = JColorChooser.showDialog(bigpanel, @@ -119,6 +121,7 @@ public class TextColourChooser slider.addChangeListener(new ChangeListener() { + @Override public void stateChanged(ChangeEvent evt) { thresholdChanged(slider.getValue()); diff --git a/src/jalview/jbgui/GPreferences.java b/src/jalview/jbgui/GPreferences.java index 25727d0..90053f5 100755 --- a/src/jalview/jbgui/GPreferences.java +++ b/src/jalview/jbgui/GPreferences.java @@ -1236,7 +1236,7 @@ public class GPreferences extends JPanel .getString("label.open_overview")); openoverv.setHorizontalAlignment(SwingConstants.RIGHT); openoverv.setHorizontalTextPosition(SwingConstants.LEFT); - openoverv.setText(MessageManager.getString(("label.open_overview"))); + openoverv.setText(MessageManager.getString("label.open_overview")); JPanel jPanel2 = new JPanel(); jPanel2.setBounds(new Rectangle(7, 17, 158, 310)); jPanel2.setLayout(new GridLayout(14, 1)); diff --git a/utils/MessageBundleChecker.java b/utils/MessageBundleChecker.java new file mode 100644 index 0000000..441e474 --- /dev/null +++ b/utils/MessageBundleChecker.java @@ -0,0 +1,308 @@ +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.HashSet; +import java.util.Properties; +import java.util.TreeSet; + +/** + * This class scans Java source files for calls to MessageManager and reports + * + * It does not handle dynamically constructed keys, these are reported as + * possible errors for manual inspection.
+ * For comparing translated bundles with Messages.properties, see i18nAnt.xml + * + * @author gmcarstairs + * + */ +public class MessageBundleChecker +{ + /* + * number of text lines to read at a time in order to parse + * code that is split over several lines + */ + static int bufferSize = 3; + + static final String METHOD1 = "MessageManager.getString("; + + static final String METHOD2 = "MessageManager.getStringOrReturn("; + + static final String METHOD3 = "MessageManager.formatMessage("; + + static final String[] METHODS = { METHOD1, METHOD2, METHOD3 }; + + /* + * root of the Java source folders we want to scan + */ + String sourcePath; + + /* + * contents of Messages.properties + */ + private Properties messages; + + /* + * keys from Messages.properties + * we remove entries from here as they are found to be used + * any left over are unused entries + */ + private TreeSet messageKeys; + + private int javaCount; + + private HashSet invalidKeys; + + /** + * Runs the scan given the path to the root of Java source directories + * + * @param args + * [0] path to the source folder to scan + * @param args + * [1] (optional) read buffer size (default is 3); increasing this + * may detect more results but will give higher error counts due to + * double counting of the same code + * @throws IOException + */ + public static void main(String[] args) throws IOException + { + if (args.length != 1 && args.length != 2) + { + System.out.println("Usage: [readBufferSize]"); + return; + } + if (args.length == 2) + { + bufferSize = Integer.valueOf(args[1]); + } + new MessageBundleChecker().doMain(args[0]); + } + + /** + * Main method to perform the work + * + * @param srcPath + * @throws IOException + */ + private void doMain(String srcPath) throws IOException + { + System.out.println("Scanning " + srcPath + + " for calls to MessageManager"); + sourcePath = srcPath; + loadMessages(); + File dir = new File(srcPath); + if (!dir.exists()) + { + System.out.println(srcPath + " not found"); + return; + } + invalidKeys = new HashSet(); + if (dir.isDirectory()) + { + scanDirectory(dir); + } + else + { + scanFile(dir); + } + reportResults(); + } + + /** + * Prints out counts to sysout + */ + private void reportResults() + { + System.out.println("\nScanned " + javaCount + " source files"); + System.out.println("Message.properties has " + messages.size() + + " keys"); + System.out.println("Found " + invalidKeys.size() + + " possibly invalid parameter calls"); + + System.out.println(messageKeys.size() + + " keys not found, possibly unused"); + for (String key : messageKeys) + { + System.out.println(" " + key); + } + } + + /** + * Scan all files within a directory + * + * @param dir + * @throws IOException + */ + private void scanDirectory(File dir) throws IOException + { + File[] files = dir.listFiles(); + if (files != null) + { + for (File f : files) + { + if (f.isDirectory()) + { + scanDirectory(f); + } + else + { + scanFile(f); + } + } + } + } + + /** + * Scan a Java file + * + * @param f + */ + private void scanFile(File f) throws IOException + { + String path = f.getPath(); + if (!path.endsWith(".java")) + { + return; + } + javaCount++; + + String[] lines = new String[bufferSize]; + BufferedReader br = new BufferedReader(new FileReader(f)); + for (int i = 0; i < bufferSize; i++) + { + String readLine = br.readLine(); + lines[i] = stripCommentsAndTrim(readLine); + } + + int lineNo = 0; + + while (lines[bufferSize - 1] != null) + { + lineNo++; + inspectSourceLines(path, lineNo, lines); + + for (int i = 0; i < bufferSize - 1; i++) + { + lines[i] = lines[i + 1]; + } + lines[bufferSize - 1] = stripCommentsAndTrim(br.readLine()); + } + br.close(); + + } + + /* + * removes anything after (and including) '//' + */ + private String stripCommentsAndTrim(String line) + { + if (line != null) + { + int pos = line.indexOf("//"); + if (pos != -1) + { + line = line.substring(0, pos); + } + line = line.replace("\t", " ").trim(); + } + return line; + } + + /** + * Look for calls to MessageManager methods, possibly split over two or more + * lines + * + * @param path + * @param lineNo + * @param lines + */ + private void inspectSourceLines(String path, int lineNo, String[] lines) + { + String lineNos = String.format("%d-%d", lineNo, lineNo + lines.length + - 1); + String combined = combineLines(lines); + for (String method : METHODS) + { + int pos = combined.indexOf(method); + if (pos == -1) + { + continue; + } + String methodArgs = combined.substring(pos + method.length()); + if ("".equals(methodArgs)) + { + /* + * continues on next line - catch in the next read loop iteration + */ + continue; + } + if (!methodArgs.startsWith("\"")) + { + System.out.println(String.format("Trouble parsing %s line %s %s", + path.substring(sourcePath.length()), lineNos, combined)); + continue; + } + methodArgs = methodArgs.substring(1); + int quotePos = methodArgs.indexOf("\""); + if (quotePos == -1) + { + System.out.println(String.format("Trouble parsing %s line %s %s", + path.substring(sourcePath.length()), lineNos, combined)); + continue; + } + String messageKey = methodArgs.substring(0, quotePos); + if (!this.messages.containsKey(messageKey)) + { + System.out.println(String.format( + "Unmatched key '%s' at line %s of %s", messageKey, lineNos, + path.substring(sourcePath.length()))); + if (!invalidKeys.contains(messageKey)) + { + invalidKeys.add(messageKey); + } + } + messageKeys.remove(messageKey); + } + } + + private String combineLines(String[] lines) + { + String combined = ""; + if (lines != null) + { + for (String line : lines) + { + if (line != null) + { + combined += line; + } + } + } + return combined; + } + + /** + * Loads properties from Message.properties + * + * @throws IOException + */ + void loadMessages() throws IOException + { + messages = new Properties(); + FileReader reader = new FileReader(new File(sourcePath, + "../resources/lang/Messages.properties")); + messages.load(reader); + reader.close(); + + messageKeys = new TreeSet(); + for (Object key : messages.keySet()) + { + messageKeys.add((String) key); + } + + } + +} -- 1.7.10.2