| /******************************************************************************* |
| * Copyright (c) 2007, 2013 David Green and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * David Green - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.mylyn.internal.wikitext.ui.validation; |
| |
| import java.io.BufferedInputStream; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.io.Reader; |
| import java.io.StringWriter; |
| import java.lang.reflect.Method; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| |
| 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.IResourceDelta; |
| import org.eclipse.core.resources.IResourceDeltaVisitor; |
| import org.eclipse.core.resources.IResourceVisitor; |
| import org.eclipse.core.resources.IncrementalProjectBuilder; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.SubMonitor; |
| import org.eclipse.jface.text.Document; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.Region; |
| import org.eclipse.mylyn.internal.wikitext.ui.WikiTextUiPlugin; |
| import org.eclipse.mylyn.internal.wikitext.ui.editor.MarkupEditor; |
| import org.eclipse.mylyn.internal.wikitext.ui.editor.validation.ResourceMarkerMarkupValidator; |
| import org.eclipse.mylyn.wikitext.ui.WikiText; |
| import org.eclipse.mylyn.wikitext.validation.ValidationProblem; |
| import org.eclipse.osgi.util.NLS; |
| |
| /** |
| * A project builder that invokes validation on wikitext files |
| * |
| * @author David Green |
| */ |
| public class ValidationProjectBuilder extends IncrementalProjectBuilder { |
| |
| public static final String ID = "org.eclipse.mylyn.wikitext.ui.wikiTextValidationBuilder"; //$NON-NLS-1$ |
| |
| private static final IProject[] NO_PROJECTS = new IProject[0]; |
| |
| private static int CHECK_ANCESTORS; |
| |
| private static Method RESOURCE_DERIVED_WITH_FLAGS_METHOD; |
| static { |
| try { |
| CHECK_ANCESTORS = ((Integer) IResource.class.getDeclaredField("CHECK_ANCESTORS").get(null)).intValue(); //$NON-NLS-1$ |
| } catch (Exception e) { |
| CHECK_ANCESTORS = 0; |
| } |
| try { |
| RESOURCE_DERIVED_WITH_FLAGS_METHOD = IResource.class.getDeclaredMethod("isDerived", //$NON-NLS-1$ |
| new Class[] { int.class }); |
| } catch (Exception e) { |
| RESOURCE_DERIVED_WITH_FLAGS_METHOD = null; |
| } |
| } |
| |
| public ValidationProjectBuilder() { |
| } |
| |
| @Override |
| protected void clean(IProgressMonitor monitor) throws CoreException { |
| getProject().deleteMarkers(ValidationProblem.DEFAULT_MARKER_ID, true, IResource.DEPTH_INFINITE); |
| } |
| |
| @SuppressWarnings("rawtypes") |
| @Override |
| protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { |
| IProject project = getProject(); |
| IResourceDelta resourceDelta = getDelta(project); |
| |
| // find files that need validating. We do this first so that we can accurately represent progress |
| List<ValidationInfo> files = null; |
| if ((kind != IncrementalProjectBuilder.INCREMENTAL_BUILD && kind != IncrementalProjectBuilder.AUTO_BUILD) |
| || resourceDelta == null) { |
| files = collect(project, monitor); |
| } else { |
| files = collect(resourceDelta, monitor); |
| } |
| // validate if needed |
| if (files != null && !files.isEmpty() && !isInterrupted() && !monitor.isCanceled()) { |
| validate(files, monitor); |
| } |
| // be sure to cancel properly so that calling mechanisms will know |
| if (monitor.isCanceled()) { |
| throw new OperationCanceledException(); |
| } |
| return NO_PROJECTS; |
| } |
| |
| private static class ValidationInfo { |
| private final IFile file; |
| |
| private final String languageName; |
| |
| public ValidationInfo(IFile file, String languageName) { |
| this.file = file; |
| this.languageName = languageName; |
| } |
| } |
| |
| /** |
| * collect resources for validation from a resource delta |
| */ |
| private List<ValidationInfo> collect(IResourceDelta resourceDelta, final IProgressMonitor monitor) |
| throws CoreException { |
| final List<ValidationInfo> files = new ArrayList<>(); |
| |
| resourceDelta.accept(new IResourceDeltaVisitor() { |
| |
| public boolean visit(IResourceDelta delta) throws CoreException { |
| if (monitor.isCanceled()) { |
| throw new OperationCanceledException(); |
| } |
| if (isInterrupted()) { |
| return false; |
| } |
| IResource resource = delta.getResource(); |
| if (resource instanceof IFile) { |
| if ((delta.getKind() & (IResourceDelta.ADDED | IResourceDelta.CHANGED)) != 0) { |
| IFile file = (IFile) resource; |
| ValidationProjectBuilder.this.visit(files, file); |
| } |
| } else if (resource instanceof IContainer) { |
| if (filtered((IContainer) resource)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| }); |
| return files; |
| } |
| |
| private List<ValidationInfo> collect(IProject project, final IProgressMonitor monitor) throws CoreException { |
| final List<ValidationInfo> files = new ArrayList<>(); |
| project.accept(new IResourceVisitor() { |
| |
| public boolean visit(IResource resource) throws CoreException { |
| if (monitor.isCanceled()) { |
| throw new OperationCanceledException(); |
| } |
| if (isInterrupted()) { |
| return false; |
| } |
| if (resource instanceof IFile) { |
| IFile file = (IFile) resource; |
| ValidationProjectBuilder.this.visit(files, file); |
| } else if (resource instanceof IContainer) { |
| if (filtered((IContainer) resource)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| }); |
| return files; |
| } |
| |
| private boolean filtered(IContainer container) { |
| return false; |
| } |
| |
| private void visit(final List<ValidationInfo> files, IFile file) { |
| boolean derived = false; |
| // bug 258154 - avoid 3.4 API compilation dependency |
| if (RESOURCE_DERIVED_WITH_FLAGS_METHOD != null) { |
| try { |
| derived = ((Boolean) RESOURCE_DERIVED_WITH_FLAGS_METHOD.invoke(file, CHECK_ANCESTORS)); |
| } catch (Exception e) { |
| derived = file.isDerived(); |
| } |
| } else { |
| derived = file.isDerived(); |
| } |
| if (derived) { |
| return; |
| } |
| // only validate files for which there is a known language |
| String language = getMarkupLanguageForFile(file); |
| if (language != null) { |
| files.add(new ValidationInfo(file, language)); |
| } |
| } |
| |
| private void validate(List<ValidationInfo> files, IProgressMonitor monitor) throws CoreException { |
| if (files.isEmpty()) { |
| return; |
| } |
| final int factor = 1000; |
| monitor.beginTask(Messages.ValidationProjectBuilder_validationTask, files.size() * factor); |
| for (ValidationInfo file : files) { |
| if (monitor.isCanceled()) { |
| throw new OperationCanceledException(); |
| } |
| if (isInterrupted()) { |
| break; |
| } |
| validate(file, SubMonitor.convert(monitor, factor)); |
| } |
| monitor.done(); |
| } |
| |
| /** |
| * validate a single file |
| */ |
| private void validate(ValidationInfo file, IProgressMonitor monitor) throws CoreException { |
| int totalWork = 1000; |
| monitor.beginTask( |
| NLS.bind(Messages.ValidationProjectBuilder_validatingFileTask, new Object[] { file.file.getName() }), |
| totalWork); |
| ResourceMarkerMarkupValidator validator = new ResourceMarkerMarkupValidator(); |
| validator.setMarkupLanguage(WikiText.getMarkupLanguage(file.languageName)); |
| validator.setResource(file.file); |
| |
| if (validator.getMarkupLanguage() != null) { |
| StringWriter writer = new StringWriter(); |
| String charset = file.file.getCharset(); |
| try { |
| Reader reader = charset == null |
| ? new InputStreamReader(new BufferedInputStream(file.file.getContents())) |
| : new InputStreamReader(new BufferedInputStream(file.file.getContents()), charset); |
| try { |
| int c; |
| while ((c = reader.read()) != -1) { |
| writer.append((char) c); |
| } |
| } finally { |
| reader.close(); |
| } |
| } catch (IOException ioe) { |
| throw new CoreException(WikiTextUiPlugin.getDefault().createStatus(IStatus.ERROR, ioe)); |
| } |
| monitor.worked(totalWork / 2); |
| IDocument document = new Document(writer.toString()); |
| validator.validate(SubMonitor.convert(monitor, totalWork / 2), document, |
| new Region(0, document.getLength())); |
| } |
| monitor.done(); |
| } |
| |
| private String getMarkupLanguageForFile(IFile file) { |
| String language = MarkupEditor.getMarkupLanguagePreference(file); |
| if (language == null) { |
| language = WikiText.getMarkupLanguageNameForFilename(file.getName()); |
| } |
| return language; |
| } |
| } |