Source code: novaworx/syntax/KeywordMap.java
1 /*
2 Novaworx Development Environment
3 Copyright (C) 2000-2003 Mark Soderquist
4 Portions Copyright (C) 1998-2001 Slava Pestov
5 Portions Copyright (C) 1999-2000 Mike Dillon
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to:
19
20 Free Software Foundation, Inc.
21 59 Temple Place, Suite 330
22 Boston, MA 02111-1307 USA
23 */
24
25 package novaworx.syntax;
26
27 import java.util.Vector;
28 import javax.swing.text.Segment;
29
30 /**
31 A <code>KeywordMap</code> is similar to a hashtable in that it maps keys
32 to values. However, the keys are Swing segments. This allows lookups of
33 text substrings without the overhead of creating a new string object.
34
35 @author Slava Pestov
36 @author Mike Dillon
37 @author Mark Soderquist
38 */
39 public class KeywordMap {
40
41 private Keyword[] maKeywords;
42 private boolean mbIgnoreCase;
43 private StringBuffer moNoWordSeparators;
44 protected int miMapLength;
45
46 /**
47 Create a new <code>KeywordMap</code>.
48
49 @param mbIgnoreCase True if keys are case insensitive
50 */
51 public KeywordMap( boolean abIgnoreCase) {
52 this( abIgnoreCase, 52 );
53 }
54
55 /**
56 Create a new <code>KeywordMap</code>.
57
58 @param mbIgnoreCase True if the keys are case insensitive.
59 @param miMapLength The number of 'buckets' to create.
60 A value of 52 will give good performance for most maps.
61 */
62 public KeywordMap(boolean abIgnoreCase, int aiMapLength) {
63 miMapLength = aiMapLength;
64 mbIgnoreCase = abIgnoreCase;
65 moNoWordSeparators = new StringBuffer();
66 maKeywords = new Keyword[ aiMapLength ];
67 }
68
69 /**
70 Look up a key.
71
72 @param aoText The text segment.
73 @param aiOffset The offset of the substring within the text segment.
74 @param aiLength The length of the substring.
75 */
76 public byte lookup( Segment aoText, int aiOffset, int aiLength ) {
77 if( aiLength == 0 ) return Token.NULL;
78 Keyword oKeyword = maKeywords[ getSegmentMapKey( aoText, aiOffset, aiLength) ];
79 while( oKeyword != null) {
80 if( aiLength != oKeyword.maKeyword.length ) {
81 oKeyword = oKeyword.moNext;
82 continue;
83 }
84 if( SyntaxUtilities.regionMatches( mbIgnoreCase, aoText, aiOffset, oKeyword.maKeyword ) ) {
85 return oKeyword.myID;
86 }
87 oKeyword = oKeyword.moNext;
88 }
89 return Token.NULL;
90 }
91
92 /**
93 Adds a key-value mapping.
94
95 @param keyword The key
96 @param id The value
97 */
98 public void add( String asKeyword, byte ayID ) {
99 int iKey = getStringMapKey( asKeyword );
100
101 char[] aKeyword = asKeyword.toCharArray();
102
103 // Complete-word command needs a list of all
104 // non-alphanumeric characters used in a keyword map.
105 loop:
106 for(int iIndex = 0; iIndex < aKeyword.length; iIndex++ ) {
107 char cChar = aKeyword[ iIndex ];
108 if( !Character.isLetterOrDigit( cChar ) ) {
109 for(int iSeparatorIndex = 0; iSeparatorIndex < moNoWordSeparators.length(); iSeparatorIndex++) {
110 if( moNoWordSeparators.charAt( iSeparatorIndex ) == cChar ) continue loop;
111 }
112
113 moNoWordSeparators.append( cChar );
114 }
115 }
116
117 maKeywords[ iKey ] = new Keyword( aKeyword, ayID, maKeywords[ iKey ] );
118 }
119
120 /**
121 Returns all non-alphanumeric characters that appear in the
122 keywords of this keyword maKeywords.
123 */
124 public String getNonAlphaNumericChars() {
125 return moNoWordSeparators.toString();
126 }
127
128 /**
129 Returns an array containing all keywords in this keyword map.
130 */
131 public String[] getKeywords() {
132 Vector vVector = new Vector(100);
133 for(int iIndex = 0; iIndex < maKeywords.length; iIndex++ ) {
134 Keyword oKeyword = maKeywords[ iIndex ];
135 while( oKeyword != null ) {
136 vVector.addElement( new String( oKeyword.maKeyword) );
137 oKeyword = oKeyword.moNext;
138 }
139 }
140 String[] aReturn = new String[ vVector.size() ];
141 vVector.copyInto( aReturn );
142 return aReturn;
143 }
144
145 /**
146 Returns true if the keyword maKeywords is set to be case insensitive,
147 false otherwise.
148 */
149 public boolean getIgnoreCase() {
150 return mbIgnoreCase;
151 }
152
153 /**
154 Sets if the keyword maKeywords should be case insensitive.
155 @param mbIgnoreCase True if the keyword maKeywords should be case
156 insensitive, false otherwise.
157 */
158 public void setIgnoreCase( boolean abIgnoreCase ) {
159 mbIgnoreCase = abIgnoreCase;
160 }
161
162 protected int getStringMapKey( String asString ) {
163 return (
164 Character.toUpperCase( asString.charAt( 0 ) ) +
165 Character.toUpperCase( asString.charAt( asString.length() - 1 ) )
166 ) % miMapLength;
167 }
168
169 protected int getSegmentMapKey( Segment aoSegment, int aiOffset, int aiLength ) {
170 return (
171 Character.toUpperCase( aoSegment.array[ aiOffset ] ) +
172 Character.toUpperCase( aoSegment.array[ aiOffset + aiLength - 1 ] )
173 ) % miMapLength;
174 }
175
176 private class Keyword {
177
178 public char[] maKeyword;
179 public byte myID;
180 public Keyword moNext;
181
182 public Keyword( char[] aaKeyword, byte ayID, Keyword aoNext ) {
183 maKeyword = aaKeyword;
184 myID = ayID;
185 moNext = aoNext;
186 }
187
188 }
189
190 }