1 /*************************************************************************/ 2 /* */ 3 /* Use of the link grammar parsing system is subject to the terms of the */ 4 /* license set forth in the LICENSE file included with this software. */ 5 /* This license allows free redistribution and use in source and binary */ 6 /* forms, with or without modification, subject to certain conditions. */ 7 /* */ 8 /*************************************************************************/ 9 10 package org.linkgrammar; 11 12 import java.io.Reader; 13 import java.text.CharacterIterator; 14 import java.text.StringCharacterIterator; 15 import java.util.HashMap; 16 import java.util.Map; 17 18 /** 19 * Link-grammar server JSON-related utilities. 20 * 21 * @author Borislav Iordanov 22 * @author Linas Vepstas 23 * 24 */ 25 public class JSONUtils 26 { getBool(String name, Map<String, String> msg, boolean def)27 public static boolean getBool(String name, Map<String, String> msg, boolean def) 28 { 29 String x = msg.get(name); 30 return x == null ? def : Boolean.valueOf(x); 31 } 32 getInt(String name, Map<String, String> msg, int def)33 public static int getInt(String name, Map<String, String> msg, int def) 34 { 35 String x = msg.get(name); 36 return x == null ? def : Integer.parseInt(x); 37 } 38 getDouble(String name, Map<String, String> msg, double def)39 public static double getDouble(String name, Map<String, String> msg, double def) 40 { 41 String x = msg.get(name); 42 return x == null ? def : Double.parseDouble(x); 43 } 44 45 static char[] hex = "0123456789ABCDEF".toCharArray(); 46 jsonString(String s)47 public static String jsonString(String s) 48 { 49 if (s == null) 50 return null; 51 StringBuffer b = new StringBuffer(); 52 b.append("\""); 53 CharacterIterator it = new StringCharacterIterator(s); 54 for (char c = it.first(); c != CharacterIterator.DONE; c = it.next()) 55 { 56 if (c == '"') b.append("\\\""); 57 else if (c == '\\') b.append("\\\\"); 58 else if (c == '/') b.append("\\/"); 59 else if (c == '\b') b.append("\\b"); 60 else if (c == '\f') b.append("\\f"); 61 else if (c == '\n') b.append("\\n"); 62 else if (c == '\r') b.append("\\r"); 63 else if (c == '\t') b.append("\\t"); 64 else if (Character.isISOControl(c)) 65 { 66 int n = c; 67 for (int i = 0; i < 4; ++i) { 68 int digit = (n & 0xf000) >> 12; 69 b.append(hex[digit]); 70 n <<= 4; 71 } 72 } 73 else 74 { 75 b.append(c); 76 } 77 } 78 b.append("\""); 79 return b.toString(); 80 } 81 82 private String rawText; getRawText()83 public String getRawText() { return rawText; } 84 readMsg(Reader in)85 public Map<String, String> readMsg(Reader in) throws java.io.IOException 86 { 87 // Read chars from input until input is exhausted, or until 88 // newline is encountered. "length" will be set to the final 89 // length of the input. The char array buf stores the input; 90 // it is automatically expanded to handle very long inputs. 91 int length = 0; 92 char [] buf = new char[1024]; 93 for (int count = in.read(buf, length, buf.length - length); 94 count > -1; 95 count = in.read(buf, length, buf.length - length)) 96 { 97 length += count; 98 if (length == buf.length) 99 { 100 char [] nbuf = new char[buf.length + 512]; 101 System.arraycopy(buf, 0, nbuf, 0, buf.length); 102 buf = nbuf; 103 } 104 if (buf[length-1] == '\n') 105 break; 106 } 107 rawText = new String(buf); 108 109 // "result" will contain a map of key-value pairs extracted from 110 // the JSON input. (viz, buf is assumed to contain valid json) 111 Map<String, String> result = new HashMap<String, String>(); 112 113 // Note that we expect the JSON part of 'buf' to be in ASCII. 114 // However, the everything after 'text:' might be in UTF-8 115 // That's OK, since the code below just assumes ASCII while 116 // grepping for 'text:' and then lets Java String constructor 117 // deal with the UTF-8. Anyway, it works. 118 boolean gotText = false; 119 int start = -1; 120 int column = -1; 121 for (int offset = 0; offset < length - 1; offset++) 122 { 123 char c = buf[offset]; 124 if (start == -1) 125 start = offset; 126 else if (c == ':' && column == -1) 127 { 128 column = offset; 129 String name = new String(buf, start, column - start); 130 name = name.trim(); 131 if ("text".equals(name)) gotText = true; 132 } 133 134 // Any commas appearing in the sentence text will fuck this up, 135 // so we treat this as a JSON message, but only until the key 136 // "text" is seen. After that, all commas are assumed to be part 137 // of the text message. (ergo, "text" must the last keyword in 138 // the message.) Note also: after that, the rest might be in UTF-8 139 else if (c == '\0' || (!gotText && c == ',')) 140 { 141 if (start == -1 || column == -1) 142 throw new RuntimeException("Malformed message:" + new String(buf, 0, length)); 143 String name = new String(buf, start, column - start); 144 String value = new String(buf, column + 1, offset - column - 1); 145 name = name.trim(); 146 value = value.trim(); 147 result.put(name, value); 148 start = column = -1; 149 } 150 } 151 152 // If we are here, the the last byte wasn't null. This is the 153 // normal exit, I guess ... 154 if (start != -1 && column != -1) { 155 String name = new String(buf, start, column - start); 156 String value = new String(buf, column + 1, length - column - 1); 157 name = name.trim(); 158 value = value.trim(); 159 result.put(name, value); 160 start = column = -1; 161 } 162 if (start != -1 || column != -1) 163 throw new RuntimeException("Malformed message:" 164 + new String(buf, 0, length) 165 + "Did you forget to say \"text:\" at the start of the message?"); 166 return result; 167 } 168 } 169 170