blob: 9a56b804d795b65502c2dcacf1bff0278991d3f4 [file] [log] [blame]
/*******************************************************************************
* 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.core.internal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.jst.jsf.common.internal.policy.CanonicallyOrderedIteratorPolicy;
import org.eclipse.jst.jsf.common.internal.policy.IIteratorPolicy;
import org.eclipse.jst.jsf.common.internal.policy.IdentifierOrderedIteratorPolicy;
import org.eclipse.jst.jsf.common.internal.strategy.AbstractIdentifiableStrategy;
import org.eclipse.jst.jsf.common.internal.strategy.IIdentifiableStrategy;
import org.eclipse.jst.jsf.common.internal.strategy.IteratorPolicyBasedStrategyComposite;
import org.eclipse.jst.jsf.designtime.internal.view.model.ITagRegistry;
import org.eclipse.jst.jsf.designtime.internal.view.model.TagRegistryFactory.TagRegistryFactoryException;
/**
* Employs a policy-based strategy to construct a TagRegistry for a particular
* content-type in a particular project.
*
* @author cbateman
*
*/
public final class CompositeTagRegistryFactory
{
private static CompositeTagRegistryFactory INSTANCE;
private static ITagRegistryFactoryProvider TEST_PROVIDER;
/**
* For JUNIT TEST ONLY!!!!!
* @param factoryProvider
*/
public synchronized void setTestInjectedProvider(final ITagRegistryFactoryProvider factoryProvider)
{
TEST_PROVIDER = factoryProvider;
// TODO: this is risky
_cachedExtensionsByType.clear();
}
/**
* @return the single instance of the registry factory
*/
public static synchronized CompositeTagRegistryFactory getInstance()
{
if (INSTANCE == null)
{
INSTANCE = new CompositeTagRegistryFactory();
}
return INSTANCE;
}
private final Map<TagRegistryIdentifier, Set<ITagRegistryFactoryInfo>> _cachedExtensionsByType =
new HashMap<TagRegistryIdentifier, Set<ITagRegistryFactoryInfo>>(4);
private CompositeTagRegistryFactory()
{
// singleton: no external instantiation
}
/**
* @param id
* @return a tag registry for the id or null if there isn't one.
*/
public final ITagRegistry getRegistry(final TagRegistryIdentifier id)
{
final Set<ITagRegistryFactoryInfo> handlers = getAllTagRegistryFactories();
final Set<ITagRegistryFactoryInfo> matchingHandlers = findMatchingExtensions(
id, handlers);
if (matchingHandlers.size() > 0)
{
// optimize: if there is only one handler, no need for strategy
if (matchingHandlers.size() == 1)
{
try
{
return matchingHandlers.iterator().next().getTagRegistryFactory()
.createTagRegistry(id.getProject());
}
catch (TagRegistryFactoryException e)
{
JSFCorePlugin.log(e, "While trying to acquire a registry"); //$NON-NLS-1$
}
}
else
{
// for now, we will order by the canonical name of the extension
// id in ascending order. Later, we may make this preference
// based.
final TagRegistrySelectionStrategy selectionStrategy = new TagRegistrySelectionStrategy(
new CanonicallyOrderedIteratorPolicy<String>());
for (final Iterator<ITagRegistryFactoryInfo> it = matchingHandlers
.iterator(); it.hasNext();)
{
selectionStrategy.addStrategy(it.next().getTagRegistryFactory());
}
return selectionStrategy.perform(id.getProject());
}
}
return null;
}
/**
* @return get all tag registry factories
*/
public Set<ITagRegistryFactoryInfo> getAllTagRegistryFactories()
{
List<String> selectionOrder = new ArrayList<String>();
selectionOrder.add("testInjection"); //$NON-NLS-1$
selectionOrder.add("extensionPointInjection"); //$NON-NLS-1$
selectionOrder.add("platformDefault"); //$NON-NLS-1$
IdentifierOrderedIteratorPolicy<String> policy = new IdentifierOrderedIteratorPolicy<String>(selectionOrder);
// ignore iterator values that don't exist in the list of possible selections.
policy.setExcludeNonExplicitValues(true);
final TagRegistryFactoryProviderSelectionStrategy providerSelector
= new TagRegistryFactoryProviderSelectionStrategy(policy);
providerSelector.addStrategy(
new AbstractIdentifiableStrategy<IProject, ITagRegistryFactoryProvider, String>("testInjection", "FIXME: not for display", null) //$NON-NLS-1$ //$NON-NLS-2$
{
@Override
public ITagRegistryFactoryProvider perform(IProject input)
throws Exception
{
ITagRegistryFactoryProvider injectedProvider = null;
synchronized(CompositeTagRegistryFactory.class)
{
injectedProvider = TEST_PROVIDER;
}
if (injectedProvider != null)
{
final ITagRegistryFactoryProvider useMe = injectedProvider;
return new AbstractTagRegistryFactoryProvider()
{
@Override
public Set<ITagRegistryFactoryInfo> getTagRegistryFactories()
{
return useMe.getTagRegistryFactories();
}
};
}
return null;
}
});
providerSelector.addStrategy(
new AbstractIdentifiableStrategy<IProject, ITagRegistryFactoryProvider, String>("platformDefault", "FIXME: not for display", null) //$NON-NLS-1$ //$NON-NLS-2$
{
@Override
public ITagRegistryFactoryProvider perform(IProject input)
throws Exception
{
return new AbstractTagRegistryFactoryProvider()
{
@Override
public Set<ITagRegistryFactoryInfo> getTagRegistryFactories()
{
return TagLibraryRegistryLoader.getAllHandlers();
}
};
}
});
ITagRegistryFactoryProvider provider = providerSelector.perform(null);
if (provider != null)
{
return provider.getTagRegistryFactories();
}
return Collections.emptySet();
}
private Set<ITagRegistryFactoryInfo> findMatchingExtensions(
TagRegistryIdentifier id, Set<ITagRegistryFactoryInfo> handlers)
{
Set<ITagRegistryFactoryInfo> matching = _cachedExtensionsByType.get(id);
if (matching == null)
{
matching = new HashSet<ITagRegistryFactoryInfo>(4);
for (final ITagRegistryFactoryInfo handler : handlers)
{
if (handler.getContentTypes().contains(id.getContentType())
&& handler.getTagRegistryFactory().projectIsValid(id.getProject()))
{
matching.add(handler);
}
}
// if there is nothing matching, just store the empty set and
// discard the extra memory
if (matching.size() > 0)
{
_cachedExtensionsByType.put(id, matching);
}
else
{
_cachedExtensionsByType.put(id, Collections.EMPTY_SET);
}
}
return matching;
}
/**
* Identifies a content type/project context in which to request a tag
* registry.
*
* @author cbateman
*
*/
public final static class TagRegistryIdentifier
{
private final IProject _project;
private final IContentType _contentType;
/**
* @param project
* @param contentType
*/
public TagRegistryIdentifier(final IProject project,
final IContentType contentType)
{
_project = project;
_contentType = contentType;
}
/**
* @return project
*/
public IProject getProject()
{
return _project;
}
/**
* @return content type
*/
public IContentType getContentType()
{
return _contentType;
}
public boolean equals(final Object o) {
if (o instanceof TagRegistryIdentifier) {
final TagRegistryIdentifier other = (TagRegistryIdentifier)o;
final int otherProjectHash = other.getProject() != null ? other.getProject().hashCode() : 0;
final int thisProjectHash = getProject() != null ? getProject().hashCode() : 0;
if (otherProjectHash == thisProjectHash &&
other.getContentType().equals(this.getContentType()))
return true;
}
return false;
}
public int hashCode() {
return (getProject() != null ? getProject().hashCode() : 0) + 7*_contentType.hashCode();
}
}
private static class TagRegistrySelectionStrategy
extends
IteratorPolicyBasedStrategyComposite<IProject, ITagRegistry, ITagRegistry, String, IIdentifiableStrategy<IProject, ITagRegistry, String>>
{
private static final ITagRegistry NO_RESULT = null;
protected TagRegistrySelectionStrategy(IIteratorPolicy<String> policy)
{
super(policy);
}
@Override
public ITagRegistry getNoResult()
{
return NO_RESULT;
}
}
private static class TagRegistryFactoryProviderSelectionStrategy
extends
IteratorPolicyBasedStrategyComposite<IProject, ITagRegistryFactoryProvider, ITagRegistryFactoryProvider, String, IIdentifiableStrategy<IProject, ITagRegistryFactoryProvider, String>>
{
protected TagRegistryFactoryProviderSelectionStrategy(
IIteratorPolicy<String> policy)
{
super(policy);
}
private static final ITagRegistryFactoryProvider NO_RESULT = null;
@Override
public ITagRegistryFactoryProvider getNoResult()
{
return NO_RESULT;
}
}
}