/******************************************************************************* 
 * Copyright (c) 2007 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.seam.internal.core.validation;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

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.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.osgi.util.NLS;
import org.eclipse.wst.validation.internal.core.ValidationException;
import org.eclipse.wst.validation.internal.provisional.core.IReporter;
import org.jboss.tools.common.el.core.resolver.TypeInfoCollector;
import org.jboss.tools.common.java.IJavaSourceReference;
import org.jboss.tools.common.model.XModelObject;
import org.jboss.tools.common.model.util.EclipseResourceUtil;
import org.jboss.tools.common.text.ITextSourceReference;
import org.jboss.tools.common.validation.ContextValidationHelper;
import org.jboss.tools.common.validation.IPreferenceInfo;
import org.jboss.tools.common.validation.IProjectValidationContext;
import org.jboss.tools.common.validation.IValidatingProjectSet;
import org.jboss.tools.common.validation.IValidatingProjectTree;
import org.jboss.tools.common.validation.IValidator;
import org.jboss.tools.common.validation.PreferenceInfoManager;
import org.jboss.tools.common.validation.ValidationUtil;
import org.jboss.tools.common.validation.ValidatorManager;
import org.jboss.tools.common.validation.internal.SimpleValidatingProjectTree;
import org.jboss.tools.common.validation.internal.ValidatingProjectSet;
import org.jboss.tools.jst.web.kb.IKbProject;
import org.jboss.tools.jst.web.kb.KbProjectFactory;
import org.jboss.tools.jst.web.kb.internal.KbProject;
import org.jboss.tools.jst.web.kb.internal.validation.KBValidator;
import org.jboss.tools.jst.web.model.project.ext.store.XMLValueInfo;
import org.jboss.tools.seam.core.BijectedAttributeType;
import org.jboss.tools.seam.core.IBijectedAttribute;
import org.jboss.tools.seam.core.ISeamAnnotatedFactory;
import org.jboss.tools.seam.core.ISeamComponent;
import org.jboss.tools.seam.core.ISeamComponentDeclaration;
import org.jboss.tools.seam.core.ISeamComponentMethod;
import org.jboss.tools.seam.core.ISeamContextVariable;
import org.jboss.tools.seam.core.ISeamFactory;
import org.jboss.tools.seam.core.ISeamJavaComponentDeclaration;
import org.jboss.tools.seam.core.ISeamProject;
import org.jboss.tools.seam.core.ISeamProperty;
import org.jboss.tools.seam.core.ISeamXmlComponentDeclaration;
import org.jboss.tools.seam.core.ISeamXmlFactory;
import org.jboss.tools.seam.core.ScopeType;
import org.jboss.tools.seam.core.SeamComponentMethodType;
import org.jboss.tools.seam.core.SeamCoreBuilder;
import org.jboss.tools.seam.core.SeamCoreMessages;
import org.jboss.tools.seam.core.SeamCorePlugin;
import org.jboss.tools.seam.core.SeamPreferences;
import org.jboss.tools.seam.core.SeamProjectsSet;
import org.jboss.tools.seam.core.SeamUtil;
import org.jboss.tools.seam.core.project.facet.SeamRuntime;
import org.jboss.tools.seam.internal.core.DataModelSelectionAttribute;
import org.jboss.tools.seam.internal.core.SeamComponentDeclaration;
import org.jboss.tools.seam.internal.core.SeamProject;
import org.jboss.tools.seam.internal.core.SeamTextSourceReference;
import org.jboss.tools.seam.pages.xml.model.SeamPagesConstants;

/**
 * Validator for Java and XML files.
 * @author Alexey Kazakov
 */
public class SeamCoreValidator extends SeamValidationErrorManager implements IValidator {
	public static final String ID = "org.jboss.tools.seam.core.CoreValidator"; //$NON-NLS-1$

	public static final int NONUNIQUE_COMPONENT_NAME_MESSAGE_ID = 1;
	public static final int DUPLICATE_REMOVE_MESSAGE_ID = 2;
	public static final int DUPLICATE_DESTROY_MESSAGE_ID = 3;
	public static final int DUPLICATE_CREATE_MESSAGE_ID = 4;
	public static final int DUPLICATE_UNWRAP_MESSAGE_ID = 5;
	public static final int DESTROY_METHOD_BELONGS_TO_STATELESS_SESSION_BEAN_MESSAGE_ID = 6;
	public static final int CREATE_DOESNT_BELONG_TO_COMPONENT_MESSAGE_ID = 7;
	public static final int UNWRAP_DOESNT_BELONG_TO_COMPONENT_MESSAGE_ID = 8;
	public static final int OBSERVER_DOESNT_BELONG_TO_COMPONENT_MESSAGE_ID = 9;
	public static final int STATEFUL_COMPONENT_DOES_NOT_CONTAIN_REMOVE_ID = 10;
	public static final int STATEFUL_COMPONENT_DOES_NOT_CONTAIN_DESTROY_ID = 11;
	public static final int STATEFUL_COMPONENT_WRONG_SCOPE_ID = 12;
	public static final int ENTITY_COMPONENT_WRONG_SCOPE_ID = 13;
	public static final int UNKNOWN_COMPONENT_PROPERTY_ID = 14;
	
	private ISeamProject seamProject;
	private SeamProjectsSet set;
	private String projectName;

	/*
	 * (non-Javadoc)
	 * @see org.jboss.tools.jst.web.kb.validation.IValidator#getId()
	 */
	public String getId() {
		return ID;
	}

	public String getBuilderId() {
		return SeamCoreBuilder.BUILDER_ID;
	}

	/*
	 * (non-Javadoc)
	 * @see org.jboss.tools.jst.web.kb.validation.IValidator#getValidatingProjects(org.eclipse.core.resources.IProject)
	 */
	public IValidatingProjectTree getValidatingProjects(IProject project) {
		return getSeamValidatingProjects(project);
	}

	private static final String SHORT_ID = "jboss.seam.core"; //$NON-NLS-1$

	public static IValidatingProjectTree getSeamValidatingProjects(IProject project) {
		SeamProjectsSet set = new SeamProjectsSet(project);
		IProject war = set.getWarProject();
		IProjectValidationContext rootContext = null;
		if(war!=null && war.isAccessible()) {
			IKbProject kbProject = KbProjectFactory.getKbProject(war, false);
			if(kbProject!=null) {
				rootContext = kbProject.getValidationContext();
			} else {
				KbProject.checkKBBuilderInstalled(war);
				ISeamProject seamProject = SeamCorePlugin.getSeamProject(war, false);
				if(seamProject!=null) {
					rootContext = seamProject.getValidationContext();
				}
			}
		}
		if(rootContext==null) {
			ISeamProject seamProject = SeamCorePlugin.getSeamProject(project, false);
			if(seamProject!=null) {
				rootContext = seamProject.getValidationContext();
				war = project;
			}
		}

		Set<IProject> projects = new HashSet<IProject>();
		IProject[] array = set.getAllProjects();
		for (int i = 0; i < array.length; i++) {
			if(array[i].isAccessible()) {
				projects.add(array[i]);
			}
		}
		IValidatingProjectSet projectSet = new ValidatingProjectSet(war, projects, rootContext);
		return new SimpleValidatingProjectTree(projectSet);
	}

	/*
	 * (non-Javadoc)
	 * @see org.jboss.tools.jst.web.kb.validation.IValidator#isEnabled(org.eclipse.core.resources.IProject)
	 */
	public boolean isEnabled(IProject project) {
		return SeamPreferences.isValidationEnabled(project);
	}

	/*
	 * (non-Javadoc)
	 * @see org.jboss.tools.jst.web.kb.validation.IValidator#shouldValidate(org.eclipse.core.resources.IProject)
	 */
	public boolean shouldValidate(IProject project) {
		try {
			return project != null 
					&& project.isAccessible() 
					&& project.hasNature(ISeamProject.NATURE_ID)
					&& validateBuilderOrder(project)
					&& isPreferencesEnabled(project);
		} catch (CoreException e) {
			SeamCorePlugin.getDefault().logError(e);
		}
		return false;
	}

	private boolean validateBuilderOrder(IProject project) throws CoreException {
		return KBValidator.validateBuilderOrder(project, getBuilderId(), getId(), SeamPreferences.getInstance());
	}

	/*
	 * (non-Javadoc)
	 * @see org.jboss.tools.jst.web.kb.internal.validation.ValidationErrorManager#init(org.eclipse.core.resources.IProject, org.jboss.tools.jst.web.kb.internal.validation.ContextValidationHelper, org.jboss.tools.jst.web.kb.internal.validation.ValidatorManager, org.eclipse.wst.validation.internal.provisional.core.IReporter, org.jboss.tools.jst.web.kb.validation.IValidationContext)
	 */
	@Override
	public void init(IProject project, ContextValidationHelper validationHelper, IProjectValidationContext validationContext, org.eclipse.wst.validation.internal.provisional.core.IValidator manager, IReporter reporter) {
		super.init(project, validationHelper, validationContext, manager, reporter);

		set = new SeamProjectsSet(project);
		IProject warProject = set.getWarProject();
		
		// Fix for JBIDE-7622: set these variables to null due to prevent wrong project validations --->>>
		seamProject = null;
		projectName = null;
		// <<<---
		
		if(warProject.isAccessible()) {
			seamProject = SeamCorePlugin.getSeamProject(warProject, false);
			projectName = seamProject.getProject().getName();
		}
	}

	private boolean isPreferencesEnabled(IProject project) {
		return isEnabled(project) && SeamPreferences.shouldValidateCore(project);
	}

	/*
	 * (non-Javadoc)
	 * @see org.jboss.tools.jst.web.kb.validation.IValidator#validate(java.util.Set, org.eclipse.core.resources.IProject, org.jboss.tools.jst.web.kb.internal.validation.ContextValidationHelper, org.jboss.tools.jst.web.kb.validation.IProjectValidationContext, org.jboss.tools.jst.web.kb.internal.validation.ValidatorManager, org.eclipse.wst.validation.internal.provisional.core.IReporter)
	 */
	public IStatus validate(Set<IFile> changedFiles, IProject project, ContextValidationHelper validationHelper, IProjectValidationContext context, ValidatorManager manager, IReporter reporter) throws ValidationException {
		init(project, validationHelper, context, manager, reporter);
		if(seamProject==null) {
			return OK_STATUS;
		}
		displaySubtask(SeamValidationMessages.SEARCHING_RESOURCES, new String[]{projectName});

		IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
		Set<ISeamComponent> checkedComponents = new HashSet<ISeamComponent>();
		Set<String> markedDuplicateFactoryNames = new HashSet<String>();
		// Collect all resources which we must validate.
		Set<IPath> resources = new HashSet<IPath>(); // Resources which we have to validate.
		Set<IPath> newResources = new HashSet<IPath>(); // New (unlinked) resources file
		boolean validateUnnamedResources = false;
		for(IFile currentFile : changedFiles) {
			if(reporter.isCancelled()) {
				break;
			}
			if(!validateUnnamedResources) {
				String fileName = currentFile.getName().toLowerCase();
				// We need to check only file names here. 
				validateUnnamedResources = fileName.endsWith(".java") || fileName.endsWith(".properties") || fileName.equals("components.xml"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			}
			if (ValidationUtil.checkFileExtensionForJavaAndXml(currentFile)) {
				resources.add(currentFile.getFullPath());
				// Get new variable names from model
				Set<String> newVariableNamesOfChangedFile = getVariablesNameByResource(currentFile.getFullPath());
				Set<String> oldDeclarationsOfChangedFile = validationContext.getVariableNamesByCoreResource(SHORT_ID, currentFile.getFullPath(), true);
				for (String newVariableName : newVariableNamesOfChangedFile) {
					// Collect resources with new variable name.
					Set<IPath> linkedResources = validationContext.getCoreResourcesByVariableName(SHORT_ID, newVariableName, false);
					if(linkedResources!=null) {
						resources.addAll(linkedResources);
					}
					resources.addAll(getAllResourceOfComponent(currentFile.getFullPath()));
				}
				// Get old variable names which were linked with this resource.
				Set<String> oldVariablesNamesOfChangedFile = validationContext.getVariableNamesByCoreResource(SHORT_ID, currentFile.getFullPath(), false);
				if(oldVariablesNamesOfChangedFile!=null) {
					for (String name : oldVariablesNamesOfChangedFile) {
						Set<IPath> linkedResources = validationContext.getCoreResourcesByVariableName(SHORT_ID, name, false);
						if(linkedResources!=null) {
							resources.addAll(linkedResources);
						}
					}
				}
				// Save old declarations for EL validation. We need to validate all EL resources which use this variable name but only if the variable has been changed.
				if(oldDeclarationsOfChangedFile!=null) {
					for (String name : oldDeclarationsOfChangedFile) {
						validationContext.addVariableNameForELValidation(SHORT_ID, name);
					}
				}
				newResources.add(currentFile.getFullPath());
			}
		}
		// Validate all collected linked resources.
		// Remove all links between collected resources and variables names because they will be linked again during validation.
		validationContext.removeLinkedCoreResources(SHORT_ID, resources);

		Set<IFile> filesToValidate = new HashSet<IFile>();
		// We have to remove markers from all collected source files first
		for (IPath linkedResource : resources) {
			IFile file = root.getFile(linkedResource);
			if(file!=null && shouldBeValidated(file)) {
				filesToValidate.add(file);
				removeAllMessagesFromResource(file);
			}
		}
		// Then we can validate them
		for (IFile file : filesToValidate) {
			validateComponent(file.getFullPath(), checkedComponents, newResources);
			validateFactory(file.getFullPath(), markedDuplicateFactoryNames);
			validatePageXML(file);
			validateXMLVersion(file);
		}

		// If changed files are *.java or component.xml then re-validate all unnamed resources.
		if(validateUnnamedResources) {
			Set<IPath> unnamedResources = validationContext.getUnnamedCoreResources(SHORT_ID);
			newResources.addAll(unnamedResources);
			for (IPath path : newResources) {
				IFile file = root.getFile(path);
				if(file!=null && file.isAccessible()) {
					if(!resources.contains(path)) {
						removeAllMessagesFromResource(file);
					}
					displaySubtask(SeamValidationMessages.VALIDATING_RESOURCE, new String[]{projectName, path.toString()});
					Set<ISeamJavaComponentDeclaration> declarations = ((SeamProject)seamProject).findJavaDeclarations(path);
					for (ISeamJavaComponentDeclaration d : declarations) {
						validateMethodsOfUnknownComponent(d);
					}
				}
			}
		}

		return OK_STATUS;
	}

	/*
	 * (non-Javadoc)
	 * @see org.jboss.tools.jst.web.kb.validation.IValidator#validateAll(org.eclipse.core.resources.IProject, org.jboss.tools.jst.web.kb.internal.validation.ContextValidationHelper, org.jboss.tools.jst.web.kb.validation.IProjectValidationContext, org.jboss.tools.jst.web.kb.internal.validation.ValidatorManager, org.eclipse.wst.validation.internal.provisional.core.IReporter)
	 */
	public IStatus validateAll(IProject project, ContextValidationHelper validationHelper, IProjectValidationContext context, ValidatorManager manager, IReporter reporter) throws ValidationException {
		init(project, validationHelper, context, manager, reporter);
		if(seamProject==null) {
			return OK_STATUS;
		}

		for (IProject iProject : set.getAllProjects()) {
			removeAllMessagesFromResource(iProject);
		}

		ISeamComponent[] components = seamProject.getComponents();
		for (ISeamComponent component : components) {
			if(reporter.isCancelled()) {
				return OK_STATUS;
			}
			Set<ISeamComponentDeclaration> declarations = component.getAllDeclarations();
			for (ISeamComponentDeclaration seamComponentDeclaration : declarations) {
				validateComponent(component);
				break;
			}
		}
		ISeamFactory[] factories = seamProject.getFactories();
		Set<String> markedDuplicateFactoryNames = new HashSet<String>();
		for (ISeamFactory factory : factories) {
			if(reporter.isCancelled()) {
				return OK_STATUS;
			}
			validateFactory(factory, markedDuplicateFactoryNames);
		}

		ISeamJavaComponentDeclaration[] values = ((SeamProject)seamProject).getAllJavaComponentDeclarations();
		for (ISeamJavaComponentDeclaration d : values) {
			if(reporter.isCancelled()) {
				return OK_STATUS;
			}
			displaySubtask(SeamValidationMessages.VALIDATING_CLASS, new String[]{projectName, d.getClassName()});
			validateMethodsOfUnknownComponent(d);
		}
		IResource webContent = set.getViewsFolder();
		if(webContent instanceof IContainer && webContent.isAccessible()) {
			validateAllPageXMLFiles((IContainer)webContent);
		}

		return OK_STATUS;
	}

	void validateAllPageXMLFiles(IContainer c) {
		if(c.isAccessible()) {
			IResource[] rs = null;
			try {
				rs = c.members();
			} catch (CoreException e) {
				SeamCorePlugin.getDefault().logError(e);
				return;
			}
			for (int i = 0; i < rs.length; i++) {
				if(rs[i] instanceof IContainer) {
					 validateAllPageXMLFiles((IContainer)rs[i]);
				} else if(rs[i] instanceof IFile) {
					validatePageXML((IFile)rs[i]);
				}
			}
		}
	}

	private void validateFactory(IPath sourceFilePath, Set<String> markedDuplicateFactoryNames) {
		Set<ISeamFactory> factories = seamProject.getFactoriesByPath(sourceFilePath);
		for (ISeamFactory factory : factories) {
			validateFactory(factory, markedDuplicateFactoryNames);
		}
	}

	private void validateFactory(ISeamFactory factory, Set<String> markedDuplicateFactoryNames) {
		if(SeamUtil.isJar(factory.getSourcePath())) {
			return;
		}
		String factoryName = factory.getName();
		if(factoryName!=null) {
			displaySubtask(SeamValidationMessages.VALIDATING_FACTORY, new String[]{projectName, factoryName});
		}
		if(factory instanceof ISeamAnnotatedFactory) {
			validateAnnotatedFactory((ISeamAnnotatedFactory)factory, markedDuplicateFactoryNames);
		} else {
			validateXmlFactory((ISeamXmlFactory)factory, markedDuplicateFactoryNames);
		}
	}

	private void validateXmlFactory(ISeamXmlFactory factory, Set<String> markedDuplicateFactoryNames) {
		String name = factory.getName();
		if(name==null) {
			SeamCorePlugin.getDefault().logError(NLS.bind(SeamCoreMessages.SEAM_CORE_VALIDATOR_FACTORY_METHOD_MUST_HAVE_NAME,factory.getResource()));
			return;
		}
		validateFactoryName(factory, name, markedDuplicateFactoryNames, false);
	}

	private void validateAnnotatedFactory(ISeamAnnotatedFactory factory, Set<String> markedDuplicateFactoryNames) {
		IMember sourceMember = factory.getSourceMember();
		if(sourceMember instanceof IMethod) {
			String factoryName = factory.getName();
			if(factoryName==null) {
				// Unknown factory name
				SeamCorePlugin.getDefault().logError(NLS.bind(SeamCoreMessages.SEAM_CORE_VALIDATOR_FACTORY_METHOD_MUST_HAVE_NAME,factory.getResource()));
				return;
			}
			validateFactoryName(factory, factoryName, markedDuplicateFactoryNames, true);
		} else {
			// factory must be java method!
			// JDT should mark it.
		}
	}

	private void validateFactoryName(ISeamFactory factory, String factoryName, Set<String> markedDuplicateFactoryNames, boolean validateUnknownName) {
		ScopeType factoryScope = factory.getScope();
		Set<ISeamContextVariable> variables = seamProject.getVariablesByName(factoryName);
		boolean unknownVariable = true;
		boolean firstDuplicateVariableWasMarked = false;
		for (ISeamContextVariable variable : variables) {
			if(variable instanceof ISeamFactory) {
				if(variable!=factory && !markedDuplicateFactoryNames.contains(factoryName) && 
					(factoryScope == variable.getScope() || factoryScope.getPriority()>variable.getScope().getPriority())) {
					// Duplicate factory name. Mark it.
					// Save link to factory resource.
					ITextSourceReference location = null;
					if(!firstDuplicateVariableWasMarked) {
						firstDuplicateVariableWasMarked = true;
						// mark original factory
						validationContext.addLinkedCoreResource(SHORT_ID, factoryName, factory.getSourcePath(), true);
						location = SeamUtil.getLocationOfName(factory);
						this.addError(SeamValidationMessages.DUPLICATE_VARIABLE_NAME, SeamPreferences.DUPLICATE_VARIABLE_NAME, new String[]{factoryName}, location, factory.getResource());
					}
					// Mark duplicate variable.
					if(!SeamUtil.isJar(variable.getSourcePath())) {
						IResource resource = SeamUtil.getComponentResourceWithName(variable);
						validationContext.addLinkedCoreResource(SHORT_ID, factoryName, resource.getFullPath(), true);
						location = SeamUtil.getLocationOfName(variable);
						this.addError(SeamValidationMessages.DUPLICATE_VARIABLE_NAME, SeamPreferences.DUPLICATE_VARIABLE_NAME, new String[]{factoryName}, location, resource);
					}
				}
			} else {
				// We know that variable name
				unknownVariable = false;
			}
		}
		if(firstDuplicateVariableWasMarked) {
			markedDuplicateFactoryNames.add(factoryName);
		}
		boolean voidReturnType = false;
		if(factory instanceof ISeamAnnotatedFactory) {
			IMember sourceMember = ((ISeamAnnotatedFactory)factory).getSourceMember();
			if(sourceMember instanceof IMethod) {
				IMethod method = (IMethod)sourceMember;
				try {
					String returnType = method.getReturnType();
					if("V".equals(returnType)) { //$NON-NLS-1$
						// return type is void
						voidReturnType = true;
					}
				} catch (JavaModelException e) {
					SeamCorePlugin.getDefault().logError(SeamCoreMessages.SEAM_CORE_VALIDATOR_ERROR_VALIDATING_SEAM_CORE, e);
				}
			}
		}
		if(unknownVariable && validateUnknownName && voidReturnType) {
			// mark unknown factory name
			// save link to factory resource
			validationContext.addLinkedCoreResource(SHORT_ID, factoryName, factory.getSourcePath(), true);
			this.addError(SeamValidationMessages.UNKNOWN_FACTORY_NAME, SeamPreferences.UNKNOWN_FACTORY_NAME, new String[]{factoryName}, SeamUtil.getLocationOfName(factory), factory.getResource());
		}
	}

	private void validateComponent(IPath sourceFilePath, Set<ISeamComponent> checkedComponents, Set<IPath> unnamedResources) {
		Set<ISeamComponent> components = seamProject.getComponentsByPath(sourceFilePath);
		if(components.isEmpty()) {
			unnamedResources.add(sourceFilePath);
			return;
		}
		for (ISeamComponent component : components) {
			// Don't validate one component twice.
			if(!checkedComponents.contains(component)) {
				validateComponent(component);
				checkedComponents.add(component);
			}
		}
	}

	/*
	 * Returns set of variables which are linked with this resource
	 */
	private Set<String> getVariablesNameByResource(IPath resourcePath) {
		Set<ISeamContextVariable> variables = seamProject.getVariablesByPath(resourcePath);
		Set<String> result = new HashSet<String>();
		if(variables!=null) {
			for (ISeamContextVariable variable : variables) {
				String name = variable.getName();
				result.add(name);
			}
		}
		return result;
	}

	/*
	 * Collect all resources of all declarations of all components which is declared in the source.
	 */
	private Set<IPath> getAllResourceOfComponent(IPath sourceComponentFilePath) {
		Set<IPath> result = new HashSet<IPath>();
		Set<ISeamComponent> components = seamProject.getComponentsByPath(sourceComponentFilePath);
		for (ISeamComponent component : components) {
			Set<ISeamComponentDeclaration> declarations = component.getAllDeclarations();
			for (ISeamComponentDeclaration seamComponentDeclaration : declarations) {
				result.add(seamComponentDeclaration.getResource().getFullPath());
			}
		}
		return result;
	}

	/*
	 * Validates the component 
	 */
	private void validateComponent(ISeamComponent component) {
		ISeamJavaComponentDeclaration firstJavaDeclaration = component.getJavaDeclaration();
		if(firstJavaDeclaration!=null) {
			String componentName = component.getName();
			if(componentName!=null) {
				displaySubtask(SeamValidationMessages.VALIDATING_COMPONENT, new String[]{projectName, componentName});
			}
			HashMap<Integer, ISeamJavaComponentDeclaration> usedPrecedences = new HashMap<Integer, ISeamJavaComponentDeclaration>();
			Set<ISeamJavaComponentDeclaration> markedDeclarations = new HashSet<ISeamJavaComponentDeclaration>();
			int firstJavaDeclarationPrecedence = firstJavaDeclaration.getPrecedence();
			usedPrecedences.put(firstJavaDeclarationPrecedence, firstJavaDeclaration);
			Set<ISeamComponentDeclaration> declarations = component.getAllDeclarations();
			for (ISeamComponentDeclaration declaration : declarations) {
				if(declaration instanceof ISeamJavaComponentDeclaration) {
					ISeamJavaComponentDeclaration jd = (ISeamJavaComponentDeclaration)declaration;

					//do not check files declared in another project
//					if(jd.getSeamProject() != seamProject) continue;

					IType type = (IType)jd.getSourceMember();
					boolean sourceJavaDeclaration = !type.isBinary();
					if(sourceJavaDeclaration) {
						// Save link between component name and java source file.
						validationContext.addLinkedCoreResource(SHORT_ID, componentName, declaration.getSourcePath(), true);
						// Save link between component name and all supers of java declaration.
						try {
							IType[] superTypes = TypeInfoCollector.getSuperTypes(type).getSuperTypes();
							for (int i = 0; superTypes != null && i < superTypes.length; i++) {
								if(!superTypes[i].isBinary()) {
									IPath path = superTypes[i].getResource().getFullPath();
									validationContext.addLinkedCoreResource(SHORT_ID, componentName, path, true);
								}
							}
						} catch (JavaModelException e) {
							SeamCorePlugin.getPluginLog().logError(e);
						}
					}
					if(declaration!=firstJavaDeclaration) {
						// Validate @Name
						// Component class with the same component name. Check precedence.
						ISeamJavaComponentDeclaration javaDeclaration = (ISeamJavaComponentDeclaration)declaration;
						int javaDeclarationPrecedence = javaDeclaration.getPrecedence();
						ISeamJavaComponentDeclaration checkedDeclaration = usedPrecedences.get(javaDeclarationPrecedence);
						if(checkedDeclaration==null) {
							usedPrecedences.put(javaDeclarationPrecedence, javaDeclaration);
						} else if(sourceJavaDeclaration) {
							boolean sourceCheckedDeclaration = !((IType)checkedDeclaration.getSourceMember()).isBinary();
							IResource javaDeclarationResource = javaDeclaration.getResource();
							// Mark nonunique name.
							if(!markedDeclarations.contains(checkedDeclaration) && sourceCheckedDeclaration) {
								// Mark first wrong declaration with that name
								IResource checkedDeclarationResource = checkedDeclaration.getResource();
								ITextSourceReference location = ((SeamComponentDeclaration)checkedDeclaration).getLocationFor(SeamComponentDeclaration.PATH_OF_NAME);
								if(!SeamUtil.isEmptyLocation(location)) {
									addError(SeamValidationMessages.NONUNIQUE_COMPONENT_NAME_MESSAGE, SeamPreferences.NONUNIQUE_COMPONENT_NAME, new String[]{componentName}, location, checkedDeclarationResource, NONUNIQUE_COMPONENT_NAME_MESSAGE_ID);
								}
								markedDeclarations.add(checkedDeclaration);
							}
							// Mark next wrong declaration with that name
							markedDeclarations.add(javaDeclaration);
							ITextSourceReference location = ((SeamComponentDeclaration)javaDeclaration).getLocationFor(SeamComponentDeclaration.PATH_OF_NAME);
							if(!SeamUtil.isEmptyLocation(location)) {
								addError(SeamValidationMessages.NONUNIQUE_COMPONENT_NAME_MESSAGE, SeamPreferences.NONUNIQUE_COMPONENT_NAME, new String[]{componentName}, location, javaDeclarationResource, NONUNIQUE_COMPONENT_NAME_MESSAGE_ID);
							}
						}
					}
				}
			}
			boolean source = !((IType)firstJavaDeclaration.getSourceMember()).isBinary();
			if(source) {
				// Validate all elements in first java declaration but @Name.
				validateJavaDeclaration(component, firstJavaDeclaration);
			}
		}
		validateXmlComponentDeclarations(component);
	}

	private static final String BUILT_IN_COMPONENT_NAME_PREFIX = "org.jboss.seam.";

	private boolean isBuiltInComponentName(String componentName) {
		return componentName.startsWith(BUILT_IN_COMPONENT_NAME_PREFIX);
	}

	private void validateXmlComponentDeclarations(ISeamComponent component) {
		String componentName = component.getName();
		if(componentName!=null) {
			HashMap<String, ISeamXmlComponentDeclaration> usedPrecedences = new HashMap<String, ISeamXmlComponentDeclaration>();
			Set<ISeamXmlComponentDeclaration> markedDeclarations = new HashSet<ISeamXmlComponentDeclaration>();
			Set<ISeamJavaComponentDeclaration> markedJavaDeclarations = new HashSet<ISeamJavaComponentDeclaration>();
			Set<ISeamXmlComponentDeclaration> declarations = component.getXmlDeclarations();
			ISeamXmlComponentDeclaration firstNamedDeclaration = null;
			for (ISeamXmlComponentDeclaration declaration : declarations) {
				if(SeamUtil.isJar(declaration)) {
					return;
				}
				//do not check files declared in another project
//				if(declaration.getSeamProject() != seamProject) continue;

				validationContext.addLinkedCoreResource(SHORT_ID, componentName, declaration.getSourcePath(), true);

				String precedence = declaration.getPrecedence();
				if(firstNamedDeclaration == null && declaration.getName()!=null) {
					firstNamedDeclaration = declaration;
					usedPrecedences.put(precedence, declaration);
				}
				if(declaration.getName()!=null && firstNamedDeclaration!=declaration) {
					// Check precedence
					ISeamXmlComponentDeclaration checkedDeclaration = usedPrecedences.get(precedence);
					if(checkedDeclaration==null) {
						usedPrecedences.put(precedence, declaration);
					} else {
						// Mark not-unique name.
						if(!markedDeclarations.contains(checkedDeclaration)) {
							// Mark first wrong declaration with that name
							IResource checkedDeclarationResource = checkedDeclaration.getResource();
							ITextSourceReference location = ((SeamComponentDeclaration)checkedDeclaration).getLocationFor(SeamComponentDeclaration.PATH_OF_NAME);
							if(!SeamUtil.isEmptyLocation(location)) {
								addError(SeamValidationMessages.NONUNIQUE_COMPONENT_NAME_MESSAGE, SeamPreferences.NONUNIQUE_COMPONENT_NAME, new String[]{componentName}, location, checkedDeclarationResource, NONUNIQUE_COMPONENT_NAME_MESSAGE_ID);
							}
							markedDeclarations.add(checkedDeclaration);
						}
						// Mark next wrong declaration with that name
						markedDeclarations.add(declaration);
						ITextSourceReference location = ((SeamComponentDeclaration)declaration).getLocationFor(SeamComponentDeclaration.PATH_OF_NAME);
						if(!SeamUtil.isEmptyLocation(location)) {
							addError(SeamValidationMessages.NONUNIQUE_COMPONENT_NAME_MESSAGE, SeamPreferences.NONUNIQUE_COMPONENT_NAME, new String[]{componentName}, location, declaration.getResource(), NONUNIQUE_COMPONENT_NAME_MESSAGE_ID);
						}
					}
				}

				// Check Java declarations with the same name
				Set<ISeamContextVariable> vars = seamProject.getVariablesByName(componentName);
				for (ISeamContextVariable variable : vars) {
					if(variable instanceof ISeamComponent) {
						ISeamComponent c = (ISeamComponent)variable;
						Set<ISeamComponentDeclaration> decls = c.getAllDeclarations();
						for (ISeamComponentDeclaration dec : decls) {
							if(dec instanceof ISeamJavaComponentDeclaration) {
								ISeamJavaComponentDeclaration javaDec = (ISeamJavaComponentDeclaration)dec;
								// Check names
								if(declaration.getClassName()!=null && javaDec.getName()!=null && javaDec.getName().equals(declaration.getName())) {
									// Check precedences
									String javaPrecedence = "" + javaDec.getPrecedence();
									if(javaPrecedence.equals(precedence) && !isBuiltInComponentName(componentName)) {
										if(!markedJavaDeclarations.contains(javaDec)) {
											markedJavaDeclarations.add(javaDec);
											ITextSourceReference location = ((SeamComponentDeclaration)javaDec).getLocationFor(SeamComponentDeclaration.PATH_OF_NAME);
											addError(SeamValidationMessages.NONUNIQUE_COMPONENT_NAME_MESSAGE, SeamPreferences.NONUNIQUE_COMPONENT_NAME, new String[]{componentName}, location, javaDec.getResource(), NONUNIQUE_COMPONENT_NAME_MESSAGE_ID);
										}
										if(!markedDeclarations.contains(declaration)) {
											markedDeclarations.add(declaration);
											ITextSourceReference location = ((SeamComponentDeclaration)declaration).getLocationFor(SeamComponentDeclaration.PATH_OF_NAME);
											addError(SeamValidationMessages.NONUNIQUE_COMPONENT_NAME_MESSAGE, SeamPreferences.NONUNIQUE_COMPONENT_NAME, new String[]{componentName}, location, declaration.getResource(), NONUNIQUE_COMPONENT_NAME_MESSAGE_ID);
										}
									}
								}
							}
						}
					}
				}

				String className = declaration.getClassName();
				IType type = null;
				if(className!=null) {
					// validate class name
					try {
						IProject p = seamProject.getProject();
//						type = EclipseJavaUtil.findType(EclipseResourceUtil.getJavaProject(p), className);
						IJavaProject javaProject = EclipseResourceUtil.getJavaProject(p);
						if(javaProject==null) {
							SeamCorePlugin.getDefault().logWarning("Can't get Java project for " + seamProject.getProject()!=null?seamProject.getProject().getName():"" + " Seam project.");
							return;
						}
						type = javaProject.findType(className);
						if(type==null) {
							// Mark wrong class name
							ITextSourceReference location = ((SeamComponentDeclaration)declaration).getLocationFor(ISeamXmlComponentDeclaration.CLASS);
							if(SeamUtil.isEmptyLocation(location)) {
								location = ((SeamComponentDeclaration)declaration).getLocationFor(ISeamXmlComponentDeclaration.NAME);
							}
							if(SeamUtil.isEmptyLocation(location)) {
								location = declaration;
							}
							if(!declaration.isClassNameGuessed()) {
								addError(SeamValidationMessages.UNKNOWN_COMPONENT_CLASS_NAME, SeamPreferences.UNKNOWN_COMPONENT_CLASS_NAME, new String[]{className}, location, declaration.getResource());
							} else {
								addError(SeamValidationMessages.UNKNOWN_COMPONENT_CLASS_NAME, SeamPreferences.UNKNOWN_COMPONENT_CLASS_NAME_GUESS, new String[]{className}, location, declaration.getResource());
							}
						} else if(!type.isBinary()) {
							validationContext.addLinkedCoreResource(SHORT_ID, componentName, type.getResource().getFullPath(), true);
						}
					} catch (JavaModelException e) {
						SeamCorePlugin.getDefault().logError(SeamCoreMessages.SEAM_CORE_VALIDATOR_ERROR_VALIDATING_SEAM_CORE, e);
					}
				}
				// validate properties
				Collection<ISeamProperty> properties = declaration.getProperties();
				for (ISeamProperty property : properties) {
					if(SeamUtil.isJar(property)) {
						return;
					}
					String name = property.getName();
					if(name==null) {
						return;
					}
					if(type==null && component.getJavaDeclaration()!=null) {
						IMember member = component.getJavaDeclaration().getSourceMember();
						if(member instanceof IType) {
							type = (IType)member;
						}
					}
					if(type!=null) {
						boolean ok = type.isBinary() || SeamUtil.findProperty(type, name)!=null;
						if(!ok) {
							addError(SeamValidationMessages.UNKNOWN_COMPONENT_PROPERTY, SeamPreferences.UNKNOWN_COMPONENT_PROPERTY, new String[]{type.getElementName(), componentName, name}, property, declaration.getResource(), UNKNOWN_COMPONENT_PROPERTY_ID);
						}
					}
				}
			}
		}
	}
	
	private void validateEntityComponent(ISeamComponent component) {
		if(component.isEntity()) {
			ISeamJavaComponentDeclaration javaDeclaration = component.getJavaDeclaration();
			ScopeType scope = component.getScope();
			if(scope == ScopeType.STATELESS) {
				ITextSourceReference location = getScopeLocation(component);
				addError(SeamValidationMessages.ENTITY_COMPONENT_WRONG_SCOPE, SeamPreferences.ENTITY_COMPONENT_WRONG_SCOPE, new String[]{component.getName()}, location, javaDeclaration.getResource(), ENTITY_COMPONENT_WRONG_SCOPE_ID);
			}
		}
	}

	private ITextSourceReference getScopeLocation(ISeamComponent component) {
		ISeamJavaComponentDeclaration javaDeclaration = component.getJavaDeclaration();
		ITextSourceReference location = ((SeamComponentDeclaration)javaDeclaration).getLocationFor(SeamComponentDeclaration.PATH_OF_SCOPE);
		if(SeamUtil.isEmptyLocation(location)) {
			location = getNameLocation(javaDeclaration);
		}
		return location;
	}

	private IJavaSourceReference getNameLocation(IJavaSourceReference source) {
		int length = 0;
		int offset = 0;
		try {
			length = source.getSourceMember().getNameRange().getLength();
			offset = source.getSourceMember().getNameRange().getOffset();
		} catch (JavaModelException e) {
			SeamCorePlugin.getDefault().logError(SeamCoreMessages.SEAM_CORE_VALIDATOR_ERROR_VALIDATING_SEAM_CORE, e);
		}
		return new SeamTextSourceReference(source.getSourceMember(), length, offset, source.getResource());
	}

	private void validateStatefulComponent(ISeamComponent component) {
		if(component.isStateful()) {
			ISeamJavaComponentDeclaration javaDeclaration = component.getJavaDeclaration();
			validateStatefulComponentMethods(SeamComponentMethodType.DESTROY, component, SeamValidationMessages.STATEFUL_COMPONENT_DOES_NOT_CONTAIN_DESTROY, SeamPreferences.STATEFUL_COMPONENT_DOES_NOT_CONTENT_DESTROY, STATEFUL_COMPONENT_DOES_NOT_CONTAIN_DESTROY_ID);
			validateStatefulComponentMethods(SeamComponentMethodType.REMOVE, component, SeamValidationMessages.STATEFUL_COMPONENT_DOES_NOT_CONTAIN_REMOVE, SeamPreferences.STATEFUL_COMPONENT_DOES_NOT_CONTENT_REMOVE, STATEFUL_COMPONENT_DOES_NOT_CONTAIN_REMOVE_ID);
			ScopeType scope = component.getScope();
			if(scope == ScopeType.PAGE || scope == ScopeType.STATELESS) {
				ITextSourceReference location = getScopeLocation(component);
				addError(SeamValidationMessages.STATEFUL_COMPONENT_WRONG_SCOPE, SeamPreferences.STATEFUL_COMPONENT_WRONG_SCOPE, new String[]{component.getName()}, location, javaDeclaration.getResource(), STATEFUL_COMPONENT_WRONG_SCOPE_ID);
			}
			validateDuplicateComponentMethod(SeamComponentMethodType.REMOVE, component, SeamValidationMessages.DUPLICATE_REMOVE, SeamPreferences.DUPLICATE_REMOVE, DUPLICATE_REMOVE_MESSAGE_ID);
		}
	}

	private void validateStatefulComponentMethods(SeamComponentMethodType methodType, ISeamComponent component, String message, String preferenceKey, int id) {
		ISeamJavaComponentDeclaration javaDeclaration = component.getJavaDeclaration();
		ITextSourceReference classNameLocation = getNameLocation(javaDeclaration);
		Set<ISeamComponentMethod> methods = javaDeclaration.getMethodsByType(methodType);
		if(methods==null || methods.isEmpty()) {
			addError(message, preferenceKey, new String[]{component.getName()}, classNameLocation, javaDeclaration.getResource(), id);
		}
	}

	private void validateDuplicateComponentMethods(ISeamComponent component) {
		validateDuplicateComponentMethod(SeamComponentMethodType.DESTROY, component, SeamValidationMessages.DUPLICATE_DESTROY, SeamPreferences.DUPLICATE_DESTROY, DUPLICATE_DESTROY_MESSAGE_ID);
		validateDuplicateComponentMethod(SeamComponentMethodType.CREATE, component, SeamValidationMessages.DUPLICATE_CREATE, SeamPreferences.DUPLICATE_CREATE, DUPLICATE_CREATE_MESSAGE_ID);
		validateDuplicateComponentMethod(SeamComponentMethodType.UNWRAP, component, SeamValidationMessages.DUPLICATE_UNWRAP, SeamPreferences.DUPLICATE_UNWRAP, DUPLICATE_UNWRAP_MESSAGE_ID);
	}

	private void validateDuplicateComponentMethod(SeamComponentMethodType methodType, ISeamComponent component, String message, String preferenceKey, int message_id) {
		ISeamJavaComponentDeclaration javaDeclaration = component.getJavaDeclaration();
		Set<ISeamComponentMethod> methods = javaDeclaration.getMethodsByType(methodType);
		if(methods!=null && methods.size()>1) {
			for (ISeamComponentMethod method : methods) {
				if(javaDeclaration.getSourcePath().equals(method.getSourcePath())) {
					IMethod javaMethod = (IMethod)method.getSourceMember();
					String methodName = javaMethod.getElementName();
					ITextSourceReference methodNameLocation = getNameLocation(method);
					addError(message, preferenceKey, new String[]{methodName}, methodNameLocation, javaDeclaration.getResource(), message_id);
				}
			}
		}
	}

	private void validateJavaDeclaration(ISeamComponent component, ISeamJavaComponentDeclaration declaration) {
		validateBijections(declaration);
		validateStatefulComponent(component);
		validateDuplicateComponentMethods(component);
		validateEntityComponent(component);
		validateDestroyMethod(component);
	}

	private void validateBijections(ISeamJavaComponentDeclaration declaration) {
		Set<IBijectedAttribute> bijections = declaration.getBijectedAttributes();
		if(bijections==null) {
			return;
		}
		for (IBijectedAttribute bijection : bijections) {
			if(bijection.isOfType(BijectedAttributeType.DATA_MODEL_SELECTION) || bijection.isOfType(BijectedAttributeType.DATA_MODEL_SELECTION_INDEX)) {
				validateDataModelSelection(declaration, bijection);
			} else {
				validateInAndOut(declaration, bijection);
			}
		}
	}

	private void validateInAndOut(ISeamJavaComponentDeclaration declaration, IBijectedAttribute bijection) {
		String name = bijection.getName();
		if(name==null || name.startsWith("#{") || name.startsWith("${")) { //$NON-NLS-1$ //$NON-NLS-2$
			return;
		}
		// Validate @In
		if(bijection.isOfType(BijectedAttributeType.IN)) {
			// save link between java source and variable name
			validationContext.addLinkedCoreResource(SHORT_ID, name, declaration.getSourcePath(), false);

			Set<ISeamContextVariable> variables = declaration.getVariablesByName(name);
			if(variables == null || variables.isEmpty()) variables = seamProject.getVariablesByName(name);
			if(variables == null || variables.isEmpty()) {
				ISeamProject parentProject = seamProject.getParentProject();
				if(parentProject != null) {
					variables = parentProject.getVariablesByName(name);
				}
			}
			if(variables==null || variables.size()<1) {
				// Injection has unknown name. Mark it.
				IResource declarationResource = declaration.getResource();
				ITextSourceReference nameRef = getNameLocation(bijection);
				if(nameRef == null) {
					nameRef = bijection;
				}
				addError(SeamValidationMessages.UNKNOWN_VARIABLE_NAME, SeamPreferences.UNKNOWN_VARIABLE_NAME, new String[]{name}, nameRef, declarationResource);
			}
		} else {
			// save link between java source and variable name
			validationContext.addLinkedCoreResource(SHORT_ID, name, declaration.getSourcePath(), true);
		}
	}

	private void validateDataModelSelection(ISeamJavaComponentDeclaration declaration, IBijectedAttribute bijection) {
		String dataModelName = bijection.getValue();
		String selectionName = bijection.getName();
		// save link between java source and variable name
		validationContext.addLinkedCoreResource(SHORT_ID, selectionName, declaration.getSourcePath(), false);
		if(dataModelName==null) {
			// here must be the only one @DataModel in the component
			Set<IBijectedAttribute> dataBinders = declaration.getBijectedAttributesByType(BijectedAttributeType.DATA_BINDER);
			ITextSourceReference location = null;
			if(dataBinders!=null && dataBinders.size()>1) {
				for (IBijectedAttribute dataBinder : dataBinders) {
					location = dataBinder.getLocationFor(SeamComponentDeclaration.PATH_OF_NAME);
					addError(SeamValidationMessages.MULTIPLE_DATA_BINDER, SeamPreferences.MULTIPLE_DATA_BINDER, location, declaration.getResource());
				}
			}
		} else {
			// save link between java source and Data Model name
			validationContext.addLinkedCoreResource(SHORT_ID, dataModelName, declaration.getSourcePath(), true);
			Set<IBijectedAttribute> dataBinders = declaration.getBijectedAttributesByName(dataModelName);
			if(dataBinders!=null) {
				for (IBijectedAttribute dataBinder : dataBinders) {
					if(dataBinder.isOfType(BijectedAttributeType.DATA_BINDER) || dataBinder.isOfType(BijectedAttributeType.OUT)) {
						return;
					}
				}
			}
			addError(SeamValidationMessages.UNKNOWN_DATA_MODEL, SeamPreferences.UNKNOWN_DATA_MODEL, new String[]{dataModelName}, SeamUtil.getLocationOfAttribute(bijection, DataModelSelectionAttribute.VALUE), declaration.getResource());
		}
	}

	/*
	 *  Validates methods of java classes. They must belong components.
	 */
	private void validateMethodsOfUnknownComponent(ISeamJavaComponentDeclaration declaration) {
		if(seamProject.getComponentsByPath(declaration.getSourcePath()).isEmpty()) {
			IMember member = declaration.getSourceMember();
			try {
				if(member!=null && !Flags.isAbstract(member.getFlags())) {
					validateMethodOfUnknownComponent(SeamComponentMethodType.CREATE, declaration, SeamValidationMessages.CREATE_DOESNT_BELONG_TO_COMPONENT, SeamPreferences.CREATE_DOESNT_BELONG_TO_COMPONENT, CREATE_DOESNT_BELONG_TO_COMPONENT_MESSAGE_ID);
					validateMethodOfUnknownComponent(SeamComponentMethodType.UNWRAP, declaration, SeamValidationMessages.UNWRAP_DOESNT_BELONG_TO_COMPONENT, SeamPreferences.UNWRAP_DOESNT_BELONG_TO_COMPONENT, UNWRAP_DOESNT_BELONG_TO_COMPONENT_MESSAGE_ID);
					validateMethodOfUnknownComponent(SeamComponentMethodType.OBSERVER, declaration, SeamValidationMessages.OBSERVER_DOESNT_BELONG_TO_COMPONENT, SeamPreferences.OBSERVER_DOESNT_BELONG_TO_COMPONENT, OBSERVER_DOESNT_BELONG_TO_COMPONENT_MESSAGE_ID);
				}
			} catch (JavaModelException e) {
				SeamCorePlugin.getPluginLog().logError(e);
			}
		}
		validationContext.removeUnnamedCoreResource(SHORT_ID, declaration.getSourcePath());
	}

	private void validateDestroyMethod(ISeamComponent component) {
		if(component.isStateless()) {
			ISeamJavaComponentDeclaration javaDeclaration = component.getJavaDeclaration();
			Set<ISeamComponentMethod> methods = javaDeclaration.getMethodsByType(SeamComponentMethodType.DESTROY);
			if(methods==null) {
				return;
			}
			for (ISeamComponentMethod method : methods) {
				IMethod javaMethod = (IMethod)method.getSourceMember();
				String methodName = javaMethod.getElementName();
				if(javaDeclaration.getSourcePath().equals(javaMethod.getPath())) {
					validationContext.addLinkedCoreResource(SHORT_ID, component.getName(), javaDeclaration.getSourcePath(), true);
					ITextSourceReference methodNameLocation = getNameLocation(method);
					addError(SeamValidationMessages.DESTROY_METHOD_BELONGS_TO_STATELESS_SESSION_BEAN, SeamPreferences.DESTROY_METHOD_BELONGS_TO_STATELESS_SESSION_BEAN, new String[]{methodName}, methodNameLocation, method.getResource(), DESTROY_METHOD_BELONGS_TO_STATELESS_SESSION_BEAN_MESSAGE_ID);
				}
			}
		}
	}

	private void validateMethodOfUnknownComponent(SeamComponentMethodType methodType, ISeamJavaComponentDeclaration declaration, String message, String preferenceKey, int message_id) {
		Set<ISeamComponentMethod> methods = declaration.getMethodsByType(methodType);
		if(methods!=null && !methods.isEmpty()) {
			for (ISeamComponentMethod method : methods) {
				IMethod javaMethod = (IMethod)method.getSourceMember();
				String methodName = javaMethod.getElementName();
				if(declaration.getSourcePath().equals(javaMethod.getPath())) {
					ITextSourceReference methodNameLocation = getNameLocation(method);
					addError(message, preferenceKey, new String[]{methodName}, methodNameLocation, method.getResource(), message_id);
					validationContext.addUnnamedCoreResource(SHORT_ID, declaration.getSourcePath());
				}
			}
		} else {
			validationContext.removeUnnamedCoreResource(SHORT_ID, declaration.getSourcePath());
		}
	}

	private void validateXMLVersion(IFile file) {
		String ext = file.getFileExtension();
		if(!"xml".equals(ext)) return;
		
		XModelObject o = EclipseResourceUtil.createObjectForResource(file);
		
		if(o == null) return;
		if(!o.getModelEntity().getName().startsWith("FileSeamComponent")) return;
		
		Set<String> vs = getXMLVersions(o);
		
		SeamRuntime runtime = seamProject.getRuntime();
		if(runtime == null) return;
		
		String version = runtime.getVersion().toString();
		String wrongVersion = null;
		for (String v: vs) {
			if(!v.startsWith(version)) {
				wrongVersion = v;
				break;
			}
		}
		if(wrongVersion != null) {
			addError(
				SeamValidationMessages.INVALID_XML_VERSION, 
				SeamPreferences.INVALID_XML_VERSION, 
				new String[]{wrongVersion, version}, 
				file);
		}
	}

	private Set<String> getXMLVersions(XModelObject o) {
		Set<String> result = new HashSet<String>();
		if(o.getModelEntity().getName().endsWith("11")) {
			result.add("1.1");
			return result;
		}
		String sl = o.getAttributeValue("xsi:schemaLocation");
		int i = 0;
		while(i >= 0 && i < sl.length()) {
			int j = sl.indexOf('-', i);
			if(j < 0) break;
			int k = sl.indexOf(".xsd", j);
			if(k < 0) break;
			String v = sl.substring(j + 1, k);
			if(isVersion(v)) result.add(v);
			i = k;
		}

		return result;
	}

	private boolean isVersion(String s) {
		if(s.length() == 0) return false;
		for (int i = 0; i < s.length(); i++) {
			char c = s.charAt(i);
			if(c != '.' && !Character.isDigit(c)) return false;
		}
		return true;
	}

	private void validatePageXML(IFile f) {
		if(f.isAccessible() && (f.getName().equals("pages.xml") || f.getName().endsWith(".page.xml"))) {
			XModelObject object = EclipseResourceUtil.createObjectForResource(f);
			if(object == null) return;
			if(object.getModelEntity().getName().startsWith(SeamPagesConstants.ENT_FILE_SEAM_PAGE)) {
				validatePageViewIds(object, f);
			}
		}
	}

	private void validatePageViewIds(XModelObject o, IFile f) {
		String entity = o.getModelEntity().getName();
		validatePageViewId(o, f);
		if(entity.startsWith(SeamPagesConstants.ENT_REDIRECT) || entity.startsWith(SeamPagesConstants.ENT_RENDER)) {
		} else {
			XModelObject[] cs = o.getChildren();
			for (XModelObject c: cs) {
				validatePageViewIds(c, f);
			}
		}
	}

	static String ATTR_NO_CONVERSATION_VIEW_ID = "no conversation view id";
	static String ATTR_LOGIN_VIEW_ID = "login view id";

	private void validatePageViewId(XModelObject object, IFile f) {
		validatePageViewId(object, f, SeamPagesConstants.ATTR_VIEW_ID);
		validatePageViewId(object, f, ATTR_NO_CONVERSATION_VIEW_ID);
		validatePageViewId(object, f, ATTR_LOGIN_VIEW_ID);
	}

	private void validatePageViewId(XModelObject object, IFile f, String attr) {
		if(object.getModelEntity().getAttribute(attr) == null) return;
		String path = object.getAttributeValue(attr);
		if(path == null || path.length() == 0 || path.indexOf('*') >= 0) return;
		path = path.replace('\\', '/');
		if(path.indexOf('?') >= 0) {
			path = path.substring(0, path.indexOf('?'));
		}
		XModelObject target = object.getModel().getByPath(path);
		if(target == null) {
			XMLValueInfo i = new XMLValueInfo(object, attr);
			addError(NLS.bind(SeamValidationMessages.UNRESOLVED_VIEW_ID, path), SeamPreferences.UNRESOLVED_VIEW_ID, i, f);
		}
		
	}
	
	public IMarker addError(String message, String preferenceKey,
			String[] messageArguments, ITextSourceReference location,
			IResource target, int messageId) {
		IMarker marker = addError(message, preferenceKey, messageArguments, location, target);
		try{
			if(marker!=null) {
				marker.setAttribute(MESSAGE_ID_ATTRIBUTE_NAME, new Integer(messageId));
			}
		}catch(CoreException ex){
			SeamCorePlugin.getDefault().logError(ex);
		}
		return marker;
	}

	@Override
	public void registerPreferenceInfo() {
		PreferenceInfoManager.register(getProblemType(), new SeamPreferenceInfo());
	}
	
	class SeamPreferenceInfo implements IPreferenceInfo{

		@Override
		public String getPreferencePageId() {
			return PREFERENCE_PAGE_ID;
		}

		@Override
		public String getPropertyPageId() {
			return PROPERTY_PAGE_ID;
		}

		@Override
		public String getPluginId() {
			return SeamCorePlugin.PLUGIN_ID;
		}
		
	}

}
