Skip to content

Commit 5200f25

Browse files
committed
more validation work + more perl wrapper work
1 parent 5a99ab8 commit 5200f25

18 files changed

+146
-73
lines changed

c/README.md

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,66 @@ contents of the XDI file along with a few particularly important items
4949

5050
## API
5151

52+
The API is intended to separate, as much as possible, the chores of
53+
parsing the file and validating its content.
54+
55+
The main point of entry is the function `XDI_readfile` which creates
56+
an `XDIFile` struct, then opens and parses a file for XDI content.
57+
This function will signal an error and issue a useful error message in
58+
the even that some content of the file precludes its being understood
59+
as XDI data. These situations include such things as
60+
61+
* Missing XDI versioning information in the first line of the file
62+
* Non-numeric content in the data table
63+
* Variable number of columns in the data table
64+
* Headers that cannot be interpreted as XDI metadata
65+
66+
Certain conditions that are non-conforming with the XDI specification
67+
but which do not preclude partial interpretation of the XDI file
68+
result in warning messages at this time.
69+
70+
Validating the content of the XDI metadata is treated as a separate
71+
step. There are three main functions for this purpose:
72+
73+
* `XDI_required_metadata`: This function tests that the pieces of
74+
metadata which are **required** by the specification are present in
75+
the file and intepretable. These three metadata items are
76+
`Mono.d_spacing`, `Element.symbol`, and `Element.edge`. If not
77+
present, this function returns a non-zero error code and issues an
78+
explanatory error message.
79+
80+
* `XDI_recommended_metadata`: This function tests that the pieces of
81+
metadata which are **recommended** by the specification are present
82+
in the file and intepretable. The absence of these items does not
83+
make the file non-compliant with the XDI specification, but their
84+
absence impacts the utility and quality of the data. If not
85+
present, this function returns a non-zero warning code and issues an
86+
explanatory error message.
87+
88+
* `XDI_validate_item`: This tests a specific metadata item to see if
89+
its value is interpretable according to its description in the XDI
90+
dictionary. Not all items defined in the dictionary have validation
91+
tests. If the value does not conform to its dictionary definition a
92+
non-zero warning code is returned and an explanatory error message
93+
is issued. The purpose of this validation tool is to aid in
94+
automated quality assurance. For example, data by a user for
95+
consideration in a curated standards database might require that all
96+
automated validation tests pass before the data are passed along to
97+
a human for further consideration.
98+
99+
All error messages are returned in English as the content of the
100+
`error_message` attribute of the `XDIFile` struct. The
101+
`error_message` attribute always contains a description of the error
102+
condition of the most recently performed action. The relation between
103+
the returned error/warning codes and teh error messages are tabulated
104+
below.
105+
106+
The value of separating most validation chores from the parsing of the
107+
file is that it allows an application to choose to accept structurally
108+
compliant data then to perform the level of validation appropriate to
109+
the task at hand.
110+
111+
52112
### Read an XDI file
53113

54114
Read an XDI file, store it's content in an XDIFile struct, and return
@@ -59,7 +119,7 @@ an integer return code.
59119
int ret;
60120

61121
xdifile = malloc(sizeof(XDIFile));
62-
ret = XDI_readfile("mydata.xdi", xdifile);
122+
ret = XDI_readfile(xdifile, "mydata.xdi");
63123
```
64124

65125
### Interpret the XDI_readfile error code
@@ -163,6 +223,20 @@ table. The return value is -1 if the array cannot be retrieved.
163223

164224
The array names are held in the `Column.N` metadata fields.
165225

226+
### Extract indexed arrays from the data table
227+
228+
```C
229+
double *enarray, *muarray;
230+
enarray = (double *)calloc(xdifile->npts, sizeof(double));
231+
muarray = (double *)calloc(xdifile->npts, sizeof(double));
232+
ret = XDI_get_array_index(xdifile, 1, enarray);
233+
ret = XDI_get_array_index(xdifile, 2, muarray);
234+
```
235+
236+
The return value is 0 if an array with that index is in the data
237+
table. That is, the index argument must be smaller than the `narrays`
238+
attribute. The return value is -1 if the array cannot be retrieved.
239+
166240
### Destroy and deallocate the XDIFile struct
167241

168242
To deallocate the memory from the XDIFile struct, do this:

c/xdifile.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,16 +570,22 @@ XDI_readfile(char *filename, XDIFile *xdifile) {
570570
/* ============================================================================ */
571571
/* array management section */
572572

573+
#define ENOUGH ((8 * sizeof(int) - 1) / 3 + 2)
574+
573575
_EXPORT(int)
574576
XDI_get_array_index(XDIFile *xdifile, long n, double *out) {
575577
/* get array by index (starting at 0) from an XDIFile structure */
576578
long j;
579+
char str[ENOUGH];
577580
if (n < xdifile->narrays) {
578581
for (j = 0; j < xdifile->npts; j++) {
579582
out[j] = xdifile->array[n][j];
580583
}
581584
return 0;
582585
}
586+
strcpy(xdifile->error_message, "no array of index ");
587+
sprintf(str, "%ld", n);
588+
strcat(xdifile->error_message, str);
583589
return -1;
584590
}
585591

@@ -592,6 +598,8 @@ XDI_get_array_name(XDIFile *xdifile, char *name, double *out) {
592598
return XDI_get_array_index(xdifile, i, out);
593599
}
594600
}
601+
strcpy(xdifile->error_message, "no array of name ");
602+
strcat(xdifile->error_message, name);
595603
return -1;
596604
}
597605

@@ -780,6 +788,9 @@ int XDI_validate_mono(XDIFile *xdifile, char *name, char *value) {
780788
return err;
781789
}
782790

791+
/* all tests in the Sample familty should return WRN_BAD_SAMPLE if the */
792+
/* value does not match the definition. error_message should explain */
793+
/* the problem in a way that is appropruiate to the metadata item */
783794
int XDI_validate_sample(XDIFile *xdifile, char *name, char *value) {
784795
int err;
785796
int regex_status;

c/xdifile.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,5 +155,4 @@ static char *ValidElems[] =
155155
#define ERR_NONNUMERIC -32 /* used */
156156
#define ERR_MEMERROR -64 /* NOT used */
157157

158-
159158
/* _EXPORT(char*) XDI_errorstring(int errcode); */

perl/lib/Xray/XDI.pm

Lines changed: 45 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@ has 'xdifile' => (
2525
builder => '_build_object',
2626
);
2727
# no need to fiddle with inline_constructor here
28-
has 'ok' => (is => 'rw', isa => 'Bool', traits => [qw(NoClone)], default => 0);
29-
has 'warning' => (is => 'rw', isa => 'Bool', traits => [qw(NoClone)], default => 0);
3028
has 'errorcode' => (is => 'rw', isa => 'Int', traits => [qw(NoClone)], default => 0);
31-
has 'error' => (is => 'rw', isa => 'Str', traits => [qw(NoClone)], default => q{});
3229
has 'errormessage' => (is => 'rw', isa => 'Str', traits => [qw(NoClone)], default => q{});
3330

3431
has 'filename' => (is => 'rw', isa => 'Str', traits => [qw(NoClone)], default => q{});
@@ -83,30 +80,24 @@ sub DEMOLISH {
8380
sub _build_object {
8481
my ($self) = @_;
8582
$self->errormessage(q{});
86-
$self->ok(1);
87-
$self->warning(0);
8883
if (not $self->file) {
8984
$self->errorcode(1);
9085
$self->errormessage('No file specified');
91-
$self->ok(0);
9286
return undef;
9387
};
9488
if (not -e $self->file) {
9589
$self->errorcode(1);
9690
$self->errormessage('The file '.$self->file.' does not exist');
97-
$self->ok(0);
9891
return undef;
9992
};
10093
if (not -r $self->file) {
10194
$self->errorcode(1);
10295
$self->errormessage('The file '.$self->file.' cannot be read');
103-
$self->ok(0);
10496
return undef;
10597
};
10698
if (-d $self->file) {
10799
$self->errorcode(1);
108100
$self->errormessage($self->file.' is a folder (i.e. not an XDI file)');
109-
$self->ok(0);
110101
return undef;
111102
};
112103
my $errcode = 0;
@@ -116,34 +107,8 @@ sub _build_object {
116107

117108
return $obj if ($errcode < 0);
118109

119-
#print $self->file, $/;
120-
#$self->trace;
121-
#print '>>>>>', $errcode, $/;
122-
123-
##### see xdifile.h for error codes
124-
##### see xdifile.c (line 23 and following) for error messages
125-
# if ($errcode < 0) {
126-
# my @errors = ();
127-
# foreach my $i (0 .. 10) {
128-
# push @errors, $obj->_errorstring(-1*2**$i) if (abs($errcode) & 2**$i);
129-
# };
130-
# $self->error(join(", ", @errors));
131-
# $self->ok(0);
132-
# return $obj;
133-
# };
134-
# if ($errcode > 0) {
135-
# my @errors = ();
136-
# foreach my $i (0 .. 4) {
137-
# push @errors, $obj->_errorstring(2**$i) if ($errcode & 2**$i);
138-
# };
139-
# $self->error(join(", ", @errors));
140-
# $self->ok(1);
141-
# $self->warning(1);
142-
# };
143-
144110
if (not defined $obj->_filename) {
145-
$self->error('unknown problem reading '.$self->file.' as an XDI file');
146-
$self->ok(0);
111+
$self->errormessage('unknown problem reading '.$self->file.' as an XDI file');
147112
return $obj;
148113
};
149114

@@ -356,10 +321,10 @@ Import an XDI file:
356321
357322
use Xray::XDI;
358323
my $xdi = Xray::XDI->new(file=>'data.dat');
359-
if ($xdi->ok) {
324+
if ($xdi->errorcode == 0) {
360325
# do stuff
361326
} else {
362-
print "Uh oh! ", $xdi->error, $/;
327+
print "Uh oh! ", $xdi->errormessage, $/;
363328
};
364329
365330
Export an XDI file:
@@ -376,16 +341,17 @@ The fully resolved path to the XDI file. Setting this triggers the
376341
importing of the file and the setting of all other attributes from the
377342
contents of the file.
378343
379-
=item C<ok>
344+
=item C<errormessage>
380345
381-
This is true when C<file> is properly imported. When false, the
382-
problem will be recorded in the C<error> attribute.
346+
When an XDI file is imported properly, this is an empty string. When
347+
import or validation runs into a problem, the explanation will be
348+
stroed here as a string.
383349
384-
=item C<error>
350+
=item C<errorcode>
385351
386-
When an XDI file is imported properly, this is an empty string. When
387-
import runs into a problem, the explanation will be stroed here as a
388-
string.
352+
The numeric code returned by C<libxdifile> when a problem is
353+
encountered. The C<errorcode> always corresponds to the
354+
C<errormessage>.
389355
390356
=item C<xdifile>
391357
@@ -411,6 +377,10 @@ The element of the absorber.
411377
412378
The absorption edge at which the data were measured.
413379
380+
=item C<dspacing>
381+
382+
The d-spacing (or line spacing) of the mono used in the measurement.
383+
414384
=item C<comments>
415385
416386
The user supplied comments with all white space and carriage returns
@@ -467,6 +437,31 @@ Alternately,
467437
## later....
468438
$xdi -> file('/path/to/file');
469439
440+
=item C<required>
441+
442+
Test the XDIFile object for whether it contains the metadata items
443+
B<required> by the XDI spec, C<Mono.d_spacing>, C<Element.symbol>, and
444+
C<Element.edge>.
445+
446+
my $code = $xdi->required;
447+
print $xdi->error_message, $/ if ($code);
448+
449+
=item C<recommended>
450+
451+
Test the XDIFile object for whether it contains the metadata items
452+
B<recommended> by the XDI spec.
453+
454+
my $code = $xdi->recommended;
455+
print $xdi->error_message, $/ if ($code);
456+
457+
=item C<validate>
458+
459+
Validate a sepcific metadata item against its definition in the XDI
460+
dictionary.
461+
462+
my $code = $xdi->validate($family, $name, $value);
463+
print $xdi->error_message, $/ if ($code);
464+
470465
=item C<labels>
471466
472467
Return a list of column labels in the order of appearance in the XDI
@@ -567,17 +562,6 @@ in brackets.
567562
568563
=back
569564
570-
=head1 VALIDATION
571-
572-
If the sole intent is to validate an XDI file, i.e. to determine
573-
whether or not it conforms to the specification, it should be adequate
574-
to do something like the following:
575-
576-
$xdi = Xray::XDI->new('somefile.dat');
577-
$is_valid = $xdi->ok;
578-
$problem = $xdi->error if not $is_valid;
579-
undef $xdi;
580-
print "okee dokee!\n" if $is_valid;
581565
582566
=head1 DIAGNOSTICS
583567
@@ -608,6 +592,11 @@ L<Moose>, L<MooseX::NonMoose>
608592
609593
=over 4
610594
595+
=iten *
596+
597+
need a validate method that reads, runs required and recommend, and
598+
validates all metadata, returning true if no problems found.
599+
611600
=item *
612601
613602
need an add data column method

perl/t/01_moose_nonmoose.t

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ my $here = dirname($0);
1717
my $file = File::Spec->catfile($here, '..', '..', 'data', 'co_metal_rt.xdi');
1818
my $xdi = Xray::XDI->new(file=>$file);
1919
ok($xdi =~ m{Xray::XDI}, 'created Xray::XDI object');
20-
ok($xdi->ok, 'ok flag is true');
21-
ok($xdi->error eq '', 'error text is empty');
20+
ok(($xdi->errorcode == 0), 'errorcode 0');
21+
ok(($xdi->errormessage =~ m{\A\s*\z}), 'error text is empty');
2222

2323
ok( $xdi->token('comment') eq '#', 'token: comment');
2424
ok( $xdi->token('delimiter') eq ':', 'token: delimiter');

perl/t/02_moosish.t

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ my $here = dirname($0);
1717
my $file = File::Spec->catfile($here, '..', '..', 'data', 'co_metal_rt.xdi');
1818
my $xdi = Xray::XDI->new(file=>$file);
1919

20-
ok($xdi->ok, 'file imported properly');
20+
ok($xdi->errorcode == 0, 'file imported properly');
2121

2222
##### test things that return arrays of strings #################
2323

perl/t/gooddata/01_cu_metal_10k.t

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ my $here = dirname($0);
1717
my $file = File::Spec->catfile($here, '..', '..', '..', 'data', 'cu_metal_10K.xdi');
1818
my $xdi = Xray::XDI->new(file=>$file);
1919

20-
ok($xdi->ok, 'file imported properly');
20+
ok($xdi->errorcode == 0, 'file imported properly');
2121

2222
##### test things that return arrays of strings #################
2323

perl/t/gooddata/02_cu_metal_rt.t

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ my $here = dirname($0);
1717
my $file = File::Spec->catfile($here, '..', '..', '..', 'data', 'cu_metal_rt.xdi');
1818
my $xdi = Xray::XDI->new(file=>$file);
1919

20-
ok($xdi->ok, 'file imported properly');
20+
ok($xdi->errorcode == 0, 'file imported properly');
2121

2222
##### test things that return arrays of strings #################
2323

perl/t/gooddata/03_fe2o3.t

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ my $here = dirname($0);
1717
my $file = File::Spec->catfile($here, '..', '..', '..', 'data', 'fe2o3_rt.xdi');
1818
my $xdi = Xray::XDI->new(file=>$file);
1919

20-
ok($xdi->ok, 'file imported properly');
20+
ok($xdi->errorcode == 0, 'file imported properly');
2121

2222
##### test things that return arrays of strings #################
2323

perl/t/gooddata/04_fe3c_rt.t

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ my $here = dirname($0);
1717
my $file = File::Spec->catfile($here, '..', '..', '..', 'data', 'fe3c_rt.xdi');
1818
my $xdi = Xray::XDI->new(file=>$file);
1919

20-
ok($xdi->ok, 'file imported properly');
20+
ok($xdi->errorcode == 0, 'file imported properly');
2121

2222
##### test things that return arrays of strings #################
2323

0 commit comments

Comments
 (0)