blob: 335bbf57052206c179b4f43117a9dada46988373 [file] [log] [blame]
/*******************************************************************************
* 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.JavaScriptCore;
import org.eclipse.wst.jsdt.core.compiler.IProblem;
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.internal.core.JavaModel;
import org.eclipse.wst.jsdt.internal.core.JavaModelManager;
import org.eclipse.wst.jsdt.internal.core.JavaProject;
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) {
IProject project = file.getProject();
JavaModel model = JavaModelManager.getJavaModelManager().getJavaModel();
JavaProject javaProject = (JavaProject) model.getJavaProject(project);
if (!javaProject.exists() || !javaProject.isOnIncludepath(file))
return false;
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);
parser.setProject(JavaScriptCore.create(file.getProject()));
JavaScriptUnit unit = (JavaScriptUnit) parser.createAST(new NullProgressMonitor());
if(unit.getProblems().length > 0){
for (IProblem problem : unit.getProblems()) {
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 > source.length ? source.length : offset;
int end = start;
// 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;
}
}