<commit_before>package io.appium.android.bootstrap;

import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONArray;
import org.json.JSONException;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.Hashtable;

import com.android.uiautomator.core.UiDevice;
import com.android.uiautomator.core.UiObject;
import com.android.uiautomator.core.UiSelector;
import com.android.uiautomator.core.UiObjectNotFoundException;

class AndroidCommandException extends Exception {
    public AndroidCommandException(String msg) {
        super(msg);
    }
}

class InvalidStrategyException extends AndroidCommandException {
    public InvalidStrategyException(String msg) {
        super(msg);
    }
}

class AndroidCommandHolder {
    
    public static boolean click(Double x, Double y) {
        Double[] coords = {x, y};
        ArrayList<Integer> posVals = absPosFromCoords(coords);
        return UiDevice.getInstance().click(posVals.get(0), posVals.get(1));
    }
    
    private static ArrayList<Integer> absPosFromCoords(Double[] coordVals) {
        UiDevice d = UiDevice.getInstance();
        Double screenX = new Double(d.getDisplayWidth());
        Double screenY = new Double(d.getDisplayHeight());
        ArrayList<Integer> retPos = new ArrayList<Integer>();
        Integer curVal;
        for (Double coord : coordVals) {
            if (coord < 1) {
                curVal = (int)(screenX * coord);
            } else {
                curVal = coord.intValue();
            }
            retPos.add(curVal);
        }
        return retPos;
    }
    
    public static boolean swipe(Double startX, Double startY, Double endX, Double endY, Integer steps) {
        UiDevice d = UiDevice.getInstance();
        Double[] coords = {startX, startY, endX, endY};
        ArrayList<Integer> posVals = absPosFromCoords(coords);
        return d.swipe(posVals.get(0), posVals.get(1), posVals.get(2), posVals.get(3), steps);        
    }
    
    public static JSONObject getDeviceSize() throws AndroidCommandException {
        UiDevice d = UiDevice.getInstance();
        JSONObject res = new JSONObject();
        try {
            res.put("width", d.getDisplayHeight());
            res.put("height", d.getDisplayWidth());
        } catch (JSONException e) {
            throw new AndroidCommandException("Error serializing height/width data into JSON");
        }
        return res;
    }
    
    public static JSONObject findElement(String strategy, String selector, String contextId) throws UiObjectNotFoundException, AndroidCommandException, ElementNotFoundException {
        UiSelector sel = selectorForFind(strategy, selector, false);
        return findElementWithSelector(sel, contextId);
    }
    
    public static JSONObject findElementByXpath(JSONArray path, String attr, String constraint, boolean substr, String contextId) throws UiObjectNotFoundException, AndroidCommandException, ElementNotFoundException {
        UiSelector sel = selectorForXpath(path, attr, constraint, substr);
        return findElementWithSelector(sel, contextId);
    }
    
    private static JSONObject findElementWithSelector(UiSelector sel, String contextId) throws UiObjectNotFoundException, AndroidCommandException, ElementNotFoundException {
        JSONObject res = new JSONObject();
        String elId;
        UiObject el;
        AndroidElementsHash elHash = AndroidElementsHash.getInstance();
        AndroidElement baseEl = contextElement(contextId);
        
        if (baseEl == null) {
            el = new UiObject(sel);
        } else {
            el = baseEl.getChild(sel);
        }
        
        if (el.exists()) {
            elId = elHash.addElement(el);
            try {
                res.put("ELEMENT", elId);
            } catch (JSONException e) {
                throw new AndroidCommandException("Error serializing element ID into JSON");
            }
            return res;
        } else {
            throw new ElementNotFoundException();
        }
    }
    
    public static JSONArray findElements(String strategy, String selector, String contextId) throws AndroidCommandException, UiObjectNotFoundException {
        UiSelector sel = selectorForFind(strategy, selector, true);
        return findElementsWithSelector(sel, contextId);
    }
    
    public static JSONArray findElementsByXpath(JSONArray path, String attr, String constraint, boolean substr, String contextId) throws AndroidCommandException, UiObjectNotFoundException {
        UiSelector sel = selectorForXpath(path, attr, constraint, substr);
        return findElementsWithSelector(sel, contextId);
    }
    
    private static JSONArray findElementsWithSelector(UiSelector sel, String contextId) throws AndroidCommandException, UiObjectNotFoundException {
        ArrayList<String> elIds = new ArrayList<String>();
        JSONArray res = new JSONArray();
        UiObject lastFoundObj;
        boolean keepSearching = true;
        AndroidElementsHash elHash = AndroidElementsHash.getInstance();
        AndroidElement baseEl = contextElement(contextId);
        
        int counter = 0;
        while (keepSearching) {
            if (baseEl == null) {
                Logger.info("keep searching A " + counter);
                lastFoundObj = new UiObject(sel.index(counter));
            } else {
                Logger.info("keep searching B " + counter);
                lastFoundObj = baseEl.getChild(sel.instance(counter));
            }
            counter++;
            if (lastFoundObj != null && lastFoundObj.exists()) {
                elIds.add(elHash.addElement(lastFoundObj));
            } else {
                keepSearching = false;
            }
        }
        
        for (String elId : elIds) {
            JSONObject idObj = new JSONObject();
            try {
                idObj.put("ELEMENT", elId);
            } catch (JSONException e) {
                throw new AndroidCommandException("Error serializing element ID into JSON");
            }
            res.put(idObj);
        }
        return res;
    }
    
    private static AndroidElement contextElement(String contextId) throws AndroidCommandException {
        AndroidElement baseEl = null;
        AndroidElementsHash elHash = AndroidElementsHash.getInstance();
        
        if (!contextId.isEmpty()) {
            try {
                baseEl = elHash.getElement(contextId);
            } catch (ElementNotInHashException e) {
                throw new AndroidCommandException(e.getMessage());
            }
        }
        return baseEl;
    }
    
    private static UiSelector selectorForXpath(JSONArray path, String attr, String constraint, boolean substr) throws AndroidCommandException {
        UiSelector s = new UiSelector();
        JSONObject pathObj;
        String nodeType;
        String searchType;
        String substrStr = substr ? "true" : "false";
        Logger.info("Building xpath selector from attr " + attr + " and constraint " + constraint + " and substr " + substrStr);
        String selOut = "s";

        // $driver.find_element :xpath, %(//*[contains(@text, 'agree')])
        // info: [ANDROID] [info] Building xpath selector from attr text and
        // constraint agree and substr true
        // info: [ANDROID] [info] s.className('*').textContains('agree')
        try {
          nodeType = path.getJSONObject(0).getString("node");
        } catch (JSONException e) {
          throw new AndroidCommandException(
              "Error parsing xpath path obj from JSON");
        }

        if (attr.toLowerCase().contentEquals("text") && !constraint.isEmpty()
            && substr == true && nodeType.contentEquals("*") == true) {
          selOut += ".textContains('" + constraint + "')";
          s = s.textContains(constraint);
          Logger.info(selOut);
          return s;
        }

        for (int i = 0; i < path.length(); i++) {
            try {
                pathObj = path.getJSONObject(i);
                nodeType = pathObj.getString("node");
                searchType = pathObj.getString("search");
            } catch (JSONException e) {
                throw new AndroidCommandException("Error parsing xpath path obj from JSON");
            }
            try {
                nodeType = AndroidElementClassMap.match(nodeType);
            } catch (UnallowedTagNameException e) {
                throw new AndroidCommandException(e.getMessage());
            }
            if (searchType.equals("child")) {
                s = s.childSelector(s);
                selOut += ".childSelector(s)";
            } else {
                s = s.className(nodeType);
                selOut += ".className('" + nodeType + "')";
            }<commit_msg>Add custom class name matches<commit_after>package io.appium.android.bootstrap;

import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONArray;
import org.json.JSONException;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.Hashtable;

import com.android.uiautomator.core.UiDevice;
import com.android.uiautomator.core.UiObject;
import com.android.uiautomator.core.UiSelector;
import com.android.uiautomator.core.UiObjectNotFoundException;

class AndroidCommandException extends Exception {
    public AndroidCommandException(String msg) {
        super(msg);
    }
}

class InvalidStrategyException extends AndroidCommandException {
    public InvalidStrategyException(String msg) {
        super(msg);
    }
}

class AndroidCommandHolder {
    
    public static boolean click(Double x, Double y) {
        Double[] coords = {x, y};
        ArrayList<Integer> posVals = absPosFromCoords(coords);
        return UiDevice.getInstance().click(posVals.get(0), posVals.get(1));
    }
    
    private static ArrayList<Integer> absPosFromCoords(Double[] coordVals) {
        UiDevice d = UiDevice.getInstance();
        Double screenX = new Double(d.getDisplayWidth());
        Double screenY = new Double(d.getDisplayHeight());
        ArrayList<Integer> retPos = new ArrayList<Integer>();
        Integer curVal;
        for (Double coord : coordVals) {
            if (coord < 1) {
                curVal = (int)(screenX * coord);
            } else {
                curVal = coord.intValue();
            }
            retPos.add(curVal);
        }
        return retPos;
    }
    
    public static boolean swipe(Double startX, Double startY, Double endX, Double endY, Integer steps) {
        UiDevice d = UiDevice.getInstance();
        Double[] coords = {startX, startY, endX, endY};
        ArrayList<Integer> posVals = absPosFromCoords(coords);
        return d.swipe(posVals.get(0), posVals.get(1), posVals.get(2), posVals.get(3), steps);        
    }
    
    public static JSONObject getDeviceSize() throws AndroidCommandException {
        UiDevice d = UiDevice.getInstance();
        JSONObject res = new JSONObject();
        try {
            res.put("width", d.getDisplayHeight());
            res.put("height", d.getDisplayWidth());
        } catch (JSONException e) {
            throw new AndroidCommandException("Error serializing height/width data into JSON");
        }
        return res;
    }
    
    public static JSONObject findElement(String strategy, String selector, String contextId) throws UiObjectNotFoundException, AndroidCommandException, ElementNotFoundException {
        UiSelector sel = selectorForFind(strategy, selector, false);
        return findElementWithSelector(sel, contextId);
    }
    
    public static JSONObject findElementByXpath(JSONArray path, String attr, String constraint, boolean substr, String contextId) throws UiObjectNotFoundException, AndroidCommandException, ElementNotFoundException {
        UiSelector sel = selectorForXpath(path, attr, constraint, substr);
        return findElementWithSelector(sel, contextId);
    }
    
    private static JSONObject findElementWithSelector(UiSelector sel, String contextId) throws UiObjectNotFoundException, AndroidCommandException, ElementNotFoundException {
        JSONObject res = new JSONObject();
        String elId;
        UiObject el;
        AndroidElementsHash elHash = AndroidElementsHash.getInstance();
        AndroidElement baseEl = contextElement(contextId);
        
        if (baseEl == null) {
            el = new UiObject(sel);
        } else {
            el = baseEl.getChild(sel);
        }
        
        if (el.exists()) {
            elId = elHash.addElement(el);
            try {
                res.put("ELEMENT", elId);
            } catch (JSONException e) {
                throw new AndroidCommandException("Error serializing element ID into JSON");
            }
            return res;
        } else {
            throw new ElementNotFoundException();
        }
    }
    
    public static JSONArray findElements(String strategy, String selector, String contextId) throws AndroidCommandException, UiObjectNotFoundException {
        UiSelector sel = selectorForFind(strategy, selector, true);
        return findElementsWithSelector(sel, contextId);
    }
    
    public static JSONArray findElementsByXpath(JSONArray path, String attr, String constraint, boolean substr, String contextId) throws AndroidCommandException, UiObjectNotFoundException {
        UiSelector sel = selectorForXpath(path, attr, constraint, substr);
        return findElementsWithSelector(sel, contextId);
    }
    
    private static JSONArray findElementsWithSelector(UiSelector sel, String contextId) throws AndroidCommandException, UiObjectNotFoundException {
        ArrayList<String> elIds = new ArrayList<String>();
        JSONArray res = new JSONArray();
        UiObject lastFoundObj;
        boolean keepSearching = true;
        AndroidElementsHash elHash = AndroidElementsHash.getInstance();
        AndroidElement baseEl = contextElement(contextId);
        
        int counter = 0;
        while (keepSearching) {
            if (baseEl == null) {
                Logger.info("keep searching A " + counter);
                lastFoundObj = new UiObject(sel.index(counter));
            } else {
                Logger.info("keep searching B " + counter);
                lastFoundObj = baseEl.getChild(sel.instance(counter));
            }
            counter++;
            if (lastFoundObj != null && lastFoundObj.exists()) {
                elIds.add(elHash.addElement(lastFoundObj));
            } else {
                keepSearching = false;
            }
        }
        
        for (String elId : elIds) {
            JSONObject idObj = new JSONObject();
            try {
                idObj.put("ELEMENT", elId);
            } catch (JSONException e) {
                throw new AndroidCommandException("Error serializing element ID into JSON");
            }
            res.put(idObj);
        }
        return res;
    }
    
    private static AndroidElement contextElement(String contextId) throws AndroidCommandException {
        AndroidElement baseEl = null;
        AndroidElementsHash elHash = AndroidElementsHash.getInstance();
        
        if (!contextId.isEmpty()) {
            try {
                baseEl = elHash.getElement(contextId);
            } catch (ElementNotInHashException e) {
                throw new AndroidCommandException(e.getMessage());
            }
        }
        return baseEl;
    }
    
    private static UiSelector selectorForXpath(JSONArray path, String attr, String constraint, boolean substr) throws AndroidCommandException {
        UiSelector s = new UiSelector();
        JSONObject pathObj;
        String nodeType;
        String searchType;
        String substrStr = substr ? "true" : "false";
        Logger.info("Building xpath selector from attr " + attr + " and constraint " + constraint + " and substr " + substrStr);
        String selOut = "s";

        // $driver.find_element :xpath, %(//*[contains(@text, 'agree')])
        // info: [ANDROID] [info] Building xpath selector from attr text and
        // constraint agree and substr true
        // info: [ANDROID] [info] s.className('*').textContains('agree')
        try {
          nodeType = path.getJSONObject(0).getString("node");
        } catch (JSONException e) {
          throw new AndroidCommandException(
              "Error parsing xpath path obj from JSON");
        }

        if (attr.toLowerCase().contentEquals("text") && !constraint.isEmpty()
            && substr == true && nodeType.contentEquals("*") == true) {
          selOut += ".textContains('" + constraint + "')";
          s = s.textContains(constraint);
          Logger.info(selOut);
          return s;
        }

        // //*[contains(@tag, "button")]
        if (attr.toLowerCase().contentEquals("tag") && !constraint.isEmpty()
            && substr == true && nodeType.contentEquals("*") == true) {
          // (?i) = case insensitive match. Esape everything that isn't an alpha num.
          // use .* to match on contains.
          constraint = "(?i)^.*" + constraint.replaceAll("([^\\p{Alnum}])", "\\\\$1") + ".*$";
          selOut += ".classNameMatches('" + constraint + "')";
          s = s.classNameMatches(constraint);
          Logger.info(selOut);
          return s;
        }

        for (int i = 0; i < path.length(); i++) {
            try {
                pathObj = path.getJSONObject(i);
                nodeType = pathObj.getString("node");
                searchType = pathObj.getString("search");
            } catch (JSONException e) {
                throw new AndroidCommandException("Error parsing xpath path obj from JSON");
            }
            try {
                nodeType = AndroidElementClassMap.match(nodeType);
            } catch (UnallowedTagNameException e) {
                throw new AndroidCommandException(e.getMessage());
            }
            if (searchType.equals("child")) {
                s = s.childSelector(s);
                selOut += ".childSelector(s)";
            } else {
                s = s.className(nodeType);
                selOut += ".className('" + nodeType + "')";
            }
{'fraction_non_alphanumeric': 0.09960291197882197, 'fraction_numerical': 0.001158173395102581, 'mean_word_length': 2.318631039531479, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3875941', 'n_tokens_mistral': 4675, 'n_tokens_neox': 4591, 'n_words': 1169}