1/*********************************************************************
2 *   Copyright 1993, UCAR/Unidata
3 *   See netcdf/COPYRIGHT file for copying and redistribution conditions.
4 *********************************************************************/
5
6#include "ncdap.h"
7#include "daputil.h"
8#include "dapdump.h"
9
10#ifdef DAPDEBUG
11extern char* ocfqn(OCddsnode);
12#endif
13
14CDFnodev4node = NULL;
15
16/* Forward*/
17static NCerror sequencecheckr(CDFnodenodeNClistvarsCDFnodetopseq);
18static NCerror restructr(NCDAPCOMMON*, CDFnode*, CDFnode*, NClist*);
19static NCerror repairgrids(NCDAPCOMMON*, NClist*);
20static NCerror structwrap(NCDAPCOMMON*, CDFnode*, CDFnode*, int, CDFnode*, int);
21static int findin(CDFnodeparentCDFnodechild);
22static CDFnodemakenewstruct(NCDAPCOMMON*, CDFnode*, CDFnode*);
23static NCerror mapnodesr(CDFnode*, CDFnode*, int depth);
24static NCerror mapfcn(CDFnodedstnodeCDFnodesrcnode);
25static NCerror definedimsetplus(NCDAPCOMMONnccommCDFnodenode);
26static NCerror definedimsetall(NCDAPCOMMONnccommCDFnodenode);
27static NCerror definetransdimset(NCDAPCOMMONnccommCDFnodenode);
28static NCerror definedimsettransR(NCDAPCOMMONnccommCDFnodenode);
29static NCerror definedimsetsR(NCDAPCOMMONnccommCDFnodenode);
30static NCerror buildcdftreer(NCDAPCOMMON*, OCddsnodeCDFnode*, CDFtree*, CDFnode**);
31static void free1cdfnode(CDFnodenode);
32static NCerror fixnodes(NCDAPCOMMON*, NClistcdfnodes);
33static void defdimensions(OCddsnode ocnodeCDFnodecdfnodeNCDAPCOMMONnccommCDFtreetree);
34
35/* Accumulate useful node sets  */
36NCerror
37computecdfnodesets(NCDAPCOMMONnccommCDFtreetree)
38{
39    unsigned int i;
40    NClistvarnodes;
41    NClistallnodes;
42
43    allnodes = tree->nodes;
44    varnodes = nclistnew();
45
46    if(tree->seqnodes == NULLtree->seqnodes = nclistnew();
47    if(tree->gridnodes == NULLtree->gridnodes = nclistnew();
48    nclistclear(tree->seqnodes);
49    nclistclear(tree->gridnodes);
50
51    computevarnodes(nccomm,allnodes,varnodes);
52    nclistfree(tree->varnodes);
53    tree->varnodes = varnodes;
54    varnodes = NULL;
55
56    /* Now compute other sets of interest */
57    for(i=0;i<nclistlength(allnodes);i++) {
58 CDFnodenode = (CDFnode*)nclistget(allnodes,i);
59 switch (node->nctype) {
60 case NC_Sequence:
61     nclistpush(tree->seqnodes,(void*)node);
62     break;
63 case NC_Grid:
64     nclistpush(tree->gridnodes,(void*)node);
65     break;
66 default: break;
67 }
68    }
69    return NC_NOERR;
70}
71
72NCerror
73computevarnodes(NCDAPCOMMONnccommNClistallnodesNClistvarnodes)
74{
75    unsigned int i,len;
76    NClistallvarnodes = nclistnew();
77    for(i=0;i<nclistlength(allnodes);i++) {
78 CDFnodenode = (CDFnode*)nclistget(allnodes,i);
79#if 0
80 /* If this node has a bad name, repair it */
81 if(dap_badname(node->ocname)) {
82     char* newname = dap_repairname(node->ocname);
83     nullfree(node->ocname);
84     node->ocname = newname;
85 }
86#endif
87 if(node->nctype == NC_Atomic)
88     nclistpush(allvarnodes,(void*)node);
89    }
90    /* Further process the variable nodes to get the final set */
91    /* Use toplevel vars first */
92    len = nclistlength(allvarnodes);
93    for(i=0;i<len;i++) {
94 CDFnodenode = (CDFnode*)nclistget(allvarnodes,i);
95 if(node == NULL) continue;
96        if(daptoplevel(node)) {
97     nclistpush(varnodes,(void*)node);
98     nclistset(allvarnodes,i,(void*)NULL);
99 }
100    }
101    /*... then grid arrays and maps.
102      but exclude the coordinate variables if we are trying to
103      exactly mimic nc-dap
104    */
105    for(i=0;i<len;i++) {
106 CDFnodenode = (CDFnode*)nclistget(allvarnodes,i);
107 if(node == NULL) continue;
108 if(dapgridarray(node)) {
109     nclistpush(varnodes,(void*)node);
110     nclistset(allvarnodes,i,(void*)NULL);
111        } else if(dapgridmap(node)) {
112     if(!FLAGSET(nccomm->controls,NCF_NCDAP))
113 nclistpush(varnodes,(void*)node);
114     nclistset(allvarnodes,i,(void*)NULL);
115 }
116    }
117    /*... then all others */
118    for(i=0;i<len;i++) {
119 CDFnodenode = (CDFnode*)nclistget(allvarnodes,i);
120 if(node == NULL) continue;
121        nclistpush(varnodes,(void*)node);
122    }
123    nclistfree(allvarnodes);
124#ifdef DEBUG2
125for(i=0;i<nclistlength(varnodes);i++) {
126CDFnodenode = (CDFnode*)nclistget(varnodes,i);
127if(node == NULL) continue;
128fprintf(stderr,"computevarnodes: var: %s\n",makecdfpathstring(node,"."));
129}
130#endif
131    return NC_NOERR;
132}
133
134
135
136NCerror
137fixgrid(NCDAPCOMMONnccommCDFnodegrid)
138{
139    unsigned int i,glen;
140    CDFnodearray;
141
142    glen = nclistlength(grid->subnodes);
143    array = (CDFnode*)nclistget(grid->subnodes,0);
144    if(nccomm->controls.flags & (NCF_NC3)) {
145        /* Rename grid Array: variable, but leave its oc base name alone */
146        nullfree(array->ncbasename);
147        array->ncbasename = nulldup(grid->ncbasename);
148        if(!array->ncbasename) return NC_ENOMEM;
149    }
150    /* validate and modify the grid structure */
151    if((glen-1) != nclistlength(array->array.dimset0)) goto invalid;
152    for(i=1;i<glen;i++) {
153 CDFnodearraydim = (CDFnode*)nclistget(array->array.dimset0,i-1);
154 CDFnodemap = (CDFnode*)nclistget(grid->subnodes,i);
155 CDFnodemapdim;
156 /* map must have 1 dimension */
157 if(nclistlength(map->array.dimset0) != 1) goto invalid;
158 /* and the map name must match the ith array dimension */
159 if(arraydim->ocname != NULL && map->ocname != NULL
160    && strcmp(arraydim->ocname,map->ocname) != 0)
161     goto invalid;
162 /* and the map name must match its dim name (if any) */
163 mapdim = (CDFnode*)nclistget(map->array.dimset0,0);
164 if(mapdim->ocname != NULL && map->ocname != NULL
165    && strcmp(mapdim->ocname,map->ocname) != 0)
166     goto invalid;
167 /* Add appropriate names for the anonymous dimensions */
168 /* Do the map name first, so the array dim may inherit */
169 if(mapdim->ocname == NULL) {
170     nullfree(mapdim->ncbasename);
171     mapdim->ocname = nulldup(map->ocname);
172     if(!mapdim->ocname) return NC_ENOMEM;
173     mapdim->ncbasename = cdflegalname(mapdim->ocname);
174     if(!mapdim->ncbasename) return NC_ENOMEM;
175 }
176 if(arraydim->ocname == NULL) {
177     nullfree(arraydim->ncbasename);
178     arraydim->ocname = nulldup(map->ocname);
179     if(!arraydim->ocname) return NC_ENOMEM;
180     arraydim->ncbasename = cdflegalname(arraydim->ocname);
181     if(!arraydim->ncbasename) return NC_ENOMEM;
182 }
183        if(FLAGSET(nccomm->controls,(NCF_NCDAP|NCF_NC3))) {
184     char tmp[3*NC_MAX_NAME];
185            /* Add the grid name to the basename of the map */
186     snprintf(tmp,sizeof(tmp),"%s%s%s",map->container->ncbasename,
187   nccomm->cdf.separator,
188   map->ncbasename);
189     nullfree(map->ncbasename);
190            map->ncbasename = nulldup(tmp);
191     if(!map->ncbasename) return NC_ENOMEM;
192 }
193    }
194    return NC_NOERR;
195invalid:
196    return NC_EINVAL; /* mal-formed grid */
197}
198
199NCerror
200fixgrids(NCDAPCOMMONnccomm)
201{
202    unsigned int i;
203    NClistgridnodes = nccomm->cdf.ddsroot->tree->gridnodes;
204
205    for(i=0;i<nclistlength(gridnodes);i++) {
206        CDFnodegrid = (CDFnode*)nclistget(gridnodes,i);
207        (void)fixgrid(nccomm,grid);
208 /* Ignore mal-formed grids */
209    }
210    return NC_NOERR;
211}
212
213/*
214Figure out the names for variables.
215*/
216NCerror
217computecdfvarnames(NCDAPCOMMONnccommCDFnoderootNClistvarnodes)
218{
219    unsigned int i,j,d;
220
221    /* clear all elided marks; except for dataset and grids */
222    for(i=0;i<nclistlength(root->tree->nodes);i++) {
223 CDFnodenode = (CDFnode*)nclistget(root->tree->nodes,i);
224 node->elided = 0;
225 if(node->nctype == NC_Grid || node->nctype == NC_Dataset)
226     node->elided = 1;
227    }
228
229    /* ensure all variables have an initial full name defined */
230    for(i=0;i<nclistlength(varnodes);i++) {
231 CDFnodevar = (CDFnode*)nclistget(varnodes,i);
232 nullfree(var->ncfullname);
233 var->ncfullname = makecdfpathstring(var,nccomm->cdf.separator);
234#ifdef DEBUG2
235fprintf(stderr,"var names: %s %s %s\n",
236 var->ocname,var->ncbasename,var->ncfullname);
237#endif
238    }
239
240    /*  unify all variables with same fullname and dimensions
241 basevar fields says: "for duplicate grid variables";
242        when does this happen?
243    */
244    if(FLAGSET(nccomm->controls,NCF_NC3)) {
245        for(i=0;i<nclistlength(varnodes);i++) {
246     int match;
247     CDFnodevar = (CDFnode*)nclistget(varnodes,i);
248     for(j=0;j<i;j++) {
249         CDFnodetestnode = (CDFnode*)nclistget(varnodes,j);
250 match = 1;
251         if(testnode->array.basevar != NULL)
252     continue; /* already processed */
253         if(strcmp(var->ncfullname,testnode->ncfullname) != 0)
254     match = 0;
255 else if(nclistlength(testnode->array.dimsetall)
256 != nclistlength(var->array.dimsetall))
257     match = 0;
258         else for(d=0;d<nclistlength(testnode->array.dimsetall);d++) {
259     CDFnodevdim = (CDFnode*)nclistget(var->array.dimsetall,d);
260     CDFnodetdim = (CDFnode*)nclistget(testnode->array.dimsetall,d);
261             if(vdim->dim.declsize != tdim->dim.declsize) {
262         match = 0;
263 break;
264     }
265 }
266 if(match) {
267     testnode->array.basevar = var;
268fprintf(stderr,"basevar invoked: %s\n",var->ncfullname);
269 }
270     }
271 }
272    }
273
274    /* Finally, verify unique names */
275    for(i=0;i<nclistlength(varnodes);i++) {
276 CDFnodevar1 = (CDFnode*)nclistget(varnodes,i);
277 if(var1->array.basevar != NULL) continue;
278 for(j=0;j<i;j++) {
279     CDFnodevar2 = (CDFnode*)nclistget(varnodes,j);
280     if(var2->array.basevar != NULL) continue;
281     if(strcmp(var1->ncfullname,var2->ncfullname)==0) {
282 PANIC1("duplicate var names: %s",var1->ncfullname);
283     }
284 }
285    }
286    return NC_NOERR;
287}
288
289
290/* locate and connect usable sequences and vars.
291A sequence is usable iff:
2921. it has a path from one of its subnodes to a leaf and that
293   path does not contain a sequence.
2942. No parent container has dimensions.
295*/
296
297NCerror
298sequencecheck(NCDAPCOMMONnccomm)
299{
300    (void)sequencecheckr(nccomm->cdf.ddsroot,
301                          nccomm->cdf.ddsroot->tree->varnodes,NULL);
302    return NC_NOERR;
303}
304
305
306static NCerror
307sequencecheckr(CDFnodenodeNClistvarsCDFnodetopseq)
308{
309    unsigned int i;
310    NCerror err = NC_NOERR;
311    int ok = 0;
312    if(topseq == NULL && nclistlength(node->array.dimset0) > 0) {
313 err = NC_EINVAL; /* This container has dimensions, so no sequence within it
314                            can be usable */
315    } else if(node->nctype == NC_Sequence) {
316 /* Recursively walk the path for each subnode of this sequence node
317           looking for a path without any sequence */
318 for(i=0;i<nclistlength(node->subnodes);i++) {
319     CDFnodesub = (CDFnode*)nclistget(node->subnodes,i);
320     err = sequencecheckr(sub,vars,node);
321     if(err == NC_NOERRok = 1; /* there is at least 1 usable var below */
322 }
323 if(topseq == NULL && ok == 1) {
324     /* this sequence is usable because it has scalar container
325               (by construction) and has a path to a leaf without an intermediate
326               sequence. */
327     err = NC_NOERR;
328     node->usesequence = 1;
329 } else {
330     /* this sequence is unusable because it has no path
331               to a leaf without an intermediate sequence. */
332     node->usesequence = 0;
333     err = NC_EINVAL;
334 }
335    } else if(nclistcontains(vars,(void*)node)) {
336 /* If we reach a leaf, then topseq is usable, so save it */
337 node->array.sequence = topseq;
338    } else { /* Some kind of non-sequence container node with no dimensions */
339 /* recursively compute usability */
340 for(i=0;i<nclistlength(node->subnodes);i++) {
341     CDFnodesub = (CDFnode*)nclistget(node->subnodes,i);
342     err = sequencecheckr(sub,vars,topseq);
343     if(err == NC_NOERRok = 1;
344 }
345 err = (ok?NC_NOERR:NC_EINVAL);
346    }
347    return err;
348}
349
350/*
351Originally, if one did a constraint on a Grid such that only
352one array or map in the grid was returned, that element was
353returned as a top level variable.  This is incorrect because
354it loses the Grid scope information.
355
356Eventually, this behavior was changed so that such partial
357grids are converted to structures where the structure name
358is the grid name. This preserves the proper scoping.
359However, it is still the case that some servers do the old
360behavior.
361
362The rules that most old-style servers appear to adhere to are these.
3631. Asking for just a grid array or a single grid map
364   returns just the array not wrapped in a structure.
3652. Asking for a subset of the fields (array plus map) of a grid
366   returns those fields wrapped in a structure.
3673. However, there is an odd situation: asking for a grid array
368   plus any subset of maps that includes the last map in the grid
369   returns a malformed grid. This is clearly a bug.
370
371For case 1, we insert a structure node so that case 1 is consistent
372with case 2. Case 3 should cause an error with a malformed grid.
373
374[Note: for some reason, this code has been difficult to get right;
375I have rewritten 6 times and it probably is still not right.]
376[2/25/2013 Sigh! Previous fixes have introducted another bug,
377so now we fix the fix.]
378
379Input is
380(1) the root of the dds that needs to be re-gridded
381(2) the full datadds tree that defines where the grids are.
382(3) the projections that were used to produce (1) from (2).
383
384*/
385
386NCerror
387restruct(NCDAPCOMMONnccCDFnodeddsrootCDFnodepatternrootNClistprojections)
388{
389    NCerror ncstat = NC_NOERR;
390    NClistrepairs = nclistnew();
391
392    /* The current restruct assumes that the ddsroot tree
393       has missing grids compared to the pattern.
394       It is also assumed that order of the nodes
395       in the ddsroot is the same as in the pattern.
396    */
397    if(ddsroot->tree->restructed) {
398      nclistfree(repairs);
399      return NC_NOERR;
400    }
401
402#ifdef DEBUG
403fprintf(stderr,"restruct: ddsroot=%s\n",dumptree(ddsroot));
404fprintf(stderr,"restruct: patternroot=%s\n",dumptree(patternroot));
405#endif
406
407    /* Match roots */
408    if(!simplenodematch(ddsroot,patternroot))
409 ncstat = NC_EDATADDS;
410    else if(!restructr(ncc,ddsroot,patternroot,repairs))
411 ncstat = NC_EDATADDS;
412    else if(nclistlength(repairs) > 0) {
413 /* Do the repairs */
414 ncstat = repairgrids(nccrepairs);
415    }
416
417    if(repairs)
418      nclistfree(repairs);
419
420    return THROW(ncstat);
421}
422
423/*
424Locate nodes in the tree rooted at node
425that correspond to a single grid field in the pattern
426when the pattern is a grid.
427Wrap that grid field in a synthesized structure.
428
429The key thing to look for is the case where
430we have an atomic variable that appear where
431we expected a grid.
432
433*/
434
435static int
436restructr(NCDAPCOMMONnccCDFnodedxdparentCDFnodepatternparentNClistrepairlist)
437{
438    int indexijmatch;
439
440#ifdef DEBUG
441fprintf(stderr,"restruct: matched: %s -> %s\n",
442ocfqn(dxdparent->ocnode),ocfqn(patternparent->ocnode));
443#endif
444
445    /* walk each node child and locate its match
446       in the pattern's children; recurse on matches,
447       non-matches may be nodes needing wrapping.
448    */
449
450    for(index=0;index<nclistlength(dxdparent->subnodes);index++) {
451        CDFnodedxdsubnode = (CDFnode*)nclistget(dxdparent->subnodes,index);
452 CDFnodematchnode = NULL;
453
454 /* Look for a matching pattern node with same ocname */
455        for(i=0;i<nclistlength(patternparent->subnodes);i++) {
456            CDFnodepatternsubnode = (CDFnode*)nclistget(patternparent->subnodes,i);
457     if(strcmp(dxdsubnode->ocname,patternsubnode->ocname) == 0) {
458 matchnode = patternsubnode;
459 break;
460     }
461 }
462#ifdef DEBUG
463fprintf(stderr,"restruct: candidate: %s -> %s\n",
464ocfqn(dxdsubnode->ocnode),ocfqn(matchnode->ocnode));
465#endif
466 if(simplenodematch(dxdsubnode,matchnode)) {
467     /* this subnode of the node matches the corresponding
468               node of the pattern, so it is ok =>
469               recurse looking for nested mis-matches
470            */
471     if(!restructr(ncc,dxdsubnode,matchnode,repairlist))
472 return 0;
473 } else {
474            /* If we do not have a direct match, then we need to look
475               at all the grids to see if this node matches a field
476               in one of the grids
477            */
478            for(match=0,i=0;!match && i<nclistlength(patternparent->subnodes);i++) {
479                CDFnodesubtemp = (CDFnode*)nclistget(patternparent->subnodes,i);
480                if(subtemp->nctype == NC_Grid) {
481     /* look inside */
482                    for(j=0;j<nclistlength(patternparent->subnodes);j++) {
483                        CDFnodegridfield = (CDFnode*)nclistget(subtemp->subnodes,j);
484                        if(simplenodematch(dxdsubnode,gridfield)) {
485                            /* We need to do this repair */
486                            nclistpush(repairlist,(void*)dxdsubnode);
487                            nclistpush(repairlist,(void*)gridfield);
488                            match = 1;
489                            break;
490                        }
491                    }
492                }
493            }
494     if(!match) return 0; /* we failed */
495 }
496    }
497    return 1; /* we matched everything at this level */
498}
499
500/* Wrap the node wrt the pattern grid or pattern struct */
501
502static NCerror
503repairgrids(NCDAPCOMMONnccNClistrepairlist)
504{
505    NCerror ncstat = NC_NOERR;
506    int i;
507    assert(nclistlength(repairlist) % 2 == 0);
508    for(i=0;i<nclistlength(repairlist);i+=2) {
509 CDFnodenode = (CDFnode*)nclistget(repairlist,i);
510 CDFnodepattern = (CDFnode*)nclistget(repairlist,i+1);
511 int index = findin(node->container,node);
512 int tindex = findin(pattern->container,pattern);
513 ncstat = structwrap(nccnode,node->container,index,
514                             pattern->container,tindex);
515#ifdef DEBUG
516fprintf(stderr,"repairgrids: %s -> %s\n",
517ocfqn(node->ocnode),ocfqn(pattern->ocnode));
518#endif
519
520    }
521    return ncstat;
522}
523
524static NCerror
525structwrap(NCDAPCOMMONnccCDFnodenodeCDFnodeparent, int parentindex,
526                           CDFnodepatterngrid, int gridindex)
527{
528    CDFnodenewstruct;
529
530    ASSERT((patterngrid->nctype == NC_Grid));
531    newstruct = makenewstruct(nccnode,patterngrid);
532    if(newstruct == NULL) {return THROW(NC_ENOMEM);}
533
534    /* replace the node with the new structure
535       in the parent's list of children*/
536    nclistset(parent->subnodes,parentindex,(void*)newstruct);
537
538    /* Update the list of all nodes in the tree */
539    nclistpush(node->root->tree->nodes,(void*)newstruct);
540    return NC_NOERR;
541}
542
543static int
544findin(CDFnodeparentCDFnodechild)
545{
546    int i;
547    NClistsubnodes = parent->subnodes;
548    for(i=0;i<nclistlength(subnodes);i++) {
549 if(nclistget(subnodes,i) == child)
550     return i;
551    }
552    return -1;
553}
554
555/* Create a structure to surround projected grid array or map;
556   this occurs because some servers (that means you ferret and you thredds!)
557   do not adhere to the DAP2 protocol spec.
558*/
559
560static CDFnode*
561makenewstruct(NCDAPCOMMONnccCDFnodenodeCDFnodepatternnode)
562{
563    CDFnodenewstruct = makecdfnode(ncc,patternnode->ocname,OC_Structure,
564                                      patternnode->ocnodenode->container);
565    if(newstruct == NULL) return NULL;
566    newstruct->nc_virtual = 1;
567    newstruct->ncbasename = nulldup(patternnode->ncbasename);
568    newstruct->subnodes = nclistnew();
569    newstruct->pattern = patternnode;
570    node->container = newstruct;
571    nclistpush(newstruct->subnodes,(void*)node);
572    return newstruct;
573}
574
575/**
576Make the constrained dds nodes (root)
577point to the corresponding unconstrained
578dds nodes (fullroot).
579 */
580
581NCerror
582mapnodes(CDFnoderootCDFnodefullroot)
583{
584    NCerror ncstat = NC_NOERR;
585    ASSERT(root != NULL && fullroot != NULL);
586    if(!simplenodematch(root,fullroot))
587 {THROWCHK(ncstat=NC_EINVAL); goto done;}
588    /* clear out old associations*/
589    unmap(root);
590    ncstat = mapnodesr(root,fullroot,0);
591done:
592    return ncstat;
593}
594
595static NCerror
596mapnodesr(CDFnodeconnodeCDFnodefullnode, int depth)
597{
598    unsigned int i,j;
599    NCerror ncstat = NC_NOERR;
600
601    ASSERT((simplenodematch(connode,fullnode)));
602
603#ifdef DEBUG
604  {
605char* path1 = makecdfpathstring(fullnode,".");
606char* path2 = makecdfpathstring(connode,".");
607fprintf(stderr,"mapnode: %s->%s\n",path1,path2);
608nullfree(path1); nullfree(path2);
609  }
610#endif
611
612    /* Map node */
613    mapfcn(connode,fullnode);
614
615#if 0
616  {
617    int i;
618    for(i=0;i<nclistlength(fullnode->subnodes);i++) {
619 CDFnoden = (CDFnode*)nclistget(fullnode->subnodes,i);
620 fprintf(stderr,"fullnode.subnode[%d]: (%d) %s\n",i,n->nctype,n->ocname);
621    }
622    for(i=0;i<nclistlength(connode->subnodes);i++) {
623 CDFnoden = (CDFnode*)nclistget(connode->subnodes,i);
624 fprintf(stderr,"connode.subnode[%d]: (%d) %s\n",i,n->nctype,n->ocname);
625    }
626  }
627#endif
628
629    /* Try to match connode subnodes against fullnode subnodes */
630    ASSERT(nclistlength(connode->subnodes) <= nclistlength(fullnode->subnodes));
631
632    for(i=0;i<nclistlength(connode->subnodes);i++) {
633        CDFnodeconsubnode = (CDFnode*)nclistget(connode->subnodes,i);
634 /* Search full subnodes for a matching subnode from con */
635        for(j=0;j<nclistlength(fullnode->subnodes);j++) {
636            CDFnodefullsubnode = (CDFnode*)nclistget(fullnode->subnodes,j);
637            if(simplenodematch(fullsubnode,consubnode)) {
638                ncstat = mapnodesr(consubnode,fullsubnode,depth+1);
639            if(ncstat) goto done;
640     }
641 }
642    }
643done:
644    return THROW(ncstat);
645}
646
647
648/* The specific actions of a map are defined
649   by this function.
650*/
651static NCerror
652mapfcn(CDFnodedstnodeCDFnodesrcnode)
653{
654    /* Mark node as having been mapped */
655    dstnode->basenode = srcnode;
656    return NC_NOERR;
657}
658
659void
660unmap(CDFnoderoot)
661{
662    unsigned int i;
663    CDFtreetree = root->tree;
664    for(i=0;i<nclistlength(tree->nodes);i++) {
665 CDFnodenode = (CDFnode*)nclistget(tree->nodes,i);
666 node->basenode = NULL;
667    }
668}
669
670/*
671Move dimension data from basenodes to nodes
672*/
673
674NCerror
675dimimprint(NCDAPCOMMONnccomm)
676{
677    NCerror ncstat = NC_NOERR;
678    NClistallnodes;
679    int i,j;
680    CDFnodebasenode;
681
682    allnodes = nccomm->cdf.ddsroot->tree->nodes;
683    for(i=0;i<nclistlength(allnodes);i++) {
684 CDFnodenode = (CDFnode*)nclistget(allnodes,i);
685 int noderankbaserank;
686        /* Do dimension imprinting */
687 basenode = node->basenode;
688 if(basenode == NULL) continue;
689 noderank = nclistlength(node->array.dimset0);
690 baserank = nclistlength(basenode->array.dimset0);
691 if(noderank == 0) continue;
692        ASSERT(noderank == baserank);
693#ifdef DEBUG
694fprintf(stderr,"dimimprint %s/%d -> %s/%d\n",
695 makecdfpathstring(basenode,"."),
696 noderank,
697 makecdfpathstring(node,"."),
698 baserank);
699#endif
700        for(j=0;j<noderank;j++) {
701     CDFnodedim = (CDFnode*)nclistget(node->array.dimset0,j);
702     CDFnodebasedim = (CDFnode*)nclistget(basenode->array.dimset0,j);
703     dim->dim.declsize0 = basedim->dim.declsize;
704#ifdef DEBUG
705fprintf(stderr,"dimimprint: %d: %lu -> %lu\n",i,basedim->dim.declsize,dim->dim.declsize0);
706#endif
707        }
708    }
709    return ncstat;
710}
711
712static CDFnode*
713clonedim(NCDAPCOMMONnccommCDFnodedimCDFnodevar)
714{
715    CDFnodeclone;
716    clone = makecdfnode(nccomm,dim->ocname,OC_Dimension,
717   NULL,dim->container);
718    /* Record its existence */
719    nclistpush(dim->container->root->tree->nodes,(void*)clone);
720    clone->dim = dim->dim; /* copy most everything */
721    clone->dim.dimflags |= CDFDIMCLONE;
722    clone->dim.array = var;
723    return clone;
724}
725
726static NClist*
727clonedimset(NCDAPCOMMONnccommNClistdimsetCDFnodevar)
728{
729    NClistresult = NULL;
730    int i;
731
732    for(i=0;i<nclistlength(dimset);i++) {
733        CDFnode *dim = NULL;
734        if(result == NULL)
735           result = nclistnew();
736
737        dim = (CDFnode*)nclistget(dimset,i);
738        nclistpush(result,(void*)clonedim(nccomm,dim,var));
739    }
740    return result;
741}
742
743/* Define the dimsetplus list for a node = dimset0+pseudo dims */
744static NCerror
745definedimsetplus(NCDAPCOMMONnccomm/*notused*/, CDFnodenode)
746{
747    int ncstat = NC_NOERR;
748    NClistdimset = NULL;
749    CDFnodeclone = NULL;
750
751    if(node->array.dimset0 != NULL)
752        /* copy the dimset0 into dimset */
753        dimset = nclistclone(node->array.dimset0);
754    /* Insert the sequence or string dims */
755    if(node->array.stringdim != NULL) {
756        if(dimset == NULLdimset = nclistnew();
757 clone = node->array.stringdim;
758        nclistpush(dimset,(void*)clone);
759    }
760    if(node->array.seqdim != NULL) {
761        if(dimset == NULLdimset = nclistnew();
762 clone = node->array.seqdim;
763        nclistpush(dimset,(void*)clone);
764    }
765    node->array.dimsetplus = dimset;
766    return ncstat;
767}
768
769/* Define the dimsetall list for a node =  */
770static NCerror
771definedimsetall(NCDAPCOMMONnccomm/*notused*/, CDFnodenode)
772{
773    int i;
774    int ncstat = NC_NOERR;
775    NClistdimsetall = NULL;
776
777    if(node->container != NULL) {
778 /* We need to clone the parent dimensions because we will be assigning
779           indices vis-a-vis this variable */
780        dimsetall = clonedimset(nccomm,node->container->array.dimsetall,node);
781    }
782    /* append dimsetplus; */
783    for(i=0;i<nclistlength(node->array.dimsetplus);i++) {
784 CDFnodeclone = NULL;
785        if(dimsetall == NULLdimsetall = nclistnew();
786 clone = (CDFnode*)nclistget(node->array.dimsetplus,i);
787 nclistpush(dimsetall,(void*)clone);
788    }
789    node->array.dimsetall = dimsetall;
790#ifdef DEBUG1
791fprintf(stderr,"dimsetall: |%s|=%d\n",node->ocname,(int)nclistlength(dimsetall));
792#endif
793    return ncstat;
794}
795
796/* Define the dimsettrans list for a single node */
797static NCerror
798definetransdimset(NCDAPCOMMONnccomm/*notused*/, CDFnodenode)
799{
800    int i;
801    int ncstat = NC_NOERR;
802    NClistdimsettrans = NULL;
803
804#ifdef DEBUG1
805fprintf(stderr,"dimsettrans3: node=%s/%d\n",node->ocname,nclistlength(node->array.dimset0));
806#endif
807    if(node->container != NULL) {
808 /* We need to clone the parent dimensions because we will be assigning
809           indices vis-a-vis this variable */
810        dimsettrans = clonedimset(nccomm,node->container->array.dimsettrans,node);
811    }
812    /* concat parent dimset0 and dimset;*/
813    for(i=0;i<nclistlength(node->array.dimset0);i++) {
814 CDFnodeclone = NULL;
815        if(dimsettrans == NULLdimsettrans = nclistnew();
816  clone = (CDFnode*)nclistget(node->array.dimset0,i);
817 nclistpush(dimsettrans,(void*)clone);
818    }
819    node->array.dimsettrans = dimsettrans;
820#ifdef DEBUG1
821fprintf(stderr,"dimsettrans: |%s|=%d\n",node->ocname,(int)nclistlength(dimsettrans));
822#endif
823    return ncstat;
824}
825
826/*
827Recursively define the transitive closure of dimensions
828(dimsettrans) based on the original dimension set (dimset0):
829*/
830
831NCerror
832definedimsettrans(NCDAPCOMMONnccommCDFtreetree)
833{
834    /* recursively walk the tree */
835    definedimsettransR(nccommtree->root);
836    return NC_NOERR;
837}
838
839/*
840Recursive helper for definedimsettrans3
841*/
842static NCerror
843definedimsettransR(NCDAPCOMMONnccommCDFnodenode)
844{
845    int i;
846    int ncstat = NC_NOERR;
847
848    definetransdimset(nccomm,node);
849    /* recurse */
850    for(i=0;i<nclistlength(node->subnodes);i++) {
851 CDFnodesubnode = (CDFnode*)nclistget(node->subnodes,i);
852 if(subnode->nctype == NC_Dimension) continue; /*ignore*/
853 ASSERT((subnode->array.dimsettrans == NULL));
854 ASSERT((subnode->array.dimsetplus == NULL));
855 ASSERT((subnode->array.dimsetall == NULL));
856 ncstat = definedimsettransR(nccomm,subnode);
857 if(ncstat != NC_NOERR)
858     break;
859    }
860    return ncstat;
861}
862
863
864/*
865Recursively define two dimension sets for each structural node
866based on the original dimension set (dimset0):
8671. dimsetplus = dimset0+pseudo-dimensions (string,sequence).
8682. dimsetall = parent-dimsetall + dimsetplus
869*/
870
871NCerror
872definedimsets(NCDAPCOMMONnccommCDFtreetree)
873{
874    /* recursively walk the tree */
875    definedimsetsR(nccommtree->root);
876    return NC_NOERR;
877}
878
879/*
880Recursive helper
881*/
882static NCerror
883definedimsetsR(NCDAPCOMMONnccommCDFnodenode)
884{
885    int i;
886    int ncstat = NC_NOERR;
887
888    definedimsetplus(nccomm,node);
889    definedimsetall(nccomm,node);
890    /* recurse */
891    for(i=0;i<nclistlength(node->subnodes);i++) {
892 CDFnodesubnode = (CDFnode*)nclistget(node->subnodes,i);
893 if(subnode->nctype == NC_Dimension) continue; /*ignore*/
894 ASSERT((subnode->array.dimsettrans == NULL));
895 ASSERT((subnode->array.dimsetplus == NULL));
896 ASSERT((subnode->array.dimsetall == NULL));
897 ncstat = definedimsetsR(nccomm,subnode);
898 if(ncstat != NC_NOERR)
899     break;
900    }
901    return ncstat;
902}
903
904CDFnode*
905makecdfnode(NCDAPCOMMONnccomm, char* ocnameOCtype octype,
906             /*optional*/ OCddsnode ocnodeCDFnodecontainer)
907{
908    CDFnodenode;
909    assert(nccomm != NULL);
910    node = (CDFnode*)calloc(1,sizeof(CDFnode));
911    if(node == NULL) return (CDFnode*)NULL;
912
913    node->ocname = NULL;
914    if(ocname) {
915        size_t len = strlen(ocname);
916        if(len >= NC_MAX_NAMElen = NC_MAX_NAME-1;
917        node->ocname = (char*)malloc(len+1);
918 if(node->ocname == NULL) { nullfree(node); return NULL;}
919 memcpy(node->ocname,ocname,len);
920 node->ocname[len] = '\0';
921    }
922    node->nctype = octypetonc(octype);
923    node->ocnode = ocnode;
924    node->subnodes = nclistnew();
925    node->container = container;
926    if(ocnode != NULL) {
927 oc_dds_atomictype(nccomm->oc.conn,ocnode,&octype);
928        node->etype = octypetonc(octype);
929    }
930    if(container != NULL)
931 node->root = container->root;
932    else if(node->nctype == NC_Dataset)
933 node->root = node;
934    return node;
935}
936
937/* Given an OCnode tree, mimic it as a CDFnode tree;
938   Add DAS attributes if DAS is available. Accumulate set
939   of all nodes in preorder.
940*/
941NCerror
942buildcdftree(NCDAPCOMMONnccommOCddsnode ocrootOCdxd occlassCDFnode** cdfrootp)
943{
944    CDFnoderoot = NULL;
945    CDFtreetree = (CDFtree*)calloc(1,sizeof(CDFtree));
946    NCerror err = NC_NOERR;
947    if(!tree)
948      return OC_ENOMEM;
949
950    tree->ocroot = ocroot;
951    tree->nodes = nclistnew();
952    tree->occlass = occlass;
953    tree->owner = nccomm;
954
955    err = buildcdftreer(nccomm,ocroot,NULL,tree,&root);
956    if(!err) {
957 if(occlass != OCDAS)
958     fixnodes(nccomm,tree->nodes);
959 if(cdfrootp) *cdfrootp = root;
960    }
961    return err;
962}
963
964static NCerror
965buildcdftreer(NCDAPCOMMONnccommOCddsnode ocnodeCDFnodecontainer,
966                CDFtreetreeCDFnode** cdfnodep)
967{
968    size_t i,ocrank,ocnsubnodes;
969    OCtype octype;
970    OCtype ocatomtype;
971    char* ocname = NULL;
972    NCerror ncerr = NC_NOERR;
973    CDFnodecdfnode = NULL;
974
975    oc_dds_class(nccomm->oc.conn,ocnode,&octype);
976    if(octype == OC_Atomic)
977 oc_dds_atomictype(nccomm->oc.conn,ocnode,&ocatomtype);
978    else
979 ocatomtype = OC_NAT;
980    oc_dds_name(nccomm->oc.conn,ocnode,&ocname);
981    oc_dds_rank(nccomm->oc.conn,ocnode,&ocrank);
982    oc_dds_nsubnodes(nccomm->oc.conn,ocnode,&ocnsubnodes);
983
984#ifdef DEBUG1
985    if(ocatomtype == OC_NAT)
986 fprintf(stderr,"buildcdftree: connect: %s %s\n",oc_typetostring(octype),ocname);
987    else
988 fprintf(stderr,"buildcdftree: connect: %s %s\n",oc_typetostring(ocatomtype),ocname);
989#endif
990
991    switch (octype) {
992    case OC_Dataset:
993 cdfnode = makecdfnode(nccomm,ocname,octype,ocnode,container);
994 nclistpush(tree->nodes,(void*)cdfnode);
995 tree->root = cdfnode;
996 cdfnode->tree = tree;
997 break;
998
999    case OC_Grid:
1000    case OC_Structure:
1001    case OC_Sequence:
1002 cdfnode = makecdfnode(nccomm,ocname,octype,ocnode,container);
1003 nclistpush(tree->nodes,(void*)cdfnode);
1004#if 0
1005 if(tree->root == NULL) {
1006     tree->root = cdfnode;
1007     cdfnode->tree = tree;
1008 }
1009#endif
1010 break;
1011
1012    case OC_Atomic:
1013 cdfnode = makecdfnode(nccomm,ocname,octype,ocnode,container);
1014 nclistpush(tree->nodes,(void*)cdfnode);
1015#if 0
1016 if(tree->root == NULL) {
1017     tree->root = cdfnode;
1018     cdfnode->tree = tree;
1019 }
1020#endif
1021 break;
1022
1023    case OC_Dimension:
1024    default: PANIC1("buildcdftree: unexpect OC node type: %d",(int)octype);
1025
1026    }
1027    /* Avoid a rare but perhaps possible null-dereference
1028       of cdfnode. Not sure what error to throw, so using
1029       NC_EDAP: generic DAP error. */
1030    if(!cdfnode) {
1031      return NC_EDAP;
1032    }
1033
1034#if 0
1035    /* cross link */
1036    assert(tree->root != NULL);
1037    cdfnode->root = tree->root;
1038#endif
1039
1040    if(ocrank > 0) defdimensions(ocnode,cdfnode,nccomm,tree);
1041    for(i=0;i<ocnsubnodes;i++) {
1042 OCddsnode ocsubnode;
1043 CDFnodesubnode;
1044 oc_dds_ithfield(nccomm->oc.conn,ocnode,i,&ocsubnode);
1045 ncerr = buildcdftreer(nccomm,ocsubnode,cdfnode,tree,&subnode);
1046 if(ncerr) {
1047   if(ocname) free(ocname);
1048   return ncerr;
1049 }
1050 nclistpush(cdfnode->subnodes,(void*)subnode);
1051    }
1052    nullfree(ocname);
1053    if(cdfnodep) *cdfnodep = cdfnode;
1054    return ncerr;
1055}
1056
1057void
1058freecdfroot(CDFnoderoot)
1059{
1060    int i;
1061    CDFtreetree;
1062    NCDAPCOMMONnccomm;
1063    if(root == NULL) return;
1064    tree = root->tree;
1065    ASSERT((tree != NULL));
1066    /* Explicitly FREE the ocroot */
1067    nccomm = tree->owner;
1068    oc_root_free(nccomm->oc.conn,tree->ocroot);
1069    tree->ocroot = NULL;
1070    for(i=0;i<nclistlength(tree->nodes);i++) {
1071 CDFnodenode = (CDFnode*)nclistget(tree->nodes,i);
1072 free1cdfnode(node);
1073    }
1074    nclistfree(tree->nodes);
1075    nclistfree(tree->varnodes);
1076    nclistfree(tree->seqnodes);
1077    nclistfree(tree->gridnodes);
1078    nullfree(tree);
1079}
1080
1081/* Free up a single node, but not any
1082   nodes it points to.
1083*/
1084static void
1085free1cdfnode(CDFnodenode)
1086{
1087    unsigned int j,k;
1088    if(node == NULL) return;
1089    nullfree(node->ocname);
1090    nullfree(node->ncbasename);
1091    nullfree(node->ncfullname);
1092    if(node->attributes != NULL) {
1093 for(j=0;j<nclistlength(node->attributes);j++) {
1094     NCattributeatt = (NCattribute*)nclistget(node->attributes,j);
1095     nullfree(att->name);
1096     for(k=0;k<nclistlength(att->values);k++)
1097 nullfree((char*)nclistget(att->values,k));
1098     nclistfree(att->values);
1099     nullfree(att);
1100 }
1101    }
1102    nullfree(node->dodsspecial.dimname);
1103    nclistfree(node->subnodes);
1104    nclistfree(node->attributes);
1105    nclistfree(node->array.dimsetplus);
1106    nclistfree(node->array.dimsetall);
1107    nclistfree(node->array.dimset0);
1108
1109    /* Clean up the ncdap4 fields also */
1110    nullfree(node->typename);
1111    nullfree(node->vlenname);
1112    nullfree(node);
1113}
1114
1115
1116
1117/* Return true if node and node1 appear to refer to the same thing;
1118   takes grid->structure changes into account.
1119*/
1120int
1121nodematch(CDFnodenode1CDFnodenode2)
1122{
1123    return simplenodematch(node1,node2);
1124}
1125
1126/*
1127Try to figure out if two nodes
1128are the "related" =>
1129    same name && same nc_type and same arity
1130but: Allow Grid == Structure
1131*/
1132
1133int
1134simplenodematch(CDFnodenode1CDFnodenode2)
1135{
1136    /* Test all the obvious stuff */
1137    if(node1 == NULL || node2 == NULL)
1138 return 0;
1139
1140    /* Add hack to address the screwed up Columbia server
1141       which returns different Dataset {...} names
1142       depending on the constraint.
1143    */
1144
1145    if(FLAGSET(node1->root->tree->owner->controls,NCF_COLUMBIA)
1146       && node1->nctype == NC_Dataset) return 1;
1147
1148    if(strcmp(node1->ocname,node2->ocname)!=0) /* same names */
1149 return 0;
1150    if(nclistlength(node1->array.dimset0)
1151 != nclistlength(node2->array.dimset0)) /* same arity */
1152 return 0;
1153
1154    if(node1->nctype != node2->nctype) {
1155 /* test for struct-grid match */
1156 int structgrid = ((node1->nctype == NC_Grid && node2->nctype == NC_Structure)
1157                          || (node1->nctype == NC_Structure && node2->nctype == NC_Grid) ? 1 : 0);
1158 if(!structgrid)
1159     return 0;
1160    }
1161
1162    if(node1->nctype == NC_Atomic && node1->etype != node2->etype)
1163 return 0;
1164
1165    return 1;
1166}
1167
1168/* Ensure every node has an initial base name defined and fullname */
1169/* Exceptions: anonymous dimensions. */
1170static NCerror
1171fix1node(NCDAPCOMMONnccommCDFnodenode)
1172{
1173    if(node->nctype == NC_Dimension && node->ocname == NULL) return NC_NOERR;
1174    ASSERT((node->ocname != NULL));
1175    nullfree(node->ncbasename);
1176    node->ncbasename = cdflegalname(node->ocname);
1177    if(node->ncbasename == NULL) return NC_ENOMEM;
1178    nullfree(node->ncfullname);
1179    node->ncfullname = makecdfpathstring(node,nccomm->cdf.separator);
1180    if(node->ncfullname == NULL) return NC_ENOMEM;
1181    if(node->nctype == NC_Atomic)
1182        node->externaltype = nctypeconvert(nccomm,node->etype);
1183    return NC_NOERR;
1184}
1185
1186static NCerror
1187fixnodes(NCDAPCOMMONnccommNClistcdfnodes)
1188{
1189    int i;
1190    for(i=0;i<nclistlength(cdfnodes);i++) {
1191 CDFnodenode = (CDFnode*)nclistget(cdfnodes,i);
1192 NCerror err = fix1node(nccomm,node);
1193 if(err) return err;
1194    }
1195    return NC_NOERR;
1196}
1197
1198static void
1199defdimensions(OCddsnode ocnodeCDFnodecdfnodeNCDAPCOMMONnccommCDFtreetree)
1200{
1201    size_t i,ocrank;
1202
1203    oc_dds_rank(nccomm->oc.conn,ocnode,&ocrank);
1204    assert(ocrank > 0);
1205    for(i=0;i<ocrank;i++) {
1206 CDFnodecdfdim;
1207 OCddsnode ocdim;
1208 char* ocname;
1209 size_t declsize;
1210
1211 oc_dds_ithdimension(nccomm->oc.conn,ocnode,i,&ocdim);
1212 oc_dimension_properties(nccomm->oc.conn,ocdim,&declsize,&ocname);
1213
1214 cdfdim = makecdfnode(nccomm,ocname,OC_Dimension,
1215                              ocdim,cdfnode->container);
1216 nullfree(ocname);
1217 nclistpush(tree->nodes,(void*)cdfdim);
1218 /* Initially, constrained and unconstrained are same */
1219 cdfdim->dim.declsize = declsize;
1220 cdfdim->dim.array = cdfnode;
1221 if(cdfnode->array.dimset0 == NULL)
1222     cdfnode->array.dimset0 = nclistnew();
1223 nclistpush(cdfnode->array.dimset0,(void*)cdfdim);
1224    }
1225}


HyperKWIC - Version 7.20DA executed at 11:37 on 27 Oct 2017 | Polyhedron Solutions - INTERNAL USE | COMMERCIAL (Any O/S) SN 4AKIed