blob: b1ff7bfff7b3ad3b68b085f10b6fc47074b1f44c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.sling.ide.eclipse.internal.validation;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import org.apache.sling.ide.eclipse.core.internal.Activator;
import org.apache.sling.ide.log.Logger;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.wst.validation.ValidationResult;
import org.eclipse.wst.validation.ValidationState;
import org.eclipse.wst.validation.internal.core.ValidationException;
import org.eclipse.wst.validation.internal.provisional.core.IReporter;
import org.eclipse.wst.validation.internal.provisional.core.IValidationContext;
import org.eclipse.wst.xml.core.internal.validation.core.AbstractNestedValidator;
import org.eclipse.wst.xml.core.internal.validation.core.NestedValidatorContext;
import org.eclipse.wst.xml.core.internal.validation.eclipse.Validator;
@SuppressWarnings("restriction")
/** Almost the standard xml validator from WST with the following deviations - missing grammar is never marked - also .content.xml is
* validated (the default XML validator skips all files starting with ".") */
public class IgnoreMissingGrammarXmlValidator extends Validator {
private final String GET_FILE = "getFile"; //$NON-NLS-1$
private final String GET_PROJECT_FILES = "getAllFiles"; //$NON-NLS-1$
private final String GET_INPUTSTREAM = "inputStream"; //$NON-NLS-1$
@Override
protected void setupValidation(NestedValidatorContext context) {
super.setupValidation(context);
// always ignore missing grammar constraints being referenced in the XML
indicateNoGrammar = 0;
}
/** Perform the validation using version 2 of the validation framework. Copied from {@link AbstractNestedValidator#validate(IResource,
* int, ValidationState, IProgressMonitor). Cannot use original one as that skips resources starting with "." */
@Override
public ValidationResult validate(IResource resource, int kind, ValidationState state, IProgressMonitor monitor) {
ValidationResult result = new ValidationResult();
IFile file = null;
if (resource instanceof IFile)
file = (IFile) resource;
if (file != null && shouldValidate(file)) {
IReporter reporter = result.getReporter(monitor);
NestedValidatorContext nestedcontext = getNestedContext(state, false);
boolean teardownRequired = false;
if (nestedcontext == null) {
// validationstart was not called, so manually setup and tear down
nestedcontext = getNestedContext(state, true);
nestedcontext.setProject(file.getProject());
setupValidation(nestedcontext);
teardownRequired = true;
} else {
nestedcontext.setProject(file.getProject());
}
doValidate(file, null, result, reporter, nestedcontext);
if (teardownRequired)
teardownValidation(nestedcontext);
}
return result;
}
/*
* (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)
*
* Copied from {@link AbstractNestedValidator#validateInJob(IValidationContext context, IReporter reporter). Cannot use original one as
* that skips resources starting with "."
*/
@Override
public IStatus validateInJob(IValidationContext context, IReporter reporter) throws ValidationException {
NestedValidatorContext nestedcontext = new NestedValidatorContext();
setupValidation(nestedcontext);
String[] fileURIs = context.getURIs();
if (fileURIs != null && fileURIs.length > 0) {
int numFiles = fileURIs.length;
for (int i = 0; i < numFiles && !reporter.isCancelled(); i++) {
String fileName = fileURIs[i];
if (fileName != null) {
Object[] parms = { fileName };
IFile file = (IFile) context.loadModel(GET_FILE, parms);
if (file != null && shouldValidate(file)) {
nestedcontext.setProject(file.getProject());
// The helper may not have a file stored in it but may have an InputStream if being
// called from a source other than the validation framework such as an editor.
if (context.loadModel(GET_INPUTSTREAM) instanceof InputStream) {
doValidate(file, (InputStream) context.loadModel(GET_INPUTSTREAM), null, reporter, nestedcontext); // do we need
// the
// fileName?
// what is int
// ruleGroup?
} else {
doValidate(file, null, null, reporter, nestedcontext);
}
}
}
}
}
// TODO: Is this needed? Shouldn't the framework pass the complete list?
// Should I know that I'm validating a project as opposed to files?
else {
Object[] parms = { getValidatorID() };
Collection files = (Collection) context.loadModel(GET_PROJECT_FILES, parms);
// files can be null if they're outside of the workspace
if (files != null) {
Iterator iter = files.iterator();
while (iter.hasNext() && !reporter.isCancelled()) {
IFile file = (IFile) iter.next();
if (shouldValidate(file)) {
doValidate(file, null, null, reporter, nestedcontext);
}
}
}
}
teardownValidation(nestedcontext);
if (reporter.isCancelled())
return Status.CANCEL_STATUS;
return Status.OK_STATUS;
}
/** Call the original private method named validate with the same parameters from {@link AbstractNestedValidator} through reflection.
*
* @param file
* @param inputstream
* @param result
* @param reporter
* @param context */
private void doValidate(IFile file, InputStream inputstream, ValidationResult result, IReporter reporter,
NestedValidatorContext context) {
try {
Method method = AbstractNestedValidator.class.getDeclaredMethod("validate", IFile.class, InputStream.class,
ValidationResult.class,
IReporter.class, NestedValidatorContext.class);
method.setAccessible(true);
method.invoke(this, file, inputstream, result, reporter, context);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException
| SecurityException e) {
Logger logger = Activator.getDefault().getPluginLogger();
logger.error("Failed calling validate method on AbstractNestedValidator, probably WTP version is incompatible.", e);
}
}
/** Determine if a given file should be validated. Mostly copied from {@link AbstractNestedValidator#shouldValidate(...)} but will not
* skip {@code .content.xml} files.
*
* @param file The file that may be validated.
* @return True if the file should be validated, false otherwise. */
private static boolean shouldValidate(IFile file) {
IResource resource = file;
do {
if (resource.isDerived() || resource.isTeamPrivateMember() ||
!resource.isAccessible() || (resource.getName().charAt(0) == '.' && !".content.xml".equals(resource.getName()))) {
return false;
}
resource = resource.getParent();
} while ((resource.getType() & IResource.PROJECT) == 0);
return true;
}
}