| /******************************************************************************* |
| * Copyright (c) 2001, 2008 Oracle Corporation and others. |
| * 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: |
| * Oracle Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jst.jsf.ui.internal.tagregistry; |
| |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.atomic.AtomicLong; |
| |
| 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.jobs.Job; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.viewers.IStructuredContentProvider; |
| import org.eclipse.jface.viewers.ITreeContentProvider; |
| import org.eclipse.jface.viewers.StructuredViewer; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.jst.jsf.common.runtime.internal.view.model.common.ITagAttribute; |
| import org.eclipse.jst.jsf.common.runtime.internal.view.model.common.ITagElement; |
| import org.eclipse.jst.jsf.common.runtime.internal.view.model.common.Namespace; |
| import org.eclipse.jst.jsf.core.internal.CompositeTagRegistryFactory; |
| import org.eclipse.jst.jsf.core.internal.ITagRegistryFactoryInfo; |
| import org.eclipse.jst.jsf.designtime.internal.view.model.ITagRegistry; |
| import org.eclipse.jst.jsf.designtime.internal.view.model.ITagRegistry.TagRegistryChangeEvent; |
| import org.eclipse.jst.jsf.designtime.internal.view.model.ITagRegistry.TagRegistryChangeEvent.EventType; |
| import org.eclipse.jst.jsf.designtime.internal.view.model.TagRegistryFactory; |
| import org.eclipse.jst.jsf.designtime.internal.view.model.TagRegistryFactory.TagRegistryFactoryException; |
| import org.eclipse.jst.jsf.ui.internal.JSFUiPlugin; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.ui.PlatformUI; |
| |
| /** |
| * Structured content provider for tag libraries. |
| * |
| * @author cbateman |
| * |
| */ |
| public class TaglibContentProvider implements IStructuredContentProvider, |
| ITreeContentProvider, ITagRegistry.ITagRegistryListener |
| { |
| private final static Object[] NO_CHILDREN = new Object[0]; |
| private IProject _curInput; |
| private Map<ITagRegistry, TagRegistryInstance> _curTagRegistries = |
| new HashMap<ITagRegistry, TagRegistryInstance>(); |
| private Viewer _curViewer; |
| private final AtomicLong _changeStamp = new AtomicLong( |
| 0); |
| |
| public Object[] getElements(final Object inputElement) |
| { |
| |
| if (inputElement instanceof IProject) |
| { |
| return _curTagRegistries.values().toArray(); |
| // return _rootNamespaces.values().toArray(); |
| } |
| |
| return NO_CHILDREN; |
| } |
| |
| public void dispose() |
| { |
| // nothing to do |
| } |
| |
| public void inputChanged(final Viewer viewer, final Object oldInput, |
| final Object newInput) |
| { |
| // update our change stamp to invalid outstanding update tasks |
| _changeStamp.incrementAndGet(); |
| _curViewer = viewer; |
| |
| if (oldInput instanceof IProject) |
| { |
| for (final TagRegistryInstance tagRegistry : _curTagRegistries.values()) |
| { |
| tagRegistry.getRegistry().removeListener(this); |
| } |
| } |
| |
| if (newInput instanceof IProject) |
| { |
| _curInput = (IProject) newInput; |
| |
| final Set<ITagRegistryFactoryInfo> factories = CompositeTagRegistryFactory |
| .getInstance().getAllTagRegistryFactories(); |
| |
| _curTagRegistries.clear(); |
| |
| for (ITagRegistryFactoryInfo factoryInfo : factories) |
| { |
| TagRegistryFactory factory = factoryInfo |
| .getTagRegistryFactory(); |
| ITagRegistry registry; |
| try |
| { |
| registry = factory.createTagRegistry(_curInput); |
| if (registry != null) |
| { |
| final TagRegistryInstance registryInstance = |
| new TagRegistryInstance(factoryInfo, registry); |
| _curTagRegistries.put(registry, registryInstance); |
| registry.addListener(this); |
| |
| new UpdateNamespacesListJob(_curInput, _changeStamp.get(), |
| registryInstance).schedule(); |
| } |
| } |
| catch (TagRegistryFactoryException e) |
| { |
| JSFUiPlugin.log(IStatus.ERROR, |
| "Problem getting tag registry", e); //$NON-NLS-1$ |
| } |
| } |
| } |
| else |
| { |
| _curInput = null; |
| _curTagRegistries.clear(); |
| } |
| } |
| |
| public Object[] getChildren(final Object parentElement) |
| { |
| if (parentElement instanceof IProject) |
| { |
| return _curTagRegistries.values().toArray(); |
| } |
| else if (parentElement instanceof TagRegistryInstance) |
| { |
| final TagRegistryInstance regInstance = (TagRegistryInstance) parentElement; |
| |
| if (!regInstance.isUpToDate()) |
| { |
| return new Object[] {new TreePlaceholder(Messages.TaglibContentProvider_Calculating, null)}; |
| } |
| return regInstance.getNamespaces().values().toArray(); |
| } |
| else if (parentElement instanceof Namespace) |
| { |
| final Namespace ns = (Namespace) parentElement; |
| |
| // this could be very expensive if not initialized |
| if (ns.isInitialized()) |
| { |
| return ((Namespace) parentElement).getViewElements().toArray(); |
| } |
| |
| // fire up a job that ensures the namespace is initialized |
| // and then fires refresh again on this element |
| final Job updateNamespaceJob = new Job(Messages.TaglibContentProvider_JobDesc) |
| { |
| @Override |
| protected IStatus run(final IProgressMonitor monitor) |
| { |
| ns.getViewElements(); |
| PlatformUI.getWorkbench().getDisplay().asyncExec( |
| new Runnable() |
| { |
| public void run() |
| { |
| // avoid infinite recursion |
| if (ns.isInitialized()) |
| { |
| TaglibContentProvider.this |
| .viewerRefresh(ns); |
| } |
| else |
| { |
| MessageDialog |
| .openError( |
| Display |
| .getCurrent() |
| .getActiveShell(), |
| Messages.TaglibContentProvider_NamespaceErrorTitle, |
| Messages.TaglibContentProvider_NamespaceErrorDescription); |
| } |
| } |
| }); |
| return Status.OK_STATUS; |
| } |
| }; |
| |
| updateNamespaceJob.schedule(); |
| |
| return new Object[] |
| { new TreePlaceholder(Messages.TaglibContentProvider_TagCalculatingWaitMessage, null) }; |
| } |
| else if (parentElement instanceof ITagElement) |
| { |
| final Map<String, ? extends ITagAttribute> attributes = ((ITagElement)parentElement).getAttributes(); |
| if (attributes != null) |
| { |
| return attributes.values().toArray(); |
| } |
| } |
| return NO_CHILDREN; |
| } |
| |
| public Object getParent(final Object element) |
| { |
| // no support for parent traversal right now |
| return null; |
| } |
| |
| public boolean hasChildren(final Object element) |
| { |
| // avoid an infinite refresh loop on the namespaces in the tag registry |
| if (element instanceof TagRegistryInstance) |
| { |
| return true; |
| } |
| // finding all children of a namespace can be expensive |
| else if (element instanceof Namespace) |
| { |
| return ((Namespace) element).hasViewElements(); |
| } |
| return getChildren(element).length > 0; |
| } |
| |
| public void registryChanged(final TagRegistryChangeEvent changeEvent) |
| { |
| if (_curViewer != null) |
| { |
| TagRegistryInstance registryInstance = |
| _curTagRegistries.get(changeEvent.getSource()); |
| |
| if (registryInstance != null) |
| { |
| _curViewer.getControl().getDisplay().asyncExec( |
| new RegistryChangeTask(changeEvent.getType(), changeEvent |
| .getAffectedObjects(), _changeStamp.get(),registryInstance)); |
| } |
| } |
| } |
| |
| private final class RegistryChangeTask implements Runnable |
| { |
| private final EventType _eventType; |
| private final long _timestamp; |
| private final List<? extends Namespace> _affectedObjects; |
| private final TagRegistryInstance _registryInstance; |
| |
| RegistryChangeTask(final TagRegistryChangeEvent.EventType eventType, |
| final List<? extends Namespace> affectedObjects, |
| final long timestamp, final TagRegistryInstance registryInstance) |
| { |
| _eventType = eventType; |
| _timestamp = timestamp; |
| _affectedObjects = affectedObjects; |
| _registryInstance = registryInstance; |
| } |
| |
| public void run() |
| { |
| // if changes have been made since this task was queued, then abort |
| // since we don't know if our data is still valid |
| if (_timestamp != TaglibContentProvider.this._changeStamp.get()) |
| { |
| return; |
| } |
| |
| switch (_eventType) |
| { |
| case ADDED_NAMESPACE: |
| case CHANGED_NAMESPACE: |
| { |
| for (final Namespace ns : _affectedObjects) |
| { |
| _registryInstance.getNamespaces().put(ns.getNSUri(), ns); |
| } |
| |
| viewerRefresh(_curInput); |
| } |
| break; |
| |
| case REMOVED_NAMESPACE: |
| { |
| for (final Namespace ns : _affectedObjects) |
| { |
| _registryInstance.getNamespaces().remove(ns.getNSUri()); |
| } |
| viewerRefresh(_curInput); |
| } |
| break; |
| |
| case REGISTRY_DISPOSED: |
| { |
| _registryInstance.getRegistry().removeListener(TaglibContentProvider.this); |
| _curTagRegistries.remove(_registryInstance); |
| viewerRefresh(_curInput); |
| } |
| } |
| } |
| } |
| |
| private void viewerRefresh(final Object parentElement) |
| { |
| if (_curViewer instanceof StructuredViewer) |
| { |
| final StructuredViewer viewer = (StructuredViewer) _curViewer; |
| viewer.refresh(parentElement); |
| } |
| else |
| { |
| _curViewer.refresh(); |
| } |
| } |
| |
| private class UpdateNamespacesListJob extends Job |
| { |
| |
| private final long _timestamp; |
| private final IProject _project; |
| private final TagRegistryInstance _registry; |
| |
| public UpdateNamespacesListJob(final IProject project, |
| final long timestamp, final TagRegistryInstance registry) |
| { |
| super("Updating available namespaces for project " //$NON-NLS-1$ |
| + project.getName()); |
| _project = project; |
| _timestamp = timestamp; |
| _registry = registry; |
| } |
| |
| @Override |
| protected IStatus run(final IProgressMonitor monitor) |
| { |
| if (!_project.isAccessible() |
| || _registry.isUpToDate()) |
| { |
| return new Status(IStatus.CANCEL, JSFUiPlugin.PLUGIN_ID, ""); //$NON-NLS-1$ |
| } |
| |
| final Collection<? extends Namespace> libs = _registry.getRegistry() |
| .getAllTagLibraries(); |
| _registry.getNamespaces().clear(); |
| |
| for (Namespace ns : libs) |
| { |
| if (ns.getNSUri() != null) |
| { |
| _registry.getNamespaces().put(ns.getNSUri(), ns); |
| |
| } |
| } |
| |
| _registry.setUpToDate(true); |
| PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() |
| { |
| public void run() |
| { |
| // only bother if the provider hasn't changed asynchronously |
| if (_timestamp == TaglibContentProvider.this._changeStamp |
| .get()) |
| { |
| viewerRefresh(_curInput); |
| } |
| } |
| }); |
| |
| return Status.OK_STATUS; |
| } |
| } |
| |
| static class TagRegistryInstance |
| { |
| private final ITagRegistryFactoryInfo _info; |
| private final ITagRegistry _registry; |
| private final Map<String, Namespace> _namespaces; |
| private boolean _isUpToDate; |
| |
| public TagRegistryInstance(final ITagRegistryFactoryInfo info, |
| ITagRegistry registry) |
| { |
| _info = info; |
| _registry = registry; |
| _namespaces = new ConcurrentHashMap<String, Namespace>(); |
| } |
| |
| public ITagRegistryFactoryInfo getInfo() |
| { |
| return _info; |
| } |
| |
| public ITagRegistry getRegistry() |
| { |
| return _registry; |
| } |
| |
| public Map<String, Namespace> getNamespaces() |
| { |
| return _namespaces; |
| } |
| |
| public synchronized boolean isUpToDate() |
| { |
| return _isUpToDate; |
| } |
| |
| public synchronized void setUpToDate(boolean isUpToDate) |
| { |
| _isUpToDate = isUpToDate; |
| } |
| } |
| |
| /** |
| * Takes the place of a real tree model object while the real object is |
| * being retrieved. |
| * |
| */ |
| public static class TreePlaceholder |
| { |
| private final String _text; |
| private final Image _image; |
| |
| TreePlaceholder(final String text, final Image image) |
| { |
| _text = text; |
| _image = image; |
| } |
| |
| /** |
| * @return the placeholder text or null if none |
| */ |
| public String getText() |
| { |
| return _text; |
| } |
| |
| /** |
| * @return the image or null if none |
| */ |
| public Image getImage() |
| { |
| return _image; |
| } |
| |
| } |
| } |