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