Merge "sourcedr: Use relative path to android root dir"
diff --git a/vndk/tools/source-deps-reviewer/sourcedr/functional_tests.py b/vndk/tools/source-deps-reviewer/sourcedr/functional_tests.py
index c97aed8..630ced4 100755
--- a/vndk/tools/source-deps-reviewer/sourcedr/functional_tests.py
+++ b/vndk/tools/source-deps-reviewer/sourcedr/functional_tests.py
@@ -1,32 +1,39 @@
 #!/usr/bin/env python3
 
-from sourcedr.data_utils import *
+from sourcedr.data_utils import data_path, load_data, remove_data
 from sourcedr.preprocess import CodeSearch
-from sourcedr.server import *
+from sourcedr.server import app, args
 
 from flask import Flask, jsonify, render_template, request
 from flask_testing import LiveServerTestCase, TestCase
 from urllib.request import urlopen
+
 import flask_testing
+import json
 import os
 import unittest
 
 app.config['TESTING'] = True
 
+ANDROID_ROOT = 'sourcedr/test'
+
 class TestPreprocess(unittest.TestCase):
     def test_prepare(self):
         remove_data()
-        engine = CodeSearch.create_default(android_root='sourcedr/test')
+        engine = CodeSearch.create_default(android_root=ANDROID_ROOT)
         engine.build_index()
         engine.find(patterns=['dlopen'], is_regexs=[False])
         self.assertTrue(os.path.exists(data_path))
 
 class TestViews(TestCase):
     def create_app(self):
+        # TODO: This refers to `sourcedr.server.args`.  This should be removed
+        # in the upcoming refactor process.
+        args.android_root = ANDROID_ROOT
         return app
 
     def setUp(self):
-        engine = CodeSearch.create_default(android_root='sourcedr/test')
+        engine = CodeSearch.create_default(android_root=ANDROID_ROOT)
         engine.build_index()
         engine.find(patterns=['dlopen'], is_regexs=[False])
 
@@ -34,15 +41,15 @@
         remove_data()
 
     def test_get_file(self):
-        test_arg = 'sourcedr/test/example.c'
+        test_arg = 'example.c'
         response = self.client.get('/get_file',
                                    query_string=dict(path=test_arg))
         ret = response.json['result']
-        with open(test_arg, 'r') as f:
+        with open(os.path.join(ANDROID_ROOT, test_arg), 'r') as f:
             self.assertEqual(ret, f.read())
 
     def test_load_file(self):
-        test_arg = os.path.abspath('sourcedr/test/dlopen/test.c')
+        test_arg = 'dlopen/test.c'
         test_arg += ':10:    handle = dlopen("libm.so.6", RTLD_LAZY);'
         response = self.client.get('/load_file',
                                    query_string=dict(path=test_arg))
diff --git a/vndk/tools/source-deps-reviewer/sourcedr/preprocess.py b/vndk/tools/source-deps-reviewer/sourcedr/preprocess.py
index 9176fec..a859c9e 100755
--- a/vndk/tools/source-deps-reviewer/sourcedr/preprocess.py
+++ b/vndk/tools/source-deps-reviewer/sourcedr/preprocess.py
@@ -170,7 +170,8 @@
         return cs
 
     def __init__(self, android_root, index_path):
-        self.android_root = android_root
+        android_root = os.path.expanduser(android_root)
+        self.android_root = os.path.abspath(android_root)
         self.env = dict(os.environ)
         self.env["CSEARCHINDEX"] = os.path.abspath(index_path)
         self.filters = {}
@@ -180,7 +181,7 @@
             self.filters[ext] = Filter
 
     def build_index(self):
-        android_root = os.path.expanduser(self.android_root)
+        android_root = self.android_root
         print('building csearchindex for the directory ' + android_root + '...')
         subprocess.call(['cindex', android_root], env=self.env)
 
@@ -195,6 +196,20 @@
             pass
         return code
 
+    def remove_prefix(self, raw_grep):
+        ret = b''
+        patt = re.compile(b'([^:]+):(\\d+):(.*)$')
+        for line in raw_grep.split(b'\n'):
+            match = patt.match(line)
+            if not match:
+                continue
+            file_path = os.path.relpath(match.group(1),
+                                        self.android_root.encode('utf-8'))
+            line_no = match.group(2)
+            code = match.group(3)
+            ret += file_path + b':' + line_no + b':' + code + b'\n'
+        return ret
+
     def process_grep(self, raw_grep, pattern, is_regex):
         pattern = pattern.encode('utf-8')
         if not is_regex:
@@ -225,11 +240,12 @@
             if any(patt in file_path for patt in PATH_PATTERN_BLACK_LIST):
                 continue
 
+            abs_file_path = os.path.join(self.android_root.encode('utf-8'),
+                                            file_path)
             # Check if any pattern can be found after sanitize_code
-            if not pattern.search(self.sanitize_code(file_path)):
+            if not pattern.search(self.sanitize_code(abs_file_path)):
                 continue
-
-            suspect[file_path].append((file_path, line_no, code))
+            suspect[abs_file_path].append((file_path, line_no, code))
 
         suspect = sorted(suspect.items())
 
@@ -283,13 +299,13 @@
         try:
             raw_grep = subprocess.check_output(
                 ['csearch', '-n', pattern],
-                cwd=os.path.expanduser(self.android_root),
+                cwd=self.android_root,
                 env=self.env)
         except subprocess.CalledProcessError as e:
             if e.output == b'':
                 print('nothing found')
                 return b''
-        return raw_grep
+        return self.remove_prefix(raw_grep)
 
     def raw_search(self, pattern, is_regex):
         if not is_regex:
diff --git a/vndk/tools/source-deps-reviewer/sourcedr/server.py b/vndk/tools/source-deps-reviewer/sourcedr/server.py
index be4e323..fdccf8a 100755
--- a/vndk/tools/source-deps-reviewer/sourcedr/server.py
+++ b/vndk/tools/source-deps-reviewer/sourcedr/server.py
@@ -20,22 +20,27 @@
 if sys.version_info < (3,):
     input = raw_input
 
+# XXX: Global variable to workaround args.android_root in test cases.  This
+# should be removed in the upcoming refactoring process.
+args = argparse.Namespace()
+
 app = Flask(__name__)
 
 # whether the code segment is exactly in file
 def same(fl, code):
+    fl = os.path.join(args.android_root, fl)
     with open(fl, 'r') as f:
         fc = f.read()
         return code in fc
 
 # check if the file needes to be reiewed again
 def check(codes):
+    ret = []
     for item in codes:
         fl = item.split(':')[0]
         code = item[len(fl) + 1:]
-        if not same(fl, code):
-            return False
-    return True
+        ret.append(same(fl, code))
+    return ret
 
 @app.route('/get_started')
 def _get_started():
@@ -43,7 +48,7 @@
     for key, item in sorted(data.items()):
         lst.append(key)
         if item[0]:
-            done.append(check(item[1]))
+            done.append(all(check(item[1])))
         else:
             done.append(False)
 
@@ -64,11 +69,14 @@
         return jsonify(result='')
     deps, codes = data[path]
 
-    return jsonify(deps=json.dumps(deps), codes=json.dumps(codes))
+    return jsonify(deps=json.dumps(deps), codes=json.dumps(codes),
+                   okays=json.dumps(check(codes)))
 
 @app.route('/get_file')
 def _get_file():
     path = request.args.get('path')
+    path = os.path.join(args.android_root, path)
+
     if not os.path.exists(path):
         return jsonify(result='No such file')
     with open(path, 'r') as f:
@@ -98,7 +106,6 @@
     save_new_pattern(patt, is_regex)
     return jsonify(result='done')
 
-
 # This function does a temporary grep to the directory
 # Not adding the result to database
 @app.route('/temporary_search')
@@ -174,6 +181,7 @@
     # a CodeSearch engine must be initialized with the
     # root of the directory and the path of the csearch index file
     engine = CodeSearch.create_default(args.android_root, args.index_path)
+    args.android_root = os.path.expanduser(args.android_root)
 
     print('Be careful that previous data files will merge with new data files.')
     print('Delete previous data files(data.json, patterns) if you want ' +
diff --git a/vndk/tools/source-deps-reviewer/sourcedr/static/js/main.js b/vndk/tools/source-deps-reviewer/sourcedr/static/js/main.js
index db62041..e805705 100644
--- a/vndk/tools/source-deps-reviewer/sourcedr/static/js/main.js
+++ b/vndk/tools/source-deps-reviewer/sourcedr/static/js/main.js
@@ -5,6 +5,33 @@
   var counter = 0;
   var current_item = null;
 
+  // make item list sortable
+  $( function() {
+    $("#item_list").sortable();
+    $("#item_list").disableSelection();
+  });
+
+  function moveToTop(index) {
+    if (index == 0) {
+        return;
+    }
+    let target = $('#item_list').children().eq(index);
+    let tp = $('#item_list').children().eq(0);
+    let old_offset = target.position();
+    tp.before(target);
+    let new_offset = target.position();
+    let tmp = target.clone().appendTo('#item_list')
+                    .css('position', 'absolute')
+                    .css('left', old_offset.left)
+                    .css('top', old_offset.top);
+    target.hide();
+    let new_pos = {'top': new_offset.top, 'left': new_offset.left};
+    tmp.animate(new_pos, 'slow', function() {
+      target.show();
+      tmp.remove();
+    });
+  }
+
   function getSelText() {
     let txt = window.getSelection();
     $('#selected_text').val(txt);
@@ -18,8 +45,9 @@
       '<input type="submit" class="delete" value="X">' +'</li>';
   }
 
-  function codeHtml(text, cnt) {
-    return '<li><span id="code' + cnt + '">' + text +
+  function codeHtml(text, cnt, okay) {
+    return (okay? '<li>' : '<li style="color:red;">') +
+      '<span id="code' + cnt + '">' + text +
       '</span><input type="submit" class="delete" value="X">' + '</li>';
   }
 
@@ -90,7 +118,7 @@
 
   function enterCode() {
     let text = $('#code_file_path').val() + ':' + $('#selected_text').val();
-    $('#code_list').append(codeHtml(text, ccounter));
+    $('#code_list').append(codeHtml(text, ccounter, true));
     $('.delete').click(function () {
       $(this).parent().remove();
     });
@@ -98,13 +126,13 @@
     return false;
   }
 
-  function setCode(codes) {
+  function setCode(codes, okays) {
     $('#code_list').empty();
     ccounter = 0;
     let len = codes.length;
     for (let i = 0; i < len; i++) {
       let text = codes[i];
-      $('#code_list').append(codeHtml(text, ccounter));
+      $('#code_list').append(codeHtml(text, ccounter, okays[i]));
       $('.delete').click(function () {
         $(this).parent().remove();
       });
@@ -165,6 +193,23 @@
       deps: JSON.stringify(deps),
       codes: JSON.stringify(codes)
     });
+    let target = $(current_item).text().split(':')[2];
+    let children = $('#item_list')[0].children;
+    let len = children.length;
+    for (let i = 0; i < len; i++) {
+        let tt = children[i].getElementsByTagName('a')[0].innerHTML;
+        if (tt == $(current_item).text()) {
+            continue;
+        }
+        if (children[i].getElementsByTagName('a')[0].className ==
+            'list-group-item list-group-item-success' ) {
+            continue;
+        }
+        let content = tt.split(':')[2];
+        if (content == target) {
+            moveToTop(i);
+        }
+    }
     return false;
   }
 
@@ -189,6 +234,8 @@
 
   function unsetHighlightLine() {
     $('#browsing_file').removeAttr('data-line');
+    // Add this line to ensure that all highlight divs are removed
+    $('.line-highlight').remove();
   }
 
   function removeAnchor() {
@@ -216,8 +263,9 @@
     }, function (data) {
       let deps = JSON.parse(data.deps);
       let codes = JSON.parse(data.codes);
+      let okays = JSON.parse(data.okays);
       setTask(deps);
-      setCode(codes);
+      setCode(codes, okays);
     });
 
     setBrowsingFile(file);
@@ -237,8 +285,7 @@
     $inputs.each(function () {
       values[this.name] = $(this).val();
     });
-    let path = $('#path_prefix').text() +
-               $('input[name="browsing_path"]').val();
+    let path = $('input[name="browsing_path"]').val();
     setBrowsingFile(path);
     unsetHighlightLine();
     return false;
diff --git a/vndk/tools/source-deps-reviewer/sourcedr/templates/index.html b/vndk/tools/source-deps-reviewer/sourcedr/templates/index.html
index 9cf75ec..f03ced3 100644
--- a/vndk/tools/source-deps-reviewer/sourcedr/templates/index.html
+++ b/vndk/tools/source-deps-reviewer/sourcedr/templates/index.html
@@ -9,13 +9,15 @@
   <link rel="stylesheet" href="static/css/main.css"/>
   <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
   <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
+  <!-- Added for sortable list -->
+  <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
 </head>
 <body>
 
 <div class="container-fluid">
   <div class="row content">
     <h2 style="padding-left:20px;">Code review tool</h2>
-    <div id="item_list" class="col-sm-3 sidenav hidden-xs"></div>
+    <ol id="item_list" class="col-sm-3"></ol>
 
     <div class="col-sm-5">
       <h3>Browsing:</h3>
diff --git a/vndk/tools/source-deps-reviewer/sourcedr/test/example.c b/vndk/tools/source-deps-reviewer/sourcedr/test/example.c
index 4648f14..f8e329f 100644
--- a/vndk/tools/source-deps-reviewer/sourcedr/test/example.c
+++ b/vndk/tools/source-deps-reviewer/sourcedr/test/example.c
@@ -1,9 +1,10 @@
 int main() {
-    printf("This is a simple testing filen");
+    printf("This is a simple testing file\n");
     int dlopen_analysis = 1;
     "This line with dlopen shouldn't be found"
     /*
      * This dlopen shouldn't be found
      */
     dlopen("dlopen");
+    handle = dlopen("libm.so.6", RTLD_LAZY);
 }