1 | /********************************************************************* |
---|
2 | * Copyright 1993, UCAR/Unidata |
---|
3 | * See netcdf/COPYRIGHT file for copying and redistribution conditions. |
---|
4 | * $Header: /upc/share/CVS/netcdf-3/libncdce3/constraints3.c,v 1.40 2010/05/27 21:34:07 dmh Exp $ |
---|
5 | *********************************************************************/ |
---|
6 | |
---|
7 | #include "ncdap3.h" |
---|
8 | #include "dapodom.h" |
---|
9 | #include "dapdebug.h" |
---|
10 | #include "dapdump.h" |
---|
11 | #include "dceparselex.h" |
---|
12 | |
---|
13 | static void completesegments3(NClist* fullpath, NClist* segments); |
---|
14 | static NCerror qualifyprojectionnames3(DCEprojection* proj); |
---|
15 | static NCerror qualifyprojectionsizes3(DCEprojection* proj); |
---|
16 | static NCerror matchpartialname3(NClist* nodes, NClist* segments, CDFnode** nodep); |
---|
17 | static int matchsuffix3(NClist* matchpath, NClist* segments); |
---|
18 | static int iscontainer(CDFnode* node); |
---|
19 | static DCEprojection* projectify(CDFnode* field, DCEprojection* container); |
---|
20 | static int slicematch(NClist* seglist1, NClist* seglist2); |
---|
21 | |
---|
22 | /* Parse incoming url constraints, if any, |
---|
23 | to check for syntactic correctness */ |
---|
24 | NCerror |
---|
25 | parsedapconstraints(NCDAPCOMMON* dapcomm, char* constraints, |
---|
26 | DCEconstraint* dceconstraint) |
---|
27 | { |
---|
28 | NCerror ncstat = NC_NOERR; |
---|
29 | char* errmsg; |
---|
30 | |
---|
31 | ASSERT(dceconstraint != NULL); |
---|
32 | nclistclear(dceconstraint->projections); |
---|
33 | nclistclear(dceconstraint->selections); |
---|
34 | |
---|
35 | ncstat = dapceparse(constraints,dceconstraint,&errmsg); |
---|
36 | if(ncstat) { |
---|
37 | nclog(NCLOGWARN,"DCE constraint parse failure: %s",errmsg); |
---|
38 | nullfree(errmsg); |
---|
39 | nclistclear(dceconstraint->projections); |
---|
40 | nclistclear(dceconstraint->selections); |
---|
41 | } |
---|
42 | return ncstat; |
---|
43 | } |
---|
44 | |
---|
45 | /* Map constraint paths to CDFnode paths in specified tree and fill |
---|
46 | in the declsizes. |
---|
47 | The difficulty is that suffix paths are legal. |
---|
48 | */ |
---|
49 | |
---|
50 | NCerror |
---|
51 | mapconstraints3(DCEconstraint* constraint, |
---|
52 | CDFnode* root) |
---|
53 | { |
---|
54 | int i; |
---|
55 | NCerror ncstat = NC_NOERR; |
---|
56 | NClist* nodes = root->tree->nodes; |
---|
57 | NClist* dceprojections = constraint->projections; |
---|
58 | #if 0 |
---|
59 | NClist* dceselections = constraint->selections; |
---|
60 | #endif |
---|
61 | |
---|
62 | /* Convert the projection paths to leaves in the dds tree */ |
---|
63 | for(i=0;i<nclistlength(dceprojections);i++) { |
---|
64 | DCEprojection* proj = (DCEprojection*)nclistget(dceprojections,i); |
---|
65 | if(proj->discrim != CES_VAR) continue; /* ignore functions*/ |
---|
66 | ncstat = matchpartialname3(nodes,proj->var->segments, |
---|
67 | (CDFnode**)&proj->var->annotation); |
---|
68 | if(ncstat) goto done; |
---|
69 | } |
---|
70 | |
---|
71 | #ifdef DEBUG |
---|
72 | fprintf(stderr,"mapconstraint.projections: %s\n", |
---|
73 | dumpprojections(dceprojections)); |
---|
74 | #endif |
---|
75 | |
---|
76 | done: |
---|
77 | return THROW(ncstat); |
---|
78 | } |
---|
79 | |
---|
80 | |
---|
81 | /* Fill in: |
---|
82 | 1. projection segments |
---|
83 | 2. projection segment slices declsize |
---|
84 | 3. selection path |
---|
85 | */ |
---|
86 | NCerror |
---|
87 | qualifyconstraints3(DCEconstraint* constraint) |
---|
88 | { |
---|
89 | NCerror ncstat = NC_NOERR; |
---|
90 | int i; |
---|
91 | #ifdef DEBUG |
---|
92 | fprintf(stderr,"qualifyconstraints.before: %s\n", |
---|
93 | dumpconstraint(constraint)); |
---|
94 | #endif |
---|
95 | if(constraint != NULL) { |
---|
96 | for(i=0;i<nclistlength(constraint->projections);i++) { |
---|
97 | DCEprojection* p = (DCEprojection*)nclistget(constraint->projections,i); |
---|
98 | ncstat = qualifyprojectionnames3(p); |
---|
99 | ncstat = qualifyprojectionsizes3(p); |
---|
100 | } |
---|
101 | } |
---|
102 | #ifdef DEBUG |
---|
103 | fprintf(stderr,"qualifyconstraints.after: %s\n", |
---|
104 | dumpconstraint(constraint)); |
---|
105 | #endif |
---|
106 | return ncstat; |
---|
107 | } |
---|
108 | |
---|
109 | /* convert all names in projections in paths to be fully qualified |
---|
110 | by adding prefix segment objects. |
---|
111 | */ |
---|
112 | static NCerror |
---|
113 | qualifyprojectionnames3(DCEprojection* proj) |
---|
114 | { |
---|
115 | NCerror ncstat = NC_NOERR; |
---|
116 | NClist* fullpath = nclistnew(); |
---|
117 | |
---|
118 | ASSERT((proj->discrim == CES_VAR |
---|
119 | && proj->var->annotation != NULL |
---|
120 | && ((CDFnode*)proj->var->annotation)->ocnode != OCNULL)); |
---|
121 | collectnodepath3((CDFnode*)proj->var->annotation,fullpath,!WITHDATASET); |
---|
122 | #ifdef DEBUG |
---|
123 | fprintf(stderr,"qualify: %s -> ", |
---|
124 | dumpprojection(proj)); |
---|
125 | #endif |
---|
126 | /* Now add path nodes to create full path */ |
---|
127 | completesegments3(fullpath,proj->var->segments); |
---|
128 | |
---|
129 | #ifdef DEBUG |
---|
130 | fprintf(stderr,"%s\n", |
---|
131 | dumpprojection(proj)); |
---|
132 | #endif |
---|
133 | nclistfree(fullpath); |
---|
134 | return ncstat; |
---|
135 | } |
---|
136 | |
---|
137 | /* Make sure that the slice declsizes are all defined for this projection */ |
---|
138 | static NCerror |
---|
139 | qualifyprojectionsizes3(DCEprojection* proj) |
---|
140 | { |
---|
141 | int i,j; |
---|
142 | ASSERT(proj->discrim == CES_VAR); |
---|
143 | #ifdef DEBUG |
---|
144 | fprintf(stderr,"qualifyprojectionsizes.before: %s\n", |
---|
145 | dumpprojection(proj)); |
---|
146 | #endif |
---|
147 | for(i=0;i<nclistlength(proj->var->segments);i++) { |
---|
148 | DCEsegment* seg = (DCEsegment*)nclistget(proj->var->segments,i); |
---|
149 | NClist* dimset = NULL; |
---|
150 | CDFnode* cdfnode = (CDFnode*)seg->annotation; |
---|
151 | ASSERT(cdfnode != NULL); |
---|
152 | dimset = cdfnode->array.dimsetplus; |
---|
153 | seg->rank = nclistlength(dimset); |
---|
154 | /* For this, we do not want any string dimensions */ |
---|
155 | if(cdfnode->array.stringdim != NULL) seg->rank--; |
---|
156 | for(j=0;j<seg->rank;j++) { |
---|
157 | CDFnode* dim = (CDFnode*)nclistget(dimset,j); |
---|
158 | if(dim->dim.basedim != NULL) dim = dim->dim.basedim; |
---|
159 | ASSERT(dim != null); |
---|
160 | if(seg->slicesdefined) |
---|
161 | seg->slices[j].declsize = dim->dim.declsize; |
---|
162 | else |
---|
163 | dcemakewholeslice(seg->slices+j,dim->dim.declsize); |
---|
164 | } |
---|
165 | seg->slicesdefined = 1; |
---|
166 | seg->slicesdeclized = 1; |
---|
167 | } |
---|
168 | #ifdef DEBUG |
---|
169 | fprintf(stderr,"qualifyprojectionsizes.after: %s\n", |
---|
170 | dumpprojection(proj)); |
---|
171 | #endif |
---|
172 | return NC_NOERR; |
---|
173 | } |
---|
174 | |
---|
175 | static void |
---|
176 | completesegments3(NClist* fullpath, NClist* segments) |
---|
177 | { |
---|
178 | int i,delta; |
---|
179 | /* add path nodes to segments to create full path */ |
---|
180 | delta = (nclistlength(fullpath) - nclistlength(segments)); |
---|
181 | ASSERT((delta >= 0)); |
---|
182 | for(i=0;i<delta;i++) { |
---|
183 | DCEsegment* seg = (DCEsegment*)dcecreate(CES_SEGMENT); |
---|
184 | CDFnode* node = (CDFnode*)nclistget(fullpath,i); |
---|
185 | seg->name = nulldup(node->ocname); |
---|
186 | seg->annotation = (void*)node; |
---|
187 | seg->rank = nclistlength(node->array.dimset0); |
---|
188 | nclistinsert(segments,i,(ncelem)seg); |
---|
189 | } |
---|
190 | /* Now modify the segments to point to the appropriate node |
---|
191 | and fill in the slices. |
---|
192 | */ |
---|
193 | for(i=delta;i<nclistlength(segments);i++) { |
---|
194 | DCEsegment* seg = (DCEsegment*)nclistget(segments,i); |
---|
195 | CDFnode* node = (CDFnode*)nclistget(fullpath,i); |
---|
196 | seg->annotation = (void*)node; |
---|
197 | } |
---|
198 | } |
---|
199 | |
---|
200 | /* |
---|
201 | We are given a set of segments (in path) |
---|
202 | representing a partial path for a CDFnode variable. |
---|
203 | Our goal is to locate all matching |
---|
204 | variables for which the path of that |
---|
205 | variable has a suffix matching |
---|
206 | the given partial path. |
---|
207 | If one node matches exactly, then use that one; |
---|
208 | otherwise there had better be exactly one |
---|
209 | match else ambiguous. |
---|
210 | Additional constraints (4/12/2010): |
---|
211 | 1. if a segment is dimensioned, then use that info |
---|
212 | to distinguish e.g a grid node from a possible |
---|
213 | grid array within it of the same name. |
---|
214 | Treat sequences as of rank 1. |
---|
215 | 2. if there are two matches, and one is the grid |
---|
216 | and the other is the grid array within that grid, |
---|
217 | then choose the grid array. |
---|
218 | 3. If there are multiple matches choose the one with the |
---|
219 | shortest path |
---|
220 | 4. otherwise complain about ambiguity |
---|
221 | */ |
---|
222 | |
---|
223 | /** |
---|
224 | * Given a path as segments, |
---|
225 | * try to locate the CDFnode |
---|
226 | * instance (from a given set) |
---|
227 | * that corresponds to the path. |
---|
228 | * The key difficulty is that the |
---|
229 | * path may only be a suffix of the |
---|
230 | * complete path. |
---|
231 | */ |
---|
232 | |
---|
233 | static NCerror |
---|
234 | matchpartialname3(NClist* nodes, NClist* segments, CDFnode** nodep) |
---|
235 | { |
---|
236 | int i,nsegs; |
---|
237 | NCerror ncstat = NC_NOERR; |
---|
238 | DCEsegment* lastseg = NULL; |
---|
239 | NClist* namematches = nclistnew(); |
---|
240 | NClist* matches = nclistnew(); |
---|
241 | NClist* matchpath = nclistnew(); |
---|
242 | |
---|
243 | /* Locate all nodes with the same name |
---|
244 | as the last element in the segment path |
---|
245 | */ |
---|
246 | nsegs = nclistlength(segments); |
---|
247 | lastseg = (DCEsegment*)nclistget(segments,nsegs-1); |
---|
248 | for(i=0;i<nclistlength(nodes);i++) { |
---|
249 | CDFnode* node = (CDFnode*)nclistget(nodes,i); |
---|
250 | if(node->ocname == null) |
---|
251 | continue; |
---|
252 | /* Path names come from oc space */ |
---|
253 | if(strcmp(node->ocname,lastseg->name) != 0) |
---|
254 | continue; |
---|
255 | /* Only look at selected kinds of nodes */ |
---|
256 | if(node->nctype != NC_Sequence |
---|
257 | && node->nctype != NC_Structure |
---|
258 | && node->nctype != NC_Grid |
---|
259 | && node->nctype != NC_Primitive |
---|
260 | ) |
---|
261 | continue; |
---|
262 | nclistpush(namematches,(ncelem)node); |
---|
263 | } |
---|
264 | if(nclistlength(namematches)==0) { |
---|
265 | nclog(NCLOGERR,"No match for projection name: %s",lastseg->name); |
---|
266 | ncstat = NC_EDDS; |
---|
267 | goto done; |
---|
268 | } |
---|
269 | |
---|
270 | /* Now, collect and compare paths of the matching nodes */ |
---|
271 | for(i=0;i<nclistlength(namematches);i++) { |
---|
272 | CDFnode* matchnode = (CDFnode*)nclistget(namematches,i); |
---|
273 | nclistclear(matchpath); |
---|
274 | collectnodepath3(matchnode,matchpath,0); |
---|
275 | /* Do a suffix match */ |
---|
276 | if(matchsuffix3(matchpath,segments)) { |
---|
277 | nclistpush(matches,(ncelem)matchnode); |
---|
278 | #ifdef DEBUG |
---|
279 | fprintf(stderr,"matchpartialname: pathmatch: %s :: %s\n", |
---|
280 | matchnode->ncfullname,dumpsegments(segments)); |
---|
281 | #endif |
---|
282 | } |
---|
283 | } |
---|
284 | /* |matches|==0 => no match; |matches|>1 => ambiguity */ |
---|
285 | switch (nclistlength(matches)) { |
---|
286 | case 0: |
---|
287 | nclog(NCLOGERR,"No match for projection name: %s",lastseg->name); |
---|
288 | ncstat = NC_EDDS; |
---|
289 | break; |
---|
290 | case 1: |
---|
291 | if(nodep) |
---|
292 | *nodep = (CDFnode*)nclistget(matches,0); |
---|
293 | break; |
---|
294 | default: { |
---|
295 | CDFnode* minnode = NULL; |
---|
296 | int minpath = 0; |
---|
297 | int nmin = 0; /* to catch multiple ones with same short path */ |
---|
298 | /* ok, see if one of the matches has a path that is shorter |
---|
299 | then all the others */ |
---|
300 | for(i=0;i<nclistlength(matches);i++) { |
---|
301 | CDFnode* candidate = (CDFnode*)nclistget(matches,i); |
---|
302 | nclistclear(matchpath); |
---|
303 | collectnodepath3(candidate,matchpath,0); |
---|
304 | if(minpath == 0) { |
---|
305 | minpath = nclistlength(matchpath); |
---|
306 | minnode = candidate; |
---|
307 | } else if(nclistlength(matchpath) == minpath) { |
---|
308 | nmin++; |
---|
309 | } else if(nclistlength(matchpath) < minpath) { |
---|
310 | minpath = nclistlength(matchpath); |
---|
311 | minnode = candidate; |
---|
312 | nmin = 1; |
---|
313 | } |
---|
314 | } /*for*/ |
---|
315 | if(minnode == NULL || nmin > 1) { |
---|
316 | nclog(NCLOGERR,"Ambiguous match for projection name: %s", |
---|
317 | lastseg->name); |
---|
318 | ncstat = NC_EDDS; |
---|
319 | } else if(nodep) |
---|
320 | *nodep = minnode; |
---|
321 | } break; |
---|
322 | } |
---|
323 | #ifdef DEBUG |
---|
324 | fprintf(stderr,"matchpartialname: choice: %s %s for %s\n", |
---|
325 | (nclistlength(matches) > 1?"":"forced"), |
---|
326 | (*nodep)->ncfullname,dumpsegments(segments)); |
---|
327 | #endif |
---|
328 | |
---|
329 | done: |
---|
330 | return THROW(ncstat); |
---|
331 | } |
---|
332 | |
---|
333 | static int |
---|
334 | matchsuffix3(NClist* matchpath, NClist* segments) |
---|
335 | { |
---|
336 | int i,j; |
---|
337 | int nsegs = nclistlength(segments); |
---|
338 | int pathlen = nclistlength(matchpath); |
---|
339 | ASSERT(pathlen >= nsegs); |
---|
340 | for(i=0;i<pathlen;i++) { |
---|
341 | int pathmatch = 1; |
---|
342 | /* Starting at this point in the path, try to match the segment list */ |
---|
343 | for(j=0;j<nsegs && (i+j < pathlen);j++) { |
---|
344 | int segmatch = 1; |
---|
345 | DCEsegment* seg = (DCEsegment*)nclistget(segments,j); |
---|
346 | CDFnode* node = (CDFnode*)nclistget(matchpath,i+j); |
---|
347 | int rank = seg->rank; |
---|
348 | /* Do the names match (in oc name space) */ |
---|
349 | if(strcmp(seg->name,node->ocname) != 0) { |
---|
350 | segmatch = 0;/* no match */ |
---|
351 | } else |
---|
352 | /* Do the ranks match (watch out for sequences) */ |
---|
353 | if(rank == 0) /* rank == 9 matches any set of dimensions */ |
---|
354 | segmatch = 1; |
---|
355 | else if(node->nctype == NC_Sequence) |
---|
356 | segmatch = (rank == 1?1:0); |
---|
357 | else /*!NC_Sequence*/ |
---|
358 | segmatch = (rank == nclistlength(node->array.dimset0)?1:0); |
---|
359 | if(!segmatch) pathmatch = 0; |
---|
360 | } |
---|
361 | if(pathmatch) return 1; |
---|
362 | } |
---|
363 | return 0; |
---|
364 | } |
---|
365 | |
---|
366 | |
---|
367 | /* Convert a DCEprojection instance into a string |
---|
368 | that can be used with the url |
---|
369 | */ |
---|
370 | |
---|
371 | char* |
---|
372 | buildprojectionstring3(NClist* projections) |
---|
373 | { |
---|
374 | char* pstring; |
---|
375 | NCbytes* buf = ncbytesnew(); |
---|
376 | dcelisttobuffer(projections,buf,","); |
---|
377 | pstring = ncbytesdup(buf); |
---|
378 | ncbytesfree(buf); |
---|
379 | return pstring; |
---|
380 | } |
---|
381 | |
---|
382 | char* |
---|
383 | buildselectionstring3(NClist* selections) |
---|
384 | { |
---|
385 | NCbytes* buf = ncbytesnew(); |
---|
386 | char* sstring; |
---|
387 | dcelisttobuffer(selections,buf,"&"); |
---|
388 | sstring = ncbytesdup(buf); |
---|
389 | ncbytesfree(buf); |
---|
390 | return sstring; |
---|
391 | } |
---|
392 | |
---|
393 | char* |
---|
394 | buildconstraintstring3(DCEconstraint* constraints) |
---|
395 | { |
---|
396 | NCbytes* buf = ncbytesnew(); |
---|
397 | char* result = NULL; |
---|
398 | dcetobuffer((DCEnode*)constraints,buf); |
---|
399 | result = ncbytesdup(buf); |
---|
400 | ncbytesfree(buf); |
---|
401 | return result; |
---|
402 | } |
---|
403 | |
---|
404 | |
---|
405 | /* Given the arguments to vara |
---|
406 | construct a corresponding projection |
---|
407 | with any pseudo dimensions removed |
---|
408 | */ |
---|
409 | NCerror |
---|
410 | buildvaraprojection3(Getvara* getvar, |
---|
411 | const size_t* startp, const size_t* countp, const ptrdiff_t* stridep, |
---|
412 | DCEprojection** projectionp) |
---|
413 | { |
---|
414 | int i,j; |
---|
415 | NCerror ncstat = NC_NOERR; |
---|
416 | CDFnode* var = getvar->target; |
---|
417 | DCEprojection* projection = NULL; |
---|
418 | NClist* path = nclistnew(); |
---|
419 | NClist* segments = NULL; |
---|
420 | int dimindex; |
---|
421 | |
---|
422 | ncstat = dapvar2projection(var,&projection); |
---|
423 | #ifdef DEBUG |
---|
424 | fprintf(stderr,"buildvaraprojection: %s\n",dumpprojection(projection)); |
---|
425 | #endif |
---|
426 | |
---|
427 | /* We need to assign the start/count/stride info to each segment; |
---|
428 | declsize will have been set |
---|
429 | */ |
---|
430 | segments = projection->var->segments; |
---|
431 | dimindex = 0; |
---|
432 | for(i=0;i<nclistlength(segments);i++) { |
---|
433 | DCEsegment* segment = (DCEsegment*)nclistget(segments,i); |
---|
434 | for(j=0;j<segment->rank;j++) { |
---|
435 | DCEslice* slice = &segment->slices[j]; |
---|
436 | /* make each slice represent the corresponding |
---|
437 | start/count/stride */ |
---|
438 | slice->first = startp[dimindex+j]; |
---|
439 | slice->stride = stridep[dimindex+j]; |
---|
440 | slice->count = countp[dimindex+j]; |
---|
441 | slice->length = slice->count * slice->stride; |
---|
442 | if(slice->length > slice->declsize) |
---|
443 | slice->length = slice->declsize; |
---|
444 | slice->stop = (slice->first + slice->length); |
---|
445 | if(slice->stop > slice->declsize) |
---|
446 | slice->stop = slice->declsize; |
---|
447 | } |
---|
448 | dimindex += segment->rank; |
---|
449 | } |
---|
450 | #ifdef DEBUG |
---|
451 | fprintf(stderr,"buildvaraprojection.final: %s\n",dumpprojection(projection)); |
---|
452 | #endif |
---|
453 | |
---|
454 | #ifdef DEBUG |
---|
455 | fprintf(stderr,"buildvaraprojection3: projection=%s\n", |
---|
456 | dumpprojection(projection)); |
---|
457 | #endif |
---|
458 | |
---|
459 | if(projectionp) *projectionp = projection; |
---|
460 | |
---|
461 | nclistfree(path); |
---|
462 | if(ncstat) dcefree((DCEnode*)projection); |
---|
463 | return ncstat; |
---|
464 | } |
---|
465 | |
---|
466 | int |
---|
467 | iswholeslice(DCEslice* slice, CDFnode* dim) |
---|
468 | { |
---|
469 | if(slice->first != 0 || slice->stride != 1) return 0; |
---|
470 | if(dim != NULL) { |
---|
471 | if(slice->stop != dim->dim.declsize) return 0; |
---|
472 | } else if(dim == NULL) { |
---|
473 | if(slice->declsize == 0 |
---|
474 | || slice->count != slice->declsize) return 0; |
---|
475 | } |
---|
476 | return 1; |
---|
477 | } |
---|
478 | |
---|
479 | int |
---|
480 | iswholesegment(DCEsegment* seg) |
---|
481 | { |
---|
482 | int i,whole; |
---|
483 | NClist* dimset = NULL; |
---|
484 | unsigned int rank; |
---|
485 | |
---|
486 | if(seg->rank == 0) return 1; |
---|
487 | if(!seg->slicesdefined) return 0; |
---|
488 | if(seg->annotation == NULL) return 0; |
---|
489 | dimset = ((CDFnode*)seg->annotation)->array.dimset0; |
---|
490 | rank = nclistlength(dimset); |
---|
491 | whole = 1; /* assume so */ |
---|
492 | for(i=0;i<rank;i++) { |
---|
493 | CDFnode* dim = (CDFnode*)nclistget(dimset,i); |
---|
494 | if(!iswholeslice(&seg->slices[i],dim)) {whole = 0; break;} |
---|
495 | } |
---|
496 | return whole; |
---|
497 | } |
---|
498 | |
---|
499 | int |
---|
500 | iswholeprojection(DCEprojection* proj) |
---|
501 | { |
---|
502 | int i,whole; |
---|
503 | |
---|
504 | ASSERT((proj->discrim == CES_VAR)); |
---|
505 | |
---|
506 | whole = 1; /* assume so */ |
---|
507 | for(i=0;i<nclistlength(proj->var->segments);i++) { |
---|
508 | DCEsegment* segment = (DCEsegment*)nclistget(proj->var->segments,i); |
---|
509 | if(!iswholesegment(segment)) {whole = 0; break;} |
---|
510 | } |
---|
511 | return whole; |
---|
512 | } |
---|
513 | |
---|
514 | int |
---|
515 | iswholeconstraint(DCEconstraint* con) |
---|
516 | { |
---|
517 | int i; |
---|
518 | if(con == NULL) return 1; |
---|
519 | if(con->projections != NULL) { |
---|
520 | for(i=0;i<nclistlength(con->projections);i++) { |
---|
521 | if(!iswholeprojection((DCEprojection*)nclistget(con->projections,i))) |
---|
522 | return 0; |
---|
523 | } |
---|
524 | } |
---|
525 | if(con->selections != NULL) |
---|
526 | return 0; |
---|
527 | return 1; |
---|
528 | } |
---|
529 | |
---|
530 | |
---|
531 | /* |
---|
532 | Given a set of projections, we need to produce |
---|
533 | an expanded, correct, and equivalent set of projections. |
---|
534 | The term "correct" means we must fix the following cases: |
---|
535 | 1. Multiple occurrences of the same leaf variable |
---|
536 | with differing projection slices. Fix is to complain. |
---|
537 | 2. Occurrences of container and one or more of its fields. |
---|
538 | Fix is to suppress the container. |
---|
539 | The term "expanded" means |
---|
540 | 1. Expand all occurrences of only a container by |
---|
541 | replacing it with all of its fields. |
---|
542 | */ |
---|
543 | |
---|
544 | NCerror |
---|
545 | fixprojections(NClist* list) |
---|
546 | { |
---|
547 | int i,j,k; |
---|
548 | NCerror ncstat = NC_NOERR; |
---|
549 | NClist* tmp = nclistnew(); /* misc. uses */ |
---|
550 | |
---|
551 | #ifdef DEBUG |
---|
552 | fprintf(stderr,"fixprojection: list = %s\n",dumpprojections(list)); |
---|
553 | #endif |
---|
554 | |
---|
555 | if(nclistlength(list) == 0) goto done; |
---|
556 | |
---|
557 | /* Step 1: remove duplicates and complain about slice mismatches */ |
---|
558 | for(i=0;i<nclistlength(list);i++) { |
---|
559 | DCEprojection* p1 = (DCEprojection*)nclistget(list,i); |
---|
560 | if(p1 == NULL) continue; |
---|
561 | if(p1->discrim != CES_VAR) continue; /* dont try to unify functions */ |
---|
562 | for(j=i;j<nclistlength(list);j++) { |
---|
563 | DCEprojection* p2 = (DCEprojection*)nclistget(list,j); |
---|
564 | if(p2 == NULL) continue; |
---|
565 | if(p1 == p2) continue; |
---|
566 | if(p2->discrim != CES_VAR) continue; |
---|
567 | if(p1->var->annotation != p2->var->annotation) continue; |
---|
568 | /* check for slice mismatches */ |
---|
569 | if(!slicematch(p1->var->segments,p2->var->segments)) { |
---|
570 | /* complain */ |
---|
571 | nclog(NCLOGWARN,"Malformed projection: same variable with different slicing"); |
---|
572 | } |
---|
573 | /* remove p32 */ |
---|
574 | nclistset(list,j,(ncelem)NULL); |
---|
575 | dcefree((DCEnode*)p2); |
---|
576 | } |
---|
577 | } |
---|
578 | |
---|
579 | /* Step 2: remove containers when a field is also present */ |
---|
580 | for(i=0;i<nclistlength(list);i++) { |
---|
581 | DCEprojection* p1 = (DCEprojection*)nclistget(list,i); |
---|
582 | if(p1 == NULL) continue; |
---|
583 | if(p1->discrim != CES_VAR) continue; /* dont try to unify functions */ |
---|
584 | if(!iscontainer((CDFnode*)p1->var->annotation)) |
---|
585 | continue; |
---|
586 | for(j=i;j<nclistlength(list);j++) { |
---|
587 | DCEprojection* p2 = (DCEprojection*)nclistget(list,j); |
---|
588 | if(p2 == NULL) continue; |
---|
589 | if(p2->discrim != CES_VAR) continue; |
---|
590 | nclistclear(tmp); |
---|
591 | collectnodepath3((CDFnode*)p2->var->annotation,tmp,WITHDATASET); |
---|
592 | for(k=0;k<nclistlength(tmp);k++) { |
---|
593 | void* candidate = (void*)nclistget(tmp,k); |
---|
594 | if(candidate == p1->var->annotation) { |
---|
595 | nclistset(list,i,(ncelem)NULL); |
---|
596 | dcefree((DCEnode*)p1); |
---|
597 | goto next; |
---|
598 | } |
---|
599 | } |
---|
600 | } |
---|
601 | next: continue; |
---|
602 | } |
---|
603 | |
---|
604 | /* Step 3: expand all containers recursively down to the leaf nodes */ |
---|
605 | for(;;) { |
---|
606 | nclistclear(tmp); |
---|
607 | for(i=0;i<nclistlength(list);i++) { |
---|
608 | DCEprojection* target = (DCEprojection*)nclistget(list,i); |
---|
609 | CDFnode* leaf; |
---|
610 | if(target == NULL) continue; |
---|
611 | if(target->discrim != CES_VAR) |
---|
612 | continue; /* dont try to unify functions */ |
---|
613 | leaf = (CDFnode*)target->var->annotation; |
---|
614 | ASSERT(leaf != NULL); |
---|
615 | if(iscontainer(leaf)) {/* capture container */ |
---|
616 | if(!nclistcontains(tmp,(ncelem)target)) |
---|
617 | nclistpush(tmp,(ncelem)target); |
---|
618 | nclistset(list,i,(ncelem)NULL); |
---|
619 | } |
---|
620 | } |
---|
621 | if(nclistlength(tmp) == 0) break; /*done*/ |
---|
622 | /* Now explode the containers */ |
---|
623 | for(i=0;i<nclistlength(tmp);i++) { |
---|
624 | DCEprojection* container = (DCEprojection*)nclistget(tmp,i); |
---|
625 | CDFnode* leaf = (CDFnode*)container->var->annotation; |
---|
626 | for(j=0;i<nclistlength(leaf->subnodes);j++) { |
---|
627 | CDFnode* field = (CDFnode*)nclistget(leaf->subnodes,j); |
---|
628 | /* Convert field node to a proper constraint */ |
---|
629 | DCEprojection* proj = projectify(field,container); |
---|
630 | nclistpush(list,(ncelem)proj); |
---|
631 | } |
---|
632 | /* reclaim the container */ |
---|
633 | dcefree((DCEnode*)container); |
---|
634 | } |
---|
635 | } /*for(;;)*/ |
---|
636 | |
---|
637 | /* remove all NULL elements */ |
---|
638 | for(i=nclistlength(list)-1;i>=0;i--) { |
---|
639 | DCEprojection* target = (DCEprojection*)nclistget(list,i); |
---|
640 | if(target == NULL) |
---|
641 | nclistremove(list,i); |
---|
642 | } |
---|
643 | |
---|
644 | done: |
---|
645 | #ifdef DEBUG |
---|
646 | fprintf(stderr,"fixprojection: exploded = %s\n",dumpprojections(list)); |
---|
647 | #endif |
---|
648 | nclistfree(tmp); |
---|
649 | return ncstat; |
---|
650 | } |
---|
651 | |
---|
652 | static int |
---|
653 | iscontainer(CDFnode* node) |
---|
654 | { |
---|
655 | return (node->nctype == NC_Dataset |
---|
656 | || node->nctype == NC_Sequence |
---|
657 | || node->nctype == NC_Structure |
---|
658 | || node->nctype == NC_Grid); |
---|
659 | } |
---|
660 | |
---|
661 | static DCEprojection* |
---|
662 | projectify(CDFnode* field, DCEprojection* container) |
---|
663 | { |
---|
664 | DCEprojection* proj = (DCEprojection*)dcecreate(CES_PROJECT); |
---|
665 | DCEvar* var = (DCEvar*)dcecreate(CES_VAR); |
---|
666 | DCEsegment* seg = (DCEsegment*)dcecreate(CES_SEGMENT); |
---|
667 | proj->discrim = CES_VAR; |
---|
668 | proj->var = var; |
---|
669 | var->annotation = (void*)field; |
---|
670 | /* Dup the segment list */ |
---|
671 | var->segments = dceclonelist(container->var->segments); |
---|
672 | seg->rank = 0; |
---|
673 | nclistpush(var->segments,(ncelem)seg); |
---|
674 | return proj; |
---|
675 | } |
---|
676 | |
---|
677 | static int |
---|
678 | slicematch(NClist* seglist1, NClist* seglist2) |
---|
679 | { |
---|
680 | int i,j; |
---|
681 | if((seglist1 == NULL || seglist2 == NULL) && seglist1 != seglist2) |
---|
682 | return 0; |
---|
683 | if(nclistlength(seglist1) != nclistlength(seglist2)) |
---|
684 | return 0; |
---|
685 | for(i=0;i<nclistlength(seglist1);i++) { |
---|
686 | DCEsegment* seg1 = (DCEsegment*)nclistget(seglist1,i); |
---|
687 | DCEsegment* seg2 = (DCEsegment*)nclistget(seglist2,i); |
---|
688 | if(seg1->rank != seg2->rank) |
---|
689 | return 0; |
---|
690 | for(j=0;j<seg1->rank;j++) { |
---|
691 | if(seg1->slices[j].first != seg2->slices[j].first |
---|
692 | || seg1->slices[j].count != seg2->slices[j].count |
---|
693 | || seg1->slices[j].stride != seg2->slices[j].stride) |
---|
694 | return 0; |
---|
695 | } |
---|
696 | } |
---|
697 | return 1; |
---|
698 | } |
---|
699 | |
---|
700 | /* Convert a CDFnode var to a projection; include |
---|
701 | pseudodimensions; always whole variable. |
---|
702 | */ |
---|
703 | int |
---|
704 | dapvar2projection(CDFnode* var, DCEprojection** projectionp) |
---|
705 | { |
---|
706 | int i,j; |
---|
707 | int ncstat = NC_NOERR; |
---|
708 | NClist* path = nclistnew(); |
---|
709 | NClist* segments; |
---|
710 | DCEprojection* projection = NULL; |
---|
711 | int dimindex; |
---|
712 | |
---|
713 | /* Collect the nodes needed to construct the projection segment */ |
---|
714 | collectnodepath3(var,path,!WITHDATASET); |
---|
715 | |
---|
716 | segments = nclistnew(); |
---|
717 | dimindex = 0; /* point to next subset of slices */ |
---|
718 | nclistsetalloc(segments,nclistlength(path)); |
---|
719 | for(i=0;i<nclistlength(path);i++) { |
---|
720 | DCEsegment* segment = (DCEsegment*)dcecreate(CES_SEGMENT); |
---|
721 | CDFnode* n = (CDFnode*)nclistget(path,i); |
---|
722 | int localrank; |
---|
723 | NClist* dimset; |
---|
724 | |
---|
725 | segment->annotation = (void*)n; |
---|
726 | segment->name = nulldup(n->ocname); |
---|
727 | /* We need to assign whole slices to each segment */ |
---|
728 | localrank = nclistlength(n->array.dimsetplus); |
---|
729 | segment->rank = localrank; |
---|
730 | dimset = n->array.dimsetplus; |
---|
731 | for(j=0;j<localrank;j++) { |
---|
732 | DCEslice* slice; |
---|
733 | CDFnode* dim; |
---|
734 | slice = &segment->slices[j]; |
---|
735 | dim = (CDFnode*)nclistget(dimset,j); |
---|
736 | ASSERT(dim->dim.declsize0 > 0); |
---|
737 | dcemakewholeslice(slice,dim->dim.declsize0); |
---|
738 | } |
---|
739 | segment->slicesdefined = 1; |
---|
740 | segment->slicesdeclized = 1; |
---|
741 | dimindex += localrank; |
---|
742 | nclistpush(segments,(ncelem)segment); |
---|
743 | } |
---|
744 | |
---|
745 | projection = (DCEprojection*)dcecreate(CES_PROJECT); |
---|
746 | projection->discrim = CES_VAR; |
---|
747 | projection->var = (DCEvar*)dcecreate(CES_VAR); |
---|
748 | projection->var->annotation = (void*)var; |
---|
749 | projection->var->segments = segments; |
---|
750 | |
---|
751 | #ifdef DEBUG1 |
---|
752 | fprintf(stderr,"dapvar2projection: projection=%s\n", |
---|
753 | dumpprojection(projection)); |
---|
754 | #endif |
---|
755 | |
---|
756 | nclistfree(path); |
---|
757 | if(ncstat) dcefree((DCEnode*)projection); |
---|
758 | else if(projectionp) *projectionp = projection; |
---|
759 | return ncstat; |
---|
760 | } |
---|
761 | |
---|
762 | /* |
---|
763 | Given a set of projections and a projection |
---|
764 | representing a variable (from, say vara or prefetch) |
---|
765 | construct a single projection for fetching that variable |
---|
766 | with the proper constraints. |
---|
767 | */ |
---|
768 | int |
---|
769 | daprestrictprojection(NClist* projections, DCEprojection* var, DCEprojection** resultp) |
---|
770 | { |
---|
771 | int ncstat = NC_NOERR; |
---|
772 | int i; |
---|
773 | DCEprojection* result = NULL; |
---|
774 | #ifdef DEBUG1 |
---|
775 | fprintf(stderr,"restrictprojection.before: constraints=|%s| vara=|%s|\n", |
---|
776 | dumpprojections(projections), |
---|
777 | dumpprojection(var)); |
---|
778 | #endif |
---|
779 | |
---|
780 | ASSERT(var != NULL); |
---|
781 | |
---|
782 | /* the projection list will contain at most 1 match for the var by construction */ |
---|
783 | for(result=null,i=0;i<nclistlength(projections);i++) { |
---|
784 | DCEprojection* p1 = (DCEprojection*)nclistget(projections,i); |
---|
785 | if(p1 == NULL || p1->discrim != CES_VAR) continue; |
---|
786 | if(p1->var->annotation == var->var->annotation) { |
---|
787 | result = p1; |
---|
788 | break; |
---|
789 | } |
---|
790 | } |
---|
791 | if(result == NULL) { |
---|
792 | result = (DCEprojection*)dceclone((DCEnode*)var); /* use only the var projection */ |
---|
793 | goto done; |
---|
794 | } |
---|
795 | result = (DCEprojection*)dceclone((DCEnode*)result); /* so we can modify */ |
---|
796 | |
---|
797 | #ifdef DEBUG1 |
---|
798 | fprintf(stderr,"restrictprojection.choice: |%s|\n",dumpprojection(result)); |
---|
799 | #endif |
---|
800 | /* We need to merge the projection from the projection list |
---|
801 | with the var projection |
---|
802 | */ |
---|
803 | ncstat = dcemergeprojections(result,var); /* result will be modified */ |
---|
804 | |
---|
805 | done: |
---|
806 | if(resultp) *resultp = result; |
---|
807 | #ifdef DEBUG |
---|
808 | fprintf(stderr,"restrictprojection.after=|%s|\n", |
---|
809 | dumpprojection(result)); |
---|
810 | #endif |
---|
811 | return ncstat; |
---|
812 | } |
---|
813 | |
---|
814 | /* Shift the slice so it runs from 0..count by step 1 */ |
---|
815 | static void |
---|
816 | dapshiftslice(DCEslice* slice) |
---|
817 | { |
---|
818 | size_t first = slice->first; |
---|
819 | size_t stride = slice->stride; |
---|
820 | if(first == 0 && stride == 1) return; /* no need to do anything */ |
---|
821 | slice->first = 0; |
---|
822 | slice->stride = 1; |
---|
823 | slice->length = slice->count; |
---|
824 | slice->stop = slice->count; |
---|
825 | } |
---|
826 | |
---|
827 | int |
---|
828 | dapshiftprojection(DCEprojection* projection) |
---|
829 | { |
---|
830 | int ncstat = NC_NOERR; |
---|
831 | int i,j; |
---|
832 | NClist* segments; |
---|
833 | |
---|
834 | #ifdef DEBUG1 |
---|
835 | fprintf(stderr,"dapshiftprojection.before: %s\n",dumpprojection(projection)); |
---|
836 | #endif |
---|
837 | |
---|
838 | ASSERT(projection->discrim == CES_VAR); |
---|
839 | segments = projection->var->segments; |
---|
840 | for(i=0;i<nclistlength(segments);i++) { |
---|
841 | DCEsegment* seg = (DCEsegment*)nclistget(segments,i); |
---|
842 | for(j=0;j<seg->rank;j++) { |
---|
843 | DCEslice* slice = seg->slices+j; |
---|
844 | dapshiftslice(slice); |
---|
845 | } |
---|
846 | } |
---|
847 | |
---|
848 | #ifdef DEBUG1 |
---|
849 | fprintf(stderr,"dapshiftprojection.after: %s\n",dumpprojection(projection)); |
---|
850 | #endif |
---|
851 | |
---|
852 | return ncstat; |
---|
853 | } |
---|