Bug 481344 - JSDT should support the use of sourcevalidation extension point

JSDT is made to support "org.eclipse.wst.sse.ui.sourcevalidation" extention point for As-You-Type validation.

Change-Id: I89d8d24b34fba84fd219ccc2df945cbee01efed3
Signed-off-by: Victor Rubezhny <vrubezhny@exadel.com>
diff --git a/bundles/org.eclipse.wst.jsdt.core/src/org/eclipse/wst/jsdt/internal/esprima/EsprimaParser.java b/bundles/org.eclipse.wst.jsdt.core/src/org/eclipse/wst/jsdt/internal/esprima/EsprimaParser.java
index 6f1cab2..18e86b5 100644
--- a/bundles/org.eclipse.wst.jsdt.core/src/org/eclipse/wst/jsdt/internal/esprima/EsprimaParser.java
+++ b/bundles/org.eclipse.wst.jsdt.core/src/org/eclipse/wst/jsdt/internal/esprima/EsprimaParser.java
@@ -236,6 +236,7 @@
 		String description = (String) error.getMember("description"); //$NON-NLS-1$
 		Number index = (Number) error.getMember("index"); //$NON-NLS-1$
 		Number line = (Number) error.getMember("lineNumber"); //$NON-NLS-1$
+		Number column = (Number) error.getMember("column"); //$NON-NLS-1$
 
 		char[] fileName = null;
 		if(unit != null){
@@ -248,10 +249,10 @@
 					0,
 					null,
 					ProblemSeverities.Error,
-					0,
-					0,
+					index.intValue(),
+					-1,
 					line.intValue(),
-					index.intValue());
+					column.intValue());
 		return result;
 	}
 
diff --git a/bundles/org.eclipse.wst.jsdt.ui/META-INF/MANIFEST.MF b/bundles/org.eclipse.wst.jsdt.ui/META-INF/MANIFEST.MF
index 08c662b..9d326b1 100644
--- a/bundles/org.eclipse.wst.jsdt.ui/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.wst.jsdt.ui/META-INF/MANIFEST.MF
@@ -3,7 +3,13 @@
 Bundle-Version: 2.0.0.qualifier
 Bundle-Activator: org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin
 Import-Package: com.ibm.icu.text,
- com.ibm.icu.util
+ com.ibm.icu.util,
+ org.eclipse.wst.sse.core.utils,
+ org.eclipse.wst.sse.ui.internal.reconcile.validator,
+ org.eclipse.wst.validation,
+ org.eclipse.wst.validation.internal.core,
+ org.eclipse.wst.validation.internal.operations,
+ org.eclipse.wst.validation.internal.provisional.core
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-Vendor: %providerName
@@ -128,7 +134,10 @@
  org.eclipse.ui.forms;bundle-version="[3.4.0,4.0.0)",
  org.eclipse.ui.navigator;bundle-version="[3.4.0,4.0.0)",
  org.eclipse.ui.navigator.resources;bundle-version="[3.4.0,4.0.0)",
- org.eclipse.wst.jsdt.manipulation;bundle-version="[1.0.200,2.0.0)"
+ org.eclipse.wst.jsdt.manipulation;bundle-version="[1.0.200,2.0.0)",
+ org.eclipse.wst.sse.core;bundle-version="1.1.1000",
+ org.eclipse.wst.sse.ui;bundle-version="1.3.500",
+ org.eclipse.core.filebuffers
 Eclipse-LazyStart: true
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Bundle-Localization: plugin
diff --git a/bundles/org.eclipse.wst.jsdt.ui/plugin.xml b/bundles/org.eclipse.wst.jsdt.ui/plugin.xml
index 5db8ccb..8c485ad 100644
--- a/bundles/org.eclipse.wst.jsdt.ui/plugin.xml
+++ b/bundles/org.eclipse.wst.jsdt.ui/plugin.xml
@@ -6012,4 +6012,22 @@
       </stylesheet>
    </extension>
    
+   
+    <!--======================================================================================-->
+    <!-- As you type validation                                                               -->
+    <!--======================================================================================-->
+
+    <extension point="org.eclipse.wst.sse.ui.sourcevalidation">
+        <validator
+            scope="total"
+            class="org.eclipse.wst.jsdt.internal.validation.JSDTSourceValidator"
+            id="org.eclipse.wst.jsdt.ui.internal.validation.jsdtsyntaxvalidator">
+            <contentTypeIdentifier
+                id="org.eclipse.wst.jsdt.core.jsSource">
+                <partitionType id="__dftl_partition_content_type" />
+                <partitionType id="__java_string" />
+            </contentTypeIdentifier>
+        </validator>
+    </extension>
+    
 </plugin>
diff --git a/bundles/org.eclipse.wst.jsdt.ui/src/org/eclipse/wst/jsdt/internal/ui/text/JavaCompositeReconcilingStrategy.java b/bundles/org.eclipse.wst.jsdt.ui/src/org/eclipse/wst/jsdt/internal/ui/text/JavaCompositeReconcilingStrategy.java
index 009efdb..bb905f4 100644
--- a/bundles/org.eclipse.wst.jsdt.ui/src/org/eclipse/wst/jsdt/internal/ui/text/JavaCompositeReconcilingStrategy.java
+++ b/bundles/org.eclipse.wst.jsdt.ui/src/org/eclipse/wst/jsdt/internal/ui/text/JavaCompositeReconcilingStrategy.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * Copyright (c) 2000, 2016 IBM Corporation and others.
  * 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
@@ -20,7 +20,8 @@
 import org.eclipse.ui.texteditor.ITextEditor;
 import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
 import org.eclipse.wst.jsdt.internal.ui.text.java.IProblemRequestorExtension;
-import org.eclipse.wst.jsdt.internal.ui.text.java.JavaReconcilingStrategy;
+//import org.eclipse.wst.jsdt.internal.ui.text.java.JavaReconcilingStrategy;
+import org.eclipse.wst.jsdt.internal.ui.text.java.JavascriptValidationStrategy;
 import org.eclipse.wst.jsdt.internal.ui.text.spelling.JavaSpellingReconcileStrategy;
 import org.eclipse.wst.jsdt.ui.text.IJavaScriptPartitions;
 
@@ -31,7 +32,8 @@
 public class JavaCompositeReconcilingStrategy  extends CompositeReconcilingStrategy {
 
 	private ITextEditor fEditor;
-	private JavaReconcilingStrategy fJavaStrategy;
+//	private JavaReconcilingStrategy fJavaStrategy;
+	private JavascriptValidationStrategy fJavascriptValidationStrategy;
 
 	/**
 	 * Creates a new Java reconciling strategy.
@@ -42,9 +44,11 @@
 	 */
 	public JavaCompositeReconcilingStrategy(ISourceViewer viewer, ITextEditor editor, String documentPartitioning) {
 		fEditor= editor;
-		fJavaStrategy= new JavaReconcilingStrategy(editor);
+//		fJavaStrategy= new JavaReconcilingStrategy(editor);
+		fJavascriptValidationStrategy = new JavascriptValidationStrategy(viewer); 
 		setReconcilingStrategies(new IReconcilingStrategy[] {
-			fJavaStrategy,
+//			fJavaStrategy,
+			fJavascriptValidationStrategy,
 			new JavaSpellingReconcileStrategy(viewer, EditorsUI.getSpellingService(), IJavaScriptPartitions.JAVA_PARTITIONING)
 		});
 	}
@@ -106,7 +110,7 @@
 	 * @param notify <code>true</code> if listeners should be notified
 	 */
 	public void notifyListeners(boolean notify) {
-		fJavaStrategy.notifyListeners(notify);
+//		fJavaStrategy.notifyListeners(notify);
 	}
 
 	/*
@@ -132,7 +136,7 @@
 	 * 
 	 */
 	public void aboutToBeReconciled() {
-		fJavaStrategy.aboutToBeReconciled();
-
+//		fJavaStrategy.aboutToBeReconciled();
+		fJavascriptValidationStrategy.aboutToBeReconciled();
 	}
 }
diff --git a/bundles/org.eclipse.wst.jsdt.ui/src/org/eclipse/wst/jsdt/internal/ui/text/java/JavascriptValidationStrategy.java b/bundles/org.eclipse.wst.jsdt.ui/src/org/eclipse/wst/jsdt/internal/ui/text/java/JavascriptValidationStrategy.java
new file mode 100644
index 0000000..877f140
--- /dev/null
+++ b/bundles/org.eclipse.wst.jsdt.ui/src/org/eclipse/wst/jsdt/internal/ui/text/java/JavascriptValidationStrategy.java
@@ -0,0 +1,280 @@
+/*******************************************************************************
+ * Copyright (c) 2016 RedHat, Inc.
+ * 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:
+ *  RedHat, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.jsdt.internal.ui.text.java;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.filebuffers.FileBuffers;
+import org.eclipse.core.filebuffers.ITextFileBuffer;
+import org.eclipse.core.filebuffers.ITextFileBufferManager;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.content.IContentType;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextUtilities;
+import org.eclipse.jface.text.reconciler.DirtyRegion;
+import org.eclipse.jface.text.reconciler.IReconcilingStrategy;
+import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
+import org.eclipse.wst.jsdt.ui.JavaScriptUI;
+import org.eclipse.wst.jsdt.ui.text.IJavaScriptPartitions;
+import org.eclipse.wst.sse.ui.internal.reconcile.validator.ValidatorBuilder;
+import org.eclipse.wst.sse.ui.internal.reconcile.validator.ValidatorMetaData;
+import org.eclipse.wst.sse.ui.internal.reconcile.validator.ValidatorStrategy;
+
+/**
+ * @author V.V. Rubezhny
+ *
+ */
+public class JavascriptValidationStrategy implements IReconcilingStrategy, IReconcilingStrategyExtension {
+	private static final boolean DEBUG_VALIDATORS = Boolean.TRUE.toString().equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.jsdt.ui/debug/reconcilerValidators")); //$NON-NLS-1$
+	private final String SSE_UI_ID = "org.eclipse.wst.sse.ui"; //$NON-NLS-1$
+
+	private IDocument fDocument;
+	private ValidatorStrategy fValidatorStrategy;
+	/**
+	 * true if as you type validation is enabled,
+	 * false otherwise
+	 * @TODO: do we need a preference for this???
+	 */
+	private boolean fValidationEnabled = true;
+	private ISourceViewer fSourceViewer;
+	
+	/**
+	 * @param editor
+	 */
+	public JavascriptValidationStrategy(ISourceViewer viewer) {
+		this.fSourceViewer = viewer;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#setDocument(org.eclipse.jface.text.IDocument)
+	 */
+	public void setDocument(IDocument document) {
+		this.fDocument = document;
+		if (getValidatorStrategy() != null) {
+			getValidatorStrategy().setDocument(document);
+		}
+	}
+
+	private IDocument getDocument() {
+		return fDocument;
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#reconcile(org.eclipse.jface.text.reconciler.DirtyRegion, org.eclipse.jface.text.IRegion)
+	 */
+	public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) {
+		reconcile(dirtyRegion);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#reconcile(org.eclipse.jface.text.IRegion)
+	 */
+	public void reconcile(IRegion partition) {
+		ITypedRegion[] partitions = computePartitioning(partition);
+		
+		// call the validator strategy once for each effected partition
+		DirtyRegion dirty = null;
+		for (int i = 0; i < partitions.length; i++) {
+			dirty = createDirtyRegion(partitions[i], DirtyRegion.INSERT);
+
+			// [source]validator (extension) for this partition
+			if (getValidatorStrategy() != null) {
+				getValidatorStrategy().reconcile(partitions[i], dirty);
+			}
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension#setProgressMonitor(org.eclipse.core.runtime.IProgressMonitor)
+	 */
+	public void setProgressMonitor(IProgressMonitor monitor) {
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension#initialReconcile()
+	 */
+	public void initialReconcile() {
+		int length = getDocument().getLength();
+		reconcile(new Region(0, length));
+	}
+	
+	/**
+	 * Called before reconciling is started.
+	 *
+	 * 
+	 */
+	public void aboutToBeReconciled() {
+		if (getValidatorStrategy() != null) {
+			getValidatorStrategy().beginProcessing();
+		}
+	}
+	
+	/**
+	 * @param dirtyRegion
+	 * @return
+	 */
+	protected ITypedRegion[] computePartitioning(IRegion dirtyRegion) {
+		int drOffset = dirtyRegion.getOffset();
+		int drLength = dirtyRegion.getLength();
+
+		return computePartitioning(drOffset, drLength);
+	}
+
+	
+	protected ITypedRegion[] computePartitioning(int drOffset, int drLength) {
+		ITypedRegion[] tr = new ITypedRegion[0];
+		IDocument doc = getDocument();
+		if (doc != null){
+			int docLength = doc.getLength();
+	
+			if (drOffset > docLength) {
+				drOffset = docLength;
+				drLength = 0;
+			}
+			else if (drOffset + drLength > docLength) {
+				drLength = docLength - drOffset;
+			}
+	
+			try {
+				// dirty region may span multiple partitions
+				tr = TextUtilities.computePartitioning(doc, getDocumentPartitioning(), drOffset, drLength, true);
+			}
+			catch (BadLocationException e) {
+				String info = "dr: [" + drOffset + ":" + drLength + "] doc: [" + docLength + "] "; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+				IStatus status= new Status(IStatus.ERROR, JavaScriptUI.ID_PLUGIN, IStatus.OK, info, e);
+				JavaScriptPlugin.getDefault().getLog().log(status);
+				tr = new ITypedRegion[0];
+			}
+		}
+		return tr;
+	}
+	
+	protected DirtyRegion createDirtyRegion(IRegion region, String type) {
+		return createDirtyRegion(region.getOffset(),  region.getLength(), type);
+	}
+	
+	protected DirtyRegion createDirtyRegion(int offset, int length, String type) {
+		DirtyRegion durty = null;
+		IDocument doc = getDocument();
+
+		if (doc != null) {
+			// safety for BLE
+			int docLen = doc.getLength();
+			if (offset > docLen) {
+				offset = docLen;
+				length = 0;
+			}
+			else if (offset + length >= docLen)
+				length = docLen - offset;
+			try {
+				durty = new DirtyRegion(offset, length, type, doc.get(offset, length));
+			}
+			catch (BadLocationException e) {
+				String info = "dr: [" + offset + ":" + length + "] doc: [" + docLen + "] "; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+				IStatus status= new Status(IStatus.ERROR, JavaScriptUI.ID_PLUGIN, IStatus.OK, info, e);
+				JavaScriptPlugin.getDefault().getLog().log(status);
+			}
+		}
+		return durty;
+	}
+	
+	protected String getDocumentPartitioning() {
+		return IJavaScriptPartitions.JAVA_PARTITIONING;
+	}
+	
+	protected ISourceViewer getTextViewer() {
+		return fSourceViewer;
+	}
+	
+	/**
+	 * @return Returns the ValidatorStrategy.
+	 */
+	protected ValidatorStrategy getValidatorStrategy() {
+		ValidatorStrategy validatorStrategy = null;
+		if (fValidatorStrategy == null && fValidationEnabled) {
+			if (getTextViewer() instanceof ISourceViewer) {
+				ISourceViewer viewer = (ISourceViewer) getTextViewer();
+				String contentTypeId = getContentType();
+
+				if (contentTypeId != null) {
+					validatorStrategy = new ValidatorStrategy(viewer, contentTypeId);
+					ValidatorBuilder vBuilder = new ValidatorBuilder();
+					ValidatorMetaData[] vmds = vBuilder.getValidatorMetaData(SSE_UI_ID);
+					List enabledValidators = new ArrayList(1);
+					/* if any "must" handle this content type, just add them */
+					boolean foundSpecificContentTypeValidators = false;
+					for (int i = 0; i < vmds.length; i++) {
+						if (vmds[i].mustHandleContentType(contentTypeId)) {
+							if (DEBUG_VALIDATORS) {
+								String info = contentTypeId + " using specific validator " + vmds[i].getValidatorId(); //$NON-NLS-1$
+								IStatus status= new Status(IStatus.INFO, JavaScriptUI.ID_PLUGIN, info);
+								JavaScriptPlugin.getDefault().getLog().log(status);
+							}
+							foundSpecificContentTypeValidators = true;
+							enabledValidators.add(vmds[i]);
+						}
+					}
+					if (!foundSpecificContentTypeValidators) {
+						for (int i = 0; i < vmds.length; i++) {
+							if (vmds[i].canHandleContentType(contentTypeId)) {
+								if (DEBUG_VALIDATORS) {
+									String info = contentTypeId + " using inherited(?) validator " + vmds[i].getValidatorId(); //$NON-NLS-1$
+									IStatus status= new Status(IStatus.INFO, JavaScriptUI.ID_PLUGIN, info);
+									JavaScriptPlugin.getDefault().getLog().log(status);
+								}
+								enabledValidators.add(vmds[i]);
+							}
+						}
+					}
+					for (int i = 0; i < enabledValidators.size(); i++) {
+						validatorStrategy.addValidatorMetaData((ValidatorMetaData) enabledValidators.get(i));
+					}
+				}
+			}
+			fValidatorStrategy = validatorStrategy;
+		} else if(fValidatorStrategy != null && fValidationEnabled) {
+			validatorStrategy = fValidatorStrategy;
+		}
+		return validatorStrategy;
+	}
+	
+	private String getContentType() {
+		IDocument doc = getDocument();
+		if (doc == null)
+			return null;
+		
+		ITextFileBufferManager textFileBufferManager = FileBuffers.getTextFileBufferManager();
+		if (textFileBufferManager != null) {
+			ITextFileBuffer textFileBuffer = textFileBufferManager.getTextFileBuffer(doc);
+			if (textFileBuffer != null && textFileBuffer.getLocation() != null) {
+				try {
+					IContentType ct = textFileBuffer.getContentType();
+					if (ct != null)
+						return ct.getId();
+				} catch (CoreException e) {
+					// Ignore;
+				}
+			}
+		}
+		return null;
+	}
+}
diff --git a/bundles/org.eclipse.wst.jsdt.ui/src/org/eclipse/wst/jsdt/internal/validation/JSDTSourceValidator.java b/bundles/org.eclipse.wst.jsdt.ui/src/org/eclipse/wst/jsdt/internal/validation/JSDTSourceValidator.java
new file mode 100644
index 0000000..7495366
--- /dev/null
+++ b/bundles/org.eclipse.wst.jsdt.ui/src/org/eclipse/wst/jsdt/internal/validation/JSDTSourceValidator.java
@@ -0,0 +1,312 @@
+/*******************************************************************************
+ * Copyright (c) 2016 RedHat, Inc.
+ * 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:
+ *  RedHat, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.jsdt.internal.validation;
+
+import java.util.Locale;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExecutableExtension;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Position;
+import org.eclipse.wst.jsdt.core.compiler.CategorizedProblem;
+import org.eclipse.wst.jsdt.core.dom.AST;
+import org.eclipse.wst.jsdt.core.dom.ASTParser;
+import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit;
+import org.eclipse.wst.jsdt.internal.compiler.problem.DefaultProblem;
+import org.eclipse.wst.jsdt.ui.JavaScriptUI;
+import org.eclipse.wst.sse.core.utils.StringUtils;
+import org.eclipse.wst.sse.ui.internal.reconcile.validator.ISourceValidator;
+import org.eclipse.wst.validation.AbstractValidator;
+import org.eclipse.wst.validation.ValidationResult;
+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.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.IValidatorJob;
+
+/**
+ * @author V.V. Rubezhny
+ *
+ */
+public class JSDTSourceValidator extends AbstractValidator implements IValidatorJob, IExecutableExtension, ISourceValidator {
+	private static final ASTParser parser = ASTParser.newParser(AST.JLS3);
+
+	private String[] fAdditionalContentTypesIDs = null;
+	private IDocument fDocument;
+	
+	static boolean shouldValidate(IFile file) {
+		IResource resource = file;
+		do {
+			if (resource.isDerived() || resource.isTeamPrivateMember() || !resource.isAccessible()
+					|| (resource.getName().charAt(0) == '.' && resource.getType() == IResource.FOLDER)) {
+				return false;
+			}
+			resource = resource.getParent();
+		} while ((resource.getType() & IResource.PROJECT) == 0);
+		return true;
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.wst.validation.internal.provisional.core.IValidator#cleanup(org.eclipse.wst.validation.internal.provisional.core.IReporter)
+	 */
+	public void cleanup(IReporter reporter) {
+		// Nothing to do
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.wst.validation.internal.provisional.core.IValidator#validate(org.eclipse.wst.validation.internal.provisional.core.IValidationContext, org.eclipse.wst.validation.internal.provisional.core.IReporter)
+	 */
+	public void validate(IValidationContext helper, IReporter reporter) throws ValidationException {
+		if (helper == null)
+			return;
+		if ((reporter != null) && (reporter.isCancelled() == true)) {
+			throw new OperationCanceledException();
+		}
+		String[] deltaArray = helper.getURIs();
+		if (deltaArray != null && deltaArray.length > 0) {
+			validateDelta(helper, reporter);
+		} else {
+			validateFull(helper, reporter);
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement, java.lang.String, java.lang.Object)
+	 */
+	public void setInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException {
+		fAdditionalContentTypesIDs = new String[0];
+		if (data instanceof String && data.toString().length() > 0) {
+			fAdditionalContentTypesIDs = StringUtils.unpack(data.toString());
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.wst.validation.internal.provisional.core.IValidatorJob#validateInJob(org.eclipse.wst.validation.internal.provisional.core.IValidationContext, org.eclipse.wst.validation.internal.provisional.core.IReporter)
+	 */
+	public IStatus validateInJob(IValidationContext helper, IReporter reporter) throws ValidationException {
+		IStatus status = Status.OK_STATUS;
+		validate(helper, reporter);
+		return status;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.wst.validation.internal.provisional.core.IValidatorJob#getSchedulingRule(org.eclipse.wst.validation.internal.provisional.core.IValidationContext)
+	 */
+	public ISchedulingRule getSchedulingRule(IValidationContext helper) {
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.wst.sse.ui.internal.reconcile.validator.ISourceValidator#connect(org.eclipse.jface.text.IDocument)
+	 */
+	public void connect(IDocument document) {
+		fDocument = document;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.wst.sse.ui.internal.reconcile.validator.ISourceValidator#disconnect(org.eclipse.jface.text.IDocument)
+	 */
+	public void disconnect(IDocument document) {
+		fDocument = null;
+	}
+
+	/* (non-Javadoc)
+	 * 
+ 	 * This validate call is for the ISourceValidator partial document validation approach
+ 	 * 
+	 * @see org.eclipse.wst.sse.ui.internal.reconcile.validator.ISourceValidator#validate(org.eclipse.jface.text.IRegion, org.eclipse.wst.validation.internal.provisional.core.IValidationContext, org.eclipse.wst.validation.internal.provisional.core.IReporter)
+	 */
+	public void validate(IRegion dirtyRegion, IValidationContext helper, IReporter reporter) {
+		if (helper == null || fDocument == null)
+			return;
+
+		if ((reporter != null) && (reporter.isCancelled() == true)) {
+			throw new OperationCanceledException();
+		}
+
+		// TODO Do region validation here
+	}
+
+	/**
+	 */
+	private void validateContainer(IValidationContext helper, IReporter reporter, IContainer container) {
+		try {
+			IResource[] resourceArray = container.members(false);
+			for (int i = 0; i < resourceArray.length; i++) {
+				IResource resource = resourceArray[i];
+				if (resource == null || reporter.isCancelled())
+					continue;
+
+				if (resource instanceof IFile) {
+					Message message = new LocalizedMessage(IMessage.LOW_SEVERITY, resource.getFullPath().toString()
+							.substring(1));
+					reporter.displaySubtask(this, message);
+					validateFile(helper, reporter, (IFile) resource, null);
+				} else if (resource instanceof IContainer) {
+					validateContainer(helper, reporter, (IContainer) resource);
+				}
+			}
+		}
+		catch (CoreException ex) {
+		}
+	}
+	
+	/**
+	 */
+	private void validateDelta(IValidationContext helper, IReporter reporter) {
+		String[] deltaArray = helper.getURIs();
+		for (int i = 0; i < deltaArray.length; i++) {
+			String delta = deltaArray[i];
+			if (delta == null)
+				continue;
+
+			if (reporter != null) {
+				Message message = new LocalizedMessage(IMessage.LOW_SEVERITY, delta.substring(1));
+				reporter.displaySubtask(this, message);
+			}
+
+			IResource resource = getResource(delta);
+			if (resource == null || !(resource instanceof IFile))
+				continue;
+			validateFile(helper, reporter, (IFile) resource, null);
+		}
+	}
+
+	/**
+	 * @param result 
+	 */
+	private void validateFile(IValidationContext helper, IReporter reporter, IFile file, ValidationResult result) {
+		if ((reporter != null) && (reporter.isCancelled() == true)) {
+			throw new OperationCanceledException();
+		}
+		if (!shouldValidate(file)) {
+			return;
+		}
+
+		char[] source = fDocument.get().toCharArray();
+		if (source == null) {
+			return;
+		}
+		
+		parser.setSource(source);
+		JavaScriptUnit unit = (JavaScriptUnit) parser.createAST(new NullProgressMonitor());
+		if(unit.getProblems().length > 0){
+			CategorizedProblem[] resourceProblems = (CategorizedProblem[]) unit.getProblems();
+			for (CategorizedProblem problem : resourceProblems) {
+
+				final String msg = problem.getMessage();
+				String[] arguments = problem.getArguments();
+				int severity = problem.isError() ? IMessage.HIGH_SEVERITY : IMessage.ERROR_AND_WARNING;
+				int lineNumber = problem.getSourceLineNumber();
+				int sourceStart = problem.getSourceStart();
+				int sourceEnd = problem.getSourceEnd();
+				int columnNumber = (problem instanceof DefaultProblem) ? ((DefaultProblem)problem).getSourceColumnNumber() : -1;
+				
+				Message valMessage = new Message(JavaScriptUI.ID_PLUGIN, severity, JavaScriptUI.ID_PLUGIN + ".problem" ) {
+					/**
+					 * @see IMessage#getText(Locale, ClassLoader)
+					 */
+					public java.lang.String getText(Locale locale, ClassLoader classLoader) {
+						return msg;
+					}
+
+				};
+				Position position = sourceEnd == -1 ? calcPosition(source, sourceStart) : new Position(sourceStart, sourceEnd - sourceStart);
+				
+				valMessage.setOffset(position.getOffset());
+				valMessage.setLength(position.getLength());
+				valMessage.setLineNo(lineNumber);
+//				System.out.println(getClass().getName() + ": " + valMessage.getLineNumber() + 
+//						"[" + valMessage.getOffset() + ":" + valMessage.getLength() + "] : " + 
+//						valMessage.getText() + 
+//						"==>>>" + String.copyValueOf(source, position.getOffset(), position.getLength()) + "<<<==");
+				reporter.addMessage(this, valMessage);
+			}
+		}
+	}
+
+	private Position calcPosition(char[] source, int offset) {
+		int start = offset;
+		int end = offset;
+
+		// Search forward
+		while (source.length > end && 
+				(Character.isLetterOrDigit(source[end]) || source[end] == '_')) {
+			end++;
+		}
+		if (end == start) {
+			// search backward
+			
+			// skip end of statement && spaces
+			boolean acceptSemiColon = true;
+			while (end > 0 && 
+					((acceptSemiColon && source[end - 1] == ';') ||
+					Character.isWhitespace(source[end - 1]))) {
+				if (source[end - 1] == ';') {
+					acceptSemiColon = false;
+				}
+				end--;
+			}
+			start = end;
+			while (start > 0 && 
+					(Character.isLetterOrDigit(source[start - 1]) || source[start - 1] == '_')) {
+				start--;
+			}
+		}
+		
+		return new Position(start, end - start);
+	}
+
+	/**
+	 */
+	private void validateFull(IValidationContext helper, IReporter reporter) {
+		IProject project = null;
+		String[] fileDelta = helper.getURIs();
+		if (helper instanceof IWorkbenchContext) {
+			IWorkbenchContext wbHelper = (IWorkbenchContext) helper;
+			project = wbHelper.getProject();
+		} else if (fileDelta.length > 0) {
+			// won't work for project validation (b/c nothing in file delta)
+			project = getResource(fileDelta[0]).getProject();
+		}
+		
+		if (project == null)
+			return;
+		validateContainer(helper, reporter, project);
+	}
+
+	/*
+	 * added to get rid or dependency on IWorkbenchHelper
+	 */
+	public IResource getResource(String delta) {
+		Path path = new Path(delta);
+		if (path.segmentCount() > 1)
+			return ResourcesPlugin.getWorkspace().getRoot().getFile(path);
+		if (path.segmentCount() == 1)
+			return ResourcesPlugin.getWorkspace().getRoot().getProject(delta);
+		return null;
+	}
+}
diff --git a/bundles/org.eclipse.wst.jsdt.ui/src/org/eclipse/wst/jsdt/internal/validation/LocalizedMessage.java b/bundles/org.eclipse.wst.jsdt.ui/src/org/eclipse/wst/jsdt/internal/validation/LocalizedMessage.java
new file mode 100644
index 0000000..a36f9cf
--- /dev/null
+++ b/bundles/org.eclipse.wst.jsdt.ui/src/org/eclipse/wst/jsdt/internal/validation/LocalizedMessage.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2016 RedHat, Inc.
+ * 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:
+ *  RedHat, Inc. - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.wst.jsdt.internal.validation;
+
+import java.util.Locale;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.wst.validation.internal.core.Message;
+
+/**
+ * copied from org.eclipse.wst.validation.internal.operations.LocalizedMessage
+ * 
+ * This class is provided for validators which run only in Eclipse and whose messages, because they
+ * come from another tool, are already localized. LocalizedMessage cannot be used by any validator
+ * which needs to run in both WebSphere and Eclipse.
+ */
+public 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();
+	}
+}