commit a copy of snit
[scpubgit/TenDotTcl.git] / snit / validate.tcl
CommitLineData
d4567ecb 1#-----------------------------------------------------------------------
2# TITLE:
3# validate.tcl
4#
5# AUTHOR:
6# Will Duquette
7#
8# DESCRIPTION:
9# Snit validation types.
10#
11#-----------------------------------------------------------------------
12
13namespace eval ::snit:: {
14 namespace export \
15 boolean \
16 double \
17 enum \
18 fpixels \
19 integer \
20 listtype \
21 pixels \
22 stringtype \
23 window
24}
25
26#-----------------------------------------------------------------------
27# snit::boolean
28
29snit::type ::snit::boolean {
30 #-------------------------------------------------------------------
31 # Type Methods
32
33 typemethod validate {value} {
34 if {![string is boolean -strict $value]} {
35 return -code error -errorcode INVALID \
36 "invalid boolean \"$value\", should be one of: 1, 0, true, false, yes, no, on, off"
37
38 }
39
40 return $value
41 }
42
43 #-------------------------------------------------------------------
44 # Constructor
45
46 # None needed; no options
47
48 #-------------------------------------------------------------------
49 # Public Methods
50
51 method validate {value} {
52 $type validate $value
53 }
54}
55
56#-----------------------------------------------------------------------
57# snit::double
58
59snit::type ::snit::double {
60 #-------------------------------------------------------------------
61 # Options
62
63 # -min value
64 #
65 # Minimum value
66
67 option -min -default "" -readonly 1
68
69 # -max value
70 #
71 # Maximum value
72
73 option -max -default "" -readonly 1
74
75 #-------------------------------------------------------------------
76 # Type Methods
77
78 typemethod validate {value} {
79 if {![string is double -strict $value]} {
80 return -code error -errorcode INVALID \
81 "invalid value \"$value\", expected double"
82 }
83
84 return $value
85 }
86
87 #-------------------------------------------------------------------
88 # Constructor
89
90 constructor {args} {
91 # FIRST, get the options
92 $self configurelist $args
93
94 if {"" != $options(-min) &&
95 ![string is double -strict $options(-min)]} {
96 return -code error \
97 "invalid -min: \"$options(-min)\""
98 }
99
100 if {"" != $options(-max) &&
101 ![string is double -strict $options(-max)]} {
102 return -code error \
103 "invalid -max: \"$options(-max)\""
104 }
105
106 if {"" != $options(-min) &&
107 "" != $options(-max) &&
108 $options(-max) < $options(-min)} {
109 return -code error "-max < -min"
110 }
111 }
112
113 #-------------------------------------------------------------------
114 # Public Methods
115
116 # Fixed method for the snit::double type.
117 # WHD, 6/7/2010.
118 method validate {value} {
119 $type validate $value
120
121 if {("" != $options(-min) && $value < $options(-min)) ||
122 ("" != $options(-max) && $value > $options(-max))} {
123
124 set msg "invalid value \"$value\", expected double"
125
126 if {"" != $options(-min) && "" != $options(-max)} {
127 append msg " in range $options(-min), $options(-max)"
128 } elseif {"" != $options(-min)} {
129 append msg " no less than $options(-min)"
130 } elseif {"" != $options(-max)} {
131 append msg " no greater than $options(-max)"
132 }
133
134 return -code error -errorcode INVALID $msg
135 }
136
137 return $value
138 }
139}
140
141#-----------------------------------------------------------------------
142# snit::enum
143
144snit::type ::snit::enum {
145 #-------------------------------------------------------------------
146 # Options
147
148 # -values list
149 #
150 # Valid values for this type
151
152 option -values -default {} -readonly 1
153
154 #-------------------------------------------------------------------
155 # Type Methods
156
157 typemethod validate {value} {
158 # No -values specified; it's always valid
159 return $value
160 }
161
162 #-------------------------------------------------------------------
163 # Constructor
164
165 constructor {args} {
166 $self configurelist $args
167
168 if {[llength $options(-values)] == 0} {
169 return -code error \
170 "invalid -values: \"\""
171 }
172 }
173
174 #-------------------------------------------------------------------
175 # Public Methods
176
177 method validate {value} {
178 if {[lsearch -exact $options(-values) $value] == -1} {
179 return -code error -errorcode INVALID \
180 "invalid value \"$value\", should be one of: [join $options(-values) {, }]"
181 }
182
183 return $value
184 }
185}
186
187#-----------------------------------------------------------------------
188# snit::fpixels
189
190snit::type ::snit::fpixels {
191 #-------------------------------------------------------------------
192 # Options
193
194 # -min value
195 #
196 # Minimum value
197
198 option -min -default "" -readonly 1
199
200 # -max value
201 #
202 # Maximum value
203
204 option -max -default "" -readonly 1
205
206 #-------------------------------------------------------------------
207 # Instance variables
208
209 variable min "" ;# -min, no suffix
210 variable max "" ;# -max, no suffix
211
212 #-------------------------------------------------------------------
213 # Type Methods
214
215 typemethod validate {value} {
216 if {[catch {winfo fpixels . $value} dummy]} {
217 return -code error -errorcode INVALID \
218 "invalid value \"$value\", expected fpixels"
219 }
220
221 return $value
222 }
223
224 #-------------------------------------------------------------------
225 # Constructor
226
227 constructor {args} {
228 # FIRST, get the options
229 $self configurelist $args
230
231 if {"" != $options(-min) &&
232 [catch {winfo fpixels . $options(-min)} min]} {
233 return -code error \
234 "invalid -min: \"$options(-min)\""
235 }
236
237 if {"" != $options(-max) &&
238 [catch {winfo fpixels . $options(-max)} max]} {
239 return -code error \
240 "invalid -max: \"$options(-max)\""
241 }
242
243 if {"" != $min &&
244 "" != $max &&
245 $max < $min} {
246 return -code error "-max < -min"
247 }
248 }
249
250 #-------------------------------------------------------------------
251 # Public Methods
252
253 method validate {value} {
254 $type validate $value
255
256 set val [winfo fpixels . $value]
257
258 if {("" != $min && $val < $min) ||
259 ("" != $max && $val > $max)} {
260
261 set msg "invalid value \"$value\", expected fpixels"
262
263 if {"" != $min && "" != $max} {
264 append msg " in range $options(-min), $options(-max)"
265 } elseif {"" != $min} {
266 append msg " no less than $options(-min)"
267 }
268
269 return -code error -errorcode INVALID $msg
270 }
271
272 return $value
273 }
274}
275
276#-----------------------------------------------------------------------
277# snit::integer
278
279snit::type ::snit::integer {
280 #-------------------------------------------------------------------
281 # Options
282
283 # -min value
284 #
285 # Minimum value
286
287 option -min -default "" -readonly 1
288
289 # -max value
290 #
291 # Maximum value
292
293 option -max -default "" -readonly 1
294
295 #-------------------------------------------------------------------
296 # Type Methods
297
298 typemethod validate {value} {
299 if {![string is integer -strict $value]} {
300 return -code error -errorcode INVALID \
301 "invalid value \"$value\", expected integer"
302 }
303
304 return $value
305 }
306
307 #-------------------------------------------------------------------
308 # Constructor
309
310 constructor {args} {
311 # FIRST, get the options
312 $self configurelist $args
313
314 if {"" != $options(-min) &&
315 ![string is integer -strict $options(-min)]} {
316 return -code error \
317 "invalid -min: \"$options(-min)\""
318 }
319
320 if {"" != $options(-max) &&
321 ![string is integer -strict $options(-max)]} {
322 return -code error \
323 "invalid -max: \"$options(-max)\""
324 }
325
326 if {"" != $options(-min) &&
327 "" != $options(-max) &&
328 $options(-max) < $options(-min)} {
329 return -code error "-max < -min"
330 }
331 }
332
333 #-------------------------------------------------------------------
334 # Public Methods
335
336 method validate {value} {
337 $type validate $value
338
339 if {("" != $options(-min) && $value < $options(-min)) ||
340 ("" != $options(-max) && $value > $options(-max))} {
341
342 set msg "invalid value \"$value\", expected integer"
343
344 if {"" != $options(-min) && "" != $options(-max)} {
345 append msg " in range $options(-min), $options(-max)"
346 } elseif {"" != $options(-min)} {
347 append msg " no less than $options(-min)"
348 }
349
350 return -code error -errorcode INVALID $msg
351 }
352
353 return $value
354 }
355}
356
357#-----------------------------------------------------------------------
358# snit::list
359
360snit::type ::snit::listtype {
361 #-------------------------------------------------------------------
362 # Options
363
364 # -type type
365 #
366 # Specifies a value type
367
368 option -type -readonly 1
369
370 # -minlen len
371 #
372 # Minimum list length
373
374 option -minlen -readonly 1 -default 0
375
376 # -maxlen len
377 #
378 # Maximum list length
379
380 option -maxlen -readonly 1
381
382 #-------------------------------------------------------------------
383 # Type Methods
384
385 typemethod validate {value} {
386 if {[catch {llength $value} result]} {
387 return -code error -errorcode INVALID \
388 "invalid value \"$value\", expected list"
389 }
390
391 return $value
392 }
393
394 #-------------------------------------------------------------------
395 # Constructor
396
397 constructor {args} {
398 # FIRST, get the options
399 $self configurelist $args
400
401 if {"" != $options(-minlen) &&
402 (![string is integer -strict $options(-minlen)] ||
403 $options(-minlen) < 0)} {
404 return -code error \
405 "invalid -minlen: \"$options(-minlen)\""
406 }
407
408 if {"" == $options(-minlen)} {
409 set options(-minlen) 0
410 }
411
412 if {"" != $options(-maxlen) &&
413 ![string is integer -strict $options(-maxlen)]} {
414 return -code error \
415 "invalid -maxlen: \"$options(-maxlen)\""
416 }
417
418 if {"" != $options(-maxlen) &&
419 $options(-maxlen) < $options(-minlen)} {
420 return -code error "-maxlen < -minlen"
421 }
422 }
423
424
425 #-------------------------------------------------------------------
426 # Methods
427
428 method validate {value} {
429 $type validate $value
430
431 set len [llength $value]
432
433 if {$len < $options(-minlen)} {
434 return -code error -errorcode INVALID \
435 "value has too few elements; at least $options(-minlen) expected"
436 } elseif {"" != $options(-maxlen)} {
437 if {$len > $options(-maxlen)} {
438 return -code error -errorcode INVALID \
439 "value has too many elements; no more than $options(-maxlen) expected"
440 }
441 }
442
443 # NEXT, check each value
444 if {"" != $options(-type)} {
445 foreach item $value {
446 set cmd $options(-type)
447 lappend cmd validate $item
448 uplevel \#0 $cmd
449 }
450 }
451
452 return $value
453 }
454}
455
456#-----------------------------------------------------------------------
457# snit::pixels
458
459snit::type ::snit::pixels {
460 #-------------------------------------------------------------------
461 # Options
462
463 # -min value
464 #
465 # Minimum value
466
467 option -min -default "" -readonly 1
468
469 # -max value
470 #
471 # Maximum value
472
473 option -max -default "" -readonly 1
474
475 #-------------------------------------------------------------------
476 # Instance variables
477
478 variable min "" ;# -min, no suffix
479 variable max "" ;# -max, no suffix
480
481 #-------------------------------------------------------------------
482 # Type Methods
483
484 typemethod validate {value} {
485 if {[catch {winfo pixels . $value} dummy]} {
486 return -code error -errorcode INVALID \
487 "invalid value \"$value\", expected pixels"
488 }
489
490 return $value
491 }
492
493 #-------------------------------------------------------------------
494 # Constructor
495
496 constructor {args} {
497 # FIRST, get the options
498 $self configurelist $args
499
500 if {"" != $options(-min) &&
501 [catch {winfo pixels . $options(-min)} min]} {
502 return -code error \
503 "invalid -min: \"$options(-min)\""
504 }
505
506 if {"" != $options(-max) &&
507 [catch {winfo pixels . $options(-max)} max]} {
508 return -code error \
509 "invalid -max: \"$options(-max)\""
510 }
511
512 if {"" != $min &&
513 "" != $max &&
514 $max < $min} {
515 return -code error "-max < -min"
516 }
517 }
518
519 #-------------------------------------------------------------------
520 # Public Methods
521
522 method validate {value} {
523 $type validate $value
524
525 set val [winfo pixels . $value]
526
527 if {("" != $min && $val < $min) ||
528 ("" != $max && $val > $max)} {
529
530 set msg "invalid value \"$value\", expected pixels"
531
532 if {"" != $min && "" != $max} {
533 append msg " in range $options(-min), $options(-max)"
534 } elseif {"" != $min} {
535 append msg " no less than $options(-min)"
536 }
537
538 return -code error -errorcode INVALID $msg
539 }
540
541 return $value
542 }
543}
544
545#-----------------------------------------------------------------------
546# snit::stringtype
547
548snit::type ::snit::stringtype {
549 #-------------------------------------------------------------------
550 # Options
551
552 # -minlen len
553 #
554 # Minimum list length
555
556 option -minlen -readonly 1 -default 0
557
558 # -maxlen len
559 #
560 # Maximum list length
561
562 option -maxlen -readonly 1
563
564 # -nocase 0|1
565 #
566 # globs and regexps are case-insensitive if -nocase 1.
567
568 option -nocase -readonly 1 -default 0
569
570 # -glob pattern
571 #
572 # Glob-match pattern, or ""
573
574 option -glob -readonly 1
575
576 # -regexp regexp
577 #
578 # Regular expression to match
579
580 option -regexp -readonly 1
581
582 #-------------------------------------------------------------------
583 # Type Methods
584
585 typemethod validate {value} {
586 # By default, any string (hence, any Tcl value) is valid.
587 return $value
588 }
589
590 #-------------------------------------------------------------------
591 # Constructor
592
593 constructor {args} {
594 # FIRST, get the options
595 $self configurelist $args
596
597 # NEXT, validate -minlen and -maxlen
598 if {"" != $options(-minlen) &&
599 (![string is integer -strict $options(-minlen)] ||
600 $options(-minlen) < 0)} {
601 return -code error \
602 "invalid -minlen: \"$options(-minlen)\""
603 }
604
605 if {"" == $options(-minlen)} {
606 set options(-minlen) 0
607 }
608
609 if {"" != $options(-maxlen) &&
610 ![string is integer -strict $options(-maxlen)]} {
611 return -code error \
612 "invalid -maxlen: \"$options(-maxlen)\""
613 }
614
615 if {"" != $options(-maxlen) &&
616 $options(-maxlen) < $options(-minlen)} {
617 return -code error "-maxlen < -minlen"
618 }
619
620 # NEXT, validate -nocase
621 if {[catch {snit::boolean validate $options(-nocase)} result]} {
622 return -code error "invalid -nocase: $result"
623 }
624
625 # Validate the glob
626 if {"" != $options(-glob) &&
627 [catch {string match $options(-glob) ""} dummy]} {
628 return -code error \
629 "invalid -glob: \"$options(-glob)\""
630 }
631
632 # Validate the regexp
633 if {"" != $options(-regexp) &&
634 [catch {regexp $options(-regexp) ""} dummy]} {
635 return -code error \
636 "invalid -regexp: \"$options(-regexp)\""
637 }
638 }
639
640
641 #-------------------------------------------------------------------
642 # Methods
643
644 method validate {value} {
645 # Usually we'd call [$type validate $value] here, but
646 # as it's a no-op, don't bother.
647
648 # FIRST, validate the length.
649 set len [string length $value]
650
651 if {$len < $options(-minlen)} {
652 return -code error -errorcode INVALID \
653 "too short: at least $options(-minlen) characters expected"
654 } elseif {"" != $options(-maxlen)} {
655 if {$len > $options(-maxlen)} {
656 return -code error -errorcode INVALID \
657 "too long: no more than $options(-maxlen) characters expected"
658 }
659 }
660
661 # NEXT, check the glob match, with or without case.
662 if {"" != $options(-glob)} {
663 if {$options(-nocase)} {
664 set result [string match -nocase $options(-glob) $value]
665 } else {
666 set result [string match $options(-glob) $value]
667 }
668
669 if {!$result} {
670 return -code error -errorcode INVALID \
671 "invalid value \"$value\""
672 }
673 }
674
675 # NEXT, check regexp match with or without case
676 if {"" != $options(-regexp)} {
677 if {$options(-nocase)} {
678 set result [regexp -nocase -- $options(-regexp) $value]
679 } else {
680 set result [regexp -- $options(-regexp) $value]
681 }
682
683 if {!$result} {
684 return -code error -errorcode INVALID \
685 "invalid value \"$value\""
686 }
687 }
688
689 return $value
690 }
691}
692
693#-----------------------------------------------------------------------
694# snit::window
695
696snit::type ::snit::window {
697 #-------------------------------------------------------------------
698 # Type Methods
699
700 typemethod validate {value} {
701 if {![winfo exists $value]} {
702 return -code error -errorcode INVALID \
703 "invalid value \"$value\", value is not a window"
704 }
705
706 return $value
707 }
708
709 #-------------------------------------------------------------------
710 # Constructor
711
712 # None needed; no options
713
714 #-------------------------------------------------------------------
715 # Public Methods
716
717 method validate {value} {
718 $type validate $value
719 }
720}