// Copyright 2012 Georg-August-Universität Göttingen, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package de.ugoe.cs.autoquest.plugin.html;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.xml.sax.SAXException;
import de.ugoe.cs.autoquest.eventcore.Event;
import de.ugoe.cs.autoquest.eventcore.IEventType;
import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModelException;
import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
import de.ugoe.cs.autoquest.plugin.html.eventcore.HTMLEventTypeFactory;
import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLDocumentSpec;
import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLGUIElement;
import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLGUIElementSpec;
import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLPageElement;
import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLPageElementSpec;
import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLServerSpec;
/**
*
* This class provides the functionality to parse XML log files generated by the HTMLMonitor of
* AutoQUEST. The result of parsing a file is a collection of event sequences and a GUI model.
*
*
* The parser can be configured with parsing parameters to ignore, e.g., ids or indexes of
* parsed GUI elements. Details can be found in the manual pages of the respective parsing commands.
*
*
* @author Fabian Glaser, Patrick Harms
* @version 1.0
*
*/
public class HTMLLogParser extends AbstractDefaultLogParser {
/**
*
* the pattern used for parsing HTML GUI element paths
*
* a map containing replacement specifications for ids of GUI elements
*
*/
private Map idReplacements;
/**
*
* initializes the parser with the parsing parameters to be considered
*
*
* @param parseParams the parsing parameters to be considered
*/
public HTMLLogParser(Map> parseParams) {
this.parseParams = parseParams;
for (String paramKey : parseParams.keySet()) {
if (!"clearId".equals(paramKey) && !"clearIndex".equals(paramKey) &&
!"idReplacements".equals(paramKey))
{
throw new IllegalArgumentException("unknown parse parameter key " + paramKey);
}
}
}
/* (non-Javadoc)
* @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#handleGUIElement(String, Map)
*/
@Override
protected boolean handleGUIElement(String id, Map parameters)
throws SAXException
{
HTMLGUIElementSpec specification = null;
String parentId = parameters.get("parent");
HTMLGUIElement parent = (HTMLGUIElement) super.getGUIElementTree().find(parentId);
if (parameters.containsKey("host")) {
// this is a server specification
int port = 80;
String portStr = parameters.get("port");
if (portStr != null) {
port = Integer.parseInt(portStr);
}
specification = new HTMLServerSpec(parameters.get("host"), port);
}
else if (parameters.containsKey("path")) {
// this is a document specification
if (parent != null) {
if (!(parent.getSpecification() instanceof HTMLServerSpec)) {
throw new SAXException
("invalid log: parent GUI element of a document is not of type server");
}
specification = new HTMLDocumentSpec
((HTMLServerSpec) parent.getSpecification(), parameters.get("path"),
parameters.get("query"), parameters.get("title"));
}
else if (parentId == null) {
throw new SAXException("invalid log: a document has no parent id");
}
}
else if (parameters.containsKey("tagname")) {
String tagName = parameters.get("tagname");
if (!tagNameMustBeConsidered(tagName)) {
return true;
}
if (parent != null) {
if (!childrenMustBeConsidered(parent)) {
return true;
}
IGUIElement document = parent;
while ((document != null) &&
(!(document.getSpecification() instanceof HTMLDocumentSpec)))
{
document = document.getParent();
}
if (document == null) {
throw new SAXException
("invalid log: parent hierarchy of a page element does not contain a " +
"document");
}
int index = -1;
String indexStr = parameters.get("index");
if ((indexStr != null) && (!"".equals(indexStr))) {
index = Integer.parseInt(indexStr);
}
String htmlId = parameters.get("htmlid");
if (clearIndex(tagName, index, htmlId, parent)) {
index = -1;
}
String idReplacement = replaceHTMLId(tagName, index, htmlId, parent);
if (idReplacement != null) {
htmlId = idReplacement;
}
else if (clearHTMLId(tagName, index, htmlId, parent)) {
htmlId = null;
}
if ((htmlId == null) && (index == -1)) {
// set at least a default index, if all is to be ignored.
index = 0;
}
specification = new HTMLPageElementSpec
((HTMLDocumentSpec) document.getSpecification(),
tagName.intern(), htmlId == null ? null : htmlId.intern(), index);
}
else if (parentId == null) {
throw new SAXException("invalid log: a page element has no parent id");
}
}
else {
throw new SAXException("invalid log: unknown GUI element");
}
if (specification != null) {
try {
super.getGUIElementTree().add(id, parentId, specification);
}
catch (GUIModelException e) {
throw new SAXException("could not handle GUI element with id " +
id + ": " + e.getMessage(), e);
}
return true;
}
else {
return false;
}
}
/**
*
* checks if for a specific GUI element the index shall be ignored or not by considering the
* parsing parameters.
*
*
* @param tagName the tag of the considered GUI element
* @param index the index of the GUI element
* @param id the id of the GUI element
* @param parent the parent GUI element of the considered GUI element
*
* @return true if the index shall be ignored, false else.
*/
private boolean clearIndex(String tagName, int index, String id, HTMLGUIElement parent) {
return clearSomething("clearIndex", tagName, index, id, parent);
}
/**
*
* checks if the parsing parameters define a replacement for the id of the given GUI element
* and if so returns this replacement
*
*
* @param tagName the tag of the considered GUI element
* @param index the index of the GUI element
* @param id the id of the GUI element
* @param parent the parent GUI element of the considered GUI element
*
* @return the identified replacement
*/
private String replaceHTMLId(String tagName, int index, String htmlId, HTMLGUIElement parent)
throws SAXException
{
if ((idReplacements == null) && (parseParams.containsKey("idReplacements"))) {
idReplacements = new HashMap();
for (String fileName : parseParams.get("idReplacements")) {
Properties props = new Properties();
FileInputStream stream = null;
try {
stream = new FileInputStream(new File(fileName));
props.load(stream);
}
catch (FileNotFoundException e) {
throw new SAXException("could not find file " + fileName, e);
}
catch (IOException e) {
throw new SAXException("error reading file " + fileName, e);
}
finally {
if (stream != null) {
try {
stream.close();
}
catch (IOException e) {
// ignore
}
}
}
for (Map.Entry