1 /* @source ajdas **************************************************************
2 **
3 ** support for DAS sequence data sources
4 **
5 ** @author Copyright (c) 2009 Mahmut Uludag
6 ** @version $Revision: 1.30 $
7 ** @modified $Date: 2012/12/07 10:20:44 $ by $Author: rice $
8 ** @@
9 **
10 ** This library is free software; you can redistribute it and/or
11 ** modify it under the terms of the GNU Lesser General Public
12 ** License as published by the Free Software Foundation; either
13 ** version 2.1 of the License, or (at your option) any later version.
14 **
15 ** This library is distributed in the hope that it will be useful,
16 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 ** Lesser General Public License for more details.
19 **
20 ** You should have received a copy of the GNU Lesser General Public
21 ** License along with this library; if not, write to the Free Software
22 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
23 ** MA  02110-1301,  USA.
24 ******************************************************************************/
25 
26 #include "ajdas.h"
27 
28 #include "ajlib.h"
29 #include "ajfileio.h"
30 #include "ajhttp.h"
31 #include "ajsys.h"
32 #include "ajtime.h"
33 #include "ajtextread.h"
34 
35 #include "expat.h"
36 
37 
38 
39 
40 static void dasRegistryElementstart(void *data, const XML_Char *name,
41                                     const XML_Char **atts);
42 
43 static void dasEntrypointsElementstart(void *data, const XML_Char *name,
44                                        const XML_Char **atts);
45 
46 static AjBool dasQueryParse(AjPFilebuff buff, void* results,
47 			    XML_StartElementHandler starth,
48 			    XML_CharacterDataHandler elementh);
49 
50 static AjBool dasWriteDBdefinition(AjPFile cachef, const AjPDasSource source,
51 				   AjBool titleAndURI);
52 
53 
54 
55 
56 /* @func ajDasServerNew *******************************************************
57 **
58 ** Returns a new DasServer object
59 **
60 ** @return [AjPDasServer] DAS server object
61 **
62 ** @release 6.4.0
63 ******************************************************************************/
64 
ajDasServerNew(void)65 AjPDasServer ajDasServerNew(void)
66 {
67     AjPDasServer ret = NULL;
68 
69     AJNEW0(ret);
70 
71     ret->host  = ajStrNew();
72     ret->path  = ajStrNew();
73     ret->port  = 80;
74 
75     ret->sources = ajListNew();
76 
77     return ret;
78 }
79 
80 
81 
82 
83 /* @func ajDasServerDel *******************************************************
84 **
85 ** Delete DASserver object
86 **
87 ** @param [u] thys [AjPDasServer*] DASserver object
88 ** @return [void]
89 **
90 ** @release 6.4.0
91 ******************************************************************************/
92 
ajDasServerDel(AjPDasServer * thys)93 void ajDasServerDel(AjPDasServer *thys)
94 {
95     AjPDasServer pthis = NULL;
96     AjPDasSource source;
97     AjPDasCoordinate coord;
98     AjIList iter;
99     AjIList iterc;
100 
101     if(!thys)
102         return;
103 
104     if(!*thys)
105         return;
106 
107     pthis = *thys;
108 
109     ajStrDel(&pthis->host);
110     ajStrDel(&pthis->path);
111 
112     iter = ajListIterNew(pthis->sources);
113 
114     while(!ajListIterDone(iter))
115     {
116 	source = ajListIterGet(iter);
117         ajStrDel(&source->uri);
118         ajStrDel(&source->title);
119         ajStrDel(&source->description);
120         ajStrDel(&source->sequence_query_uri);
121         ajStrDel(&source->entry_points_uri);
122         ajStrDel(&source->features_query_uri);
123 
124         iterc = ajListIterNew(source->coordinates);
125 
126         while(!ajListIterDone(iterc))
127         {
128             coord = ajListIterGet(iterc);
129             ajStrDel(&coord->authority);
130             ajStrDel(&coord->source);
131             ajStrDel(&coord->taxid);
132             ajStrDel(&coord->test_range);
133             ajStrDel(&coord->uri);
134             ajStrDel(&coord->version);
135             AJFREE(coord);
136         }
137 
138         ajListFree(&source->coordinates);
139         AJFREE(source);
140         ajListIterDel(&iterc);
141     }
142 
143     ajListIterDel(&iter);
144     ajListFree(&pthis->sources);
145 
146     AJFREE(pthis);
147 
148     *thys = NULL;
149 
150     return;
151 }
152 
153 
154 
155 
156 /* @func ajDasSegmentDel ******************************************************
157 **
158 ** Delete DAS segment objects returned by entry_points queries
159 **
160 ** @param [u] Psegment [AjPDasSegment*] DAS segment object
161 ** @return [void]
162 **
163 ** @release 6.4.0
164 ******************************************************************************/
165 
ajDasSegmentDel(AjPDasSegment * Psegment)166 void ajDasSegmentDel(AjPDasSegment* Psegment)
167 {
168     if(Psegment==NULL || *Psegment==NULL)
169         return;
170 
171     ajStrDel(&(*Psegment)->id);
172     ajStrDel(&(*Psegment)->type);
173     ajStrDel(&(*Psegment)->orientation);
174 
175     AJFREE(*Psegment);
176 
177     return;
178 }
179 
180 
181 
182 
183 /* @funcstatic dasRegistryElementstart ****************************************
184 **
185 ** Handler for reading DAS registry XML elements
186 **
187 ** @param [u] data [void*] structure for storing results
188 ** @param [r] name [const XML_Char*] XML element name
189 ** @param [r] atts [const XML_Char**] array of element attributes,
190 **                                    name/value pairs
191 ** @return [void]
192 **
193 ** @release 6.4.0
194 ******************************************************************************/
195 
dasRegistryElementstart(void * data,const XML_Char * name,const XML_Char ** atts)196 static void dasRegistryElementstart(void *data, const XML_Char *name,
197                                     const XML_Char **atts)
198 {
199     AjPList sources       = NULL;
200     AjPDasSource source   = NULL;
201     const char* key       = NULL;
202     const char* value     = NULL;
203     const char* type      = NULL;
204     const char* query_uri = NULL;
205 
206     ajuint i;
207 
208     sources = (AjPList) data;
209 
210     ajDebug("dasRegistryElementstart: %s\n",name);
211 
212     if(ajCharMatchCaseC("SOURCES",name))
213 	return;
214 
215     if(ajCharMatchCaseC(name,"SOURCE"))
216     {
217 	AJNEW0(source);
218 	source->coordinates = ajListNew();
219 
220 	for(i = 0; atts[i]; i += 2)
221 	{
222 	    key   = atts[i];
223 	    value = atts[i+1];
224 
225 	    if(ajCharMatchC(key,"uri"))
226 		ajStrAssignC(&source->uri,value);
227 	    else if(ajCharMatchC(key,"title"))
228 	    {
229 		ajStrAssignC(&source->title,value);
230 		ajStrTrimWhite(&source->title);
231 	    }
232 	    else if(ajCharMatchC(key,"description"))
233 		ajStrAssignC(&source->description,value);
234 	}
235 
236 	ajListPushAppend(sources,source);
237     }
238     else if(ajCharMatchCaseC(name,"CAPABILITY"))
239     {
240 	ajListPeekLast(sources,(void**)&source);
241 
242 	for(i = 0; atts[i]; i += 2)
243 	{
244 	    key   = atts[i];
245 	    value = atts[i+1];
246 
247 	    if(ajCharMatchC(key,"type"))
248 		type = value;
249 	    else
250 		query_uri = value;
251 	}
252 
253 	if(ajCharMatchCaseC(type,"das1:features"))
254 	{
255 	    source->features = ajTrue;
256 	    ajStrAssignC(&source->features_query_uri,query_uri);
257 	}
258 	else if(ajCharMatchCaseC(type,"das1:sequence"))
259 	{
260 	    source->sequence = ajTrue;
261 	    ajStrAssignC(&source->sequence_query_uri,query_uri);
262 	}
263 	else if(ajCharMatchCaseC(type,"das1:entry_points"))
264 	{
265 	    source->entry_points = ajTrue;
266 	    ajStrAssignC(&source->entry_points_uri,query_uri);
267 	}
268     }
269     else if(ajCharMatchCaseC(name,"COORDINATES"))
270     {
271 	AjPDasCoordinate coord;
272 	AJNEW0(coord);
273 	ajListPeekLast(sources,(void**)&source);
274 
275 	for(i = 0; atts[i]; i += 2)
276 	{
277 	    key   = atts[i];
278 	    value = atts[i+1];
279 
280 	    if(ajCharMatchCaseC(key,"uri"))
281 		ajStrAssignC(&coord->uri, value);
282 	    else if(ajCharMatchCaseC(key,"taxid"))
283 		ajStrAssignC(&coord->taxid, value);
284 	    else if(ajCharMatchCaseC(key,"source"))
285 		ajStrAssignC(&coord->source,value);
286 	    else if(ajCharMatchCaseC(key,"test_range"))
287 		ajStrAssignC(&coord->test_range,value);
288 	}
289 
290 	ajListPushAppend(source->coordinates,coord);
291     }
292     else if(!(ajCharMatchC(name,"MAINTAINER")   ||
293 	    ajCharMatchC(name,"VERSION")       ||
294 	    ajCharMatchC(name,"PROP")
295     ))
296 	ajDebug("DAS 'sources' request returned an unknown tag: %s",name);
297 
298     return;
299 }
300 
301 
302 
303 
304 /* @funcstatic dasEntrypointsElementstart *************************************
305 **
306 ** Handler for reading DAS entry-point XML elements
307 **
308 ** @param [u] data [void*] structure for storing results
309 ** @param [r] name [const XML_Char*] XML element name
310 ** @param [r] atts [const XML_Char**] array of name/value pairs
311 ** @return [void]
312 **
313 ** @release 6.4.0
314 ******************************************************************************/
315 
dasEntrypointsElementstart(void * data,const XML_Char * name,const XML_Char ** atts)316 static void dasEntrypointsElementstart(void *data, const XML_Char *name,
317                                      const XML_Char **atts)
318 {
319     const char* key   = NULL;
320     AjPStr value = NULL;
321 
322     AjPList segments = data;
323     AjPDasSegment segment;
324 
325     ajuint i;
326 
327 
328     ajDebug("dasEntrypointsElementstart: %s\n",name);
329 
330     if(ajCharMatchCaseC("DASEP",name) || ajCharMatchCaseC("ENTRY_POINTS",name))
331     {
332         return;
333     }
334 
335 
336     if(!(ajCharMatchC(name,"SEGMENT")))
337         ajDebug("DAS 'entrypoints' request returned an unknown tag: %s",name);
338 
339     {
340 
341 	AJNEW0(segment);
342 
343 	for(i = 0; atts[i]; i += 2)
344 	{
345 	    key   = atts[i];
346 	    value = ajStrNewC(atts[i+1]);
347 
348 	    if(ajCharMatchC(key,"id"))
349 		ajStrAssignS(&segment->id,value);
350 	    else if(ajCharMatchC(key,"start"))
351 	    {
352 		ajStrToUint(value, &segment->start);
353 	    }
354 	    else if(ajCharMatchC(key,"stop"))
355 	    {
356 		ajStrToUint(value, &segment->stop);
357 	    }
358 	    else if(ajCharMatchC(key,"subparts"))
359 	    {
360 		ajStrToBool(value, &segment->subparts);
361 	    }
362 	    else if(ajCharMatchC(key,"orientation"))
363 	    {
364 		ajStrAssignS(&segment->orientation,value);
365 	    }
366 	    else if(ajCharMatchC(key,"type"))
367 	    {
368 		ajStrAssignS(&segment->type,value);
369 	    }
370 
371 	    ajStrDel(&value);
372 	}
373 
374 	ajListPushAppend(segments,segment);
375     }
376 
377 
378     return;
379 }
380 
381 
382 
383 
384 /* @func ajDasParseRegistry ***************************************************
385 **
386 ** Parses XML list of DAS sources, either coming from dasregistry.org
387 ** or from individual DAS servers supporting DAS 'sources' command
388 **
389 ** @param [u] buff [AjPFilebuff] XML input stream
390 ** @param [u] sources [AjPList] pointer to the data structure for storing
391 **                              parser results of DAS/XML 'source' objects
392 ** @return [AjBool] True on success
393 **
394 ** @release 6.4.0
395 ******************************************************************************/
396 
ajDasParseRegistry(AjPFilebuff buff,AjPList sources)397 AjBool ajDasParseRegistry(AjPFilebuff buff, AjPList sources)
398 {
399 
400     return dasQueryParse(buff, sources, dasRegistryElementstart, NULL);
401 }
402 
403 
404 
405 
406 /* @func ajDasParseEntrypoints ************************************************
407 **
408 ** Parses XML list of DAS entry-points, either coming from dasregistry.org
409 ** or from individual DAS servers supporting DAS 'entry_points' command
410 **
411 ** @param [u] buff [AjPFilebuff] XML input stream
412 ** @param [u] segments [AjPList] pointer to the data structure for storing
413 **                               parser results of DAS/XML 'segment' objects
414 ** @return [AjBool] True on success
415 **
416 ** @release 6.4.0
417 ******************************************************************************/
418 
ajDasParseEntrypoints(AjPFilebuff buff,AjPList segments)419 AjBool ajDasParseEntrypoints(AjPFilebuff buff, AjPList segments)
420 {
421 
422     return dasQueryParse(buff, segments, dasEntrypointsElementstart, NULL);
423 }
424 
425 
426 
427 
428 /* @func ajDasServerSethostS **************************************************
429 **
430 ** Set the DAS server host name
431 **
432 ** @param [u] server [AjPDasServer]  DASserver object
433 ** @param [r] host [const AjPStr] DAS server host name
434 ** @return [AjBool] True on success
435 **
436 ** @release 6.4.0
437 ******************************************************************************/
438 
ajDasServerSethostS(AjPDasServer server,const AjPStr host)439 AjBool ajDasServerSethostS(AjPDasServer server, const AjPStr host)
440 {
441     ajStrAssignS(&server->host, host);
442 
443     return ajTrue;
444 }
445 
446 
447 
448 
449 /* @func ajDasServerSetpathS **************************************************
450 **
451 ** Set the DAS server URL path
452 **
453 ** @param [u] server [AjPDasServer]  DASserver object
454 ** @param [r] path [const AjPStr] DAS server URL path
455 ** @return [AjBool] True on success
456 **
457 ** @release 6.4.0
458 ******************************************************************************/
459 
ajDasServerSetpathS(AjPDasServer server,const AjPStr path)460 AjBool ajDasServerSetpathS(AjPDasServer server, const AjPStr path)
461 {
462     ajStrAssignS(&server->path, path);
463 
464     return ajTrue;
465 }
466 
467 
468 
469 
470 /* @func ajDasServerSetport ***************************************************
471 **
472 ** Set the DAS server port
473 **
474 ** @param [u] server [AjPDasServer]  DASserver object
475 ** @param [r] port [ajuint] DAS server port
476 ** @return [AjBool] True on success
477 **
478 ** @release 6.4.0
479 ******************************************************************************/
480 
ajDasServerSetport(AjPDasServer server,ajuint port)481 AjBool ajDasServerSetport(AjPDasServer server, ajuint port)
482 {
483     server->port = port;
484 
485     return ajTrue;
486 }
487 
488 
489 
490 
491 /* @func ajDasServerGetSources ************************************************
492 **
493 ** Makes an http request for getting either list of all DAS sources
494 ** or a particular DAS source on a given DAS server. Http response is parsed
495 ** and results are stored in server object's 'sources' attribute
496 **
497 ** @param [u] server [AjPDasServer] DAS server object
498 ** @param [r] cmd [const AjPStr] DAS 'sources' command, queries either
499 **                         all DAS sources or a particular DAS source
500 ** @return [void]
501 **
502 ** @release 6.4.0
503 ******************************************************************************/
504 
ajDasServerGetSources(AjPDasServer server,const AjPStr cmd)505 void ajDasServerGetSources(AjPDasServer server, const AjPStr cmd)
506 {
507     AjPStr cmdpath   = ajStrNew();
508     AjPStr httpver   = ajStrNew();
509     AjPStr dbname    = ajStrNew();
510     AjPStr dbproxy   = ajStrNew();
511     AjPTextin textin = ajTextinNewDatatype(AJDATATYPE_TEXT);
512 
513     if(ajStrGetCharLast(server->path)!='/')
514 	ajFmtPrintS(&cmdpath, "%S/%S", server->path, cmd);
515     else
516 	ajFmtPrintS(&cmdpath, "%S%S", server->path, cmd);
517 
518     ajFilebuffDel(&textin->Filebuff);
519 
520     textin->Filebuff = ajHttpRead(httpver, dbname, dbproxy,
521                                   server->host, server->port, cmdpath);
522 
523     if(textin->Filebuff)
524     {
525 	ajFilebuffHtmlNoheader(textin->Filebuff);
526 	ajDasParseRegistry(textin->Filebuff, server->sources);
527     }
528 
529     ajTextinDel(&textin);
530     ajStrDel(&cmdpath);
531     ajStrDel(&httpver);
532     ajStrDel(&dbname);
533     ajStrDel(&dbproxy);
534 }
535 
536 
537 
538 
539 /* @func ajDasGetSequenceQueryURI *********************************************
540 **
541 ** Given a DAS server URL, returns sequence query URI for the DAS source
542 ** specified in user query.
543 **
544 ** It first makes a DAS source command/query for the specific DAS source.
545 ** If above fails then makes another query for all DAS sources
546 ** and checks them against the current DAS source.
547 **
548 ** @param [r] qry [const AjPQuery] sequence query object
549 ** @param [r] sourceURIorTitle [const AjPStr] DAS source URI or title
550 **                                      specified in current query
551 ** @return [AjPStr] sequence-query URI, null if not found
552 **
553 ** @release 6.4.0
554 ******************************************************************************/
555 
ajDasGetSequenceQueryURI(const AjPQuery qry,const AjPStr sourceURIorTitle)556 AjPStr ajDasGetSequenceQueryURI(const AjPQuery qry,
557 				  const AjPStr sourceURIorTitle)
558 {
559     AjPDasServer server = NULL;
560     AjPDasSource source = NULL;
561     AjPStr host  = NULL;
562     AjPStr path  = NULL;
563     AjPStr cmd   = NULL;
564     AjIList iter = NULL;
565 
566     AjPStr ret = NULL;
567     ajint port = 80;
568     AjBool validresponse = ajTrue;
569 
570     ajDebug("ajDasGetSequenceQueryURI: searching query URI for '%S'",
571 	    sourceURIorTitle);
572 
573     if(!ajHttpQueryUrl(qry, &port, &host, &path))
574     {
575 	ajStrDel(&host);
576 	ajStrDel(&path);
577 	return ajFalse;
578     }
579 
580     server = ajDasServerNew();
581 
582     ajDasServerSethostS(server,host);
583     ajDasServerSetport(server,port);
584     ajDasServerSetpathS(server,path);
585 
586     /* first try direct source command */
587 
588     ajFmtPrintS(&cmd, "sources/%S", sourceURIorTitle);
589     ajDasServerGetSources(server, cmd);
590 
591     if (ajListGetLength(server->sources)==1)
592     {
593 	ajListPeekFirst(server->sources, (void**)&source);
594 
595 	if(source == NULL || ajListGetLength(source->coordinates)==0)
596 	{
597 	    ajDebug("DAS data source query '%S' didn't return a valid result",
598 	           cmd);
599 	    validresponse = ajFalse;
600 	}
601 	else if(source->sequence == ajFalse)
602 	{
603 	    ajErr("DAS data source (%S,%S) doesn't support sequence queries",
604 	           source->uri, source->title);
605 	    ajStrDel(&host);
606 	    ajStrDel(&path);
607 	    ajDasServerDel(&server);
608 	    ajStrDel(&cmd);
609 
610 	    return NULL;
611 	}
612 
613 	if(validresponse && ajStrMatchS(sourceURIorTitle, source->uri))
614 	{
615 	    if(source->sequence_query_uri!=NULL)
616 	    {
617 		ret = ajStrNewRef(source->sequence_query_uri);
618 		ajDebug("dasGetSequenceQueryURI: DAS source found "
619 			"by direct query '%S'",	ret);
620 	    }
621 	    else
622 	    {
623 		//TODO: here we can try building the query_uri unless host
624 		// is dasregistry.....
625 		ajFmtPrintS(&ret, "%S",host);
626 		ajWarn("server source description didn't have a query_uri...");
627 	    }
628 	}
629     }
630 
631     if(ret==NULL)
632     {
633 	ajDebug("dasGetSequenceQueryURI: trying a full 'sources' query");
634 	ajStrAssignC(&cmd, "sources");
635 	ajDasServerGetSources(server, cmd);
636 
637 	iter = ajListIterNew(server->sources);
638 
639 	while(!ajListIterDone(iter))
640 	{
641 	    source = ajListIterGet(iter);
642 
643 	    if((ajStrMatchS(sourceURIorTitle, source->title) ||
644 		    ajStrMatchS(sourceURIorTitle, source->uri)))
645 	    {
646 		if(source->sequence == ajFalse)
647 		{
648 		    ajErr("DAS data source (%S,%S) doesn't support "
649 			    "sequence queries", source->uri, source->title);
650 		    ajStrDel(&host);
651 		    ajStrDel(&path);
652 		    ajDasServerDel(&server);
653 		    ajStrDel(&cmd);
654 		    ajListIterDel(&iter);
655 
656 		    return NULL;
657 		}
658 
659 		ret = ajStrNewRef(source->sequence_query_uri);
660 		ajDebug("ajDasGetSequenceQueryURI: URI found '%S'", ret);
661 		break;
662 	    }
663 	}
664 
665 	ajListIterDel(&iter);
666     }
667 
668     ajStrDel(&host);
669     ajStrDel(&path);
670     ajStrDel(&cmd);
671 
672     ajDasServerDel(&server);
673 
674     return ret;
675 }
676 
677 
678 
679 
680 /* @funcstatic dasQueryParse **************************************************
681 **
682 ** Parses DAS query responses
683 **
684 ** @param [u] buff [AjPFilebuff] buffer object holding DAS query response
685 ** @param [u] results [void*] object for storing parser output
686 ** @param [u] starth [XML_StartElementHandler] handler for processing
687 **                                             XML element attributes
688 ** @param [u] elementh [XML_CharacterDataHandler] handler for processing
689 **                                                XML element data
690 ** @return [AjBool] True on success
691 **
692 ** @release 6.4.0
693 ******************************************************************************/
694 
dasQueryParse(AjPFilebuff buff,void * results,XML_StartElementHandler starth,XML_CharacterDataHandler elementh)695 static AjBool dasQueryParse(AjPFilebuff buff, void* results,
696 			    XML_StartElementHandler starth,
697 			    XML_CharacterDataHandler elementh)
698 {
699     XML_Parser parser = NULL;
700     AjPStr line       = NULL;
701 
702     int done;
703     size_t len;
704 
705     if(!buff)
706 	return ajFalse;
707 
708     line = ajStrNew();
709     parser = XML_ParserCreate(NULL);
710 
711     XML_SetElementHandler(parser, starth, NULL);
712 
713     if(elementh!=NULL)
714 	XML_SetCharacterDataHandler(parser, elementh);
715 
716     XML_SetUserData(parser, results);
717 
718     done = 0;
719 
720     do
721     {
722 	ajBuffreadLine(buff,&line);
723 	done = ajFilebuffIsEmpty(buff);
724 	len = ajStrGetLen(line);
725 
726 	if(!XML_Parse(parser, line->Ptr, len, done))
727 	{
728 	    ajErr("Failed to parse received DAS response at line %d: '%s'",
729 		    XML_GetCurrentLineNumber(parser),
730 		    XML_ErrorString(XML_GetErrorCode(parser)));
731 
732 	    XML_ParserFree(parser);
733 	    ajStrDel(&line);
734 
735 	    return ajFalse;
736 	}
737 
738     } while (!done);
739 
740     XML_ParserFree(parser);
741     ajStrDel(&line);
742 
743     return ajTrue;
744 }
745 
746 
747 
748 
749 /* @func ajDasSourceGetDBname *************************************************
750 **
751 ** Returns an EMBOSS DB name for a given DAS source
752 **
753 ** @param [r] source [const AjPDasSource] DAS source object
754 ** @param [r] titleAndURI [AjBool] Include URI in name
755 ** @return [AjPStr] DB name
756 **
757 ** @release 6.4.0
758 ** @@
759 ******************************************************************************/
760 
ajDasSourceGetDBname(const AjPDasSource source,AjBool titleAndURI)761 AjPStr ajDasSourceGetDBname(const AjPDasSource source, AjBool titleAndURI)
762 {
763     AjPStr dbname = NULL;
764 
765     if (titleAndURI)
766     {
767 	dbname = ajStrNewS(source->uri);
768 
769 	ajStrAppendK(&dbname, '_');
770 
771 	ajStrAppendS(&dbname, source->title);
772     }
773     else
774 	dbname = ajStrNewS(source->title);
775 
776     ajStrExchangeSetCC(&dbname, " \":+-()./", "_________");
777 
778     ajStrTrimC(&dbname, "_");
779 
780     ajStrTrimStartC(&dbname, "0123456789");
781 
782     return dbname;
783 }
784 
785 
786 
787 
788 /* @func ajDasPrintCachefile **************************************************
789 **
790 ** Prints DB definition for the specified DAS server object
791 **
792 ** @param [r] server [const AjPDasServer] DAS server object
793 ** @param [w] cachef [AjPFile] server cachefile with DB definitions
794 ** @return [void]
795 **
796 ** @release 6.4.0
797 ** @@
798 ******************************************************************************/
799 
ajDasPrintCachefile(const AjPDasServer server,AjPFile cachef)800 void ajDasPrintCachefile(const AjPDasServer server, AjPFile cachef)
801 {
802     AjPTime today = NULL;
803     AjIList iter  = NULL;
804     AjPStr fname  = NULL;
805 
806     AjPTable titlecount = NULL;
807     AjPDasSource source = NULL;
808 
809     AjPStr title = NULL;
810 
811     ajuint* count = NULL;
812 
813     titlecount = ajTablestrNewCaseConst(ajListGetLength(server->sources)+20);
814 
815     fname  = ajStrNewS(ajFileGetPrintnameS(cachef));
816 
817     ajFilenameTrimPath(&fname);
818 
819     today =  ajTimeNewTodayFmt("cachefile");
820 
821     ajFmtPrintF(cachef,"# %S %D\n\n", fname, today);
822 
823     ajStrDel(&fname);
824     ajTimeDel(&today);
825 
826     iter = ajListIterNew(server->sources);
827 
828     while(!ajListIterDone(iter))
829     {
830 	source = ajListIterGet(iter);
831 
832 	title = source->title;
833 
834 	count = ajTableFetchmodS(titlecount,title);
835 
836 	if (count != NULL)
837         {
838 	    (*count)++;
839         }
840 	else
841 	{
842 	    AJNEW(count);
843 	    *count = 1;
844             ajTablePut(titlecount, title, (void*)count);
845 	}
846 
847     }
848 
849     ajListIterRewind(iter);
850 
851 
852     while(!ajListIterDone(iter))
853     {
854 	source = ajListIterGet(iter);
855 
856 	title = source->title;
857 
858 	count = ajTableFetchmodS(titlecount,title);
859 
860 	dasWriteDBdefinition(cachef, source, *count>1);
861     }
862 
863     ajListIterDel(&iter);
864     ajTableDelValdel(&titlecount, &ajMemFree);
865 
866     return;
867 }
868 
869 
870 
871 
872 /* @funcstatic dasWriteDBdefinition *******************************************
873 **
874 ** Writes a DAS source DB definition to the specified cache-file
875 **
876 ** @param [u] cachef [AjPFile] cache file
877 ** @param [r] source [const AjPDasSource] DAS source object
878 ** @param [r] titleAndURI [AjBool] Include URI in name
879 ** @return [AjBool] True on success
880 **
881 ** @release 6.4.0
882 ** @@
883 ******************************************************************************/
884 
dasWriteDBdefinition(AjPFile cachef,const AjPDasSource source,AjBool titleAndURI)885 static AjBool dasWriteDBdefinition(AjPFile cachef, const AjPDasSource source,
886 				   AjBool titleAndURI)
887 {
888     AjPStr entry = NULL;
889     ajint ibegin = 0;
890     ajint iend   = 0;
891     ajlong i     = 0;
892 
893     AjPDasCoordinate coord = NULL;
894     AjIList coordsi = NULL;
895 
896     AjPStr dbname  = NULL;
897     AjPStr comment = NULL;
898     AjPStr type    = NULL;
899     AjPStr format  = NULL;
900     AjPStr url     = NULL;
901     AjPStr example = NULL;
902     AjPStr taxon   = NULL;
903     AjPStr fields  = NULL;
904 
905     if(!source->sequence && !source->features)
906 	return ajFalse;
907 
908     dbname = ajDasSourceGetDBname(source, titleAndURI);
909     comment = ajStrNewS(source->description);
910 
911     ajStrExchangeKK(&comment, '"', '\'');
912     ajStrRemoveWhiteExcess(&comment);
913 
914     coordsi = ajListIterNew(source->coordinates);
915 
916     type = ajStrNew();
917     format = ajStrNew();
918     url = ajStrNew();
919     taxon = ajStrNew();
920 
921     while(!ajListIterDone(coordsi))
922     {
923 	coord = ajListIterGet(coordsi);
924 
925 	example = ajDasTestrangeParse(coord->test_range, &entry,
926 	                              &ibegin, &iend);
927 
928 	if (source->sequence)
929 	{
930 	    if(ajStrFindCaseC(coord->source, "nucleotide")!=-1 ||
931 		    ajStrFindCaseC(coord->source, "chromosome")!=-1 ||
932 		    ajStrFindCaseC(coord->source, "contig")!=-1 ||
933 		    ajStrFindCaseC(coord->source, "scaffold")!=-1)
934 		ajStrAssignC(&type, "Nucleotide");
935 	    else if(ajStrFindCaseC(coord->source, "Protein")!=-1)
936 		ajStrAssignC(&type,"Protein");
937 	    else
938 		ajStrAssignC(&type,"Sequence");
939 
940 	    ajStrAssignC(&format, "das");
941 
942 	    ajStrAssignS(&url, source->sequence_query_uri);
943 
944             if(!ajStrPrefixCaseC(url, "http://"))
945                 ajStrInsertC(&url, 0, "http://");
946 
947 	    i = ajStrFindlastC(url,"/sequence");
948 
949 	    if(i != -1)
950 	      ajStrCutEnd(&url,ajStrGetLen(url) - (size_t) i);
951 
952 	}
953 
954 	if (source->features)
955 	{
956 	    if(source->sequence)
957 	    {
958 		ajStrAppendC(&type,", ");
959 		ajStrAppendC(&format,", ");
960 	    }
961 
962 	    if(ajStrFindCaseC(coord->source, "nucleotide")!=-1 ||
963 		    ajStrFindCaseC(coord->source, "chromosome")!=-1 ||
964 		    ajStrFindCaseC(coord->source, "contig")!=-1 ||
965 		    ajStrFindCaseC(coord->source, "scaffold")!=-1)
966 		ajStrAppendC(&type, "Nucfeatures");
967 	    else if(ajStrFindCaseC(coord->source, "Protein")!=-1)
968 		ajStrAppendC(&type, "Protfeatures");
969 	    else
970 		ajStrAppendC(&type,"Features");
971 
972 	    ajStrAppendC(&format, "dasgff");
973 
974 	    if(ajStrGetLen(url) == 0)
975 	    {
976 		ajStrAssignS(&url, source->features_query_uri);
977 
978                 if(!ajStrPrefixCaseC(url, "http://"))
979                     ajStrInsertC(&url, 0, "http://");
980 
981 		i = ajStrFindlastC(url,"/features");
982 
983 		if(i != -1)
984 		  ajStrCutEnd(&url,ajStrGetLen(url)- (size_t) i);
985 
986 	    }
987 
988 	    fields = ajStrNewC("segment,type,category,categorize,feature_id");
989 	}
990 	else
991 	    fields = ajStrNewC("segment");
992 
993 	if (coord->taxid)
994 	    ajFmtPrintS(&taxon, "  taxon: \"%S\"\n", coord->taxid);
995 
996 	ajFmtPrintF(cachef,
997 		"DBNAME %S [\n"
998 		"  method: \"das\"\n"
999 		"  type: \"%S\"\n"
1000 		"%S"
1001 		"  format: \"%S\"\n"
1002 		"  url: \"%S\"\n"
1003 		"  example: \"%S\"\n"
1004 		"  comment: \"%S\"\n"
1005 		"  hasaccession: \"N\"\n"
1006 		"  identifier: \"segment\"\n"
1007 		"  fields: \"%S\"\n"
1008 		"]\n\n",
1009 		dbname, type,
1010 		taxon,
1011 		format, url,
1012 		example,
1013 		comment,
1014 		fields);
1015 
1016 	break; /* TODO: multiple coordinates */
1017     }
1018 
1019     ajStrDel(&dbname);
1020     ajStrDel(&comment);
1021     ajStrDel(&type);
1022     ajStrDel(&format);
1023     ajStrDel(&url);
1024     ajStrDel(&url);
1025     ajStrDel(&example);
1026     ajStrDel(&taxon);
1027     ajStrDel(&fields);
1028 
1029     ajStrDel(&entry);
1030 
1031     ajListIterDel(&coordsi);
1032 
1033     return ajTrue;
1034 }
1035 
1036 
1037 
1038 
1039 /* @func ajDasTestrangeParse **************************************************
1040 **
1041 ** Parses a DAS test_range attribute and returns sequence identifier, and
1042 ** 'begin' and 'end' positions if any.
1043 **
1044 ** @param [r] testrange [const AjPStr] DAS test_range string
1045 ** @param [w] id [AjPStr*] sequence identifier
1046 ** @param [w] ibegin [ajint*] sequence begin position
1047 ** @param [w] iend [ajint*] sequence end position
1048 ** @return [AjPStr] returns the example query in EMBOSS USA syntax
1049 **
1050 ** @release 6.4.0
1051 ** @@
1052 ******************************************************************************/
1053 
ajDasTestrangeParse(const AjPStr testrange,AjPStr * id,ajint * ibegin,ajint * iend)1054 AjPStr ajDasTestrangeParse(const AjPStr testrange, AjPStr* id,
1055                            ajint* ibegin, ajint* iend)
1056 {
1057     AjPStr tmp=NULL;
1058     AjPStrTok token;
1059     AjPStr ret = NULL;
1060 
1061     *ibegin = 0;
1062     *iend = 0;
1063 
1064     /* Parse the test_range string (identifier:begin,end) */
1065 
1066     token = ajStrTokenNewC(testrange, ":,");
1067 
1068     ret = ajStrNew();
1069 
1070     if(!(ajStrTokenNextParseNoskip(token, id)))
1071     {
1072 	ajStrTokenDel(&token);
1073 	return ret;
1074     }
1075 
1076     if(ajStrTokenNextParseNoskip(token, &tmp))
1077     {
1078 	ajStrToInt(tmp, ibegin);
1079 
1080 	if(ajStrTokenNextParseNoskip(token, &tmp))
1081 	{
1082 	    ajStrToInt(tmp, iend);
1083 	    ajFmtPrintS(&ret,"%S[%d:%d]", *id, *ibegin, *iend);
1084 	}
1085     }
1086 
1087     if(*iend == 0)
1088 	ajStrAssignRef(&ret, *id);
1089 
1090     ajStrTokenDel(&token);
1091     ajStrDel(&tmp);
1092 
1093     return ret;
1094 }
1095