InputManager.java

Go to the documentation of this file.
00001 // (C) 2003 by Dominique Unruh. GPL 00002 00003 import java.util.*; 00004 import java.io.*; 00005 import java.net.*; 00006 import java.util.zip.*; 00007 import java.lang.reflect.*; 00008 00009 public class InputManager { 00010 public static final int SOURCE_KEYBOARD = 1; 00011 public static final int SOURCE_GUI = 2; 00012 public static final int SOURCE_CLIPBOARD = 3; 00013 public static final int SOURCE_FILE = 4; 00014 00015 public static final int KEY_BACKSPACE = -1; 00016 00017 public static final String INPUT_BREAK = "\uFFFF{{BREAK}}"; 00018 public static final String INPUT_BACKSPACE = "\uFFFF{{BACKSPACE}}"; 00019 00020 public static final int DOCUMENTATION_COLS = 3; 00021 public static final int CONTINUATIONS_LENGTH = 300; 00022 00023 public static class StringReplacement extends InputReplacement { 00024 String string; 00025 String tempReplacement; 00026 String replacement; 00027 String cont[]; 00028 public StringReplacement(String _string, String _repl) { 00029 string = _string; tempReplacement = replacement = _repl; }; 00030 public StringReplacement(String _string, String _tr, String _fr) { 00031 string = _string; tempReplacement = _tr; replacement = _fr; }; 00032 public String getReplacement(String input) { return tempReplacement; }; 00033 public String getFinalReplacement(String input) { 00034 return replacement; }; 00035 public String getInputInfo() { return string; }; 00036 public String[] isContinuationOf(String input) { 00037 if (string.startsWith(input) && !string.equals(input)) { 00038 if (cont==null) cont = new String[]{string}; 00039 return cont; 00040 } 00041 return null; }; 00042 public String replacesPrefixOf(String input) { 00043 if (input.startsWith(string)) 00044 return string; 00045 return null; 00046 } 00047 } 00048 public class SwitchReplacement extends StringReplacement { 00049 public static final int ON = 1; 00050 public static final int OFF = 2; 00051 int state; 00052 int targetGroup; 00053 public SwitchReplacement(String s, int target, int st) { 00054 super(s,null); 00055 targetGroup = target; 00056 state = st; } 00057 public String getReplacement(String input) { return string; }; 00058 public String getFinalReplacement(String input) { 00059 Group g = getGroup(targetGroup); 00060 try { 00061 switch (state) { 00062 case ON: g.activate(); break; 00063 case OFF: g.deactivate(); break; 00064 default: return "!!! internal error, group state "+ 00065 state+" !!!"; 00066 } 00067 } catch (Exception e) { 00068 e.printStackTrace(); 00069 imContext.showMessage 00070 ("Could not switch state of input method "+ 00071 g.getFullName()+":\n"+e); 00072 } 00073 return ""; 00074 } 00075 public String getReplacementInfo() { 00076 String name = getGroup(targetGroup).getScreenName(); 00077 switch (state) { 00078 case ON: return "enable "+name; 00079 case OFF: return "disable "+name; 00080 default: throw new RuntimeException(); 00081 } 00082 } 00083 } 00084 00085 static class Group implements Documentation { 00086 private String name = null; 00087 private int index; 00088 private String description = "no description available"; 00089 private URL source = null; 00090 private boolean hidden = false; 00091 private String screenName = null; 00092 00093 private boolean active = false; 00094 private URL url = null; 00095 private InputManager inputManager = null; 00096 00097 boolean isHidden() { return hidden; }; 00098 void setHidden(boolean h) { hidden = h; }; 00099 00100 String getName() { return name; }; 00101 String getScreenName() { 00102 if (screenName!=null) return screenName; 00103 return name; }; 00104 String getFullName() { 00105 if (screenName!=null && !screenName.equals(name)) 00106 return screenName+" ("+name+")"; 00107 return name; } 00108 public String getFullDocuName() { 00109 return "input method "+getFullName(); }; 00110 void setScreenName(String sn) { screenName = sn; }; 00111 void load() throws IOException { 00112 URL _url = url; 00113 try { 00114 if (url!=null) { 00115 inputManager.imContext.showMessage 00116 ("Autoloading input method "+getFullName()); 00117 url = null; 00118 inputManager.loadConfig(_url); 00119 _url = null; 00120 } 00121 } finally { 00122 url = _url; 00123 } 00124 } 00125 public void setSourceURL(URL s) { source = s; }; 00126 public URL getSourceURL() { return source; }; 00127 public void activate() throws IOException { 00128 if (!hidden && inputManager.singleInputMethod) 00129 inputManager.deselectNonHiddenGroups(); 00130 load(); 00131 active = true; 00132 inputManager.lastActivatedGroup = this; 00133 if (!hidden) 00134 inputManager.imContext.groupStateChanged(name,active); 00135 } 00136 public Group(InputManager im, String n, int i) { 00137 im.newGroups = true; 00138 inputManager = im; name = n; index = i; }; 00139 public void deactivate() { 00140 active = false; 00141 if (!hidden) 00142 inputManager.imContext.groupStateChanged(name,active); 00143 }; 00144 public boolean isActive() { return active; }; 00145 public void autoloadURL(URL _url) { url = _url; }; 00146 public void setDescription(String descr) { description = descr; }; 00147 public String getDescription() throws IOException { 00148 load(); return description; }; 00149 public String getDocumentation() throws IOException { 00150 StringBuffer docu = new StringBuffer(); 00151 docu.append("<html><head>\n"); 00152 docu.append("<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n"); 00153 docu.append("<link rel=stylesheet type=text/css href=international_input.css>\n"); 00154 String title = "Input method “"+getScreenName()+ 00155 "” - Documentation"; 00156 docu.append("<title>"); 00157 docu.append(HTMLEscapeSB(title,true)); 00158 docu.append("</title>\n</head><body><h1>"); 00159 docu.append(title); 00160 docu.append("</h1>\n"); 00161 docu.append(getDescription()); 00162 URL source = getSourceURL(); 00163 if (source!=null) { 00164 docu.append("<p><b><a href=\""); docu.append(source); 00165 docu.append("\" type=\"text/plain\" charset=\"utf-8\">[Source]</a></b>"); 00166 } 00167 docu.append("<p><h3>All mappings:</h3><p><center><table cellpadding=5 border=1 rules=rows frame=box><tr>\n"); 00168 int rplsize = inputManager.replacements.size(); 00169 int count = 0; 00170 for (int i=0; i<DOCUMENTATION_COLS; i++) { 00171 if (i>0) docu.append("<th>&nbsp;.&nbsp;.&nbsp;.&nbsp;.&nbsp;.&nbsp;.&nbsp;.&nbsp;.&nbsp;</th>"); 00172 docu.append("<th>Input</th><th>Output</th>"); 00173 } 00174 for (int i=0; i<rplsize; i++) { 00175 InputReplacement rpl = 00176 (InputReplacement)inputManager.replacements.elementAt(i); 00177 if (rpl.group!=index) continue; 00178 if (count%DOCUMENTATION_COLS==0) docu.append("</tr>\n<tr>"); 00179 else docu.append("</td><td>"); 00180 docu.append("<td><tt>"); 00181 docu.append(HTMLEscapeSB(rpl.getInputInfo(),true)); 00182 docu.append("</tt></td><td class=replacement>"); 00183 docu.append(HTMLEscapeSB(prefixNSM(rpl.getReplacementInfo()),true)); 00184 docu.append("</td>"); 00185 count++; 00186 } 00187 docu.append("</tr></table></center>\n"); 00188 docu.append("</body></html>\n"); 00189 return docu.toString(); 00190 } 00191 } 00192 public static class WebPage implements Documentation { 00193 public static class Token { 00194 public static final int SOURCE_WORD = 1; 00195 public static final int SOURCE_CONST = 2; 00196 00197 public static final int MODE_NORMAL = 1; 00198 public static final int MODE_EXAMPLE = 2; 00199 public static final int MODE_HIGHLIGHT = 3; 00200 00201 public static final int ENCODE_URL = 1; 00202 public static final int ENCODE_DEC = 2; 00203 public static final int ENCODE_HEX = 3; 00204 String name; 00205 int source; 00206 int encoding = ENCODE_URL; 00207 String string; // For SOURCE_CONST 00208 String charset = "utf-8"; 00209 public Token(String _name) { 00210 name = _name; source = SOURCE_WORD; }; 00211 public void setConst(String _str) { 00212 source = SOURCE_CONST; string = _str; }; 00213 public void setCharset(String cs) { 00214 charset = cs; }; 00215 public void setEncoding(int mode) { 00216 encoding = mode; }; 00217 public String getName() { return name; }; 00218 private String getRawReplacement(String word, int mode) { 00219 if (mode==MODE_HIGHLIGHT) 00220 return "<span class=webpage-url-token>"+ 00221 HTMLEscapeSB(name,false)+"</span>"; 00222 switch (source) { 00223 case SOURCE_CONST: return string; 00224 case SOURCE_WORD: return word; 00225 default: throw new RuntimeException 00226 ("Internal error: Unknown source "+source); 00227 } 00228 }; 00229 public String getReplacement(String word, int mode) { 00230 String repl = getRawReplacement(word,mode); 00231 switch (mode) { 00232 case MODE_NORMAL: 00233 case MODE_EXAMPLE: 00234 switch (encoding) { 00235 case ENCODE_URL: 00236 return urlEncode(repl,charset).toString(); 00237 case ENCODE_HEX: 00238 case ENCODE_DEC: 00239 if (repl.length()==0) return ""; 00240 return Integer.toString(repl.charAt(0),encoding==ENCODE_HEX?16:10); 00241 default: 00242 throw new RuntimeException 00243 ("Internal error: Unknown encoding "+source); 00244 } 00245 case MODE_HIGHLIGHT: 00246 return repl; 00247 default: throw new RuntimeException 00248 ("Internal error: Unknown mode "+source); 00249 } 00250 }; 00251 public String getDocumentationPart() { 00252 StringBuffer docu = new StringBuffer(); 00253 docu.append("<span class=webpage-token>"); 00254 docu.append(HTMLEscapeSB(name,false)); 00255 docu.append("</span> "); 00256 // describe source 00257 switch (source) { 00258 case SOURCE_CONST: 00259 docu.append("has constant value \"<span class=webpage-token-value>"); 00260 docu.append(HTMLEscapeSB(string,true)); 00261 docu.append("</span>\""); 00262 break; 00263 case SOURCE_WORD: 00264 docu.append("is replaced by the selection"); 00265 break; 00266 default: 00267 docu.append("<b>has unknown source "+source+"!!!!!"); 00268 break; 00269 } 00270 // describe charset 00271 switch (encoding) { 00272 case ENCODE_URL: 00273 docu.append(", and is ").append(HTMLEscapeSB(charset,false)); 00274 docu.append(" encoded"); 00275 break; 00276 case ENCODE_DEC: 00277 docu.append(", the first character's code position in decimal is inserted in the URL"); 00278 break; 00279 case ENCODE_HEX: 00280 docu.append(", the first character's code position in hex is inserted in the URL"); 00281 break; 00282 } 00283 docu.append("."); 00284 return docu.toString(); 00285 } 00286 }; 00287 private String name; 00288 private String shortName; 00289 private String url; 00290 private boolean needsTopFrame = false; 00291 private LinkedList tokens = new LinkedList(); 00292 private String description = "no description available"; 00293 private URL source = null; 00294 public WebPage(String _name, String _url) { 00295 name = _name; url = _url; }; 00296 public String getName() { return name; }; 00297 public String getFullDocuName() { 00298 return "web page \""+getName()+"\""; }; 00299 public void addToken(Token token) { 00300 tokens.add(token); }; 00301 public String getShortName() { 00302 if (shortName!=null) return shortName; 00303 return name; }; 00304 public void setShortName(String sn) { shortName = sn; }; 00305 public boolean getNeedsTopFrame() { return needsTopFrame; }; 00306 public void setNeedsTopFrame(boolean tf) { 00307 needsTopFrame = tf; }; 00308 public String getToolTipText() { return url; }; 00309 public URL run(String word) throws MalformedURLException { 00310 return new URL(replaceTokens(word,Token.MODE_NORMAL)); }; 00311 public String replaceTokens(String word, int mode) { 00312 StringBuffer u = new StringBuffer(url); 00313 int fromIndex = 0; 00314 while (true) { 00315 String u2 = new String(u); 00316 int pos = u2.length(); 00317 Token tok = null; 00318 for (ListIterator e = tokens.listIterator(); 00319 e.hasNext();) { 00320 Token thistok = (Token)e.next(); 00321 int thispos = u2.indexOf(thistok.getName(),fromIndex); 00322 if (thispos==-1) continue; 00323 if (pos>thispos) { pos = thispos; tok = thistok; }; 00324 } 00325 if (tok==null) break; 00326 String repl = tok.getReplacement(word,mode); 00327 u.replace(pos,pos+tok.getName().length(),repl); 00328 fromIndex = pos+repl.length(); 00329 } 00330 return u.toString(); 00331 } 00332 public String getDocumentation() { 00333 StringBuffer docu = new StringBuffer(); 00334 docu.append("<html><head>\n"); 00335 docu.append("<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n"); 00336 docu.append("<link rel=stylesheet type=text/css href=international_input.css>\n"); 00337 String title = "Web page “"+getName()+"” - Documentation"; 00338 docu.append("<title>"); 00339 docu.append(HTMLEscapeSB(title,true)); 00340 docu.append("</title>\n</head><body><h1>"); 00341 docu.append(title); 00342 docu.append("</h1>\n"); 00343 docu.append(getDescription()); 00344 URL source = getSourceURL(); 00345 if (source!=null) { 00346 docu.append("<p><b><a href=\""); docu.append(source); 00347 docu.append("\" type=\"text/plain\" charset=\"utf-8\">[Source]</a></b>\n"); 00348 } 00349 String htmlURL = replaceTokens("test",Token.MODE_HIGHLIGHT); 00350 String testURL = replaceTokens("test",Token.MODE_EXAMPLE); 00351 docu.append("<p>URL: <a class=webpage-url href=\""); 00352 docu.append(testURL).append("\">").append(htmlURL); 00353 docu.append("</a>\n"); 00354 docu.append("<p><ul>\n"); 00355 for (ListIterator e = tokens.listIterator(); 00356 e.hasNext();) { 00357 Token tok = (Token)e.next(); 00358 docu.append("<li>"); 00359 docu.append(tok.getDocumentationPart()); 00360 } 00361 docu.append("</ul>"); 00362 docu.append("</body></html>\n"); 00363 return docu.toString(); 00364 } 00365 public String getDescription() { 00366 return description; } 00367 public void setDescription(String d) { 00368 description = d; }; 00369 public void setSourceURL(URL s) { source = s; }; 00370 public URL getSourceURL() { return source; }; 00381 public boolean equals(Object a) { 00382 if (a==null) return false; 00383 if (a==this) return true; 00384 if (!(a instanceof WebPage)) return false; 00385 WebPage aa = (WebPage)a; 00386 if (!name.equals(aa.name)) return false; 00387 if (!url.equals(aa.url)) return false; 00388 return true; 00389 }; 00390 } 00391 00392 Vector replacements = new Vector(); 00393 Vector groups = new Vector(); 00394 Vector webPages = new Vector(); 00395 InputViewer viewer = null; 00396 String temporaryOutput = null; 00397 String temporaryInput = null; 00398 Group lastActivatedGroup = null; 00399 InternationalInputContext iiContext = null; 00400 InputManagerContext imContext = null; 00401 boolean newGroups = false; 00402 boolean singleInputMethod = true; 00403 00404 Group getGroup(int index) { 00405 return (Group)groups.get(index); }; 00406 Group getGroup(String name) { 00407 if (name==null) 00408 throw new IllegalArgumentException 00409 ("Group name is null"); 00410 for (Enumeration e = groups.elements(); 00411 e.hasMoreElements();) { 00412 Group g = (Group)e.nextElement(); 00413 if (g.name.equals(name)) return g; 00414 } 00415 Group g = new Group(this,name,groups.size()); 00416 groups.add(g); 00417 return g; 00418 } 00419 00420 public void activateGroup(String name) throws IOException { 00421 getGroup(name).activate(); 00422 } 00423 00424 public void deactivateGroup(String name) throws IOException { 00425 getGroup(name).deactivate(); 00426 } 00427 00428 public String getLastActivatedGroupName() { 00429 if (lastActivatedGroup==null) return null; 00430 return lastActivatedGroup.name; 00431 } 00432 00433 public void setViewer(InputViewer _viewer) { 00434 viewer = _viewer; }; 00435 00436 static String prefixNSM(String str) { 00437 if (str.length()==0) return str; 00438 char c = str.charAt(0); 00439 try { 00440 if (Normalizer.isCombining(c)) 00441 return "◌"+str; 00442 } catch (IOException e) { 00443 e.printStackTrace(); } 00444 return str; 00445 } 00446 00447 public static StringBuffer HTMLEscapeSB(String str, boolean br) { 00448 StringBuffer res = new StringBuffer(); 00449 int len = str.length(); 00450 for (int i=0; i<len; i++) { 00451 char c = str.charAt(i); 00452 switch (c) { 00453 case '&': res.append("&amp;"); break; 00454 case '<': res.append("&lt;"); break; 00455 case '>': res.append("&gt;"); break; 00456 case '\n': res.append(br?"<br>":"\n"); break; 00457 default: res.append(c); 00458 } 00459 } 00460 return res; 00461 } 00462 00463 public void inputChar(int source, char chr) { 00464 input(source,charToString(chr)); 00465 } 00466 00467 public void input(int source, String str) { 00468 //System.out.println("Input("+source+"): "+str); 00469 if (str.equals(INPUT_BREAK)) { // || str.equals("\007") 00470 viewer.removeTemporary(); 00471 while (makeOutput(true)); 00472 //if (temporaryOutput!=null) viewer.addFinal(temporaryOutput); 00473 if (temporaryOutput!=null) 00474 throw new RuntimeException 00475 ("Internal error: makeOutput(true) did not clear temporaryOutput"); 00476 if (temporaryInput!=null) 00477 throw new RuntimeException 00478 ("Internal error: makeOutput(true) did not clear temporaryInput"); 00479 } else if (str.equals(INPUT_BACKSPACE)) { 00480 //System.out.println("bs"); 00481 viewer.removeTemporary(); 00482 if (temporaryInput!=null) { 00483 if (temporaryInput.length()>1) 00484 temporaryInput = 00485 temporaryInput.substring(0,temporaryInput.length()-1); 00486 else temporaryInput = null; 00487 while (makeOutput(false)); 00488 if (temporaryOutput!=null) 00489 viewer.addTemporary(temporaryOutput); 00490 } else 00491 viewer.specialKey(KEY_BACKSPACE); 00492 } else { 00493 viewer.removeTemporary(); 00494 if (temporaryInput==null) temporaryInput = ""; 00495 temporaryInput += str; 00496 while (makeOutput(false)); 00497 if (temporaryOutput!=null) 00498 viewer.addTemporary(temporaryOutput); 00499 } 00500 }; 00501 00502 public InputManager(InputManagerContext _context) 00503 throws IOException { 00504 imContext = _context; 00505 iiContext = imContext.getInternationalInputContext(); 00506 getGroup("default").setHidden(true); 00507 getGroup("default").activate(); 00508 } 00509 00514 boolean makeOutput(boolean finished) { 00515 boolean changed = false; 00516 if (temporaryInput==null) { 00517 temporaryOutput = null; return changed; } 00518 00519 InputReplacement longestRpl = null; 00520 String longestPfx = ""; 00521 boolean strIsPfx = false; 00522 int rplsize = replacements.size(); 00523 StringBuffer continuations = null; 00524 boolean needContinuations = !finished; 00525 for (int i=0; i<rplsize; i++) { 00526 InputReplacement rpl = (InputReplacement)replacements.elementAt(i); 00527 if (!getGroup(rpl.group).isActive()) continue; 00528 00529 String pfx = rpl.replacesPrefixOf(temporaryInput); 00530 // temporaryInput.startsWith(rpl.string) 00531 if (pfx != null && 00532 pfx.length() > longestPfx.length()) { 00533 longestRpl = rpl; longestPfx = pfx; } 00534 00535 if (needContinuations) { 00536 String cont[] = rpl.isContinuationOf(temporaryInput); 00537 // rpl.string.startsWith(temporaryInput) 00538 if (cont!=null) { 00539 for (int j=0; j<cont.length; j++) 00540 if (continuations==null || 00541 continuations.length()<=CONTINUATIONS_LENGTH) { 00542 if (continuations!=null) 00543 continuations.append(", "); 00544 else continuations = new StringBuffer(); 00545 continuations.append(cont[j]); 00546 } else needContinuations = false; 00547 strIsPfx = true; 00548 } 00549 } 00550 } 00551 00552 if (continuations!=null && continuations.length()>CONTINUATIONS_LENGTH) 00553 continuations = 00554 new StringBuffer(continuations.substring 00555 (0,CONTINUATIONS_LENGTH) 00556 +"..."); 00557 00558 try { 00559 if (continuations!=null) 00560 iiContext.showInfo(continuations.toString(),false); 00561 else 00562 iiContext.showInfo("",false); 00563 } catch (UnsupportedOperationException e) {}; 00564 00565 String finalOut = null; 00566 if (longestRpl!=null && strIsPfx) { 00567 temporaryOutput = longestRpl.getReplacement(temporaryInput)+ 00568 temporaryInput.substring(longestPfx.length()); 00569 } else if (longestRpl!=null && !strIsPfx) { 00570 int len = longestPfx.length(); 00571 if (len==0) 00572 throw new RuntimeException 00573 ("internal error: longestPfx.length()==0"); 00574 finalOut = longestRpl.getFinalReplacement(temporaryInput); 00575 temporaryInput = temporaryOutput = temporaryInput.substring(len); 00576 changed = true; 00577 } else if (longestRpl==null && strIsPfx) { 00578 temporaryOutput = temporaryInput; 00579 } else if (longestRpl==null && !strIsPfx) { 00580 finalOut = temporaryInput.substring(0,1); 00581 temporaryInput = temporaryOutput = temporaryInput.substring(1); 00582 changed = true; 00583 } 00584 00585 if (finalOut!=null) { 00586 viewer.addFinal(finalOut); 00587 } 00588 if (temporaryOutput!=null && temporaryOutput.length()==0) 00589 temporaryOutput = null; 00590 if (temporaryInput!=null && temporaryInput.length()==0) 00591 temporaryInput = null; 00592 00593 System.out.println("final="+finalOut+" temp-out="+temporaryOutput+ 00594 " temp-in="+temporaryInput); 00595 return changed; 00596 } 00597 00598 String charToString(int c) { 00599 if (c<0) 00600 throw new IllegalArgumentException 00601 ("chars need positive values, not "+c); 00602 char cc[] = { (char)c }; 00603 return new String(cc); 00604 } 00605 00609 public static URL makeProxyURL(URL base, URL url) { 00610 try { 00611 return new URL(base,"proxy/xxx?"+url); 00612 } catch (MalformedURLException e) { 00613 throw new RuntimeException("Internal error"); 00614 } 00615 } 00616 00617 InputStream openURL(URL url) throws IOException { 00618 //System.out.println("openURL("+url+")"); 00619 try { 00620 return url.openStream(); 00621 } catch (SecurityException e) { 00622 URL proxy = makeProxyURL(iiContext.getDocumentBase(),url); 00623 System.out.println("Security restriction. Using proxy "+proxy); 00624 return proxy.openStream(); 00625 } 00626 } 00627 00628 public void loadConfigString(String str) throws IOException { 00629 loadConfig_native 00630 (new StringReader 00631 ("#!/bin/sh international-input-config\n"+str), 00632 new URL(iiContext.getDocumentBase(),"no-source.html")); 00633 } 00634 00635 public void loadConfig(URL url) throws IOException { 00636 //System.out.println("InputManager::loadConfig("+url+")"); 00637 imContext.showMessage("Loading config file "+url); 00638 InputStream inputStream = null; 00639 String lcPath = url.toString().toLowerCase(); 00640 if (lcPath.endsWith(".gz")) { 00641 lcPath = lcPath.substring(0,lcPath.length()-3); 00642 try { 00643 inputStream = new GZIPInputStream(openURL(url)); 00644 } catch (FileNotFoundException e) { 00645 String ustr = url.toString(); 00646 url = new URL(ustr.substring(0,ustr.length()-3)); 00647 inputStream = openURL(url); 00648 } 00649 } else 00650 inputStream = openURL(url); 00651 00652 if (lcPath.endsWith(".inp")) { 00653 System.out.println("Loading "+url+" as native config file."); 00654 loadConfig_native(new InputStreamReader(inputStream,"utf-8"),url); 00655 } else if (lcPath.endsWith(".my")) { 00656 System.out.println("Loading "+url+" as Yudit .my file"); 00657 loadConfig_yudit(inputStream,url,null); 00658 // new OutputStreamWriter 00659 // (new FileOutputStream("/dev/stdout"),"utf-8")); 00660 } else { 00661 throw new FileFormatException("File "+url+" does not have a recognisable file name extension (should be .inp/.inp.gz/.my/.my.gz)"); 00662 } 00663 } 00664 00665 public void loadConfig_yudit(InputStream inputStream, URL url, 00666 Writer _converted) 00667 throws IOException { 00668 DataInputStream in = new DataInputStream(new BufferedInputStream(inputStream)); 00669 PrintWriter converted = null; 00670 if (_converted!=null) converted = new PrintWriter(_converted,true); 00671 byte buff[] = new byte[256]; 00672 00673 // Magic 00674 in.readFully(buff,0,16); 00675 String str = new String(buff,0,16,"us-ascii"); 00676 //System.out.println("Magic = ["+str+"]"); 00677 if (!str.equals("YUDIT-NtoN 1.0\000")) 00678 throw new FileFormatException 00679 ("YUDIT file must start with \"YUDIT-NtoN 1.0\\000\""); 00680 00681 // alias name 00682 in.readFully(buff,0,32); 00683 String name = yudit_cutCString(new String(buff,0,32,"us-ascii")); 00684 //System.out.println("Alias = ["+name+"]"); 00685 name = name.toLowerCase(); 00686 if (name.endsWith(".kmap")) 00687 name = name.substring(0,name.length()-5); 00688 else if (name.endsWith(".my")) 00689 name = name.substring(0,name.length()-3); 00690 else if (name.endsWith(".mys")) 00691 name = name.substring(0,name.length()-4); 00692 name = "y."+name; 00693 00694 // Comment 00695 int len = in.readInt(); 00696 if (buff.length<len) buff = new byte[len+100]; 00697 in.readFully(buff,0,len); 00698 String comment = 00699 yudit_cutCString(new String(buff,0,len,"us-ascii")); 00700 //System.out.println("Comment = ["+comment+"]"); 00701 00702 String docHead = "Imported Yudit <tt>.my</tt> file."; 00703 if (comment.length()>0) 00704 comment = docHead+"<p>Original comment:<br><pre>\n"+ 00705 HTMLEscapeSB(comment,false)+"</pre>"; 00706 else 00707 comment = docHead; 00708 if (converted!=null) { 00709 converted.println("#!/bin/sh international-input-config"); 00710 converted.println("=GROUP"); 00711 converted.println(name); 00712 converted.println("on"); 00713 converted.println("=DESCRIPTION"); 00714 converted.println(comment); 00715 converted.println("###\n"); 00716 } 00717 Group group = getGroup(name); 00718 group.setDescription(comment); 00719 group.setSourceURL(url); 00720 group.activate(); 00721 00722 // Map type 00723 in.skipBytes(4); 00724 00725 // Map size 00726 int mapSize = in.readInt(); 00727 //System.out.println("Maps: "+mapSize); 00728 00729 int maps[] = new int[mapSize+1]; 00730 for (int i=0; i<=mapSize; i++) { 00731 maps[i] = in.readInt(); 00732 //System.out.println("maps["+i+"] = "+ 00733 // Integer.toHexString(maps[i])); 00734 } 00735 00736 int mapOffset = 0; 00737 int numDecodeMaps = 0; 00738 for (int map=0; map<=mapSize; map++) { 00739 if (mapOffset<maps[map]) { 00740 in.skipBytes(maps[map]-mapOffset); 00741 mapOffset = maps[map]; } 00742 if (map==mapSize) break; 00743 // alias name 00744 in.readFully(buff,0,32); mapOffset+=32; 00745 str = yudit_cutCString(new String(buff,0,32,"us-ascii")); 00746 //System.out.println("Alias("+map+") = ["+str+"]"); 00747 // Comment 00748 len = in.readInt(); mapOffset+=4; 00749 if (buff.length<len) buff = new byte[len+100]; 00750 in.readFully(buff,0,len); mapOffset+=len; 00751 str = yudit_cutCString(new String(buff,0,len,"us-ascii")); 00752 //System.out.println("Comment("+map+") = ["+str+"]"); 00753 // Decode 00754 int t = in.readUnsignedByte(); mapOffset++; 00755 boolean decode = ((t&1)==0); 00756 //System.out.println("Decode("+map+") = "+decode); 00757 if (!decode) 00758 continue; 00759 if (++numDecodeMaps>1) { 00760 throw new UnsupportedOperationException 00761 ("Multipart maps not supported"); } 00762 00763 // sizes 00764 int inputByteSize = 1<<in.readUnsignedByte(); 00765 int outputByteSize = 1<<in.readUnsignedByte(); 00766 int inputByteLen = 1<<in.readUnsignedByte(); 00767 int outputByteLen = 1<<in.readUnsignedByte(); 00768 mapOffset+=4; 00769 //System.out.println("IS OS IL OL: "+inputByteSize+" "+ 00770 //outputByteSize+" "+inputByteLen+" "+ 00771 //outputByteLen); 00772 00773 // state machine, spare 00774 in.skipBytes(8); mapOffset+=8; 00775 00776 // codeSize + codes 00777 int codeSize = in.readInt(); mapOffset+=4; 00778 int codes[] = new int[codeSize+1]; 00779 for (int i=0; i<=codeSize; i++) { 00780 codes[i] = in.readInt(); mapOffset+=4; 00781 //System.out.println("codes["+i+"] = "+ 00782 // Integer.toHexString(codes[i])); 00783 } 00784 00785 int codeOffset = 0; 00786 for (int code=0; code<=codeSize; code++) { 00787 if (codeOffset<codes[code]) { 00788 in.skipBytes(codes[code]-codeOffset); 00789 mapOffset += codes[code]-codeOffset; 00790 codeOffset = codes[code]; } 00791 if (code==codeSize) break; 00792 00793 // key size, sub size 00794 int keySize = yudit_readNByte(in,inputByteLen); 00795 int subSize = yudit_readNByte(in,inputByteLen); 00796 mapOffset += 2*inputByteLen; 00797 codeOffset += 2*inputByteLen; 00798 //System.out.println("keySize: "+keySize); 00799 //System.out.println("subSize: "+subSize); 00800 if (keySize==0) { 00801 System.out.println("keySize == 0, ignoring."); 00802 continue; } 00803 00804 // key 00805 if (buff.length<keySize) buff = new byte[keySize+100]; 00806 in.readFully(buff,0,keySize); 00807 mapOffset += keySize; codeOffset += keySize; 00808 String key = yudit_parseString(buff,keySize,inputByteSize); 00809 if (key==null) continue; 00810 00811 // result size 00812 int resultSize = yudit_readNByte(in,outputByteLen); 00813 mapOffset += outputByteLen; codeOffset += outputByteLen; 00814 //System.out.println("resultSize: "+resultSize); 00815 00816 // result 00817 if (buff.length<resultSize) 00818 buff = new byte[resultSize+100]; 00819 in.readFully(buff,0,resultSize); 00820 mapOffset += resultSize; codeOffset += resultSize; 00821 String result = yudit_parseString(buff,resultSize, 00822 outputByteSize); 00823 if (result==null) continue; 00824 //System.out.println(key+" => "+result); 00825 00826 if (subSize!=keySize) { 00827 System.out.println(key+" => "+result+ 00828 " has subSize!=keySize ("+ 00829 subSize+"!="+keySize+")"); 00830 continue; } 00831 00832 if (converted!=null) { 00833 if (key.indexOf('\n')!=-1 || 00834 result.indexOf('\n')!=-1) 00835 throw new UnsupportedOperationException 00836 ("Cannot produce =REPLACE-NL"); 00837 converted.println("=REPLACE"); 00838 converted.println(key); 00839 converted.println(result); 00840 } 00841 StringReplacement newrpl = 00842 new StringReplacement(key,result); 00843 newrpl.group = group.index; 00844 replacements.addElement(newrpl); 00845 } 00846 } 00847 00848 if (numDecodeMaps<1) 00849 throw new FileFormatException("No decode maps"); 00850 // throw new UnsupportedOperationException("Yudit .my files not yet supported"); 00851 if (converted!=null) converted.flush(); 00852 } 00853 00854 private static String yudit_parseString(byte bytes[], int len, 00855 int width) { 00856 StringBuffer str = new StringBuffer(); 00857 switch (width) { 00858 case 1: 00859 for (int i=0; i<len; i++) { 00860 str.append((char)(bytes[i]&0xff)); } 00861 break; 00862 case 2: 00863 for (int i=0; i<len; i+=2) { 00864 str.append((char)((bytes[i]&0xff)<<8 | (bytes[i+1]&0xff))); 00865 } 00866 break; 00867 case 4: 00868 System.out.println("start"); 00869 for (int i=0; i<len; i+=4) { 00870 long cp = ((long)(bytes[i]&0xff))<<24 | ((long)(bytes[i+1]&0xff))<<16 | 00871 ((long)(bytes[i+2]&0xff))<<8 | ((long)(bytes[i+3]&0xff)); 00872 if (cp<0 || cp>0xFFFF) { 00873 //System.out.println 00874 //("String contains U+"+Long.toHexString(cp)+ 00875 //", which is not supported by Java, creating surrogates."); 00876 00877 // Create surrogate pair, since Java does not support >= U+10000 00878 str.append((char)(((cp-0x10000)>>10)+0xD800)); 00879 str.append((char)((cp&0x3FF)+0xDC00)); 00880 } else 00881 str.append((char)cp); 00882 } 00883 break; 00884 default: 00885 throw new IllegalArgumentException 00886 ("yudit_parseString only supports lens 1/2/4, not "+width); 00887 } 00888 return str.toString(); 00889 } 00890 private static int yudit_readNByte(DataInputStream in, int num) 00891 throws IOException { 00892 switch (num) { 00893 case 1: return in.readUnsignedByte(); 00894 case 2: return in.readUnsignedShort(); 00895 case 4: return in.readInt(); 00896 default: 00897 throw new IllegalArgumentException 00898 ("yudit_readNByte only supports len 1/2/4, not "+num); 00899 } 00900 } 00901 private static String yudit_cutCString(String str) { 00902 return yudit_cutCString(str,'\000'); 00903 } 00904 private static String yudit_cutCString(String str, char cut) { 00905 int pos = str.indexOf(cut); 00906 if (pos==-1) return str; 00907 return str.substring(0,pos); 00908 } 00909 00910 public void loadConfig_native(Reader inputReader, URL url) 00911 throws IOException { 00912 BufferedReader input = 00913 new BufferedReader(inputReader); 00914 boolean newWebPages = false; 00915 String line; 00916 line = input.readLine(); 00917 if (line==null || 00918 !line.startsWith("#!/bin/sh international-input-config")) 00919 throw new FileFormatException 00920 ("Config file does not start with \n"+ 00921 " \"#!/bin/sh international-input-config\", but with \n \""+ 00922 line+"\"."); 00923 int groupIndex = getGroup("default").index; 00924 while ((line=input.readLine())!=null) { 00925 if (line.equals("=REPLACE") || 00926 line.equals("=REPLACE-NL") || 00927 line.equals("=REPLACE-EXT") || 00928 line.equals("=REPLACE-EXT-NL")) { 00929 String string = input.readLine(); 00930 String by = input.readLine(); 00931 String finalBy = null; 00932 if (line.equals("=REPLACE-EXT") || 00933 line.equals("=REPLACE-EXT-NL")) { 00934 finalBy = input.readLine(); 00935 if (finalBy==null) 00936 throw new FileFormatException 00937 ("=REPLACE-EXT not followed by three lines"); 00938 } else finalBy = by; 00939 if (line.equals("=REPLACE-NL") || 00940 line.equals("=REPLACE-EXT-NL")) { 00941 String nl = input.readLine(); 00942 if (nl==null || nl.length()!=1) 00943 throw new FileFormatException 00944 ("=REPLACE-NL needs one char string as arg 3"); 00945 char c = nl.charAt(0); 00946 string = string.replace(c,'\n'); 00947 by = by.replace(c,'\n'); 00948 finalBy = finalBy.replace(c,'\n'); 00949 } 00950 if (string==null || by==null) 00951 throw new FileFormatException 00952 ("=REPLACE not followed by two lines"); 00953 StringReplacement newrpl = 00954 new StringReplacement(string,by,finalBy); 00955 newrpl.group = groupIndex; 00956 replacements.addElement(newrpl); 00957 } else if (line.equals("=GROUP")) { 00958 String groupName = input.readLine(); 00959 String state = input.readLine(); 00960 if (state==null) 00961 throw new FileFormatException 00962 ("=GROUP must be followed by two lines."); 00963 Group group = getGroup(groupName); 00964 group.setSourceURL(url); 00965 groupIndex = group.index; 00966 if (state.equals("on")) { 00967 group.activate(); 00968 } else if (state.equals("hidden")) { 00969 group.setHidden(true); 00970 group.activate(); 00971 } else if (state.equals("off")) { 00972 group.deactivate(); 00973 } else { 00974 throw new FileFormatException 00975 ("Invalid group state "+state+" for group "+group.getName()); 00976 } 00977 } else if (line.equals("=SWITCH")) { 00978 String string = input.readLine(); 00979 String group = input.readLine(); 00980 String stateName = input.readLine(); 00981 int state; 00982 if (stateName.equals("on")) { 00983 state = SwitchReplacement.ON; 00984 } else if (stateName.equals("on")) { 00985 state = SwitchReplacement.ON; 00986 } else if (stateName.equals("off")) { 00987 state = SwitchReplacement.OFF; 00988 } else { 00989 throw new FileFormatException 00990 ("Bad state argument to =SWITCH: "+stateName); 00991 } 00992 SwitchReplacement newrpl = 00993 new SwitchReplacement 00994 (string,getGroup(group).index,state); 00995 newrpl.group = groupIndex; 00996 replacements.addElement(newrpl); 00997 } else if (line.equals("=AUTOLOAD")) { 00998 String group = input.readLine(); 00999 String alurl = input.readLine(); 01000 if (alurl==null) 01001 throw new FileFormatException 01002 ("=AUTOLOAD not followed by two lines"); 01003 getGroup(group).autoloadURL(new URL(url,alurl)); 01004 } else if (line.equals("=DESCRIPTION")) { 01005 String descr = ""; 01006 while (true) { 01007 String l = input.readLine(); 01008 if (l==null) 01009 throw new FileFormatException("=DESCRIPTION ended by EOF"); 01010 if (l.equals("###")) break; 01011 descr += l+"\n"; 01012 getGroup(groupIndex).setDescription(descr); 01013 } 01014 //} else if (line.equals("=HIDE")) { 01015 //getGroup(groupIndex).setHidden(true); 01016 } else if (line.equals("=NAME")) { 01017 String name = input.readLine(); 01018 String screenName = input.readLine(); 01019 if (screenName==null) 01020 throw new FileFormatException 01021 ("=NAME not followed by two lines"); 01022 getGroup(name).setScreenName(screenName); 01023 } else if (line.equals("=JAVAREPLACE")) { 01024 InputReplacement newrpl = 01025 (InputReplacement)PluginLoader.load 01026 (InputReplacement.class,url,input); 01027 newrpl.group = groupIndex; 01028 replacements.add(newrpl); 01029 } else if (line.equals("=WEBPAGE")) { 01030 loadConfig_native_webpage(input,url); 01031 newWebPages = true; 01032 } else if (line.equals("") || line.charAt(0)=='#') { 01033 } else { 01034 throw new FileFormatException 01035 ("Unknown config command "+line); 01036 } 01037 } 01038 System.out.println("Done."); 01039 if (newGroups) { 01040 createIMMenu(); newGroups = false; 01041 } 01042 if (newWebPages) { 01043 createWebPageMenu(); newWebPages = false; 01044 } 01045 } 01046 01047 void loadConfig_native_webpage(BufferedReader input, 01048 URL source) throws IOException { 01049 String name = input.readLine(); 01050 String url = input.readLine(); 01051 if (url==null) 01052 throw new FileFormatException 01053 ("=WEBPAGE not followed by name/url (two lines)"); 01054 WebPage webpage = new WebPage(name,url); 01055 webpage.setSourceURL(source); 01056 WebPage.Token token = null; 01057 while (true) { 01058 String command = input.readLine(); 01059 if (command==null) 01060 throw new FileFormatException("=WEBPAGE ended by EOF"); 01061 else if (command.equals(":TOKEN")) { 01062 String tokname = input.readLine(); 01063 if (tokname==null) 01064 throw new FileFormatException 01065 (":TOKEN (in =WEBPAGE) not "+ 01066 "followed by a line (token's name)"); 01067 if (token!=null) webpage.addToken(token); 01068 token = new WebPage.Token(tokname); 01069 } else if (command.equals("###")) { 01070 if (token!=null) webpage.addToken(token); 01071 break; 01072 } else if (command.equals(":CONST")) { 01073 if (token==null) 01074 throw new FileFormatException 01075 (":CONST (in =WEBPAGE) must be preceded by a :TOKEN"); 01076 String str = input.readLine(); 01077 if (str==null) 01078 throw new FileFormatException 01079 (":CONST (in =WEBPAGE) not followed by a line"); 01080 token.setConst(str); 01081 } else if (command.equals(":ENCODING")) { 01082 if (token==null) 01083 throw new FileFormatException 01084 (":ENCODING (in =WEBPAGE) must be preceded by a :TOKEN"); 01085 String enc = input.readLine(); 01086 if (enc==null) 01087 throw new FileFormatException 01088 (":ENCODING (in =WEBPAGE) not followed by a line"); 01089 if (enc.equals("hex")) 01090 token.setEncoding(WebPage.Token.ENCODE_HEX); 01091 else if (enc.equals("dec")) 01092 token.setEncoding(WebPage.Token.ENCODE_DEC); 01093 else if (enc.equals("url")) 01094 token.setEncoding(WebPage.Token.ENCODE_URL); 01095 else 01096 throw new FileFormatException 01097 (":ENCODING (in =WEBPAGE) followed by invalid encoding "+enc); 01098 } else if (command.equals(":CHARSET")) { 01099 if (token==null) 01100 throw new FileFormatException 01101 (":CHARSET (in =WEBPAGE) must be preceded by a :TOKEN"); 01102 String cs = input.readLine(); 01103 if (cs==null) 01104 throw new FileFormatException 01105 (":CHARSET (in =WEBPAGE) not followed by a line"); 01106 token.setCharset(cs); 01107 } else if (command.equals(":SHORTNAME")) { 01108 String sn = input.readLine(); 01109 if (sn==null) 01110 throw new FileFormatException 01111 (":SHORTNAME (in =WEBPAGE) not followed by a line"); 01112 webpage.setShortName(sn); 01113 } else if (command.equals(":TOPFRAME")) { 01114 webpage.setNeedsTopFrame(true); 01115 } else if (command.equals(":DESCRIPTION")) { 01116 String descr = ""; 01117 while (true) { 01118 String l = input.readLine(); 01119 if (l==null) 01120 throw new FileFormatException(":DESCRIPTION (in =WEBPAGE) ended by EOF"); 01121 if (l.equals(":###")) break; 01122 descr += l+"\n"; 01123 if (token!=null) 01124 throw new FileFormatException 01125 (":DESCRIPTION (in =WEBPAGE) must come prior to any :TOKEN"); 01126 webpage.setDescription(descr); 01127 } 01128 } else { 01129 throw new FileFormatException 01130 ("Unknown webpage parameter "+command); 01131 } 01132 } 01133 if (!webPages.contains(webpage)) 01134 webPages.add(webpage); 01135 } 01136 01137 void createWebPageMenu() { 01138 //WebPage wp[] = (InputManager.WebPage[])webPages.toArray(); 01139 int numWP = webPages.size(); 01140 WebPage wp[] = new WebPage[numWP]; 01141 for (int i=0; i<numWP; i++) 01142 wp[i] = (WebPage)webPages.elementAt(i); 01143 imContext.createWebPageMenu(wp); 01144 } 01145 01146 void createIMMenu() { 01147 String im[] = new String[groups.size()]; 01148 int size = groups.size(); 01149 for (int i=0; i<size; i++) { 01150 Group g = (Group)groups.elementAt(i); 01151 if (!g.isHidden()) { 01152 im[i] = g.getName(); 01153 } 01154 } 01155 imContext.createIMMenu(im); 01156 } 01157 01158 public boolean isInputMethodActive(String name) { 01159 return getGroup(name).isActive(); 01160 } 01161 public String getInputMethodScreenName(String name) { 01162 return getGroup(name).getScreenName(); 01163 } 01164 01165 public void deselectNonHiddenGroups() { 01166 for (Enumeration e = groups.elements(); 01167 e.hasMoreElements();) { 01168 Group g = (Group)e.nextElement(); 01169 if (!g.isHidden() && g.isActive()) 01170 g.deactivate(); 01171 } 01172 } 01173 01174 public boolean getSingleInputMethod() { return singleInputMethod; }; 01175 public void setSingleInputMethod(boolean on) throws IOException { 01176 singleInputMethod = on; 01177 if (on) { 01178 Group last = lastActivatedGroup; 01179 if (!last.isActive()) last = null; 01180 deselectNonHiddenGroups(); 01181 if (last!=null) last.activate(); 01182 } 01183 } 01184 01185 public static StringBuffer urlEncode(String string) { 01186 return urlEncode(string,"utf-8"); }; 01187 public static final char hexDigits[] = { 01188 '0','1','2','3','4','5','6','7', 01189 '8','9','A','B','C','D','E','F' }; 01190 public static StringBuffer urlEncode(String string, String charset) { 01191 byte str[]; 01192 try { 01193 str = string.getBytes(charset); 01194 } catch (UnsupportedEncodingException e) { 01195 throw new RuntimeException("Internal error: "+e); 01196 } 01197 StringBuffer res = new StringBuffer(); 01198 for (int i=0; i<str.length; i++) { 01199 int code = str[i]&0xFF; 01200 if (code >= 'A' && code <= 'Z') { 01201 res.append((char)code); 01202 } else if (code >= 'a' && code <= 'z') { 01203 res.append((char)code); 01204 } else if (code >= '0' && code <= '9') { 01205 res.append((char)code); 01206 } else switch (code) { 01207 case '!': case '#': case '$': case '\'': case '(': case ')': case '*': 01208 case ',': case '-': case '.': case '/': case ':': case ';': 01209 case '<': case '=': case '>': case '@': case '[': case ']': case '^': 01210 case '_': case '`': case '\\': case '{': case '|': case '}': case '~': 01211 res.append((char)code); break; 01212 default: 01213 res.append('%'); 01214 res.append(hexDigits[code>>4]); 01215 res.append(hexDigits[code&0xF]); 01216 } 01217 } 01218 return res; 01219 } 01220 } 01221

Generated on Sun Aug 15 11:56:53 2004 for International Input by doxygen 1.3.7