/*******************************************************************************
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
*******************************************************************************/
package com.liferay.ide.xml.search.ui.validators;
import com.liferay.ide.project.core.ProjectCore;
import com.liferay.ide.project.core.ValidationPreferences;
import com.liferay.ide.project.core.ValidationPreferences.ValidationType;
import com.liferay.ide.xml.search.ui.LiferayXMLSearchUI;
import com.liferay.ide.xml.search.ui.XMLSearchConstants;
import java.util.List;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
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.ProjectScope;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.DefaultScope;
import org.eclipse.core.runtime.preferences.IScopeContext;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jdt.core.IType;
import org.eclipse.osgi.util.NLS;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.core.internal.validate.ValidationMessage;
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.IValidator;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMAttr;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.eclipse.wst.xml.search.core.properties.IPropertiesRequestor;
import org.eclipse.wst.xml.search.core.queryspecifications.IXMLQuerySpecification;
import org.eclipse.wst.xml.search.core.queryspecifications.XMLQuerySpecificationManager;
import org.eclipse.wst.xml.search.core.queryspecifications.requestor.IXMLSearchRequestor;
import org.eclipse.wst.xml.search.core.util.DOMUtils;
import org.eclipse.wst.xml.search.editor.references.IXMLReference;
import org.eclipse.wst.xml.search.editor.references.IXMLReferenceTo;
import org.eclipse.wst.xml.search.editor.references.IXMLReferenceToJava;
import org.eclipse.wst.xml.search.editor.references.validators.IXMLReferenceValidator;
import org.eclipse.wst.xml.search.editor.references.validators.IXMLReferenceValidator2;
import org.eclipse.wst.xml.search.editor.validation.IValidationResult;
import org.eclipse.wst.xml.search.editor.validation.LocalizedMessage;
import org.eclipse.wst.xml.search.editor.validation.ValidatorUtils;
import org.w3c.dom.Node;
/**
* @author Kuo Zhang
* @author Terry Jia
*/
@SuppressWarnings( "restriction" )
public class LiferayBaseValidator implements IXMLReferenceValidator, IXMLReferenceValidator2
{
public static final String MARKER_QUERY_ID = "querySpecificationId";
public static final String MESSAGE_METHOD_NOT_FOUND = Msgs.methodNotFound;
public static final String MESSAGE_PROPERTY_NOT_FOUND = Msgs.propertyNotFound;
public static final String MESSAGE_REFERENCE_NOT_FOUND = Msgs.referenceNotFound;
public static final String MESSAGE_RESOURCE_NOT_FOUND = Msgs.resourceNotFound;
public static final String MESSAGE_STATIC_VALUE_UNDEFINED = Msgs.staticValueUndefined;
public static final String MESSAGE_SYNTAX_INVALID = Msgs.syntaxInvalid;
public static final String MESSAGE_TYPE_HIERARCHY_INCORRECT = Msgs.typeHierarchyIncorrect;
public static final String MESSAGE_TYPE_NOT_FOUND = Msgs.typeNotFound;
protected static final String PREFERENCE_NODE_QUALIFIER = ProjectCore.getDefault().getBundle().getSymbolicName();
private static final String[] oldMarkerTypes =
{ "liferayPortletDescriptorMarker",
"liferayLayoutTplDescriptorMarker",
"liferayDisplayDescriptorMarker",
"liferayHookDescriptorMarker",
"portletDescriptorMarker"
};
private class ReferencedFileVisitor implements IResourceProxyVisitor
{
IFile retval = null;
IResource rootResource;
IXMLSearchRequestor searchRequestor;
public IFile getReferencedFile( IXMLSearchRequestor requestor, IResource rootResource )
{
this.searchRequestor = requestor;
this.rootResource = rootResource;
try
{
rootResource.accept( this, IContainer.EXCLUDE_DERIVED );
}
catch( CoreException e )
{
LiferayXMLSearchUI.logError( e );
}
return retval;
}
public boolean visit( IResourceProxy proxy )
{
try
{
if( proxy.getType() == IResource.FILE )
{
final IFile file = (IFile) proxy.requestResource();
if( searchRequestor.accept( file, rootResource ) )
{
IStructuredModel model = null;
try
{
model = StructuredModelManager.getModelManager().getModelForRead( file );
if( searchRequestor.accept( model ) )
{
retval = file;
return false;
}
}
finally
{
if( model != null )
{
model.releaseFromRead();
}
}
}
}
}
catch( Exception e )
{
return true;
}
return true;
}
}
class ReferencedPropertiesVisitor implements IResourceProxyVisitor
{
IPropertiesRequestor propertiesRequestor;
IFile retval = null;
IResource rootResource;
public IFile getReferencedFile( IPropertiesRequestor requestor, IResource rootResource )
{
this.propertiesRequestor = requestor;
this.rootResource = rootResource;
try
{
rootResource.accept( this, IContainer.EXCLUDE_DERIVED );
}
catch( CoreException e )
{
LiferayXMLSearchUI.logError( e );
}
return retval;
}
public boolean visit( IResourceProxy proxy )
{
try
{
if( proxy.getType() == IResource.FILE )
{
final IFile file = (IFile) proxy.requestResource();
if( propertiesRequestor.accept( file, rootResource ) )
{
retval = file;
return false;
}
}
}
catch( Exception e )
{
return true;
}
return true;
}
}
protected void addMessage(
IDOMNode node, IFile file, IValidator validator, IReporter reporter, boolean batchMode,
String messageText, int severity, String liferayPluginValidationType )
{
addMessage(
node, file, validator, reporter, batchMode, messageText, severity, liferayPluginValidationType, null );
}
protected void addMessage(
IDOMNode node, IFile file, IValidator validator, IReporter reporter, boolean batchMode,
String messageText, int severity, String liferayPluginValidationType, String querySpecificationId )
{
int startOffset = getStartOffset( node );
int length = node.getEndOffset() - startOffset;
final LocalizedMessage message =
createMessage( startOffset, length, messageText, severity, node.getStructuredDocument() );
if( message != null )
{
if( batchMode )
{
message.setTargetObject( file );
message.setAttribute( MARKER_QUERY_ID, querySpecificationId );
message.setAttribute(
XMLSearchConstants.LIFERAY_PLUGIN_VALIDATION_TYPE, liferayPluginValidationType );
reporter.addMessage( validator, message );
}
}
}
protected LocalizedMessage createMessage(
int start, int length, String messageText, int severity, IStructuredDocument structuredDocument )
{
return ValidatorUtils.createMessage( start, length, messageText, severity, structuredDocument );
}
protected void doValidate(
IXMLReference reference, IDOMNode node, IFile file, IValidator validator, IReporter reporter, boolean batchMode )
{
if( reference.isExpression() )
{
return; // reference expression is not used in Liferay-IDE
}
if( !validateSyntax( reference, node, file, validator, reporter, batchMode ) )
{
return; // if the syntax is incorrect, stop validating
}
final List<IXMLReferenceTo> refTos = reference.getTo();
// refTos.size() == 0 means the reference is only used for syntax validation, which is already done in last
// step, and multiple reference-tos are not used in Liferay-IDE
if( refTos == null || refTos.size() != 1 )
{
return;
}
final IXMLReferenceTo referenceTo = refTos.get( 0 );
switch( referenceTo.getType() )
{
case XML:
validateReferenceToXML( referenceTo, node, file, validator, reporter, batchMode );
break;
case JAVA:
validateReferenceToJava( referenceTo, node, file, validator, reporter, batchMode );
break;
case JAVA_METHOD:
validateReferenceToJavaMethod( referenceTo, node, file, validator, reporter, batchMode );
break;
case RESOURCE:
validateReferenceToResource( referenceTo, node, file, validator, reporter, batchMode );
break;
case PROPERTY:
validateReferenceToProperty( referenceTo, node, file, validator, reporter, batchMode );
break;
case STATIC:
validateReferenceToStatic( referenceTo, node, file, validator, reporter, batchMode );
break;
default:
return;
}
}
protected String getMessageText( ValidationType validationType, IXMLReferenceTo referenceTo, Node node, IFile file )
{
final String nodeValue = DOMUtils.getNodeValue( node );
final String textContent = nodeValue == null ? "" : nodeValue;
switch( validationType )
{
case SYNTAX_INVALID:
return NLS.bind( MESSAGE_SYNTAX_INVALID, textContent );
case TYPE_NOT_FOUND:
return NLS.bind( MESSAGE_TYPE_NOT_FOUND, textContent );
case METHOD_NOT_FOUND:
return NLS.bind( MESSAGE_METHOD_NOT_FOUND, textContent );
case TYPE_HIERARCHY_INCORRECT:
{
if( referenceTo != null && referenceTo.getType() == IXMLReferenceTo.ToType.JAVA && file != null )
{
IType[] superTypes = ( (IXMLReferenceToJava) referenceTo ).getExtends( node, file );
if( superTypes != null && superTypes.length > 0 )
{
StringBuilder sb = new StringBuilder();
for( IType type : superTypes )
{
sb.append( type.getFullyQualifiedName() );
sb.append( ", " );
}
final String superTypeNames = sb.toString().replaceAll( ", $", "" );
return NLS.bind( MESSAGE_TYPE_HIERARCHY_INCORRECT, textContent, superTypeNames );
}
}
}
case RESOURCE_NOT_FOUND:
return NLS.bind( MESSAGE_RESOURCE_NOT_FOUND, textContent );
case REFERENCE_NOT_FOUND:
final IFile referencedFile = getReferencedFile( referenceTo, node, file );
return NLS.bind( MESSAGE_REFERENCE_NOT_FOUND, textContent, referencedFile != null ? referencedFile.getName() : "" );
case PROPERTY_NOT_FOUND:
final IFile languagePropertiesFile = getReferencedFile( referenceTo, node, file );
return NLS.bind( MESSAGE_PROPERTY_NOT_FOUND, textContent, languagePropertiesFile != null
? languagePropertiesFile.getName() : "any resource files" );
case STATIC_VALUE_UNDEFINED:
return NLS.bind( MESSAGE_STATIC_VALUE_UNDEFINED, textContent );
}
return null;
}
protected String getMessageText( ValidationType validationType, Node node )
{
return getMessageText( validationType, null, node, null );
}
/**
* get the exactly file which contains the element referenced by another xml element
*/
protected IFile getReferencedFile( IXMLReferenceTo referenceTo, Node node, IFile file )
{
IXMLQuerySpecification querySpecification =
XMLQuerySpecificationManager.getDefault().getQuerySpecification( referenceTo.getQuerySpecificationId() );
if( !querySpecification.isMultiResource() )
{
IResource resource = querySpecification.getResource( node, file );
IXMLSearchRequestor requestor = querySpecification.getRequestor();
return new ReferencedFileVisitor().getReferencedFile( requestor, resource );
}
return null;
}
protected IScopeContext[] getScopeContexts( IProject project )
{
final ProjectScope projectScope = new ProjectScope( project );
return projectScope.getNode( PREFERENCE_NODE_QUALIFIER ).getBoolean(
ProjectCore.USE_PROJECT_SETTINGS, false ) ? new IScopeContext[] { projectScope,
InstanceScope.INSTANCE, DefaultScope.INSTANCE } : new IScopeContext[] { InstanceScope.INSTANCE,
DefaultScope.INSTANCE };
}
protected int getServerity( ValidationType validationType, IFile file )
{
final String liferayPluginValidationType = getLiferayPluginValidationType( validationType, file );
// get severity from users' settings
return Platform.getPreferencesService().getInt(
PREFERENCE_NODE_QUALIFIER, liferayPluginValidationType, IMessage.NORMAL_SEVERITY, getScopeContexts( file.getProject() ) );
}
protected int getStartOffset( IDOMNode node )
{
int nodeType = node.getNodeType();
switch( nodeType )
{
case Node.ATTRIBUTE_NODE:
return ( (IDOMAttr) node ).getValueRegionStartOffset();
}
return node.getStartOffset();
}
protected String getLiferayPluginValidationType( ValidationType validationType, IFile file )
{
return ValidationPreferences.getValidationPreferenceKey( file.getName(), validationType );
}
protected ValidationType getValidationType( IXMLReferenceTo referenceTo, int nbElements )
{
switch( referenceTo.getType() )
{
case XML:
return ValidationType.REFERENCE_NOT_FOUND;
case JAVA:
if( nbElements == -1 )
{
return ValidationType.TYPE_HIERARCHY_INCORRECT;
}
return ValidationType.TYPE_NOT_FOUND;
case JAVA_METHOD:
return ValidationType.METHOD_NOT_FOUND;
case RESOURCE:
return ValidationType.RESOURCE_NOT_FOUND;
case PROPERTY:
return ValidationType.PROPERTY_NOT_FOUND;
case STATIC:
return ValidationType.STATIC_VALUE_UNDEFINED;
default:
return null;
}
}
protected boolean isMultipleElementsAllowed( IDOMNode node, int nbElements )
{
return true;
}
@Override
public boolean isValidTarget( IProject project )
{
final String[] ignoreList =
LiferayXMLSearchUI.getDefault().getPreferenceStore().getString(
LiferayXMLSearchUI.PREF_KEY_IGNORE_PROJECTS_LIST ).split( "," );
for( String ignore : ignoreList )
{
if( ignore.trim().equals( project.getName() ) )
{
return false;
}
}
return true;
}
/**
* Subclasses override the method to set their own markers
*/
protected void setMarker( IValidator validator, IFile file )
{
}
@Override
public void validate( IXMLReference reference, IDOMNode node, IFile file,
IValidator validator, IReporter reporter, boolean batchMode )
{
if( reference != null )
{
setMarker( validator, file );
doValidate( reference, node, file, validator, reporter, batchMode );
}
// clean old marker types added in 2.2.0 but removed in 2.2.2
for( String type : oldMarkerTypes )
{
try
{
final IMarker[] markers =
file.findMarkers( LiferayXMLSearchUI.PLUGIN_ID + "." + type, false, IResource.DEPTH_ONE );
for( IMarker marker : markers )
{
try
{
marker.delete();
}
catch( CoreException e )
{
// best effort
}
}
}
catch( CoreException e )
{
// best effort
}
}
}
/**
* default implementation of all kinds of validation
*/
protected void validateReferenceToAllType( IXMLReferenceTo referenceTo, IDOMNode node,IFile file,
IValidator validator, IReporter reporter, boolean batchMode )
{
final String nodeValue = DOMUtils.getNodeValue( node );
final IValidationResult result =
referenceTo.getSearcher().searchForValidation( node, nodeValue, -1, -1, file, referenceTo );
if( result != null )
{
boolean addMessage = false;
int nbElements = result.getNbElements();
if( nbElements > 0 )
{
if( nbElements > 1 && !isMultipleElementsAllowed( node, nbElements ) )
{
addMessage = true;
}
}
else
{
addMessage = true;
}
if( addMessage )
{
ValidationType validationType = getValidationType( referenceTo, nbElements );
int severity = getServerity( validationType, file );
final String liferayPluginValidationType = getLiferayPluginValidationType( validationType, file );
if( severity != ValidationMessage.IGNORE )
{
final String messageText = getMessageText( validationType, referenceTo, node, file );
addMessage(
node, file, validator, reporter, batchMode, messageText, severity, liferayPluginValidationType,
referenceTo.getQuerySpecificationId() );
}
}
}
}
protected void validateReferenceToJava( IXMLReferenceTo referenceTo, IDOMNode node, IFile file,
IValidator validator, IReporter reporter, boolean batchMode )
{
validateReferenceToAllType( referenceTo, node, file, validator, reporter, batchMode );
}
protected void validateReferenceToJavaMethod(
IXMLReferenceTo referenceTo, IDOMNode node, IFile file, IValidator validator, IReporter reporter,
boolean batchMode )
{
validateReferenceToAllType( referenceTo, node, file, validator, reporter, batchMode );
}
protected void validateReferenceToProperty(
IXMLReferenceTo referenceTo, IDOMNode node, IFile file, IValidator validator, IReporter reporter,
boolean batchMode )
{
validateReferenceToAllType( referenceTo, node, file, validator, reporter, batchMode );
}
protected void validateReferenceToResource( IXMLReferenceTo referenceTo, IDOMNode node, IFile file,
IValidator validator, IReporter reporter, boolean batchMode )
{
validateReferenceToAllType( referenceTo, node, file, validator, reporter, batchMode );
}
protected void validateReferenceToStatic(
IXMLReferenceTo referenceTo, IDOMNode node, IFile file, IValidator validator, IReporter reporter,
boolean batchMode )
{
validateReferenceToAllType( referenceTo, node, file, validator, reporter, batchMode );
}
protected void validateReferenceToXML( IXMLReferenceTo referenceTo, IDOMNode node, IFile file,
IValidator validator, IReporter reporter, boolean batchMode )
{
validateReferenceToAllType( referenceTo, node, file, validator, reporter, batchMode );
}
protected boolean validateSyntax( IXMLReference reference, IDOMNode node, IFile file,
IValidator validator, IReporter reporter, boolean batchMode )
{
return true;
}
protected static class Msgs extends NLS
{
public static String propertyNotFound;
public static String referenceNotFound;
public static String resourceNotFound;
public static String staticValueUndefined;
public static String syntaxInvalid;
public static String typeHierarchyIncorrect;
public static String typeNotFound;
public static String methodNotFound;
static
{
initializeMessages( LiferayBaseValidator.class.getName(), Msgs.class );
}
}
}