001: /*
002: * KeywordMap.java - Fast keyword->id map
003: * Copyright (C) 1998, 1999 Slava Pestov
004: * Copyright (C) 1999 Mike Dillon
005: *
006: * You may use and modify this package for any purpose. Redistribution is
007: * permitted, in both source and binary form, provided that this notice
008: * remains intact in all source distributions of this package.
009: */
010:
011: package org.syntax.jedit;
012:
013: import org.syntax.jedit.tokenmarker.*;
014: import javax.swing.text.Segment;
015:
016: /**
017: * A <code>KeywordMap</code> is similar to a hashtable in that it maps keys
018: * to values. However, the `keys' are Swing segments. This allows lookups of
019: * text substrings without the overhead of creating a new string object.
020: * <p>
021: * This class is used by <code>CTokenMarker</code> to map keywords to ids.
022: *
023: * @author Slava Pestov, Mike Dillon
024: * @version $Id: KeywordMap.java 3074 2004-11-08 04:24:58Z bquig $
025: */
026: public class KeywordMap {
027: /**
028: * Creates a new <code>KeywordMap</code>.
029: * @param ignoreCase True if keys are case insensitive
030: */
031: public KeywordMap(boolean ignoreCase) {
032: this (ignoreCase, 52);
033: this .ignoreCase = ignoreCase;
034: }
035:
036: /**
037: * Creates a new <code>KeywordMap</code>.
038: * @param ignoreCase True if the keys are case insensitive
039: * @param mapLength The number of `buckets' to create.
040: * A value of 52 will give good performance for most maps.
041: */
042: public KeywordMap(boolean ignoreCase, int mapLength) {
043: this .mapLength = mapLength;
044: this .ignoreCase = ignoreCase;
045: map = new Keyword[mapLength];
046: }
047:
048: /**
049: * Looks up a key.
050: * @param text The text segment
051: * @param offset The offset of the substring within the text segment
052: * @param length The length of the substring
053: */
054: public byte lookup(Segment text, int offset, int length) {
055: if (length == 0)
056: return Token.NULL;
057: Keyword k = map[getSegmentMapKey(text, offset, length)];
058: while (k != null) {
059: if (length != k.keyword.length) {
060: k = k.next;
061: continue;
062: }
063: if (SyntaxUtilities.regionMatches(ignoreCase, text, offset,
064: k.keyword))
065: return k.id;
066: k = k.next;
067: }
068: return Token.NULL;
069: }
070:
071: /**
072: * Adds a key-value mapping.
073: * @param keyword The key
074: * @Param id The value
075: */
076: public void add(String keyword, byte id) {
077: int key = getStringMapKey(keyword);
078: map[key] = new Keyword(keyword.toCharArray(), id, map[key]);
079: }
080:
081: /**
082: * Returns true if the keyword map is set to be case insensitive,
083: * false otherwise.
084: */
085: public boolean getIgnoreCase() {
086: return ignoreCase;
087: }
088:
089: /**
090: * Sets if the keyword map should be case insensitive.
091: * @param ignoreCase True if the keyword map should be case
092: * insensitive, false otherwise
093: */
094: public void setIgnoreCase(boolean ignoreCase) {
095: this .ignoreCase = ignoreCase;
096: }
097:
098: // protected members
099: protected int mapLength;
100:
101: protected int getStringMapKey(String s) {
102: return (Character.toUpperCase(s.charAt(0)) + Character
103: .toUpperCase(s.charAt(s.length() - 1)))
104: % mapLength;
105: }
106:
107: protected int getSegmentMapKey(Segment s, int off, int len) {
108: return (Character.toUpperCase(s.array[off]) + Character
109: .toUpperCase(s.array[off + len - 1]))
110: % mapLength;
111: }
112:
113: // private members
114: class Keyword {
115: public Keyword(char[] keyword, byte id, Keyword next) {
116: this .keyword = keyword;
117: this .id = id;
118: this .next = next;
119: }
120:
121: public char[] keyword;
122: public byte id;
123: public Keyword next;
124: }
125:
126: private Keyword[] map;
127: private boolean ignoreCase;
128: }
|