blob: 93313cb958195327375b89e5ead86d7505f4e143 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2014 IBM Corporation and others.
* 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
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.pde.api.tools.util.tests;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.Javadoc;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.TagElement;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.ltk.core.refactoring.CompositeChange;
import org.eclipse.ltk.core.refactoring.TextFileChange;
import org.eclipse.pde.api.tools.internal.ApiDescriptionProcessor;
import org.eclipse.pde.api.tools.internal.util.Signatures;
import org.eclipse.pde.api.tools.model.tests.TestSuiteHelper;
import org.eclipse.pde.api.tools.tests.AbstractApiTest;
import org.eclipse.pde.api.tools.tests.util.FileUtils;
import org.eclipse.pde.api.tools.tests.util.ProjectUtils;
import org.eclipse.pde.api.tools.ui.internal.ApiUIPlugin;
import org.eclipse.pde.api.tools.ui.internal.wizards.ApiToolingSetupRefactoring;
import org.eclipse.pde.api.tools.ui.internal.wizards.WizardMessages;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.TextEdit;
import com.ibm.icu.text.MessageFormat;
/**
* This class tests the {@link ApiDescriptionProcessor}
*
* @since 1.0.0
*/
@SuppressWarnings("unchecked")
public class ApiDescriptionProcessorTests extends AbstractApiTest {
/**
* The source directory for the javadoc updating test source
*/
private static String JAVADOC_SRC_DIR = null;
static {
JAVADOC_SRC_DIR = TestSuiteHelper.getPluginDirectoryPath().append("test-source").append("javadoc").toOSString();
}
/**
* Visitor used to inspect the 'after' class files once they have had tags
* added to ensure the tags as specified in the component.xml file were
* added.
*/
class ChangeVisitor extends ASTVisitor {
String type, membername, signature, innertypename;
String[] expectedtags = null;
boolean processed = false;
/**
* Constructor
*
* @param type
* @param membername
* @param signature
*/
public ChangeVisitor(String type, String innertypename, String membername, String signature, String[] expectedtags) {
this.type = type;
this.membername = membername;
this.signature = signature;
this.expectedtags = expectedtags;
this.innertypename = innertypename;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.FieldDeclaration)
*/
public boolean visit(FieldDeclaration node) {
if (signature != null) {
// means we are looking for a method
return false;
}
List<VariableDeclarationFragment> fields = node.fragments();
VariableDeclarationFragment fragment = null;
for(Iterator<VariableDeclarationFragment> iter = fields.iterator(); iter.hasNext();) {
fragment = iter.next();
if(fragment.getName().getFullyQualifiedName().equals(membername)) {
Javadoc docnode = node.getJavadoc();
assertNotNull("the field: "+membername+" must have a javadoc node", docnode);
assertTrue("the field: "+membername+" should contain all of the tags: "+ getStringValue(expectedtags), containsAllTags(docnode.tags()));
processed = true;
}
}
return false;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.MethodDeclaration)
*/
public boolean visit(MethodDeclaration node) {
if(node.getName().getFullyQualifiedName().equals(membername)) {
String sig = Signatures.getMethodSignatureFromNode(node);
if(signature.equals(sig)) {
Javadoc docnode = node.getJavadoc();
assertNotNull("the method: "+membername+" ["+signature+"] must have a javadoc node", docnode);
assertTrue("the method: "+membername+" ["+signature+"] should contain all of the tags: " + getStringValue(expectedtags), containsAllTags(docnode.tags()));
processed = true;
}
}
return false;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.TypeDeclaration)
*/
public boolean visit(TypeDeclaration node) {
if(membername == null && signature == null) {
String name = node.getName().getFullyQualifiedName();
if((innertypename == null && name.equals(type)) || name.equals(innertypename)) {
Javadoc docnode = node.getJavadoc();
assertNotNull("the type: "+name+" must have a javadoc node", docnode);
assertTrue("the type: "+name+" should contain all of the tags: " + getStringValue(expectedtags), containsAllTags(docnode.tags()));
processed = true;
}
}
return membername != null || innertypename != null;
}
/**
* Determines if the listing of doc tags from the declaration contains all of the expected tags
* @param tags
* @return true if the tag list contains all of the expected tags, false otherwise
*/
private boolean containsAllTags(List<TagElement> tags) {
boolean allfound = true;
TagElement element = null;
for(int i = 0; i < expectedtags.length; i++) {
boolean contained = false;
for(int j = 0; j < tags.size(); j++) {
element = tags.get(j);
if(expectedtags[i].equals(element.getTagName())) {
contained = true;
}
}
allfound &= contained;
}
return allfound;
}
private String getStringValue(String[] tags) {
StringBuffer buffer = new StringBuffer();
for (int i = 0, max = tags.length; i < max; i++) {
if (i > 0) {
buffer.append(',');
}
buffer.append(tags[i]);
}
return String.valueOf(buffer);
}
}
private static IPath ROOT_PATH = TestSuiteHelper.getPluginDirectoryPath().append("test-source").append("javadoc");
private static File componentxml = new File(ROOT_PATH.append("component.xml").toOSString());
/* (non-Javadoc)
* @see junit.framework.TestCase#setUp()
*/
@Override
protected void setUp() throws Exception {
super.setUp();
createProject(TESTING_PROJECT_NAME, null);
IJavaProject project = getTestingJavaProject(TESTING_PROJECT_NAME);
assertNotNull("The java project must have been created", project);
IPackageFragmentRoot srcroot = ProjectUtils.addSourceContainer(project, ProjectUtils.SRC_FOLDER);
assertNotNull("the src root must have been created", srcroot);
File src = new File(JAVADOC_SRC_DIR);
assertTrue("the source dir must exist", src.exists());
assertTrue("the source dir must be a directory", src.isDirectory());
assertNotNull("the srcroot for the test java project must not be null", srcroot);
FileUtils.importFilesFromDirectory(src, srcroot.getPath().append("javadoc"), new NullProgressMonitor());
ApiToolingSetupRefactoring refactoring = new ApiToolingSetupRefactoring();
CompositeChange change = new CompositeChange("Test");
createTagChanges(change, project, componentxml);
refactoring.addChange(change);
performRefactoring(refactoring);
}
/* (non-Javadoc)
* @see junit.framework.TestCase#tearDown()
*/
@Override
protected void tearDown() throws Exception {
super.tearDown();
deleteProject(TESTING_PROJECT_NAME);
}
/**
* Tests that the component.xml file is parsed if it is provided in one of
* the doc'd three forms:
* <ol>
* <li>a directory containing the component.xml file</li>
* <li>a component.xml file</li>
* <li>a jar file containing the component.xml file</li>
* </ol>
*/
public void testSerializeComponentXml() {
String xml = ApiDescriptionProcessor.serializeComponentXml(new File(ROOT_PATH.toOSString()));
assertNotNull("The component xml file must exist and be parsable from a root directory", xml);
xml = ApiDescriptionProcessor.serializeComponentXml(componentxml);
assertNotNull("The component xml file must exist and be parsable from a component.xml file", xml);
xml = ApiDescriptionProcessor.serializeComponentXml(new File(ROOT_PATH.append("component.jar").toOSString()));
assertNotNull("The component xml file must exist and be parsable from a jar file", xml);
}
/**
* Creates all of the text edit changes collected from the processor. The collected edits are arranged as multi-edits
* for the one file that they belong to
* @param projectchange
* @param project
* @param cxml
*/
private void createTagChanges(CompositeChange projectchange, IJavaProject project, File cxml) {
try {
HashMap<IFile, Set<TextEdit>> map = new HashMap<IFile, Set<TextEdit>>();
ApiDescriptionProcessor.collectTagUpdates(project, cxml, map);
IFile file = null;
TextFileChange change = null;
MultiTextEdit multiedit = null;
Set<TextEdit> alledits = null;
TextEdit edit = null;
for(Iterator<IFile> iter = map.keySet().iterator(); iter.hasNext();) {
file = iter.next();
change = new TextFileChange(MessageFormat.format(WizardMessages.JavadocTagRefactoring_2, new Object[] {file.getName()}), file);
multiedit = new MultiTextEdit();
change.setEdit(multiedit);
alledits = map.get(file);
if(alledits != null) {
for(Iterator<TextEdit> iter2 = alledits.iterator(); iter2.hasNext();) {
edit = (TextEdit) iter2.next();
multiedit.addChild(edit);
}
}
if(change != null) {
projectchange.add(change);
}
}
}
catch (CoreException e) {
ApiUIPlugin.log(e);
}
catch (IOException e) {
ApiUIPlugin.log(e);
}
}
/**
* Processes the change from original to updated
*
* @param typename the name of the type to query
* @param membername the name of the member
* @param signature the signature of the member
* @param the tags we expect to see
*/
protected void processUpdatedItem(String typename, String innertypename, String membername, String signature, String[] expectedtags) throws Exception{
try {
IJavaProject project = getTestingJavaProject(TESTING_PROJECT_NAME);
IType type = project.findType("javadoc", typename);
assertNotNull("the type for javadoc." + typename + " must exist", type);
ASTParser parser = ASTParser.newParser(AST.JLS8);
parser.setSource(type.getCompilationUnit());
CompilationUnit cunit = (CompilationUnit) parser.createAST(new NullProgressMonitor());
ChangeVisitor visitor = new ChangeVisitor(typename, innertypename, membername, signature, expectedtags);
cunit.accept(visitor);
assertTrue("the specified node should have been processed", visitor.processed);
} catch (JavaModelException jme) {
fail("the test class javadoc." + typename + " had problems loading");
}
}
/**
* Tests the addition of a javadoc tag to a class. Uses
* <code>JavadocTestClass1</code>
*/
public void testProcessClassAddition() throws Exception {
processUpdatedItem("JavadocTestClass1", null, null, null, new String[] {"@noinstantiate"});
}
/**
* Tests the addition of a javadoc tag to a class that does not have a
* javadoc section yet Uses <code>JavadocTestClass7</code>
*/
public void testProcessClassAdditionNoDocElement() throws Exception {
processUpdatedItem("JavadocTestClass7", null, null, null, new String[] {"@noextend", "@noinstantiate"});
}
/**
* Tests the addition of a javadoc tag to a method that does not have a
* javadoc section yet Uses <code>JavadocTestClass7</code>
*/
public void testProcessMethodAdditionNoDocElement() throws Exception {
processUpdatedItem("JavadocTestClass7", null, "m1", "()V", new String[] {"@nooverride"});
}
/**
* Tests the addition of a javadoc tag to a field that does not have a
* javadoc section yet Uses <code>JavadocTestClass7</code>
*/
public void testProcessFieldAdditionNoDocElement() throws Exception {
processUpdatedItem("JavadocTestClass7", null, "f1", null, new String[] {"@noreference"});
}
/**
* Tests the addition of a javadoc tag to an inner class. Uses
* <code>JavadocTestClass2</code>
*/
public void testProcessInnerClassAddition() throws Exception {
processUpdatedItem("JavadocTestClass2", "Inner", null, null, new String[] {"@noinstantiate"});
}
/**
* Tests the addition of a javadoc tags to methods. Uses
* <code>JavadocTestClass3</code>
*/
public void testProcessMethodAddition() throws Exception {
processUpdatedItem("JavadocTestClass3", null, "m1", "()V", new String[] {"@nooverride"});
processUpdatedItem("JavadocTestClass3", null, "m2", "()V", new String[] {"@noreference"});
}
/**
* Tests the addition of a javadoc tags to fields. Uses
* <code>JavadocTestClass4</code>
*/
public void testProcessFieldAddition() throws Exception {
processUpdatedItem("JavadocTestClass4", null, "f1", null, new String[] {"@noreference"});
processUpdatedItem("JavadocTestClass4", null, "f2", null, new String[] {"@noreference"});
}
/**
* Tests the addition of a javadoc tags to methods in inner classes. Uses
* <code>JavadocTestClass6</code>
*/
public void testProcessInnerMethodAddition() throws Exception {
processUpdatedItem("JavadocTestClass6", "Inner2", "m1", "()V", new String[] {"@nooverride"});
processUpdatedItem("JavadocTestClass6", "Inner2", "m2", "()V", new String[] {"@noreference"});
}
/**
* Tests the addition of a javadoc tags to fields in inner classes. Uses
* <code>JavadocTestClass5</code>
*/
public void testProcessInnerFieldAddition() throws Exception {
processUpdatedItem("JavadocTestClass5", "Inner2", "f1", null, new String[] {"@noreference"});
processUpdatedItem("JavadocTestClass5", "Inner2", "f2", null, new String[] {"@noreference"});
}
/**
* Tests that the old 'subclass' attribute is correctly resolved to 'noextend' restriction for tag updating.
* Tests the case of bug 210786 (https://bugs.eclipse.org/bugs/show_bug.cgi?id=210786)
* Uses <code>JavadocTestClass8</code>
*/
public void testProcessSubclassAttribute() throws Exception {
processUpdatedItem("JavadocTestClass8", null, null, null, new String[] {"@noextend"});
}
}