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