/** * Tests that an exported package removal in the PDE model is reflected in the workspace api * baseline */ public void testWPUpdateExportPackageRemoved() throws Exception { IJavaProject project = getTestingProject(); assertNotNull("The testing project must exist", project); // add package assertTestPackage( project, new Path(project.getElementName()).append(ProjectUtils.SRC_FOLDER).makeAbsolute(), "export1"); setPackageToApi(project, "export1"); IApiAnnotations annot = getTestProjectApiDescription().resolveAnnotations(Factory.packageDescriptor("export1")); assertNotNull("there must be an annotation for the new exported package", annot); assertTrue( "the newly exported package must be API visibility", annot.getVisibility() == VisibilityModifiers.API); // remove exported packages IBundleProjectService service = ProjectUtils.getBundleProjectService(); IBundleProjectDescription description = service.getDescription(project.getProject()); description.setPackageExports(null); description.apply(null); // check the API description annot = getTestProjectApiDescription().resolveAnnotations(Factory.packageDescriptor("export1")); assertNotNull("should still be an annotation for the package", annot); assertTrue( "unexported package must be private", VisibilityModifiers.isPrivate(annot.getVisibility())); }
/** * Tests that making Javadoc changes to the source file TestClass2 cause the workspace baseline to * be updated. * * <p>This test adds a @noinstantiate tag to the source file TestClass2 */ public void testWPUpdateSourceTypeChanged() throws Exception { IJavaProject project = getTestingProject(); assertNotNull("The testing project must exist", project); IPackageFragmentRoot root = project.findPackageFragmentRoot( new Path(project.getElementName()).append(ProjectUtils.SRC_FOLDER).makeAbsolute()); assertNotNull("the 'src' package fragment root must exist", root); NullProgressMonitor monitor = new NullProgressMonitor(); IPackageFragment fragment = root.getPackageFragment("a.b.c"); FileUtils.importFileFromDirectory( SRC_LOC.append("TestClass2.java").toFile(), fragment.getPath(), monitor); ICompilationUnit element = (ICompilationUnit) project.findElement(new Path("a/b/c/TestClass2.java")); assertNotNull("TestClass2 must exist in the test project", element); updateTagInSource(element, "TestClass2", null, "@noinstantiate", false); IApiDescription desc = getTestProjectApiDescription(); assertNotNull("the testing project api description must exist", desc); IApiAnnotations annot = desc.resolveAnnotations(Factory.typeDescriptor("a.b.c.TestClass2")); assertNotNull("the annotations for a.b.c.TestClass2 cannot be null", annot); assertTrue( "there must be a noinstantiate setting for TestClass2", (annot.getRestrictions() & RestrictionModifiers.NO_INSTANTIATE) != 0); assertTrue( "there must be a noextend setting for TestClass2", (annot.getRestrictions() & RestrictionModifiers.NO_EXTEND) != 0); }
/** * Asserts if the given restriction is on the specified source * * @param packagename * @param sourcename */ public void assertSourceResctriction(String packagename, String sourcename, int restriction) throws CoreException { IApiDescription desc = getTestProjectApiDescription(); assertNotNull("the testing project api description must exist", desc); IApiAnnotations annot = desc.resolveAnnotations(Factory.typeDescriptor(packagename + "." + sourcename)); assertNotNull( "the annotations for " + packagename + "." + sourcename + " cannot be null", annot); assertTrue( "there must be a noinstantiate setting for TestClass1", annot.getRestrictions() == restriction); }
/** Tests that adding a package does not update the workspace baseline */ public void testWPUpdatePackageAdded() throws Exception { IJavaProject project = getTestingProject(); assertNotNull("The testing project must exist", project); // add the package assertTestPackage( project, new Path(project.getElementName()).append(ProjectUtils.SRC_FOLDER).makeAbsolute(), "a.test1.c.d"); IApiDescription desc = getTestProjectApiDescription(); assertNotNull("the testing project api description must exist", desc); IApiAnnotations annot = desc.resolveAnnotations(Factory.packageDescriptor("a.test1.c.d")); assertNotNull("the annotations for package " + TESTING_PACKAGE + " should exist", annot); }
/** * Tests that removing a source file from an API aware project causes the workspace description to * be updated */ public void testWPUpdateSourceRemoved() throws Exception { IJavaProject project = getTestingProject(); assertNotNull("The testing project must exist", project); IPackageFragmentRoot root = project.findPackageFragmentRoot( new Path(project.getElementName()).append(ProjectUtils.SRC_FOLDER).makeAbsolute()); assertNotNull("the 'src' package fragment root must exist", root); assertTestSource(root, TESTING_PACKAGE, "TestClass1"); IJavaElement element = project.findElement(new Path("a/b/c/TestClass1.java")); assertNotNull("the class a.b.c.TestClass1 must exist in the project", element); element.getResource().delete(true, new NullProgressMonitor()); IApiDescription desc = getTestProjectApiDescription(); assertNotNull("the testing project api description must exist", desc); IApiAnnotations annot = desc.resolveAnnotations(Factory.typeDescriptor("a.b.c.TestClass1")); assertNull("the annotations for a.b.c.TestClass1 should no longer be present", annot); }
/** * Tests that removing a package updates the workspace baseline This test removes the a.b.c * package being used in all tests thus far, and should be run last */ public void testWPUpdatePackageRemoved() throws Exception { IJavaProject project = getTestingProject(); assertNotNull("The testing project must exist", project); // add the package IPath srcroot = new Path(project.getElementName()).append(ProjectUtils.SRC_FOLDER).makeAbsolute(); IPackageFragment fragment = assertTestPackage(project, srcroot, "a.test2"); assertNotNull("the package " + TESTING_PACKAGE + " must exist", fragment); // remove the package fragment.delete(true, new NullProgressMonitor()); IApiDescription desc = getTestProjectApiDescription(); assertNotNull("the testing project api description must exist", desc); IApiAnnotations annot = desc.resolveAnnotations(Factory.packageDescriptor("a.test2")); assertNull("the annotations for package " + TESTING_PACKAGE + " should not exist", annot); }
/** * Tests that an exported package addition in the PDE model is reflected in the workspace api * baseline */ public void testWPUpdateExportPackageAdded() throws Exception { IJavaProject project = getTestingProject(); assertNotNull("The testing project must exist", project); // add package assertTestPackage( project, new Path(project.getElementName()).append(ProjectUtils.SRC_FOLDER).makeAbsolute(), "export1"); // update setPackageToApi(project, "export1"); IApiAnnotations annot = getTestProjectApiDescription().resolveAnnotations(Factory.packageDescriptor("export1")); assertNotNull("there must be an annotation for the new exported package", annot); assertTrue( "the newly exported package must be API visibility", annot.getVisibility() == VisibilityModifiers.API); }
/** * Tests that removing a tag from a field updates the workspace baseline * * <p>This test adds a @noextend tag to the field 'field' in TestField9 */ public void testWPUpdateSourceFieldRemoveTag() throws Exception { IJavaProject project = getTestingProject(); assertNotNull("The testing project must exist", project); IPackageFragmentRoot root = project.findPackageFragmentRoot( new Path(project.getElementName()).append(ProjectUtils.SRC_FOLDER).makeAbsolute()); assertNotNull("the 'src' package fragment root must exist", root); assertTestSource(root, TESTING_PACKAGE, "TestField9"); ICompilationUnit element = (ICompilationUnit) project.findElement(new Path("a/b/c/TestField9.java")); assertNotNull("TestField9 must exist in the test project", element); updateTagInSource(element, "field1", null, "@noreference", true); IApiDescription desc = getTestProjectApiDescription(); assertNotNull("the testing project api description must exist", desc); IApiAnnotations annot = desc.resolveAnnotations(Factory.fieldDescriptor("a.b.c.TestField9", "field")); assertNotNull("the annotations for 'field' cannot be null", annot); assertTrue("there must be a no restrictions for 'field'", annot.getRestrictions() == 0); }
/** * Tests that changing a directive to x-internal on an exported package causes the workspace api * baseline to be updated */ public void testWPUPdateExportPackageDirectiveChangedToInternal() throws Exception { IJavaProject project = getTestingProject(); assertNotNull("The testing project must exist", project); // add package assertTestPackage( project, new Path(project.getElementName()).append(ProjectUtils.SRC_FOLDER).makeAbsolute(), "export1"); // export the package ProjectUtils.addExportedPackage(project.getProject(), "export1", true, null); // check the description IApiAnnotations annot = getTestProjectApiDescription().resolveAnnotations(Factory.packageDescriptor("export1")); assertNotNull("there must be an annotation for the new exported package", annot); assertTrue( "the changed exported package must be PRIVATE visibility", annot.getVisibility() == VisibilityModifiers.PRIVATE); }
/** * Tests that removing a tag from a type updates the workspace baseline * * <p>This test removes a @noinstantiate tag to an inner class in TestClass3 */ public void testWPUpdateSourceTypeRemoveTag() throws Exception { IJavaProject project = getTestingProject(); assertNotNull("The testing project must exist", project); IPackageFragmentRoot root = project.findPackageFragmentRoot( new Path(project.getElementName()).append(ProjectUtils.SRC_FOLDER).makeAbsolute()); assertNotNull("the 'src' package fragment root must exist", root); assertTestSource(root, TESTING_PACKAGE, "TestClass3"); ICompilationUnit element = (ICompilationUnit) project.findElement(new Path("a/b/c/TestClass3.java")); assertNotNull("TestClass3 must exist in the test project", element); updateTagInSource(element, "InnerTestClass3", null, "@noextend", true); IApiDescription desc = getTestProjectApiDescription(); assertNotNull("the testing project api description must exist", desc); IApiAnnotations annot = desc.resolveAnnotations(Factory.typeDescriptor("a.b.c.TestClass3$InnerTestClass3")); assertNotNull("the annotations for 'InnerTestClass3' cannot be null", annot); assertTrue( "there must be a no restrictions for 'InnerTestClass3'", (annot.getRestrictions() & RestrictionModifiers.NO_INSTANTIATE) == 0); }
/** * Tests that changing the javadoc for a method updates the workspace baseline * * <p>This test adds a @noextend tag to the method foo() in TestClass1 */ public void testWPUpdateSourceMethodChanged() throws Exception { IJavaProject project = getTestingProject(); assertNotNull("The testing project must exist", project); IPackageFragmentRoot root = project.findPackageFragmentRoot( new Path(project.getElementName()).append(ProjectUtils.SRC_FOLDER).makeAbsolute()); assertNotNull("the 'src' package fragment root must exist", root); assertTestSource(root, TESTING_PACKAGE, "TestClass1"); ICompilationUnit element = (ICompilationUnit) project.findElement(new Path("a/b/c/TestClass1.java")); assertNotNull("TestClass1 must exist in the test project", element); updateTagInSource(element, "foo", "()V", "@nooverride", false); IApiDescription desc = getTestProjectApiDescription(); assertNotNull("the testing project api description must exist", desc); IApiAnnotations annot = desc.resolveAnnotations(Factory.methodDescriptor("a.b.c.TestClass1", "foo", "()V")); assertNotNull("the annotations for foo() cannot be null", annot); assertTrue( "there must be a nooverride setting for foo()", (annot.getRestrictions() & RestrictionModifiers.NO_OVERRIDE) != 0); }
/** * Visitor to scan a compilation unit. We only care about javadoc nodes that have either type or * enum declarations as parents, so we have to override the ones we don't care about. */ static class Visitor extends ASTVisitor { private IApiDescription fDescription = null; /** * Package descriptor. Initialized to default package, and overridden if a package declaration * is visited. */ private IPackageDescriptor fPackage = Factory.packageDescriptor(""); // $NON-NLS-1$ /** Type descriptor for type currently being visited. */ private IReferenceTypeDescriptor fType = null; /** * Used to look up binaries when resolving method signatures, or <code>null</code> if not * provided. */ private IApiTypeContainer fContainer = null; /** List of exceptions encountered, or <code>null</code> */ private CoreException fException; /** * Constructor * * @param description API description to annotate * @param container class file container or <code>null</code>, used to resolve method signatures */ public Visitor(IApiDescription description, IApiTypeContainer container) { fDescription = description; fContainer = container; } /* (non-Javadoc) * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.Javadoc) */ public boolean visit(Javadoc node) { List tags = node.tags(); ASTNode parent = node.getParent(); if (parent != null) { switch (parent.getNodeType()) { case ASTNode.TYPE_DECLARATION: { TypeDeclaration type = (TypeDeclaration) parent; if (type.isInterface()) { processTags( fType, pruneTags(tags, type), IApiJavadocTag.TYPE_INTERFACE, IApiJavadocTag.MEMBER_NONE); } else { processTags( fType, pruneTags(tags, type), IApiJavadocTag.TYPE_CLASS, IApiJavadocTag.MEMBER_NONE); } break; } case ASTNode.METHOD_DECLARATION: { MethodDeclaration method = (MethodDeclaration) parent; String signature = Signatures.getMethodSignatureFromNode(method); if (signature != null) { String methodname = method.getName().getFullyQualifiedName(); int member = IApiJavadocTag.MEMBER_METHOD; if (method.isConstructor()) { member = IApiJavadocTag.MEMBER_CONSTRUCTOR; methodname = "<init>"; // $NON-NLS-1$ } IMethodDescriptor descriptor = fType.getMethod(methodname, signature); processTags(descriptor, pruneTags(tags, method), getEnclosingType(method), member); } break; } case ASTNode.FIELD_DECLARATION: { FieldDeclaration field = (FieldDeclaration) parent; List fields = field.fragments(); VariableDeclarationFragment fragment = null; for (Iterator iter = fields.iterator(); iter.hasNext(); ) { fragment = (VariableDeclarationFragment) iter.next(); processTags( fType.getField(fragment.getName().getFullyQualifiedName()), pruneTags(tags, field), getEnclosingType(field), IApiJavadocTag.MEMBER_FIELD); } break; } } } return false; } private int getEnclosingType(ASTNode node) { ASTNode lnode = node; while (!(lnode instanceof AbstractTypeDeclaration)) { lnode = lnode.getParent(); } if (lnode instanceof TypeDeclaration) { if (((TypeDeclaration) lnode).isInterface()) { return IApiJavadocTag.TYPE_INTERFACE; } } return IApiJavadocTag.TYPE_CLASS; } /** * A type has been entered - update the type being visited. * * @param name name from type node */ private void enterType(SimpleName name) { if (fType == null) { fType = fPackage.getType(name.getFullyQualifiedName()); } else { fType = fType.getType(name.getFullyQualifiedName()); } } /** A type has been exited - update the type being visited. */ private void exitType() { fType = fType.getEnclosingType(); } /** * Processes the tags for the given {@link IElementDescriptor} * * @param descriptor the descriptor * @param tags the listing of tags from the AST * @param type one of <code>CLASS</code> or <code>INTERFACE</code> * @param member one of <code>METHOD</code> or <code>FIELD</code> or <code>NONE</code> */ protected void processTags(IElementDescriptor descriptor, List tags, int type, int member) { JavadocTagManager jtm = ApiPlugin.getJavadocTagManager(); TagElement tag = null; String tagname = null; int restrictions = RestrictionModifiers.NO_RESTRICTIONS; for (Iterator iter = tags.iterator(); iter.hasNext(); ) { tag = (TagElement) iter.next(); tagname = tag.getTagName(); restrictions |= jtm.getRestrictionsForTag(tagname, type, member); } if (restrictions != RestrictionModifiers.NO_RESTRICTIONS) { IElementDescriptor ldesc = descriptor; if (ldesc.getElementType() == IElementDescriptor.METHOD) { try { ldesc = resolveMethod((IMethodDescriptor) ldesc); } catch (CoreException e) { fException = e; } } fDescription.setRestrictions(ldesc, restrictions); } } /** * Method to post process returned flags from the {@link Javadoc} node of the element * * @param tags the tags to process * @param element the {@link ASTNode} the tag appears on * @return the list of valid tags to process restrictions for */ private List pruneTags(final List tags, ASTNode node) { ArrayList pruned = new ArrayList(tags.size()); TagElement tag = null; switch (node.getNodeType()) { case ASTNode.TYPE_DECLARATION: { TypeDeclaration type = (TypeDeclaration) node; int flags = type.getModifiers(); for (Iterator iterator = tags.iterator(); iterator.hasNext(); ) { tag = (TagElement) iterator.next(); String tagname = tag.getTagName(); if (type.isInterface() && ("@noextend".equals(tagname) || //$NON-NLS-1$ "@noimplement".equals(tagname))) { // $NON-NLS-1$ pruned.add(tag); } else { if ("@noextend".equals(tagname)) { // $NON-NLS-1$ if (!Flags.isFinal(flags)) { pruned.add(tag); continue; } } if ("@noinstantiate".equals(tagname)) { // $NON-NLS-1$ if (!Flags.isAbstract(flags)) { pruned.add(tag); continue; } } } } break; } case ASTNode.METHOD_DECLARATION: { MethodDeclaration method = (MethodDeclaration) node; int flags = method.getModifiers(); for (Iterator iterator = tags.iterator(); iterator.hasNext(); ) { tag = (TagElement) iterator.next(); if ("@noreference".equals(tag.getTagName())) { // $NON-NLS-1$ pruned.add(tag); continue; } if ("@nooverride".equals(tag.getTagName())) { // $NON-NLS-1$ ASTNode parent = method.getParent(); int pflags = 0; if (parent instanceof BodyDeclaration) { pflags = ((BodyDeclaration) parent).getModifiers(); } if (!Flags.isFinal(flags) && !Flags.isStatic(flags) && !Flags.isFinal(pflags)) { pruned.add(tag); continue; } } } break; } case ASTNode.FIELD_DECLARATION: { FieldDeclaration field = (FieldDeclaration) node; for (Iterator iterator = tags.iterator(); iterator.hasNext(); ) { tag = (TagElement) iterator.next(); boolean isfinal = Flags.isFinal(field.getModifiers()); if (isfinal || (isfinal && Flags.isStatic(field.getModifiers()))) { break; } if ("@noreference".equals(tag.getTagName())) { // $NON-NLS-1$ pruned.add(tag); break; } } break; } } return pruned; } /** * Returns whether to continue processing children. * * @return whether to continue processing children. */ private boolean isContinue() { return fException == null; } /** * Returns an exception that aborted processing, or <code>null</code> if none. * * @return an exception that aborted processing, or <code>null</code> if none */ CoreException getException() { return fException; } /* (non-Javadoc) * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.TypeDeclaration) */ public boolean visit(TypeDeclaration node) { if (isPrivate(node.getModifiers())) { return false; } enterType(node.getName()); return isContinue(); } /* (non-Javadoc) * @see org.eclipse.jdt.core.dom.ASTVisitor#endVisit(org.eclipse.jdt.core.dom.TypeDeclaration) */ public void endVisit(TypeDeclaration node) { if (!isPrivate(node.getModifiers())) { exitType(); } } /* (non-Javadoc) * @see org.eclipse.jdt.core.dom.ASTVisitor#endVisit(org.eclipse.jdt.core.dom.AnnotationTypeDeclaration) */ public void endVisit(AnnotationTypeDeclaration node) { if (!isPrivate(node.getModifiers())) { exitType(); } } /* (non-Javadoc) * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.AnnotationTypeDeclaration) */ public boolean visit(AnnotationTypeDeclaration node) { if (isPrivate(node.getModifiers())) { return false; } enterType(node.getName()); return isContinue(); } /* (non-Javadoc) * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.EnumDeclaration) */ public boolean visit(EnumDeclaration node) { if (isPrivate(node.getModifiers())) { return false; } enterType(node.getName()); return isContinue(); } /* (non-Javadoc) * @see org.eclipse.jdt.core.dom.ASTVisitor#endVisit(org.eclipse.jdt.core.dom.EnumDeclaration) */ public void endVisit(EnumDeclaration node) { if (!isPrivate(node.getModifiers())) { exitType(); } } /* (non-Javadoc) * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.PackageDeclaration) */ public boolean visit(PackageDeclaration node) { Name name = node.getName(); fPackage = Factory.packageDescriptor(name.getFullyQualifiedName()); return false; } /* (non-Javadoc) * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.MethodDeclaration) */ public boolean visit(MethodDeclaration node) { if (isPrivate(node.getModifiers())) { return false; } return isContinue(); } /* (non-Javadoc) * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.FieldDeclaration) */ public boolean visit(FieldDeclaration node) { if (isPrivate(node.getModifiers())) { return false; } return isContinue(); } private boolean isPrivate(int flags) { return Flags.isPrivate(flags); } /** * Returns a method descriptor with a resolved signature for the given method descriptor with an * unresolved signature. * * @param descriptor method to resolve * @return resolved method descriptor or the same method descriptor if unable to resolve * @exception CoreException if unable to resolve the method and a class file container was * provided for this purpose */ private IMethodDescriptor resolveMethod(IMethodDescriptor descriptor) throws CoreException { if (fContainer != null) { IReferenceTypeDescriptor type = descriptor.getEnclosingType(); IApiTypeRoot classFile = fContainer.findTypeRoot(type.getQualifiedName()); if (classFile != null) { IApiType structure = classFile.getStructure(); if (structure != null) { IApiMethod[] methods = structure.getMethods(); for (int i = 0; i < methods.length; i++) { IApiMethod method = methods[i]; if (descriptor.getName().equals(method.getName())) { String signature = method.getSignature(); String descriptorSignature = descriptor.getSignature().replace('/', '.'); if (Signatures.matchesSignatures( descriptorSignature, signature.replace('/', '.'))) { return descriptor.getEnclosingType().getMethod(method.getName(), signature); } String genericSignature = method.getGenericSignature(); if (genericSignature != null) { if (Signatures.matchesSignatures( descriptorSignature, genericSignature.replace('/', '.'))) { return descriptor.getEnclosingType().getMethod(method.getName(), signature); } } } } } } throw new CoreException( new Status( IStatus.ERROR, ApiPlugin.PLUGIN_ID, MessageFormat.format( "Unable to resolve method signature: {0}", new String[] {descriptor.toString()}), null)); //$NON-NLS-1$ } return descriptor; } }
/* (non-Javadoc) * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.PackageDeclaration) */ public boolean visit(PackageDeclaration node) { Name name = node.getName(); fPackage = Factory.packageDescriptor(name.getFullyQualifiedName()); return false; }