| /** |
| * Copyright (c) 2006 Eclipse.org |
| * |
| * 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: |
| * bblajer - initial API and implementation |
| */ |
| package org.eclipse.gmf.tests; |
| |
| import java.text.MessageFormat; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.MultiStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IMember; |
| import org.eclipse.jdt.core.IPackageFragment; |
| import org.eclipse.jdt.core.IPackageFragmentRoot; |
| import org.eclipse.jdt.core.ISourceRange; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| |
| /** |
| * Utility that checks various assumptions on the generated code. Currently, only the correct placement of <code>@generated</code> tags is checked. |
| */ |
| public class JDTUtil { |
| private final IJavaProject myJavaProject; |
| private ViolationAggregator myAggregator; |
| |
| private static interface IMemberProcessor { |
| public IStatus processMember(IMember member, boolean isLocalMember) throws JavaModelException; |
| } |
| |
| private static class ViolationAggregator { |
| private MultiStatus myStatus = new MultiStatus(Plugin.getPluginID(), 0, "JDT Violations", null); |
| |
| public void add(IStatus status) { |
| myStatus.merge(status); |
| } |
| |
| public IStatus getStatus() { |
| if (myStatus.isOK()) { |
| return Status.OK_STATUS; |
| } |
| StringBuffer buffer = new StringBuffer(); |
| IStatus[] children = myStatus.getChildren(); |
| for(int i = 0; i < children.length; i++) { |
| if (children[i].matches(myStatus.getSeverity())) { |
| buffer.append(children[i].getMessage()).append("\n"); |
| } |
| } |
| return new Status(myStatus.getSeverity(), Plugin.getPluginID(), 0, buffer.toString(), null); |
| } |
| } |
| |
| public JDTUtil(IJavaProject javaProject) { |
| myJavaProject = javaProject; |
| } |
| |
| public JDTUtil(IProject p) { |
| this(JavaCore.create(p)); |
| } |
| |
| public IStatus collectProblems() { |
| return collectProblems(new IMemberProcessor[] {new GeneratedTagEnsurer(), new GeneratedTagAbsenceInLocalMembersEnsurer()}); |
| } |
| |
| private IStatus collectProblems(IMemberProcessor[] processors) { |
| if (myAggregator == null) { |
| myAggregator = new ViolationAggregator(); |
| collectProblems(myJavaProject, processors); |
| } |
| return myAggregator.getStatus(); |
| } |
| |
| private void collectProblems(IJavaElement jElement, IMemberProcessor[] processors) { |
| try { |
| switch (jElement.getElementType()) { |
| case IJavaElement.JAVA_PROJECT: |
| { |
| IJavaProject jProject = (IJavaProject) jElement; |
| IPackageFragmentRoot[] roots = jProject.getPackageFragmentRoots(); |
| for(int i = 0; i < roots.length; i++) { |
| if (roots[i].getKind() == IPackageFragmentRoot.K_SOURCE) { |
| collectProblems(roots[i], processors); |
| } |
| } |
| } |
| break; |
| case IJavaElement.PACKAGE_FRAGMENT_ROOT: |
| { |
| IPackageFragmentRoot root = (IPackageFragmentRoot) jElement; |
| IJavaElement[] children = root.getChildren(); |
| for(int i = 0; i < children.length; i++) { |
| collectProblems(children[i], processors); |
| } |
| } |
| break; |
| case IJavaElement.PACKAGE_FRAGMENT: |
| { |
| IPackageFragment pf = (IPackageFragment) jElement; |
| ICompilationUnit[] compilationUnits = pf.getCompilationUnits(); |
| for(int i = 0; i < compilationUnits.length; i++) { |
| collectProblems(compilationUnits[i], processors); |
| } |
| } |
| break; |
| case IJavaElement.COMPILATION_UNIT: |
| { |
| ICompilationUnit compilationUnit = (ICompilationUnit) jElement; |
| IType[] types = compilationUnit.getTypes(); |
| for(int i = 0; i < types.length; i++) { |
| collectProblems(types[i], processors); |
| } |
| } |
| break; |
| case IJavaElement.TYPE: |
| { |
| IMember member = (IMember) jElement; |
| collectProblems(member, processors, false); |
| } |
| break; |
| } |
| } catch (JavaModelException e) { |
| myAggregator.add(e.getStatus()); |
| } |
| } |
| |
| private void collectProblems(IMember member, IMemberProcessor[] processors, boolean isLocalMember) throws JavaModelException { |
| for (int i = 0; i < processors.length; i++) { |
| IStatus status = processors[i].processMember(member, isLocalMember); |
| if (status != null && !status.isOK()) { |
| myAggregator.add(status); |
| } |
| } |
| isLocalMember |= !(member instanceof IType); |
| IJavaElement[] children = member.getChildren(); |
| for(int i = 0; i < children.length; i++) { |
| collectProblems((IMember) children[i], processors, isLocalMember); |
| } |
| } |
| |
| private abstract static class JavadocMemberProcessor implements IMemberProcessor { |
| protected static final String GENERATED = "@generated"; //$NON-NLS-1$ |
| protected final String getJavadoc(IMember member) throws JavaModelException { |
| ISourceRange javadocRange = member.getJavadocRange(); |
| if (javadocRange == null) { |
| return ""; //$NON-NLS-1$ |
| } |
| return member.getCompilationUnit().getSource().substring(javadocRange.getOffset(), javadocRange.getLength() + javadocRange.getOffset()); |
| } |
| } |
| |
| public static class GeneratedTagEnsurer extends JavadocMemberProcessor { |
| public IStatus processMember(IMember member, boolean isLocalMember) throws JavaModelException { |
| if (isLocalMember) { |
| return null; |
| } |
| if (member.getElementType() == IJavaElement.INITIALIZER && member.getOccurrenceCount() > 1) { |
| return newViolation(member, "Multiple sibling initializers should be avoided in the generated code: they may not always be merged correctly"); |
| } |
| String javadoc = getJavadoc(member); |
| int index = javadoc.lastIndexOf(GENERATED); |
| if (index == -1) { |
| return newViolation(member, "@generated is missing"); |
| } |
| if (javadoc.indexOf(GENERATED) != index) { |
| return newViolation(member, "There is more than one @generated tag"); |
| } |
| String afterGenerated = javadoc.substring(index + GENERATED.length()).trim(); |
| String asteriskIgnored = afterGenerated.replace('*', ' ').trim(); |
| if (asteriskIgnored.length() == 0) { |
| return newViolation(member, "JavaDoc comment with @generated tag is not terminated properly"); |
| } |
| switch (asteriskIgnored.charAt(0)) { |
| case '@': |
| //Next tag starts here, thus @generated is OK |
| break; |
| case '/': |
| //Most likely, this is an end of a JavaDoc comment terminator. |
| //TODO: check this assumption using the content of afterGenerated |
| break; |
| default: |
| return newViolation(member, "@generated tag will be interpreted as @generated NOT"); |
| } |
| return null; |
| } |
| } |
| |
| private static class GeneratedTagAbsenceInLocalMembersEnsurer extends JavadocMemberProcessor { |
| public IStatus processMember(IMember member, boolean isLocalMember) throws JavaModelException { |
| if (!isLocalMember) { |
| return null; |
| } |
| String javadoc = getJavadoc(member); |
| if (javadoc.indexOf(GENERATED) != -1) { |
| return newViolation(member, "@generated tag in local members is misleading"); |
| } |
| return null; |
| } |
| } |
| |
| private static IStatus newViolation(IMember member, String description) { |
| return new Status(IStatus.ERROR, Plugin.getPluginID(), 0, MessageFormat.format("{0} has problem: {1}", member.getHandleIdentifier(), description), null); |
| } |
| } |