PageRenderTime 49ms CodeModel.GetById 41ms app.highlight 6ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre4/org/gjt/sp/jedit/syntax/KeywordMap.java

#
Java | 256 lines | 135 code | 22 blank | 99 comment | 15 complexity | c431afa766c9bd17d058359fe36f2de5 MD5 | raw file
  1/*
  2 * KeywordMap.java - Fast keyword->id map
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 1998, 2002 Slava Pestov
  7 * Copyright (C) 1999 Mike Dillon
  8 *
  9 * This program is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU General Public License
 11 * as published by the Free Software Foundation; either version 2
 12 * of the License, or any later version.
 13 *
 14 * This program is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 17 * GNU General Public License for more details.
 18 *
 19 * You should have received a copy of the GNU General Public License
 20 * along with this program; if not, write to the Free Software
 21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 22 */
 23package org.gjt.sp.jedit.syntax;
 24
 25import javax.swing.text.Segment;
 26import java.util.Vector;
 27
 28/**
 29 * A <code>KeywordMap</code> is similar to a hashtable in that it maps keys
 30 * to values. However, the `keys' are Swing segments. This allows lookups of
 31 * text substrings without the overhead of creating a new string object.
 32 *
 33 * @author Slava Pestov, Mike Dillon
 34 * @version $Id: KeywordMap.java 4759 2003-06-05 00:01:49Z spestov $
 35 */
 36public class KeywordMap
 37{
 38	//{{{ KeywordMap constructor
 39	/**
 40	 * Creates a new <code>KeywordMap</code>.
 41	 * @param ignoreCase True if keys are case insensitive
 42	 */
 43	public KeywordMap(boolean ignoreCase)
 44	{
 45		this(ignoreCase, 52);
 46		this.ignoreCase = ignoreCase;
 47		noWordSep = new StringBuffer();
 48	} //}}}
 49
 50	//{{{ KeywordMap constructor
 51	/**
 52	 * Creates a new <code>KeywordMap</code>.
 53	 * @param ignoreCase True if the keys are case insensitive
 54	 * @param mapLength The number of `buckets' to create.
 55	 * A value of 52 will give good performance for most maps.
 56	 */
 57	public KeywordMap(boolean ignoreCase, int mapLength)
 58	{
 59		this.mapLength = mapLength;
 60		this.ignoreCase = ignoreCase;
 61		map = new Keyword[mapLength];
 62	} //}}}
 63
 64	//{{{ lookup() method
 65	/**
 66	 * Looks up a key.
 67	 * @param text The text segment
 68	 * @param offset The offset of the substring within the text segment
 69	 * @param length The length of the substring
 70	 */
 71	public byte lookup(Segment text, int offset, int length)
 72	{
 73		if(length == 0)
 74			return Token.NULL;
 75		Keyword k = map[getSegmentMapKey(text, offset, length)];
 76		while(k != null)
 77		{
 78			if(length != k.keyword.length)
 79			{
 80				k = k.next;
 81				continue;
 82			}
 83			if(SyntaxUtilities.regionMatches(ignoreCase,text,offset,
 84				k.keyword))
 85				return k.id;
 86			k = k.next;
 87		}
 88		return Token.NULL;
 89	} //}}}
 90
 91	//{{{ add() method
 92	/**
 93	 * Adds a key-value mapping.
 94	 * @param keyword The key
 95	 * @param id The value
 96	 */
 97	public void add(String keyword, byte id)
 98	{
 99		add(keyword.toCharArray(),id);
100	} //}}}
101
102	//{{{ add() method
103	/**
104	 * Adds a key-value mapping.
105	 * @param keyword The key
106	 * @param id The value
107	 * @since jEdit 4.2pre3
108	 */
109	public void add(char[] keyword, byte id)
110	{
111		int key = getStringMapKey(keyword);
112
113		// complete-word command needs a list of all non-alphanumeric
114		// characters used in a keyword map.
115loop:		for(int i = 0; i < keyword.length; i++)
116		{
117			char ch = keyword[i];
118			if(!Character.isLetterOrDigit(ch))
119			{
120				for(int j = 0; j < noWordSep.length(); j++)
121				{
122					if(noWordSep.charAt(j) == ch)
123						continue loop;
124				}
125
126				noWordSep.append(ch);
127			}
128		}
129
130		noWordSepStr = null;
131		map[key] = new Keyword(keyword,id,map[key]);
132	} //}}}
133
134	//{{{ getNonAlphaNumericChars() method
135	/**
136	 * Returns all non-alphanumeric characters that appear in the
137	 * keywords of this keyword map.
138	 * @since jEdit 4.0pre3
139	 */
140	public String getNonAlphaNumericChars()
141	{
142		return noWordSep.toString();
143	} //}}}
144
145	//{{{ getKeywords() method
146	/**
147	 * Returns an array containing all keywords in this keyword map.
148	 * @since jEdit 4.0pre3
149	 */
150	public String[] getKeywords()
151	{
152		Vector vector = new Vector(100);
153		for(int i = 0; i < map.length; i++)
154		{
155			Keyword keyword = map[i];
156			while(keyword != null)
157			{
158				vector.addElement(new String(keyword.keyword));
159				keyword = keyword.next;
160			}
161		}
162		String[] retVal = new String[vector.size()];
163		vector.copyInto(retVal);
164		return retVal;
165	} //}}}
166
167	//{{{ getIgnoreCase() method
168	/**
169	 * Returns true if the keyword map is set to be case insensitive,
170	 * false otherwise.
171	 */
172	public boolean getIgnoreCase()
173	{
174		return ignoreCase;
175	} //}}}
176
177	//{{{ setIgnoreCase() method
178	/**
179	 * Sets if the keyword map should be case insensitive.
180	 * @param ignoreCase True if the keyword map should be case
181	 * insensitive, false otherwise
182	 */
183	public void setIgnoreCase(boolean ignoreCase)
184	{
185		this.ignoreCase = ignoreCase;
186	} //}}}
187
188	//{{{ add() method
189	/**
190	 * Adds the content of another keyword map to this one.
191	 * @since jEdit 4.2pre3
192	 */
193	public void add(KeywordMap map)
194	{
195		for(int i = 0; i < map.map.length; i++)
196		{
197			Keyword k = map.map[i];
198			while(k != null)
199			{
200				add(k.keyword,k.id);
201				k = k.next;
202			}
203		}
204	} //}}}
205
206	//{{{ Private members
207
208	//{{{ Instance variables
209	private int mapLength;
210	private Keyword[] map;
211	private boolean ignoreCase;
212	private StringBuffer noWordSep;
213	private String noWordSepStr;
214	//}}}
215
216	//{{{ getStringMapKey() method
217	private int getStringMapKey(String s)
218	{
219		return (Character.toUpperCase(s.charAt(0)) +
220				Character.toUpperCase(s.charAt(s.length()-1)))
221				% mapLength;
222	} //}}}
223
224	//{{{ getStringMapKey() method
225	private int getStringMapKey(char[] s)
226	{
227		return (Character.toUpperCase(s[0]) +
228				Character.toUpperCase(s[s.length-1]))
229				% mapLength;
230	} //}}}
231
232	//{{{ getSegmentMapKey() method
233	protected int getSegmentMapKey(Segment s, int off, int len)
234	{
235		return (Character.toUpperCase(s.array[off]) +
236				Character.toUpperCase(s.array[off + len - 1]))
237				% mapLength;
238	} //}}}
239
240	//}}}
241
242	//{{{ Keyword class
243	class Keyword
244	{
245		public Keyword(char[] keyword, byte id, Keyword next)
246		{
247			this.keyword = keyword;
248			this.id = id;
249			this.next = next;
250		}
251
252		public char[] keyword;
253		public byte id;
254		public Keyword next;
255	} //}}}
256}