/******************************************************************************* * Copyright (c) 2007, 2014 Spring IDE Developers * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Spring IDE Developers - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.beans.core.internal.model.validation; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.IAnnotation; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; import org.springframework.ide.eclipse.beans.core.BeansCorePlugin; import org.springframework.ide.eclipse.beans.core.internal.model.BeansModelUtils; import org.springframework.ide.eclipse.beans.core.internal.model.resources.BeansResourceChangeListener; import org.springframework.ide.eclipse.beans.core.model.IBean; import org.springframework.ide.eclipse.beans.core.model.IBeansConfig; import org.springframework.ide.eclipse.beans.core.model.IBeansConfigSet; import org.springframework.ide.eclipse.beans.core.model.IBeansImport; import org.springframework.ide.eclipse.beans.core.model.IBeansModelElement; import org.springframework.ide.eclipse.beans.core.model.IBeansProject; import org.springframework.ide.eclipse.beans.core.model.IImportedBeansConfig; import org.springframework.ide.eclipse.core.MarkerUtils; import org.springframework.ide.eclipse.core.java.ITypeStructureCache; import org.springframework.ide.eclipse.core.java.JdtUtils; import org.springframework.ide.eclipse.core.java.TypeStructureState; import org.springframework.ide.eclipse.core.model.IModelElement; import org.springframework.ide.eclipse.core.model.IResourceModelElement; import org.springframework.ide.eclipse.core.model.ISpringProject; import org.springframework.ide.eclipse.core.model.validation.AbstractValidator; import org.springframework.ide.eclipse.core.model.validation.IValidationContext; import org.springframework.ide.eclipse.core.model.validation.IValidationElementLifecycleManager; import org.springframework.ide.eclipse.core.model.validation.IValidationElementLifecycleManagerExtension; import org.springframework.ide.eclipse.core.model.validation.IValidator; /** * {@link IValidator} implementation that is responsible for validating the {@link IBeansModelElement}s. * @author Torsten Juergeleit * @author Christian Dupuis * @author Martin Lippert * @since 2.0 */ public class BeansConfigValidator extends AbstractValidator { private Set<String> affectedBeans = new LinkedHashSet<String>(); public Set<IResource> deriveResources(Object object) { Set<IResource> resources = new LinkedHashSet<IResource>(); if (object instanceof ISpringProject) { object = BeansCorePlugin.getModel().getProject(((ISpringProject) object).getProject()); } else if (object instanceof IFile) { object = BeansCorePlugin.getModel().getConfig((IFile) object); } if (object instanceof IBeansModelElement) { if (object instanceof IBeansProject) { for (IBeansConfig config : ((IBeansProject) object).getConfigs()) { resources.add(config.getElementResource()); } } else if (object instanceof IBeansConfigSet) { for (IBeansConfig config : ((IBeansConfigSet) object).getConfigs()) { resources.add(config.getElementResource()); } } else if (object instanceof IResourceModelElement) { resources.add(((IResourceModelElement) object).getElementResource()); } } return resources; } @Override public void cleanup(IResource resource, IProgressMonitor monitor) throws CoreException { MarkerUtils.deleteAllMarkers(resource, getMarkerId()); } public Set<IResource> getAffectedResources(IResource resource, int kind, int deltaKind) throws CoreException { Set<IResource> resources = new LinkedHashSet<IResource>(); if (resource instanceof IFile) { // First check for a beans config file Set<IBeansConfig> configs = BeansCorePlugin.getModel().getConfigs((IFile) resource, true); if (configs != null && configs.size() > 0) { for (IBeansConfig beansConfig : configs) { // Resolve imported config files to their root importing one if (beansConfig instanceof IImportedBeansConfig) { IBeansConfig importingConfig = BeansModelUtils.getParentOfClass(beansConfig, IBeansConfig.class); if (importingConfig != null) { resources.add(importingConfig.getElementResource()); addBeans(importingConfig); } } else { resources.add(resource); addBeans(beansConfig); } } // Add resources that are in a config set with the changed resources propagateChangedResourceToConfigSets(resources); } else if (BeansResourceChangeListener.requiresRefresh((IFile) resource)) { propagateChangedResourceToProject(resource, resources); } else if (kind != IncrementalProjectBuilder.FULL_BUILD) { // Now check for bean classes and java structure TypeStructureState structureState = getProjectContributorState().get(TypeStructureState.class); BeansTypeHierachyState hierachyState = getProjectContributorState().get(BeansTypeHierachyState.class); if (structureState == null || structureState.hasStructuralChanges(resource, ITypeStructureCache.FLAG_ANNOTATION | ITypeStructureCache.FLAG_ANNOTATION_VALUE | ITypeStructureCache.FLAG_TAB_BITS)) { // Capture removal of java source files if (deltaKind == IResourceDelta.REMOVED && resource.getName().endsWith(JdtUtils.JAVA_FILE_EXTENSION)) { propagateChangedResourceToProject(resource, resources); } else { for (IBean bean : hierachyState.getBeansByContainingTypes(resource)) { IBeansConfig beansConfig = BeansModelUtils.getConfig(bean); // Resolve imported config files to their root importing one if (beansConfig instanceof IImportedBeansConfig) { IBeansConfig importingConfig = BeansModelUtils.getParentOfClass(beansConfig, IBeansConfig.class); if (importingConfig != null) { resources.add(importingConfig.getElementResource()); affectedBeans.add(bean.getElementID()); } } else { resources.add(beansConfig.getElementResource()); affectedBeans.add(bean.getElementID()); } // capture all beans if configuration class has changed if (isConfigurationBean(bean)) { addBeans(beansConfig); } } } } } } return resources; } private boolean isConfigurationBean(IBean bean) { IType beanType = BeansModelUtils.resolveBeanType(bean); if (beanType != null) { try { for(IAnnotation annotation : beanType.getAnnotations()) { if ("Configuration".equals(annotation.getElementName())) { return true; } } } catch (JavaModelException e) { // ignore, no annotations can be found } } return false; } /** * Propagates a change to a particular resource to its project so that all {@link IBeansConfig}s will get * revalidated. */ private void propagateChangedResourceToProject(IResource resource, Set<IResource> resources) { IBeansProject beansProject = BeansCorePlugin.getModel().getProject(resource.getProject()); if (beansProject != null) { for (IBeansConfig beansConfig : beansProject.getConfigs()) { resources.add(beansConfig.getElementResource()); addBeans(beansConfig); } } } /** * Add resources that share a config set to the list. */ private void propagateChangedResourceToConfigSets(Set<IResource> resources) { for (IResource resource : new HashSet<IResource>(resources)) { IBeansConfig beansConfig = BeansCorePlugin.getModel().getConfig((IFile) resource); for (IBeansConfigSet beansConfigSet : BeansModelUtils.getConfigSets(beansConfig)) { for (IBeansConfig bc : beansConfigSet.getConfigs()) { if (!resources.contains(bc.getElementResource())) { resources.add(bc.getElementResource()); addBeans(bc); } } } for (IBeansProject beansProject : BeansCorePlugin.getModel().getProjects()) { for (IBeansConfig bc : beansProject.getConfigs()) { for (IBeansImport beansImport : bc.getImports()) { for (IImportedBeansConfig importedBeansConfig : beansImport.getImportedBeansConfigs()) { if (resource.equals(importedBeansConfig.getElementResource())) { if (!resources.contains(bc.getElementResource())) { resources.add(bc.getElementResource()); addBeans(bc); } } } } } } } } @Override protected IValidationContext createContext(IResourceModelElement rootElement, IResourceModelElement contextElement) { if (rootElement instanceof IBeansConfig) { return new BeansValidationContext((IBeansConfig) rootElement, contextElement); } return null; } @Override protected boolean supports(IModelElement element) { // Validate only those beans that have been changed if (element instanceof IBean) { if (affectedBeans.contains(element.getElementID())) { return true; } else if (((IBean) element).isInnerBean()) { return supports(BeansModelUtils.getParentOfClass(element, IBean.class)); } } // Stop at imports because the contents is validated on the root config level else if (element instanceof IBeansModelElement || element instanceof IBeansImport) { return true; } return false; } /** * Check whether the element is defiend with a beans profile and if so, if this profile is activated in the * validation context * * @see org.springframework.ide.eclipse.core.model.validation.AbstractValidator#shouldValidate(org.springframework.ide.eclipse.core.model.IModelElement, org.springframework.ide.eclipse.core.model.validation.IValidationContext) */ @Override protected boolean shouldValidate(IModelElement element, IValidationContext validationContext) { IResourceModelElement contextElement = validationContext.getContextElement(); return !BeansModelUtils.isProfileDisabled(contextElement, element); } private void addBeans(IBeansConfig beansConfig) { for (IBean bean : BeansModelUtils.getBeans(beansConfig)) { affectedBeans.add(bean.getElementID()); } } @Override protected IValidationElementLifecycleManager createValidationElementLifecycleManager() { return new BeanElementLifecycleManager(); } private static class BeanElementLifecycleManager implements IValidationElementLifecycleManagerExtension { private IBeansConfig rootElement = null; @SuppressWarnings("unused") private int kind = -1; /** * {@inheritDoc} */ public void destroy() { } /** * {@inheritDoc} */ public Set<IResourceModelElement> getContextElements() { Set<IResourceModelElement> contextElements = new LinkedHashSet<IResourceModelElement>(); contextElements.addAll(BeansModelUtils.getConfigSets(rootElement)); if (contextElements.isEmpty()) { contextElements.add(rootElement); } return contextElements; } /** * {@inheritDoc} */ public IResourceModelElement getRootElement() { return rootElement; } /** * {@inheritDoc} */ public void init(IResource resource) { if (resource instanceof IFile) { rootElement = BeansCorePlugin.getModel().getConfig((IFile) resource); } } /** * {@inheritDoc} */ public void setKind(int kind) { this.kind = kind; } } }