// =====================================================================
//
// Copyright (C) 2012 - 2016, Philip Graf
//
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// =====================================================================
package ch.acanda.eclipse.pmd.java.resolution;
import static org.junit.Assert.assertFalse;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import com.google.common.base.Optional;
/**
* Reads the test data from an xml file with the following format:
*
* <pre>
* <?xml version="1.0" encoding="UTF-8"?>
* <tests>
* <!-- The PMD reference id of the rule that is being tested -->
* <pmdReferenceId>rulesets/java/basic.xml/ExtendsObject</pmdReferenceId>
*
* <!-- The language and version of the provided source code -->
* <language>java 1.7</language>
*
* <!-- Every test must have a name. It will be shown in assertion messages
* so you can identify the failing test. There can be more than one test in a file. -->
* <test name="SimpleExtendsObject">
*
* <!-- The setup contains all the data to set up the test -->
* <setup>
*
* <!-- The source must be a valid Java compilation unit and must contain a marker.
* The marker marks the position where the quick fix should be applied. -->
* <source>
* class Example extends <marker>Object</marker> {
* }
* </source>
*
* </setup>
*
* <!-- The 'expected' part contains all the expected values. -->
* <expected>
*
* <!-- The expected source after the quick fix has been applied. -->
* <source>
* class Example {
* }
* </source>
*
* <!-- The expected image of the quick fix. This must be the name of a field in
* {@link ch.acanda.eclipse.pmd.ui.util.PMDPluginImages PMDPluginImages}.
* The image is optional. If no image is provided the test verifies only that
* the image is not {@code null}. -->
* <image>QUICKFIX_REMOVE</image>
*
* <!-- The expected label of the quick fix. The label is optional.
* If no label is provided the test verifies only that the label is not {@code null}. -->
* <label>Remove 'extends Object'</label>
*
* <!-- The expected description of the quick fix. The description is optional.
* If no description is provided the test verifies only that the description is not {@code null}. -->
* <description>Removes <b>extends Object</b> from the type declaration of Example</description>
*
* </expected>
* </test>
* </tests>
* </pre>
*
* @author Philip Graf
*/
public class QuickFixTestData {
public static List<TestParameters> createTestData(final InputStream testCase) {
final List<TestParameters> data = new ArrayList<>();
try {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
final DocumentBuilder docBuilder = factory.newDocumentBuilder();
final Document doc = docBuilder.parse(testCase);
final Optional<String> pmdReferenceId = getOptionalValue(doc, "pmdReferenceId");
final Optional<String> language = getOptionalValue(doc, "language");
final NodeList tests = doc.getElementsByTagName("test");
for (int i = 0, size = tests.getLength(); i < size; i++) {
data.add(getParameters(pmdReferenceId, language, (Element) tests.item(i)));
}
} catch (final ParserConfigurationException | SAXException | IOException e) {
throw new IllegalArgumentException("Invalid test case", e);
}
return data;
}
private static TestParameters getParameters(final Optional<String> pmdReferenceId, final Optional<String> language,
final Element test) {
final TestParameters params = new TestParameters();
params.pmdReferenceId = pmdReferenceId;
params.language = language;
params.name = test.getAttribute("name");
final Element setup = (Element) test.getElementsByTagName("setup").item(0);
final NodeList source = setup.getElementsByTagName("source");
params.source = ltrim(source.item(0).getFirstChild().getNodeValue());
params.offset = params.source.length();
params.source += ((Element) source.item(0)).getElementsByTagName("marker").item(0).getFirstChild().getNodeValue();
params.length = params.source.length() - params.offset;
params.source += rtrim(source.item(0).getChildNodes().item(2).getNodeValue());
params.rulename = getOptionalValue(setup, "rulename");
final Element expected = (Element) test.getElementsByTagName("expected").item(0);
params.expectedSource = getValue(expected, "source");
params.expectedImage = getOptionalValue(expected, "image");
params.expectedLabel = getOptionalValue(expected, "label");
params.expectedDescription = getOptionalValue(expected, "description");
return params;
}
private static Optional<String> getOptionalValue(final Element element, final String tagName) {
return getOptionalValue(element.getElementsByTagName(tagName));
}
private static Optional<String> getOptionalValue(final NodeList elements) {
if (elements.getLength() == 0) {
return Optional.absent();
}
return Optional.of(elements.item(0).getFirstChild().getNodeValue().trim());
}
private static String getValue(final Element element, final String tagName) {
return getValue(element.getElementsByTagName(tagName));
}
private static Optional<String> getOptionalValue(final Document element, final String tagName) {
return getOptionalValue(element.getElementsByTagName(tagName));
}
private static String getValue(final NodeList elements) {
assertFalse(elements.getLength() == 0);
return elements.item(0).getFirstChild().getNodeValue().trim();
}
private static String ltrim(final String s) {
final int len = s.length();
int pos = 0;
while (pos < len && s.charAt(pos) <= ' ') {
pos++;
}
return s.substring(pos);
}
private static String rtrim(final String s) {
int pos = s.length() - 1;
while (pos >= 0 && s.charAt(pos) <= ' ') {
pos--;
}
return s.substring(0, pos + 1);
}
public static final class TestParameters {
public Optional<String> pmdReferenceId;
public Optional<String> language;
public String name;
public int offset;
public int length;
public String source;
public Optional<String> rulename;
public String expectedSource;
public Optional<String> expectedImage;
public Optional<String> expectedLabel;
public Optional<String> expectedDescription;
}
}