View | Details | Raw Unified | Return to bug 63808 | Differences between
and this patch

Collapse All | Expand All

(-)ui/org/eclipse/jdt/internal/ui/text/JavaCommentScanner.java (-3 / +60 lines)
Lines 12-17 Link Here
12
package org.eclipse.jdt.internal.ui.text;
12
package org.eclipse.jdt.internal.ui.text;
13
13
14
import java.util.ArrayList;
14
import java.util.ArrayList;
15
import java.util.Arrays;
15
import java.util.HashMap;
16
import java.util.HashMap;
16
import java.util.List;
17
import java.util.List;
17
import java.util.Map;
18
import java.util.Map;
Lines 40-45 Link Here
40
 * Java comment scanner.
41
 * Java comment scanner.
41
 */
42
 */
42
public class JavaCommentScanner extends AbstractJavaScanner{
43
public class JavaCommentScanner extends AbstractJavaScanner{
44
	
45
	final String REGION_TAG_OPEN = "region"; //$NON-NLS-1$
46
	final String REGION_TAG_CLOSE = "/region"; //$NON-NLS-1$
47
	final String REGION_TAG_TITLE = "title"; //$NON-NLS-1$
43
48
44
	private static class AtJavaIdentifierDetector implements IWordDetector {
49
	private static class AtJavaIdentifierDetector implements IWordDetector {
45
50
Lines 51-56 Link Here
51
			return c == '.' || Character.isJavaIdentifierPart(c);
56
			return c == '.' || Character.isJavaIdentifierPart(c);
52
		}
57
		}
53
	}
58
	}
59
	
60
	private static class AtTagIdentifierDetector implements IWordDetector {
61
		
62
		public boolean isWordStart(char c) {
63
			return c == '<';
64
		}
65
		
66
		public boolean isWordPart(char c) {
67
			return c == '/' || Character.isJavaIdentifierPart(c) 
68
					&& !(c == '>');
69
		}
70
	}
54
71
55
	private class TaskTagMatcher extends CombinedWordRule.WordMatcher {
72
	private class TaskTagMatcher extends CombinedWordRule.WordMatcher {
56
73
Lines 153-161 Link Here
153
			fCaseSensitive= caseSensitive;
170
			fCaseSensitive= caseSensitive;
154
		}
171
		}
155
	}
172
	}
156
173
	
157
	private static final String COMPILER_TASK_TAGS= JavaCore.COMPILER_TASK_TAGS;
174
	private static final String COMPILER_TASK_TAGS= JavaCore.COMPILER_TASK_TAGS;
158
	protected static final String TASK_TAG= IJavaColorConstants.TASK_TAG;
175
	protected static final String TASK_TAG= IJavaColorConstants.TASK_TAG;
176
	protected static final String REGION_TAG= IJavaColorConstants.REGION_TAG;
159
	/**
177
	/**
160
	 * Preference key of a string preference, specifying if task tag detection is case-sensitive.
178
	 * Preference key of a string preference, specifying if task tag detection is case-sensitive.
161
	 * @since 3.0
179
	 * @since 3.0
Lines 167-173 Link Here
167
	 */
185
	 */
168
	private static final String ENABLED= JavaCore.ENABLED;
186
	private static final String ENABLED= JavaCore.ENABLED;
169
187
170
	private TaskTagMatcher fTaskTagMatcher;
188
	private TaskTagMatcher fTaskTagMatcher;	
171
	private Preferences fCorePreferenceStore;
189
	private Preferences fCorePreferenceStore;
172
	private String fDefaultTokenProperty;
190
	private String fDefaultTokenProperty;
173
	private String[] fTokenProperties;
191
	private String[] fTokenProperties;
Lines 182-187 Link Here
182
		fCorePreferenceStore= coreStore;
200
		fCorePreferenceStore= coreStore;
183
		fDefaultTokenProperty= defaultTokenProperty;
201
		fDefaultTokenProperty= defaultTokenProperty;
184
		fTokenProperties= tokenProperties;
202
		fTokenProperties= tokenProperties;
203
		
204
		// FIXME: Dirty Hack! Expensive.
205
		// Add always REGION_TAG to list.
206
		List list = Arrays.asList(fTokenProperties);
207
		if (list.contains(IJavaColorConstants.JAVA_SINGLE_LINE_COMMENT)
208
				&& !list.contains(REGION_TAG)) {
209
			list = new ArrayList(list);
210
			list.add(REGION_TAG);
211
			fTokenProperties = (String[])list.toArray(new String[list.size()]);
212
		}
185
213
186
		initialize();
214
		initialize();
187
	}
215
	}
Lines 220-232 Link Here
220
		List list= new ArrayList();
248
		List list= new ArrayList();
221
		Token defaultToken= getToken(fDefaultTokenProperty);
249
		Token defaultToken= getToken(fDefaultTokenProperty);
222
250
223
		List matchers= createMatchers();
251
		List matchers= createMatchers();		
224
		if (matchers.size() > 0) {
252
		if (matchers.size() > 0) {
225
			CombinedWordRule combinedWordRule= new CombinedWordRule(new AtJavaIdentifierDetector(), defaultToken);
253
			CombinedWordRule combinedWordRule= new CombinedWordRule(new AtJavaIdentifierDetector(), defaultToken);
226
			for (int i= 0, n= matchers.size(); i < n; i++)
254
			for (int i= 0, n= matchers.size(); i < n; i++)
227
				combinedWordRule.addWordMatcher((WordMatcher) matchers.get(i));
255
				combinedWordRule.addWordMatcher((WordMatcher) matchers.get(i));
228
			list.add(combinedWordRule);
256
			list.add(combinedWordRule);
229
		}
257
		}
258
		matchers = createRegionMatchers();
259
		if (matchers.size() > 0) {
260
			CombinedWordRuleXMLTag combinedWordRule= new CombinedWordRuleXMLTag(
261
					new AtTagIdentifierDetector(), defaultToken);
262
			for (int i= 0, n= matchers.size(); i < n; i++)
263
				combinedWordRule.addWordMatcher((WordMatcher) matchers.get(i));
264
			list.add(combinedWordRule);
265
		}
230
266
231
		setDefaultReturnToken(defaultToken);
267
		setDefaultReturnToken(defaultToken);
232
268
Lines 257-262 Link Here
257
			fTaskTagMatcher.setCaseSensitive(isCaseSensitive);
293
			fTaskTagMatcher.setCaseSensitive(isCaseSensitive);
258
			list.add(fTaskTagMatcher);
294
			list.add(fTaskTagMatcher);
259
		}
295
		}
296
		
297
		return list;
298
	}
299
	
300
	/**
301
	 * Creates a list of titled region word matchers.
302
	 * 
303
	 * @return a list of titled region word matchers.
304
	 */
305
	protected List createRegionMatchers() {
306
		List list = new ArrayList();
307
		
308
		IToken token = getToken(REGION_TAG);
309
		if (token != null) {
310
			CombinedWordRuleXMLTag.TagMatcher regionMatcher= new CombinedWordRuleXMLTag.TagMatcher();
311
			regionMatcher.addWord(REGION_TAG_OPEN, new String[]{REGION_TAG_TITLE}, token);			
312
			list.add(regionMatcher);
313
			regionMatcher= new CombinedWordRuleXMLTag.TagMatcher();
314
			regionMatcher.addWord(REGION_TAG_CLOSE, token);
315
			list.add(regionMatcher);
316
		}
260
317
261
		return list;
318
		return list;
262
	}
319
	}
(-)ui/org/eclipse/jdt/internal/ui/text/CombinedWordRule.java (-11 / +29 lines)
Lines 49-55 Link Here
49
	public static class WordMatcher {
49
	public static class WordMatcher {
50
50
51
		/** The table of predefined words and token for this matcher */
51
		/** The table of predefined words and token for this matcher */
52
		private Map fWords= new HashMap();
52
		protected Map fWords= new HashMap();
53
53
54
		/**
54
		/**
55
		 * Adds a word and the token to be returned if it is detected.
55
		 * Adds a word and the token to be returned if it is detected.
Lines 92-100 Link Here
92
	public static class CharacterBuffer {
92
	public static class CharacterBuffer {
93
93
94
		/** Buffer content */
94
		/** Buffer content */
95
		private char[] fContent;
95
		protected char[] fContent;
96
		/** Buffer content size */
96
		/** Buffer content size */
97
		private int fLength= 0;
97
		protected int fLength= 0;
98
98
99
		/** Is hash code cached? */
99
		/** Is hash code cached? */
100
		private boolean fIsHashCached= false;
100
		private boolean fIsHashCached= false;
Lines 223-241 Link Here
223
	}
223
	}
224
224
225
	/** Internal setting for the uninitialized column constraint */
225
	/** Internal setting for the uninitialized column constraint */
226
	private static final int UNDEFINED= -1;
226
	protected static final int UNDEFINED= -1;
227
227
228
	/** The word detector used by this rule */
228
	/** The word detector used by this rule */
229
	private IWordDetector fDetector;
229
	protected IWordDetector fDetector;
230
	/** The default token to be returned on success and if nothing else has been specified. */
230
	/** The default token to be returned on success and if nothing else has been specified. */
231
	private IToken fDefaultToken;
231
	protected IToken fDefaultToken;
232
	/** The column constraint */
232
	/** The column constraint */
233
	private int fColumn= UNDEFINED;
233
	protected int fColumn= UNDEFINED;
234
	/** Buffer used for pattern detection */
234
	/** Buffer used for pattern detection */
235
	private CharacterBuffer fBuffer= new CharacterBuffer(16);
235
	protected CharacterBuffer fBuffer;
236
236
237
	/** List of word matchers */
237
	/** List of word matchers */
238
	private List fMatchers= new ArrayList();
238
	protected List fMatchers= new ArrayList();
239
239
240
	/**
240
	/**
241
	 * Creates a rule which, with the help of an word detector, will return the token
241
	 * Creates a rule which, with the help of an word detector, will return the token
Lines 294-303 Link Here
294
	 * @see WordMatcher#addWord(String, IToken)
294
	 * @see WordMatcher#addWord(String, IToken)
295
	 */
295
	 */
296
	public CombinedWordRule(IWordDetector detector, WordMatcher matcher, IToken defaultToken) {
296
	public CombinedWordRule(IWordDetector detector, WordMatcher matcher, IToken defaultToken) {
297
297
		this(detector, matcher, defaultToken, new CharacterBuffer(16));
298
	}
299
	
300
	/**
301
	 * Creates a rule which, with the help of an word detector, will return the token
302
	 * associated with the detected word. If no token has been associated, the
303
	 * specified default token will be returned.
304
	 *
305
	 * @param detector the word detector to be used by this rule, may not be <code>null</code>
306
	 * @param matcher the initial word matcher
307
	 * @param defaultToken the default token to be returned on success
308
	 *		if nothing else is specified, may not be <code>null</code>
309
	 * @param buffer the buffer CharacterBuffer to use 
310
	 *
311
	 * @see WordMatcher#addWord(String, IToken)	 
312
	 */
313
	public CombinedWordRule(IWordDetector detector, WordMatcher matcher, IToken defaultToken,
314
	                        CharacterBuffer buffer) {
298
		Assert.isNotNull(detector);
315
		Assert.isNotNull(detector);
299
		Assert.isNotNull(defaultToken);
316
		Assert.isNotNull(defaultToken);
300
317
318
		fBuffer= buffer;
301
		fDetector= detector;
319
		fDetector= detector;
302
		fDefaultToken= defaultToken;
320
		fDefaultToken= defaultToken;
303
		if (matcher != null)
321
		if (matcher != null)
Lines 365-371 Link Here
365
	 *
383
	 *
366
	 * @param scanner the scanner to be used
384
	 * @param scanner the scanner to be used
367
	 */
385
	 */
368
	private void unreadBuffer(ICharacterScanner scanner) {
386
	protected void unreadBuffer(ICharacterScanner scanner) {
369
		for (int i= fBuffer.length() - 1; i >= 0; i--)
387
		for (int i= fBuffer.length() - 1; i >= 0; i--)
370
			scanner.unread();
388
			scanner.unread();
371
	}
389
	}
(-)ui/org/eclipse/jdt/ui/text/IJavaColorConstants.java (+5 lines)
Lines 113-118 Link Here
113
	String TASK_TAG= "java_comment_task_tag"; //$NON-NLS-1$
113
	String TASK_TAG= "java_comment_task_tag"; //$NON-NLS-1$
114
114
115
	/**
115
	/**
116
	 * Tag for a region start and ending.
117
	 */
118
	String REGION_TAG = "java_region_tag"; //$NON-NLS-1$
119
120
	/**
116
	 * The color key for JavaDoc keywords (<code>@foo</code>) in JavaDoc comments
121
	 * The color key for JavaDoc keywords (<code>@foo</code>) in JavaDoc comments
117
	 * (value <code>"java_doc_keyword"</code>).
122
	 * (value <code>"java_doc_keyword"</code>).
118
	 */
123
	 */
(-)ui/org/eclipse/jdt/ui/text/JavaTextTools.java (-1 / +2 lines)
Lines 163-169 Link Here
163
		fColorManager= new JavaColorManager(autoDisposeOnDisplayDispose);
163
		fColorManager= new JavaColorManager(autoDisposeOnDisplayDispose);
164
		fCodeScanner= new JavaCodeScanner(fColorManager, store);
164
		fCodeScanner= new JavaCodeScanner(fColorManager, store);
165
		fMultilineCommentScanner= new JavaCommentScanner(fColorManager, store, coreStore, IJavaColorConstants.JAVA_MULTI_LINE_COMMENT);
165
		fMultilineCommentScanner= new JavaCommentScanner(fColorManager, store, coreStore, IJavaColorConstants.JAVA_MULTI_LINE_COMMENT);
166
		fSinglelineCommentScanner= new JavaCommentScanner(fColorManager, store, coreStore, IJavaColorConstants.JAVA_SINGLE_LINE_COMMENT);
166
		fSinglelineCommentScanner= new JavaCommentScanner(fColorManager, store, coreStore, IJavaColorConstants.JAVA_SINGLE_LINE_COMMENT,  
167
				new String[] {IJavaColorConstants.JAVA_SINGLE_LINE_COMMENT, IJavaColorConstants.TASK_TAG, IJavaColorConstants.REGION_TAG} );
167
		fStringScanner= new SingleTokenJavaScanner(fColorManager, store, IJavaColorConstants.JAVA_STRING);
168
		fStringScanner= new SingleTokenJavaScanner(fColorManager, store, IJavaColorConstants.JAVA_STRING);
168
		fJavaDocScanner= new JavaDocScanner(fColorManager, store, coreStore);
169
		fJavaDocScanner= new JavaDocScanner(fColorManager, store, coreStore);
169
	}
170
	}
(-)ui/org/eclipse/jdt/ui/text/folding/DefaultJavaFoldingStructureProvider.java (-14 / +257 lines)
Lines 34-39 Link Here
34
import org.eclipse.jface.text.Region;
34
import org.eclipse.jface.text.Region;
35
import org.eclipse.jface.text.TextSelection;
35
import org.eclipse.jface.text.TextSelection;
36
import org.eclipse.jface.text.source.Annotation;
36
import org.eclipse.jface.text.source.Annotation;
37
import org.eclipse.jface.text.source.projection.IProjectionAnnotationTitled;
37
import org.eclipse.jface.text.source.projection.IProjectionListener;
38
import org.eclipse.jface.text.source.projection.IProjectionListener;
38
import org.eclipse.jface.text.source.projection.IProjectionPosition;
39
import org.eclipse.jface.text.source.projection.IProjectionPosition;
39
import org.eclipse.jface.text.source.projection.ProjectionAnnotation;
40
import org.eclipse.jface.text.source.projection.ProjectionAnnotation;
Lines 65-70 Link Here
65
import org.eclipse.jdt.core.compiler.InvalidInputException;
66
import org.eclipse.jdt.core.compiler.InvalidInputException;
66
import org.eclipse.jdt.core.dom.CompilationUnit;
67
import org.eclipse.jdt.core.dom.CompilationUnit;
67
68
69
import org.eclipse.jdt.internal.core.util.PublicScanner.TitledRegionStruct;
70
68
import org.eclipse.jdt.internal.corext.SourceRange;
71
import org.eclipse.jdt.internal.corext.SourceRange;
69
72
70
import org.eclipse.jdt.ui.PreferenceConstants;
73
import org.eclipse.jdt.ui.PreferenceConstants;
Lines 177-182 Link Here
177
		public void addProjectionRange(JavaProjectionAnnotation annotation, Position position) {
180
		public void addProjectionRange(JavaProjectionAnnotation annotation, Position position) {
178
			fMap.put(annotation, position);
181
			fMap.put(annotation, position);
179
		}
182
		}
183
		
184
		/**
185
		 * Returns the saved positions.
186
		 * 
187
		 * @return the saved positions.
188
		 */
189
		public Collection getPositions() {
190
			return fMap.values();
191
		}
180
192
181
		/**
193
		/**
182
		 * Returns <code>true</code> if header comments should be collapsed.
194
		 * Returns <code>true</code> if header comments should be collapsed.
Lines 222-236 Link Here
222
		public boolean collapseMembers() {
234
		public boolean collapseMembers() {
223
			return fAllowCollapsing && fCollapseMembers;
235
			return fAllowCollapsing && fCollapseMembers;
224
		}
236
		}
237
		
238
		/**
239
		 * Returns <code>true</code> if regions should be collapsed.
240
		 * 
241
		 * @return <code>true</code> if regions should be collapsed
242
		 */
243
		public boolean collapseRegions() {
244
			return fAllowCollapsing && fCollapseRegions;
245
		}
225
	}
246
	}
226
	
247
	
227
	/**
248
	/**
228
	 * A {@link ProjectionAnnotation} for java code.
249
	 * A {@link ProjectionAnnotation} for java code.
229
	 */
250
	 */
230
	protected static final class JavaProjectionAnnotation extends ProjectionAnnotation {
251
	protected static final class JavaProjectionAnnotation extends ProjectionAnnotation 
252
			implements IProjectionAnnotationTitled {
231
253
232
		private IJavaElement fJavaElement;
254
		private IJavaElement fJavaElement;
233
		private boolean fIsComment;
255
		private boolean fIsComment;
256
		private boolean fIsRegion;
257
		private String fTitle;
234
258
235
		/**
259
		/**
236
		 * Creates a new projection annotation.
260
		 * Creates a new projection annotation.
Lines 239-250 Link Here
239
		 *        <code>false</code> to set it to expanded
263
		 *        <code>false</code> to set it to expanded
240
		 * @param element the java element this annotation refers to
264
		 * @param element the java element this annotation refers to
241
		 * @param isComment <code>true</code> for a foldable comment, <code>false</code> for a
265
		 * @param isComment <code>true</code> for a foldable comment, <code>false</code> for a
242
		 *        foldable code element
266
		 *        foldable code element. Must be <code>false</code> for a region.
267
		 * @param title title <code>String</code> for a titled foldable region,
268
		 * 			<code>null</code> for a non-titled region.
243
		 */
269
		 */
244
		public JavaProjectionAnnotation(boolean isCollapsed, IJavaElement element, boolean isComment) {
270
		public JavaProjectionAnnotation(boolean isCollapsed, IJavaElement element,
271
		                                boolean isComment, String title) {
245
			super(isCollapsed);
272
			super(isCollapsed);
246
			fJavaElement= element;
273
			fJavaElement= element;
247
			fIsComment= isComment;
274
			fIsComment= isComment;
275
			fTitle = title; 
276
			fIsRegion= (fTitle != null);
277
		}
278
		
279
		/**
280
		 * Creates a new not titled projection annotation.
281
		 * 
282
		 * @param isCollapsed <code>true</code> to set the initial state to collapsed,
283
		 *        <code>false</code> to set it to expanded
284
		 * @param element the java element this annotation refers to
285
		 * @param isComment <code>true</code> for a foldable comment, <code>false</code> for a
286
		 *        foldable code element
287
		 * @see #JavaProjectionAnnotation(boolean, IJavaElement, boolean, String)
288
		 */
289
		public JavaProjectionAnnotation(boolean isCollapsed, IJavaElement element, boolean isComment) {
290
			this(isCollapsed, element, isComment, null);
248
		}
291
		}
249
292
250
		IJavaElement getElement() {
293
		IJavaElement getElement() {
Lines 263-268 Link Here
263
			fIsComment= isComment;
306
			fIsComment= isComment;
264
		}
307
		}
265
308
309
		public boolean isRegion() {
310
			return fIsRegion;
311
		}
312
313
		public void setIsRegion(boolean isRegion) {
314
			fIsRegion= isRegion;
315
		}
316
		
317
		/**
318
		 * Check if the other <code>JavaProjectionAnnotation</code>
319
		 * has the same title. 
320
		 * 
321
		 * @param annotation the other JavaProjectionAnnotation
322
		 * @return if the titels of both objects are equal
323
		 */
324
		public boolean equalsTitle(JavaProjectionAnnotation annotation) {
325
			if (this.fTitle == null) {
326
				return annotation.fTitle == null;
327
			}
328
			return this.fTitle.equals(annotation.fTitle);
329
		}
330
		
266
		/*
331
		/*
267
		 * @see java.lang.Object#toString()
332
		 * @see java.lang.Object#toString()
268
		 */
333
		 */
Lines 270-276 Link Here
270
			return "JavaProjectionAnnotation:\n" + //$NON-NLS-1$
335
			return "JavaProjectionAnnotation:\n" + //$NON-NLS-1$
271
					"\telement: \t"+ fJavaElement.toString() + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
336
					"\telement: \t"+ fJavaElement.toString() + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
272
					"\tcollapsed: \t" + isCollapsed() + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
337
					"\tcollapsed: \t" + isCollapsed() + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
273
					"\tcomment: \t" + isComment() + "\n"; //$NON-NLS-1$ //$NON-NLS-2$
338
					"\tcomment: \t" + isComment() + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
339
					"\tregion: \t" + isRegion() + "\n"; //$NON-NLS-1$ //$NON-NLS-2$
340
		}	
341
		
342
		/*
343
		 * (non-Javadoc)
344
		 * @see java.lang.Object#hashCode()
345
		 */
346
		public boolean equals(Object obj) {
347
			if (!(obj instanceof JavaProjectionAnnotation)) {
348
				return false;
349
			}
350
			JavaProjectionAnnotation annot = (JavaProjectionAnnotation)obj;
351
			return fJavaElement == annot.fJavaElement
352
					&& isCollapsed() == annot.isCollapsed()
353
					&& isComment() == annot.isComment()
354
					&& isRegion() == annot.isRegion()
355
					&& equalsTitle(annot);
356
		}
357
358
		public String getTitle() {
359
			return fTitle;
360
		}
361
		
362
		public void setTitle(String title) {
363
			fTitle = title;
274
		}
364
		}
275
	}
365
	}
276
	
366
	
Lines 308-314 Link Here
308
	 */
398
	 */
309
	private static final class MemberFilter implements Filter {
399
	private static final class MemberFilter implements Filter {
310
		public boolean match(JavaProjectionAnnotation annotation) {
400
		public boolean match(JavaProjectionAnnotation annotation) {
311
			if (!annotation.isComment() && !annotation.isMarkedDeleted()) {
401
			if (!annotation.isComment() && !annotation.isRegion() && !annotation.isMarkedDeleted()) {
312
				IJavaElement element= annotation.getElement();
402
				IJavaElement element= annotation.getElement();
313
				if (element instanceof IMember) {
403
				if (element instanceof IMember) {
314
					if (element.getElementType() != IJavaElement.TYPE || ((IMember) element).getDeclaringType() != null) {
404
					if (element.getElementType() != IJavaElement.TYPE || ((IMember) element).getDeclaringType() != null) {
Lines 321-326 Link Here
321
	}
411
	}
322
	
412
	
323
	/**
413
	/**
414
	 * Matches regions
415
	 * 
416
	 * @author jand	
417
	 */
418
	private static final class RegionFilter implements Filter {
419
		public boolean match(JavaProjectionAnnotation annotation) {
420
			if (annotation.isRegion() && !annotation.isMarkedDeleted()) {
421
				return true;
422
			}
423
			return false;
424
		}
425
	}
426
	
427
	/**
324
	 * Matches java elements contained in a certain set.
428
	 * Matches java elements contained in a certain set.
325
	 */
429
	 */
326
	private static final class JavaElementSetFilter implements Filter {
430
	private static final class JavaElementSetFilter implements Filter {
Lines 334-340 Link Here
334
438
335
		public boolean match(JavaProjectionAnnotation annotation) {
439
		public boolean match(JavaProjectionAnnotation annotation) {
336
			boolean stateMatch= fMatchCollapsed == annotation.isCollapsed();
440
			boolean stateMatch= fMatchCollapsed == annotation.isCollapsed();
337
			if (stateMatch && !annotation.isComment() && !annotation.isMarkedDeleted()) {
441
			if (stateMatch && !annotation.isComment() && !annotation.isMarkedDeleted()
442
					&& !annotation.isRegion()) {
338
				IJavaElement element= annotation.getElement();
443
				IJavaElement element= annotation.getElement();
339
				if (fSet.contains(element)) {
444
				if (fSet.contains(element)) {
340
					return true;
445
					return true;
Lines 551-556 Link Here
551
	}
656
	}
552
657
553
	/**
658
	/**
659
	 * Projection position that will return one foldable region including the
660
	 * titled region.
661
	 */
662
	private static final class TitledRegionPosition extends Position implements IProjectionPosition {
663
		private String fTitle;
664
		
665
		TitledRegionPosition(String title, int offset, int length) {
666
			super(offset, length);
667
			fTitle = title;
668
		}
669
670
		/*
671
		 * @see org.eclipse.jface.text.source.projection.IProjectionPosition#computeFoldingRegions(org.eclipse.jface.text.IDocument)
672
		 */
673
		public IRegion[] computeProjectionRegions(IDocument document) throws BadLocationException {
674
			int firstLine= document.getLineOfOffset(offset + 0);			
675
			int lastLine= document.getLineOfOffset(offset + length);
676
			
677
			if (firstLine < lastLine) {
678
				int postOffset= document.getLineOffset(firstLine + 1);
679
				IRegion postRegion= new Region(postOffset, offset + length - postOffset);				
680
				return new IRegion[] { postRegion };							
681
			}
682
683
			return null;
684
		}
685
686
		/*
687
		 * @see org.eclipse.jface.text.source.projection.IProjectionPosition#computeCaptionOffset(org.eclipse.jface.text.IDocument)
688
		 */
689
		public int computeCaptionOffset(IDocument document) {			
690
			return 0;
691
		}
692
	}
693
	
694
	/**
554
	 * Projection position that will return two foldable regions: one folding away
695
	 * Projection position that will return two foldable regions: one folding away
555
	 * the lines before the one containing the simple name of the java element, one
696
	 * the lines before the one containing the simple name of the java element, one
556
	 * folding away any lines after the caption.
697
	 * folding away any lines after the caption.
Lines 700-711 Link Here
700
	private boolean fCollapseInnerTypes= true;
841
	private boolean fCollapseInnerTypes= true;
701
	private boolean fCollapseMembers= false;
842
	private boolean fCollapseMembers= false;
702
	private boolean fCollapseHeaderComments= true;
843
	private boolean fCollapseHeaderComments= true;
844
	private boolean fCollapseRegions= true;
703
845
704
	/* filters */
846
	/* filters */
705
	/** Member filter, matches nested members (but not top-level types). */
847
	/** Member filter, matches nested members (but not top-level types). */
706
	private final Filter fMemberFilter = new MemberFilter();
848
	private final Filter fMemberFilter = new MemberFilter();
707
	/** Comment filter, matches comments. */
849
	/** Comment filter, matches comments. */
708
	private final Filter fCommentFilter = new CommentFilter();
850
	private final Filter fCommentFilter = new CommentFilter();
851
	/** Region filter, matches regions. */
852
	private final Filter fRegionFilter = new RegionFilter();
709
	
853
	
710
	/**
854
	/**
711
	 * Reusable scanner.
855
	 * Reusable scanner.
Lines 870-875 Link Here
870
		fCollapseJavadoc= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_JAVADOC);
1014
		fCollapseJavadoc= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_JAVADOC);
871
		fCollapseMembers= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_METHODS);
1015
		fCollapseMembers= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_METHODS);
872
		fCollapseHeaderComments= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_HEADERS);
1016
		fCollapseHeaderComments= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_HEADERS);
1017
		fCollapseRegions= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_REGIONS);
873
	}
1018
	}
874
1019
875
	private void update(FoldingStructureComputationContext ctx) {
1020
	private void update(FoldingStructureComputationContext ctx) {
Lines 911-926 Link Here
911
					Tuple tuple= (Tuple) x.next();
1056
					Tuple tuple= (Tuple) x.next();
912
					JavaProjectionAnnotation existingAnnotation= tuple.annotation;
1057
					JavaProjectionAnnotation existingAnnotation= tuple.annotation;
913
					Position existingPosition= tuple.position;
1058
					Position existingPosition= tuple.position;
914
					if (newAnnotation.isComment() == existingAnnotation.isComment()) {
1059
					if ((newAnnotation.isComment() == existingAnnotation.isComment())
1060
						&& (newAnnotation.isRegion() == existingAnnotation.isRegion())) {
915
						boolean updateCollapsedState= ctx.allowCollapsing() && existingAnnotation.isCollapsed() != newAnnotation.isCollapsed();
1061
						boolean updateCollapsedState= ctx.allowCollapsing() && existingAnnotation.isCollapsed() != newAnnotation.isCollapsed();
916
						if (!isMalformedAnonymousType && existingPosition != null && (!newPosition.equals(existingPosition) || updateCollapsedState)) {
1062
						if (!isMalformedAnonymousType 
1063
								&& existingPosition != null 
1064
								&& (!newPosition.equals(existingPosition) || updateCollapsedState 
1065
										|| !newAnnotation.equalsTitle(existingAnnotation))) {
917
							existingPosition.setOffset(newPosition.getOffset());
1066
							existingPosition.setOffset(newPosition.getOffset());
918
							existingPosition.setLength(newPosition.getLength());
1067
							existingPosition.setLength(newPosition.getLength());
919
							if (updateCollapsedState)
1068
							existingAnnotation.setTitle(newAnnotation.getTitle());
1069
							if (updateCollapsedState) {
920
								if (newAnnotation.isCollapsed())
1070
								if (newAnnotation.isCollapsed())
921
									existingAnnotation.markCollapsed();
1071
									existingAnnotation.markCollapsed();
922
								else
1072
								else
923
									existingAnnotation.markExpanded();
1073
									existingAnnotation.markExpanded();
1074
							}
924
							updates.add(existingAnnotation);
1075
							updates.add(existingAnnotation);
925
						}
1076
						}
926
						matched= true;
1077
						matched= true;
Lines 963-969 Link Here
963
				return;
1114
				return;
964
			
1115
			
965
			ctx.getScanner().setSource(source.toCharArray());
1116
			ctx.getScanner().setSource(source.toCharArray());
966
			computeFoldingStructure(parent.getChildren(), ctx);
1117
			IJavaElement[] elements = parent.getChildren();						
1118
			computeFoldingStructure(elements, ctx);
1119
1120
			if (elements.length > 0) {
1121
				computeRegionFolding(elements[0], ctx);
1122
			}
967
		} catch (JavaModelException x) {
1123
		} catch (JavaModelException x) {
968
		}
1124
		}
969
	}
1125
	}
Lines 980-985 Link Here
980
			}
1136
			}
981
		}
1137
		}
982
	}
1138
	}
1139
	
1140
	private void computeRegionFolding(IJavaElement element, FoldingStructureComputationContext ctx) {		
1141
		IScanner scanner = ctx.getScanner();
1142
		try {
1143
			scanner.readRegions();
1144
			
1145
			// Find all regions
1146
			if (scanner.hasRegions()) {
1147
				List regionsList = scanner.getRegions();
1148
				int listLen = regionsList.size();
1149
				for (int i=0; i<listLen; i++) {
1150
					TitledRegionStruct struct = 
1151
						(TitledRegionStruct)regionsList.get(i);
1152
					int posLen = struct.positionsCount;
1153
					int closeLen = struct.closePositionsCount;
1154
					for (int j=0; j<posLen; j++) {										
1155
						if (j >= closeLen) {
1156
							// Adding this region nevertheless, to prevent "jumping" regions
1157
							addTitledRegion(element, new TitledRegion("", 0, 0), ctx); //$NON-NLS-1$
1158
						} else {						
1159
							int begin = struct.positions[j][0];
1160
							int end = struct.closePositions[j][1];
1161
							
1162
							boolean failed=false;
1163
							// Search for overlaps with other regions
1164
							Collection positions = ctx.getPositions();
1165
							for (Iterator iterator = positions.iterator(); iterator.hasNext(); ) {
1166
								Position position = (Position)iterator.next();
1167
								int positionEnd = position.offset + position.length;
1168
								if (position.offset < begin) {
1169
									if (positionEnd > begin && positionEnd < end) {
1170
										failed=true;
1171
									}
1172
								} else if (position.offset < end) {
1173
									if (positionEnd > end) {
1174
										failed=true;
1175
									}
1176
								}
1177
							}
1178
							if (failed) {
1179
								addTitledRegion(element, new TitledRegion("", 0, 0), ctx); //$NON-NLS-1$
1180
							} else {
1181
								addTitledRegion(element, new TitledRegion(struct.getTitle(j),
1182
										begin, end-begin), ctx);
1183
							}
1184
						}
1185
					}
1186
				}
1187
			}
1188
		} catch (InvalidInputException e) {			
1189
//			e.printStackTrace();
1190
		}
1191
	}
1192
	
1193
	private void addTitledRegion(IJavaElement element, TitledRegion region, 
1194
	                             FoldingStructureComputationContext ctx) {		
1195
		IRegion normalized= alignRegion(region, ctx);
1196
		if (normalized != null) {
1197
			Position position= createTitledPosition(normalized, region.getTitle());
1198
			if (position != null) {
1199
				ctx.addProjectionRange(new JavaProjectionAnnotation(ctx.collapseRegions(), 
1200
						element, false, region.getTitle()), position);
1201
			}
1202
		}
1203
	}
983
1204
984
	/**
1205
	/**
985
	 * Computes the folding structure for a given {@link IJavaElement java element}. Computed
1206
	 * Computes the folding structure for a given {@link IJavaElement java element}. Computed
Lines 1092-1098 Link Here
1092
	 * @param ctx the folding context
1313
	 * @param ctx the folding context
1093
	 * @return the regions to be folded
1314
	 * @return the regions to be folded
1094
	 */
1315
	 */
1095
	protected final IRegion[] computeProjectionRanges(ISourceReference reference, FoldingStructureComputationContext ctx) {
1316
	protected final IRegion[] computeProjectionRanges(ISourceReference reference, FoldingStructureComputationContext ctx) {		
1096
		try {
1317
		try {
1097
				ISourceRange range= reference.getSourceRange();
1318
				ISourceRange range= reference.getSourceRange();
1098
				if (!SourceRange.isAvailable(range))
1319
				if (!SourceRange.isAvailable(range))
Lines 1132-1141 Link Here
1132
						case ITerminalSymbols.TokenNameCOMMENT_LINE:
1353
						case ITerminalSymbols.TokenNameCOMMENT_LINE:
1133
							continue;
1354
							continue;
1134
					}
1355
					}
1135
1356
					
1136
					break;
1357
					break;
1137
				}
1358
				}
1138
1359
				
1139
				regions.add(new Region(start, shift + range.getLength() - start));
1360
				regions.add(new Region(start, shift + range.getLength() - start));
1140
1361
1141
				IRegion[] result= new IRegion[regions.size()];
1362
				IRegion[] result= new IRegion[regions.size()];
Lines 1143-1148 Link Here
1143
				return result;
1364
				return result;
1144
		} catch (JavaModelException e) {
1365
		} catch (JavaModelException e) {
1145
		} catch (InvalidInputException e) {
1366
		} catch (InvalidInputException e) {
1367
		} catch (Exception e) {
1146
		}
1368
		}
1147
1369
1148
		return new IRegion[0];
1370
		return new IRegion[0];
Lines 1217-1222 Link Here
1217
	protected final Position createMemberPosition(IRegion aligned, IMember member) {
1439
	protected final Position createMemberPosition(IRegion aligned, IMember member) {
1218
		return new JavaElementPosition(aligned.getOffset(), aligned.getLength(), member);
1440
		return new JavaElementPosition(aligned.getOffset(), aligned.getLength(), member);
1219
	}
1441
	}
1442
	
1443
	/**
1444
	 * Creates a comment folding position from an
1445
	 * {@link #alignRegion(IRegion, DefaultJavaFoldingStructureProvider.FoldingStructureComputationContext) aligned}
1446
	 * region with a title.
1447
	 * 
1448
	 * @param aligned
1449
	 * @param title
1450
	 * @return a folding position corresponding to <code>aligned</code>
1451
	 */
1452
	protected final Position createTitledPosition(IRegion aligned, String title) {
1453
		return new TitledRegionPosition(title, aligned.getOffset(), aligned.getLength());
1454
	}
1220
1455
1221
	/**
1456
	/**
1222
	 * Aligns <code>region</code> to start and end at a line offset. The region's start is
1457
	 * Aligns <code>region</code> to start and end at a line offset. The region's start is
Lines 1360-1366 Link Here
1360
		Iterator it= annotations.iterator();
1595
		Iterator it= annotations.iterator();
1361
		while (it.hasNext()) {
1596
		while (it.hasNext()) {
1362
			JavaProjectionAnnotation annotation= (JavaProjectionAnnotation) it.next();
1597
			JavaProjectionAnnotation annotation= (JavaProjectionAnnotation) it.next();
1363
			if (tuple.annotation.isComment() == annotation.isComment()) {
1598
			if ((tuple.annotation.isComment() == annotation.isComment())
1599
				&& (tuple.annotation.isRegion() == annotation.isRegion())) {
1364
				Position position= positionMap == null ? ctx.getModel().getPosition(annotation) : (Position) positionMap.get(annotation);
1600
				Position position= positionMap == null ? ctx.getModel().getPosition(annotation) : (Position) positionMap.get(annotation);
1365
				if (position == null)
1601
				if (position == null)
1366
					continue;
1602
					continue;
Lines 1421-1426 Link Here
1421
	public final void collapseComments() {
1657
	public final void collapseComments() {
1422
		modifyFiltered(fCommentFilter, false);
1658
		modifyFiltered(fCommentFilter, false);
1423
	}
1659
	}
1660
	
1661
	/**
1662
	 * Collapse all regions.
1663
	 */
1664
	public final void collapseRegions() {
1665
		modifyFiltered(fRegionFilter, false);
1666
	}
1424
1667
1425
	/*
1668
	/*
1426
	 * @see org.eclipse.jdt.ui.text.folding.IJavaFoldingStructureProviderExtension#collapseElements(org.eclipse.jdt.core.IJavaElement[])
1669
	 * @see org.eclipse.jdt.ui.text.folding.IJavaFoldingStructureProviderExtension#collapseElements(org.eclipse.jdt.core.IJavaElement[])
(-)ui/org/eclipse/jdt/ui/PreferenceConstants.java (+43 lines)
Lines 1366-1371 Link Here
1366
	public final static String EDITOR_SINGLE_LINE_COMMENT_UNDERLINE= IJavaColorConstants.JAVA_SINGLE_LINE_COMMENT + EDITOR_UNDERLINE_SUFFIX;
1366
	public final static String EDITOR_SINGLE_LINE_COMMENT_UNDERLINE= IJavaColorConstants.JAVA_SINGLE_LINE_COMMENT + EDITOR_UNDERLINE_SUFFIX;
1367
1367
1368
	/**
1368
	/**
1369
	 * Bold "region" keyword. 
1370
	 * 
1371
	 * @since 3.4
1372
	 */
1373
	public final static String REGION_BOLD = IJavaColorConstants.REGION_TAG + EDITOR_BOLD_SUFFIX;
1374
	
1375
	/**
1376
	 * Italic "region" keyword.
1377
	 * 
1378
	 * @since 3.4
1379
	 */
1380
	public final static String REGION_ITALIC = IJavaColorConstants.REGION_TAG + EDITOR_ITALIC_SUFFIX;
1381
	
1382
	/**
1383
	 * Underlined "region" keyword.
1384
	 * 
1385
	 * @since 3.4
1386
	 */
1387
	public final static String REGION_UNDERLINE = IJavaColorConstants.REGION_TAG + EDITOR_UNDERLINE_SUFFIX;
1388
	
1389
	/**
1390
	 * Underlined "region" keyword.
1391
	 * 
1392
	 * @since 3.4
1393
	 */
1394
	public final static String REGION_STRIKETHROUGH = IJavaColorConstants.REGION_TAG + EDITOR_STRIKETHROUGH_SUFFIX;
1395
	
1396
	
1397
	/**
1369
	 * A named preference that holds the color used to render java keywords.
1398
	 * A named preference that holds the color used to render java keywords.
1370
	 * <p>
1399
	 * <p>
1371
	 * Value is of type <code>String</code>. A RGB color value encoded as a string
1400
	 * Value is of type <code>String</code>. A RGB color value encoded as a string
Lines 3216-3221 Link Here
3216
	 * @since 3.1
3245
	 * @since 3.1
3217
	 */
3246
	 */
3218
	public static final String EDITOR_FOLDING_HEADERS= "editor_folding_default_headers"; //$NON-NLS-1$
3247
	public static final String EDITOR_FOLDING_HEADERS= "editor_folding_default_headers"; //$NON-NLS-1$
3248
	
3249
	/**
3250
	 * A named preference that stores the value for region folding for the default folding provider.
3251
	 * 
3252
	 * @since 3.4
3253
	 */
3254
	public static final String EDITOR_FOLDING_REGIONS = "editor_folding_regions";  //$NON-NLS-1$
3219
3255
3220
3256
3221
	//---------- Properties File Editor ----------
3257
	//---------- Properties File Editor ----------
Lines 3649-3654 Link Here
3649
3685
3650
		store.setDefault(PreferenceConstants.EDITOR_TAB_WIDTH, 4);
3686
		store.setDefault(PreferenceConstants.EDITOR_TAB_WIDTH, 4);
3651
		store.setDefault(PreferenceConstants.EDITOR_SPACES_FOR_TABS, false);
3687
		store.setDefault(PreferenceConstants.EDITOR_SPACES_FOR_TABS, false);
3688
		
3689
		// "<region>" tags are default: bold, like TODO, FIXME, XXX, etc.
3690
		store.setDefault(PreferenceConstants.REGION_BOLD, true);
3691
		store.setDefault(PreferenceConstants.REGION_ITALIC, false);
3692
		store.setDefault(PreferenceConstants.REGION_STRIKETHROUGH, false);
3693
		store.setDefault(PreferenceConstants.REGION_UNDERLINE, false);
3652
3694
3653
		setDefaultAndFireEvent(
3695
		setDefaultAndFireEvent(
3654
				store,
3696
				store,
Lines 3877-3882 Link Here
3877
		store.setDefault(PreferenceConstants.EDITOR_FOLDING_METHODS, false);
3919
		store.setDefault(PreferenceConstants.EDITOR_FOLDING_METHODS, false);
3878
		store.setDefault(PreferenceConstants.EDITOR_FOLDING_IMPORTS, true);
3920
		store.setDefault(PreferenceConstants.EDITOR_FOLDING_IMPORTS, true);
3879
		store.setDefault(PreferenceConstants.EDITOR_FOLDING_HEADERS, true);
3921
		store.setDefault(PreferenceConstants.EDITOR_FOLDING_HEADERS, true);
3922
		store.setDefault(PreferenceConstants.EDITOR_FOLDING_REGIONS, true);
3880
		
3923
		
3881
		// properties file editor
3924
		// properties file editor
3882
		setDefaultAndFireEvent(
3925
		setDefaultAndFireEvent(
(-)ui/org/eclipse/jdt/ui/text/folding/TitledRegion.java (+16 lines)
Added Link Here
1
package org.eclipse.jdt.ui.text.folding;
2
3
import org.eclipse.jface.text.Region;
4
5
public class TitledRegion extends Region {
6
	private String fTitle;
7
8
	public TitledRegion(String title, int offset, int length) {
9
		super(offset, length);
10
		fTitle = title;
11
	}
12
	
13
	public String getTitle() {
14
		return fTitle;
15
	}
16
}
(-)ui/org/eclipse/jdt/internal/ui/text/CombinedWordRuleXMLTag.java (+430 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2006 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.jdt.internal.ui.text;
12
13
14
import org.eclipse.core.runtime.Assert;
15
16
import org.eclipse.jface.text.rules.ICharacterScanner;
17
import org.eclipse.jface.text.rules.IToken;
18
import org.eclipse.jface.text.rules.IWordDetector;
19
import org.eclipse.jface.text.rules.Token;
20
21
22
/**
23
 * Extension of CombinedWorldRule which allows to identify xml tags.
24
 *
25
 * @see IWordDetector
26
 * @see CombinedWordRule
27
 * @since 3.0
28
 */
29
public class CombinedWordRuleXMLTag extends CombinedWordRule {
30
31
	/**
32
	 * Word matcher, that associates matched words with tokens.
33
	 */
34
	public static class TagMatcher extends WordMatcher {
35
36
		/**
37
		 * Adds a word and the token to be returned if it is detected.
38
		 *
39
		 * @param word the word this rule will search for, may not be <code>null</code>
40
		 * @param attributes the xml tag attributes, must not be <code>null</code>
41
		 * @param token the token to be returned if the word has been found, may not be <code>null</code>
42
		 */
43
		public void addWord(String word, String[] attributes, IToken token) {
44
			Assert.isNotNull(word);
45
			Assert.isNotNull(attributes);
46
			Assert.isNotNull(token);
47
48
			fWords.put(word, new TagBuffer(word, attributes, token));			
49
		}
50
		
51
		/**
52
		 * Adds a word and the token to be returned if it is detected.
53
		 *
54
		 * @param word the word this rule will search for, may not be <code>null</code>
55
		 * @param token the token to be returned if the word has been found, may not be <code>null</code>
56
		 */
57
		public void addWord(String word, IToken token) {
58
			addWord(word, new String[0], token);
59
		}
60
61
		/**
62
		 * Returns the token associated to the given word and the scanner state.
63
		 *
64
		 * @param scanner the scanner
65
		 * @param word the word
66
		 * @param attributes the attributes
67
		 * @return the token or <code>null</code> if none is associated by this matcher
68
		 */
69
		public IToken evaluate(ICharacterScanner scanner, String word,
70
		                       String[] attributes) {
71
			TagBuffer buffer = (TagBuffer) fWords.get(word);
72
			if (buffer != null) {
73
				int len = attributes.length;
74
				String[] bufferAttributes = buffer.getAttributes();
75
				if (len != bufferAttributes.length) {
76
					return Token.UNDEFINED;
77
				}
78
				if (len > 0) {
79
					boolean found = false;
80
					int[] usedAttrIndex = new int[len];
81
					for (int i=0; i<len; i++) {
82
						found= false;
83
						for (int j=0; j < len; j++) {
84
							if (usedAttrIndex[j] < 0) {
85
								continue;
86
							}
87
							if (bufferAttributes[i].equals(attributes[j])) {
88
								found= true;
89
								usedAttrIndex[j] = -1;
90
								break;
91
							}
92
						}
93
						if (!found) {
94
							break;
95
						}
96
					}
97
					if (!found) {
98
						return Token.UNDEFINED;
99
					}
100
				}
101
				return buffer.getToken();
102
			}
103
			return Token.UNDEFINED;
104
		}
105
106
		/**
107
		 * Removes all words.
108
		 */
109
		public void clearWords() {
110
			fWords.clear();
111
		}
112
	}
113
114
	/**
115
	 * Character buffer, mutable <b>or</b> suitable for use as key in hash maps.
116
	 */
117
	public static class TagBuffer extends CharacterBuffer {
118
119
		/** Is hash code cached? */
120
		private boolean fIsHashCached= false;
121
		/** The hash code */
122
		private int fHashCode;
123
124
		/**
125
		 * List of tag attributes.
126
		 */
127
		protected String[] fAttributes = null;
128
		
129
		/**
130
		 * Nr. of attributes.
131
		 */
132
		protected int fAttributesCount = 0;
133
		
134
		/**
135
		 * The token belonging to this.
136
		 */
137
		protected IToken fToken = null;
138
		
139
		/**
140
		 * Creates a buffer with the reserved size.
141
		 * 
142
		 * @param size
143
		 */
144
		public TagBuffer(int size) {
145
			super(size);
146
		}
147
148
		/**
149
		 * Initialize with the given content.
150
		 *
151
		 * @param content the initial content
152
		 * @param attributes the attributes, may be <code>null</code>
153
		 * @param token the token belonging to this object, may be <code>null</code>
154
		 */
155
		public TagBuffer(String content, String[] attributes, IToken token) {
156
			super(content);
157
			fAttributes= attributes;
158
			fToken = token;
159
			if (fAttributes != null) {
160
				fAttributesCount= fAttributes.length;
161
			}
162
		}		
163
		
164
		public String getBuffer() {
165
			return new String(fContent, 0, fLength);
166
		}		
167
168
		/**
169
		 * Returns the content as string.
170
		 *
171
		 * @return the content
172
		 */
173
		public String toString() {
174
			String attributes = "["; //$NON-NLS-1$
175
			for (int i=0; i < fAttributesCount; i++) {
176
				attributes += fAttributes[i]; 
177
				if (i < fAttributes.length-1) {
178
					attributes += ","; //$NON-NLS-1$
179
				}
180
			}
181
			attributes += "]"; //$NON-NLS-1$
182
			return super.toString() + attributes + "," + fToken; //$NON-NLS-1$
183
		}
184
185
		/*
186
		 * @see java.lang.Object#hashCode()
187
		 */
188
		public int hashCode() {
189
			if (!fIsHashCached) {
190
				fHashCode= super.hashCode();
191
				for (int i=0; i<fAttributesCount; i++) {
192
					fHashCode += fAttributes[i].hashCode();
193
				}
194
				fIsHashCached= true;
195
			}
196
			return fHashCode;
197
		}
198
199
200
		/*
201
		 * @see java.lang.Object#equals(java.lang.Object)
202
		 */
203
		public boolean equals(Object obj) {
204
			if (!super.equals(obj)) {
205
				return false;
206
			}
207
			if (!(obj instanceof TagBuffer)) {
208
				return false;
209
			}
210
			TagBuffer buffer = (TagBuffer)obj;
211
			String[] attr = buffer.getAttributes();
212
			for (int i=0; i < fAttributesCount; i++) {
213
				if (!fAttributes.equals(attr[i])) {
214
					return false;
215
				}
216
			}
217
			return true;
218
		}
219
				
220
		public int getAttributesCount() {
221
			return fAttributesCount;
222
		}
223
		
224
		public String[] getAttributes() {
225
			return fAttributes;
226
		}
227
		
228
		public IToken getToken() {
229
			return fToken;
230
		}
231
	}
232
233
	/**
234
	 * Creates a rule which, with the help of an word detector, will return the token
235
	 * associated with the detected word. If no token has been associated, the
236
	 * specified default token will be returned.
237
	 *
238
	 * @param detector the word detector to be used by this rule, may not be <code>null</code>
239
	 * @param defaultToken the default token to be returned on success
240
	 *		if nothing else is specified, may not be <code>null</code>
241
	 *
242
	 * @see TagMatcher#addWord(String, IToken)
243
	 */
244
	public CombinedWordRuleXMLTag(IWordDetector detector, IToken defaultToken) {
245
		super(detector, null, defaultToken, new TagBuffer(16));
246
	}
247
	
248
	/**
249
	 * Creates a rule which, with the help of an word detector, will return the token
250
	 * associated with the detected word. If no token has been associated, the
251
	 * specified default token will be returned.
252
	 *
253
	 * @param detector the word detector to be used by this rule, may not be <code>null</code>
254
	 * @param matcher the initial word matcher
255
	 * @param defaultToken the default token to be returned on success
256
	 *		if nothing else is specified, may not be <code>null</code>
257
	 *
258
	 * @see TagMatcher#addWord(String, IToken)
259
	 */
260
	public CombinedWordRuleXMLTag(IWordDetector detector, WordMatcher matcher, IToken defaultToken) {
261
		super(detector, matcher, defaultToken, new TagBuffer(16));
262
	}
263
	
264
	/*
265
	 * @see IRule#evaluate(ICharacterScanner)
266
	 */
267
	public IToken evaluate(ICharacterScanner scanner) {
268
		int c= scanner.read();
269
		if (fDetector.isWordStart((char) c)) {
270
			if (fColumn == UNDEFINED || (fColumn == scanner.getColumn() - 1)) {
271
272
				fBuffer.clear();
273
				do {
274
					// No leading '<'
275
					c= scanner.read();
276
					if (c == ICharacterScanner.EOF || !fDetector.isWordPart((char) c)) {
277
							break;
278
					}
279
					fBuffer.append((char) c);					
280
				} while (true);
281
				
282
				String[] attributes = null;
283
				int attributesCount;
284
285
				boolean noAttributes = false;
286
				if (c == ICharacterScanner.EOF) {
287
					scanner.unread();
288
					return fDefaultToken;
289
				} else {
290
					for (; Character.isWhitespace((char)c) && c != ICharacterScanner.EOF; ) {
291
						c= scanner.read(); 
292
					}
293
					
294
					if (c == ICharacterScanner.EOF) {
295
						scanner.unread();
296
						return fDefaultToken;
297
					} else {												
298
						if (c == '>') {
299
							noAttributes = true;
300
						} else {
301
							boolean firstFound = false; // 'attr' 1st part closed
302
							boolean attrReading = true; // reading 'attr="xy"'
303
							boolean stringReading = false; // reading '"xy"'
304
							boolean stringFound = false;   // reading of '"xy"' done
305
							boolean equalFound = false;   // '=' found					
306
							int attribIndex = 0;
307
							attributesCount = 1;
308
							attributes = new String[1];
309
							attributes[0] = new String(new char[]{(char)c});
310
							boolean error = false;
311
							whileLoop:						
312
							while (true) {
313
								c = scanner.read();
314
								
315
								char ch = (char)c;
316
								if (c == ICharacterScanner.EOF) {	
317
									scanner.unread();
318
									error = true;
319
									break;
320
								} else if (ch == '>') {
321
									if (stringFound) {
322
										attrReading = false;
323
										break;
324
									} else if (!stringReading) {
325
										break;
326
									}												
327
								} else if (Character.isWhitespace(ch)) {
328
									if (attrReading) {
329
										if (stringFound) {
330
											// Whitespace between attributes
331
											attrReading = false;									
332
										} else {
333
											firstFound = true;
334
										}
335
									}
336
								} else {							
337
									switch (ch) {
338
										case '"': if (attrReading) { 
339
											if (!equalFound) {
340
												error = true;
341
												break whileLoop;
342
											} else {
343
												if (stringReading) {
344
													stringFound = true;
345
												} else {
346
													stringReading = true;
347
												}
348
											}
349
										} else {
350
											error = true;
351
											break whileLoop;
352
										}
353
										break;
354
										case '=': if (attrReading) {
355
											if (equalFound && !stringReading) {
356
												// 2* '='
357
												error = true;
358
												break whileLoop;
359
											} else {
360
												equalFound=true;
361
												firstFound=true;
362
											}
363
										} else {
364
											error = true;
365
											break whileLoop;
366
										}
367
										break;								
368
										default:
369
											if (!attrReading) {
370
												attributesCount++;
371
												System.arraycopy(attributes, 0, 
372
														attributes = new String[attributesCount], 
373
														0, attributesCount-1);
374
												attribIndex++;
375
												
376
												attributes[attribIndex] = new String(new char[]{ch});
377
												// Reset to start values
378
												firstFound = false;
379
												attrReading = true;
380
												stringReading = false;
381
												stringFound = false;
382
												equalFound = false;
383
											} else {
384
												if (firstFound) { 
385
													if (!stringReading) {
386
														error=true;
387
														break whileLoop;
388
													}
389
												} else {										
390
													attributes[attribIndex] += ch;
391
												}
392
											}
393
									}													
394
								}
395
							}
396
							noAttributes = error || attrReading;															
397
						}
398
					}
399
				}
400
				if (noAttributes) {
401
					attributes = new String[0];
402
					attributesCount = 0;
403
				}
404
405
				IToken token = null;
406
				boolean tokenFound = false;
407
				for (int i= 0, n= fMatchers.size(); i < n; i++) {
408
					token = ((TagMatcher) fMatchers.get(i)).evaluate(scanner, 
409
							((TagBuffer)fBuffer).getBuffer(), attributes);
410
					if (!token.isUndefined()) {
411
						tokenFound = true;
412
						break;
413
					}
414
				}
415
				
416
				if (tokenFound) {					
417
					return token;
418
				}
419
420
				if (fDefaultToken.isUndefined())
421
					unreadBuffer(scanner);
422
423
				return fDefaultToken;
424
			}
425
		}
426
427
		scanner.unread();
428
		return Token.UNDEFINED;
429
	}	
430
}

Return to bug 63808