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 to
19 * values. However, the `keys' are Swing segments. This allows lookups of text
20 * 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 *
32 * @param ignoreCase
33 * True if keys are case insensitive
34 */
35 public KeywordMap( boolean ignoreCase )
36 {
37 this( ignoreCase, 52 );
38 this.ignoreCase = ignoreCase;
39 }
40
41 /***
42 * Creates a new <code>KeywordMap</code>.
43 *
44 * @param ignoreCase
45 * True if the keys are case insensitive
46 * @param mapLength
47 * The number of `buckets' to create. A value of 52 will give good
48 * performance for most maps.
49 */
50 public KeywordMap( boolean ignoreCase, int mapLength )
51 {
52 this.mapLength = mapLength;
53 this.ignoreCase = ignoreCase;
54 map = new Keyword[mapLength];
55 }
56
57 /***
58 * Looks up a key.
59 *
60 * @param text
61 * The text segment
62 * @param offset
63 * The offset of the substring within the text segment
64 * @param length
65 * The length of the substring
66 */
67 public byte lookup( Segment text, int offset, int length )
68 {
69 if( length == 0 )
70 return Token.NULL;
71 Keyword k = map[getSegmentMapKey( text, offset, length )];
72 while( k != null )
73 {
74 if( length != k.keyword.length )
75 {
76 k = k.next;
77 continue;
78 }
79 if( SyntaxUtilities.regionMatches( ignoreCase, text, offset, k.keyword ) )
80 return k.id;
81 k = k.next;
82 }
83 return Token.NULL;
84 }
85
86 /***
87 * Adds a key-value mapping.
88 *
89 * @param keyword
90 * The key
91 * @Param id The value
92 */
93 public void add( String keyword, byte id )
94 {
95 int key = getStringMapKey( keyword );
96 map[key] = new Keyword( keyword.toCharArray(), id, map[key] );
97 }
98
99 /***
100 * Returns true if the keyword map is set to be case insensitive, false
101 * otherwise.
102 */
103 public boolean getIgnoreCase()
104 {
105 return ignoreCase;
106 }
107
108 /***
109 * Sets if the keyword map should be case insensitive.
110 *
111 * @param ignoreCase
112 * True if the keyword map should be case insensitive, false
113 * otherwise
114 */
115 public void setIgnoreCase( boolean ignoreCase )
116 {
117 this.ignoreCase = ignoreCase;
118 }
119
120
121 protected int mapLength;
122
123 protected int getStringMapKey( String s )
124 {
125 return ( Character.toUpperCase( s.charAt( 0 ) ) + Character.toUpperCase( s.charAt( s.length() - 1 ) ) )
126 % mapLength;
127 }
128
129 protected int getSegmentMapKey( Segment s, int off, int len )
130 {
131 return ( Character.toUpperCase( s.array[off] ) + Character.toUpperCase( s.array[off + len - 1] ) ) % mapLength;
132 }
133
134
135 class Keyword
136 {
137 public Keyword( char[] keyword, byte id, Keyword next )
138 {
139 this.keyword = keyword;
140 this.id = id;
141 this.next = next;
142 }
143
144 public char[] keyword;
145 public byte id;
146 public Keyword next;
147 }
148
149 private Keyword[] map;
150 private boolean ignoreCase;
151 }