org.safs.selenium.SeleniumGUIUtilities.java Source code

Java tutorial

Introduction

Here is the source code for org.safs.selenium.SeleniumGUIUtilities.java

Source

/** 
 * Copyright (C) SAS Institute, All rights reserved.
 * General Public License: http://www.opensource.org/licenses/gpl-license.php
 **/

package org.safs.selenium;

import java.awt.Rectangle;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.dom4j.Document;
import org.openqa.selenium.WebDriver;
//PACKAGE NAME CHANGE between Selenium 2.37 and 2.41 handled dynamically in selectWindow() and getAllWindowNames() methods
//import org.openqa.selenium.WebDriverBackedSelenium;
//import com.thoughtworks.selenium.webdriven.WebDriverBackedSelenium;
import org.safs.ApplicationMap;
import org.safs.DDGUIUtilities;
import org.safs.Log;
import org.safs.SAFSException;
import org.safs.SAFSObjectNotFoundException;
import org.safs.STAFHelper;
import org.safs.TestRecordData;
import org.safs.Tree;
import org.safs.logging.LogUtilities;
import org.safs.selenium.util.HtmlFrameComp;

import com.thoughtworks.selenium.Selenium;
import com.thoughtworks.selenium.SeleniumException;

/**
 * SAFS/Selenium-specific subclass of DDGUIUtilities.
 * The primary feature is that the SeleniumGUIUtilities uses the new SAFS RMI bridge to 
 * communicate with our own Java Proxies embedded in each JVM.
 * 
 * @author  CANAGL, PHSABO
 * @since   AUG 15, 2006
 * @author CANAGL, PHSABO FEB 06, 2007 - Fixed getWindowID and getXPath calls
 * @author CANAGL  FEB 07, 2007 - prepend Type=HTML; if no type specified in recString
 *                               in getXPathString function.
 * @author CANAGL  FEB 01, 2010 - moved ComponentFunctions.DLL usage here until it is factored out
 * @author LeiWang MAR 31, 2011 - Use DocumentParser to parse HTML page instead of user-extensions.js
 * @author LeiWang JUN 01, 2011 - In method getGuiObject():  initialize domParser before using it.
 *                                In method normalizeXPath(): Modify xpath so that it begins with string "//".
 * @author LeiWang JUN 21, 2011 - Modify method getDocumentParser(): If selenium switches between frame,
 *                                we will not modify the main url stored in domParser.
 *                                Overload method setWindowFocus() and maximizeWindow(): use selenium's API 
 *                                instead of native call to to the job.
 * @author LeiWang JUN 28, 2011 - Add method getAttribute() and getAttributes().                              
 * @see org.safs.DDGUIUtilities
 **/
public class SeleniumGUIUtilities extends DDGUIUtilities {

    private long gSecTimeout;
    //   protected final Pattern framePattern = Pattern.compile("(//.*FRAME\\[.+?\\])//.+");
    public static final Pattern FRAME_PATTERN = Pattern.compile("(/.*FRAME(\\[.+?\\])?)(/.+)?");
    public boolean frameSwitched = false;
    public String UNNAMED_WINDOW = "null";
    public DocumentParser domParser = null;

    //This map contains the pairs(frameXpath, HtmlFrameComp)
    //The frameXpath should be counted from the "root url" of DocumentParser
    private Map<String, HtmlFrameComp> frames = new TreeMap<String, HtmlFrameComp>(new Comparator<String>() {
        public int compare(String o1, String o2) {
            //We just compare the length of the frameXpath, put the longest at the first, shortest at the last, it is descending
            //As TreeMap will be in ascending key order, so inverse the compare calculation.
            int compareInt = o2.length() - o1.length();
            if (compareInt == 0) {
                compareInt = o2.compareTo(o1);
            }
            return compareInt;
        }
    });

    /**
     * No-argument constructor.
     * Simply calls super()
     * @see DDGUIUtilities#DDGUIUtilities()
     */
    public SeleniumGUIUtilities() {
        super();
    }

    /**
     * Constructor providing the STAFHelper and LogUtilities needed for
     * proper operation. 
     * @param helper The STAFHelper for performing STAF requests.
     * @param log the LogUtilities for logging.
     * @see org.safs.STAFHelper
     * @see org.safs.logging.LogUtilities
     **/
    public SeleniumGUIUtilities(STAFHelper helper, LogUtilities log) {
        super();
        setLogUtilities(log); //must be first
        setSTAFHelper(helper);
    }

    /**
     * Constructor providing the STAFHelper, LogUtilities, and TestRecordData for proper operation.
     * The SAFS/Selenium engine generally uses an instanceof STestRecordHelper for TestRecordData.
     *
     * @param helper The STAFHelper for performing STAF requests.
     * @param data the TestRecordData generally an instanceof STestRecordHelper.
     * @param log the LogUtilities for logging.
     * @see org.safs.STAFHelper
     * @see STestRecordHelper
     * @see org.safs.logging.LogUtilities
     **/
    public SeleniumGUIUtilities(STAFHelper helper, TestRecordData data, LogUtilities log) {
        this(helper, log);
        setTestRecordData(data);
    }

    /**
     * Uses the Application Map caching mechanism provided by the superclass.
     * Retrieves cached references or recognition strings from the App Map and attempts to 
     * identify the matching component via the remote AUT JVM proxies.
     * <p>
     * @param appMapName  the name/ID of the App Map currently in use.
     * @param windowName  the name/ID of the window as predefined in the App Map.
     * @param compName    the name/ID of the child component of the window as predefined 
     * in the App Map.
     * @param secTimeout the number of seconds allowed to located the object before a 
     * SAFSObjectNotFoundException is thrown.
     * 
     * @return 0 on success. throw SAFSObjectNotFoundException if not successful.
     * @throws SAFSObjectNotFoundException if specified parent or child cannot be found.
     * @see org.safs.DDGUIUtilities#waitForObject(String,String,String,long)
     */
    public int waitForObject(String appMapName, String windowName, String compName, long secTimeout)
            throws SAFSObjectNotFoundException {

        gSecTimeout = secTimeout;
        ApplicationMap map = null;
        STestRecordHelper trdata = (STestRecordHelper) this.trdata;
        Selenium selenium = SApplicationMap.getSelenium(windowName);

        try {
            Log.info("SGU: Looking for " + windowName + "." + compName + " using AppMap:" + appMapName);
            map = getAppMap(appMapName);

            if (map == null) {
                if (registerAppMap(appMapName, appMapName)) {
                    map = (ApplicationMap) getAppMap(appMapName);
                    if (map == null) {
                        Log.debug("SGU: WFO could NOT retrieve registered AppMap " + appMapName);
                        throw new SAFSObjectNotFoundException("Could not retrieve App Map " + appMapName);
                    }
                }
                // what if NOT registered?
                else {
                    Log.debug("SGU: WFO could NOT register AppMap " + appMapName);
                    throw new SAFSObjectNotFoundException("Could not register App Map " + appMapName);
                }
            }
            //TODO: Find and inject into window not launched by us.
            String winRec = map.getParentGUIID(windowName, true);
            Log.info("SGU: winRec retrieved: " + winRec);
            SGuiObject winObj = (SGuiObject) map.getParentObject(windowName);
            Log.info("SGU: winObj retrieved: " + winObj);

            if (winObj != null && winObj.isDynamic())
                winObj = null;

            boolean notDone = true;

            while (winObj == null && notDone) {
                winObj = getWindowIdFromTitle(selenium, winRec);
                if (winObj != null) {
                    map.setParentObject(windowName, winObj);
                }
                notDone = (winObj == null);
                if (notDone) {
                    gSecTimeout--;
                    if (gSecTimeout >= 0) {
                        try {
                            Thread.sleep(1000);
                        } catch (Exception t) {
                        }
                    } else {
                        notDone = false;
                    }
                }
            }

            if (winObj == null) {
                throw new SAFSObjectNotFoundException("Could not find Window " + windowName);
            }

            if (!selectWindow(selenium, winObj.getWindowId(), gSecTimeout))
                throw new SAFSObjectNotFoundException("Could not find Window " + windowName);

            //these may not be needed if seeking parent window only          
            String compRec = null;
            SGuiObject compObj = null;

            boolean isParent = windowName.equalsIgnoreCase(compName);
            notDone = true;
            boolean winValid = false;
            boolean compValid = false;

            // get values if a child component is the target and not just the window
            if (!isParent) {
                compRec = map.getChildGUIID(windowName, compName, true);
                Log.info("SGU: compRec retrieved: " + compRec);
                compObj = (SGuiObject) map.getChildObject(windowName, compName);
                Log.info("SGU: compObj retrieved: " + compObj);
            } else {
                //throw NullPointerExceptions if not found/valid
                trdata.setWindowGuiId(winRec);
                trdata.setWindowTestObject(winObj);
                Log.info("SGU Matched: " + winObj.toString());
                return 0;
            }

            do {
                Log.debug("SGU:Trying " + windowName + "." + compName + " using AppMap:" + appMapName);

                // can add validate winObj still alive code in here  
                if (!winValid) {
                    notDone = true;
                    while (notDone) {
                        try {
                            winValid = (selenium.getTitle() != null);
                            notDone = false;
                        } catch (SeleniumException e) {
                            if (notDone) {
                                gSecTimeout--;
                                if (gSecTimeout >= 0) {
                                    try {
                                        Thread.sleep(1000);
                                    } catch (Exception t) {
                                    }
                                } else {
                                    notDone = false;
                                }
                            }
                        }
                    }
                    if (!winValid) {
                        compValid = false;
                        winObj = null;
                        compObj = null;
                        map.setParentObject(windowName, winObj);
                        map.setChildObject(windowName, compName, compObj);
                    }
                }
                map.setParentObject(windowName, winObj);
                Log.info("SGU Window: " + winObj);
                // only seek the child if we already have the parent windowGood2go
                if ((!isParent) && (winObj != null)) {
                    if (compObj == null || compObj.isDynamic()) {
                        Log.debug("Trying xpath script for: '" + compName + "' with recognition '" + compRec + "'");
                        //System.out.println("Trying xpath script for: '" + compName + "' with recognition '"+ compRec +"'");
                        try {
                            compObj = getGuiObject(compRec, selenium, winObj.getWindowId());
                            if (compObj != null && !selenium.isVisible(compObj.getLocator()))
                                compObj = null;
                        } catch (Exception e) {
                            Log.debug("IGNORING SGU:getXPathString error:", e);
                        }

                        // may throw NullPointerException?
                        if ((compObj != null) && (compObj.getLocator().equals(""))) {
                            compObj = null; //If compObj is "", getXPathString did not find the object
                        }
                        // delete following line to remove caching components
                        if (compObj != null)
                            map.setChildObject(windowName, compName, compObj);
                        Log.info("SGU Component: " + compObj);
                    } else {
                        // can add validate compObj still alive code in here  
                        if (!compValid) {
                            compValid = selenium.isElementPresent(compObj.getLocator())
                                    && selenium.isVisible(compObj.getLocator());
                            if (!compValid) {
                                compObj = null;
                                map.setChildObject(windowName, compName, compObj);
                            }
                        }
                    }
                }

                notDone = (compObj == null);
                if (notDone) {
                    gSecTimeout--;
                    //sleep 1 second before trying again if timeout > 0 specified
                    if (gSecTimeout >= 0) {
                        try {
                            Thread.sleep(1000);
                        } catch (Exception t) {
                            ;
                        }
                    }
                    // we have timed out
                    else {
                        notDone = false;
                    }
                }
                // is done
                else {
                    trdata.setCompGuiId(compRec);
                    trdata.setWindowGuiId(winRec);
                    trdata.setCompTestObject(compObj);
                    trdata.setWindowTestObject(winObj);
                    trdata.setCompType(compObj.getCompType());
                }
            } while (notDone);
            //           throw NullPointerExceptions if not found/valid
            Log.info("SGU Matched: " + compObj.toString());

            return 0;
        } catch (Exception x) {
            String msg = windowName + "." + compName + " using MapName:" + appMapName + ";ApplicationMap:";
            msg += (map == null) ? "NULL" : map.getMapName();
            Log.debug("SGU:WFO EXCEPTION:" + x.getMessage() + " using " + msg + "\n", x);
            throw new SAFSObjectNotFoundException(msg);
        }
    }

    /**
     * Gets the object defined by the windowName and childName
     * @param mapName  the name/ID of the App Map currently in use.
     * @param windowName  the name/ID of the window as predefined in the App Map.
     * @param childName    the name/ID of the child component of the window as predefined in the App Map.
     * @param ignoreCache if true, try getting the component from the appmap
     * @return the object defined by the windowName and childName
     */
    public SGuiObject getTestObject(String mapname, String windowName, String childName, boolean ignoreCache) {
        if (mapname == null || windowName == null)
            return null;
        ApplicationMap map = getAppMap(mapname);

        if (map == null) {
            if (registerAppMap(mapname, mapname)) {
                map = getAppMap(mapname);
                if (map == null) {
                    Log.info("SDDG: gto1 could NOT retrieve registered AppMap " + mapname);
                    return null;
                }
            }
            // what if NOT registered?
            else {
                Log.info("SDDG: gto1 could NOT register AppMap " + mapname);
                return null;
            }
        }

        //Selenium selenium = SApplicationMap.getSelenium(windowName);

        SGuiObject tobj = null;
        if ((childName == null) || (windowName.equalsIgnoreCase(childName))) {

            if (!ignoreCache) {
                tobj = (SGuiObject) map.getParentObject(windowName);
            }
            if (tobj == null) {
                //map.setParentObject(windowName, null);
                try {
                    waitForObject(trdata.getAppMapName(), windowName, windowName, 0);
                } catch (SAFSObjectNotFoundException e) {
                    Log.info("SDDG: Could not waitForObject " + windowName);
                    return null;
                }
                tobj = (SGuiObject) map.getParentObject(windowName);
                if (tobj == null) {
                    Log.info("SDDG: Could not waitForObject " + windowName);
                    return null;
                }
            } else {
                return tobj;
            }

        } else {

            if (!ignoreCache) {
                tobj = (SGuiObject) map.getChildObject(windowName, childName);
            }
            if (tobj == null) {
                //map.setChildObject(windowName, childName, null);
                try {
                    waitForObject(trdata.getAppMapName(), windowName, childName, 0);
                } catch (SAFSObjectNotFoundException e) {
                    Log.info("SDDG: Could not waitForObject " + childName);
                    return null;
                }
                tobj = (SGuiObject) map.getChildObject(windowName, childName);
                if (tobj == null) {
                    Log.info("SDDG: Could not waitForObject " + childName);
                    return null;
                }
                ((STestRecordHelper) trdata).setCompTestObject(tobj);
            } else {
                Log.info("SDDG: returning cached object: " + tobj.getCompType());
                return tobj;
            }
        }

        try {
            Log.info("SDDG: compTestObject : " + tobj.getCompType());
        } catch (Exception npe2) {
            Log.info("SDDG: No Mapped TestObject named \"" + childName + "\" found.");
        }

        return tobj;
    }

    /**
     * selects a window to send future commands to
     * @param selenium Current Selenium object
     * @param id windowId--can be null
     * @param timeout how long to wait for the window in seconds
     * @return whether window was found
     */
    public boolean selectWindow(Selenium selenium, String id, long timeout) {

        // workaround Selenium.selectWindow deadlock defect
        //
        ThreadedSelectWindow selectWindow = new ThreadedSelectWindow(Thread.currentThread(), selenium, id, timeout);
        try {
            selectWindow.start();
            Thread.sleep((timeout * 1000) + 1000); //backup timeout for deadlocks
        } catch (Exception e) {
        }
        if (selectWindow.isSearching()) {
            Log.debug("SGU: selectWindow thread may have deadlocked after " + selectWindow.searchSeconds()
                    + " seconds.");
        }
        // hasten cleanup
        boolean found = selectWindow.isSelected();
        selectWindow = null;

        if (!found) {
            WebDriver driver = null;
            try {
                if (selenium instanceof com.thoughtworks.selenium.webdriven.WebDriverBackedSelenium) {
                    //WebDriver driver = ((WebDriverBackedSelenium) selenium).getUnderlyingWebDriver();
                    driver = ((com.thoughtworks.selenium.webdriven.WebDriverBackedSelenium) selenium)
                            .getWrappedDriver();
                }
            } catch (Throwable noclass) {
                Log.debug(
                        "SGU: selectWindow handling older selenium install " + noclass.getClass().getSimpleName());
                /** WebDriverBackedSelenium version 37 move to com.thoughtworks.selenium.webdriven in 42 selenium version
                try{
                   if(selenium instanceof org.openqa.selenium.WebDriverBackedSelenium){
                      driver = ((org.openqa.selenium.WebDriverBackedSelenium) selenium).getWrappedDriver();
                   }
                }catch(Throwable ignore){
                   Log.debug("SGU: selectWindow ignoring newer selenium install "+ ignore.getClass().getSimpleName());
                }
                **/
            }
            if (driver != null) {
                driver.switchTo().window(driver.getWindowHandle());
                found = true;
            }
        }

        Log.info("SGU: selectWindow found window? " + found);
        return found;
    }

    /**
     * Wraps Selenium.getAllWindowNames to handle Selenium defect.
     * Something buggy about later versions of Selenium.
     * Later versions return the window names in one comma-delimited string
     * in the first array item.  This wrapper will handle this situation 
     * if present.
     * <p>
     * @return String array.  Empty window names are replaced with INVALID_WINDOWNAME 
     * in the array before it is returned.  An array of one "null" item will 
     * be returned if no windows were found. 
     */
    public String[] getAllWindowNames(Selenium selenium) {
        String[] names = null;
        String comma = ",";
        try {
            //         names = selenium.getAllWindowNames();
            names = selenium.getAllWindowTitles();
            // check for defect of comma-delimited single string
            // catch will catch a NullPointerException
            if (names.length == 1) {
                Log.debug("SGU: getAllWindowNames single array item: " + names[0]);
                if (names[0].indexOf(comma) > -1) {
                    names = names[0].split(comma);
                }
            }
        } catch (Exception e) {
            names = new String[1];
            Log.debug("SGU: getAllWindowNames ignoring Exception: ", e);

            try { // if com.thoughtworks.selenium.webdriven package is NOT present at runtime an Error/Exception will be thrown
                if (selenium instanceof com.thoughtworks.selenium.webdriven.WebDriverBackedSelenium) {
                    //               WebDriver driver = ((WebDriverBackedSelenium) selenium).getUnderlyingWebDriver();
                    //               WebDriver driver = ((com.thoughtworks.selenium.webdriven.WebDriverBackedSelenium) selenium).getWrappedDriver();
                    names[0] = ((com.thoughtworks.selenium.webdriven.WebDriverBackedSelenium) selenium)
                            .getWrappedDriver().getTitle();
                }
            } catch (Throwable notfound) {
                Log.debug("SGU: getAllWindowNames handling older selenium install "
                        + notfound.getClass().getSimpleName());
                /** WebDriverBackedSelenium version 37 move to com.thoughtworks.selenium.webdriven in 42 selenium version
                try{
                   if(selenium instanceof org.openqa.selenium.WebDriverBackedSelenium){
                      names[0] = ((org.openqa.selenium.WebDriverBackedSelenium) selenium).getWrappedDriver().getTitle();
                   }
                }catch(Throwable ignore){
                   Log.debug("SGU: getAllWindowNames ignoring newer package name change "+ ignore.getClass().getSimpleName());
                }
                */
            }

        } finally {
            String thename = null;
            for (int i = 0; i < names.length; i++) {
                thename = names[i].trim();
                Log.debug("SGU: found windowName: " + thename);
                if (thename.equals("")) {
                    Log.debug("SGU: making windowName: '" + UNNAMED_WINDOW + "'");
                    names[i] = UNNAMED_WINDOW;
                }
            }
        }
        return names;
    }

    /**
     * Get's the window's id and returns it in the form of an SGuiObject
     * @param selenium current selenium object
     * @param winRec window recognition string
     * @return object representing the window defined by the winRec
     */
    public SGuiObject getWindowIdFromTitle(Selenium selenium, String winRec) {
        String[] windowNames = null;
        selectWindow(selenium, null, 5);
        windowNames = getAllWindowNames(selenium);
        if (windowNames != null && windowNames.length > 0) {
            boolean isDynamic = ApplicationMap.isGUIIDDynamic(winRec);
            winRec = ApplicationMap.extractTaggedGUIID(winRec);
            winRec = winRec.substring(winRec.lastIndexOf("=") + 1);
            if (winRec.indexOf("{") == 0 && winRec.indexOf("}") == winRec.length() - 1) {
                winRec = winRec.substring(1);
                winRec = winRec.substring(0, winRec.length() - 1);
            }
            boolean windowLoaded = false;
            String window = null;
            String title = null;
            boolean titleObtained = false;
            int time = 0;
            Log.info("SGU: getWindowIdFromTitle: " + winRec);
            for (int i = 0; i < windowNames.length; i++) {
                window = windowNames[i].trim();
                // invalid windows
                windowLoaded = selectWindow(selenium, window, 5);
                if (windowLoaded) {
                    title = "";
                    time = 0;
                    titleObtained = false;
                    do {
                        try {
                            title = selenium.getTitle();
                            titleObtained = true;
                        } catch (Exception e) {
                            try {
                                Thread.sleep(1000);
                                time++;
                            } catch (InterruptedException e1) {
                            }
                        }
                    } while (!titleObtained && time < 5);

                    if (titleObtained && checkTitle(title, winRec)) {
                        Log.debug("SGU: Selecting window: '" + title + "' with id: '" + window + "'");
                        return new SGuiObject("//HTML[1]", "Window", window, isDynamic);
                    } else {
                        Log.debug("SGU: getWindowIdFromTitle could not retrieve title of '" + window + "'.");
                    }
                } else {
                    Log.debug("SGU: getWindowIdFromTitle failed loading of '" + window + "'.");
                }
            }
        }
        return null;
    }

    /**
     * Checks whether the window title matches the regular expression contained by the winRec
     * @param title window title obtained by Selenium
     * @param winRec windows recognition string
     * @return true if matched, false otherwise
     */
    private boolean checkTitle(String title, String winRec) {
        String aRec = org.safs.StringUtils.convertWildcardsToRegularExpression(winRec);
        try {
            return org.safs.StringUtils.matchRegex(aRec, title);
        } catch (SAFSException e) {
            return false;
        }
    }

    /**
     * Attempts to "activate" or give focus to the specified window/object via the remote 
     * AUT JVM proxies.
     * 
     * @return 0 on success.
     * @throws SAFSObjectNotFoundException if the specified component cannot be found.
     * @see org.safs.DDGUIUtilities#setActiveWindow(String,String,String)
     */
    public int setActiveWindow(String appMapName, String windowName, String compName)
            throws SAFSObjectNotFoundException {
        Log.info("SGU:Activating " + windowName + "." + compName + " from AppMap:" + appMapName);
        ApplicationMap map = null;
        try {
            map = getAppMap(appMapName);
            boolean isParent = windowName.equalsIgnoreCase(compName);
            Selenium selenium = SApplicationMap.getSelenium(windowName);
            SGuiObject winObj = (SGuiObject) map.getParentObject(windowName);
            Object compObj;
            if (winObj == null)
                waitForObject(appMapName, windowName, windowName, 0);
            if (!isParent) {
                compObj = map.getChildObject(windowName, compName);
                // if not previously found then find it with no timeout value
                // routine will pass thru exception thrown from waitForObject                            
                if (compObj == null)
                    waitForObject(appMapName, windowName, compName, 0);
                compObj = map.getChildObject(windowName, compName);
                if (compObj == null)
                    throw new NullPointerException();
            }

            if (winObj == null)
                throw new NullPointerException();

            // server setActive or server.invoke, etc..
            selectWindow(selenium, winObj.getWindowId(), 5);
            return 0;
        } catch (NullPointerException np) {
            String msg = windowName + "." + compName + " using MapName:" + appMapName + ";ApplicationMap:";
            msg += (map == null) ? "NULL" : map.getMapName();
            Log.debug("SGU:SAW EXCEPTION with " + msg);
            throw new SAFSObjectNotFoundException(msg);
        } catch (SAFSObjectNotFoundException nf) {
            throw nf;
        }
    }

    /**
     * Selects the top frame if its not already selected
     * @param selenium Current selenium object
     */
    public void selectTopFrame(Selenium selenium) {
        if (frameSwitched) {
            selenium.selectFrame("relative=top");
            frameSwitched = false;
        }
    }

    private String _getProperty(Selenium selenium, String xpath, String property) {
        String val = null;
        try {
            val = selenium.getAttribute(xpath + "@" + property);
        } catch (Exception x) {
            if ((val != null) && (val.equals("undefined"))) {
                val = null;
            }
        }
        return val;
    }

    /**
     * Return the value of a specific attribute/property
     * @param selenium current Selenium object
     * @param xpath Selenium XPATH to desired element
     * @param attribute case-sensitive property name
     * @return value of property or null
     */
    public String getProperty(Selenium selenium, String xpath, String attribute) {
        String val = null;
        if (attribute.equalsIgnoreCase("text") || attribute.equalsIgnoreCase("innerText")) {

            val = _getProperty(selenium, xpath, "innerText");
            if (val == null)
                val = _getProperty(selenium, xpath, "innertext");
            if (val == null)
                val = _getProperty(selenium, xpath, "textContent");
            if ((val != null) && (val.length() == 0)) {
                val = null;
                val = _getProperty(selenium, xpath, "value");
            }
            if (val == null)
                val = _getProperty(selenium, xpath, "alt");
        } else {
            val = _getProperty(selenium, xpath, attribute);
        }
        return val;
    }

    /**
     * Returns the xpath of the component represented by a robot recognition string
     * @param recString robot recognition string
     * @param selenium current selenium object
     * @param windowId window containing the object
     * @return xpath string
     */
    public SGuiObject getGuiObject(String recString, Selenium selenium, String windowId) {
        String xPath = "";

        selectTopFrame(selenium);

        boolean isDynamic = ApplicationMap.isGUIIDDynamic(recString);
        recString = ApplicationMap.extractTaggedGUIID(recString);
        recString = recString.replaceAll("\"", ""); // removes extra " in the recognition string.
        if (!(recString.toUpperCase().startsWith("TYPE="))) {
            recString = "Type=HTML;" + recString;
        }
        String[] parts = recString.split(";\\\\;");

        domParser = getDocumentParser(selenium);

        String url = domParser.getUrl();
        String type = "";
        Pattern typePattern = Pattern.compile("Type=(.+?)$");
        Pattern idPattern = Pattern.compile("(.+?)=(.+?)\"?$");
        for (int i = 0; i < parts.length; i++) {
            String[] subParts = parts[i].split(";");

            Matcher typeMatcher = typePattern.matcher(subParts[0]);
            Log.debug("SGU Pattern Matching subPart:" + subParts[0]);

            if (typeMatcher.find()) {
                type = typeMatcher.group(1);
            } else {
                type = "HTML";
            }
            Log.debug("SGU typeMatcher matching: " + type);

            String[] idTypes = new String[subParts.length - 1];
            String[] ids = new String[subParts.length - 1];
            for (int j = 1; j < subParts.length; j++) {
                Matcher idMatcher = idPattern.matcher(subParts[j]);
                Log.debug("SGU SubPart Matching: " + subParts[j]);

                if (idMatcher.find()) {
                    idTypes[j - 1] = idMatcher.group(1);
                    ids[j - 1] = idMatcher.group(2);
                } else {
                    //TODO:Error Report
                    Log.debug("SGU: ERROR: match not found for: " + subParts[j]);
                }
            }

            Document doc = domParser.getDocument(url, false);
            String xPart = typeToXPath(doc, type, idTypes, ids, selenium);
            HtmlFrameComp precedingFrame = null;
            if (!xPart.equals("")) {
                xPath += xPart;
                if (xPart.indexOf("FRAME") != -1) {
                    //If the xpart contains the "FRAME", we should navigate to the
                    //deepest frame and change the current url also.
                    //               this.navigateFrames(domParser.getUrl(), xPath + "//f", selenium);
                    precedingFrame = navigateFrames(url, xPath, selenium);
                    url = precedingFrame.getSrc();
                    Log.debug("Navigate to Frame, whose src is " + url);
                    if (url == null || url.equals("")) {
                        Log.error("Frame's src does NOT exist!!! Can NOT continue. Abort.");
                        return null;
                    }
                }
            } else {
                //If xPart is "", that means, we fail to match the whole RS
                //For example, "Type=HTMLFrame;name=toc1;\;Type=HTMLLink;Index=111"
                //We find xpath for "Type=HTMLFrame;name=toc1", but fail for "Type=HTMLLink;Index=111"
                //Maybe we should break the loop and return null
                return null;
            }
        }

        xPath = SeleniumGUIUtilities.normalizeXPath(xPath);

        SGuiObject co = new SGuiObject(xPath, type, windowId, isDynamic);
        Log.debug("XPATH SCRIPT RETURNED: " + co.getLocator());
        return co;
    }

    public static String normalizeXPath(String xPath) {
        xPath = xPath.trim();
        Log.debug("Before normalization, xpath=" + xPath);
        //remove the last // from the xpath
        if (xPath.lastIndexOf("//") == xPath.length() - 2 && xPath.length() > 0) {
            xPath = xPath.substring(0, xPath.length() - 2);
        }

        //For selenium, the xpath must start with "//" so that is can be processed.
        //Without an explicit locator prefix, Selenium uses the following default strategies: 
        //xpath, for locators starting with "//"
        if (xPath.startsWith("/")) {
            if (xPath.length() > 1 && !xPath.substring(1, 1).equals("/")) {
                xPath = "/" + xPath;
            }
        } else {
            xPath = "//" + xPath;
        }

        Log.debug("After normalization, xpath=" + xPath);

        return xPath;
    }

    public static Matcher matchPattern(Pattern pattern, String xpath) {
        Matcher m = pattern.matcher(xpath);

        return m;
    }

    //TODO If the frame is not in format /HTML/FRAMSET/FRAME[1]/HTML/BODY
    //but /HTML/FRAMSET/FRAME/HTML/BODY, This function will give the wrong RS, need to be improved.
    protected String getFramePath(String xpath) {
        String framepath = "";
        Matcher m = FRAME_PATTERN.matcher(xpath);
        if (m.find()) {
            //TODO Here problem comes from substring
            framepath = "Type=HTMLFrame;name="
                    + m.group().substring(m.group().lastIndexOf("[") + 1, m.group().lastIndexOf("]"))
                    + DocumentParser.RECOGNITION_LEVEL_SEPARATOR;
            return framepath + getFramePath(xpath.substring(m.end()));
        } else {
            return "";
        }
    }

    /**
     * Selects any frames that are part of the xpath and <br>
     * returns the last component's xpath<br>
     * 
     * @param xpath xpath of the frames and component that needs to be interacted with
     * @param selenium current selenium object
     * @return the last component's xpath
     */
    protected String navigateFrames(String xpath, Selenium selenium) {
        String childXpath = null;

        HtmlFrameComp frameComp = navigateFrames(null, xpath, selenium, null);
        if (frameComp != null) {
            childXpath = frameComp.getChildXpath();
        }

        return childXpath;
    }

    /**
     * Selects any frames that are part of the xpath and <br>
     * returns the last Frame preceding final component xpath<br>
     * 
     * @param url   from where the xpath will be parsed, that is, the page indicated by url
     *              should contain the element described by xpath
     * @param xpath xpath of the frames and component that needs to be interacted with
     * @param selenium current selenium object
     * @return HtmlFrameComp, it contains<br>
     *         src:               the url (the last frame's src), where the last XPATH is located<br>
     *         locator:           the frame's locator recognized by selenium<br>
     *         fullXpath:         the full xpath to represent the last preceding frame<br>
     *         recognitionString: the recognition string of preceding frames<br>
     *         childXpath:        last part XPATH, without preceding frame's xpath<br>
     */
    protected HtmlFrameComp navigateFrames(String url, String xpath, Selenium selenium) {
        return navigateFrames(url, xpath, selenium, null);
    }

    /**
     * Selects any frames that are part of the xpath and <br>
     * returns the last Frame preceding final component xpath<br>
     * 
     * @param url   from where the xpath will be parsed, that is, the page indicated by url
     *              should contain the element described by xpath
     * @param xpath xpath of the frames and component that needs to be interacted with
     * @param selenium current selenium object
     * @param boundsOut object that will contain the bounds of the component after its found 
     * @return HtmlFrameComp, it contains<br>
     *         src:               the url (the last frame's src), where the last XPATH is located<br>
     *         locator:           the frame's locator recognized by selenium<br>
     *         fullXpath:         the full xpath to represent the last preceding frame<br>
     *         recognitionString: the recognition string of preceding frames<br>
     *         childXpath:        last part XPATH, without preceding frame's xpath<br>
     */
    protected HtmlFrameComp navigateFrames(String url, String xpath, Selenium selenium, Rectangle boundsOut) {
        selectTopFrame(selenium);

        if (url == null) {
            //We suppose that the xpath is to be searched within the current page, that is,
            //the url stored in the HtmlDomParser.
            DocumentParser parser = getDocumentParser(selenium);
            url = parser.getUrl();
        }
        //Try to remove the longest frame-prefix-xpath
        //And store the final component xpath in it.
        HtmlFrameComp frame = getPrefixingFrame(xpath);
        if (frame != null) {
            selenium.selectFrame(frame.getLocator());
            frameSwitched = true;
        } else {
            frame = new HtmlFrameComp(url, xpath);
        }

        //IMPORTANT!!!, If xpath does not contain FRAME, we should return directly<br>
        //and avoid to call navigateFramesR(), which will cost a lot of time<br>
        Matcher m = FRAME_PATTERN.matcher(frame.getChildXpath());
        if (!m.find()) {
            return frame;
        }

        return navigateFramesR(frame, selenium, boundsOut);
    }

    /**
     * Helper method for the navigateFrames() method
     * @param frame   Contains the information for navigation. Its field childXpath is to be navigated.
     * @param selenium current selenium object
     * @param boundsOut object that will contain the bounds of the component after its found
     * @return HtmlFrameComp, it contains<br>
     *         src:               the url (the last frame's src), where the last XPATH is located<br>
     *         locator:           the frame's locator recognized by selenium<br>
     *         fullXpath:         the full xpath to represent the last preceding frame<br>
     *         recognitionString: the recognition string of preceding frames<br>
     *         childXpath:        last part XPATH, without preceding frame's xpath<br><br>
     *         
     *         For example:<br>
     *         1. The childXpath does NOT contain FRAME<br>
     *            Input frame:
     *            src="http://tadsrv/safs/"<br>
     *            locator=""<br>
     *            fullXpath=""<br>
     *            recognitionString=""<br>
     *            childXpath="/HTML/BODY/TABLE"<br>
     *            The output frame will be exactly the same object.<br><br>
     *            
     *         2. The childXpath DOES contain some FRAMEs<br>
     *            Input frame:
     *            src="http://tadsrv/safs/"<br>
     *            locator=""<br>
     *            fullXpath=""<br>
     *            recognitionString=""<br>
     *            childXpath="/HTML/FRAMESET/FRAME[1]/HTML/BODY/TABLE"<br><br>
     *            
     *            We assume that the src of HTML/FRAMESET/FRAME[1] is 'framePage.htm'<br>
     *            We assume that the name of HTML/FRAMESET/FRAME[1] is 'frame1'<br>
     *            Output frame will be:
     *            src="http://tadsrv/safs/framePage.htm"<br>
     *            locator="name=frame1"<br>
     *            fullXpath="HTML/FRAMESET/FRAME[1]"<br>
     *            recognitionString="Type=HTMLFrame;Index=1;\\;"<br>
     *            childXpath="/HTML/BODY/TABLE"<br>
     */
    private HtmlFrameComp navigateFramesR(HtmlFrameComp frame, Selenium selenium, Rectangle boundsOut) {
        String debugmsg = getClass().getName() + ".navigateFramesR() ";
        Document document = null;
        String xpath = frame.getChildXpath();
        String url = frame.getSrc();
        String frameRS = frame.getRecognitionString();
        DocumentParser parser = getDocumentParser(selenium);

        Log.debug(debugmsg + " navigating xpath: " + xpath);
        Log.debug(debugmsg + " from url: " + url);
        Log.debug(debugmsg + " parent's frameRS: " + frameRS);

        if (url == null) {
            Log.debug(debugmsg + "  the url is null, can not continue!!!");
            return frame;
        }

        Matcher m = FRAME_PATTERN.matcher(xpath);
        if (m.find()) {
            String firstFrameXpath = m.group(1);
            Log.debug("Matched: " + firstFrameXpath);
            HtmlFrameComp childFrame = new HtmlFrameComp();
            try {
                if (boundsOut != null) {
                    Rectangle frameBounds = getComponentBounds(firstFrameXpath, selenium);
                    boundsOut.x += frameBounds.x;
                    boundsOut.y += frameBounds.y;
                }

                //            String frameName = selenium.getEval("SAFSgetAttribute('"+g+"', 'name');");
                document = parser.getDocument(url, false);
                String frameName = parser.getAttribute(document, firstFrameXpath, "name");
                String locator = "name=" + frameName;
                if (frameName == null || "".equals(frameName)) {
                    //               String frameIndex = selenium.getEval("SAFSgetFrameIndex('"+g+"')");
                    int frameIndex = parser.getFrameIndex(document, firstFrameXpath);
                    locator = "index=" + frameIndex;
                }
                childFrame.setLocator(locator);
                selenium.selectFrame(locator);
                Log.debug(debugmsg + ", switch to frame " + locator);

                if (frameRS == null) {
                    frameRS = "";
                }
                frameRS += "Type=HTMLFrame;" + locator + DocumentParser.RECOGNITION_LEVEL_SEPARATOR;

                frameSwitched = true;
            } catch (SeleniumException e) {
                Log.debug("SGU: Handling SeleniumException:", e);
                //            selenium.selectFrame(xpath.substring(0,m.end()));
                selenium.selectFrame(firstFrameXpath);
                frameSwitched = true;
            }

            String childFrameURL = parser.getFrameSrcURL(document, firstFrameXpath, url);
            String fullFrameXpath = frame.getFullXpath() + firstFrameXpath;
            String childFrameChildXpath = xpath.substring(firstFrameXpath.length());

            childFrame.setSrc(childFrameURL);
            childFrame.setFullXpath(fullFrameXpath);
            childFrame.setRecognitionString(frameRS);
            childFrame.setChildXpath(childFrameChildXpath);
            //Put the frame to the cache
            addPrefixingFrameToCache(fullFrameXpath, childFrame);

            if (childFrameURL.equals(url)) {
                Log.warn("Frame '" + firstFrameXpath + "', it does NOT has attribut 'src'.");
                Log.warn("Frame '" + firstFrameXpath + "' does NOT contain any content.");
                return childFrame;
            }

            return navigateFramesR(childFrame, selenium, boundsOut);
        } else {
            Log.debug("SGU: navigateFramesR found no FRAME to evaluate.");
            return frame;
        }
    }

    /**
     * Returns the bounds of a component as a Rectangle object
     * @param o object describing the component
     * @param selenium current selenium object
     * @return bounds as a rectangle object
     */
    protected Rectangle getComponentBounds(SGuiObject o, Selenium selenium) {
        return getComponentBounds(o.getLocator(), selenium);
    }

    /**
     * Returns the absolute bounds of a component on the screen as a Rectangle object
     * @param xpath xpath string describing the component
     * @param selenium current selenium object
     * @return bounds as a rectangle object or null
     */
    protected Rectangle getComponentBounds(String xpath, Selenium selenium) {
        DocumentParser parser = getDocumentParser(selenium);
        String[] compBounds = null;
        Rectangle compRect = null;
        Rectangle frameBounds = new Rectangle();
        HtmlFrameComp precedingFrame = null;
        String xend = null;
        String temp = null;
        String boundsSeparator = "#";
        int browser_left = 0;
        int browser_top = 0;

        try {
            //         String xpathBoundsSeparator = selenium.getEval("SAFSGetBoundsSeparator();");
            String xpathBoundsSeparator = parser.getBoundsSeparator();
            if (xpathBoundsSeparator != null && !xpathBoundsSeparator.trim().equals("")) {
                Log.debug("Selenium SPC get xpathBoundsSeparator: " + xpathBoundsSeparator);
                boundsSeparator = xpathBoundsSeparator.trim();
            }
        } catch (Exception e) {
            Log.debug("SGU: SAFSGetBoundsSeparator() Can NOT get boundsSeparator.");
        }

        try {
            //         temp = selenium.getEval("SAFSgetBrowserClientScreenPosition();");
            temp = parser.getBrowserClientScreenPosition(selenium);
            //Log.debug("SGU: SAFSgetBrowserClientScreenPosition() eval: "+ temp);
            compBounds = temp.split(boundsSeparator);
            browser_left = Integer.parseInt(compBounds[0]);
            browser_top = Integer.parseInt(compBounds[1]);
        } catch (Exception fx) {
            Log.debug("SGU: IGNORING SAFSgetBrowserClientScreenPosition() eval Exception.", fx);
        }

        int scrollLeft = 0;
        int scrollTop = 0;
        String[] scrollInfo = null;
        try {
            //         temp = selenium.getEval("SAFSgetClientScrollInfo();");
            temp = parser.getClientScrollInfo(selenium);
            //Log.debug("SGU: SAFSgetBrowserClientScreenPosition() eval: "+ temp);
            scrollInfo = temp.split(boundsSeparator);
            // skipped extracting innerWidth and height
            scrollLeft = Integer.parseInt(scrollInfo[2]);
            scrollTop = Integer.parseInt(scrollInfo[3]);
        } catch (Exception fx) {
            Log.debug("SGU: IGNORING SAFSgetClientScrollInfo() eval Exception.", fx);
        }

        precedingFrame = navigateFrames(domParser.getUrl(), xpath, selenium, frameBounds);
        xend = precedingFrame.getChildXpath();

        if (frameBounds.x == 0 && frameBounds.y == 0) {
            frameBounds.x = browser_left;
            frameBounds.y = browser_top;
        }
        Number top = null;
        Number left = null;
        Number width = null;
        Number height = null;
        if (xend == null)
            xend = xpath;
        try {
            top = selenium.getElementPositionTop(xend);
            left = selenium.getElementPositionLeft(xend);
            width = selenium.getElementWidth(xend);
            height = selenium.getElementHeight(xend);
            compRect = new Rectangle(left.intValue(), top.intValue(), width.intValue(), height.intValue());
            compRect.x += frameBounds.x;
            compRect.y += frameBounds.y;
            // account for scrolling
            compRect.x -= scrollLeft;
            compRect.y -= scrollTop;
        } catch (Exception nx) {
            Log.debug("SGU: IGNORING getComponentBounds Rectangle Exception for '" + xend + "':", nx);
        }
        return compRect;
    }

    /**
     * Helper method for the getXpathString method
     * @param type component type
     * @param idTypes attribute types, this is SAFS-Robot attribute's type
     * @param ids attribute values
     * @param selenium current selenium object
     * @return xpath string representing the component
     */
    private String typeToXPath(Document doc, String type, String[] idTypes, String[] ids, Selenium selenium) {
        String xPath = "";

        if (type.equalsIgnoreCase("HTMLFrame")) {
            String[] tags = { "FRAME", "IFRAME" };
            xPath = this.buildXPathAttributes(doc, idTypes, ids, tags, selenium);
        } else if (type.equalsIgnoreCase("Window")) {
            String page = ids[0];
            page = page.substring(1, page.length() - 2);
            xPath = page;
        } else if (type.equalsIgnoreCase("HTMLLink")) {
            xPath = this.buildXPathAttributes(doc, idTypes, ids, "A", selenium);
        } else if (type.equalsIgnoreCase("HTMLDocument")) {
            if (idTypes[0].equalsIgnoreCase("HTMLTitle") && ids[0].equals(selenium.getTitle())) {
                xPath = "//HTML[1]";
            } else {
                xPath = this.buildXPathAttributes(doc, idTypes, ids, "HTML", selenium);
            }
        } else if (type.equalsIgnoreCase("HTMLTable")) {
            xPath = this.buildXPathAttributes(doc, idTypes, ids, "TABLE", selenium);
        } else if (type.equalsIgnoreCase("HTMLTableCell")) {
            String[] tags = { "TD", "TH" };
            xPath = this.buildXPathAttributes(doc, idTypes, ids, tags, selenium);
        } else if (type.equalsIgnoreCase("HTMLTableHeaderCell")) {
            xPath = this.buildXPathAttributes(doc, idTypes, ids, "TH", selenium);
        } else if (type.equalsIgnoreCase("HTMLImage")) {
            xPath = this.buildXPathAttributes(doc, idTypes, ids, "IMG", selenium);
        } else if (type.equalsIgnoreCase("HTMLMap")) {
            xPath = this.buildXPathAttributes(doc, idTypes, ids, "MAP", selenium);
        } else if (type.equalsIgnoreCase("HTMLMapArea")) {
            xPath = this.buildXPathAttributes(doc, idTypes, ids, "AREA", selenium);
        } else if (type.equalsIgnoreCase("EditBox")) {
            String[] tags = { "INPUT", "TEXTAREA" };
            String[] idTTemp = new String[idTypes.length + 1];
            String[] idTemp = new String[ids.length + 1];
            idTTemp[0] = "type";
            idTemp[0] = "text|password|undefined"; // no 'type' attrib seems to imply type=text
            for (int i = 1; i < idTTemp.length; i++) {
                idTTemp[i] = idTypes[i - 1];
                idTemp[i] = ids[i - 1];
            }
            xPath = this.buildXPathAttributes(doc, idTTemp, idTemp, tags, selenium);
        } else if (type.equalsIgnoreCase("CheckBox")) {
            String[] idTTemp = new String[idTypes.length + 1];
            String[] idTemp = new String[ids.length + 1];
            idTTemp[0] = "type";
            idTemp[0] = "checkbox";
            for (int i = 1; i < idTTemp.length; i++) {
                idTTemp[i] = idTypes[i - 1];
                idTemp[i] = ids[i - 1];
            }
            xPath = this.buildXPathAttributes(doc, idTTemp, idTemp, "INPUT", selenium);
        } else if (type.equalsIgnoreCase("RadioButton")) {
            String[] idTTemp = new String[idTypes.length + 1];
            String[] idTemp = new String[ids.length + 1];
            idTTemp[0] = "type";
            idTemp[0] = "radio";
            for (int i = 1; i < idTTemp.length; i++) {
                idTTemp[i] = idTypes[i - 1];
                idTemp[i] = ids[i - 1];
            }
            xPath = this.buildXPathAttributes(doc, idTTemp, idTemp, "INPUT", selenium);
        } else if (type.equalsIgnoreCase("ComboBox")) {
            xPath = this.buildXPathAttributes(doc, idTypes, ids, "SELECT", selenium);
        } else if (type.equalsIgnoreCase("ListBox")) {
            xPath = this.buildXPathAttributes(doc, idTypes, ids, "SELECT", selenium);
        } else if (type.equalsIgnoreCase("PushButton")) {
            String[] tags = { "INPUT", "BUTTON" };
            String[] idTTemp = new String[idTypes.length + 1];
            String[] idTemp = new String[ids.length + 1];
            idTTemp[0] = "type";
            idTemp[0] = "submit|button";
            for (int i = 1; i < idTTemp.length; i++) {
                idTTemp[i] = idTypes[i - 1];
                idTemp[i] = ids[i - 1];
            }
            xPath = this.buildXPathAttributes(doc, idTTemp, idTemp, tags, selenium);
        } else if (type.equalsIgnoreCase("HTMLHidden")) {
            String[] idTTemp = new String[idTypes.length + 1];
            String[] idTemp = new String[ids.length + 1];
            idTTemp[0] = "type";
            idTemp[0] = "hidden";
            for (int i = 1; i < idTTemp.length; i++) {
                idTTemp[i] = idTypes[i - 1];
                idTemp[i] = ids[i - 1];
            }
            xPath = this.buildXPathAttributes(doc, idTTemp, idTemp, "INPUT", selenium);
        } else if (type.equalsIgnoreCase("HTML")) {
            xPath = this.buildXPathAttributes(doc, idTypes, ids, "DIV", selenium);
        } else {
            Log.warn("Unrecognized: " + type);
            Log.debug("SGU:typeToXpath Unrecognized component 'type': " + type);
        }

        return xPath;
    }

    /**
     * Helper method for getXpathString()
     * @param idTypes attribute types, this is SAFS-Robot attribute's type
     * @param ids attribute values
     * @param tag html tag of the component
     * @param selenium current selenium object
     * @return xpath string of the component
     */
    private String buildXPathAttributes(Document doc, String[] idTypes, String[] ids, String tag,
            Selenium selenium) {
        String[] tags = { tag };
        return buildXPathAttributes(doc, idTypes, ids, tags, selenium);
    }

    /**
     * Helper method for getXpathString()
     * @param idTypes attribute types, this is SAFS-Robot attribute's type
     * @param ids attribute values
     * @param tags potential html tags of the component
     * @param selenium current selenium object
     * @return xpath string of the component
     */
    private String buildXPathAttributes(Document doc, String[] idTypes, String[] ids, String[] tags,
            Selenium selenium) {
        //attributes contains the conditions for matching an element
        String[][] attributes = new String[idTypes.length][2];
        String xPath = "";
        //Convert the idTypes[i] to html-attribute and put it in attributes[i][0],
        //put the ids[i] (the attribute value) in attributes[i][1]
        for (int i = 0; i < idTypes.length; i++) {
            if (idTypes[i].equalsIgnoreCase("HTMLId")) {
                attributes[i][0] = "id";
                attributes[i][1] = ids[i];
            } else if (idTypes[i].equalsIgnoreCase("name")) {
                attributes[i][0] = "name";
                attributes[i][1] = ids[i];
            } else if (idTypes[i].equalsIgnoreCase("HTMLTitle")) {
                attributes[i][0] = "title";
                attributes[i][1] = ids[i];
            } else if (idTypes[i].equalsIgnoreCase("HTMLText") || idTypes[i].equalsIgnoreCase("Text")) {
                attributes[i][0] = "text";
                attributes[i][1] = ids[i];
            } else if (idTypes[i].equalsIgnoreCase("type")) {
                attributes[i][0] = "type";
                attributes[i][1] = ids[i];
            } else if (idTypes[i].equalsIgnoreCase("Index")) {
                //Get the Index
                int index = Integer.parseInt(ids[i]) - 1;
                //Copy the rest attributes as criteria for matching node.
                String[][] criteria = new String[attributes.length - 1][2];
                for (int j = 0; j < criteria.length; j++) {
                    criteria[j][0] = attributes[j][0];
                    criteria[j][1] = attributes[j][1];
                }
                //Pass criteria and index to get the matching component's xpath
                xPath = getXPath(doc, tags, criteria, index, selenium);
                //TODO If we can not find, do we need continue???
            } else {
                attributes[i][0] = idTypes[i];
                attributes[i][1] = ids[i];
            }
        }
        //If we don't have a "Index=aInt", we are just happy to return the first matching
        if (xPath.equals("")) {
            xPath = getXPath(doc, tags, attributes, 0, selenium);
        }
        return xPath;
    }

    /**
     * getXpath() helper method
     * @param document, from which document to search the matching element
     * @param tags potential html tags of the object
     * @param attributes html-tags-attributes and values
     * @param index index of the component on the page
     * @param selenium current selenium object
     * @return xpath string of component
     */
    private String getXPath(Document document, String[] tags, String[][] attributes, int index, Selenium selenium) {
        DocumentParser parser = getDocumentParser(selenium);

        String xpath = parser.getXpath(document, tags, attributes, index, false, true);
        Log.debug("SGU DocumentParser getXpath returned: " + xpath);

        return xpath;
    }

    /**
     * <em>Purpose:</em>  Create a new HtmlDoParser, it will replace the older one.
     * @param selenium
     * @return
     */
    protected DocumentParser initDocumentParser(Selenium selenium) {
        if (selenium == null) {
            Log.error("The parameter selenium should NOT be null");
            return null;
        }
        domParser = new DocumentParser(selenium, this);

        return domParser;
    }

    /**
     * <em>Purpose:</em>  Return a HtmlDoParser, if not exist, create a new one.
     * @param selenium
     * @return
     */
    public DocumentParser getDocumentParser(Selenium selenium) {
        if (selenium == null) {
            Log.error("The parameter selenium should NOT be null");
            return null;
        }

        if (domParser == null) {
            domParser = new DocumentParser(selenium, this);
        } else {
            try {
                if (!selenium.getLocation().equals(domParser.getUrl())) {
                    //Selenium API getLocation() will return "the absolute URL of the current page."
                    //if it is different than that of the main url stored in the documentParser
                    //There are 2 situations
                    //1. If we have navigated between the frames within the same page ( for example,
                    //   calling selenium.selectFrame(frameLocator) ). Although getLocation() will return 
                    //   a different url, but we do NOT need to change the main url of DocumentParser.

                    //2. If we click a link to visit another web page.
                    //   getLocation() will return a different url and we need to change the main url of DocumentParser.
                    Log.debug("selenium.getLocation(): " + selenium.getLocation());
                    Log.debug("domParser.getUrl(): " + domParser.getUrl());
                    boolean changeMainPageURL = !frameSwitched;
                    domParser.setDocument(selenium.getLocation(), selenium.getHtmlSource(), changeMainPageURL);
                } else {
                    //If the url is the same, We consider the Document is the same
                }
            } catch (Exception e) {
                Log.warn("Exception occur " + e.getMessage());
            }
        }

        return domParser;
    }

    /**
     * <em>Purpose:</em>   Return the value of attribute for an element indicated by xpath
     * <em>Note:</em>      This method will try to DocumentParser to get the value<br>
     *                      If it can not get the value, it will try the selenium's API getAttribute.<br>
     * @param selenium      The instance of Selenium
     * @param xpath         Xpath to represent the element in html page
     * @param attribute     The name of attribute
     * @return              The value of attribute
     * @see org.safs.selenium.DocumentParser#getAttribute(String, String, String)
     */
    public String getAttribute(Selenium selenium, String xpath, String attribute) {
        String value = null;
        DocumentParser parser = getDocumentParser(selenium);
        if (parser == null) {
            Log.error("Can NOT get the document parser, it is null");
            return null;
        }

        Log.debug("Object's selenium xpath is " + xpath + " ; try to get value for attribute " + attribute);
        value = parser.getAttribute(parser.getUrl(), xpath, attribute);

        return value;
    }

    /**
     * <em>Purpose:</em>   Return the attributes for an element indicated by xpath
     * <em>Note:</em>      This method will try to DocumentParser to get the properties<br>
     * @param selenium      The instance of Selenium
     * @param xpath         Xpath to represent the element in html page
     * @return              The attributes
     * @see org.safs.selenium.DocumentParser#getAttributes(String, String)
     */
    public HashMap getAttributes(Selenium selenium, String xpath) {
        HashMap value = null;
        DocumentParser parser = getDocumentParser(selenium);
        if (parser == null) {
            Log.error("Can NOT get the document parser, it is null");
            return null;
        }

        Log.debug("Object's selenium xpath is " + xpath + " ; try to get all its attributes ");
        value = parser.getAttributes(parser.getUrl(), xpath);

        return value;
    }

    /**
     * Gets all the xpaths for a certain html tag
     * @param tag html tag of the components to get the xpaths for
     * @param selenium current selenium object
     * @return array of all the xpath strings
     */
    public String[] getAllXPaths(String tag, Selenium selenium) {
        //      String attributeString = "var attrcheck = new Array();";
        //      String tagString = "var tags = new Array();tags[0] = \"" + tag + "\";";
        //      String result = selenium.getEval(attributeString+tagString+"SAFSgetXpath(tags,attrcheck);");
        //      String [] results = result.split(";");
        //      return results;

        DocumentParser parser = getDocumentParser(selenium);
        String[] tags = { tag };
        Document doc = parser.getDocument(parser.getUrl(), false);
        List<String> allXPaths = parser.getAllXPath(doc, tags);

        return allXPaths.toArray(new String[0]);
    }

    public List<?> extractListItems(Object obj, String countProp, String itemProp) throws SAFSException {
        //TODO Auto-generated method stub
        return null;
    }

    public Tree extractMenuBarItems(Object obj) throws SAFSException {
        //TODO Auto-generated method stub
        return null;
    }

    public Tree extractMenuItems(Object obj) throws SAFSException {
        //TODO Auto-generated method stub
        return null;
    }

    public Object findPropertyMatchedChild(Object obj, String property, String bench, boolean exactMatch)
            throws SAFSObjectNotFoundException {
        //TODO Auto-generated method stub
        return null;
    }

    public String getListItem(Object obj, int i, String itemProp) throws SAFSException {
        //TODO Auto-generated method stub
        return null;
    }

    private native boolean focusWindowByCaption(String title);

    private native void maximizeWindowByCaption(String title);

    private native void minimizeWindowByCaption(String title);

    private native void restoreWindowByCaption(String title);

    static {
        System.loadLibrary("ComponentFunctions");
    }

    /**
     * Uses ComponentFunctions.dll to focus the window with the given recognition string.
     * @param winRec window to be focused
     * @return true if window was found, false otherwise
     */
    public boolean setWindowFocus(String winRec) {
        winRec = parseWinRecForRegex(winRec);
        boolean focused = focusWindowByCaption(winRec);
        if (focused) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
        }
        return focused;
    }

    /**
     * Uses Selenium to focus the window with the given window id.
     * @param windowId  window's name or title
     * @param selenium  the instance of Selenium object
     * @return true if window was found, false otherwise
     */
    public boolean setWindowFocus(String windowId, Selenium selenium) {
        boolean focused = false;

        try {
            if (selectWindow(selenium, windowId, 1)) {
                selenium.windowFocus();
                focused = true;
            }
        } catch (Exception e) {
            Log.debug("Focused fail: " + e.getMessage());
            focused = false;
        }

        if (focused) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
        }
        return focused;
    }

    /**
     * Uses Selenium to maximize the window with the given window id.<br>
     * <em>Note:</em>   The window can't be resotre after it is maximized by this method.
     * @param windowId  window's name or title
     * @param selenium  the instance of Selenium object
     * @return true if window was maximized, false otherwise
     */
    public boolean maximizeWindow(String windowId, Selenium selenium) {
        boolean maximized = false;

        try {
            if (selectWindow(selenium, windowId, 1)) {
                selenium.windowMaximize();
                maximized = true;
            }
        } catch (Exception e) {
            Log.debug("Maximize fail: " + e.getMessage());
            maximized = false;
        }

        return maximized;
    }

    /**
     * Uses ComponentFunctions.dll to maximize the window with the given recognition string.
     * @param winRec window to be maximize
     */
    public void maximizeWindow(String winRec) {
        winRec = parseWinRecForRegex(winRec);
        maximizeWindowByCaption(winRec);
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
        }
    }

    /**
     * Uses ComponentFunctions.dll to maximize the window with the given recognition string.
     * @param winRec window to be maximize
     */
    public void minimizeWindow(String winRec) {
        winRec = parseWinRecForRegex(winRec);
        minimizeWindowByCaption(winRec);
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
        }
    }

    /**
     * Uses ComponentFunctions.dll to maximize the window with the given recognition string.
     * @param winRec window to be maximize
     */
    public void restoreWindow(String winRec) {
        winRec = parseWinRecForRegex(winRec);
        restoreWindowByCaption(winRec);
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
        }
    }

    private String parseWinRecForRegex(String winRec) {
        if (winRec.indexOf(";") > -1) {
            winRec = (winRec.split(";")[1]).split("=")[1];
        }
        //TODO:Escape anything else that could be reg ex operator.
        if (winRec.indexOf("{") == 0 && winRec.indexOf("}") == winRec.length() - 1) {
            winRec = winRec.substring(1);
            winRec = winRec.substring(0, winRec.length() - 1);
            winRec = winRec.replaceAll("\\*", ".*");
            winRec = winRec.replaceAll("\\?", ".?");
        } else {
            winRec = winRec.replaceAll("\\*", "\\*");
            winRec = winRec.replaceAll("\\?", "\\?");
        }
        return winRec;
    }

    /**
     * This method will try to match the longest-frame-prefix-xpath for the input xpath.<br>
     * We try the key (xpathOfFrame) one by one of cache frames, the keys are descending ordered<br>
     * that is to say, the longest xpath will be tried first, the shortest last<br>
     * If we find one kye is the prefix of input xpath, we get the object HtmlFrameComp from cache<br>
     * and return it.<br>
     * This method will also store the final component's xpath in the instance of HtmlFrameComp<br>
     * as its childXpath<br>
     * @param xpath
     * @return
     */
    private HtmlFrameComp getPrefixingFrame(String xpath) {
        HtmlFrameComp frameComp = null;
        Set<String> keys = null;
        Iterator<String> iter = null;
        String frameXpath = null;
        String childXpath = null;

        //frames is a TreeMap, it stores the frame's xpath as key in descending order
        //according to its length
        if (frames != null && !frames.isEmpty()) {
            keys = frames.keySet();
            iter = keys.iterator();
            while (iter.hasNext()) {
                frameXpath = iter.next();
                if (xpath.startsWith(frameXpath)) {
                    Log.debug("SGU.getPrefixingFrame() ################## MATCHED PREFIX = " + frameXpath
                            + "; FOR XPATH=" + xpath);
                    //Need to check the child xpath, if it begin with "/", if not continue find the next match
                    //If we have /HTML/FRAMESET/FRAME in the cache, but we try to match prefix for /HTML/FRAMESET/FRAMESET/FRAME/HTML/BODY
                    //we will get a wrong result, it will consider SET/FRAME/HTML/BODY as child contained in /HTML/FRAMESET/FRAME; while
                    //the correct result is that /HTML/BODY is child of /HTML/FRAMESET/FRAMESET/FRAME
                    childXpath = xpath.substring(frameXpath.length());
                    if (childXpath.startsWith("/")) {
                        frameComp = frames.get(frameXpath);
                        frameComp.setChildXpath(childXpath);
                        //Should never happen
                        if (!frameXpath.equals(frameComp.getFullXpath())) {
                            frameComp.setFullXpath(frameXpath);
                            Log.error("SGU.getPrefixingFrame() ############# key not match frame's full xpath");
                        }
                        break;
                    }
                }
            }
        }

        return frameComp;
    }

    private void addPrefixingFrameToCache(String xpath, HtmlFrameComp frame) {
        if (!frames.containsKey(xpath)) {
            frames.put(xpath, frame);
        }
    }

    public void clearFramesCache() {
        frames.clear();
    }
}