Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'core/bundles/org.eclipse.wst.sse.ui/src/org/eclipse/wst/sse/ui/internal/comment/CommentingStrategy.java')
-rw-r--r--core/bundles/org.eclipse.wst.sse.ui/src/org/eclipse/wst/sse/ui/internal/comment/CommentingStrategy.java355
1 files changed, 355 insertions, 0 deletions
diff --git a/core/bundles/org.eclipse.wst.sse.ui/src/org/eclipse/wst/sse/ui/internal/comment/CommentingStrategy.java b/core/bundles/org.eclipse.wst.sse.ui/src/org/eclipse/wst/sse/ui/internal/comment/CommentingStrategy.java
new file mode 100644
index 0000000000..c46248c4fd
--- /dev/null
+++ b/core/bundles/org.eclipse.wst.sse.ui/src/org/eclipse/wst/sse/ui/internal/comment/CommentingStrategy.java
@@ -0,0 +1,355 @@
+/*******************************************************************************
+ * 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.Collections;
+import java.util.List;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.sse.ui.internal.Logger;
+
+/**
+ * <p>Defines a commenting strategy defined by the <code>org.eclipse.wst.sse.ui.commentinStrategy</code>
+ * extension point and tracked by the {@link CommentingStrategyRegistry}. Though it is important to
+ * note it is not a one to one relationship of {@link CommentingStrategy}s to extensions, there is actually
+ * one {@link CommentingStrategy} for each content type defined for each
+ * <code>org.eclipse.wst.sse.ui.commentinStrategy</code> extension.<p>
+ *
+ * <p>The expected use case is that a {@link CommentingStrategy} is created off the basic configuration
+ * of the extension point and then cloned for each associated content type and then each clone has
+ * its partition information set using {@link #setPartitionInformation}.
+ * Then the {@link CommentingStrategyRegistry} can be used to retrieve applicable {@link CommentingStrategy}s
+ * and apply them to documents.</p>
+ *
+ * <p>It is important to note that any instance of a {@link CommentingStrategy} is only valid for a specific
+ * content type ID but this relationship is tracked by the {@link CommentingStrategyRegistry} and not by
+ * the strategy itself. Thus any reference to the strategy being valid for specific or general partition
+ * types is implying it is already only valid for a specific content type</p>
+ */
+public abstract class CommentingStrategy {
+ /** <code>true</code> if this strategy has any required partition types, <code>false</code> otherwise */
+ private boolean fHasRequiredPartitionTypes;
+
+ /**
+ * <p>required partition type IDs that must be seen for this strategy to be valid, the number of them
+ * that must be seen for the strategy to be valid is determined by {@link #fRequireAllRequiredPartitionTypes}
+ * this requirement is waved if the optional {@link #fAssociatedCommentPartitionTypeID} is specified and
+ * present because this strategy must be valid if its {@link #fAssociatedCommentPartitionTypeID} is present</p>
+ *
+ * @see #fRequireAllRequiredPartitionTypes
+ * @see #fAssociatedCommentPartitionTypeID
+ */
+ private List fRequriedPartitionTypeIDs;
+
+ /**
+ * <p>if <code>true</code> then {@link #fAllowablePartitionTypeIDs} is ignored because
+ * this strategy is valid for any partition type, if <code>false</code> then this
+ * strategy is only valid for those partition types listed in {@link #fAllowablePartitionTypeIDs}</p>
+ *
+ * @see #fAllowablePartitionTypeIDs
+ */
+ private boolean fAllPartitionTypesAllowable;
+
+ /**
+ * <p>the partition types that this strategy is valid for, it is also automatically valid for
+ * any partition types listed in {@link #fRequriedPartitionTypeIDs} and the optionally
+ * specified {@link #fAssociatedCommentPartitionTypeID}</p>
+ *
+ * @see #fAllPartitionTypesAllowable
+ * @see #fRequriedPartitionTypeIDs
+ * @see #fAssociatedCommentPartitionTypeID
+ */
+ private List fAllowablePartitionTypeIDs;
+
+ /**
+ * an optional associated comment partition type ID, if this partition type is seen then the
+ * the {@link #fRequriedPartitionTypeIDs} requirement is waved as to weather this strategy is
+ * valid or not
+ *
+ * @see #fRequriedPartitionTypeIDs
+ */
+ private String fAssociatedCommentPartitionTypeID;
+
+ /**
+ * <p>Default constructor, the specific initialization is done by
+ * {@link #setPartitionInformation}</p>
+ */
+ public CommentingStrategy() {
+ this.fAssociatedCommentPartitionTypeID = null;
+ this.fRequriedPartitionTypeIDs = Collections.EMPTY_LIST;
+ this.fAllowablePartitionTypeIDs = Collections.EMPTY_LIST;
+ this.fHasRequiredPartitionTypes = false;
+ this.fAllPartitionTypesAllowable = false;
+ }
+
+ /**
+ * <p>Used to set up the partition information for this strategy</p>
+ * <p>This information is used to determine if a strategy is valid for a set of
+ * {@link ITypedRegion}s.</p>
+ *
+ * @param allowablePartitionTypeIDs the partition types this strategy is valid for, the strategy will also
+ * be considered valid for any of the required partition types
+ * @param allPartitionTypesAllowable if <code>true</code> then this strategy is valid for any partition types
+ * thus ignoring the <code>allowablePartitionTypeIDs</code>, <code>false</code> otherwise
+ * @param requiredPartitionTypeIDs partition type IDs that must be seen for this strategy to be valid, there
+ * are exceptions to this rule, see {@link #isApplicableFor(ITypedRegion[])}
+ * @param requireAllRequiredPartitionTypes <code>true</code> if all of the <code>requiredPartitionTypeIDs must
+ * be seen for this strategy to be valid, <code>false</code> otherwise, there are exceptions to these rules, see
+ * {@link #isApplicableFor(ITypedRegion[])}
+ * @param associatedCommentPartitionTypeID optional comment partition type associated with this strategy,
+ * maybe <code>null</code>
+ *
+ * @see #isApplicableFor(ITypedRegion[])
+ */
+ protected final void setPartitionInformation(List allowablePartitionTypeIDs, boolean allPartitionTypesAllowable,
+ List requiredPartitionTypeIDs, String associatedCommentPartitionTypeID) {
+
+ this.fAllPartitionTypesAllowable = allPartitionTypesAllowable;
+
+ this.fRequriedPartitionTypeIDs = requiredPartitionTypeIDs;
+ if(this.fRequriedPartitionTypeIDs == null) {
+ this.fRequriedPartitionTypeIDs = Collections.EMPTY_LIST;
+ }
+
+ this.fHasRequiredPartitionTypes = this.fRequriedPartitionTypeIDs.size() != 0;
+
+ this.fAllowablePartitionTypeIDs = allowablePartitionTypeIDs;
+ if(this.fAllowablePartitionTypeIDs == null) {
+ this.fAllowablePartitionTypeIDs = Collections.EMPTY_LIST;
+ }
+
+ this.fAssociatedCommentPartitionTypeID = associatedCommentPartitionTypeID;
+ }
+
+ /**
+ * <p>Applies this strategy to the given model starting at the given offset for the given length</p>
+ *
+ * @param document {@link IStructuredDocument} to apply this strategy too
+ * @param offset the offset to start this comment at
+ * @param length the length of the region to apply this comment too
+ *
+ * @throws BadLocationException it is not the fault of the strategy if callers passes a bad
+ * <code>offset</code> and/or <code>length</code> for the given <code>model</code>
+ */
+ public abstract void apply(IStructuredDocument document, int offset, int length) throws BadLocationException;
+
+ /**
+ * <p>Remove any comments associated with this strategy from the given model for the given offset to
+ * the given length. Weather a comment surrounding the given range should be removed can also be
+ * specified</p>
+ *
+ * @param document {@link IStructuredDocument} to remove comments associated with this strategy from
+ * @param offset the location to start removing comments associated with this strategy from
+ * @param length the length of the region to remove associated comments from
+ * @param removeEnclosing weather a comment should be removed if it incloses the region specified
+ * by the given <code>offset</code> and <code>length</code>
+ *
+ * @throws BadLocationException it is not the fault of the strategy if callers passes a bad
+ * <code>offset</code> and/or <code>length</code> for the given <code>model</code>
+ */
+ public abstract void remove(IStructuredDocument document, int offset, int length, boolean removeEnclosing) throws BadLocationException;
+
+ /**
+ * <p>Determines if the given region is a comment region commented by this strategy.</p>
+ *
+ * @param document {@link IStructuredDocument} containing the given <code>region</code>
+ * @param region determine if this region is a comment region commented by this strategy
+ * @return <code>true</code> if the given <code>region</code> has already been
+ * commented by this strategy, <code>false</code> otherwise
+ *
+ * @throws BadLocationException it is not the fault of the strategy if callers passes a bad
+ * <code>offset</code> and/or <code>length</code> for the given <code>model</code>
+ */
+ public abstract boolean alreadyCommenting(IStructuredDocument document, IRegion region) throws BadLocationException;
+
+ /**
+ * <p>Implementers should return a copy of themselves</p>
+ * <p>Allows the {@link CommentingStrategyRegistry} to create a {@link CommentingStrategy} for
+ * each of its associated content types.</p>
+ *
+ * @return implementers should return an object of type {@link CommentingStrategy}
+ *
+ * @see java.lang.Object#clone()
+ */
+ public abstract Object clone();
+
+ /**
+ * <p>Determines if this strategy is applicable for the given regions for either commenting or un-commenting.
+ * For this strategy to be applicable the given regions must contain at least one or all of the
+ * {@link #fRequriedPartitionTypeIDs} (depending on the value of {@link #fRequireAllRequiredPartitionTypes})
+ * or contain at least one region of type {@link #fAssociatedCommentPartitionTypeID}. Also if the value of
+ * {@link #fAllPartitionTypesAllowable} is <code>false</code> the given regions must all be of type
+ * {@link #fAllowablePartitionTypeIDs} and/or {@link #fRequriedPartitionTypeIDs} and/or
+ * {@link #fAssociatedCommentPartitionTypeID} otherwise the regions can be of any type except for they still
+ * must beet the required partition type requirements</p>
+ *
+ * @param regions determine if this strategy is applicable for these regions
+ * @return <code>true</code> if this strategy is applicable for the given <code>regions</code>
+ * <code>false</code> otherwise.
+ */
+ public final boolean isApplicableFor(ITypedRegion[] regions) {
+ List partitionTypeIDs = getPartitionTypeIDs(regions);
+
+ boolean foundAssociatedCommentPartitions = false;
+ if(this.fAssociatedCommentPartitionTypeID != null) {
+ foundAssociatedCommentPartitions = partitionTypeIDs.contains(this.fAssociatedCommentPartitionTypeID);
+ if(foundAssociatedCommentPartitions) {
+ //remove all instances of the comment partition type
+ boolean removed;
+ do {
+ removed = partitionTypeIDs.remove(this.fAssociatedCommentPartitionTypeID);
+ } while(removed);
+ }
+ }
+
+ //determine if required partitions requirements are met
+ boolean requiredPartitionsRequirementsMet = !this.fHasRequiredPartitionTypes ||
+ partitionTypeIDs.removeAll(this.fRequriedPartitionTypeIDs);
+
+ //determine if allowable partitions requirements are met
+ boolean allowablePartitionsRequirementsMet = false;
+ if(this.fAllPartitionTypesAllowable) {
+ allowablePartitionsRequirementsMet = true;
+ } else {
+ partitionTypeIDs.removeAll(this.fAllowablePartitionTypeIDs);
+
+ //at this point all required partitions and allowable partitions have been removed
+ allowablePartitionsRequirementsMet = partitionTypeIDs.size() == 0;
+ }
+
+ return (requiredPartitionsRequirementsMet || foundAssociatedCommentPartitions) && allowablePartitionsRequirementsMet;
+ }
+
+ /**
+ * <p>Convenience method to take a list of regions and create one encompassing region to pass to
+ * {@link #alreadyCommenting(IDocument, IRegion)}</p>
+ *
+ * @param document {@link IDocument} that contains the given <code>regions</code>
+ * @param regions {@link IRegion}s to combine into one region and pass onto
+ * {@link #alreadyCommenting(IDocument, IRegion)}
+ *
+ * @return the result of a call to {@link #alreadyCommenting(IDocument, IRegion)} combining
+ * all of the given <code>regions</code> into one region
+ *
+ * @throws BadLocationException it is not the fault of the strategy if callers passes a bad
+ * <code>offset</code> and/or <code>length</code> for the given <code>model</code>
+ */
+ public final boolean alreadyCommenting(IStructuredDocument document, IRegion[] regions) throws BadLocationException {
+ boolean alreadyCommenting = false;
+ if(regions != null && regions.length > 0) {
+ //create one region spanning all the given regions
+ int offset = regions[0].getOffset();
+ int length = regions[regions.length-1].getOffset() +
+ regions[regions.length-1].getLength() - offset;
+
+ IRegion region = new Region(offset, length);
+ alreadyCommenting = this.alreadyCommenting(document, region);
+ }
+
+ return alreadyCommenting;
+ }
+ /**
+ * <p>Given a list of {@link ITypedRegion}s returns the sub set of that list that
+ * are of the comment region type associated with this strategy</p>
+ *
+ * @param typedRegions {@link ITypedRegion}s to filter down to only the comment regions
+ * associated with this strategy
+ *
+ * @return {@link List} of {@link ITypedRegion}s from the given <code>typedRegions</code>
+ * that are of the comment partition type associated with this strategy
+ */
+ protected List getAssociatedCommentedRegions(ITypedRegion[] typedRegions) {
+ List commentedRegions = new ArrayList();
+
+ for(int i = 0; i < typedRegions.length; ++i) {
+ if(typedRegions[i].getType().equals(this.fAssociatedCommentPartitionTypeID)) {
+ commentedRegions.add(typedRegions[i]);
+ }
+ }
+
+ return commentedRegions;
+ }
+
+ /**
+ * <p>Given a list of {@link ITypedRegion}s returns a list of the partition
+ * type IDs taken from the given regions.</p>
+ *
+ * @param regions {@link ITypedRegion}s to get the partition type IDs from
+ * @return {@link List} of the partition type IDs taken from the given <code>regions</code>
+ */
+ private static List getPartitionTypeIDs(ITypedRegion[] regions) {
+ List partitionTypes = new ArrayList(regions.length);
+ for(int i = 0; i < regions.length; ++i) {
+ partitionTypes.add(regions[i].getType());
+ }
+
+ return partitionTypes;
+ }
+
+ /**
+ * <p>This method modifies the given document to remove the given comment
+ * prefix at the given comment prefix offset and the given comment
+ * suffix at the given comment suffix offset. In the case of removing
+ * a line comment that does not have a suffix, pass <code>null</code>
+ * for the comment suffix and it and its associated offset will
+ * be ignored.</p>
+ *
+ * <p><b>NOTE:</b> it is a good idea if a model is at hand when calling this to
+ * warn the model of an impending update</p>
+ *
+ * @param document the document to remove the comment from
+ * @param commentPrefixOffset the offset of the comment prefix
+ * @param commentSuffixOffset the offset of the comment suffix
+ * (ignored if <code>commentSuffix</code> is <code>null</code>)
+ * @param commentPrefix the prefix of the comment to remove from its associated given offset
+ * @param commentSuffix the suffix of the comment to remove from its associated given offset,
+ * or null if there is not suffix to remove for this comment
+ */
+ protected static void uncomment(IDocument document, int commentPrefixOffset, String commentPrefix,
+ int commentSuffixOffset, String commentSuffix) {
+
+ try {
+ //determine if there is a space after the comment prefix that should also be removed
+ int commentPrefixLength = commentPrefix.length();
+ String postCommentPrefixChar = document.get(commentPrefixOffset + commentPrefix.length(), 1);
+ if(postCommentPrefixChar.equals(" ")) {
+ commentPrefixLength++;
+ }
+
+ //remove the comment prefix
+ document.replace(commentPrefixOffset, commentPrefixLength, ""); //$NON-NLS-1$
+
+ if(commentSuffix != null) {
+ commentSuffixOffset -= commentPrefixLength;
+
+ //determine if there is a space before the comment suffix that should also be removed
+ int commentSuffixLength = commentSuffix.length();
+ String preCommentSuffixChar = document.get(commentSuffixOffset-1, 1);
+ if(preCommentSuffixChar.equals(" ")) {
+ commentSuffixLength++;
+ commentSuffixOffset--;
+ }
+
+ //remove the comment suffix
+ document.replace(commentSuffixOffset, commentSuffixLength, ""); //$NON-NLS-1$
+ }
+ }
+ catch (BadLocationException e) {
+ Logger.log(Logger.WARNING_DEBUG, e.getMessage(), e);
+ }
+ }
+}

Back to the top