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