blob: bfbd890f5c414ccb11d06e9351690d4e09759285 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jst.jsp.ui.internal.validation;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.core.filebuffers.ITextFileBuffer;
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.IPath;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.content.IContentDescription;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jst.jsp.core.internal.contentproperties.JSPFContentProperties;
import org.eclipse.jst.jsp.core.internal.provisional.contenttype.ContentTypeIdForJSP;
import org.eclipse.jst.jsp.core.internal.validation.JSPContentValidator;
import org.eclipse.jst.jsp.ui.internal.Logger;
import org.eclipse.wst.html.core.internal.document.HTMLDocumentTypeConstants;
import org.eclipse.wst.html.core.internal.validate.HTMLValidationAdapterFactory;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.FileBufferModelManager;
import org.eclipse.wst.sse.core.internal.provisional.INodeAdapterFactory;
import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
import org.eclipse.wst.sse.core.internal.validate.ValidationAdapter;
import org.eclipse.wst.sse.ui.internal.reconcile.validator.ISourceValidator;
import org.eclipse.wst.validation.internal.ConfigurationManager;
import org.eclipse.wst.validation.internal.ProjectConfiguration;
import org.eclipse.wst.validation.internal.ValidationConfiguration;
import org.eclipse.wst.validation.internal.ValidationRegistryReader;
import org.eclipse.wst.validation.internal.core.Message;
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.xml.core.internal.document.DocumentTypeAdapter;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
import org.w3c.dom.Text;
/**
* Source validator for JSP content. This validator currently only validates
* html content in jsp.
*/
public class JSPContentSourceValidator extends JSPContentValidator implements ISourceValidator {
/*
* Most of this class was copied from the ISourceValidator aspects of
* html.ui's HTMLValidator
*/
private IDocument fDocument;
private boolean fEnableSourceValidation;
private IContentType fJSPFContentType = null;
public void connect(IDocument document) {
fDocument = document;
// special checks to see source validation should really execute
IFile file = null;
IStructuredModel model = null;
try {
model = StructuredModelManager.getModelManager().getExistingModelForRead(document);
if (model != null) {
String baseLocation = model.getBaseLocation();
// The baseLocation may be a path on disk or relative to the
// workspace root. Don't translate on-disk paths to
// in-workspace resources.
IPath basePath = new Path(baseLocation);
if (basePath.segmentCount() > 1) {
file = ResourcesPlugin.getWorkspace().getRoot().getFile(basePath);
/*
* If the IFile doesn't exist, make sure it's not returned
*/
if (!file.exists())
file = null;
}
}
}
finally {
if (model != null) {
model.releaseFromRead();
}
}
fEnableSourceValidation = (file != null && isBatchValidatorPreferenceEnabled(file) && shouldValidate(file) && fragmentCheck(file));
}
public void disconnect(IDocument document) {
fDocument = null;
}
/**
* This validate call is for the ISourceValidator partial document
* validation approach
*
* @param dirtyRegion
* @param helper
* @param reporter
* @see org.eclipse.wst.sse.ui.internal.reconcile.validator.ISourceValidator
*/
public void validate(IRegion dirtyRegion, IValidationContext helper, IReporter reporter) {
if (helper == null || fDocument == null || !fEnableSourceValidation)
return;
if ((reporter != null) && (reporter.isCancelled() == true)) {
throw new OperationCanceledException();
}
IStructuredModel model = StructuredModelManager.getModelManager().getExistingModelForRead(fDocument);
if (model == null)
return; // error
try {
IDOMDocument document = null;
if (model instanceof IDOMModel) {
document = ((IDOMModel) model).getDocument();
}
if (document == null || !hasHTMLFeature(document))
return; // ignore
ITextFileBuffer fb = FileBufferModelManager.getInstance().getBuffer(fDocument);
if (fb == null)
return;
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(fb.getLocation());
if (file == null || !file.exists())
return;
// this will be the wrong region if it's Text (instead of Element)
// we don't know how to validate Text
IndexedRegion ir = getCoveringNode(dirtyRegion); // model.getIndexedRegion(dirtyRegion.getOffset());
if (ir instanceof Text) {
while (ir != null && ir instanceof Text) {
// it's assumed that this gets the IndexedRegion to
// the right of the end offset
ir = model.getIndexedRegion(ir.getEndOffset());
}
}
if (ir instanceof INodeNotifier) {
INodeAdapterFactory factory = HTMLValidationAdapterFactory.getInstance();
ValidationAdapter adapter = (ValidationAdapter) factory.adapt((INodeNotifier) ir);
if (adapter == null)
return; // error
if (reporter != null) {
HTMLValidationReporter rep = null;
rep = getReporter(reporter, file, (IDOMModel) model);
rep.clear();
adapter.setReporter(rep);
Message mess = new LocalizedMessage(IMessage.LOW_SEVERITY, file.getFullPath().toString().substring(1));
reporter.displaySubtask(this, mess);
}
adapter.validate(ir);
}
}
finally {
if (model != null)
model.releaseFromRead();
}
}
private IndexedRegion getCoveringNode(IRegion dirtyRegion) {
IndexedRegion largestRegion = null;
if (fDocument instanceof IStructuredDocument) {
IStructuredDocumentRegion[] regions = ((IStructuredDocument) fDocument).getStructuredDocumentRegions(dirtyRegion.getOffset(), dirtyRegion.getLength());
largestRegion = getLargest(regions);
}
return largestRegion;
}
private IndexedRegion getLargest(IStructuredDocumentRegion[] sdRegions) {
if (sdRegions == null || sdRegions.length == 0)
return null;
IndexedRegion currentLargest = getCorrespondingNode(sdRegions[0]);
for (int i = 0; i < sdRegions.length; i++) {
if (!sdRegions[i].isDeleted()) {
IndexedRegion corresponding = getCorrespondingNode(sdRegions[i]);
if (currentLargest instanceof Text)
currentLargest = corresponding;
if (corresponding != null) {
if (!(corresponding instanceof Text)) {
if (corresponding.getStartOffset() <= currentLargest.getStartOffset() && corresponding.getEndOffset() >= currentLargest.getEndOffset())
currentLargest = corresponding;
}
}
}
}
return currentLargest;
}
protected IndexedRegion getCorrespondingNode(IStructuredDocumentRegion sdRegion) {
IStructuredModel sModel = StructuredModelManager.getModelManager().getExistingModelForRead(fDocument);
IndexedRegion indexedRegion = null;
try {
if (sModel != null)
indexedRegion = sModel.getIndexedRegion(sdRegion.getStart());
}
finally {
if (sModel != null)
sModel.releaseFromRead();
}
return indexedRegion;
}
private boolean hasHTMLFeature(IDOMDocument document) {
DocumentTypeAdapter adapter = (DocumentTypeAdapter) document.getAdapterFor(DocumentTypeAdapter.class);
if (adapter == null)
return false;
return adapter.hasFeature(HTMLDocumentTypeConstants.HTML);
}
private HTMLValidationReporter getReporter(IReporter reporter, IFile file, IDOMModel model) {
return new HTMLValidationReporter(this, reporter, file, model);
}
/**
* Gets current validation configuration based on current project (which
* is based on current document) or global configuration if project does
* not override
*
* @return ValidationConfiguration
*/
private ValidationConfiguration getValidationConfiguration(IFile file) {
ValidationConfiguration configuration = null;
if (file != null) {
IProject project = file.getProject();
if (project != null) {
try {
ProjectConfiguration projectConfiguration = ConfigurationManager.getManager().getProjectConfiguration(project);
configuration = projectConfiguration;
if (projectConfiguration == null || projectConfiguration.useGlobalPreference()) {
configuration = ConfigurationManager.getManager().getGlobalConfiguration();
}
}
catch (InvocationTargetException e) {
Logger.log(Logger.WARNING_DEBUG, e.getMessage(), e);
}
}
}
return configuration;
}
/**
* Checks if validator is enabled according in Validation preferences
*
* @param vmd
* @return
*/
private boolean isBatchValidatorPreferenceEnabled(IFile file) {
if (file == null) {
return true;
}
boolean enabled = true;
ValidationConfiguration configuration = getValidationConfiguration(file);
if (configuration != null) {
org.eclipse.wst.validation.internal.ValidatorMetaData metadata = ValidationRegistryReader.getReader().getValidatorMetaData(JSPContentValidator.class.getName());
if (metadata != null) {
if (!configuration.isBuildEnabled(metadata) && !configuration.isManualEnabled(metadata))
enabled = false;
}
}
return enabled;
}
/**
* Checks if file is a jsp fragment or not. If so, check if the fragment
* should be validated or not.
*
* @param file
* Assumes shouldValidate was already called on file so it
* should not be null and does exist
* @return false if file is a fragment and it should not be validated,
* true otherwise
*/
private boolean fragmentCheck(IFile file) {
// copied from JSPValidator
boolean shouldValidate = true;
// quick check to see if this is possibly a jsp fragment
if (getJSPFContentType().isAssociatedWith(file.getName())) {
// get preference for validate jsp fragments
boolean shouldValidateFragments = Boolean.valueOf(JSPFContentProperties.getProperty(JSPFContentProperties.VALIDATE_FRAGMENTS, file, true)).booleanValue();
/*
* if jsp fragments should not be validated, check if file is
* really jsp fragment
*/
if (!shouldValidateFragments) {
boolean isFragment = isFragment(file);
shouldValidate = !isFragment;
}
}
return shouldValidate;
}
/**
* Determines if file is jsp fragment or not (does a deep, indepth check,
* looking into contents of file)
*
* @param file
* assumes file is not null and exists
* @return true if file is jsp fragment, false otherwise
*/
private boolean isFragment(IFile file) {
// copied from JSPValidator
boolean isFragment = false;
InputStream is = null;
try {
IContentDescription contentDescription = file.getContentDescription();
// it can be null
if (contentDescription == null) {
is = file.getContents();
contentDescription = Platform.getContentTypeManager().getDescriptionFor(is, file.getName(), new QualifiedName[]{IContentDescription.CHARSET});
}
if (contentDescription != null) {
String fileCtId = contentDescription.getContentType().getId();
isFragment = (fileCtId != null && ContentTypeIdForJSP.ContentTypeID_JSPFRAGMENT.equals(fileCtId));
}
}
catch (IOException e) {
// ignore, assume it's invalid JSP
}
catch (CoreException e) {
// ignore, assume it's invalid JSP
}
finally {
// must close input stream in case others need it
if (is != null)
try {
is.close();
}
catch (Exception e) {
// not sure how to recover at this point
}
}
return isFragment;
}
private boolean shouldValidate(IFile file) {
// copied from JSPValidator
IResource resource = file;
do {
if (resource.isDerived() || resource.isTeamPrivateMember() || !resource.isAccessible() || resource.getName().charAt(0) == '.') {
return false;
}
resource = resource.getParent();
}
while ((resource.getType() & IResource.PROJECT) == 0);
return true;
}
/**
* Returns JSP fragment content type
*
* @return jspf content type
*/
private IContentType getJSPFContentType() {
// copied from JSPValidator
if (fJSPFContentType == null) {
fJSPFContentType = Platform.getContentTypeManager().getContentType(ContentTypeIdForJSP.ContentTypeID_JSPFRAGMENT);
}
return fJSPFContentType;
}
}