/******************************************************************************* 
 * Copyright (c) 2010 Red Hat, Inc. 
 * Distributed under license by Red Hat, Inc. All rights reserved. 
 * This program is 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: 
 * Red Hat, Inc. - initial API and implementation 
 ******************************************************************************/

package org.jboss.tools.cdi.internal.core.validation;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaConventions;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
import org.jboss.tools.cdi.core.CDIConstants;
import org.jboss.tools.cdi.core.CDICorePlugin;
import org.jboss.tools.cdi.core.IClassBean;
import org.jboss.tools.cdi.core.IDecorator;
import org.jboss.tools.cdi.core.IInterceptor;
import org.jboss.tools.cdi.core.IStereotype;
import org.jboss.tools.cdi.core.preferences.CDIPreferences;
import org.jboss.tools.cdi.xml.beans.model.CDIBeansConstants;
import org.jboss.tools.common.EclipseUtil;
import org.jboss.tools.common.model.XModelObject;
import org.jboss.tools.common.model.impl.XModelObjectImpl;
import org.jboss.tools.common.model.util.EclipseJavaUtil;
import org.jboss.tools.common.model.util.EclipseResourceUtil;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * beans.xml validator
 * 
 * @author Alexey Kazakov
 */
public class BeansXmlValidationDelegate extends CDICoreValidationDelegate {

	private AlternativeClassValidator alternativeClassValidator;
	private AlternativeStereotypeValidator alternativeStereotypeValidator;
	private DecoratorTypeValidator decoratorTypeValidator;
	private InterceptorTypeValidator interceptorTypeValidator;

	public BeansXmlValidationDelegate(CDICoreValidator validator) {
		super(validator);
	}

	private AlternativeClassValidator getAlternativeClassValidator() {
		if(alternativeClassValidator==null) {
			alternativeClassValidator = new AlternativeClassValidator();
		}
		return alternativeClassValidator;
	}

	private AlternativeStereotypeValidator getAlternativeStereotypeValidator() {
		if(alternativeStereotypeValidator==null) {
			alternativeStereotypeValidator = new AlternativeStereotypeValidator();
		}
		return alternativeStereotypeValidator;
	}

	private DecoratorTypeValidator getDecoratorTypeValidator() {
		if(decoratorTypeValidator==null) {
			decoratorTypeValidator = new DecoratorTypeValidator();
		}
		return decoratorTypeValidator;
	}

	private InterceptorTypeValidator getInterceptorTypeValidator() {
		if(interceptorTypeValidator==null) {
			interceptorTypeValidator = new InterceptorTypeValidator();
		}
		return interceptorTypeValidator;
	}

	public void validateBeansXml(CDICoreValidator.CDIValidationContext context, IFile beansXml) {
		XModelObject f = EclipseResourceUtil.createObjectForResource(beansXml);
		String xmodelpath = (f == null) ? "" : f.getPath();

		IModelManager manager = StructuredModelManager.getModelManager();
		if(manager == null) {
			// this may happen if plug-in org.eclipse.wst.sse.core 
			// is stopping or un-installed, that is Eclipse is shutting down.
			// there is no need to report it, just stop validation.
			return;
		}

		IStructuredModel model = null;
		try {
			model = manager.getModelForRead(beansXml);
			if (model instanceof IDOMModel) {
				IDOMModel domModel = (IDOMModel) model;
				IDOMDocument document = domModel.getDocument();

				/*
				 * 5.1.1. Declaring selected alternatives for a bean archive
				 *  - Each child <class> element must specify the name of an alternative bean class. If there is no class with the specified
				 *    name, or if the class with the specified name is not an alternative bean class, the container automatically detects the problem
				 *    and treats it as a deployment problem.
				 *  - If the same type is listed twice under the <alternatives> element, the container automatically detects the problem and
				 *    treats it as a deployment problem.
				 */
				validateTypeBeanForBeansXml(context, getAlternativeClassValidator(), document, beansXml, xmodelpath + "/" + CDIBeansConstants.NODE_ALTERNATIVES); //$NON-NLS-1$

				/*
				 * 5.1.1. Declaring selected alternatives for a bean archive
				 *  - Each child <stereotype> element must specify the name of an @Alternative stereotype annotation. If there is no annotation
				 *    with the specified name, or the annotation is not an @Alternative stereotype, the container automatically detects the
				 *    problem and treats it as a deployment problem.
				 *  - If the same type is listed twice under the <alternatives> element, the container automatically detects the problem and
				 *    treats it as a deployment problem. 
				 */
				validateTypeBeanForBeansXml(context, getAlternativeStereotypeValidator(), document,	beansXml, xmodelpath + "/" + CDIBeansConstants.NODE_ALTERNATIVES); //$NON-NLS-1$

				/*
				 * 8.2. Decorator enablement and ordering
				 *  - Each child <class> element must specify the name of a decorator bean class. If there is no class with the specified name,
				 *    or if the class with the specified name is not a decorator bean class, the container automatically detects the problem and
				 *    treats it as a deployment problem.
				 *  - If the same class is listed twice under the <decorators> element, the container automatically detects the problem and
				 *    treats it as a deployment problem.
				 */
				validateTypeBeanForBeansXml(context, getDecoratorTypeValidator(), document, beansXml, xmodelpath + "/" + CDIBeansConstants.NODE_DECORATORS); //$NON-NLS-1$

				/*
				 * 9.4. Interceptor enablement and ordering
				 * 	- Each child <class> element must specify the name of an interceptor class. If there is no class with the specified name, or if
				 * 	  the class with the specified name is not an interceptor class, the container automatically detects the problem and treats it as
				 * 	  a deployment problem.
				 *  - If the same class is listed twice under the <interceptors> element, the container automatically detects the problem and treats it as
				 *    a deployment problem.
				 */
				validateTypeBeanForBeansXml(context, getInterceptorTypeValidator(), document, beansXml, xmodelpath + "/" + CDIBeansConstants.NODE_INTERCEPTORS); //$NON-NLS-1$
			}
		} catch (CoreException e) {
			CDICorePlugin.getDefault().logError(e);
        } catch (IOException e) {
        	CDICorePlugin.getDefault().logError(e);
		} finally {
			if (model != null) {
				model.releaseFromRead();
			}
		}
	}

	private void validateTypeBeanForBeansXml(CDICoreValidator.CDIValidationContext context, TypeValidator typeValidator, IDOMDocument document, IFile beansXml, String xmodelpath) {
		try {
			NodeList parentNodeList = document.getElementsByTagName(typeValidator.getParrentElementname());
			for (int i = 0; i < parentNodeList.getLength(); i++) {
				Node parentNode = parentNodeList.item(i);
				if(parentNode instanceof Element) {
					List<TypeNode> typeNodes = getTypeElements((Element)parentNode, typeValidator.getTypeElementName());
					Map<String, TypeNode> uniqueTypes = new HashMap<String, TypeNode>();
					for (TypeNode typeNode : typeNodes) {
						String typepath = xmodelpath;
						String attr = null;
						if(typeNode.getTypeName() != null) {
							typepath = typepath + "/" + typeNode.getTypeName(); //$NON-NLS-1$
							attr = typeValidator.getTypeElementName();
						}
						IType type = getType(beansXml, typeNode, typeValidator, typepath, attr);
						if(type!=null) {
							if(!validator.isAsYouTypeValidation() && !type.isBinary()) {
								validator.getValidationContext().addLinkedCoreResource(CDICoreValidator.SHORT_ID, beansXml.getFullPath().toOSString(), type.getPath(), false);
								Set<IPath> relatedResources = new HashSet<IPath>();
								IResource resource = type.getResource();
								if(resource instanceof IFile) {
									validator.collectAllRelatedInjectionsForBean((IFile)resource, relatedResources);
									for (IPath path : relatedResources) {
										validator.getValidationContext().addLinkedCoreResource(CDICoreValidator.SHORT_ID, path.toOSString(), beansXml.getFullPath(), false);
									}
								}
							}
							String typeError = typeValidator.validateType(context, type);
							if(typeError != null) {
								IMarker marker = validator.addProblem(typeValidator.getIllegalTypeErrorMessage(), CDIPreferences.ILLEGAL_TYPE_NAME_IN_BEANS_XML,
										new String[]{typeNode.getTypeName()}, typeNode.getLength(), typeNode.getStartOffset(), beansXml, typeValidator.getIllegalTypeErrorMessageId());
								if(marker != null) bindMarkerToModel(marker, typepath, typeValidator.getTypeElementName());
								if(type.isBinary()) {
									continue;
								}
							}
							TypeNode node = uniqueTypes.get(typeNode.getTypeName());
							if(node!=null) {
								if(!node.isMarkedAsDuplicated()) {
									IMarker marker = validator.addProblem(typeValidator.getDuplicateTypeErrorMessage(), CDIPreferences.DUPLICATE_TYPE_IN_BEANS_XML,
											new String[]{}, node.getLength(), node.getStartOffset(), beansXml);
									if(marker != null) bindMarkerToModel(marker, typepath, typeValidator.getTypeElementName());
								}
								node.setMarkedAsDuplicated(true);
								typeNode.setMarkedAsDuplicated(true);
								typeNode.setDuplicationIndex(node.getDuplicationIndex() + 1);
								IMarker marker = validator.addProblem(typeValidator.getDuplicateTypeErrorMessage(), CDIPreferences.DUPLICATE_TYPE_IN_BEANS_XML,
										new String[]{}, typeNode.getLength(), typeNode.getStartOffset(), beansXml);
								if(marker != null) {
									int di = typeNode.getDuplicationIndex();
									if(di > 0) {
										typepath += XModelObjectImpl.DUPLICATE + di;
									}
									bindMarkerToModel(marker, typepath, typeValidator.getTypeElementName());
								}
							}
							uniqueTypes.put(typeNode.getTypeName(), typeNode);
						}
					}
				}
			}
		} catch (JavaModelException e) {
			CDICorePlugin.getDefault().logError(e);
        }
	}

	private Map<IProject, IJavaProject> javaProjects;

	public IJavaProject getJavaProject(IResource resource) {
		if(javaProjects == null) {
			javaProjects = new HashMap<IProject, IJavaProject>();
		}
		IProject project = resource.getProject();
		if(project.isAccessible()) {
			IJavaProject javaProject = javaProjects.get(project);
			if(javaProject==null) {
				javaProject = EclipseUtil.getJavaProject(project);
				if(javaProject!=null) {
					javaProjects.put(project, javaProject);
				}
			}
			return javaProject;
		}
		return null;
	}

	private IType getType(IFile beansXml, TypeNode node, TypeValidator typeValidator, String xmodelpath, String attr) {
		IType type = null;
		String typeName = node.getTypeName();
		if(typeName!=null && typeName.trim().length() > 0) {
			try {
				IJavaProject javaProject = getJavaProject(beansXml);
				if(javaProject!=null) {
					type = EclipseJavaUtil.findType(javaProject, typeName);
				}
			} catch (JavaModelException e) {
				CDICorePlugin.getDefault().logError(e);
				return null;
			}
		} else {
			IMarker marker = validator.addProblem(typeValidator.getEmptyTypeErrorMessage(), CDIPreferences.ILLEGAL_TYPE_NAME_IN_BEANS_XML,
					new String[]{node.getTypeName()}, node.getLength(), node.getStartOffset(), beansXml, typeValidator.getUnknownTypeErrorMessageId());
			bindMarkerToModel(marker, xmodelpath, attr);
			return null;
		}
		if(type==null) {
			addLinkedResourcesForUnknownType(beansXml, node.getTypeName());
			IMarker marker = validator.addProblem(typeValidator.getUnknownTypeErrorMessage(), CDIPreferences.ILLEGAL_TYPE_NAME_IN_BEANS_XML,
					new String[]{node.getTypeName()}, node.getLength(), node.getStartOffset(), beansXml, typeValidator.getUnknownTypeErrorMessageId());
			bindMarkerToModel(marker, xmodelpath, attr);
		}
		return type;
	}

	private void bindMarkerToModel(IMarker marker, String path, String attribute) {
		try {
			if(marker!=null) {
				marker.setAttribute("path", path); //$NON-NLS-1$
				if(attribute != null) {
					marker.setAttribute("attribute", attribute); //$NON-NLS-1$
				}
			}
		} catch(CoreException e) {
			CDICorePlugin.getDefault().logError(e);
		}
	}

	private void addLinkedResourcesForUnknownType(IFile beansXml, String typeName) {
		if(!validator.isAsYouTypeValidation() && typeName!=null && typeName.trim().length()>0) {
			IStatus status = JavaConventions.validateJavaTypeName(typeName, CompilerOptions.VERSION_1_7, CompilerOptions.VERSION_1_7);
			if(status.getSeverity()!=IStatus.ERROR) {
				String packagePath = typeName.replace('.', '/');
				Set<IFolder> sources = EclipseResourceUtil.getSourceFolders(beansXml.getProject());
				for (IFolder source : sources) {
					IPath path = source.getFullPath().append(packagePath + ".java"); //$NON-NLS-1$
					validator.getValidationContext().addLinkedCoreResource(CDICoreValidator.SHORT_ID, beansXml.getFullPath().toOSString(), path, false);
				}
			}
		}
	}

	private List<TypeNode> getTypeElements(Element parentElement, String typeElementName) {
		List<TypeNode> result = new ArrayList<TypeNode>();
		NodeList list = parentElement.getElementsByTagName(typeElementName);
		for (int i = 0; i < list.getLength(); i++) {
			Node classNode = list.item(i);
			NodeList children = classNode.getChildNodes();

			boolean empty = true;
			for (int j = 0; j < children.getLength(); j++) {
				Node node = children.item(j);
				if(node.getNodeType() == Node.TEXT_NODE) {
					String value = node.getNodeValue();
					if(value!=null) {
						String className = value.trim();
						if(className.length()==0) {
							continue;
						}
						empty = false;
						if(node instanceof IndexedRegion) {
							int start = ((IndexedRegion)node).getStartOffset() + value.indexOf(className);
							int length = className.length();
							result.add(new TypeNode(start, length, className));
							break;
						}
					}
				}
			}

			if(empty && classNode instanceof IndexedRegion) {
				int start = ((IndexedRegion)classNode).getStartOffset();
				int end = ((IndexedRegion)classNode).getEndOffset();
				int length = end - start;
				result.add(new TypeNode(start, length, null));
			}
		}
		return result;
	}

	private static class TypeNode {
		private int startOffset;
		private int length;
		private String typeName;
		private boolean markedAsDuplicated;
		private int duplicationIndex = 0;

		public TypeNode(int startOffset, int length, String typeName) {
			this.startOffset = startOffset;
			this.length = length;
			this.typeName = typeName;
		}

		public int getStartOffset() {
			return startOffset;
		}

		public void setStartOffset(int startOffset) {
			this.startOffset = startOffset;
		}

		public int getLength() {
			return length;
		}

		public void setLength(int length) {
			this.length = length;
		}

		/**
		 * Returns type name or null if value in XML is empty or whitespace.
		 * @return
		 */
		public String getTypeName() {
			return typeName;
		}

		public void setTypeName(String typeName) {
			this.typeName = typeName;
		}

		public boolean isMarkedAsDuplicated() {
			return markedAsDuplicated;
		}

		public void setMarkedAsDuplicated(boolean markedAsDuplicated) {
			this.markedAsDuplicated = markedAsDuplicated;
		}
	
		public int getDuplicationIndex() {
			return duplicationIndex;
		}

		public void setDuplicationIndex(int i) {
			duplicationIndex = i;
		}
	}

	private static interface TypeValidator {

		String validateType(CDICoreValidator.CDIValidationContext context, IType type) throws JavaModelException;

		String getTypeElementName();

		String getParrentElementname();

		String getEmptyTypeErrorMessage();

		String getUnknownTypeErrorMessage();
		
		int getUnknownTypeErrorMessageId();

		String getIllegalTypeErrorMessage();
		
		int getIllegalTypeErrorMessageId();

		String getDuplicateTypeErrorMessage();
	}

	private abstract class AbstractTypeValidator implements TypeValidator {

		@Override
		public String getTypeElementName() {
			return "class"; //$NON-NLS-1$
		}

		@Override
		public String validateType(CDICoreValidator.CDIValidationContext context, IType type) throws JavaModelException {
			if(!validateKindOfType(type)) {
				return getIllegalTypeErrorMessage();
			}
			if(type.isBinary()) {			
				if(!validateBinaryType(type)) {
					return getIllegalTypeErrorMessage();
				}
			} else if(!validateSourceType(context, type)) {
				return getIllegalTypeErrorMessage();
			}
			return null;
		}
		
		abstract public boolean validateSourceType(CDICoreValidator.CDIValidationContext context, IType type);

		/**
		 * Validates if the type represens class/annotation/...
		 * @param type
		 * @return
		 * @throws JavaModelException 
		 */
		public boolean validateKindOfType(IType type) throws JavaModelException {
			return type.isClass();
		}

		public boolean validateBinaryType(IType type) throws JavaModelException {
			IAnnotation[] annotations = type.getAnnotations();
			for (IAnnotation annotation : annotations) {
				if(annotation.getElementName().equals(getAnnotationName())) {
					return true;
				}
			}
			return false;
		}

		protected abstract String getAnnotationName();
	}

	private class AlternativeClassValidator extends AbstractTypeValidator {

		@Override
		public boolean validateSourceType(CDICoreValidator.CDIValidationContext context, IType type) {
			IClassBean classBean = context.getCdiProject().getBeanClass(type);
			return classBean!=null && classBean.isAlternative();
		}

		@Override
		public String getParrentElementname() {
			return "alternatives"; //$NON-NLS-1$
		}

		@Override
		public String getEmptyTypeErrorMessage() {
			return CDIValidationMessages.EMPTY_ALTERNATIVE_BEAN_CLASS_NAME;
		}

		@Override
		public String getUnknownTypeErrorMessage() {
			return CDIValidationMessages.UNKNOWN_ALTERNATIVE_BEAN_CLASS_NAME;
		}

		@Override
		public int getUnknownTypeErrorMessageId() {
			return CDIValidationErrorManager.UNKNOWN_ALTERNATIVE_BEAN_CLASS_NAME_ID;
		}

		@Override
		public String getIllegalTypeErrorMessage() {
			return CDIValidationMessages.ILLEGAL_ALTERNATIVE_BEAN_CLASS;
		}

		@Override
		public int getIllegalTypeErrorMessageId() {
			return CDIValidationErrorManager.ILLEGAL_ALTERNATIVE_BEAN_CLASS_ID;
		}

		@Override
		public String getDuplicateTypeErrorMessage() {
			return CDIValidationMessages.DUPLICATE_ALTERNATIVE_TYPE;
		}

		@Override
		protected String getAnnotationName() {
			return CDIConstants.ALTERNATIVE_ANNOTATION_TYPE_NAME;
		}
	}

	private class AlternativeStereotypeValidator extends AbstractTypeValidator {

		@Override
		public boolean validateSourceType(CDICoreValidator.CDIValidationContext context, IType type) {
			IStereotype stereotype = context.getCdiProject().getStereotype(type);
			return stereotype!=null && stereotype.isAlternative();
		}

		@Override
		public boolean validateKindOfType(IType type) throws JavaModelException {
			return type.isAnnotation();
		}

		@Override
		public String getTypeElementName() {
			return "stereotype"; //$NON-NLS-1$
		}

		@Override
		public String getParrentElementname() {
			return "alternatives"; //$NON-NLS-1$
		}

		@Override
		public String getEmptyTypeErrorMessage() {
			return CDIValidationMessages.EMPTY_ALTERNATIVE_ANNOTATION_NAME;
		}

		@Override
		public String getUnknownTypeErrorMessage() {
			return CDIValidationMessages.UNKNOWN_ALTERNATIVE_ANNOTATION_NAME;
		}

		@Override
		public int getUnknownTypeErrorMessageId() {
			return CDIValidationErrorManager.UNKNOWN_ALTERNATIVE_ANNOTATION_NAME_ID;
		}

		@Override
		public String getIllegalTypeErrorMessage() {
			return CDIValidationMessages.ILLEGAL_ALTERNATIVE_ANNOTATION;
		}

		@Override
		public int getIllegalTypeErrorMessageId() {
			return CDIValidationErrorManager.ILLEGAL_ALTERNATIVE_ANNOTATION_ID;
		}

		@Override
		public String getDuplicateTypeErrorMessage() {
			return CDIValidationMessages.DUPLICATE_ALTERNATIVE_TYPE;
		}

		@Override
		protected String getAnnotationName() {
			return CDIConstants.ALTERNATIVE_ANNOTATION_TYPE_NAME;
		}
	}

	private class DecoratorTypeValidator extends AbstractTypeValidator {

		@Override
		public boolean validateSourceType(CDICoreValidator.CDIValidationContext context, IType type) {
			IClassBean classBean = context.getCdiProject().getBeanClass(type);
			return classBean instanceof IDecorator;
		}

		@Override
		public String getParrentElementname() {
			return "decorators"; //$NON-NLS-1$
		}

		@Override
		public String getEmptyTypeErrorMessage() {
			return CDIValidationMessages.EMPTY_DECORATOR_BEAN_CLASS_NAME;
		}

		@Override
		public String getUnknownTypeErrorMessage() {
			return CDIValidationMessages.UNKNOWN_DECORATOR_BEAN_CLASS_NAME;
		}

		@Override
		public int getUnknownTypeErrorMessageId() {
			return CDIValidationErrorManager.UNKNOWN_DECORATOR_BEAN_CLASS_NAME_ID;
		}

		@Override
		public String getIllegalTypeErrorMessage() {
			return CDIValidationMessages.ILLEGAL_DECORATOR_BEAN_CLASS;
		}

		@Override
		public int getIllegalTypeErrorMessageId() {
			return CDIValidationErrorManager.ILLEGAL_DECORATOR_BEAN_CLASS_ID;
		}

		@Override
		public String getDuplicateTypeErrorMessage() {
			return CDIValidationMessages.DUPLICATE_DECORATOR_CLASS;
		}

		@Override
		protected String getAnnotationName() {
			return CDIConstants.DECORATOR_STEREOTYPE_TYPE_NAME;
		}
	}

	private class InterceptorTypeValidator extends AbstractTypeValidator {

		@Override
		public boolean validateSourceType(CDICoreValidator.CDIValidationContext context, IType type) {
			IClassBean classBean = context.getCdiProject().getBeanClass(type);
			return classBean instanceof IInterceptor;
		}

		@Override
		public String getParrentElementname() {
			return "interceptors"; //$NON-NLS-1$
		}

		@Override
		public String getEmptyTypeErrorMessage() {
			return CDIValidationMessages.EMPTY_INTERCEPTOR_CLASS_NAME;
		}

		@Override
		public String getUnknownTypeErrorMessage() {
			return CDIValidationMessages.UNKNOWN_INTERCEPTOR_CLASS_NAME;
		}

		@Override
		public int getUnknownTypeErrorMessageId() {
			return CDIValidationErrorManager.UNKNOWN_INTERCEPTOR_CLASS_NAME_ID;
		}

		@Override
		public String getIllegalTypeErrorMessage() {
			return CDIValidationMessages.ILLEGAL_INTERCEPTOR_CLASS;
		}

		@Override
		public int getIllegalTypeErrorMessageId() {
			return CDIValidationErrorManager.ILLEGAL_INTERCEPTOR_CLASS_ID;
		}

		@Override
		public String getDuplicateTypeErrorMessage() {
			return CDIValidationMessages.DUPLICATE_INTERCEPTOR_CLASS;
		}

		@Override
		protected String getAnnotationName() {
			return CDIConstants.INTERCEPTOR_ANNOTATION_TYPE_NAME;
		}
	}
}