001: package net.xoetrope.builder.editor.syntaxhighlight;
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.22 2005/01/05 17:20:48 luano 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: Keyword k = map[getSegmentMapKey(text, offset, length)];
057: while (k != null) {
058: if (length != k.keyword.length) {
059: k = k.next;
060: continue;
061: }
062: if (SyntaxUtilities.regionMatches(ignoreCase, text, offset,
063: k.keyword))
064: return k.id;
065: k = k.next;
066: }
067: return Token.NULL;
068: }
069:
070: /**
071: * Adds a key-value mapping.
072: * @param keyword The key
073: * @param id The value
074: */
075: public void add(String keyword, byte id) {
076: int key = getStringMapKey(keyword);
077: map[key] = new Keyword(keyword.toCharArray(), id, map[key]);
078: }
079:
080: /**
081: * Returns true if the keyword map is set to be case insensitive,
082: * false otherwise.
083: */
084: public boolean getIgnoreCase() {
085: return ignoreCase;
086: }
087:
088: /**
089: * Sets if the keyword map should be case insensitive.
090: * @param ignoreCase True if the keyword map should be case
091: * insensitive, false otherwise
092: */
093: public void setIgnoreCase(boolean ignoreCase) {
094: this .ignoreCase = ignoreCase;
095: }
096:
097: // protected members
098: protected int mapLength;
099:
100: protected int getStringMapKey(String s) {
101: return (Character.toUpperCase(s.charAt(0)) + Character
102: .toUpperCase(s.charAt(s.length() - 1)))
103: % mapLength;
104: }
105:
106: protected int getSegmentMapKey(Segment s, int off, int len) {
107: return (Character.toUpperCase(s.array[off]) + Character
108: .toUpperCase(s.array[off + len - 1]))
109: % mapLength;
110: }
111:
112: // private members
113: class Keyword {
114: public Keyword(char[] keyword, byte id, Keyword next) {
115: this .keyword = keyword;
116: this .id = id;
117: this .next = next;
118: }
119:
120: public char[] keyword;
121: public byte id;
122: public Keyword next;
123: }
124:
125: private Keyword[] map;
126: private boolean ignoreCase;
127: }
|