Writing Plug-Ins in C/C++ for Eclipse CDT
by Doug Schaefer and Sebastien Marineau-Mes


Listing One

<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<plugin
   id="org.eclipse.cdt.stylist"
   name="Stylist Plug-in"
   version="1.0.0"
   provider-name=""
   class="org.eclipse.cdt.stylist.StylistPlugin">

   <runtime>
      <library name="stylist.jar">
         <export name="*"/>
      </library>
   </runtime>
   <requires>
      <import plugin="org.eclipse.ui"/>
      <import plugin="org.eclipse.core.resources"/>
      <import plugin="org.eclipse.core.runtime.compatibility"/>
      <import plugin="org.eclipse.cdt.core"/>
   </requires>

   <extension
         id="publicDataChecker"
         name="Public Data Checker"
         point="org.eclipse.core.resources.builders">
      <builder>
         <run
               class="org.eclipse.cdt.stylist.PublicDataChecker">
         </run>
      </builder>
   </extension>
   <extension
         point="org.eclipse.ui.popupMenus">
      <objectContribution
            objectClass="org.eclipse.cdt.core.model.ICProject"
            id="org.eclipse.cdt.stylist.contribution1">
         <menu
               label="CDT Stylist"
               path="additions"
               id="org.eclipse.cdt.stylist.menu1">
            <separator
                  name="group1">
            </separator>
         </menu>
         <action
               label="Enable Public Data Checking"
               class="org.eclipse.cdt.stylist.popup.actions.
                                                    EnablePublicDataChecking"
               menubarPath="org.eclipse.cdt.stylist.menu1/group1"
               enablesFor="+"
               id="org.eclipse.cdt.stylist.newAction">
         </action>
      </objectContribution>
   </extension>
   <extension
         id="publicDataProblem"
         name="Illegal public data"
         point="org.eclipse.core.resources.markers">
      <super
            type="org.eclipse.core.resources.problemmarker">
      </super>
   </extension>
</plugin>


Listing Two

package org.eclipse.cdt.stylist.popup.actions;
import ...
public class EnablePublicDataChecking implements IObjectActionDelegate {

    /** Constructor for Action1. */
    public EnablePublicDataChecking() {
        super();
    }
    /** @see IObjectActionDelegate#setActivePart(IAction,IWorkbenchPart) */
    public void setActivePart(IAction action, IWorkbenchPart targetPart) {
    }
    IStructuredSelection selection;
    /** @see IActionDelegate#run(IAction) */
    public void run(IAction action) {
        if (selection == null)
            return;
        Iterator iter = selection.iterator();
        while (iter.hasNext())
            try {
                PublicDataChecker.addBuilder(((ICProject)iter.next()).
                                                              getProject());
                } catch (CoreException e) {
                // Report the error
            }
    }

    /**
     * @see IActionDelegate#selectionChanged(IAction, ISelection)
     */
    public void selectionChanged(IAction action, ISelection selection) {
        if (selection instanceof IStructuredSelection)
            this.selection = (IStructuredSelection)selection;
        else
            this.selection = null;
   }

}


Listing Three

package org.eclipse.cdt.stylist;
import ...

/** @see IncrementalProjectBuilder  */
public class PublicDataChecker extends IncrementalProjectBuilder {
    /**
     *
     */
    public PublicDataChecker() {
    }
    // These ids must match the ids in their respective extensions
    private static final String ID = StylistPlugin.ID + ".publicDataChecker";
    private static final String PROBLEM_ID = StylistPlugin.ID +
                                                       ".publicDataProblem";
    private static final String PROBLEM_MSG = "publicDataProblem";

    public static void addBuilder(IProject project) throws CoreException {
        IProjectDescription desc = project.getDescription();
        ICommand[] builders = desc.getBuildSpec();

        for (int i = 0; i <builders.length; ++i)
            if (ID.equals(builders[i].getBuilderName()))
                return;
        ICommand[] newBuilders = new ICommand[builders.length + 1];
        ICommand builder = desc.newCommand();
        builder.setBuilderName(ID);

        System.arraycopy(builders, 0, newBuilders, 0, builders.length);
        newBuilders[builders.length] = builder;
        desc.setBuildSpec(newBuilders);
        project.setDescription(desc, null);
    }
    /** @see IncrementalProjectBuilder#build */
    protected IProject [] build(int kind, Map args,
                          IProgressMonitor monitor) throws CoreException {
        IProject project = getProject();
        if (kind == FULL_BUILD) {
            project.accept(new IResourceProxyVisitor() {
                public boolean visit(IResourceProxy proxy)
                                                 throws CoreException {
                    if (proxy.getType() ==
                            IResource.FILE && proxy.getName().endsWith(".h"))
                        checkFile((IFile)proxy.requestResource());
                    return true;
                }
            }, 0);
        } else {
            IResourceDelta delta = getDelta(project);
            delta.accept(new IResourceDeltaVisitor() {
                public boolean visit(IResourceDelta delta)
                                             throws CoreException {
                    if (delta.getFlags() == IResourceDelta.CONTENT
                            && delta.getKind() != IResourceDelta.REMOVED)
                   {
                        IResource resource = delta.getResource();
                        if (resource.getType() ==
                                IResource.FILE &&
                                           resource.getName().endsWith(".h"))
                            checkFile((IFile)resource);
                    }
                    return true;
                }
            } );
        }
        return null;
    }
    private void checkFile(final IFile file) throws CoreException {
        ICElement cfile = CModelManager.getDefault().create(file, null);
        if (!(cfile instanceof ITranslationUnit))
            return;
        // Remove existing markers
        file.deleteMarkers(PROBLEM_ID, false, IResource.DEPTH_ZERO);

        // Look for the classes in this file
        ITranslationUnit unit = (ITranslationUnit)cfile;
        unit.accept(new ICElementVisitor(){
            public boolean visit(ICElement element) throws CoreException {
                if (element.getElementType() == ICElement.C_CLASS) {
                    checkClass(file, (IStructure)element);
                    // return false since checkClass already visited the kids
                    return false;
                }
                return true;
            }
        });
    }
    private void checkClass(final IFile file, IStructure cls)
                                                  throws CoreException {
        cls.accept(new ICElementVisitor() {
            public boolean visit(ICElement element) throws CoreException {
                if (element instanceof IField) {
                    IField field = (IField)element;
                    if (field.getVisibility() == ASTAccessVisibility.PUBLIC) {
                        IMarker marker = file.createMarker(PROBLEM_ID);
                        marker.setAttribute(IMarker.SEVERITY,
                                                   IMarker.SEVERITY_ERROR);
                        marker.setAttribute(IMarker.MESSAGE,
                            StylistPlugin.getResourceString(PROBLEM_MSG));
                        ISourceRange range = field.getSourceRange();
                        int start = range.getIdStartPos();
                        marker.setAttribute(IMarker.CHAR_START, start);
                        marker.setAttribute(IMarker.CHAR_END, start +
                                                       range.getIdLength());
                        marker.setAttribute(IMarker.LINE_NUMBER,
                                                       range.getStartLine());
                    }
                }
                return true;
            }
        });
    }

}



