1
2
3
4
5
6
7
8
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
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
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 }