Back to index

im-sdk  12.3.91
LWESyntax.java
Go to the documentation of this file.
00001 /*
00002 Copyright 1990-2001 Sun Microsystems, Inc. All Rights Reserved.
00003 
00004 Permission is hereby granted, free of charge, to any person obtaining a
00005 copy of this software and associated documentation files (the
00006 "Software"), to deal in the Software without restriction, including
00007 without limitation the rights to use, copy, modify, merge, publish,
00008 distribute, sublicense, and/or sell copies of the Software, and to
00009 permit persons to whom the Software is furnished to do so, subject to
00010 the following conditions: The above copyright notice and this
00011 permission notice shall be included in all copies or substantial
00012 portions of the Software.
00013 
00014 
00015 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
00016 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00017 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
00018 IN NO EVENT SHALL THE OPEN GROUP OR SUN MICROSYSTEMS, INC. BE LIABLE
00019 FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
00020 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
00021 THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE EVEN IF
00022 ADVISED IN ADVANCE OF THE POSSIBILITY OF SUCH DAMAGES.
00023 
00024 
00025 Except as contained in this notice, the names of The Open Group and/or
00026 Sun Microsystems, Inc. shall not be used in advertising or otherwise to
00027 promote the sale, use or other dealings in this Software without prior
00028 written authorization from The Open Group and/or Sun Microsystems,
00029 Inc., as applicable.
00030 
00031 
00032 X Window System is a trademark of The Open Group
00033 
00034 OSF/1, OSF/Motif and Motif are registered trademarks, and OSF, the OSF
00035 logo, LBX, X Window System, and Xinerama are trademarks of the Open
00036 Group. All other trademarks and registered trademarks mentioned herein
00037 are the property of their respective owners. No right, title or
00038 interest in or to any trademark, service mark, logo or trade name of
00039 Sun Microsystems, Inc. or its licensors is granted.
00040 
00041 */
00042 
00043 
00044 package sun.awt.im.iiimp;
00045 
00046 import java.io.InputStreamReader;
00047 import java.io.BufferedReader;
00048 import java.io.StringReader;
00049 import java.util.StringTokenizer;
00050 import java.util.Hashtable;
00051 import java.util.Vector;
00052 import java.text.AttributedString;
00053 import java.text.AttributedCharacterIterator;
00054 import java.awt.AWTEvent;
00055 import java.awt.event.KeyEvent;
00056 import java.awt.event.InputEvent;
00057 import java.awt.font.TextAttribute;
00058 import java.awt.im.InputMethodHighlight;
00059 import com.sun.iiim.*;
00060 
00061 public class LWESyntax implements Cloneable {
00062 
00066     static final String COMMENT =         "#";
00067     static final String DEFMODE =         "defmode";
00068     static final String INITIALMODE =            "initialmode";
00069     static final String MODE =                   "mode";
00070     static final String STATUS_KEY =             "_STATUS_";
00071     static final String FALLTHROUGH =            "fallthrough";
00072     static final String FALLTHROUGH_KEY =        "_FALLTHROUGH_";
00073 
00077     static final String MODE_SWITCH       = "goto";
00078     static final String CONVERT           = "convert";
00079     static final String BACKSPACE  = "backspace";
00080     static final String CONVERT_S  = "convert-s";
00081     static final String UNCONVERT  = "unconvert";
00082     static final String NEXT              = "next";
00083     static final String NEXT_S            = "next-s";
00084     static final String PREVIOUS   = "previous";
00085     static final String PREVIOUS_S = "previous-s";
00086     static final String FORWARD           = "forward";
00087     static final String BACKWARD   = "backward";
00088     static final String MOVE_TOP   = "move-top";
00089     static final String MOVE_BOTTOM       = "move-bottom";
00090     static final String CLEAR             = "clear";
00091     static final String EXPAND            = "expand";
00092     static final String EXPAND_S   = "expand-s";
00093     static final String SHRINK            = "shrink";
00094     static final String SHRINK_S   = "shrink-s";
00095     static final String EXPAND_NOCONV     = "expand-noconv";
00096     static final String EXPAND_NOCONV_S = "expand-noconv-s";
00097     static final String SHRINK_NOCONV     = "shrink-noconv";
00098     static final String SHRINK_NOCONV_S   = "shrink-noconv-s";
00099     static final String FIX        = "fix";
00100     // .....
00101 
00105     public LWESyntax(String syntax)
00106                      throws InvalidSyntaxException {
00107        BufferedReader bReader = new BufferedReader(new StringReader(syntax));
00108        readRule(bReader);
00109     }
00110 
00111     public LWESyntax(InputStreamReader reader)
00112                      throws InvalidSyntaxException {
00113        BufferedReader bReader = new BufferedReader(reader);
00114        readRule(bReader);
00115     }
00116 
00117     private void readRule(BufferedReader bReader) throws InvalidSyntaxException {
00118        // Syntax is represented by kinput2's ccdef rule,
00119        // so it will come as character stream.
00120        // This constructer need to interpret ccdef syntax
00121        // and construct the Hashtable chain.
00122        modeTable = new Hashtable();
00123 
00124        String line;
00125        int lineNo = 0;
00126        try {
00127            while ((line = bReader.readLine()) != null) {
00128               parseRule(line);
00129               lineNo++;
00130            }
00131        } catch(Exception e) {
00132            throw new InvalidSyntaxException(lineNo);
00133        }
00134     }
00135 
00136     LWESyntax getCopy() {
00137        try {
00138            return (LWESyntax)clone();
00139        } catch(Exception e) {
00140            if (Manager.DEBUG) {
00141               e.printStackTrace();
00142            }
00143            return null;
00144        }
00145     }
00146     
00147     private Hashtable modeTable;
00148     private Hashtable firstTable;
00149     private Hashtable currentTable;
00150 
00151     private void parseRule(String line) throws InvalidSyntaxException {
00152        String rule = line.trim();
00153        
00154        if (rule.startsWith(COMMENT)) {
00155            // comment
00156            return;
00157        }
00158        
00159        StringTokenizer st = new StringTokenizer(rule);
00160        if (st.hasMoreTokens() == false) {
00161            // blank line
00162            return;
00163        }
00164        
00165        String firstToken = st.nextToken();
00166        
00167        if (firstToken.equals(DEFMODE)) {
00168            // mode kind declaration
00169            while (st.hasMoreTokens()) {
00170               String mode = st.nextToken();
00171               modeTable.put(mode, new Hashtable());
00172            }
00173            return;
00174        }
00175 
00176        if (firstToken.equals(INITIALMODE)) {
00177            // initial mode declaration
00178            if (!st.hasMoreTokens()) {
00179               throw new InvalidSyntaxException();
00180            }
00181            String mode = st.nextToken();
00182            firstTable = (Hashtable)modeTable.get(mode);
00183            if (firstTable == null) {
00184               throw new InvalidSyntaxException();
00185            }
00186            return;
00187        }
00188 
00189        if (firstToken.equals(MODE)) {
00190            // begin rule definition
00191            try {
00192               String mode = st.nextToken();
00193               currentTable = (Hashtable)modeTable.get(mode);
00194               if (currentTable == null) {
00195                   throw new InvalidSyntaxException();
00196               }
00197               String status = st.nextToken();
00198               currentTable.put(STATUS_KEY, status);
00199 
00200               // fallthrough handling
00201               if (st.hasMoreTokens()) {
00202                   String fallthrough = st.nextToken();
00203                   if (fallthrough.equals(FALLTHROUGH)) {
00204                      String target = st.nextToken();
00205                      Hashtable ftTable = (Hashtable)modeTable.get(target);
00206                      if (ftTable == null) {
00207                          throw new InvalidSyntaxException();
00208                      }
00209                      currentTable.put(FALLTHROUGH_KEY, ftTable);
00210                   }
00211               }
00212            } catch(Exception e) {
00213               throw new InvalidSyntaxException();
00214            }
00215            return;
00216        }
00217 
00218        if (firstToken.equals("endmode")) {
00219            return;
00220        }
00221 
00222        try {
00223            String secondToken = st.nextToken();
00224 
00225            Hashtable secondTable;
00226            if (currentTable.containsKey(secondToken)) {
00227               secondTable = (Hashtable)currentTable.get(secondToken);
00228            } else {
00229               secondTable = new Hashtable();
00230               currentTable.put(secondToken, secondTable);
00231            }
00232 
00233            ActionList actions = new ActionList();
00234 
00235            while(st.hasMoreTokens()) {
00236               actions.addAction(st.nextToken());
00237            }
00238 
00239            secondTable.put(firstToken, actions);
00240        } catch(Exception e) {
00241            throw new InvalidSyntaxException();
00242        }
00243     }   
00244 
00250     public IIIMEvent[] getEventList(IIIMEvent e) throws InvalidSyntaxException {
00251        // Get event and search mapping tables maintaining
00252        // Mode and Context, and returns events
00253        // and/or move to the next mode.
00254        AWTEvent aev = e.getAWTEvent(); 
00255        if (aev instanceof KeyEvent) {
00256            e.consume();
00257            KeyEvent kev = (KeyEvent)aev;
00258            int id = kev.getID();
00259            if (id == KeyEvent.KEY_RELEASED) {
00260               return null;
00261            }
00262            if (id == KeyEvent.KEY_TYPED && unprintable(kev.getKeyChar())) {
00263               return null;
00264            }
00265 
00266            if (id == KeyEvent.KEY_PRESSED && printable(kev.getKeyCode())) {
00267               if (kev.getModifiers() == 0) {
00268                   return null;
00269               }
00270            }
00271 
00272            /*
00273             * process only
00274             *    - typed event with printable keyChar
00275             *        ex: a, A, &, $, etc...
00276             *    - pressed event with unprintalbe keyCode
00277             *        ex: VK_TAB, VK_F1, VK_PAGE_DOWN etc...
00278             */
00279 
00280            String input = null;
00281            String rawInput = null;
00282 
00283            if (id == KeyEvent.KEY_TYPED) {
00284               rawInput = String.valueOf(kev.getKeyChar());
00285               input = "'" + rawInput + "'";
00286            } else {
00287               // KEY_PRESSED event which does not produce
00288               // following KEY_TYPED event
00289               rawInput = input =
00290                   makeKeyString(KeyEvent.getKeyText(kev.getKeyCode()),
00291                               kev.getModifiers());
00292            }
00293 
00294            Hashtable secondTable = (Hashtable)firstTable.get(input);
00295 
00296            if (secondTable == null) {
00297               // check fallthrough
00298               Hashtable ftTable = (Hashtable)firstTable.get(FALLTHROUGH_KEY);
00299               if (ftTable != null) { 
00300                   secondTable = (Hashtable)ftTable.get(input);
00301                   if (secondTable != null) {
00302                      // check fallthroh table
00303                      ActionList result =
00304                          searchResult(secondTable, context);
00305                      if (result != null) {
00306                          return processActions(result, context);
00307                      }
00308                   }
00309               }
00310 
00311               if (!Character.isISOControl(kev.getKeyChar())) {
00312                   if (input.startsWith("'")) { 
00313                      context.append(rawInput);
00314                   }
00315               }
00316                   
00317               return makePreeditEvent(context.toString());
00318            }
00319 
00320            // Context maching
00321            ActionList result = searchResult(secondTable, context);
00322            if (result == null) {
00323               // check fallthrough
00324               Hashtable ftTable = (Hashtable)firstTable.get(FALLTHROUGH_KEY);
00325               if (ftTable != null) {
00326                   secondTable = (Hashtable)ftTable.get(input);
00327                   if (secondTable != null) {
00328                      // check fallthrough table
00329                      result = searchResult(secondTable, context);
00330                      if (result != null) {
00331                          return processActions(result, context);
00332                      }
00333                   }
00334               }
00335 
00336               if (!Character.isISOControl(kev.getKeyChar())) {
00337                   if (input.startsWith("'")) {
00338                      context.append(rawInput);
00339                   }
00340               }
00341 
00342               return makePreeditEvent(context.toString());
00343            }
00344            return processActions(result, context);
00345        }
00346        return null;
00347     }
00348 
00349     private ActionList searchResult(Hashtable table, StringBuffer keyString) {
00350        String key = keyString.toString();
00351        /*
00352         * if keyString is "XYZ", and result is null,
00353         * then try "YZ", "Z", "" respectively.
00354         */
00355        while(true) {
00356            String skey = "\"" + key + "\"";
00357            Object value = table.get(skey);
00358            if (value != null) {
00359               int len = keyString.length();
00360               int keyLen = key.length();
00361               if (len > 0) {
00362                   keyString.delete(len - keyLen, len);
00363               }
00364               return (ActionList)value;
00365            }
00366            if (key.length() < 1) {
00367               return null;
00368            }
00369            key = key.substring(1, key.length());
00370        }
00371     }
00372 
00373     private IIIMEvent[] processActions(ActionList result, StringBuffer context)
00374        throws InvalidSyntaxException {
00375            
00376        String[] actionArray = result.getActions();
00377        Vector retVector = new Vector();
00378        
00379        for (int i = 0; i < actionArray.length; i++) {
00380            String action = actionArray[i];
00381            if (action.equals("")) {
00382               continue;
00383            }
00384            if (action.startsWith("\"")) {
00385               String raw = action.substring(1, action.length() - 1);
00386               context.append(raw);
00387               IIIMEvent[] imea = makePreeditEvent(context.toString());
00388               if (imea != null)
00389                   retVector.add(imea[0]);
00390            } else {
00391               // check mode switch
00392               if (action.equals(MODE_SWITCH)) {
00393                   if (actionArray.length <= i + 1) {
00394                      throw new InvalidSyntaxException(0);
00395                   }
00396                   String newMode = actionArray[++i];
00397                   firstTable = (Hashtable)modeTable.get(newMode);
00398                   if (firstTable == null) {
00399                      throw new InvalidSyntaxException(0);
00400                   }
00401                   retVector.add(new IIIMCommittedEvent
00402                               (context.toString()));
00403                   context.setLength(0);
00404               } else if (action.equals(CONVERT)) {
00405                   IIIMActionEvent e =
00406                      new IIIMActionEvent
00407                      (IIIMActionEvent.FORWARD_STRING,
00408                       new String[] {action, context.toString()});
00409 System.out.println(" make a event = " + action);
00410                   retVector.add(e);
00411                   context.setLength(0);
00412               } else if (action.equals(BACKSPACE)){
00413                   if (context.length() > 0) {
00414                      StringBuffer newSB =  context.delete(context.length() - 1,
00415                                                       context.length());
00416                      IIIMEvent[] ie = makePreeditEvent(newSB.toString());
00417                      retVector.add(ie[0]);
00418                   }
00419               } else {
00420                   debug(" action = " + action +
00421                                    " is not supported yet.");
00422               }
00423            }
00424        }
00425        // context.setLength(0);
00426        Object[] oa = retVector.toArray();
00427        IIIMEvent[] iiimea = new IIIMEvent[oa.length];
00428        for (int i = 0; i < oa.length; i++) {
00429            iiimea[i] = (IIIMEvent)oa[i];
00430        }
00431        return iiimea;
00432     }
00433 
00434     private void debug(String str) {
00435        if (Manager.DEBUG) {
00436            System.err.println(str);
00437        }
00438     }
00439 
00440     String getCurrentContext() {
00441        return context.toString();
00442     }
00443 
00444     private IIIMEvent[] makePreeditEvent(String str) {
00445        AttributedString attrstr = new AttributedString(str);
00446        if (str.length() > 0) {
00447            attrstr.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,
00448                              InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT,
00449                              0, str.length());
00450        }
00451        AttributedCharacterIterator iterator = attrstr.getIterator();
00452        IIIMPreeditEvent pe =
00453            new IIIMPreeditEvent(IIIMPreeditEvent.DRAW, iterator, 0);
00454        IIIMEvent[] iiimea = new IIIMEvent[1];
00455        iiimea[0] = pe;
00456        return iiimea;
00457     }
00458 
00459     private static int[] masks = {
00460        InputEvent.SHIFT_MASK,
00461        InputEvent.CTRL_MASK,
00462        InputEvent.META_MASK,
00463        // below mask will be added if ccdef uses these
00464        // 
00465        // InputEvent.ALT_MASK,
00466        // InputEvent.ALT_GRAPH_MASK,
00467        // InputEvent.BUTTON1_MASK,
00468        // InputEvent.BUTTON2_MASK,
00469        // InputEvent.BUTTON3_MASK,
00470     };
00471 
00472     private static String[] maskStrings = {
00473        "shift",
00474        "control",
00475        "mod1",
00476     };
00477        
00478     private static String makeKeyString(String key, int mod) {
00479        String modString = "";
00480        for (int i = 0; i < masks.length; i++) {
00481            if ((masks[i] & mod) != 0) { 
00482               modString += (maskStrings[i] + "-");
00483            }
00484        }
00485        String ret = modString;
00486        if (key.equals("Backspace")) {
00487            ret = "'^H'";
00488        } else {
00489            if (modString.equals("control-") && key.length() == 1) {
00490               ret = "'^" + key + "'";
00491            } else {
00492               ret += key;
00493            }
00494        }
00495        return ret;
00496     }
00497 
00498     private StringBuffer context = new StringBuffer();
00499 
00500     private static boolean printable(int keyCode) {
00501        switch(keyCode) {
00502          case KeyEvent.VK_0:
00503          case KeyEvent.VK_1:
00504          case KeyEvent.VK_2:
00505          case KeyEvent.VK_3:
00506          case KeyEvent.VK_4:
00507          case KeyEvent.VK_5:
00508          case KeyEvent.VK_6:
00509          case KeyEvent.VK_7:
00510          case KeyEvent.VK_8:
00511          case KeyEvent.VK_9:
00512          case KeyEvent.VK_A:
00513          case KeyEvent.VK_B:
00514          case KeyEvent.VK_C:
00515          case KeyEvent.VK_D:
00516          case KeyEvent.VK_E:
00517          case KeyEvent.VK_F:
00518          case KeyEvent.VK_G:
00519          case KeyEvent.VK_H:
00520          case KeyEvent.VK_I:
00521          case KeyEvent.VK_J:
00522          case KeyEvent.VK_K:
00523          case KeyEvent.VK_L:
00524          case KeyEvent.VK_M:
00525          case KeyEvent.VK_N:
00526          case KeyEvent.VK_O:
00527          case KeyEvent.VK_P:
00528          case KeyEvent.VK_Q:
00529          case KeyEvent.VK_R:
00530          case KeyEvent.VK_S:
00531          case KeyEvent.VK_T:
00532          case KeyEvent.VK_U:
00533          case KeyEvent.VK_V:
00534          case KeyEvent.VK_W:
00535          case KeyEvent.VK_X:
00536          case KeyEvent.VK_Y:
00537          case KeyEvent.VK_Z:
00538          case KeyEvent.VK_SPACE:
00539          case KeyEvent.VK_COMMA:
00540          case KeyEvent.VK_MINUS:
00541          case KeyEvent.VK_PERIOD:
00542          case KeyEvent.VK_SLASH:
00543          case KeyEvent.VK_SEMICOLON:
00544          case KeyEvent.VK_EQUALS:
00545          case KeyEvent.VK_OPEN_BRACKET:
00546          case KeyEvent.VK_BACK_SLASH:
00547          case KeyEvent.VK_CLOSE_BRACKET:
00548          case KeyEvent.VK_NUMPAD0:
00549          case KeyEvent.VK_NUMPAD1:
00550          case KeyEvent.VK_NUMPAD2:
00551          case KeyEvent.VK_NUMPAD3:
00552          case KeyEvent.VK_NUMPAD4:
00553          case KeyEvent.VK_NUMPAD5:
00554          case KeyEvent.VK_NUMPAD6:
00555          case KeyEvent.VK_NUMPAD7:
00556          case KeyEvent.VK_NUMPAD8:
00557          case KeyEvent.VK_NUMPAD9:
00558          case KeyEvent.VK_MULTIPLY:
00559          case KeyEvent.VK_ADD:
00560          case KeyEvent.VK_SEPARATER:
00561          case KeyEvent.VK_SUBTRACT:
00562          case KeyEvent.VK_DECIMAL:
00563          case KeyEvent.VK_DIVIDE:
00564          case KeyEvent.VK_BACK_QUOTE:
00565          case KeyEvent.VK_QUOTE:
00566          case KeyEvent.VK_DEAD_GRAVE:
00567          case KeyEvent.VK_DEAD_ACUTE:
00568          case KeyEvent.VK_DEAD_CIRCUMFLEX:
00569          case KeyEvent.VK_DEAD_TILDE:
00570          case KeyEvent.VK_DEAD_MACRON:
00571          case KeyEvent.VK_DEAD_BREVE:
00572          case KeyEvent.VK_DEAD_ABOVEDOT:
00573          case KeyEvent.VK_DEAD_DIAERESIS:
00574          case KeyEvent.VK_DEAD_ABOVERING:
00575          case KeyEvent.VK_DEAD_DOUBLEACUTE:
00576          case KeyEvent.VK_DEAD_CARON:
00577          case KeyEvent.VK_DEAD_CEDILLA:
00578          case KeyEvent.VK_DEAD_OGONEK:
00579          case KeyEvent.VK_DEAD_IOTA:
00580          case KeyEvent.VK_DEAD_VOICED_SOUND:
00581          case KeyEvent.VK_DEAD_SEMIVOICED_SOUND:
00582          case KeyEvent.VK_AMPERSAND:
00583          case KeyEvent.VK_ASTERISK:
00584          case KeyEvent.VK_QUOTEDBL:
00585          case KeyEvent.VK_LESS:
00586          case KeyEvent.VK_GREATER:
00587          case KeyEvent.VK_BRACELEFT:
00588          case KeyEvent.VK_BRACERIGHT:
00589          case KeyEvent.VK_AT:
00590          case KeyEvent.VK_COLON:
00591          case KeyEvent.VK_CIRCUMFLEX:
00592          case KeyEvent.VK_DOLLAR:
00593          case KeyEvent.VK_EURO_SIGN:
00594          case KeyEvent.VK_EXCLAMATION_MARK:
00595          case KeyEvent.VK_INVERTED_EXCLAMATION_MARK:
00596          case KeyEvent.VK_LEFT_PARENTHESIS:
00597          case KeyEvent.VK_NUMBER_SIGN:
00598          case KeyEvent.VK_PLUS:
00599          case KeyEvent.VK_RIGHT_PARENTHESIS:
00600          case KeyEvent.VK_UNDERSCORE:
00601            return true;
00602        }
00603        return false;
00604     }
00605 
00606     private static boolean unprintable(char c) {
00607        if (c >= '\u0000' && c <= '\u0019' || c == '\u007f') {
00608            return true;
00609        }
00610        return false;
00611     }
00612 
00613     class ActionList {
00614 
00615        Vector vector = new Vector();
00616 
00617        void addAction(String action) {
00618            vector.add(action);
00619        }
00620 
00621        int size() {
00622            return vector.size();
00623        }
00624 
00625        String[] getActions() {
00626            Object[] oa = vector.toArray();
00627            String[] sa = new String[oa.length];
00628            for (int i = 0; i < sa.length; i++) {
00629               sa[i] = (String)oa[i];
00630            }
00631            return sa;
00632        }
00633     }
00634 }