package org.test4j.spec.scenario.xmlparser;
import java.io.Serializable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import org.dom4j.CharacterData;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.test4j.spec.inner.StepType;
import org.test4j.spec.scenario.step.XmlJSpecStep;
import org.test4j.spec.scenario.step.xml.MethodDescription;
import org.test4j.spec.scenario.xmlparser.entity.ScenarioList;
import org.test4j.spec.scenario.xmlparser.entity.ScenarioMethod;
import org.test4j.spec.scenario.xmlparser.entity.StoryDescription;
import org.test4j.spec.scenario.xmlparser.entity.StoryScenario;
import org.test4j.spec.scenario.xmlparser.entity.TemplateList;
import org.test4j.spec.scenario.xmlparser.entity.TemplateMethod;
import org.test4j.tools.commons.StringHelper;
/**
* 将一个xml的document对象转换为StoryFeature对象
*
* @author darui.wudr 2012-7-17 下午4:48:57
*/
@SuppressWarnings({ "unchecked", "serial" })
public class StoryFeatureXMLBuilder implements Serializable {
private final Document document;
public StoryFeatureXMLBuilder(Document document) {
this.document = document;
}
/**
* 根据xml对象构造StoryFeature对象
*
* @param document
* @return
*/
public StoryFeature build() {
StoryFeature story = new StoryFeature();
StoryDescription description = this.buildStoryDescription();
story.setDescription(description);
TemplateList templates = this.buildStoryTemplates();
story.setTemplates(templates);
ScenarioList scenarios = this.buildStoryScenarios(templates);
story.setScenarios(scenarios);
return story;
}
/**
* 更新用例文档的描述
*
* @param document
* @param description
*/
public static void setStoryDescription(Document document, String description) {
Element node = (Element) document.selectSingleNode(StoryQName.xpathDescription);
if (node == null) {
node = document.getRootElement().addElement(StoryQName.nodeDescription);
}
node.setText(description);
}
/**
* 返回用例文档的描述
*
* @param document
* @return
*/
public static String getStoryDescription(Document document) {
Element node = (Element) document.selectSingleNode(StoryQName.xpathDescription);
return node == null ? "" : node.getText();
}
/**
* 构建用例场景列表
*
* @return
*/
ScenarioList buildStoryScenarios(TemplateList templates) {
ScenarioList list = new ScenarioList();
List<Element> nodes = this.document.selectNodes(StoryQName.xpathScenario);
if (nodes == null) {
return list;
}
int index = 0;
for (Element node : nodes) {
StoryScenario scenario = buildStoryScenario(node, ++index, templates);
list.addScenario(scenario);
}
return list;
}
/**
* 构建场景
*
* @param node
* @return
*/
private StoryScenario buildStoryScenario(Element node, int scenarioIndex, TemplateList templates) {
String name = node.attributeValue(StoryQName.attrName, "");
String skip = node.attributeValue(StoryQName.attrSkip, "false");
StoryScenario scenario = new StoryScenario(name, skip);
Element descNode = (Element) node.selectSingleNode(StoryQName.nodeDescription);
String description = descNode == null ? "" : descNode.getText();
scenario.setDescription(description);
scenario.setPathID(scenarioIndex);
List<Element> methods = node.selectNodes(StoryQName.nodeScenarioMethod);
int methodIndex = 0;
for (Element mnode : methods) {
ScenarioMethod method = buildScenarioMethod(mnode, templates);
method.setPathID(scenarioIndex, ++methodIndex);
scenario.addMethod(method);
}
return scenario;
}
/**
* 构建场景
*
* @param node
* @return
*/
private static StoryScenario buildStoryScenario(Element node) {
String name = node.attributeValue(StoryQName.attrName, "");
String skip = node.attributeValue(StoryQName.attrSkip, "false");
StoryScenario scenario = new StoryScenario(name, skip);
Element descNode = (Element) node.selectSingleNode(StoryQName.nodeDescription);
String description = descNode == null ? "" : descNode.getText();
scenario.setDescription(description);
return scenario;
}
/**
* 构建场景步骤
*
* @param mnode
* @return
*/
private static ScenarioMethod buildScenarioMethod(Element node, TemplateList templates) {
String name = node.attributeValue(StoryQName.attrName, "");
String type = node.attributeValue(StoryQName.attrType, "given").toLowerCase();
String skip = node.attributeValue(StoryQName.attrSkip, "false").toLowerCase();
ScenarioMethod method = new ScenarioMethod(name, type, skip);
String template = XmlJSpecStep.getTemplateText(name, node);
LinkedHashMap<String, String> paras = new LinkedHashMap<String, String>();
boolean useTemplate = false;
if (templates != null) {
TemplateMethod templateMethod = templates.findTemplate(name, type);
if (templateMethod != null) {
useTemplate = true;
template = templateMethod.getTemplateText();
paras = templateMethod.getParas();
}
}
method.setParas(paras);
method.setTemplateText(template);
parseParameter(paras, useTemplate, node);
String displayText = new MethodDescription(template, paras).getMethodDisplayText();
method.setDisplayText(displayText);
String initialText = StoryFeatureXMLBuilder.buildElementText(node);
method.setInitialText(initialText);
return method;
}
/**
* 构建用例模板列表
*
* @return
*/
private TemplateList buildStoryTemplates() {
TemplateList list = new TemplateList();
List<Element> nodes = this.document.selectNodes(StoryQName.xpathTemplateMethod);
if (nodes == null) {
return list;
}
int index = 0;
for (Element node : nodes) {
TemplateMethod method = buildTemplateMethod(node);
method.setPathID(++index);
list.add(method);
}
return list;
}
/**
* 构建用例步骤模板
*
* @param node
* @return
*/
private static TemplateMethod buildTemplateMethod(Element node) {
String name = node.attributeValue(StoryQName.attrName, "");
String type = node.attributeValue(StoryQName.attrType, "given");
TemplateMethod method = new TemplateMethod(name, type);
String template = XmlJSpecStep.getTemplateText(name, node);
method.setTemplateText(template);
LinkedHashMap<String, String> paras = new LinkedHashMap<String, String>();
parseParameter(paras, false, node);
method.setParas(paras);
String displayText = new MethodDescription(template, paras).getMethodDisplayText();
method.setDisplayText(displayText);
String initialText = StoryFeatureXMLBuilder.buildElementText(node);
method.setInitialText(initialText);
return method;
}
/**
* 构建用例文件描述
*
* @param html
*/
StoryDescription buildStoryDescription() {
StoryDescription description = new StoryDescription();
Element desc = (Element) document.selectSingleNode(StoryQName.xpathDescription);
description.setDescription(desc.getText());
return description;
}
/**
* 构造节点中的文本内容+子节点内容
*
* @param method
* @return
*/
@SuppressWarnings("rawtypes")
public static String buildElementText(Element method) {
StringBuffer buff = new StringBuffer();
for (Iterator it = method.nodeIterator(); it.hasNext();) {
Node item = (Node) it.next();
if (item instanceof CharacterData) {
String text = ((CharacterData) item).getText();
buff.append(text == null ? "" : text.trim());
} else {
String xml = ((Node) item).asXML();
buff.append("\n").append(xml == null ? "" : xml.trim());
}
}
return buff.toString();
}
/**
* 解析方法的参数列表
*
* @param element
*/
static void parseParameter(LinkedHashMap<String, String> paras, boolean useTemplate, Element element) {
List<Element> paraNodes = element.selectNodes("para");
for (Element paraNode : paraNodes) {
String name = paraNode.attributeValue("name");
if (StringHelper.isEmpty(name)) {
String error = String.format("the parameter name of method can't be empty!");
throw new RuntimeException(error);
}
if (useTemplate && !paras.containsKey(name)) {
String error = String.format("the template doesn't contain parameter[%s].", name);
throw new RuntimeException(error);
}
if (!useTemplate && paras.containsKey(name)) {
String error = String.format("the method have duplicated parameter[%s].", name);
throw new RuntimeException(error);
}
String json = paraNode.getText().trim();
paras.put(name, json);
}
}
/**
* 根据document和方法的xpathID构造ScenarioMethod对象(方法的原始内容)
*
* @param document
* @param xpathID
* @return
*/
public static ScenarioMethod getScenarioMethodInitialContent(Document document, String xpathID) {
ScenarioMethod method = new ScenarioMethod("not found method by pathID", StepType.Given);
if (!xpathID.startsWith(StoryQName.xpathScenario) || !xpathID.contains(StoryQName.nodeScenarioMethod)) {
return method;
}
Element element = (Element) document.selectSingleNode(xpathID);
if (element == null) {
return method;
}
return buildScenarioMethod(element, null);
}
/**
* 根据document和方法的xpathID构造TemplateMethod对象
*
* @param document2
* @param xpathID
* @return
*/
public static TemplateMethod getTemplateMethod(Document document, String xpathID) {
TemplateMethod method = new TemplateMethod("not found method by pathID", StepType.Given);
if (!xpathID.startsWith(StoryQName.xpathTemplateMethod)) {
return method;
}
Element element = (Element) document.selectSingleNode(xpathID);
if (element == null) {
return method;
}
return buildTemplateMethod(element);
}
/**
* 根据XPathID获取用例场景实例
*
* @param document
* @param xpathID
* @return
*/
public static StoryScenario getStoryScenario(Document document, String xpathID) {
StoryScenario scenario = new StoryScenario("");
if (!xpathID.startsWith(StoryQName.xpathScenario) || xpathID.contains(StoryQName.nodeScenarioMethod)) {
return scenario;
}
Element element = (Element) document.selectSingleNode(xpathID);
if (element == null) {
return scenario;
}
return buildStoryScenario(element);
}
}