package nl.hsac.fitnesse.fixture.util.selenium.by;

import org.openqa.selenium.By;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebElement;

import java.util.List;

/**
 * Own subclass of By which supports placeholders in its pattern.
 * The pattern will only be expanded on first usage.
 */
public abstract class LazyPatternBy extends By {
    private final String pattern;
    private final String[] parameters;
    private By nested;

    /**
     * Creates By based on xPath, supporting placeholder replacement.
     * It also supports the fictional 'normalized()' function that does whitespace normalization, that also
     * considers a '&nbsp;' whitespace.
     * @param pattern basic XPATH, possibly with placeholders.
     * @param parameters values for placeholders.
     * @return ByXPath.
     */
    public LazyPatternBy(String pattern, String... parameters) {
        this.pattern = pattern;
        this.parameters = parameters;
    }

    @Override
    public List<WebElement> findElements(SearchContext context) {
        return getNested().findElements(context);
    }

    @Override
    public String toString() {
        return getNested().toString();
    }

    @Override
    public int hashCode() {
        return getNested().hashCode();
    }

    @Override
    public boolean equals(Object o) {
        if (o == null) {
            return false;
        } else {
            By nested = getNested();
            if (o instanceof LazyPatternBy) {
                return nested.equals(((LazyPatternBy) o).getNested());
            } else {
                return nested.equals(o);
            }
        }
    }

    private final synchronized By getNested() {
        if (nested == null) {
            String expr = createExpression(pattern, parameters);
            nested = createNested(expr);
        }
        return nested;
    }

    protected abstract By createNested(String expr);

    protected String createExpression(String pattern, String... parameters) {
        return fillPattern(pattern, parameters);
    }

    /**
     * Fills in placeholders in pattern using the supplied parameters.
     * @param pattern pattern to fill (in String.format style).
     * @param parameters parameters to use.
     * @return filled in pattern.
     */
    private String fillPattern(String pattern, String[] parameters) {
        boolean containsSingleQuote = false;
        boolean containsDoubleQuote = false;
        Object[] escapedParams = new Object[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
            String param = parameters[i];
            containsSingleQuote = containsSingleQuote || param.contains("'");
            containsDoubleQuote = containsDoubleQuote || param.contains("\"");
            escapedParams[i] = param;
        }
        if (containsDoubleQuote && containsSingleQuote) {
            throw new RuntimeException("Unsupported combination of single and double quotes");
        }
        String patternToUse;
        if (containsSingleQuote) {
            patternToUse = pattern.replace("'", "\"");
        } else {
            patternToUse = pattern;
        }
        return String.format(patternToUse, escapedParams);
    }
}

@@ -180,16 +180,48 @@
 il.List;
+%0Aimport java.util.regex.Pattern;
 %0A%0A/**%0A *
@@ -390,16 +390,94 @@
 ds By %7B%0A
+    private final static Pattern SINGLE_QUOTE_PATTERN = Pattern.compile(%22'%22);%0A
     priv
@@ -2129,47 +2129,194 @@
     
-return fillPattern(pattern, parameters)
+String result;%0A        if (parameters.length == 0) %7B%0A            result = pattern;%0A        %7D else %7B%0A            result = fillPattern(pattern, parameters);%0A        %7D%0A        return result
 ;%0A  
@@ -3332,23 +3332,53 @@
 e = 
+SINGLE_QUOTE_PATTERN.matcher(
 pattern
+)
 .replace
 (%22'%22
@@ -3377,14 +3377,12 @@
 lace
-(%22'%22, 
+All(
 %22%5C%22%22

Optimize creating LazyPatternBy instances