
com.xceptance.xlt.engine.scripting.docgen.ScriptDocGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xlt Show documentation
Show all versions of xlt Show documentation
XLT (Xceptance LoadTest) is an extensive load and performance test tool developed and maintained by Xceptance.
/*
* Copyright (c) 2005-2024 Xceptance Software Technologies GmbH
*
* 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 com.xceptance.xlt.engine.scripting.docgen;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Stack;
import java.util.regex.Pattern;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.xceptance.common.io.FileUtils;
import com.xceptance.common.util.ParameterCheckUtils;
import com.xceptance.common.util.ProductInformation;
import com.xceptance.common.util.RegExUtils;
import com.xceptance.xlt.common.XltConstants;
import com.xceptance.xlt.engine.scripting.MacroProcessor;
import com.xceptance.xlt.engine.scripting.ScriptException;
import com.xceptance.xlt.engine.scripting.TestDataUtils;
import com.xceptance.xlt.engine.util.ScriptingUtils;
import com.xceptance.xlt.report.util.TaskManager;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
/**
* Script Doc Generator.
*
* @author Hartmut Arlt (Xceptance Software Technologies GmbH)
*/
public class ScriptDocGenerator
{
private static final Logger LOG = LoggerFactory.getLogger(ScriptDocGenerator.class);
private static final Pattern VAR_EXPR_PATTERN = Pattern.compile("(? worklist;
private Set calls;
private final ScriptDocGeneratorConfiguration config;
public ScriptDocGenerator(final File testSuiteDirectory, final File outputDirectory, final Properties commandLineProperties)
throws IOException
{
ParameterCheckUtils.isNotNull(testSuiteDirectory, "testSuiteDirectory");
ParameterCheckUtils.isNotNull(outputDirectory, "outputDirectory");
if (!testSuiteDirectory.exists() || !testSuiteDirectory.canRead() || !testSuiteDirectory.isDirectory())
{
throw new IllegalArgumentException("Test suite path '" + testSuiteDirectory.getAbsolutePath() +
"' does not denote an existent and readable directory.");
}
final File scriptsDirectory = new File(testSuiteDirectory, "scripts");
if (!scriptsDirectory.canRead())
{
throw new IllegalArgumentException("Failed to access directory 'scripts' in test suite '" +
testSuiteDirectory.getAbsolutePath() + "'. Please make sure it exists and can be read.");
}
if (outputDirectory.exists())
{
if (!outputDirectory.isDirectory())
{
throw new IllegalArgumentException("Cannot write to '" + outputDirectory.getAbsolutePath() +
"' because it already exists and is not a directory.");
}
if (!outputDirectory.canWrite())
{
throw new IllegalArgumentException("Directory '" + outputDirectory.getAbsolutePath() +
"' already exists and is read-only.");
}
}
outputDir = outputDirectory;
scriptDir = scriptsDirectory;
scriptDirPath = scriptDir.getAbsolutePath();
config = new ScriptDocGeneratorConfiguration(commandLineProperties);
freeMarkerConfig = new Configuration(Configuration.VERSION_2_3_21);
freeMarkerConfig.setDefaultEncoding(XltConstants.UTF8_ENCODING);
freeMarkerConfig.setDirectoryForTemplateLoading(config.getTemplateDir());
freeMarkerConfig.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
}
public void run() throws Exception
{
final File suitePath = scriptDir.getParentFile().getCanonicalFile();
suiteInfo = new TestSuiteInfo(suitePath);
// Process packages first
final PackageInfo pkgInfo = new PackageInfo();
pkgInfo.setTestData(TestDataUtils.getPackageTestData(null, scriptDir.getAbsolutePath(), pkgInfo.name));
suiteInfo.addPackage(pkgInfo);
pkgTree = new PackageTree(pkgInfo);
final Pattern scriptFilePattern = Pattern.compile("^.+(? 0)
{
_addPackageToTree(pkgName);
}
return scriptFilePattern.matcher(name).matches();
}
});
worklist = new LinkedList();
calls = new HashSet();
final ArrayList failures = new ArrayList();
for (final File f : allFiles)
{
try
{
final BaseInfo info = parseScriptFile(f);
if (info != null)
{
suiteInfo.addScript(info);
}
}
catch (final Exception e)
{
LOG.error("Failed to parse script", e);
failures.add(f);
}
}
readInfoFromSuiteFile(suitePath);
postProcess();
dumpToDisk();
pkgTree = null;
if (!failures.isEmpty())
{
final StringBuilder sb = new StringBuilder("The following files could not be parsed successfully:\n");
for (final File f : failures)
{
sb.append(" - ").append(f.getAbsolutePath()).append('\n');
}
sb.append("\nPlease see the log for details.");
throw new ScriptException(sb.toString());
}
}
private void _addPackageToTree(String pkgName)
{
if (pkgTree.getNodeForName(pkgName) == null)
{
final String superPkgName = ScriptingUtils.getParentPackageName(pkgName);
PackageTreeNode parent = pkgTree.getNodeForName(superPkgName);
if (parent == null)
{
_addPackageToTree(superPkgName);
parent = pkgTree.getNodeForName(superPkgName);
}
final PackageInfo info = new PackageInfo(pkgName);
info.setTestData(TestDataUtils.getPackageTestData(null, scriptDir.getAbsolutePath(), info.name));
parent.addChildPackage(info);
suiteInfo.addPackage(info);
}
}
/**
* Post-process all module calls whose comment fields were empty and the module's description takes precedence then.
*/
private void postProcess()
{
while (!worklist.isEmpty())
{
final Step step = worklist.remove(0);
final String stepName = step.getName();
final String packageName = ScriptingUtils.getScriptPackage(stepName);
final PackageTreeNode pkgNode = pkgTree.getNodeForName(packageName);
if (pkgNode == null)
{
LOG.error("No such script package '" + packageName + "'");
}
else if (pkgNode._info == null)
{
LOG.error("Script package '" + packageName + "' was found but no information about it.");
}
else
{
for (final BaseInfo info : pkgNode._info.getModules())
{
if (info.name.equals(stepName))
{
step.setDescription(info.description);
break;
}
}
}
}
for (final String call : calls)
{
final PackageTreeNode node = pkgTree.getNodeForName(ScriptingUtils.getScriptPackage(call));
if (node != null && node._info != null)
{
for (final BaseInfo info : node._info.getModules())
{
if (info.name.equals(call))
{
if (info instanceof ModuleScriptInfo)
{
((ModuleScriptInfo) info).setCalled(true);
}
else if (info instanceof JavaModuleInfo)
{
((JavaModuleInfo) info).setCalled(true);
}
break;
}
}
}
}
}
private String getPkgName(String absolutePath)
{
String s = StringUtils.substringAfter(absolutePath, scriptDirPath);
s = s.replace('\\', '/').replace('/', '.');
return s.startsWith(".") ? s.substring(1) : s;
}
/**
* Writes all the output files to disk.
*/
private void dumpToDisk() throws Exception
{
org.apache.commons.io.FileUtils.forceMkdir(outputDir);
processTemplates();
// Copy static resources to output directory.
FileUtils.copyDirectory(config.getResourceDir(), outputDir, true);
// Finally, create the report XML file.
try (final OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(new File(outputDir, "doc.xml")),
XltConstants.UTF8_ENCODING))
{
final XStream xstream = new XStream(new DomDriver());
xstream.autodetectAnnotations(true);
xstream.setMode(XStream.NO_REFERENCES);
xstream.toXML(suiteInfo, osw);
}
}
/**
* Processes the configured set of templates in parallel.
*
* @throws InterruptedException
* thrown if submitting task to manager of waiting for tasks to complete has failed
*/
private void processTemplates() throws InterruptedException
{
final HashMap dataModel = new HashMap();
dataModel.put("suite", suiteInfo);
dataModel.put("xlt_version", ProductInformation.getProductInformation().getVersion());
final List failures = Collections.synchronizedList(new ArrayList());
final TaskManager taskMgr = TaskManager.getInstance();
for (final Map.Entry entry : config.getTemplates().entrySet())
{
taskMgr.addTask(new Runnable()
{
/**
* {@inheritDoc}
*/
@Override
public void run()
{
try
{
try (final OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(new File(outputDir,
entry.getValue())),
XltConstants.UTF8_ENCODING))
{
final Template template = freeMarkerConfig.getTemplate(entry.getKey());
template.process(dataModel, osw);
}
}
catch (final Throwable t)
{
LOG.error("Failed to process template", t);
failures.add(t);
}
}
});
}
taskMgr.waitForAllTasksToComplete();
if (!failures.isEmpty())
{
final StringBuilder sb = new StringBuilder("Template processing has failed:");
for (final Throwable t : failures)
{
sb.append("\n - ").append(t.getMessage());
}
throw new ScriptException(sb.toString());
}
}
private void readInfoFromSuiteFile(final File pathToSuite) throws Exception
{
final String schemaFileName = "scriptdeveloper-project.xsd";
final File descriptorFile = new File(pathToSuite, SUITE_DESCRIPTOR_FILENAME);
if (descriptorFile.exists() && descriptorFile.isFile() && descriptorFile.canRead())
{
LOG.info("Parsing suite descriptor file: " + descriptorFile);
final Document document = parseXMLFile(descriptorFile, TestDataUtils.class.getResource(schemaFileName));
final NodeList list = document.getDocumentElement().getChildNodes();
for (int i = 0; i < list.getLength(); i++)
{
final Node n = list.item(i);
if (n instanceof Element)
{
final String tagName = ((Element) n).getTagName();
final String content = n.getTextContent();
if ("description".equals(tagName))
{
suiteInfo.description = content;
}
else if ("name".equals(tagName))
{
suiteInfo.name = content;
}
else
{
LOG.info("Don't know how to handle '" + tagName + "' elements");
}
}
}
}
}
private BaseInfo parseScriptFile(final File scriptFile) throws Exception
{
final String schemaFileName = "xlt-script.xsd";
LOG.info("Parsing script file: " + scriptFile);
final String content = org.apache.commons.io.FileUtils.readFileToString(scriptFile, "UTF-8");
final String rootElementTagName = RegExUtils.getFirstMatch(content, "<\\?xml.+?\\?>\\s*<(\\S+)[^>]*?>", 1);
if (rootElementTagName == null)
{
throw new ScriptException("Failed to parse document element tag name of script file '" + scriptFile + "'");
}
final Document document = parseXMLFile(scriptFile, TestDataUtils.class.getResource(schemaFileName));
// let us see what we have
final Element rootElement = document.getDocumentElement();
final String rootElemName = rootElement.getTagName();
BaseInfo info = null;
if ("javamodule".equals(rootElemName))
{
info = processJavaModule(rootElement, scriptFile);
}
else if ("testcase".equals(rootElemName))
{
info = processTestCase(rootElement, scriptFile);
}
else if ("scriptmodule".equals(rootElemName))
{
info = processModule(rootElement, scriptFile);
}
return info;
}
private ScriptInfo processModule(final Element rootElement, final File scriptFile) throws Exception
{
String name = StringUtils.substringBefore(scriptFile.getName(), ".xml");
final String pkgName = getPkgName(scriptFile.getParentFile().getAbsolutePath());
if (StringUtils.isNotEmpty(pkgName))
{
name = pkgName + "." + name;
}
final ModuleScriptInfo info;
if (rootElement.hasAttribute("id"))
{
info = new ModuleScriptInfo(name, rootElement.getAttribute("id"));
}
else
{
info = new ModuleScriptInfo(name);
}
return processCommandScript(rootElement, info, scriptFile);
}
private ScriptInfo processTestCase(final Element rootElement, final File scriptFile) throws Exception
{
String name = StringUtils.substringBefore(scriptFile.getName(), ".xml");
final String pkgName = getPkgName(scriptFile.getParentFile().getAbsolutePath());
if (StringUtils.isNotEmpty(pkgName))
{
name = pkgName + "." + name;
}
final String baseURL = rootElement.getAttribute("baseURL");
final boolean isDisabled = "true".equals(rootElement.getAttribute("disabled"));
final TestScriptInfo info;
if (rootElement.hasAttribute("id"))
{
info = new TestScriptInfo(name, rootElement.getAttribute("id"));
}
else
{
info = new TestScriptInfo(name);
}
info.disabled = isDisabled;
info.baseUrl = baseURL;
return processCommandScript(rootElement, info, scriptFile);
}
private JavaModuleInfo processJavaModule(final Element rootElement, final File scriptFile) throws Exception
{
String name = StringUtils.substringBefore(scriptFile.getName(), ".xml");
final String pkgName = getPkgName(scriptFile.getParentFile().getAbsolutePath());
if (StringUtils.isNotEmpty(pkgName))
{
name = pkgName + "." + name;
}
final String implClassName = rootElement.getAttribute("class");
final JavaModuleInfo info;
if (rootElement.hasAttribute("id"))
{
info = new JavaModuleInfo(name, rootElement.getAttribute("id"), implClassName);
}
else
{
info = new JavaModuleInfo(name, implClassName);
}
final NodeList children = rootElement.getChildNodes();
for (int i = 0; i < children.getLength(); i++)
{
final Node n = children.item(i);
if (n instanceof Element)
{
final Element e = (Element) n;
final String tagName = e.getTagName();
if ("tags".equals(tagName))
{
info.tags = n.getTextContent();
}
else if ("description".equals(tagName))
{
info.description = n.getTextContent();
}
else if ("parameter".equals(tagName))
{
info.addParameter(e.getAttribute("name"), e.getAttribute("desc"));
}
}
}
return info;
}
private ScriptInfo processCommandScript(final Element rootElement, final ScriptInfo info, final File scriptFile)
{
final boolean isModule = info instanceof ModuleScriptInfo;
final PackageTreeNode pkgNode = pkgTree.getNodeForName(ScriptingUtils.getScriptPackage(info.name));
info.setTestData(TestDataUtils.getTestData(scriptFile));
final NodeList children = rootElement.getChildNodes();
for (int i = 0; i < children.getLength(); i++)
{
final Node n = children.item(i);
if (n instanceof Element)
{
final Element e = (Element) n;
final String tagName = e.getTagName();
switch (tagName)
{
case "description":
info.description = e.getTextContent();
break;
case "tags":
info.tags = e.getTextContent();
break;
case "parameter":
if (isModule)
{
((ModuleScriptInfo) info).addParameter(e.getAttribute("name"), e.getAttribute("desc"));
}
break;
case "module":
case "action":
case "command":
handleScriptElement(e, info, pkgNode, false);
break;
case "postSteps":
if (isModule)
{
throw new ScriptException("Only test case scripts can have post-steps.");
}
final NodeList postStepItems = e.getChildNodes();
for (int j = 0; j < postStepItems.getLength(); j++)
{
final Node postStepItem = postStepItems.item(j);
if (postStepItem instanceof Element)
{
final Element postStep = (Element) postStepItem;
switch (postStep.getTagName())
{
case "module":
case "action":
case "command":
handleScriptElement(postStep, info, pkgNode, true);
break;
default:
break;
}
}
}
default:
break;
}
}
}
return info;
}
/**
* @param scriptElement
* @param info
* @param insidePostSteps
*/
private void handleScriptElement(final Element scriptElement, final ScriptInfo info, final PackageTreeNode pkgNode,
final boolean insidePostSteps)
{
final String tagName = scriptElement.getTagName();
final boolean isDisabled = "true".equals(scriptElement.getAttribute("disabled"));
String haystack = null;
if ("module".equals(tagName))
{
final String stepName = scriptElement.getAttribute("name");
String comment = null;
String conditionExpression = null;
boolean conditionDisabled = false;
if (isDisabled)
{
info.addCall(stepName);
}
final NodeList moduleChilds = scriptElement.getChildNodes();
for (int j = 0; j < moduleChilds.getLength(); j++)
{
final Node moduleChild = moduleChilds.item(j);
if (moduleChild instanceof Element)
{
final Element _el = (Element) moduleChild;
final String moduleChildName = _el.getTagName();
if ("parameter".equals(moduleChildName))
{
if (!isDisabled)
{
haystack = StringUtils.defaultString(haystack) + _el.getAttribute("value");
}
}
else if ("comment".equals(moduleChildName))
{
comment = _el.getTextContent();
}
else if ("condition".equals(moduleChildName))
{
conditionExpression = _el.getTextContent();
conditionDisabled = Boolean.valueOf(_el.getAttribute("disabled"));
if (!conditionDisabled)
{
haystack = StringUtils.defaultString(haystack) + conditionExpression;
}
}
}
}
final Step step = new Step(stepName, comment, isDisabled, true, conditionDisabled, conditionExpression);
if (insidePostSteps)
{
((TestScriptInfo) info).addPostStep(step);
}
else
{
info.addStep(step);
}
if (StringUtils.isBlank(comment))
{
worklist.add(step);
}
calls.add(stepName);
}
else
{
if ("command".equals(tagName))
{
if (!isDisabled)
{
final String cmdName = scriptElement.getAttribute("name");
String target = scriptElement.hasAttribute("target") ? scriptElement.getAttribute("target") : null;
if (target == null)
{
final NodeList targetElements = scriptElement.getElementsByTagName("target");
if (targetElements.getLength() > 0)
{
target = targetElements.item(0).getTextContent();
}
}
String value = scriptElement.hasAttribute("value") ? scriptElement.getAttribute("value") : null;
if (value == null)
{
final NodeList valueElements = scriptElement.getElementsByTagName("value");
if (valueElements.getLength() > 0)
{
value = valueElements.item(0).getTextContent();
}
}
if (cmdName.startsWith("store") && StringUtils.isNotBlank(value))
{
info.addStore(value);
}
haystack = StringUtils.defaultString(target) + StringUtils.defaultString(value);
}
}
else if ("action".equals(tagName))
{
final String stepName = scriptElement.getAttribute("name");
String comment = null;
final NodeList stepChildren = scriptElement.getChildNodes();
for (int j = 0; j < stepChildren.getLength(); j++)
{
final Node stepChild = stepChildren.item(j);
if (stepChild instanceof Element && "comment".equals(((Element) stepChild).getTagName()))
{
comment = stepChild.getTextContent();
break;
}
}
final Step step = new Step(stepName, comment, isDisabled);
if (insidePostSteps)
{
((TestScriptInfo) info).addPostStep(step);
}
else
{
info.addStep(step);
}
}
}
if (StringUtils.isNotBlank(haystack))
{
for (final String match : RegExUtils.getAllMatches(haystack, VAR_EXPR_PATTERN))
{
final String varName = match.substring(2, match.length() - 1);
if (!info.getTestData().containsKey(varName) && !info.hasStore(varName) && !MacroProcessor.getInstance().isMacro(varName))
{
final Map pkgData = pkgNode.getEffectiveTestData();
String value = pkgData.get(varName);
if (value == null)
{
value = suiteInfo.getGlobalTestData().get(varName);
}
info.addExternalParam(varName, value);
}
}
}
}
private Document parseXMLFile(final File xmlFile, final URL schemaLocation) throws Exception
{
// parse the file
final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
final Schema schema = schemaFactory.newSchema(schemaLocation);
final SAXParserFactory saxFactory = SAXParserFactory.newInstance();
saxFactory.setNamespaceAware(true);
saxFactory.setSchema(schema);
final SAXParser saxParser = saxFactory.newSAXParser();
final DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
final DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
final Document document = docBuilder.newDocument();
final DefaultHandler handler = new DefaultHandler(document);
saxParser.parse(xmlFile, handler);
if (handler.errors > 0)
{
throw new SAXException(String.format("Parsing the XML file '%s' produced %d error(s) and %d warning(s)", xmlFile.toString(),
handler.errors, handler.warnings));
}
if (handler.warnings > 0)
{
if (LOG.isWarnEnabled())
{
LOG.warn(String.format("Parsing the XML file '%s' produced %d warning(s)", xmlFile.toString(), handler.warnings));
}
}
return document;
}
private static class DefaultHandler extends org.xml.sax.helpers.DefaultHandler
{
private final Document doc;
final Stack elementStack = new Stack();
final StringBuilder textBuffer = new StringBuilder();
private DefaultHandler(final Document document)
{
super();
doc = document;
}
/**
* {@inheritDoc}
*/
@Override
public void startElement(final String uri, final String localName, final String qName, final Attributes attributes)
throws SAXException
{
addTextIfNeeded();
final Element element = doc.createElement(qName);
for (int i = 0; i < attributes.getLength(); i++)
{
element.setAttribute(attributes.getQName(i), attributes.getValue(i));
}
elementStack.push(element);
}
/**
* {@inheritDoc}
*/
@Override
public void endElement(final String uri, final String localName, final String qName)
{
addTextIfNeeded();
final Element closedElement = elementStack.pop();
if (elementStack.isEmpty())
{ // root element
doc.appendChild(closedElement);
}
else
{
final Element parentElement = elementStack.peek();
parentElement.appendChild(closedElement);
}
}
/**
* {@inheritDoc}
*/
@Override
public void characters(final char ch[], final int start, final int length) throws SAXException
{
textBuffer.append(ch, start, length);
}
private void addTextIfNeeded()
{
if (textBuffer.length() > 0)
{
final Element element = elementStack.peek();
final Node textNode = doc.createTextNode(textBuffer.toString());
element.appendChild(textNode);
textBuffer.delete(0, textBuffer.length());
}
}
/**
* The number of errors encountered.
*/
private int errors;
/**
* The number of warnings encountered.
*/
private int warnings;
/**
* {@inheritDoc}
*/
@Override
public void error(final SAXParseException exception) throws SAXException
{
LOG.error(report(exception));
errors++;
}
/**
* {@inheritDoc}
*/
@Override
public void fatalError(final SAXParseException exception) throws SAXException
{
LOG.error(report(exception));
errors++;
}
/**
* {@inheritDoc}
*/
@Override
public void warning(final SAXParseException exception) throws SAXException
{
LOG.warn(report(exception));
warnings++;
}
/**
* Logs the given exception.
*
* @param exception
* the exception
*/
private String report(final SAXParseException exception)
{
return String.format("### %d:%d - %s\n", exception.getLineNumber(), exception.getColumnNumber(), exception.getMessage());
}
}
private static class PackageTree
{
private final PackageTreeNode _root;
private PackageTree(final PackageInfo rootInfo)
{
_root = new PackageTreeNode(rootInfo);
}
private PackageTreeNode getNodeForName(final String packageName)
{
final String name = StringUtils.defaultString(packageName);
PackageTreeNode node = _root;
if (node._info.name.equals(name))
{
return node;
}
node = getNodeForName(ScriptingUtils.getParentPackageName(name));
if (node != null)
{
for (final PackageTreeNode c : node._children)
{
if (c._info.name.equals(packageName))
{
return c;
}
}
}
return null;
}
}
private static class PackageTreeNode
{
private final PackageTreeNode _parent;
private final List _children = new ArrayList();
private final PackageInfo _info;
private PackageTreeNode(final PackageInfo info)
{
_parent = null;
_info = info;
}
private PackageTreeNode(final PackageTreeNode parentNode, final PackageInfo info)
{
_parent = parentNode;
_info = info;
}
void addChildPackage(final PackageInfo info)
{
_children.add(new PackageTreeNode(this, info));
}
Map getEffectiveTestData()
{
final Map data;
if (_parent != null)
{
data = _parent.getEffectiveTestData();
}
else
{
data = new HashMap();
}
data.putAll(_info.getTestData());
return data;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy