| /******************************************************************************* |
| * Copyright (c) 2010 IBM 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: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.wst.sse.ui.internal.comment; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IExtensionRegistry; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.InvalidRegistryObjectException; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.content.IContentType; |
| import org.eclipse.jface.text.ITypedRegion; |
| import org.eclipse.wst.sse.ui.internal.Logger; |
| import org.eclipse.wst.sse.ui.internal.SSEUIPlugin; |
| |
| /** |
| * <p>The registry of {@link CommentingStrategy}s defined by the <code>org.eclipse.wst.sse.ui.commentinStrategy</code> |
| * extension point.</p> |
| */ |
| public class CommentingStrategyRegistry { |
| /** The extension schema name of the extension point */ |
| private static final String EXTENSION_POINT = "commentingStrategy"; //$NON-NLS-1$ |
| |
| /** The extension schema name of proposal block comment child elements. */ |
| private static final String ELEM_PROPOSAL_BLOCK_COMMENTING_STRATEGY = "blockCommentingStrategy"; //$NON-NLS-1$ |
| |
| /** The extension schema name of proposal line comment child elements. */ |
| private static final String ELEM_PROPOSAL_LINE_COMMENTING_STRATEGY = "lineCommentingStrategy"; //$NON-NLS-1$ |
| |
| /** The extension schema name of the content type child elements. */ |
| private static final String ELEM_CONTENT_TYPE = "contentType"; //$NON-NLS-1$ |
| |
| /** The extension schema name of the required partition types child elements */ |
| private static final String ELEM_REQUIRED_PARTITION_TYPES= "requiredPartitionTypes"; //$NON-NLS-1$ |
| |
| /** The extension schema name of the allowable partition types child elements */ |
| private static final String ELEM_ALLOWABLE_PARTITION_TYPES= "allowablePartitionTypes"; //$NON-NLS-1$ |
| |
| /** The extension schema name of partition type child elements */ |
| private static final String ELEM_PARTITION_TYPE= "partitionType"; //$NON-NLS-1$ |
| |
| /** The extension schema name of the prefix attribute */ |
| private static final String ATTR_PREFIX = "prefix"; //$NON-NLS-1$ |
| |
| /** The extension schema name of the suffix attribute */ |
| private static final String ATTR_SUFFIX = "suffix"; //$NON-NLS-1$ |
| |
| /** The extension schema name of the associatedCommentPartitionTypeID attribute */ |
| private static final String ATTR_ASSOCIATED_COMMENT_PARTITION_TPYPE_ID = "associatedCommentPartitionTypeID"; //$NON-NLS-1$ |
| |
| /** The extension schema name of the anyPartitionType attribute */ |
| private static final String ATTR_ANY_PARTITION_TYPE = "anyPartitionType"; //$NON-NLS-1$ |
| |
| /** The extension schema name for ID attribute */ |
| private static final String ATTR_ID= "id"; //$NON-NLS-1$ |
| |
| /** the singleton instance of the registry */ |
| private static CommentingStrategyRegistry fSingleton = null; |
| |
| /** <code>true</code> if this registry has been loaded. */ |
| private boolean fLoaded; |
| |
| /** |
| * <p>Registry of content type IDs to {@link BlockCommentingStrategy}s</p> |
| * |
| * <code>{@link Map}<{@link String}, {@link List}<{@link BlockContentType}>></code> |
| * <ul> |
| * <li><b>key:</b> content type ID</li> |
| * <li><b>value:</b> {@link List} of associated {@link BlockContentType}s</li> |
| * <ul> |
| */ |
| private Map fBlockCommentTypes; |
| |
| /** |
| * <p>Registry of content type IDs to {@link LineCommentingStrategy}s</p> |
| * |
| * <code>{@link Map}<{@link String}, {@link List}<{@link LineContentType}>></code> |
| * <ul> |
| * <li><b>key:</b> content type ID</li> |
| * <li><b>value:</b> {@link List} of associated {@link LineContentType}s</li> |
| * <ul> |
| */ |
| private Map fLineCommentTypes; |
| |
| /** |
| * @return the single instance of the {@link CommentingStrategyRegistry} |
| */ |
| public static synchronized CommentingStrategyRegistry getDefault() { |
| if(fSingleton == null) { |
| fSingleton = new CommentingStrategyRegistry(); |
| } |
| |
| return fSingleton; |
| } |
| |
| /** |
| * Singleton constructor for the registry |
| */ |
| private CommentingStrategyRegistry() { |
| this.fLoaded = false; |
| this.fBlockCommentTypes = new HashMap(); |
| this.fLineCommentTypes = new HashMap(); |
| } |
| |
| /** |
| * @param contentTypeID get only {@link BlockCommentingStrategy}s associated with this content type |
| * @param regions get only {@link BlockCommentingStrategy}s associated with these types of regions |
| * @return all the {@link BlockCommentingStrategy}s associated with the given content type and regions |
| */ |
| public CommentingStrategy getBlockCommentingStrategy(String contentTypeID, ITypedRegion[] regions) { |
| return getCommentingStrategy(contentTypeID, regions, this.fBlockCommentTypes); |
| } |
| |
| /** |
| * @param contentTypeID get only {@link LineCommentingStrategy}s associated with this content type |
| * @param regions get only {@link LineCommentingStrategy}s associated with these types of regions |
| * @return all the {@link LineCommentingStrategy}s associated with the given content type and regions |
| */ |
| public CommentingStrategy getLineCommentingStrategy(String contentTypeID, ITypedRegion[] regions) { |
| return getCommentingStrategy(contentTypeID, regions, this.fLineCommentTypes); |
| } |
| |
| /** |
| * <p>get all the {@link CommentingStrategy}s associated with the given content type and regions |
| * from the given registry</p> |
| * |
| * @param contentTypeID get only {@link CommentingStrategy}s associated with this content type |
| * @param regions get only {@link CommentingStrategy}s associated with these types of regions |
| * @param registry get the {@link CommentingStrategy}s from this registry |
| * @return all the {@link CommentingStrategy}s associated with the given content type and regions |
| * from the given registry |
| */ |
| private CommentingStrategy getCommentingStrategy(String contentTypeID, ITypedRegion[] regions, Map registry) { |
| ensureExtensionPointRead(); |
| |
| CommentingStrategy match = null; |
| IContentType contentType = Platform.getContentTypeManager().getContentType(contentTypeID); |
| |
| /* get all the commenting strategies for the given content type id, |
| * including those registered for parent content types |
| */ |
| List possibleCommentingStrategies = new ArrayList(); |
| while(contentType != null) { |
| List contentTypeCommentingStrategies = (List)registry.get(contentType.getId()); |
| if(contentTypeCommentingStrategies != null && contentTypeCommentingStrategies.size() > 0) { |
| possibleCommentingStrategies.addAll(contentTypeCommentingStrategies); |
| } |
| contentType = contentType.getBaseType(); |
| } |
| |
| /* find the commenting strategy applicable for the given regions, |
| * because strategies were added starting from the most specific |
| * content type first, the most specific strategy will always be chosen |
| */ |
| for(int i = 0; i < possibleCommentingStrategies.size() && match == null; ++i) { |
| CommentingStrategy commentType = (CommentingStrategy)possibleCommentingStrategies.get(i); |
| if(commentType.isApplicableFor(regions)) { |
| match = commentType; |
| } |
| } |
| |
| return match; |
| } |
| |
| /** |
| * <p>Ensures that the extensions are read and this registry is built</p> |
| */ |
| private void ensureExtensionPointRead() { |
| if(!fLoaded) { |
| load(); |
| fLoaded = true; |
| } |
| } |
| |
| /** |
| * <p>Load the extension points into the registry</p> |
| */ |
| private void load() { |
| IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry(); |
| List extensionElements = new ArrayList(Arrays.asList(extensionRegistry.getConfigurationElementsFor(SSEUIPlugin.ID, EXTENSION_POINT))); |
| |
| //for each extension |
| for (Iterator iter= extensionElements.iterator(); iter.hasNext();) { |
| IConfigurationElement element = (IConfigurationElement) iter.next(); |
| try { |
| CommentingStrategy newCommentingStrategy = null; |
| Map commentingStrategyRegistry = null; |
| //either a block or line commenting strategy |
| if(element.getName().equals(ELEM_PROPOSAL_BLOCK_COMMENTING_STRATEGY)) { |
| String prefix = element.getAttribute(ATTR_PREFIX); |
| checkExtensionAttributeNotNull(prefix, ATTR_PREFIX, element); |
| |
| String suffix = element.getAttribute(ATTR_SUFFIX); |
| checkExtensionAttributeNotNull(suffix, ATTR_SUFFIX, element); |
| |
| if(prefix != null && suffix != null) { |
| newCommentingStrategy = new BlockCommentingStrategy(prefix, suffix); |
| commentingStrategyRegistry = this.fBlockCommentTypes; |
| } |
| } else if(element.getName().equals(ELEM_PROPOSAL_LINE_COMMENTING_STRATEGY)) { |
| String prefix = element.getAttribute(ATTR_PREFIX); |
| checkExtensionAttributeNotNull(prefix, ATTR_PREFIX, element); |
| |
| if(prefix != null) { |
| newCommentingStrategy = new LineCommentingStrategy(prefix); |
| commentingStrategyRegistry = this.fLineCommentTypes; |
| } |
| } |
| |
| //add the new strategy to the registry |
| if(commentingStrategyRegistry != null && newCommentingStrategy != null) { |
| addCommentingStrategyToRegistry(element, commentingStrategyRegistry, newCommentingStrategy); |
| } else { |
| Logger.log(Logger.WARNING, "Invalid CommentingStrategy extension: " + element); //$NON-NLS-1$ |
| } |
| } catch (CoreException e) { |
| Logger.logException(e); |
| } catch (InvalidRegistryObjectException x) { |
| /* Element is not valid any longer as the contributing plug-in was unloaded or for |
| * some other reason. Do not include the extension in the list and log it |
| */ |
| String message = "The extension ''" + element.toString() + "'' is invalid."; //$NON-NLS-1$ //$NON-NLS-2$ |
| IStatus status= new Status(IStatus.WARNING, SSEUIPlugin.ID, IStatus.WARNING, message, x); |
| Logger.log(status); |
| } |
| } |
| } |
| |
| /** |
| * <p>Checks that the given attribute value is not <code>null</code>.</p> |
| * |
| * @param value the object to check if not null |
| * @param attribute the attribute |
| * |
| * @throws InvalidRegistryObjectException if the registry element is no longer valid |
| * @throws CoreException if <code>value</code> is <code>null</code> |
| */ |
| private static void checkExtensionAttributeNotNull(Object value, String attribute, |
| IConfigurationElement element) throws InvalidRegistryObjectException, CoreException { |
| |
| if (value == null) { |
| String message = "The extension \"" + element.getDeclaringExtension().getUniqueIdentifier() + //$NON-NLS-1$ |
| "\" from plug-in \"" + element.getContributor().getName() + //$NON-NLS-1$ |
| "\" did not specify a value for the required \"" + attribute + //$NON-NLS-1$ |
| "\" attribute for the element \"" + element.getName() + "\". Disabling the extension."; //$NON-NLS-1$ //$NON-NLS-2$ |
| IStatus status= new Status(IStatus.WARNING, SSEUIPlugin.ID, IStatus.OK, message, null); |
| throw new CoreException(status); |
| } |
| } |
| |
| /** |
| * <p>Using the content type element children of the given element add a copy of the given |
| * base commenting strategy to the given registry</p> |
| * |
| * @param element a {@link IConfigurationElement} with contentType element children |
| * @param commentingStrategyRegistry {@link Map} of content type ids to {@link CommentingStrategy}s to register |
| * the given {@link CommentingStrategy} with based on the <code>element</code> |
| * @param baseCommentingStrategy {@link CommentingStrategy} that will be cloned and configured for each |
| * content type in the given <code>element</code> |
| */ |
| private static void addCommentingStrategyToRegistry(IConfigurationElement element, |
| Map commentingStrategyRegistry, CommentingStrategy baseCommentingStrategy) { |
| |
| //get all the content type elements |
| IConfigurationElement[] contentTypeElements = element.getChildren(ELEM_CONTENT_TYPE); |
| if(contentTypeElements.length > 0) { |
| for(int contentTypeIndex = 0; contentTypeIndex < contentTypeElements.length; ++contentTypeIndex) { |
| try { |
| String contentTypeID = contentTypeElements[contentTypeIndex].getAttribute(ATTR_ID); |
| checkExtensionAttributeNotNull(contentTypeID, ATTR_ID, contentTypeElements[contentTypeIndex]); |
| |
| List commentTypes = (List)commentingStrategyRegistry.get(contentTypeID); |
| if(commentTypes == null) { |
| commentTypes = new ArrayList(); |
| commentingStrategyRegistry.put(contentTypeID, commentTypes); |
| } |
| |
| //this element is required |
| List allowablePartitionTypeIDs = new ArrayList(); |
| IConfigurationElement[] allowablePartitionTypes = |
| contentTypeElements[contentTypeIndex].getChildren(ELEM_ALLOWABLE_PARTITION_TYPES); |
| boolean anyPartitionType = false; |
| //determine anyPartitionType attribute value |
| String anyPartitionTypeValue = allowablePartitionTypes[0].getAttribute(ATTR_ANY_PARTITION_TYPE); |
| if(anyPartitionTypeValue != null) { |
| anyPartitionType = Boolean.valueOf(anyPartitionTypeValue).booleanValue(); |
| } |
| |
| //get the optional partition types |
| allowablePartitionTypes = allowablePartitionTypes[0].getChildren(ELEM_PARTITION_TYPE); |
| if(allowablePartitionTypes.length > 0) { |
| for (int partitionTypeIndex = 0; partitionTypeIndex < allowablePartitionTypes.length; ++partitionTypeIndex) { |
| String partitionTypeID = allowablePartitionTypes[partitionTypeIndex].getAttribute(ATTR_ID); |
| checkExtensionAttributeNotNull(partitionTypeID, ATTR_ID, allowablePartitionTypes[partitionTypeIndex]); |
| |
| allowablePartitionTypeIDs.add(partitionTypeID); |
| } |
| } |
| |
| //this element is optional |
| List requiredPartitionTypeIDs = new ArrayList(); |
| IConfigurationElement[] requiredPartitionTypes = |
| contentTypeElements[contentTypeIndex].getChildren(ELEM_REQUIRED_PARTITION_TYPES); |
| if(requiredPartitionTypes.length > 0) { |
| //get the required partition types |
| requiredPartitionTypes = requiredPartitionTypes[0].getChildren(ELEM_PARTITION_TYPE); |
| if(requiredPartitionTypes.length > 0) { |
| for (int partitionTypeIndex = 0; partitionTypeIndex < requiredPartitionTypes.length; ++partitionTypeIndex) { |
| String partitionTypeID = requiredPartitionTypes[partitionTypeIndex].getAttribute(ATTR_ID); |
| checkExtensionAttributeNotNull(partitionTypeID, ATTR_ID, requiredPartitionTypes[partitionTypeIndex]); |
| |
| requiredPartitionTypeIDs.add(partitionTypeID); |
| } |
| } |
| } |
| |
| //get the optional associated comment partition type ID |
| String associatedCommentPartitionTypeID = |
| contentTypeElements[contentTypeIndex].getAttribute(ATTR_ASSOCIATED_COMMENT_PARTITION_TPYPE_ID); |
| |
| //register the strategy |
| CommentingStrategy newCommentingStrategy = (CommentingStrategy)baseCommentingStrategy.clone(); |
| newCommentingStrategy.setPartitionInformation(allowablePartitionTypeIDs, anyPartitionType, |
| requiredPartitionTypeIDs, associatedCommentPartitionTypeID); |
| commentTypes.add(newCommentingStrategy); |
| } catch(CoreException e) { |
| Logger.logException(e); |
| } |
| } |
| } else { |
| Logger.log(Logger.WARNING, "The commmenting strategy element: " + element +//$NON-NLS-1$ |
| " does not contain any required " + ELEM_CONTENT_TYPE + "s"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| } |