| /**
|
| * Copyright (c) 2013, 2018 Angelo ZERR and others.
|
| * All rights reserved. This program and the accompanying materials
|
| * are made available under the terms of the Eclipse Public License 2.0
|
| * which accompanies this distribution, and is available at
|
| * https://www.eclipse.org/legal/epl-2.0/
|
| *
|
| * SPDX-License-Identifier: EPL-2.0
|
| *
|
| * Contributors:
|
| * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
|
| */ |
| package org.eclipse.wst.json.core.internal.validation;
|
|
|
| import java.io.BufferedReader;
|
| import java.io.IOException;
|
| import java.io.InputStreamReader;
|
| import java.io.UnsupportedEncodingException;
|
| import java.util.List;
|
| import java.util.Locale;
|
| import java.util.Stack;
|
|
|
| import org.eclipse.core.resources.IFile;
|
| import org.eclipse.core.resources.IProject;
|
| import org.eclipse.core.resources.IResource;
|
| import org.eclipse.core.resources.IResourceProxy;
|
| import org.eclipse.core.resources.IResourceProxyVisitor;
|
| import org.eclipse.core.resources.IWorkspaceRoot;
|
| import org.eclipse.core.resources.ResourcesPlugin;
|
| import org.eclipse.core.runtime.CoreException;
|
| import org.eclipse.core.runtime.IPath;
|
| import org.eclipse.core.runtime.IProgressMonitor;
|
| import org.eclipse.core.runtime.NullProgressMonitor;
|
| import org.eclipse.core.runtime.Path;
|
| import org.eclipse.core.runtime.Platform;
|
| import org.eclipse.core.runtime.Preferences;
|
| import org.eclipse.core.runtime.content.IContentDescription;
|
| import org.eclipse.core.runtime.content.IContentType;
|
| import org.eclipse.osgi.util.NLS;
|
| import org.eclipse.wst.json.core.JSONCorePlugin;
|
| import org.eclipse.wst.json.core.contenttype.ContentTypeIdForJSON;
|
| import org.eclipse.wst.json.core.internal.JSONCoreMessages;
|
| import org.eclipse.wst.json.core.internal.Logger;
|
| import org.eclipse.wst.json.core.internal.parser.JSONLineTokenizer;
|
| import org.eclipse.wst.json.core.preferences.JSONCorePreferenceNames;
|
| import org.eclipse.wst.json.core.regions.JSONRegionContexts;
|
| import org.eclipse.wst.json.core.util.JSONUtil;
|
| import org.eclipse.wst.json.core.validation.AnnotationMsg;
|
| import org.eclipse.wst.json.core.validation.ISeverityProvider;
|
| import org.eclipse.wst.json.core.validation.JSONSyntaxValidatorHelper;
|
| import org.eclipse.wst.sse.core.StructuredModelManager;
|
| import org.eclipse.wst.sse.core.internal.document.DocumentReader;
|
| import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
|
| import org.eclipse.wst.validation.AbstractValidator;
|
| import org.eclipse.wst.validation.ValidationResult;
|
| import org.eclipse.wst.validation.ValidationState;
|
| 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.operations.LocalizedMessage;
|
| 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 JSONSyntaxValidator extends AbstractValidator implements
|
| IValidator, ISeverityProvider {
|
|
|
| /** The validation reporter */
|
| private IReporter fReporter;
|
|
|
| private IContentType jsonContentType;
|
|
|
| @Override
|
| public void cleanup(IReporter reporter) {
|
| jsonContentType = null;
|
| }
|
|
|
| @Override
|
| public void validate(IValidationContext helper, IReporter reporter)
|
| throws ValidationException {
|
| final String[] uris = helper.getURIs();
|
| IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
|
| if (uris.length > 0) {
|
| IFile currentFile = null;
|
|
|
| for (int i = 0; i < uris.length && !reporter.isCancelled(); i++) {
|
| // might be called with just project path?
|
| IPath path = new Path(uris[i]);
|
| if (path.segmentCount() > 1) {
|
| currentFile = root.getFile(path);
|
| if (shouldValidate(currentFile, true)) {
|
| validateFile(currentFile, reporter);
|
| }
|
| } else if (uris.length == 1) {
|
| validateProject(helper, reporter);
|
| }
|
| }
|
| } else
|
| validateProject(helper, reporter);
|
| }
|
|
|
| public ValidationResult validate(IResource resource, int kind,
|
| ValidationState state, IProgressMonitor monitor) {
|
| if (resource.getType() != IResource.FILE)
|
| return null;
|
| ValidationResult result = new ValidationResult();
|
| fReporter = result.getReporter(monitor);
|
| validateFile((IFile) resource, fReporter);
|
| return result;
|
| }
|
|
|
| /**
|
| * Convenience method for validating a resource and getting back the
|
| * reporter
|
| *
|
| * @param resource
|
| * The resource to be validated.
|
| * @param kind
|
| * The way the resource changed. It uses the same values as the
|
| * kind parameter in IResourceDelta.
|
| * @param state
|
| * A way to pass arbitrary, validator specific, data from one
|
| * invocation of a validator to the next, during the validation
|
| * phase. At the end of the validation phase, this object will be
|
| * cleared, thereby allowing any of this state information to be
|
| * garbaged collected.
|
| * @return the validator's reporter
|
| */
|
| public IReporter validate(IResource resource, int kind,
|
| ValidationState state) {
|
| validate(resource, kind, state, new NullProgressMonitor());
|
| return fReporter;
|
| }
|
|
|
| /**
|
| * Validates the given file. It will stream the contents of the file without
|
| * creating a model for the file; it will only use existing
|
| *
|
| * @param file
|
| * the file to validate
|
| * @param reporter
|
| * the reporter
|
| */
|
| private void validateFile(IFile file, IReporter reporter) {
|
| Message message = new LocalizedMessage(IMessage.LOW_SEVERITY, file
|
| .getFullPath().toString().substring(1));
|
| reporter.displaySubtask(JSONSyntaxValidator.this, message);
|
|
|
| JSONLineTokenizer tokenizer = null;
|
| try {
|
| IStructuredModel model = StructuredModelManager.getModelManager()
|
| .getExistingModelForRead(file);
|
| try {
|
| if (model == null) {
|
| tokenizer = new JSONLineTokenizer(new BufferedReader(
|
| new InputStreamReader(file.getContents(true),
|
| getCharset(file))));
|
| } else {
|
| tokenizer = new JSONLineTokenizer(new BufferedReader(
|
| new DocumentReader(model.getStructuredDocument())));
|
| }
|
| JSONSyntaxValidatorHelper.validate(tokenizer, reporter, this,
|
| this);
|
| } finally {
|
| if (model != null) {
|
| model.releaseFromRead();
|
| model = null;
|
| }
|
| }
|
| } catch (UnsupportedEncodingException e) {
|
| } catch (CoreException e) {
|
| } catch (IOException e) {
|
| }
|
| }
|
|
|
| private void validateProject(IValidationContext helper,
|
| final IReporter reporter) {
|
| // if uris[] length 0 -> validate() gets called for each project
|
| if (helper instanceof IWorkbenchContext) {
|
| IProject project = ((IWorkbenchContext) helper).getProject();
|
| IResourceProxyVisitor visitor = new IResourceProxyVisitor() {
|
| public boolean visit(IResourceProxy proxy) throws CoreException {
|
| if (shouldValidate(proxy)) {
|
| validateFile((IFile) proxy.requestResource(), reporter);
|
| }
|
| return true;
|
| }
|
| };
|
| try {
|
| // collect all jsp files for the project
|
| project.accept(visitor, IResource.DEPTH_INFINITE);
|
| } catch (CoreException e) {
|
| Logger.logException(e);
|
| }
|
| }
|
| }
|
|
|
| private boolean shouldValidate(IResourceProxy proxy) {
|
| if (proxy.isDerived()) {
|
| return false;
|
| }
|
| if (proxy.getType() == IResource.FILE) {
|
| String name = proxy.getName();
|
| if (name.toLowerCase(Locale.US).endsWith(".json")) { //$NON-NLS-1$
|
| return true;
|
| }
|
| }
|
| return shouldValidate(proxy.requestResource(), false);
|
| }
|
|
|
| private boolean shouldValidate(IResource file, boolean checkExtension) {
|
| if (file == null || !file.exists() || file.getType() != IResource.FILE)
|
| return false;
|
| if (checkExtension) {
|
| String extension = file.getFileExtension();
|
| if (extension != null
|
| && "json".endsWith(extension.toLowerCase(Locale.US))) //$NON-NLS-1$
|
| return true;
|
| }
|
|
|
| IContentDescription contentDescription = null;
|
| try {
|
| contentDescription = ((IFile) file).getContentDescription();
|
| if (contentDescription != null) {
|
| IContentType contentType = contentDescription.getContentType();
|
| return contentDescription != null
|
| && contentType.isKindOf(getJSONContentType());
|
| }
|
| } catch (CoreException e) {
|
| Logger.logException(e);
|
| }
|
| return false;
|
| }
|
|
|
| private IContentType getJSONContentType() {
|
| if (jsonContentType == null) {
|
| jsonContentType = Platform.getContentTypeManager().getContentType(
|
| ContentTypeIdForJSON.ContentTypeID_JSON); //$NON-NLS-1$
|
| }
|
| return jsonContentType;
|
| }
|
|
|
| private String getCharset(IFile file) {
|
| if (file != null && file.isAccessible()) {
|
| try {
|
| return file.getCharset(true);
|
| } catch (CoreException e) {
|
| }
|
| }
|
| return ResourcesPlugin.getEncoding();
|
| }
|
|
|
| @Override
|
| public int getSeverity(String preferenceName) {
|
| return getPluginPreference().getInt(preferenceName);
|
| }
|
|
|
| private Preferences getPluginPreference() {
|
| return JSONCorePlugin.getDefault().getPluginPreferences();
|
| }
|
| }
|