Home | History | Annotate | Download | only in query
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.settings.intelligence.search.query;
     18 
     19 import android.text.TextUtils;
     20 
     21 /**
     22  * Utils for Query-time operations.
     23  */
     24 
     25 public class SearchQueryUtils {
     26 
     27     public static final int NAME_NO_MATCH = -1;
     28 
     29     /**
     30      * Returns "difference" between resultName and query string. resultName must contain all
     31      * characters from query as a prefix to a word, in the same order.
     32      * If not, returns NAME_NO_MATCH.
     33      * If they do match, returns an int value representing  how different they are,
     34      * and larger values means they are less similar.
     35      * <p/>
     36      * Example:
     37      * resultName: Abcde, query: Abcde, Returns 0
     38      * resultName: Abcde, query: abc, Returns 2
     39      * resultName: Abcde, query: ab, Returns 3
     40      * resultName: Abcde, query: bc, Returns NAME_NO_MATCH
     41      * resultName: Abcde, query: xyz, Returns NAME_NO_MATCH
     42      * resultName: Abc de, query: de, Returns 4
     43      */
     44     public static int getWordDifference(String resultName, String query) {
     45         if (TextUtils.isEmpty(resultName) || TextUtils.isEmpty(query)) {
     46             return NAME_NO_MATCH;
     47         }
     48 
     49         final char[] queryTokens = query.toLowerCase().toCharArray();
     50         final char[] resultTokens = resultName.toLowerCase().toCharArray();
     51         final int resultLength = resultTokens.length;
     52         if (queryTokens.length > resultLength) {
     53             return NAME_NO_MATCH;
     54         }
     55 
     56         int i = 0;
     57         int j;
     58 
     59         while (i < resultLength) {
     60             j = 0;
     61             // Currently matching a prefix
     62             while ((i + j < resultLength) && (queryTokens[j] == resultTokens[i + j])) {
     63                 // Matched the entire query
     64                 if (++j >= queryTokens.length) {
     65                     // Use the diff in length as a proxy of how close the 2 words match.
     66                     // Value range from 0 to infinity.
     67                     return resultLength - queryTokens.length;
     68                 }
     69             }
     70 
     71             i += j;
     72 
     73             // Remaining string is longer that the query or we have search the whole result name.
     74             if (queryTokens.length > resultLength - i) {
     75                 return NAME_NO_MATCH;
     76             }
     77 
     78             // This is the first index where result name and query name are different
     79             // Find the next space in the result name or the end of the result name.
     80             while ((i < resultLength) && (!Character.isWhitespace(resultTokens[i++]))) ;
     81 
     82             // Find the start of the next word
     83             while ((i < resultLength) && !(Character.isLetter(resultTokens[i])
     84                     || Character.isDigit(resultTokens[i]))) {
     85                 // Increment in body because we cannot guarantee which condition was true
     86                 i++;
     87             }
     88         }
     89         return NAME_NO_MATCH;
     90     }
     91 }
     92