· 10 years ago · Aug 08, 2016, 06:58 PM
1//TokenDetails:
2public class TokenDetails {
3
4 private String token;
5 private long tokenExpiration;
6
7 public String getToken() {
8 return token;
9 }
10
11 public long getTokenExpiration() {
12 return tokenExpiration;
13 }
14
15 public TokenDetails(String token, long tokenExpiration) {
16 this.token = token;
17 this.tokenExpiration = tokenExpiration;
18 }
19}
20
21
22//Translate:
23/*
24 * microsoft-translator-java-api
25 *
26 * Copyright 2012 Jonathan Griggs <jonathan.griggs at gmail.com>.
27 *
28 * Licensed under the Apache License, Version 2.0 (the "License");
29 * you may not use this file except in compliance with the License.
30 * You may obtain a copy of the License at
31 *
32 * http://www.apache.org/licenses/LICENSE-2.0
33 *
34 * Unless required by applicable law or agreed to in writing, software
35 * distributed under the License is distributed on an "AS IS" BASIS,
36 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
37 * See the License for the specific language governing permissions and
38 * limitations under the License.
39 */
40package com.memetix.mst.translate;
41
42import com.memetix.mst.language.Language;
43import com.memetix.mst.MicrosoftTranslatorAPI;
44import java.net.URL;
45import java.net.URLEncoder;
46/**
47 * Translate
48 *
49 * Makes calls to the Microsoft Translator API /Translate service
50 *
51 * Uses the AJAX Interface V2 - see: http://msdn.microsoft.com/en-us/library/ff512406.aspx
52 *
53 * @author Jonathan Griggs <jonathan.griggs at gmail.com>
54 */
55public final class Translate extends MicrosoftTranslatorAPI {
56
57 private static final String SERVICE_URL = "http://api.microsofttranslator.com/V2/Ajax.svc/Translate?";
58 private static final String ARRAY_SERVICE_URL = "http://api.microsofttranslator.com/V2/Ajax.svc/TranslateArray?";
59 private static final String ARRAY_JSON_OBJECT_PROPERTY = "TranslatedText";
60
61 //prevent instantiation
62 private Translate(){};
63
64 /**
65 * Translates text from a given Language to another given Language using Microsoft Translator.
66 *
67 * @param text The String to translate.
68 * @param from The language code to translate from.
69 * @param to The language code to translate to.
70 * @return The translated String.
71 * @throws Exception on error.
72 */
73 public static String execute(final String text, final Language from, final Language to, String clientId, String clientSecret) throws Exception {
74 //Run the basic service validations first
75 validateServiceState(text, clientId, clientSecret);
76 final String params =
77 PARAM_FROM_LANG + URLEncoder.encode(from.toString(),ENCODING)
78 + PARAM_TO_LANG + URLEncoder.encode(to.toString(),ENCODING)
79 + PARAM_TEXT_SINGLE + URLEncoder.encode(text,ENCODING);
80
81 final URL url = new URL(SERVICE_URL + params);
82 final String response = retrieveString(url, clientId, clientSecret);
83 return response;
84 }
85
86 /**
87 * Translates text from a given Language to another given Language using Microsoft Translator.
88 *
89 * Default the from to AUTO_DETECT
90 *
91 * @param text The String to translate.
92 * @param to The language code to translate to.
93 * @return The translated String.
94 * @throws Exception on error.
95 */
96 public static String execute(final String text, final Language to, String clientId, String clientSecret) throws Exception {
97 return execute(text,Language.AUTO_DETECT,to, clientId, clientSecret);
98 }
99
100 /**
101 * Translates an array of texts from a given Language to another given Language using Microsoft Translator's TranslateArray
102 * service
103 *
104 * Note that the Microsoft Translator expects all source texts to be of the SAME language.
105 *
106 * @param texts The Strings Array to translate.
107 * @param from The language code to translate from.
108 * @param to The language code to translate to.
109 * @return The translated Strings Array[].
110 * @throws Exception on error.
111 */
112 public static String[] execute(final String[] texts, final Language from, final Language to, String clientId, String clientSecret) throws Exception {
113 //Run the basic service validations first
114 validateServiceState(texts, clientId, clientSecret);
115 final String params =
116 PARAM_FROM_LANG + URLEncoder.encode(from.toString(),ENCODING)
117 + PARAM_TO_LANG + URLEncoder.encode(to.toString(),ENCODING)
118 + PARAM_TEXT_ARRAY + URLEncoder.encode(buildStringArrayParam(texts),ENCODING);
119
120 final URL url = new URL(ARRAY_SERVICE_URL + params);
121 final String[] response = retrieveStringArr(url,ARRAY_JSON_OBJECT_PROPERTY, clientId, clientSecret);
122 return response;
123 }
124
125 /**
126 * Translates an array of texts from an Automatically detected language to another given Language using Microsoft Translator's TranslateArray
127 * service
128 *
129 * Note that the Microsoft Translator expects all source texts to be of the SAME language.
130 *
131 * This is an overloaded convenience method that passes Language.AUTO_DETECT as fromLang to
132 * execute(texts[],fromLang,toLang)
133 *
134 * @param texts The Strings Array to translate.
135 * @param to The language code to translate to.
136 * @return The translated Strings Array[].
137 * @throws Exception on error.
138 */
139 public static String[] execute(final String[] texts, final Language to, String clientId, String clientSecret) throws Exception {
140 return execute(texts,Language.AUTO_DETECT,to,clientId,clientSecret);
141 }
142
143 private static void validateServiceState(final String[] texts, String clientId, String clientSecret) throws Exception {
144 int length = 0;
145 for(String text : texts) {
146 length+=text.getBytes(ENCODING).length;
147 }
148 if(length>10240) {
149 throw new RuntimeException("TEXT_TOO_LARGE - Microsoft Translator (Translate) can handle up to 10,240 bytes per request");
150 }
151 validateServiceState(clientId, clientSecret);
152 }
153
154
155 private static void validateServiceState(final String text,String clientId, String clientSecret) throws Exception {
156 final int byteLength = text.getBytes(ENCODING).length;
157 if(byteLength>10240) {
158 throw new RuntimeException("TEXT_TOO_LARGE - Microsoft Translator (Translate) can handle up to 10,240 bytes per request");
159 }
160 validateServiceState(clientId,clientSecret);
161 }
162
163
164
165}
166
167
168//MicrosoftTranslatorAPI:
169/*
170 * microsoft-translator-java-api
171 *
172 * Copyright 2012 Jonathan Griggs <jonathan.griggs at gmail.com>.
173 *
174 * Licensed under the Apache License, Version 2.0 (the "License");
175 * you may not use this file except in compliance with the License.
176 * You may obtain a copy of the License at
177 *
178 * http://www.apache.org/licenses/LICENSE-2.0
179 *
180 * Unless required by applicable law or agreed to in writing, software
181 * distributed under the License is distributed on an "AS IS" BASIS,
182 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
183 * See the License for the specific language governing permissions and
184 * limitations under the License.
185 */
186package com.memetix.mst;
187
188import java.io.BufferedReader;
189import java.io.InputStream;
190import java.io.InputStreamReader;
191import java.io.OutputStreamWriter;
192import java.net.HttpURLConnection;
193import java.net.URL;
194import java.net.URLEncoder;
195import java.util.HashMap;
196
197import com.memetix.mst.token.TokenDetails;
198import org.json.simple.JSONArray;
199import org.json.simple.JSONObject;
200import org.json.simple.JSONValue;
201
202/**
203 *
204 * MicrosoftAPI
205 *
206 * Makes the generic Microsoft Translator API calls. Different service classes then
207 * extend this to make the specific service calls.
208 *
209 * Uses the AJAX Interface V2 - see: http://msdn.microsoft.com/en-us/library/ff512404.aspx
210 *
211 * @author Jonathan Griggs
212 */
213public abstract class MicrosoftTranslatorAPI {
214 //Encoding type
215 protected static final String ENCODING = "UTF-8";
216
217 private static String DatamarketAccessUri = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13";
218 private static String referrer;
219 private static String contentType = "text/plain";
220
221 private static HashMap<String, TokenDetails> tokens = new HashMap<>();
222
223 protected static final String PARAM_APP_ID = "appId=",
224 PARAM_TO_LANG = "&to=",
225 PARAM_FROM_LANG = "&from=",
226 PARAM_TEXT_SINGLE = "&text=",
227 PARAM_TEXT_ARRAY = "&texts=",
228 PARAM_SPOKEN_LANGUAGE = "&language=",
229 PARAM_SENTENCES_LANGUAGE = "&language=",
230 PARAM_LOCALE = "&locale=",
231 PARAM_LANGUAGE_CODES = "&languageCodes=";
232
233
234 /**
235 * Sets the API key.
236 *
237 * Note: Should ONLY be used with API Keys generated prior to March 31, 2012. All new applications should obtain a ClientId and Client Secret by following
238 * the guide at: http://msdn.microsoft.com/en-us/library/hh454950.aspx
239 * @param pKey The API key.
240 */
241 public static void setContentType(final String pKey) {
242 contentType = pKey;
243 }
244
245 /**
246 * Sets the Http Referrer.
247 * @param pReferrer The HTTP client referrer.
248 */
249 public static void setHttpReferrer(final String pReferrer) {
250 referrer = pReferrer;
251 }
252 /**
253 * Gets the OAuth access token.
254 * @param clientId The Client key.
255 * @param clientSecret The Client Secret
256 */
257 public static String getToken(final String clientId, final String clientSecret) throws Exception {
258 final String params = "grant_type=client_credentials&scope=http://api.microsofttranslator.com"
259 + "&client_id=" + URLEncoder.encode(clientId,ENCODING)
260 + "&client_secret=" + URLEncoder.encode(clientSecret,ENCODING) ;
261
262 final URL url = new URL(DatamarketAccessUri);
263 final HttpURLConnection uc = (HttpURLConnection) url.openConnection();
264 if(referrer!=null)
265 uc.setRequestProperty("referer", referrer);
266 uc.setRequestProperty("Content-Type","application/x-www-form-urlencoded; charset=" + ENCODING);
267 uc.setRequestProperty("Accept-Charset",ENCODING);
268 uc.setRequestMethod("POST");
269 uc.setDoOutput(true);
270
271 OutputStreamWriter wr = new OutputStreamWriter(uc.getOutputStream());
272 wr.write(params);
273 wr.flush();
274
275 try {
276 final int responseCode = uc.getResponseCode();
277 final String result = inputStreamToString(uc.getInputStream());
278 if(responseCode!=200) {
279 throw new Exception("Error from Microsoft Translator API: " + result);
280 }
281 return result;
282 } finally {
283 if(uc!=null) {
284 uc.disconnect();
285 }
286 }
287 }
288
289 /**
290 * Forms an HTTP request, sends it using GET method and returns the result of the request as a String.
291 *
292 * @param url The URL to query for a String response.
293 * @return The translated String.
294 * @throws Exception on error.
295 */
296 private static String retrieveResponse(final URL url, String clientId, String clientSecret) throws Exception {
297 TokenDetails tokenDetails = tokens.get(clientId);
298 if(clientId!=null&&clientSecret!=null&&tokenDetails!=null&&System.currentTimeMillis()>tokenDetails.getTokenExpiration()) {
299 String tokenJson = getToken(clientId,clientSecret);
300 Integer expiresIn = Integer.parseInt((String)((JSONObject)JSONValue.parse(tokenJson)).get("expires_in"));
301 long tokenExpiration = System.currentTimeMillis()+((expiresIn*1000)-1);
302 String token = "Bearer " + (String)((JSONObject)JSONValue.parse(tokenJson)).get("access_token");
303
304 tokenDetails = new TokenDetails(token, tokenExpiration);
305 tokens.put(clientId, tokenDetails);
306 }
307 final HttpURLConnection uc = (HttpURLConnection) url.openConnection();
308 if(referrer!=null)
309 uc.setRequestProperty("referer", referrer);
310 uc.setRequestProperty("Content-Type",contentType + "; charset=" + ENCODING);
311 uc.setRequestProperty("Accept-Charset",ENCODING);
312 if(tokenDetails.getToken()!=null) {
313 uc.setRequestProperty("Authorization",tokenDetails.getToken());
314 }
315 uc.setRequestMethod("GET");
316 uc.setDoOutput(true);
317
318 try {
319 final int responseCode = uc.getResponseCode();
320 final String result = inputStreamToString(uc.getInputStream());
321 if(responseCode!=200) {
322 throw new Exception("Error from Microsoft Translator API: " + result);
323 }
324 return result;
325 } finally {
326 if(uc!=null) {
327 uc.disconnect();
328 }
329 }
330 }
331
332 /**
333 * Fetches the JSON response, parses the JSON Response, returns the result of the request as a String.
334 *
335 * @param url The URL to query for a String response.
336 * @return The translated String.
337 * @throws Exception on error.
338 */
339 protected static String retrieveString(final URL url, String clientId, String clientSecret) throws Exception {
340 try {
341 final String response = retrieveResponse(url, clientId, clientSecret);
342 return jsonToString(response);
343 } catch (Exception ex) {
344 throw new Exception("[microsoft-translator-api] Error retrieving translation : " + ex.getMessage(), ex);
345 }
346 }
347
348 /**
349 * Fetches the JSON response, parses the JSON Response as an Array of JSONObjects,
350 * retrieves the String value of the specified JSON Property, and returns the result of
351 * the request as a String Array.
352 *
353 * @param url The URL to query for a String response.
354 * @return The translated String[].
355 * @throws Exception on error.
356 */
357 protected static String[] retrieveStringArr(final URL url, final String jsonProperty, String clientId, String clientSecret) throws Exception {
358 try {
359 final String response = retrieveResponse(url, clientId, clientSecret);
360 return jsonToStringArr(response,jsonProperty);
361 } catch (Exception ex) {
362 throw new Exception("[microsoft-translator-api] Error retrieving translation.", ex);
363 }
364 }
365
366 /**
367 * Fetches the JSON response, parses the JSON Response as an array of Strings
368 * and returns the result of the request as a String Array.
369 *
370 * Overloaded to pass null as the JSON Property (assume only Strings instead of JSONObjects)
371 *
372 * @param url The URL to query for a String response.
373 * @return The translated String[].
374 * @throws Exception on error.
375 */
376 protected static String[] retrieveStringArr(final URL url, String clientId, String clientSecret) throws Exception {
377 return retrieveStringArr(url,null,clientId,clientSecret);
378 }
379
380 /**
381 * Fetches the JSON response, parses the JSON Response, returns the result of the request as an array of integers.
382 *
383 * @param url The URL to query for a String response.
384 * @return The translated String.
385 * @throws Exception on error.
386 */
387 protected static Integer[] retrieveIntArray(final URL url,String clientId, String clientSecret) throws Exception {
388 try {
389 final String response = retrieveResponse(url,clientId,clientSecret);
390 return jsonToIntArr(response);
391 } catch (Exception ex) {
392 throw new Exception("[microsoft-translator-api] Error retrieving translation : " + ex.getMessage(), ex);
393 }
394 }
395
396 private static Integer[] jsonToIntArr(final String inputString) throws Exception {
397 final JSONArray jsonArr = (JSONArray)JSONValue.parse(inputString);
398 Integer[] intArr = new Integer[jsonArr.size()];
399 int i = 0;
400 for(Object obj : jsonArr) {
401 intArr[i] = ((Long)obj).intValue();
402 i++;
403 }
404 return intArr;
405 }
406
407 private static String jsonToString(final String inputString) throws Exception {
408 String json = (String)JSONValue.parse(inputString);
409 return json.toString();
410 }
411
412 // Helper method to parse a JSONArray. Reads an array of JSONObjects and returns a String Array
413 // containing the toString() of the desired property. If propertyName is null, just return the String value.
414 private static String[] jsonToStringArr(final String inputString, final String propertyName) throws Exception {
415 final JSONArray jsonArr = (JSONArray)JSONValue.parse(inputString);
416 String[] values = new String[jsonArr.size()];
417
418 int i = 0;
419 for(Object obj : jsonArr) {
420 if(propertyName!=null&&propertyName.length()!=0) {
421 final JSONObject json = (JSONObject)obj;
422 if(json.containsKey(propertyName)) {
423 values[i] = json.get(propertyName).toString();
424 }
425 } else {
426 values[i] = obj.toString();
427 }
428 i++;
429 }
430 return values;
431 }
432
433 /**
434 * Reads an InputStream and returns its contents as a String.
435 * Also effects rate control.
436 * @param inputStream The InputStream to read from.
437 * @return The contents of the InputStream as a String.
438 * @throws Exception on error.
439 */
440 private static String inputStreamToString(final InputStream inputStream) throws Exception {
441 final StringBuilder outputBuilder = new StringBuilder();
442
443 try {
444 String string;
445 if (inputStream != null) {
446 BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, ENCODING));
447 while (null != (string = reader.readLine())) {
448 // Need to strip the Unicode Zero-width Non-breaking Space. For some reason, the Microsoft AJAX
449 // services prepend this to every response
450 outputBuilder.append(string.replaceAll("\uFEFF", ""));
451 }
452 }
453 } catch (Exception ex) {
454 throw new Exception("[microsoft-translator-api] Error reading translation stream.", ex);
455 }
456
457 return outputBuilder.toString();
458 }
459
460 //Check if ready to make request, if not, throw a RuntimeException
461 protected static void validateServiceState(String clientId, String clientSecret) throws Exception {
462 if (clientId==null||clientSecret==null) {
463 throw new RuntimeException("Must provide a Windows Azure Marketplace Client Id and Client Secret - Please see http://msdn.microsoft.com/en-us/library/hh454950.aspx for further documentation");
464 }
465 }
466
467 protected static String buildStringArrayParam(Object[] values) {
468 StringBuilder targetString = new StringBuilder("[\"");
469 String value;
470 for(Object obj : values) {
471 if(obj!=null) {
472 value = obj.toString();
473 if(value.length()!=0) {
474 if(targetString.length()>2)
475 targetString.append(",\"");
476 targetString.append(value);
477 targetString.append("\"");
478 }
479 }
480 }
481 targetString.append("]");
482 return targetString.toString();
483 }
484
485}