pt.fccn.arquivo.pages.ReplayPage.java Source code

Java tutorial

Introduction

Here is the source code for pt.fccn.arquivo.pages.ReplayPage.java

Source

/**
 * Copyright (C) 2016 Fernando Melo <fernando.melo@fccn.pt>
 * Copyright (C) 2016 SAW Group - FCCN <saw@asa.fccn.pt>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package pt.fccn.arquivo.pages;

import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import java.io.FileInputStream;
import java.util.Properties;
import java.io.File;
import java.io.InputStreamReader;
import java.io.FileOutputStream;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.TreeMap;
import java.util.Set;

/**
 * @author Fernando Melo
 *
 */

public class ReplayPage {
    private final WebDriver driver;
    private TreeMap<String, String> testURLs = new TreeMap<String, String>();
    private boolean isPreProd;
    private String baseURL;
    private String cgi_path;
    private String tableofVersionsURLExpected;
    private String logoURLPTExpected;
    private String searchURL;
    private static final String filenamePreProd = "pre_production_URL_testlist.txt";
    private static final String filenameProd = "production_URL_testlist.txt";
    private static final int waitingPeriod = 10000; //Time to load the Web page in miliseconds
    private static final String helpURLPTExpected = "http://sobre.arquivo.pt/ajuda";
    private static final String emailHrefPTExpected = "mailto:?subject=Arquivo.pt%20pesquise%20p%C3%A1ginas%20do%20passado%20&body=Ol%C3%A1,%0AEncontrei%20este%20website%20que%20poder%C3%A1%20achar%20interessante:%20[sub]";
    private static final String emailOnClickExpected = "this.href = this.href.replace('[sub]',window.location)";
    private static final String tableofVersionsPTExpected = "Tabela de verses";
    private static final String activeItem = "active";
    private static final String activeDay = "active-day";
    private String serverName = "";
    private BufferedReader br;
    private String pageUrl = "";
    private String[] tokens;
    private Properties prop;
    private BufferedReader inputPt = null;
    private BufferedReader inputEn = null;
    private String baseScreenshotURL;

    public ReplayPage(WebDriver driver, boolean isPreProd) {
        System.out.println("[ReplayPage]");
        this.driver = driver;
        //driver.manage().window().setSize(new Dimension(1280, 768));
        br = null;
        pageUrl = driver.getCurrentUrl();
        tokens = pageUrl.split("/");
        serverName = "https://" + tokens[2] + "/";
        baseScreenshotURL = serverName + "screenshot/?url=";
        logoURLPTExpected = searchURL + "/?l=pt";
        prop = new Properties();
        System.out.println("[ReplayPage] read properties");
        try {
            inputPt = new BufferedReader(new InputStreamReader(new FileInputStream("pt.properties"), "UTF8"));
            inputEn = new BufferedReader(new InputStreamReader(new FileInputStream("en.properties"), "UTF8"));
            prop.load(inputPt);
            // start with properties in PT
            String currentLine;
            if (isPreProd)
                br = new BufferedReader(new FileReader(filenamePreProd));
            else
                br = new BufferedReader(new FileReader(filenameProd));
            System.out.println("[ReplayPage] read testURLs");
            while ((currentLine = br.readLine()) != null) {
                String[] parts = currentLine.split("\t");
                /*parts[0] is the timestamp/url */
                /*parts[1] is the expected title for the parts[0] url*/
                testURLs.put(parts[0], parts[1]);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (br != null)
                    br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Do the Replay Tests for our set of URLS
     */
    public boolean inspectURLs(String language) {
        System.out.println("Inspecting URLS language: " + language);
        if (language.equals("EN")) {
            //switch properties to english
            try {
                prop.load(inputEn);
            } catch (IOException e) {
                System.out.println("Error Loading English Properties");
                return false;
            }
        }

        Set<String> keys = testURLs.keySet();
        for (String currentURL : keys) {
            try {
                goToCurrentURL(currentURL, testURLs.get(currentURL));
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("Error jumping to url: " + currentURL);
                return false;
            }
            switchLanguage(language); // Can be optimized to only change TO PT on the first URL, and all others have to be in PT too

            System.out.println("[replayBarTest][inspectURLs] Bar[" + replayBarURLsOk(currentURL) + "] "
                    + "facebook[" + facebookOk(currentURL) + "] twitter[" + twitterOk(currentURL) + "] " + "email["
                    + emailOk(currentURL) + "] tableOfVersion[" + tableOfVersionsOk(currentURL) + "] " + " logo["
                    + logoOk(currentURL) + "] checkLefMenu[" + checkLeftMenu(currentURL) + "]");
            if (!replayBarURLsOk(currentURL) ||
            /*!screenshotOk(currentURL) || !printOk(currentURL) ||*/
                    !facebookOk(currentURL) || !twitterOk(currentURL) || !emailOk(currentURL)
                    || !tableOfVersionsOk(currentURL) || !logoOk(currentURL) || !checkLeftMenu(currentURL)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Check if both the expanded URL that appears on the replayBar is correct
     */
    public boolean replayBarURLsOk(String currentURL) {
        String maximzedURL = "";
        String minimizedURL = "";
        String urlWithoutDate = "";
        try {
            urlWithoutDate = currentURL.substring(15);
            urlWithoutDate = truncateURL(urlWithoutDate);
            maximzedURL = driver.findElement(By.xpath("//a[@id=\"update1\"]")).getText();

            //WebElement minimizeMaximizeElement = (new WebDriverWait(driver, 25)) /* Wait Up to 25 seconds should throw RunTimeExcpetion*/
            //    .until(ExpectedConditions.elementToBeClickable(By.xpath("//a[@id=\"minimizeOrMaximize\"]")));
            //minimizeMaximizeElement.click();

            System.out.println("URL: " + maximzedURL);

            if (maximzedURL.equals(urlWithoutDate)) {
                return true;
            } else {
                System.out.println("Expected URL: " + urlWithoutDate);
                System.out.println("Found URL: " + maximzedURL);
                return false;
            }
        } catch (NoSuchElementException e) {
            System.out.println("Expected URL: " + urlWithoutDate);
            System.out.println("Found maximized URL: " + maximzedURL);
            // Could not find a Version in the specified date
            return false;
        } catch (Exception e) {
            //should never enter here print stack trace if so
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Check if the screenshot URL and title are correct
     * TODO:: Update this new version popup
     */

    public boolean screenshotOk(String currentURL) {
        try {
            String screenshotURL = driver.findElement(By.xpath("//a[@id=\"a_screenshot\"]")).getAttribute("href");
            String screenshotTitle = driver.findElement(By.xpath("//a[@id=\"a_screenshot\"]"))
                    .getAttribute("title");

            String expectedscreenshotURL = baseScreenshotURL
                    + encodeURIComponent(serverName + "noFrame/replay/" + currentURL);
            String expectedscreenshotTitle = prop.getProperty("screenTitle");

            if (screenshotURL.equals(expectedscreenshotURL) && screenshotTitle.equals(expectedscreenshotTitle)) {
                //if URL and Title are ok lets download the screenshot
                downloadFileFromURLUsingNIO("./screenshot.png", screenshotURL);
                if (checkFileSize("./screenshot.png"))
                    return true;
                return false;
            } else {
                System.out.println("Found this Screenshot URL: " + screenshotURL);
                System.out.println("Expected this Screenshot URL: " + expectedscreenshotURL);
                System.out.println("Found this Screenshot Title: " + screenshotTitle);
                System.out.println("Expected this Screenshot Title: " + expectedscreenshotTitle);
                return false;
            }

        } catch (NoSuchElementException e) {
            System.out.println("Could not find the screenshot element");
            return false;
        } catch (Exception e) {
            System.out.println("Some problem downloading the screenshot");
            return false;
        }
    }

    /**
     * Check if the print href and title are correct
     * TODO:: Update this new version popup
     */
    public boolean printOk(String currentURL) {
        try {
            String printHref = driver.findElement(By.xpath("//li[@id=\"printLi\"]/a")).getAttribute("href");
            String printTitle = driver.findElement(By.xpath("//li[@id=\"printLi\"]/a")).getAttribute("title");

            String expectedprintHref = "javascript:getImageToPrint(\""
                    + encodeURIComponent(serverName + "noFrame/replay/" + currentURL) + "\")";
            String expectedprintTitle = prop.getProperty("printTitle");

            if (printHref.equals(expectedprintHref) && printTitle.equals(expectedprintTitle)) {
                return true;
            } else {
                System.out.println("Found this Print Href: " + printHref);
                System.out.println("Expected this Print Href: " + expectedprintHref);
                System.out.println("Found this Print Title: " + printTitle);
                System.out.println("Expected this Print Title: " + expectedprintTitle);
                return false;
            }

        } catch (NoSuchElementException e) {
            System.out.println("Could not find the print element");
            return false;
        } catch (Exception e) {
            System.out.println("Should not have reached here");
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Check if the Facebook class and title are correct
     */
    public boolean facebookOk(String currentURL) {
        try {
            //*[@id="facebook_share"]/a
            String faceClass = driver.findElement(By.xpath("//li[@class=\"facebook\"]/a")).getAttribute("class");
            String faceTitle = driver.findElement(By.xpath("//li[@class=\"facebook\"]/a")).getAttribute("title");

            String expectedfaceClass = "addthis_button_facebook";
            String expectedfaceTitle = prop.getProperty("faceTitle");

            if (faceClass.startsWith(expectedfaceClass) && faceTitle.equals(expectedfaceTitle)) {
                return true;
            } else {
                System.out.println("Found this Facebook class: " + faceClass);
                System.out.println("Expected this Facebook Class: " + expectedfaceClass);
                System.out.println("Found this Facebook Title: " + faceTitle);
                System.out.println("Expected this Facebook Title: " + expectedfaceTitle);
                return false;
            }

        } catch (NoSuchElementException e) {
            System.out.println("Could not find the facebook element");
            return false;
        } catch (Exception e) {
            System.out.println("Should not have reached here");
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Check if the Facebook class and title are correct
     */
    public boolean twitterOk(String currentURL) {
        try {
            String twitterClass = driver.findElement(By.xpath("//li[@class=\"twitter\"]/a")).getAttribute("class");
            String twitterTitle = driver.findElement(By.xpath("//li[@class=\"twitter\"]/a")).getAttribute("title");

            String expectedtwitterClass = "addthis_button_twitter";
            String expectedtwitterTitle = prop.getProperty("twitterTitle");

            if (twitterClass.contains(expectedtwitterClass) && twitterTitle.equals(expectedtwitterTitle)) {
                return true;
            } else {
                System.out.println("Found this Twitter class: " + twitterClass);
                System.out.println("Expected this Twitter Class: " + expectedtwitterClass);
                System.out.println("Found this Twitter Title: " + twitterTitle);
                System.out.println("Expected this Twitter Title: " + expectedtwitterTitle);
                return false;
            }

        } catch (NoSuchElementException e) {
            System.out.println("Could not find the twitter element");
            return false;
        } catch (Exception e) {
            System.out.println("Should not have reached here");
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Check Left Menu that contains the list of versions for the URL
     */
    public boolean checkLeftMenu(String currentURL) {
        try {
            String urlNoDate = currentURL.substring(15);
            String timestamp = currentURL.substring(0, 14); //complete timestamp in the 14 digit format
            String year = currentURL.substring(0, 4);
            String monthstr = currentURL.substring(4, 6);
            int monthInt = Integer.parseInt(monthstr);
            String[] months = prop.getProperty("months").split("#");
            String monthStr = months[(monthInt - 1)];
            String daystr = currentURL.substring(6, 8);
            int day = Integer.parseInt(daystr);
            String hours = currentURL.substring(8, 10);
            String minutes = currentURL.substring(10, 12);

            /*System.out.println("Year: " + year);
            System.out.println("Month: " + monthStr);
            System.out.println("Day: " + day);
            System.out.println("Hours: " + hours);
            System.out.println("Minutes: " + minutes); */

            // Check the year in the left menu and check if it is open
            String yearClass = driver.findElement(By.xpath("//a[@id=\"" + year + "\"]")).getAttribute("class");
            String yearText = driver.findElement(By.xpath("//a[@id=\"" + year + "\"]")).getText();
            // Check the month in the left menu and check if it is open
            String monthClass = driver.findElement(By.xpath("//a[@id=\"" + year + "_" + monthstr + "\"]"))
                    .getAttribute("class");
            String monthText = driver.findElement(By.xpath("//a[@id=\"" + year + "_" + monthstr + "\"]")).getText();
            // Check the day in the left menu and check if it is open
            String dayClass = driver.findElement(By.xpath("//a[@id=\"" + year + "_" + monthstr + "_" + day + "\"]"))
                    .getAttribute("class");
            String dayText = driver.findElement(By.xpath("//a[@id=\"" + year + "_" + monthstr + "_" + day + "\"]"))
                    .getText();
            //Check the time of the day Text and if the link is correct
            String hoursMinutesURL = driver.findElement(By.xpath("//a[@id=\"a_" + timestamp + "\"]"))
                    .getAttribute("onclick");
            hoursMinutesURL = hoursMinutesURL.replaceAll(",%20", ", ");
            String hoursClass = driver.findElement(By.xpath("//a[@id=\"a_" + timestamp + "\"]"))
                    .getAttribute("class");
            String hoursMinutesText = driver.findElement(By.xpath("//a[@id=\"a_" + timestamp + "\"]")).getText();
            String expectedURL = "jumpToVersion('" + urlNoDate + "', '" + timestamp + "')";

            if (yearText.equals(year) && yearClass.equals(activeItem) && monthText.equals(monthStr)
                    && monthClass.equals(activeItem) && dayText.equals("" + day) && dayClass.equals(activeItem)
                    && hoursMinutesText.equals(hours + ":" + minutes) && hoursClass.equals(activeDay)
                    && hoursMinutesURL.contains(expectedURL)) {
                return true;
            } else {
                System.out.println("Found this Year text: " + yearText);
                System.out.println("Expected this Year text: " + year);
                System.out.println("The year has the class " + yearClass);
                System.out.println("Expected year class=" + activeItem);
                System.out.println("Found this Month text: " + monthText);
                System.out.println("Expected this Month text: " + monthStr);
                System.out.println("Found this Month class: " + monthClass);
                System.out.println("Expected Month class: " + activeItem);
                System.out.println("Found this day Text: " + dayText);
                System.out.println("Expected this day Text: " + day);
                System.out.println("Found this hours and minutes Text: " + hoursMinutesText);
                System.out.println("Expected this hours and minutes Text: " + hours + ":" + minutes);
                System.out.println("Found this versionURL: " + hoursMinutesURL);
                System.out.println("Expected this versionURL: " + expectedURL);

                return false;
            }

        } catch (NoSuchElementException e) {
            System.out.println(
                    "Could not find at least one of the following elements: year, year_monthstr, year_monthstr_daystr, or a_timestamp");
            return false;
        } catch (Exception e) {
            System.out.println("Should not have reached here");
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Check if the Email title, onclick and href are correct
     */
    public boolean emailOk(String currentURL) {
        try {
            String emailHref = driver.findElement(By.xpath("//li[@id=\"liEmail\"]/a")).getAttribute("href");
            emailHref = URLDecoder.decode(emailHref, "UTF-8");

            String emailonClick = driver.findElement(By.xpath("//li[@id=\"liEmail\"]/a")).getAttribute("onclick");
            String emailTitle = driver.findElement(By.xpath("//li[@id=\"liEmail\"]/a")).getAttribute("title");

            String expectedemailHref = "mailto:?subject=" + prop.getProperty("emailMessage") + "[sub]";
            String currentTimestamp = currentURL.substring(0, 14);
            String dateFormatted = getDateFormatted(currentTimestamp);
            System.out.println("Expected Date: " + dateFormatted);
            String expectedemailonClick = "this.href = this.href.replace(\'[sub]\',document.title + \'%0D%0A"
                    + dateFormatted + "%0D%0A %0D%0A' + window.location )";

            String expectedemailTitle = prop.getProperty("mailTitle");

            if (emailHref.toLowerCase().equals(expectedemailHref.toLowerCase())
                    && emailonClick.toLowerCase().contains(expectedemailonClick.toLowerCase())
                    && emailTitle.toLowerCase().equals(expectedemailTitle.toLowerCase())) {
                return true;
            } else {
                System.out.println("Found this Email href: " + emailHref);
                System.out.println("Expected this Email href: " + expectedemailHref);
                System.out.println("Found this Email onclick: " + emailonClick);
                System.out.println("Expected containing this Email onclick: " + expectedemailonClick);
                System.out.println("Found this Email title: " + emailTitle);
                System.out.println("Expected this Email title: " + expectedemailTitle);
                return false;
            }

        } catch (NoSuchElementException e) {
            System.out.println("Could not find email anchor");
            return false;
        } catch (Exception e) {
            System.out.println("Should not have reached here");
            e.printStackTrace();
            return false;
        }
    }

    /**
     * input: timestamp in the 14 digit format such as 20000823154833
     * output: 23 Agosto, 2000
    */
    public String getDateFormatted(String timestamp) {
        String[] months = prop.getProperty("months").split("#");

        String year = timestamp.substring(0, 4);
        String month = timestamp.substring(4, 6);
        int monthInt = Integer.parseInt(month);
        String monthStr = months[(monthInt - 1)];

        String day = timestamp.substring(6, 8);
        if (day.charAt(0) == '0') {
            day = day.substring(1, 2);
        }
        return day + " " + monthStr + ", " + year;
    }

    /**
     * Check if the Table of Versions title and href are correct
     */
    public boolean tableOfVersionsOk(String currentURL) {
        try {
            String tableOfVersionsHref = driver.findElement(By.xpath("//a[@id=\"versionsTable\"]"))
                    .getAttribute("href");
            String tableOfVersionsTitle = driver.findElement(By.xpath("//a[@id=\"versionsTable\"]"))
                    .getAttribute("title");

            String urlNoDate = currentURL.substring(15);

            String expectedtableOfVersionsHref = serverName + "search.jsp?l=" + prop.getProperty("lang") + "&query="
                    + urlNoDate + "&btnSubmit=Pesquisar";
            String expectedtableOfVersionsTitle = prop.getProperty("versionsStoredTable");

            if (expectedtableOfVersionsHref.equals(tableOfVersionsHref)
                    && expectedtableOfVersionsTitle.equals(tableOfVersionsTitle)) {
                return true;
            } else {
                System.out.println("Found this Table of Versions href:     " + tableOfVersionsHref);
                System.out.println("Expected this Table of Versions href:  " + expectedtableOfVersionsHref);
                System.out.println("Found this Table of Versions title:    " + tableOfVersionsTitle);
                System.out.println("Expected this Table of Versions title: " + expectedtableOfVersionsTitle);
                return false;
            }

        } catch (NoSuchElementException e) {
            System.out.println("Could not find the Table of Versions Anchor");
            return false;
        } catch (Exception e) {
            System.out.println("Should not have reached here");
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Check if the Logo exists and href is correct
     */
    public boolean logoOk(String currentURL) {
        try {

            String anchorHref = driver.findElement(By.xpath("//div[@id=\"logodiv\"]/a[2]")).getAttribute("href");
            String logoAlt = driver.findElement(By.xpath("//div[@id=\"logodiv\"]/a[2]/img")).getAttribute("alt");
            String logoSrc = driver.findElement(By.xpath("//div[@id=\"logodiv\"]/a[2]/img")).getAttribute("src");

            String expectedanchorHref = serverName + "?l=" + prop.getProperty("lang");
            String optionalExpectedanchorHref = serverName.substring(0, serverName.length() - 1) + "?l="
                    + prop.getProperty("lang");
            String expectedlogoAlt = "Arquivo.pt logo";
            String expectedlogoSrc = serverName + "wayback/static/resources/img/arquivo100v.png";

            if ((expectedanchorHref.equals(anchorHref) || optionalExpectedanchorHref.equals(anchorHref))
                    && expectedlogoAlt.equals(logoAlt) && expectedlogoSrc.equals(logoSrc)) {
                return true;
            } else {
                System.out.println("Found this Anchor href: " + anchorHref);
                System.out.println("Expected this Anchor href: " + expectedanchorHref);
                System.out.println("Found this Logo Alt: " + logoAlt);
                System.out.println("Expected this Logo Alt: " + expectedlogoAlt);
                System.out.println("Found This Logo Src: " + logoSrc);
                System.out.println("Expected this Logo Src: " + expectedlogoSrc);
                return false;
            }

        } catch (NoSuchElementException e) {
            System.out.println("Could not find the Logo Anchor or the Logo Image");
            return false;
        } catch (Exception e) {
            System.out.println("Should not have reached here");
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Jump to the current URL
     * wait some time to load the Webpage
     */
    public void goToCurrentURL(String currentURL, String expectedTitle) throws Exception {
        driver.get(serverName + "wayback/" + currentURL);
        if (!(new WebDriverWait(driver, 180)) /* Wait Up to 180 seconds for page to load*/
                .until(ExpectedConditions.titleContains(expectedTitle))) {
            throw new Exception("Failed loading current URL: " + currentURL);
        }
    }

    public String truncateURL(String url) {
        if (url.startsWith("https://")) {
            url = url.substring(8, url.length());
        } else if (url.startsWith("http://")) {
            url = url.substring(7, url.length());
        }
        if (url.length() > 40) {
            return url.substring(0, 26) + "..." + url.substring((url.length() - 11), url.length());
        } else
            return url;
    }

    /**
    * Changes the Language of the Page, to the value given in lang string (PT or EN)
    */
    public void switchLanguage(String lang) {
        System.out.println("Switch language function");
        if (driver.findElement(By.xpath("//a[@id=\"changeLanguage\"]")).getText().equals(lang)) {
            System.out.println("Switching language");
            driver.findElement(By.id("changeLanguage")).click(); //change the language
            try {
                Thread.sleep(waitingPeriod); //wait for page to load
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
        } //else You are already in the desired language
    }

    private static void downloadFileFromURLUsingNIO(String fileName, String fileUrl) throws IOException {
        System.out.println("Download Starting");
        URL url = new URL(fileUrl);
        ReadableByteChannel rbc = Channels.newChannel(url.openStream());
        FileOutputStream fOutStream = new FileOutputStream(fileName);
        fOutStream.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
        fOutStream.close();
        rbc.close();
        System.out.println("Download Successful");
    }

    private static boolean checkFileSize(String pathToFile) {

        File file = new File(pathToFile);

        if (file.exists()) {

            double bytes = file.length();
            double kilobytes = (bytes / 1024);

            System.out.println("kilobytes : " + kilobytes);
            if (kilobytes > 200)
                return true;
            else {
                System.out.println("File too small to Be Ok Less or Equal to 200Kb");
                return false;
            }

        } else {
            System.out.println("File does not exist!");
            return false;
        }

    }

    /**
     * Encodes the passed String as UTF-8 using an algorithm that's compatible
     * with JavaScript's <code>encodeURIComponent</code> function. Returns
     * <code>null</code> if the String is <code>null</code>.
     *
     * @param s The String to be encoded
     * @return the encoded String
     */
    public static String encodeURIComponent(String s) {
        String result = null;

        try {
            result = URLEncoder.encode(s, "UTF-8").replaceAll("\\+", "%20").replaceAll("\\%21", "!")
                    .replaceAll("\\%27", "'").replaceAll("\\%28", "(").replaceAll("\\%29", ")")
                    .replaceAll("\\%7E", "~");
        }

        // This exception should never occur.
        catch (UnsupportedEncodingException e) {
            result = s;
        }

        return result;
    }

}