View Javadoc

1   /*
2    * KeywordMap.java - Fast keyword->id map
3    * Copyright (C) 1998, 1999 Slava Pestov
4    * Copyright (C) 1999 Mike Dillon
5    *
6    * You may use and modify this package for any purpose. Redistribution is
7    * permitted, in both source and binary form, provided that this notice
8    * remains intact in all source distributions of this package.
9    */
10  
11  package org.syntax.jedit;
12  
13  import javax.swing.text.Segment;
14  
15  import org.syntax.jedit.tokenmarker.Token;
16  
17  /***
18   * A <code>KeywordMap</code> is similar to a hashtable in that it maps keys
19   * to values. However, the `keys' are Swing segments. This allows lookups of
20   * text substrings without the overhead of creating a new string object.
21   * <p>
22   * This class is used by <code>CTokenMarker</code> to map keywords to ids.
23   *
24   * @author Slava Pestov, Mike Dillon
25   * @version $Id$
26   */
27  public class KeywordMap
28  {
29  	/***
30  	 * Creates a new <code>KeywordMap</code>.
31  	 * @param ignoreCase True if keys are case insensitive
32  	 */
33  	public KeywordMap(boolean ignoreCase)
34  	{
35  		this(ignoreCase, 52);
36  		this.ignoreCase = ignoreCase;
37  	}
38  
39  	/***
40  	 * Creates a new <code>KeywordMap</code>.
41  	 * @param ignoreCase True if the keys are case insensitive
42  	 * @param mapLength The number of `buckets' to create.
43  	 * A value of 52 will give good performance for most maps.
44  	 */
45  	public KeywordMap(boolean ignoreCase, int mapLength)
46  	{
47  		this.mapLength = mapLength;
48  		this.ignoreCase = ignoreCase;
49  		map = new Keyword[mapLength];
50  	}
51  
52  	/***
53  	 * Looks up a key.
54  	 * @param text The text segment
55  	 * @param offset The offset of the substring within the text segment
56  	 * @param length The length of the substring
57  	 */
58  	public byte lookup(Segment text, int offset, int length)
59  	{
60  		if(length == 0)
61  			return Token.NULL;
62  		Keyword k = map[getSegmentMapKey(text, offset, length)];
63  		while(k != null)
64  		{
65  			if(length != k.keyword.length)
66  			{
67  				k = k.next;
68  				continue;
69  			}
70  			if(SyntaxUtilities.regionMatches(ignoreCase,text,offset,
71  				k.keyword))
72  				return k.id;
73  			k = k.next;
74  		}
75  		return Token.NULL;
76  	}
77  
78  	/***
79  	 * Adds a key-value mapping.
80  	 * @param keyword The key
81  	 * @Param id The value
82  	 */
83  	public void add(String keyword, byte id)
84  	{
85  		int key = getStringMapKey(keyword);
86  		map[key] = new Keyword(keyword.toCharArray(),id,map[key]);
87  	}
88  
89  	/***
90  	 * Returns true if the keyword map is set to be case insensitive,
91  	 * false otherwise.
92  	 */
93  	public boolean getIgnoreCase()
94  	{
95  		return ignoreCase;
96  	}
97  
98  	/***
99  	 * Sets if the keyword map should be case insensitive.
100 	 * @param ignoreCase True if the keyword map should be case
101 	 * insensitive, false otherwise
102 	 */
103 	public void setIgnoreCase(boolean ignoreCase)
104 	{
105 		this.ignoreCase = ignoreCase;
106 	}
107 
108 	// protected members
109 	protected int mapLength;
110 
111 	protected int getStringMapKey(String s)
112 	{
113 		return (Character.toUpperCase(s.charAt(0)) +
114 				Character.toUpperCase(s.charAt(s.length()-1)))
115 				% mapLength;
116 	}
117 
118 	protected int getSegmentMapKey(Segment s, int off, int len)
119 	{
120 		return (Character.toUpperCase(s.array[off]) +
121 				Character.toUpperCase(s.array[off + len - 1]))
122 				% mapLength;
123 	}
124 
125 	// private members
126 	class Keyword
127 	{
128 		public Keyword(char[] keyword, byte id, Keyword next)
129 		{
130 			this.keyword = keyword;
131 			this.id = id;
132 			this.next = next;
133 		}
134 
135 		public char[] keyword;
136 		public byte id;
137 		public Keyword next;
138 	}
139 
140 	private Keyword[] map;
141 	private boolean ignoreCase;
142 }