1 /*
2  * Copyright 2008-2014 Arsen Chaloyan
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  * $Id: apt_nlsml_doc.c 2177 2014-09-12 05:36:20Z achaloyan@gmail.com $
17  */
18 
19 #include <stdlib.h>
20 #ifdef WIN32
21 #pragma warning(disable: 4127)
22 #endif
23 #include <apr_ring.h>
24 
25 #include "apt_nlsml_doc.h"
26 #include "apt_log.h"
27 
28 /** NLSML result */
29 struct nlsml_result_t
30 {
31 	/** List of interpretations */
32 	APR_RING_HEAD(apt_ir_head_t, nlsml_interpretation_t) interpretations;
33 	/** List of enrollment results */
34 	APR_RING_HEAD(apt_er_head_t, nlsml_enrollment_result_t) enrollment_results;
35 	/** List of verification results */
36 	APR_RING_HEAD(apt_vr_head_t, nlsml_verification_result_t) verification_results;
37 
38 	/** Optional grammar attribute */
39 	const char *grammar;
40 };
41 
42 /** NLSML instance */
43 struct nlsml_instance_t
44 {
45 	/** Ring entry */
46 	APR_RING_ENTRY(nlsml_instance_t) link;
47 
48 	/** Instance element */
49 	apr_xml_elem *elem;
50 };
51 
52 /** NLSML input */
53 struct nlsml_input_t
54 {
55 	/** Input element */
56 	apr_xml_elem *elem;
57 	/** Input mode attribute [default: "speech"] */
58 	const char *mode;
59 	/** Confidence attribute [default: 1.0] */
60 	float       confidence;
61 	/** Timestamp-start attribute */
62 	const char *timestamp_start;
63 	/** Timestamp-end attribute */
64 	const char *timestamp_end;
65 };
66 
67 /** NLSML interpretation */
68 struct nlsml_interpretation_t
69 {
70 	/** Ring entry */
71 	APR_RING_ENTRY(nlsml_interpretation_t) link;
72 
73 	/** List of instances */
74 	APR_RING_HEAD(apt_head_t, nlsml_instance_t) instances;
75 	/** Input [0..1] */
76 	nlsml_input_t *input;
77 
78 	/** Confidence attribute [default: 1.0] */
79 	float       confidence;
80 	/** Optional grammar attribute */
81 	const char *grammar;
82 };
83 
84 struct nlsml_enrollment_result_t
85 {
86 	/** Ring entry */
87 	APR_RING_ENTRY(nlsml_enrollment_result_t) link;
88 };
89 
90 struct nlsml_verification_result_t
91 {
92 	/** Ring entry */
93 	APR_RING_ENTRY(nlsml_verification_result_t) link;
94 };
95 
96 /** Load NLSML document */
nlsml_doc_load(const char * data,apr_size_t length,apr_pool_t * pool)97 static apr_xml_doc* nlsml_doc_load(const char *data, apr_size_t length, apr_pool_t *pool)
98 {
99 	apr_xml_parser *parser;
100 	apr_xml_doc *doc = NULL;
101 	const apr_xml_elem *root;
102 
103 	if(!data || !length) {
104 		apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No NLSML data available");
105 		return NULL;
106 	}
107 
108 	/* create XML parser */
109 	parser = apr_xml_parser_create(pool);
110 	if(apr_xml_parser_feed(parser,data,length) != APR_SUCCESS) {
111 		apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to feed NLSML input to the parser");
112 		return NULL;
113 	}
114 
115 	/* done with XML tree creation */
116 	if(apr_xml_parser_done(parser,&doc) != APR_SUCCESS) {
117 		apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to terminate NLSML parsing");
118 		return NULL;
119 	}
120 
121 	if(!doc || !doc->root) {
122 		apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No NLSML root element");
123 		return NULL;
124 	}
125 	root = doc->root;
126 
127 	/* NLSML validity check: root element must be <result> */
128 	if(strcmp(root->name,"result") != 0) {
129 		apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unexpected NLSML root element <%s>",root->name);
130 		return NULL;
131 	}
132 
133 	return doc;
134 }
135 
136 /** Parse confidence value */
nlsml_confidence_parse(const char * str)137 static float nlsml_confidence_parse(const char *str)
138 {
139 	float confidence = (float) atof(str);
140 	if(confidence > 1.0)
141 		confidence /= 100;
142 
143 	return confidence;
144 }
145 
146 /** Parse <instance> element */
nlsml_instance_parse(apr_xml_elem * elem,apr_pool_t * pool)147 static nlsml_instance_t* nlsml_instance_parse(apr_xml_elem *elem, apr_pool_t *pool)
148 {
149 	/* Initialize instance */
150 	nlsml_instance_t *instance = apr_palloc(pool, sizeof(*instance));
151 	APR_RING_ELEM_INIT(instance,link);
152 	instance->elem = elem;
153 
154 	return instance;
155 }
156 
157 /** Parse <input> element */
nlsml_input_parse(apr_xml_elem * elem,apr_pool_t * pool)158 static nlsml_input_t* nlsml_input_parse(apr_xml_elem *elem, apr_pool_t *pool)
159 {
160 	const apr_xml_attr *xml_attr;
161 	/* Initialize input */
162 	nlsml_input_t *input = apr_palloc(pool, sizeof(*input));
163 	input->elem = elem;
164 	input->mode = "speech";
165 	input->confidence = 1.0;
166 	input->timestamp_start = NULL;
167 	input->timestamp_end = NULL;
168 
169 	/* Find input attributes */
170 	for(xml_attr = elem->attr; xml_attr; xml_attr = xml_attr->next) {
171 		if(strcasecmp(xml_attr->name, "mode") == 0) {
172 			input->mode = xml_attr->value;
173 		}
174 		else if(strcasecmp(xml_attr->name, "confidence") == 0) {
175 			input->confidence = nlsml_confidence_parse(xml_attr->value);
176 		}
177 		else if(strcasecmp(xml_attr->name, "timestamp-start") == 0) {
178 			input->timestamp_start = xml_attr->value;
179 		}
180 		else if(strcasecmp(xml_attr->name, "timestamp-end") == 0) {
181 			input->timestamp_end = xml_attr->value;
182 		}
183 		else {
184 			apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown attribute '%s' for <%s>", xml_attr->name, elem->name);
185 		}
186 	}
187 
188 	return input;
189 }
190 
191 /** Parse <interpretation> element */
nlsml_interpretation_parse(apr_xml_elem * elem,apr_pool_t * pool)192 static nlsml_interpretation_t* nlsml_interpretation_parse(apr_xml_elem *elem, apr_pool_t *pool)
193 {
194 	apr_xml_elem *child_elem;
195 	const apr_xml_attr *xml_attr;
196 	nlsml_instance_t *instance;
197 	nlsml_input_t *input;
198 
199 	/* Initialize interpretation */
200 	nlsml_interpretation_t *interpretation = apr_palloc(pool, sizeof(*interpretation));
201 	APR_RING_ELEM_INIT(interpretation,link);
202 	interpretation->grammar = NULL;
203 	interpretation->confidence = 1.0;
204 	interpretation->input = NULL;
205 	APR_RING_INIT(&interpretation->instances, nlsml_instance_t, link);
206 
207 	/* Find optional grammar and confidence attributes */
208 	for(xml_attr = elem->attr; xml_attr; xml_attr = xml_attr->next) {
209 		if(strcasecmp(xml_attr->name, "grammar") == 0) {
210 			interpretation->grammar = xml_attr->value;
211 		}
212 		else if(strcasecmp(xml_attr->name, "confidence") == 0) {
213 			interpretation->confidence = nlsml_confidence_parse(xml_attr->value);
214 		}
215 		else {
216 			apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown attribute '%s' for <%s>", xml_attr->name, elem->name);
217 		}
218 	}
219 
220 	/* Find input and instance elements */
221 	for(child_elem = elem->first_child; child_elem; child_elem = child_elem->next) {
222 		if(strcasecmp(child_elem->name, "input") == 0) {
223 			input = nlsml_input_parse(child_elem, pool);
224 			if(input) {
225 				interpretation->input = input;
226 			}
227 		}
228 		else if(strcasecmp(child_elem->name, "instance") == 0) {
229 			instance = nlsml_instance_parse(child_elem, pool);
230 			if(instance) {
231 				APR_RING_INSERT_TAIL(&interpretation->instances, instance, nlsml_instance_t, link);
232 			}
233 		}
234 		else {
235 			apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown child element <%s> for <%s>", child_elem->name, elem->name);
236 		}
237 	}
238 
239 	return interpretation;
240 }
241 
242 /** Parse <enrollment-result> element */
nlsml_enrollment_result_parse(const apr_xml_elem * elem,apr_pool_t * pool)243 static nlsml_enrollment_result_t* nlsml_enrollment_result_parse(const apr_xml_elem *elem, apr_pool_t *pool)
244 {
245 	/* To be done */
246 	return NULL;
247 }
248 
249 /** Parse <verification-result> element */
nlsml_verification_result_parse(const apr_xml_elem * elem,apr_pool_t * pool)250 static nlsml_verification_result_t* nlsml_verification_result_parse(const apr_xml_elem *elem, apr_pool_t *pool)
251 {
252 	/* To be done */
253 	return NULL;
254 }
255 
256 /** Parse NLSML result */
nlsml_result_parse(const char * data,apr_size_t length,apr_pool_t * pool)257 APT_DECLARE(nlsml_result_t*) nlsml_result_parse(const char *data, apr_size_t length, apr_pool_t *pool)
258 {
259 	nlsml_result_t *result;
260 	apr_xml_elem *root;
261 	apr_xml_elem *child_elem;
262 	const apr_xml_attr *xml_attr;
263 	nlsml_interpretation_t *interpretation;
264 	nlsml_enrollment_result_t *enrollment_result;
265 	nlsml_verification_result_t *verification_result;
266 	apr_xml_doc *doc;
267 	/* Load XML document */
268 	doc = nlsml_doc_load(data, length, pool);
269 	if(!doc)
270 		return NULL;
271 
272 	root = doc->root;
273 
274 	/* Initialize result */
275 	result = apr_palloc(pool, sizeof(*result));
276 	APR_RING_INIT(&result->interpretations, nlsml_interpretation_t, link);
277 	APR_RING_INIT(&result->enrollment_results, nlsml_enrollment_result_t, link);
278 	APR_RING_INIT(&result->verification_results, nlsml_verification_result_t, link);
279 	result->grammar = NULL;
280 
281 	/* Find optional grammar attribute */
282 	for(xml_attr = root->attr; xml_attr; xml_attr = xml_attr->next) {
283 		if(strcasecmp(xml_attr->name, "grammar") == 0) {
284 			result->grammar = xml_attr->value;
285 		}
286 		else {
287 			apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown attribute '%s' for <%s>", xml_attr->name, root->name);
288 		}
289 	}
290 
291 	/* Find interpretation, enrollment-result, or verification-result elements */
292 	for(child_elem = root->first_child; child_elem; child_elem = child_elem->next) {
293 		if(strcasecmp(child_elem->name, "interpretation") == 0) {
294 			interpretation = nlsml_interpretation_parse(child_elem, pool);
295 			if(interpretation) {
296 				APR_RING_INSERT_TAIL(&result->interpretations, interpretation, nlsml_interpretation_t, link);
297 			}
298 		}
299 		else if(strcasecmp(child_elem->name, "enrollment-result") == 0) {
300 			enrollment_result = nlsml_enrollment_result_parse(child_elem, pool);
301 			if(enrollment_result) {
302 				APR_RING_INSERT_TAIL(&result->enrollment_results, enrollment_result, nlsml_enrollment_result_t, link);
303 			}
304 		}
305 		else if(strcasecmp(child_elem->name, "verification-result") == 0) {
306 			verification_result = nlsml_verification_result_parse(child_elem, pool);
307 			if(verification_result) {
308 				APR_RING_INSERT_TAIL(&result->verification_results, verification_result, nlsml_verification_result_t, link);
309 			}
310 		}
311 		else {
312 			apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown child element <%s> for <%s>", child_elem->name, root->name);
313 		}
314 	}
315 
316 	if(APR_RING_EMPTY(&result->interpretations, nlsml_interpretation_t, link) &&
317 		APR_RING_EMPTY(&result->enrollment_results, nlsml_enrollment_result_t, link) &&
318 			APR_RING_EMPTY(&result->verification_results, nlsml_verification_result_t, link)) {
319 		/* at least one of <interpretation>, <enrollment-result>, <verification-result> MUST be specified */
320 		apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Invalid NLSML document: at least one child element MUST be specified for <%s>", root->name);
321 	}
322 
323 	return result;
324 }
325 
326 /** Trace NLSML result (for debug purposes only) */
nlsml_result_trace(const nlsml_result_t * result,apr_pool_t * pool)327 APT_DECLARE(void) nlsml_result_trace(const nlsml_result_t *result, apr_pool_t *pool)
328 {
329 	int interpretation_count;
330 	nlsml_interpretation_t *interpretation;
331 	int instance_count;
332 	nlsml_instance_t *instance;
333 	nlsml_input_t *input;
334 	const char *instance_data;
335 	const char *input_data;
336 	const char *timestamp_start;
337 	const char *timestamp_end;
338 
339 	if(result->grammar)
340 		apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Result.grammar: %s", result->grammar);
341 
342 	interpretation_count = 0;
343 	interpretation = nlsml_first_interpretation_get(result);
344 	while(interpretation) {
345 		apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Interpretation[%d].confidence: %.2f", interpretation_count, nlsml_interpretation_confidence_get(interpretation));
346 		apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Interpretation[%d].grammar: %s", interpretation_count, nlsml_interpretation_grammar_get(interpretation));
347 
348 		instance_count = 0;
349 		instance = nlsml_interpretation_first_instance_get(interpretation);
350 		while(instance) {
351 			nlsml_instance_swi_suppress(instance);
352 			instance_data = nlsml_instance_content_generate(instance,pool);
353 			apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Interpretation[%d].instance[%d]: %s", interpretation_count, instance_count, instance_data);
354 
355 			instance_count++;
356 			instance = nlsml_interpretation_next_instance_get(interpretation, instance);
357 		}
358 
359 		input = nlsml_interpretation_input_get(interpretation);
360 		if(input) {
361 			input_data = nlsml_input_content_generate(input,pool);
362 			timestamp_start = nlsml_input_timestamp_start_get(input);
363 			timestamp_end = nlsml_input_timestamp_end_get(input);
364 			apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Interpretation[%d].input: %s", interpretation_count, input_data);
365 			apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Interpretation[%d].input.mode: %s", interpretation_count, nlsml_input_mode_get(input));
366 			apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Interpretation[%d].input.confidence: %.2f", interpretation_count, nlsml_input_confidence_get(input));
367 			if(timestamp_start)
368 				apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Interpretation[%d].input.timestamp-start: %s", interpretation_count, timestamp_start);
369 			if(timestamp_end)
370 				apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Interpretation[%d].input.timestamp-end: %s", interpretation_count, timestamp_end);
371 		}
372 
373 		interpretation_count++;
374 		interpretation = nlsml_next_interpretation_get(result, interpretation);
375 	}
376 }
377 
378 /** Get first interpretation */
nlsml_first_interpretation_get(const nlsml_result_t * result)379 APT_DECLARE(nlsml_interpretation_t*) nlsml_first_interpretation_get(const nlsml_result_t *result)
380 {
381 	nlsml_interpretation_t *first_interpretation = APR_RING_FIRST(&result->interpretations);
382 	if(first_interpretation == APR_RING_SENTINEL(&result->interpretations, nlsml_interpretation_t, link))
383 		return NULL;
384 	return first_interpretation;
385 }
386 
387 /** Get next interpretation */
nlsml_next_interpretation_get(const nlsml_result_t * result,const nlsml_interpretation_t * interpretation)388 APT_DECLARE(nlsml_interpretation_t*) nlsml_next_interpretation_get(const nlsml_result_t *result, const nlsml_interpretation_t *interpretation)
389 {
390 	nlsml_interpretation_t *next_interpretation = APR_RING_NEXT(interpretation, link);
391 	if(next_interpretation == APR_RING_SENTINEL(&result->interpretations, nlsml_interpretation_t, link))
392 		return NULL;
393 	return next_interpretation;
394 }
395 
396 /** Get first enrollment result */
nlsml_first_enrollment_result_get(const nlsml_result_t * result)397 APT_DECLARE(nlsml_enrollment_result_t*) nlsml_first_enrollment_result_get(const nlsml_result_t *result)
398 {
399 	nlsml_enrollment_result_t *first_enrollment_result = APR_RING_FIRST(&result->enrollment_results);
400 	if(first_enrollment_result == APR_RING_SENTINEL(&result->enrollment_results, nlsml_enrollment_result_t, link))
401 		return NULL;
402 	return first_enrollment_result;
403 }
404 
405 /** Get next enrollment result */
nlsml_next_enrollment_result_get(const nlsml_result_t * result,const nlsml_enrollment_result_t * enrollment_result)406 APT_DECLARE(nlsml_enrollment_result_t*) nlsml_next_enrollment_result_get(const nlsml_result_t *result, const nlsml_enrollment_result_t *enrollment_result)
407 {
408 	nlsml_enrollment_result_t *next_enrollment_result = APR_RING_NEXT(enrollment_result, link);
409 	if(next_enrollment_result == APR_RING_SENTINEL(&result->enrollment_results, nlsml_enrollment_result_t, link))
410 		return NULL;
411 	return next_enrollment_result;
412 }
413 
414 /** Get first verification result */
nlsml_first_verification_result_get(const nlsml_result_t * result)415 APT_DECLARE(nlsml_verification_result_t*) nlsml_first_verification_result_get(const nlsml_result_t *result)
416 {
417 	nlsml_verification_result_t *first_verification_result = APR_RING_FIRST(&result->verification_results);
418 	if(first_verification_result == APR_RING_SENTINEL(&result->verification_results, nlsml_verification_result_t, link))
419 		return NULL;
420 	return first_verification_result;
421 }
422 
423 /** Get next verification result */
nlsml_next_verification_result_get(const nlsml_result_t * result,const nlsml_verification_result_t * verification_result)424 APT_DECLARE(nlsml_verification_result_t*) nlsml_next_verification_result_get(const nlsml_result_t *result, const nlsml_verification_result_t *verification_result)
425 {
426 	nlsml_verification_result_t *next_verification_result = APR_RING_NEXT(verification_result, link);
427 	if(next_verification_result == APR_RING_SENTINEL(&result->verification_results, nlsml_verification_result_t, link))
428 		return NULL;
429 	return next_verification_result;
430 }
431 
432 /** Get grammar attribute of NLSML result */
nlsml_result_grammar_get(const nlsml_result_t * result)433 APT_DECLARE(const char*) nlsml_result_grammar_get(const nlsml_result_t *result)
434 {
435 	return result->grammar;
436 }
437 
438 /** Get first instance */
nlsml_interpretation_first_instance_get(const nlsml_interpretation_t * interpretation)439 APT_DECLARE(nlsml_instance_t*) nlsml_interpretation_first_instance_get(const nlsml_interpretation_t *interpretation)
440 {
441 	nlsml_instance_t *first_instance = APR_RING_FIRST(&interpretation->instances);
442 	if(first_instance == APR_RING_SENTINEL(&interpretation->instances, nlsml_instance_t, link))
443 		return NULL;
444 	return first_instance;
445 }
446 
447 /** Get next instance */
nlsml_interpretation_next_instance_get(const nlsml_interpretation_t * interpretation,const nlsml_instance_t * instance)448 APT_DECLARE(nlsml_instance_t*) nlsml_interpretation_next_instance_get(const nlsml_interpretation_t *interpretation, const nlsml_instance_t *instance)
449 {
450 	nlsml_instance_t *next_instance = APR_RING_NEXT(instance, link);
451 	if(next_instance == APR_RING_SENTINEL(&interpretation->instances, nlsml_instance_t, link))
452 		return NULL;
453 	return next_instance;
454 }
455 
456 /** Get input */
nlsml_interpretation_input_get(const nlsml_interpretation_t * interpretation)457 APT_DECLARE(nlsml_input_t*) nlsml_interpretation_input_get(const nlsml_interpretation_t *interpretation)
458 {
459 	return interpretation->input;
460 }
461 
462 /** Get interpretation confidence */
nlsml_interpretation_confidence_get(const nlsml_interpretation_t * interpretation)463 APT_DECLARE(float) nlsml_interpretation_confidence_get(const nlsml_interpretation_t *interpretation)
464 {
465 	return interpretation->confidence;
466 }
467 
468 /** Get interpretation grammar */
nlsml_interpretation_grammar_get(const nlsml_interpretation_t * interpretation)469 APT_DECLARE(const char*) nlsml_interpretation_grammar_get(const nlsml_interpretation_t *interpretation)
470 {
471 	return interpretation->grammar;
472 }
473 
474 /** Get instance element */
nlsml_instance_elem_get(const nlsml_instance_t * instance)475 APT_DECLARE(const apr_xml_elem*) nlsml_instance_elem_get(const nlsml_instance_t *instance)
476 {
477 	return instance->elem;
478 }
479 
480 /** Suppress SWI elements (normalize instance) */
nlsml_instance_swi_suppress(nlsml_instance_t * instance)481 APT_DECLARE(apt_bool_t) nlsml_instance_swi_suppress(nlsml_instance_t *instance)
482 {
483 	apr_xml_elem *child_elem;
484 	apr_xml_elem *prev_elem = NULL;
485 	apr_xml_elem *swi_literal = NULL;
486 	apt_bool_t remove;
487 	if(!instance->elem)
488 		return FALSE;
489 
490 	for(child_elem = instance->elem->first_child; child_elem; child_elem = child_elem->next) {
491 		remove = FALSE;
492 		if(strcasecmp(child_elem->name,"SWI_literal") == 0) {
493 			swi_literal = child_elem;
494 			remove = TRUE;
495 		}
496 		else if(strcasecmp(child_elem->name,"SWI_meaning") == 0) {
497 			remove = TRUE;
498 		}
499 
500 		if(remove == TRUE) {
501 			if(child_elem == instance->elem->first_child) {
502 				instance->elem->first_child = child_elem->next;
503 			}
504 			else if(prev_elem) {
505 				prev_elem->next = child_elem->next;
506 			}
507 		}
508 
509 		prev_elem = child_elem;
510 	}
511 
512 	if(APR_XML_ELEM_IS_EMPTY(instance->elem) && swi_literal) {
513 		instance->elem->first_cdata = swi_literal->first_cdata;
514 	}
515 
516 	return TRUE;
517 }
518 
519 /** Generate a plain text content of the instance element */
nlsml_instance_content_generate(const nlsml_instance_t * instance,apr_pool_t * pool)520 APT_DECLARE(const char*) nlsml_instance_content_generate(const nlsml_instance_t *instance, apr_pool_t *pool)
521 {
522 	const char *buf = NULL;
523 	if(instance->elem) {
524 		apr_size_t size;
525 		apr_xml_to_text(pool, instance->elem, APR_XML_X2T_INNER, NULL, NULL, &buf, &size);
526 	}
527 	return buf;
528 }
529 
530 /** Get input element */
nlsml_input_elem_get(const nlsml_input_t * input)531 APT_DECLARE(const apr_xml_elem*) nlsml_input_elem_get(const nlsml_input_t *input)
532 {
533 	return input->elem;
534 }
535 
536 /** Generate a plain text content of the input element */
nlsml_input_content_generate(const nlsml_input_t * input,apr_pool_t * pool)537 APT_DECLARE(const char*) nlsml_input_content_generate(const nlsml_input_t *input, apr_pool_t *pool)
538 {
539 	const char *buf = NULL;
540 	if(input->elem) {
541 		apr_size_t size;
542 		apr_xml_to_text(pool, input->elem, APR_XML_X2T_INNER, NULL, NULL, &buf, &size);
543 	}
544 	return buf;
545 }
546 
547 /** Get input mode */
nlsml_input_mode_get(const nlsml_input_t * input)548 APT_DECLARE(const char*) nlsml_input_mode_get(const nlsml_input_t *input)
549 {
550 	return input->mode;
551 }
552 
553 /** Get input confidence */
nlsml_input_confidence_get(const nlsml_input_t * input)554 APT_DECLARE(float) nlsml_input_confidence_get(const nlsml_input_t *input)
555 {
556 	return input->confidence;
557 }
558 
559 /** Get start of input timestamp */
nlsml_input_timestamp_start_get(const nlsml_input_t * input)560 APT_DECLARE(const char*) nlsml_input_timestamp_start_get(const nlsml_input_t *input)
561 {
562 	return input->timestamp_start;
563 }
564 
565 /** Get end of input timestamp */
nlsml_input_timestamp_end_get(const nlsml_input_t * input)566 APT_DECLARE(const char*) nlsml_input_timestamp_end_get(const nlsml_input_t *input)
567 {
568 	return input->timestamp_end;
569 }
570