From 0c5b93800e1dcc946a414c4b844c776a711ecc32 Mon Sep 17 00:00:00 2001
From: Jean Chalard <jchalard@google.com>
Date: Thu, 25 Oct 2012 18:30:26 +0900
Subject: [PATCH] Add the dicttool diff command. (A8)

This is an initial implementation, it does not
do everything just yet.

Bug: 7388857
Change-Id: I11543973a4657e122ca3475225059b06b9a3f2b9
---
 .../latin/dicttool/CommandList.java           |   1 +
 .../inputmethod/latin/dicttool/Diff.java      | 108 ++++++++++++++++++
 2 files changed, 109 insertions(+)
 create mode 100644 tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java

diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CommandList.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CommandList.java
index d16b069fed..889dde685d 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CommandList.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CommandList.java
@@ -19,6 +19,7 @@ package com.android.inputmethod.latin.dicttool;
 public class CommandList {
     public static void populate() {
         Dicttool.addCommand("info", Info.class);
+        Dicttool.addCommand("diff", Diff.class);
         Dicttool.addCommand("compress", Compress.Compressor.class);
         Dicttool.addCommand("uncompress", Compress.Uncompressor.class);
         Dicttool.addCommand("makedict", Makedict.class);
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java
new file mode 100644
index 0000000000..60ba9383dc
--- /dev/null
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java
@@ -0,0 +1,108 @@
+/**
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin.dicttool;
+
+import com.android.inputmethod.latin.makedict.FusionDictionary;
+
+import java.util.Arrays;
+
+public class Diff extends Dicttool.Command {
+    public static final String COMMAND = "diff";
+
+    public Diff() {
+    }
+
+    @Override
+    public String getHelp() {
+        return COMMAND + " [-p] <dict> <dict> : shows differences between two dictionaries.\n"
+                + "  If -p (porcelain) option is given, produce output suitable for a script";
+    }
+
+    @Override
+    public void run() {
+        if (mArgs.length < 2) {
+            throw new RuntimeException("Not enough arguments for command " + COMMAND);
+        }
+        final boolean porcelain;
+        if ("-p".equals(mArgs[0])) {
+            porcelain = true;
+            mArgs = Arrays.copyOfRange(mArgs, 1, mArgs.length);
+            if (mArgs.length != 2) { // There should be only 2 arguments left
+                throw new RuntimeException("Wrong number of arguments for command " + COMMAND);
+            }
+        } else {
+            porcelain = false;
+        }
+        final FusionDictionary dict0 =
+                BinaryDictOffdeviceUtils.getDictionary(mArgs[0], false /* report */);
+        if (null == dict0) throw new RuntimeException("Can't read dictionary " + mArgs[0]);
+        final FusionDictionary dict1 =
+                BinaryDictOffdeviceUtils.getDictionary(mArgs[1], false /* report */);
+        if (null == dict1) throw new RuntimeException("Can't read dictionary " + mArgs[1]);
+        if (!porcelain) {
+            System.out.println("Header :");
+            diffHeaders(dict0, dict1);
+            if (languageDiffers(dict0, dict1)) {
+                // diffHeaders returns whether the language is different. If it is, we should bail
+                // to avoid flooding the output with thousands of useless word-level diffs.
+                return;
+            }
+            System.out.println("Body :");
+        }
+        // TODO: implement the word-level diff
+    }
+
+    private static boolean languageDiffers(final FusionDictionary dict0,
+            final FusionDictionary dict1) {
+        // If either of the dictionaries have no locale, assume it's okay
+        // We only check for the language here. The rationale is that one may meaningfully diff
+        // a en_US with a en_GB dictionary, but someone who diffs a de dict with a pt_BR dict
+        // is almost certainly only interested in header-level diff, and the word diff would be very
+        // large, meaningless, and annoying.
+        if (null == dict0.mOptions.mAttributes.get("locale")) return true;
+        if (null == dict1.mOptions.mAttributes.get("locale")) return true;
+        final String dict0Lang = dict0.mOptions.mAttributes.get("locale").split("_", 3)[0];
+        final String dict1Lang = dict1.mOptions.mAttributes.get("locale").split("_", 3)[0];
+        return !dict0Lang.equals(dict1Lang);
+    }
+
+    private static void diffHeaders(final FusionDictionary dict0, final FusionDictionary dict1) {
+        if (dict0.mOptions.mFrenchLigatureProcessing != dict1.mOptions.mFrenchLigatureProcessing) {
+            System.out.println("  French ligature processing : "
+                    + dict0.mOptions.mFrenchLigatureProcessing + " <=> "
+                    + dict1.mOptions.mFrenchLigatureProcessing);
+        }
+        else if (dict0.mOptions.mGermanUmlautProcessing != dict1.mOptions.mGermanUmlautProcessing) {
+            System.out.println("  German umlaut processing : "
+                    + dict0.mOptions.mGermanUmlautProcessing + " <=> "
+                    + dict1.mOptions.mGermanUmlautProcessing);
+        }
+        for (final String optionKey : dict0.mOptions.mAttributes.keySet()) {
+            if (!dict0.mOptions.mAttributes.get(optionKey).equals(
+                    dict1.mOptions.mAttributes.get(optionKey))) {
+                System.out.println("  " + optionKey + " : "
+                        + dict0.mOptions.mAttributes.get(optionKey) + " <=> "
+                        + dict1.mOptions.mAttributes.get(optionKey));
+            }
+            dict1.mOptions.mAttributes.remove(optionKey);
+        }
+        for (final String optionKey : dict1.mOptions.mAttributes.keySet()) {
+            System.out.println("  " + optionKey + " : null <=> "
+                    + dict1.mOptions.mAttributes.get(optionKey));
+        }
+    }
+}
-- 
GitLab