package com.xceptance.xlt.common.util.action.validation;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import com.gargoylesoftware.htmlunit.StringWebResponse;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebResponse;
import com.gargoylesoftware.htmlunit.html.DomNode;
import com.gargoylesoftware.htmlunit.html.HTMLParser;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.xceptance.xlt.api.util.XltLogger;
import com.xceptance.xlt.common.util.ParameterUtils;
/**
* <p>
* Implementation of {@link XPathGetable}. <br>
* The class is intended to offer ways to select elements by passing a xpath for {@link WebReponse} objects, whose body
* can be parsed into a {@link HtmlPage}. <br>
* </p>
* <ul>
* <li>Parses WebResponse only if the mime-type is supported (see {@link #HEADERCONTENTTYPES supported types}).
* <li>Use {@link #isWebResponseParseable(WebResponse)} to see if a response is parsable.
* <li>Parses automatically and only when needed via {@link #getByXPath(String)}.
* </ul>
*
* @author matthias mitterreiter
*/
public class XPathWithParseableWebResponse implements XPathGetable
{
private HtmlPage htmlPage;
private WebResponse webResponse;
/**
* Contains all the supported mime types. Supported mime types so far:
* <ul>
* <li>Html:
* <ul>
* <li>text/html
* <li>text/application
* </ul>
* </ul>
*/
static final HashMap<String, String> HEADERCONTENTTYPES = new HashMap<String, String>();
static final String HTML = "html";
static final String TEXTHTML = "text/html";
static final String TEXTAPPLICATION = "text/application";
static
{
HEADERCONTENTTYPES.put(TEXTHTML, HTML);
HEADERCONTENTTYPES.put(TEXTAPPLICATION, HTML);
}
/**
* @param webResponse
* the WebResponse whose body should be parsed.
*/
public XPathWithParseableWebResponse(final WebResponse webResponse)
{
XltLogger.runTimeLogger.debug("Creating new Instance");
setWebResponse(webResponse);
}
private void setWebResponse(final WebResponse webResponse)
{
ParameterUtils.isNotNull(webResponse, "WebResponse");
if (isWebResponseParseable(webResponse))
{
this.webResponse = webResponse;
}
else
{
throw new IllegalArgumentException("WebResponse of Type: '" + getContentTypeFromResponse(webResponse) + "' is not parseable!");
}
}
/**
* @param webResponse
* @return true only if webResponse is parsable.
*/
public static boolean isWebResponseParseable(final WebResponse webResponse)
{
final String contentType = webResponse.getContentType();
return HEADERCONTENTTYPES.containsKey(contentType);
}
private String getContentTypeFromResponse(final WebResponse webResponse)
{
return webResponse.getContentType();
}
/**
* Parses the {@link WebResponse} in a {@link HtmlPage} only if necessary and only once. <br>
* The elements get parsed to String via {@link DomNode}.getTextContent(); <br>
* It depends onto the type of node, what comes back.
*/
@Override
public List<String> getByXPath(final String xPath)
{
loadDataFromWebResponseIfNecessary();
final List<DomNode> htmlElements = getHtmlElementListByXPath(xPath);
final List<String> resultList = getStringListFromHtmlElementList(htmlElements);
return resultList;
}
private List<String> getStringListFromHtmlElementList(final List<DomNode> htmlNodes)
{
final List<String> resultList = new ArrayList<String>();
for (final DomNode node : htmlNodes)
{
final String elementAsString = getStringFromHtmlElement(node);
XltLogger.runTimeLogger.debug("Found Element: " + elementAsString);
resultList.add(elementAsString);
}
return resultList;
}
private String getStringFromHtmlElement(final DomNode node)
{
try
{
final String elementAsString = node.getTextContent();
return elementAsString;
}
catch (final Exception e)
{
throw new IllegalArgumentException("Failed to parse DomNode: " + node.getLocalName() + " of type: " + node.getNodeType() +
" to String because: " + e.getMessage());
}
}
private void loadDataFromWebResponseIfNecessary()
{
if (this.htmlPage == null)
{
createHtmlPageFromWebResponse();
}
}
@SuppressWarnings("unchecked")
private List<DomNode> getHtmlElementListByXPath(final String xPath)
{
XltLogger.runTimeLogger.debug("Getting Elements by XPath: " + xPath);
List<DomNode> htmlElements = Collections.<DomNode>emptyList();
try
{
htmlElements = (List<DomNode>) this.htmlPage.getByXPath(xPath);
if (htmlElements == null)
{
XltLogger.runTimeLogger.debug("No Elements found!, XPath: " + xPath);
htmlElements = Collections.<DomNode>emptyList();
}
}
catch (final Exception e)
{
throw new IllegalArgumentException("Failed to get Elements by XPath: " + xPath + ", Because: " + e.getMessage());
}
return htmlElements;
}
private void createHtmlPageFromWebResponse()
{
XltLogger.runTimeLogger.debug("Creating HtmlPage from WebResponse");
try
{
final URL url = webResponse.getWebRequest().getUrl();
final StringWebResponse response = new StringWebResponse(webResponse.getContentAsString(), url);
this.htmlPage = HTMLParser.parseHtml(response, new WebClient().getCurrentWindow());
}
catch (final Exception e)
{
throw new IllegalArgumentException("Failed to create a HtmlPage from WebResponse, because: " + e.getMessage());
}
}
}