Source Validation in the Structured Source Editor

(aka as-you-type validation)

by Phil Avery, IBM
2005-11-02

Table Of Contents
  1. What is it?
  2. End User Usage
  3. Extending the Source Validation Framework
  4. FooValidator example
  5. Source Validation vs. "batch" Workspace Validation
  6. (possible) future enhancements

 What is it?

Source validation is a way to communicate errors, warnings and other information to the user "as-you-type" in the Structured Source Editor (SSE).

The SSE component allows clients to contribute their own custom validation to this feature via extension point. All that's required is an implementation of IValidator.

 End User Usage

Create a new file.

To enable source validation, make sure you've checked the preference: Window > Preferences... > General > Text Editors > Structured Text Editors > Report problems as you type

Now type in the file. Depending on how your preferences are configured, this is done by drawing various colored "squiggles" where a suspected error appears in the editor.

These errors show up in the overview ruler as well. Clicking on these errors will move the cursor to that position in the editor.

If you hover the mouse over one of these squiggles or marks in the overview ruler, you'll get hover help with detailed info about the error/warning.

 Extending the Source Validation Framework

Clients can contribute their own custom validation to the source editor if they have an IValidator. All that's required is to specify the IValidator implementation, and for which content type/partitions it can operate.

Here's an example of how HTML Validator contributes to source validation via the extension point:

<extension
           point="org.eclipse.wst.sse.ui.sourcevalidation">
        <validator
              scope="total"
              class="org.eclipse.wst.validation.html.HTMLValidator"
              id="org.eclipse.wst.validation.htmlsourcevalidator">
           <contentTypeIdentifier
                 id="org.eclipse.wst.html.core.htmlsource">
              <partitionType
                    id="org.eclipse.wst.html.HTML_DEFAULT">
              </partitionType>
           </contentTypeIdentifier>
           <contentTypeIdentifier
                 id="org.eclipse.jst.jsp.core.jspsource">
              <partitionType
                    id="org.eclipse.wst.html.HTML_DEFAULT">
              </partitionType>
           </contentTypeIdentifier>
        </validator>
</extension>

So this example says "run the HTML Validator (a total scope validator) on files of HTML or JSP content type, on partitions of HTML_DEFAULT." Total scope means the validator analyzes the entire file every time it's invoked. This is used so the source validation framework can better optimize calls to this validator.

ISourceValidator

Since validating an entire document every time a user types is expensive, we've introduced the ISourceValidator interface.

First connect(IDocument) is called to give the validator a reference to the document being validated. The corresponding disconnect(IDocument) is called when the Editor is closed, the IDocument goes away, and the ISourceValidator won't be called anymore.

While connected to the Document, validate(IRegion, IValidationContext, IReporter) will be called whenever the document has a dirty region. The ISourceValidator can then optimize it's validation according to the dirty IRegion from the call.

 The FooValidator example

This is an example of a simple validator. It looks for the first occurrence of the string "foo" in the dirty region and underlines it.

First create a pluging project called sourcevalidator.test Make sure the bundle is a singleton, that is, the MANIFEST.MF file should contain this line:
Bundle-SymbolicName: sourcevalidator.test; singleton:=true

add this extension point description to the plugin.xml

	<extension point="org.eclipse.wst.sse.ui.sourcevalidation">
		<validator
			scope="total"
			class="sourcevalidator.test.FooValidator"
			id="sourcevalidator.test.FooValidator">
			<contentTypeIdentifier id="org.eclipse.core.runtime.xml">
				<partitionType id="org.eclipse.wst.xml.XML_DEFAULT">
				</partitionType>
			</contentTypeIdentifier>
		</validator>
	</extension>
		

create the class FooValidator.java

package sourcevalidator.test;

import java.util.Locale;

import org.eclipse.core.resources.IResource;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.wst.sse.ui.internal.reconcile.validator.ISourceValidator;
import org.eclipse.wst.validation.internal.core.Message;
import org.eclipse.wst.validation.internal.core.ValidationException;
import org.eclipse.wst.validation.internal.provisional.core.IMessage;
import org.eclipse.wst.validation.internal.provisional.core.IReporter;
import org.eclipse.wst.validation.internal.provisional.core.IValidationContext;
import org.eclipse.wst.validation.internal.provisional.core.IValidator;

public class FooValidator implements ISourceValidator, IValidator {
	
	protected class LocalizedMessage extends Message {

		private String _message = null;

		public LocalizedMessage(int severity, String messageText) {
			this(severity, messageText, null);
		}

		public LocalizedMessage(int severity, String messageText, IResource targetObject) {
			this(severity, messageText, (Object) targetObject);
		}

		public LocalizedMessage(int severity, String messageText, Object targetObject) {
			super(null, severity, null);
			setLocalizedMessage(messageText);
			setTargetObject(targetObject);
		}

		public void setLocalizedMessage(String message) {
			_message = message;
		}

		public String getLocalizedMessage() {
			return _message;
		}

		public String getText() {
			return getLocalizedMessage();
		}

		public String getText(ClassLoader cl) {
			return getLocalizedMessage();
		}

		public String getText(Locale l) {
			return getLocalizedMessage();
		}

		public String getText(Locale l, ClassLoader cl) {
			return getLocalizedMessage();
		}
	}
	
	IDocument fDocument = null;
	public void connect(IDocument document) {
		fDocument = document;
	}

	public void disconnect(IDocument document) {
		fDocument = null;
	}
	
	/**
	 * implement ISourceValidator
	 */
	public void validate(IRegion dirtyRegion, IValidationContext helper, IReporter reporter) {
		// look for foo
		int start = dirtyRegion.getOffset();
		int length = dirtyRegion.getLength();
		String dirtyText = fDocument.get().substring(start, start + length);
		
		int fooIndex = dirtyText.indexOf("foo");
		if(fooIndex != -1) {
			IMessage m = new LocalizedMessage(IMessage.HIGH_SEVERITY, "found foo");
			m.setOffset(start + fooIndex);
			m.setLength("foo".length());
			try {
				m.setLineNo(fDocument.getLineOfOffset(start + fooIndex) + 1);
			}
			catch (BadLocationException e) {
				m.setLineNo(-1);
			}
			reporter.addMessage(this, m);
		}
	}

	public void cleanup(IReporter reporter) {
		// TODO Auto-generated method stub
		
	}

	public void validate(IValidationContext helper, IReporter reporter) throws ValidationException {
		// TODO Auto-generated method stub
		
	} 
}

		

Now just start the workbench up and creat any *.xml file. Type "foo" and it should be underlined! Change it to "fo" and the squiggle should disappear.

 Source Editor Validation vs. Batch Workspace Validation

Source Validation is often confused with Batch Workspace Validation. These are two different frameworks. They're invoked and displayed differently in the workbench (even if they use the same IValidator under the covers).

Source validation can be thought of as "as-you-type" validation, as errors/warnings show up in the editor as you type.

Batch Validation only occurs on save or build, or when explicitly invoked via the context menu in the Project Explorer. These errors not only show up in the editor, but also in the Problems View, and as little overlay icons on resources in the navigator views.

Batch validation adds markers to the resources in your workspace. this means that errors, warnings, and info messages will appear in the Problems View

Batch validation errors cause an icon to appear in the left hand ruler

Batch validation errors appear in some Navigator Views as little error overlay icons

If the same IValidator is used for source and batch validation, if a Batch Validation error is corrected in the "live" source editor, the icon on the left hand ruler will gray out, indicating that the problem has (probably) been fixed. When you save the file, the error should then disappear if the problem was in fact fixed.

 Future Enhancements

Enablement Preferences

It would be useful to have ability to turn off certain validators. For example, maybe you want to turn off HTML validation for JSPs because you're only working on the JSP code sections at the moment.

"Partial Document" Validation

Partial document validation would be a great performance improvement (if a validator supports it).

Other possible source validation: