/******************************************************************************* * Copyright (c) 2007, 2009 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.ui.navigator; import java.util.LinkedHashSet; import java.util.Set; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jdt.core.IType; import org.eclipse.ui.IMemento; import org.eclipse.ui.navigator.CommonNavigator; import org.eclipse.ui.navigator.ICommonContentExtensionSite; import org.eclipse.ui.navigator.ICommonContentProvider; import org.eclipse.ui.progress.IProgressConstants; import org.springframework.ide.eclipse.beans.core.BeansCorePlugin; 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.IBeansProject; import org.springframework.ide.eclipse.beans.core.model.IImportedBeansConfig; import org.springframework.ide.eclipse.beans.ui.BeansUIImages; import org.springframework.ide.eclipse.beans.ui.BeansUIPlugin; import org.springframework.ide.eclipse.beans.ui.model.BeansModelContentProvider; import org.springframework.ide.eclipse.beans.ui.model.BeansModelLabelDecorator; import org.springframework.ide.eclipse.core.SpringCore; import org.springframework.ide.eclipse.core.SpringCoreUtils; import org.springframework.ide.eclipse.core.io.ZipEntryStorage; import org.springframework.ide.eclipse.core.java.JdtUtils; import org.springframework.ide.eclipse.core.model.ILazyInitializedModelElement; import org.springframework.ide.eclipse.core.model.IModelElement; import org.springframework.ide.eclipse.core.model.IResourceModelElement; import org.springframework.ide.eclipse.core.model.ISourceModelElement; import org.springframework.ide.eclipse.core.model.ISpringProject; import org.springframework.ide.eclipse.core.model.ModelChangeEvent; import org.springframework.ide.eclipse.ui.SpringUIUtils; /** * This class is a content provider for the {@link CommonNavigator} which knows about the beans core model's * {@link IModelElement} elements. * @author Torsten Juergeleit * @author Christian Dupuis */ public class BeansNavigatorContentProvider extends BeansModelContentProvider implements ICommonContentProvider { private String providerID; @Override public Object[] getElements(Object inputElement) { if (BeansUIPlugin.PROJECT_EXPLORER_CONTENT_PROVIDER_ID.equals(providerID)) { return SpringCoreUtils.getSpringProjects().toArray(); } return super.getElements(inputElement); } @Override public boolean hasChildren(Object element) { return getChildren(element).length > 0; } @Override public Object[] getChildren(final Object parentElement) { if (parentElement instanceof ISpringProject) { IBeansProject beansProject = BeansCorePlugin.getModel().getProject( ((ISpringProject) parentElement).getProject()); if (beansProject != null && (!beansProject.getConfigs().isEmpty() || !beansProject.getConfigSets().isEmpty())) { return new Object[] { beansProject }; } } // check for lazy loading and/or long running elements; if a element is // marked to be long-running, execute the call to super.getChildren() // asynchronous and refresh the underlying viewer with the given parent // element (use parent because IBeansConfigSets need to be updated as // well) else if (parentElement instanceof ILazyInitializedModelElement && !((ILazyInitializedModelElement) parentElement).isInitialized()) { triggerDeferredElementLoading(parentElement, ((IModelElement) parentElement).getElementParent()); return IModelElement.NO_CHILDREN; } else if (parentElement instanceof IBeansImport) { return getImportChildren(parentElement); } return super.getChildren(parentElement); } protected Object[] getImportChildren(final Object parentElement) { Set<IImportedBeansConfig> importedBeansConfigs = ((IBeansImport) parentElement).getImportedBeansConfigs(); Set<Object> importedFiles = new LinkedHashSet<Object>(); for (IBeansConfig bc : importedBeansConfigs) { if (bc.isElementArchived()) { importedFiles.add(new ZipEntryStorage(bc)); } else { importedFiles.add(bc.getElementResource()); } } return importedFiles.toArray(new Object[importedFiles.size()]); } @Override protected Object[] getConfigSetChildren(IBeansConfigSet configSet) { Set<ISourceModelElement> children = new LinkedHashSet<ISourceModelElement>(); for (final IBeansConfig config : configSet.getConfigs()) { if (config instanceof ILazyInitializedModelElement && !((ILazyInitializedModelElement) config).isInitialized()) { triggerDeferredElementLoading(config, configSet); continue; } getConfigChildren(children, config); } return children.toArray(); } @Override protected Object[] getJavaTypeChildren(IType type) { // Prevent showing the Java -> Bean reference node in Spring Explorer if (BeansUIPlugin.SPRING_EXPLORER_CONTENT_PROVIDER_ID.equals(providerID)) { return IModelElement.NO_CHILDREN; } return super.getJavaTypeChildren(type); } private void triggerDeferredElementLoading(final Object config, final Object parent) { // first check if a matching job is already scheduled synchronized (getClass()) { Job[] buildJobs = Job.getJobManager().find(ModelJob.MODEL_CONTENT_FAMILY); for (int i = 0; i < buildJobs.length; i++) { Job curr = buildJobs[i]; if (curr instanceof ModelJob) { ModelJob job = (ModelJob) curr; if (job.isCoveredBy(config, parent)) { return; } } } } Job job = new ModelJob(config, parent, this); job.setPriority(Job.INTERACTIVE); job.setProperty(IProgressConstants.ICON_PROPERTY, BeansUIImages.DESC_OBJS_SPRING); job.schedule(); } private Object[] superGetChildren(Object parentElement) { return super.getChildren(parentElement); } @Override public synchronized void elementChanged(ModelChangeEvent event) { IModelElement element = event.getElement(); if (element instanceof IBeansProject) { IProject project = ((IBeansProject) element).getProject(); if (BeansUIPlugin.PROJECT_EXPLORER_CONTENT_PROVIDER_ID.equals(providerID)) { refreshViewerForElement(project); refreshViewerForElement(JdtUtils.getJavaProject(project)); } else if (BeansUIPlugin.SPRING_EXPLORER_CONTENT_PROVIDER_ID.equals(providerID)) { refreshViewerForElement(SpringCore.getModel().getProject(project)); } else { super.elementChanged(event); } } else if (element instanceof IBeansConfig) { IBeansConfig config = (IBeansConfig) element; // For a changed Spring beans config in the Eclipse Project Explorer // refresh all corresponding bean classes if (BeansUIPlugin.PROJECT_EXPLORER_CONTENT_PROVIDER_ID.equals(providerID)) { refreshBeanClasses(config); } // In order to refresh the meta data contributions we need refresh // the entire project refreshViewerForElement(config.getElementParent()); } else { super.elementChanged(event); } } /** * Refreshes the config file and all bean classes of a given beans config */ protected void refreshBeanClasses(IBeansConfig config) { Set<String> classes = config.getBeanClasses(); for (String clazz : classes) { IType type = JdtUtils.getJavaType(config.getElementResource().getProject(), clazz); if (type != null) { refreshViewerForElement(type); } } } public void init(ICommonContentExtensionSite config) { providerID = config.getExtension().getId(); } public void saveState(IMemento memento) { } public void restoreState(IMemento memento) { } @Override public String toString() { return String.valueOf(providerID); } /** * Internal {@link Job} that handles loading of {@link IBeansConfig} as Eclipse progress */ private static class ModelJob extends Job { public static final Object MODEL_CONTENT_FAMILY = new Object(); private final Object config; private final Object parent; private final BeansNavigatorContentProvider contentProvider; public ModelJob(Object config, Object parent, BeansNavigatorContentProvider contentProvider) { super("Initializing Spring Project '" + ((IResourceModelElement) config).getElementResource().getFullPath().toString().substring(1) + "'"); this.config = config; this.parent = parent; this.contentProvider = contentProvider; } @Override protected IStatus run(IProgressMonitor monitor) { synchronized (getClass()) { if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } Job[] buildJobs = Job.getJobManager().find(MODEL_CONTENT_FAMILY); for (int i = 0; i < buildJobs.length; i++) { Job curr = buildJobs[i]; if (curr != this && curr instanceof ModelJob) { ModelJob job = (ModelJob) curr; if (job.isCoveredBy(this)) { curr.cancel(); } } } } if (config instanceof IBeansProject) { IBeansProject bp = (IBeansProject) config; monitor = new SubProgressMonitor(monitor, bp.getConfigs().size()); monitor.beginTask("Initializing Spring Project '" + ((IResourceModelElement) config).getElementResource().getFullPath().toString() + "'", bp .getConfigs().size()); for (IBeansConfig beansConfig : bp.getConfigs()) { monitor.setTaskName("Loading '" + beansConfig.getElementName() + "'"); beansConfig.getBeans(); monitor.worked(1); } } else { monitor.beginTask("Loading '" + ((IResourceModelElement) config).getElementResource().getFullPath().toString() + "'", 1); contentProvider.superGetChildren(config); } SpringUIUtils.getStandardDisplay().asyncExec(new Runnable() { public void run() { contentProvider.refreshViewerForElement(config); contentProvider.refreshViewerForElement(parent); if (parent instanceof IBeansProject) { contentProvider.refreshViewerForElement(SpringCore.getModel().getProject( ((IBeansProject) parent).getProject())); } if (BeansUIPlugin.PROJECT_EXPLORER_CONTENT_PROVIDER_ID.equals(contentProvider.providerID) && config instanceof IResourceModelElement) { contentProvider.refreshViewerForElement(((IResourceModelElement) config).getElementResource()); } BeansModelLabelDecorator.update(); } }); monitor.worked(1); monitor.done(); return Status.OK_STATUS; } public boolean isCoveredBy(ModelJob other) { return other.parent.equals(parent) && other.config.equals(config); } public boolean isCoveredBy(Object otherConfig, Object otherParent) { return otherParent.equals(parent) && otherConfig.equals(config); } @Override public boolean belongsTo(Object family) { return MODEL_CONTENT_FAMILY == family; } } }