user439793
user439793

Reputation:

Implement validator in Eclipse

I am trying to implement validation in Eclipse. My work has many little projects that serve to customize our product for various customers. Since there are a ton of developers we are trying to find a way to enforce various standards and best practices.

For example, we have many XML configurations that we want to validate. The built-in validators ensure files are well-formed and follow a schema, but we would like to add validations such as checking that a Java class referenced in XML actually exists on the classpath. Another example is validating that a Java class implementing a certain interface does not have any object variables (i.e. the code needs to operate only on parameters and not maintain state).

It appears that there are two ways to add validation. The first is through a builder which adds markers. The second is through stand-alone validation. However, we are not actually building anything, and I have not found any useful tutorials or examples on validation (does not help that help.eclipse.org is currently being moved and is unavailable).

When I right-click a test project and select "validate" I get a message stating there was an error during validation, and my test message does not show up in the problem view. However, there are no errors in the Eclipse log. The host Eclipse shows nothing in the console. No exceptions logged anywhere, and no message. The project does have the required custom nature.

I was following these instructions but there is no code or fully functioning example, and Google has not been kind enough to fill in the blanks. Combined with the Eclipse help site being down right now, I am at a loss as to how to proceed.

plugin.xml:

<plugin>
  <extension name="My Validator" point="org.eclipse.wst.validation.validator"
             id="com.mycompany.pluginname.validator.MyValidator">
    <validator>
      <projectNature id="com.mycompany.pluginname.nature.MyNature"/>
      <helper class="org.eclipse.wst.validation.internal.operations.WorkbenchContext"/>
      <markerId markerIdValue="com.mycompany.pluginname.validator.DefaultMarker"/>
      <run class="com.mycompany.pluginname.validation.validator.MyValidator"/>
      <runStrategy project="true"/>
    </validator>
  </extension>
  <extension point="org.eclipse.core.resources.markers" name="My Validator"
             id="com.mycompany.pluginname.validator.DefaultMarker">
    <super type="org.eclipse.core.resources.problemmarker"/>
    <persistent value="true"/>
    <attribute name="owner"/>
    <attribute name="validationSeverity"/>
    <attribute name="targetObject"/>
    <attribute name="groupName"/>
    <attribute name="messageId"/>
  </extension>
</plugin>

Validator code:

package com.mycompany.pluginname.validation.validator;

import org.eclipse.core.resources.IProject;
import org.eclipse.wst.validation.internal.core.Message;
import org.eclipse.wst.validation.internal.core.ValidationException;
import org.eclipse.wst.validation.internal.operations.IWorkbenchContext;
import org.eclipse.wst.validation.internal.provisional.core.*;

import com.mycompany.pluginname.validation.plugin.ValidationPlugin;

@SuppressWarnings("restriction")
public class MyValidator
    implements IValidator {

  @Override
  public void cleanup(IReporter argReporter) {
    argReporter.removeAllMessages(this);
  }

  @Override
  public void validate(IValidationContext argContext, IReporter argReporter)
      throws ValidationException {
    String bundle = ValidationPlugin.getDefault().getTranslationsBundleName();
    IProject prj = ((IWorkbenchContext) argContext).getProject();
    String[] attributes =
        new String[] {"owner", "validationSeverity", "targetObject", "groupName", "messageId"};
    IMessage msg = new Message(bundle, IMessage.HIGH_SEVERITY, "test", attributes, prj);
    argReporter.addMessage(this, msg);
  }

}

I also find it odd that adding validation would require using restricted packages and interfaces. It also seems odd that it wants an IMessage rather than an IMarker.

I did look at Eclipse plugin with custom validation which seems to be oriented around creating a new editor, where I want to validate files regardless of the editor used (in fact I do not want to create an editor).

Edit: I updated to use the V2 framework, but nothing appears in the problem view. What am I doing wrong? Is there a tutorial somewhere that explains how this works? I was able to figure out the following, but obviously it is not correct:

  public ValidationResult validate(ValidationEvent argEvent, ValidationState argState,
      IProgressMonitor argMonitor) {
    final IResource resource = argEvent.getResource();
    final ValidationResult result = new ValidationResult();
    try {
      List<String> contents = Resources.readFile((IFile) resource);
      for (int i = 0; i < contents.size(); ++i) {
        int offset = contents.get(i).indexOf("bad_string");
        if (offset >= 0) {
          result.add(ValidatorMessage.create("Found bad string", resource));
          result.incrementError(1);
        }
      }
    }
    catch (Exception ex) {
      result.add(ValidatorMessage.create(ex.getMessage(), resource));
    }
    return result;
  }

I admit this is a stab in the dark: the documentation is not very descriptive and I have not found any tutorials on this V2 validator. Oh, I have a filter on this validator so it only receives specific XML files, which is why there is no input validation.

Also, since I am a pedant myself, I am using the old-style for loop there because I expect to show the line number with the error to the user. But obviously I am not quite there yet.

Another edit: here is the working code. The only issue is the squiggly is not on the correct line because the offset is from the start of the file, not the line. But it does work:

  public ValidationResult validate(ValidationEvent argEvent, ValidationState argState,
      IProgressMonitor argMonitor) {
    final IResource resource = argEvent.getResource();
    final ValidationResult result = new ValidationResult();
    try {
      List<String> contents = Resources.readFile((IFile) resource);
      int location = 0;
      for (int i = 0; i < contents.size(); ++i) {
        int offset = contents.get(i).indexOf(CONSTANT);
        if (offset >= 0) {
          ValidatorMessage vm = ValidatorMessage.create("Message", resource);
          vm.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
          vm.setAttribute(IMarker.SOURCE_ID, IMarker.PROBLEM);
          vm.setAttribute(IMarker.LINE_NUMBER, i + 1);
          vm.setAttribute(IMarker.CHAR_START, location + offset);
          vm.setAttribute(IMarker.CHAR_END, location + offset + CONSTANT.length());
          result.add(vm);
        }
        // TODO: account for different line endings.
        location += (line.length() + 2);
      }
    }
    catch (Exception ex) {
      ValidationPlugin.getDefault().warn(ex);
      result.add(ValidatorMessage.create(ex.toString(), resource));
    }
    return result;
  }

Plugin.xml:

  <extension name="My Validator" point="org.eclipse.wst.validation.validatorV2"
             id="com.company.plugin.validation.validator.MyValidator">
    <validator class="com.company.plugin.validation.validator.MyValidator">
      <include>
        <rules>
          <file type="file" name="FileName.xml"/>
        </rules>
      </include>
    </validator>
  </extension>

I actually found another SO question along these lines that corroborates what I found: Setting IMarker.CHAR_START and IMarker.CHAR_END attributes for IMarkers Annotations

Upvotes: 6

Views: 2685

Answers (1)

nitind
nitind

Reputation: 20023

sigh that document is very out of date. You should use the org.eclipse.wst.validation.validatorV2 extension point, extending the newer org.eclipse.wst.validation.AbstractValidator class.

Upvotes: 6

Related Questions