PageRenderTime 169ms CodeModel.GetById 42ms app.highlight 121ms RepoModel.GetById 0ms app.codeStats 1ms

/tools/Ruby/lib/ruby/1.8/xsd/datatypes.rb

http://github.com/agross/netopenspace
Ruby | 1269 lines | 992 code | 224 blank | 53 comment | 74 complexity | c6b57d3c397b31011746a17cf8013d1d MD5 | raw file
   1# XSD4R - XML Schema Datatype implementation.
   2# Copyright (C) 2000, 2001, 2002, 2003  NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
   3
   4# This program is copyrighted free software by NAKAMURA, Hiroshi.  You can
   5# redistribute it and/or modify it under the same terms of Ruby's license;
   6# either the dual license version in 2003, or any later version.
   7
   8
   9require 'xsd/qname'
  10require 'xsd/charset'
  11require 'uri'
  12
  13
  14###
  15## XMLSchamaDatatypes general definitions.
  16#
  17module XSD
  18
  19
  20Namespace = 'http://www.w3.org/2001/XMLSchema'
  21InstanceNamespace = 'http://www.w3.org/2001/XMLSchema-instance'
  22
  23AttrType = 'type'
  24NilValue = 'true'
  25
  26AnyTypeLiteral = 'anyType'
  27AnySimpleTypeLiteral = 'anySimpleType'
  28NilLiteral = 'nil'
  29StringLiteral = 'string'
  30BooleanLiteral = 'boolean'
  31DecimalLiteral = 'decimal'
  32FloatLiteral = 'float'
  33DoubleLiteral = 'double'
  34DurationLiteral = 'duration'
  35DateTimeLiteral = 'dateTime'
  36TimeLiteral = 'time'
  37DateLiteral = 'date'
  38GYearMonthLiteral = 'gYearMonth'
  39GYearLiteral = 'gYear'
  40GMonthDayLiteral = 'gMonthDay'
  41GDayLiteral = 'gDay'
  42GMonthLiteral = 'gMonth'
  43HexBinaryLiteral = 'hexBinary'
  44Base64BinaryLiteral = 'base64Binary'
  45AnyURILiteral = 'anyURI'
  46QNameLiteral = 'QName'
  47
  48NormalizedStringLiteral = 'normalizedString'
  49#3.3.2 token
  50#3.3.3 language
  51#3.3.4 NMTOKEN
  52#3.3.5 NMTOKENS
  53#3.3.6 Name
  54#3.3.7 NCName
  55#3.3.8 ID
  56#3.3.9 IDREF
  57#3.3.10 IDREFS
  58#3.3.11 ENTITY
  59#3.3.12 ENTITIES
  60IntegerLiteral = 'integer'
  61NonPositiveIntegerLiteral = 'nonPositiveInteger'
  62NegativeIntegerLiteral = 'negativeInteger'
  63LongLiteral = 'long'
  64IntLiteral = 'int'
  65ShortLiteral = 'short'
  66ByteLiteral = 'byte'
  67NonNegativeIntegerLiteral = 'nonNegativeInteger'
  68UnsignedLongLiteral = 'unsignedLong'
  69UnsignedIntLiteral = 'unsignedInt'
  70UnsignedShortLiteral = 'unsignedShort'
  71UnsignedByteLiteral = 'unsignedByte'
  72PositiveIntegerLiteral = 'positiveInteger'
  73
  74AttrTypeName = QName.new(InstanceNamespace, AttrType)
  75AttrNilName = QName.new(InstanceNamespace, NilLiteral)
  76
  77AnyTypeName = QName.new(Namespace, AnyTypeLiteral)
  78AnySimpleTypeName = QName.new(Namespace, AnySimpleTypeLiteral)
  79
  80class Error < StandardError; end
  81class ValueSpaceError < Error; end
  82
  83
  84###
  85## The base class of all datatypes with Namespace.
  86#
  87class NSDBase
  88  @@types = []
  89
  90  attr_accessor :type
  91
  92  def self.inherited(klass)
  93    @@types << klass
  94  end
  95
  96  def self.types
  97    @@types
  98  end
  99
 100  def initialize
 101  end
 102
 103  def init(type)
 104    @type = type
 105  end
 106end
 107
 108
 109###
 110## The base class of XSD datatypes.
 111#
 112class XSDAnySimpleType < NSDBase
 113  include XSD
 114  Type = QName.new(Namespace, AnySimpleTypeLiteral)
 115
 116  # @data represents canonical space (ex. Integer: 123).
 117  attr_reader :data
 118  # @is_nil represents this data is nil or not.
 119  attr_accessor :is_nil
 120
 121  def initialize(value = nil)
 122    init(Type, value)
 123  end
 124
 125  # true or raise
 126  def check_lexical_format(value)
 127    screen_data(value)
 128    true
 129  end
 130
 131  # set accepts a string which follows lexical space (ex. String: "+123"), or
 132  # an object which follows canonical space (ex. Integer: 123).
 133  def set(value)
 134    if value.nil?
 135      @is_nil = true
 136      @data = nil
 137      _set(nil)
 138    else
 139      @is_nil = false
 140      _set(screen_data(value))
 141    end
 142  end
 143
 144  # to_s creates a string which follows lexical space (ex. String: "123").
 145  def to_s()
 146    if @is_nil
 147      ""
 148    else
 149      _to_s
 150    end
 151  end
 152
 153private
 154
 155  def init(type, value)
 156    super(type)
 157    set(value)
 158  end
 159
 160  # raises ValueSpaceError if check failed
 161  def screen_data(value)
 162    value
 163  end
 164
 165  def _set(value)
 166    @data = value
 167  end
 168
 169  def _to_s
 170    @data.to_s
 171  end
 172end
 173
 174class XSDNil < XSDAnySimpleType
 175  Type = QName.new(Namespace, NilLiteral)
 176  Value = 'true'
 177
 178  def initialize(value = nil)
 179    init(Type, value)
 180  end
 181end
 182
 183
 184###
 185## Primitive datatypes.
 186#
 187class XSDString < XSDAnySimpleType
 188  Type = QName.new(Namespace, StringLiteral)
 189
 190  def initialize(value = nil)
 191    init(Type, value)
 192  end
 193
 194private
 195
 196  def screen_data(value)
 197    unless XSD::Charset.is_ces(value, XSD::Charset.encoding)
 198      raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
 199    end
 200    value
 201  end
 202end
 203
 204class XSDBoolean < XSDAnySimpleType
 205  Type = QName.new(Namespace, BooleanLiteral)
 206
 207  def initialize(value = nil)
 208    init(Type, value)
 209  end
 210
 211private
 212
 213  def screen_data(value)
 214    if value.is_a?(String)
 215      str = value.strip
 216      if str == 'true' || str == '1'
 217	true
 218      elsif str == 'false' || str == '0'
 219	false
 220      else
 221	raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
 222      end
 223    else
 224      value ? true : false
 225    end
 226  end
 227end
 228
 229class XSDDecimal < XSDAnySimpleType
 230  Type = QName.new(Namespace, DecimalLiteral)
 231
 232  def initialize(value = nil)
 233    init(Type, value)
 234  end
 235
 236  def nonzero?
 237    (@number != '0')
 238  end
 239
 240private
 241
 242  def screen_data(d)
 243    if d.is_a?(String)
 244      # Integer("00012") => 10 in Ruby.
 245      d.sub!(/^([+\-]?)0*(?=\d)/, "\\1")
 246    end
 247    screen_data_str(d)
 248  end
 249
 250  def screen_data_str(str)
 251    /^([+\-]?)(\d*)(?:\.(\d*)?)?$/ =~ str.to_s.strip
 252    unless Regexp.last_match
 253      raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
 254    end
 255    sign = $1 || '+'
 256    int_part = $2
 257    frac_part = $3
 258    int_part = '0' if int_part.empty?
 259    frac_part = frac_part ? frac_part.sub(/0+$/, '') : ''
 260    point = - frac_part.size
 261    number = int_part + frac_part
 262    # normalize
 263    if sign == '+'
 264      sign = ''
 265    elsif sign == '-'
 266      if number == '0'
 267	sign = ''
 268      end
 269    end
 270    [sign, point, number]
 271  end
 272
 273  def _set(data)
 274    if data.nil?
 275      @sign = @point = @number = @data = nil
 276      return
 277    end
 278    @sign, @point, @number = data
 279    @data = _to_s
 280    @data.freeze
 281  end
 282
 283  # 0.0 -> 0; right?
 284  def _to_s
 285    str = @number.dup
 286    if @point.nonzero?
 287      str[@number.size + @point, 0] = '.'
 288    end
 289    @sign + str
 290  end
 291end
 292
 293module FloatConstants
 294  NaN = 0.0/0.0
 295  POSITIVE_INF = +1.0/0.0
 296  NEGATIVE_INF = -1.0/0.0
 297  POSITIVE_ZERO = +1.0/POSITIVE_INF
 298  NEGATIVE_ZERO = -1.0/POSITIVE_INF
 299  MIN_POSITIVE_SINGLE = 2.0 ** -149
 300end
 301
 302class XSDFloat < XSDAnySimpleType
 303  include FloatConstants
 304  Type = QName.new(Namespace, FloatLiteral)
 305
 306  def initialize(value = nil)
 307    init(Type, value)
 308  end
 309
 310private
 311
 312  def screen_data(value)
 313    # "NaN".to_f => 0 in some environment.  libc?
 314    if value.is_a?(Float)
 315      return narrow32bit(value)
 316    end
 317    str = value.to_s.strip
 318    if str == 'NaN'
 319      NaN
 320    elsif str == 'INF'
 321      POSITIVE_INF
 322    elsif str == '-INF'
 323      NEGATIVE_INF
 324    else
 325      if /^[+\-\.\deE]+$/ !~ str
 326	raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
 327      end
 328      # Float("-1.4E") might fail on some system.
 329      str << '0' if /e$/i =~ str
 330      begin
 331  	return narrow32bit(Float(str))
 332      rescue ArgumentError
 333  	raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
 334      end
 335    end
 336  end
 337
 338  def _to_s
 339    if @data.nan?
 340      'NaN'
 341    elsif @data.infinite? == 1
 342      'INF'
 343    elsif @data.infinite? == -1
 344      '-INF'
 345    else
 346      sign = XSDFloat.positive?(@data) ? '+' : '-'
 347      sign + sprintf("%.10g", @data.abs).sub(/[eE]([+-])?0+/) { 'e' + $1 }
 348    end
 349  end
 350
 351  # Convert to single-precision 32-bit floating point value.
 352  def narrow32bit(f)
 353    if f.nan? || f.infinite?
 354      f
 355    elsif f.abs < MIN_POSITIVE_SINGLE
 356      XSDFloat.positive?(f) ? POSITIVE_ZERO : NEGATIVE_ZERO
 357    else
 358      f
 359    end
 360  end
 361
 362  def self.positive?(value)
 363    (1 / value) > 0.0
 364  end
 365end
 366
 367# Ruby's Float is double-precision 64-bit floating point value.
 368class XSDDouble < XSDAnySimpleType
 369  include FloatConstants
 370  Type = QName.new(Namespace, DoubleLiteral)
 371
 372  def initialize(value = nil)
 373    init(Type, value)
 374  end
 375
 376private
 377
 378  def screen_data(value)
 379    # "NaN".to_f => 0 in some environment.  libc?
 380    if value.is_a?(Float)
 381      return value
 382    end
 383    str = value.to_s.strip
 384    if str == 'NaN'
 385      NaN
 386    elsif str == 'INF'
 387      POSITIVE_INF
 388    elsif str == '-INF'
 389      NEGATIVE_INF
 390    else
 391      begin
 392	return Float(str)
 393      rescue ArgumentError
 394	# '1.4e' cannot be parsed on some architecture.
 395	if /e\z/i =~ str
 396	  begin
 397	    return Float(str + '0')
 398	  rescue ArgumentError
 399	    raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
 400	  end
 401	else
 402	  raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
 403	end
 404      end
 405    end
 406  end
 407
 408  def _to_s
 409    if @data.nan?
 410      'NaN'
 411    elsif @data.infinite? == 1
 412      'INF'
 413    elsif @data.infinite? == -1
 414      '-INF'
 415    else
 416      sign = (1 / @data > 0.0) ? '+' : '-'
 417      sign + sprintf("%.16g", @data.abs).sub(/[eE]([+-])?0+/) { 'e' + $1 }
 418    end
 419  end
 420end
 421
 422class XSDDuration < XSDAnySimpleType
 423  Type = QName.new(Namespace, DurationLiteral)
 424
 425  attr_accessor :sign
 426  attr_accessor :year
 427  attr_accessor :month
 428  attr_accessor :day
 429  attr_accessor :hour
 430  attr_accessor :min
 431  attr_accessor :sec
 432
 433  def initialize(value = nil)
 434    init(Type, value)
 435  end
 436
 437private
 438
 439  def screen_data(value)
 440    /^([+\-]?)P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?$/ =~ value.to_s.strip
 441    unless Regexp.last_match
 442      raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
 443    end
 444    if ($5 and ((!$2 and !$3 and !$4) or (!$6 and !$7 and !$8)))
 445      # Should we allow 'PT5S' here?
 446      raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
 447    end
 448    sign = $1
 449    year = $2.to_i
 450    month = $3.to_i
 451    day = $4.to_i
 452    hour = $6.to_i
 453    min = $7.to_i
 454    sec = $8 ? XSDDecimal.new($8) : 0
 455    [sign, year, month, day, hour, min, sec]
 456  end
 457
 458  def _set(data)
 459    if data.nil?
 460      @sign = @year = @month = @day = @hour = @min = @sec = @data = nil
 461      return
 462    end
 463    @sign, @year, @month, @day, @hour, @min, @sec = data
 464    @data = _to_s
 465    @data.freeze
 466  end
 467
 468  def _to_s
 469    str = ''
 470    str << @sign if @sign
 471    str << 'P'
 472    l = ''
 473    l << "#{ @year }Y" if @year.nonzero?
 474    l << "#{ @month }M" if @month.nonzero?
 475    l << "#{ @day }D" if @day.nonzero?
 476    r = ''
 477    r << "#{ @hour }H" if @hour.nonzero?
 478    r << "#{ @min }M" if @min.nonzero?
 479    r << "#{ @sec }S" if @sec.nonzero?
 480    str << l
 481    if l.empty?
 482      str << "0D"
 483    end
 484    unless r.empty?
 485      str << "T" << r
 486    end
 487    str
 488  end
 489end
 490
 491
 492require 'rational'
 493require 'date'
 494
 495module XSDDateTimeImpl
 496  SecInDay = 86400	# 24 * 60 * 60
 497
 498  def to_obj(klass)
 499    if klass == Time
 500      to_time
 501    elsif klass == Date
 502      to_date
 503    elsif klass == DateTime
 504      to_datetime
 505    else
 506      nil
 507    end
 508  end
 509
 510  def to_time
 511    begin
 512      if @data.offset * SecInDay == Time.now.utc_offset
 513        d = @data
 514	usec = (d.sec_fraction * SecInDay * 1000000).round
 515        Time.local(d.year, d.month, d.mday, d.hour, d.min, d.sec, usec)
 516      else
 517        d = @data.newof
 518	usec = (d.sec_fraction * SecInDay * 1000000).round
 519        Time.gm(d.year, d.month, d.mday, d.hour, d.min, d.sec, usec)
 520      end
 521    rescue ArgumentError
 522      nil
 523    end
 524  end
 525
 526  def to_date
 527    Date.new0(@data.class.jd_to_ajd(@data.jd, 0, 0), 0, @data.start)
 528  end
 529
 530  def to_datetime
 531    data
 532  end
 533
 534  def tz2of(str)
 535    /^(?:Z|(?:([+\-])(\d\d):(\d\d))?)$/ =~ str
 536    sign = $1
 537    hour = $2.to_i
 538    min = $3.to_i
 539
 540    of = case sign
 541      when '+'
 542	of = +(hour.to_r * 60 + min) / 1440	# 24 * 60
 543      when '-'
 544	of = -(hour.to_r * 60 + min) / 1440	# 24 * 60
 545      else
 546	0
 547      end
 548    of
 549  end
 550
 551  def of2tz(offset)
 552    diffmin = offset * 24 * 60
 553    if diffmin.zero?
 554      'Z'
 555    else
 556      ((diffmin < 0) ? '-' : '+') << format('%02d:%02d',
 557    	(diffmin.abs / 60.0).to_i, (diffmin.abs % 60.0).to_i)
 558    end
 559  end
 560
 561  def screen_data(t)
 562    # convert t to a DateTime as an internal representation.
 563    if t.respond_to?(:to_datetime)      # 1.9 or later
 564      t.to_datetime
 565    elsif t.is_a?(DateTime)
 566      t
 567    elsif t.is_a?(Date)
 568      t = screen_data_str(t)
 569      t <<= 12 if t.year < 0
 570      t
 571    elsif t.is_a?(Time)
 572      jd = DateTime.civil_to_jd(t.year, t.mon, t.mday, DateTime::ITALY)
 573      fr = DateTime.time_to_day_fraction(t.hour, t.min, [t.sec, 59].min) +
 574        t.usec.to_r / 1000000 / SecInDay
 575      of = t.utc_offset.to_r / SecInDay
 576      DateTime.new0(DateTime.jd_to_ajd(jd, fr, of), of, DateTime::ITALY)
 577    else
 578      screen_data_str(t)
 579    end
 580  end
 581
 582  def add_tz(s)
 583    s + of2tz(@data.offset)
 584  end
 585end
 586
 587class XSDDateTime < XSDAnySimpleType
 588  include XSDDateTimeImpl
 589  Type = QName.new(Namespace, DateTimeLiteral)
 590
 591  def initialize(value = nil)
 592    init(Type, value)
 593  end
 594
 595private
 596
 597  def screen_data_str(t)
 598    /^([+\-]?\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d(?:\.(\d*))?)(Z|(?:[+\-]\d\d:\d\d)?)?$/ =~ t.to_s.strip
 599    unless Regexp.last_match
 600      raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
 601    end
 602    if $1 == '0000'
 603      raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
 604    end
 605    year = $1.to_i
 606    if year < 0
 607      year += 1
 608    end
 609    mon = $2.to_i
 610    mday = $3.to_i
 611    hour = $4.to_i
 612    min = $5.to_i
 613    sec = $6.to_i
 614    secfrac = $7
 615    zonestr = $8
 616    data = DateTime.civil(year, mon, mday, hour, min, sec, tz2of(zonestr))
 617    if secfrac
 618      diffday = secfrac.to_i.to_r / (10 ** secfrac.size) / SecInDay
 619      data += diffday
 620      # FYI: new0 and jd_to_rjd are not necessary to use if you don't have
 621      # exceptional reason.
 622    end
 623    [data, secfrac]
 624  end
 625
 626  def _set(data)
 627    if data.nil?
 628      @data = @secfrac = nil
 629      return
 630    end
 631    @data, @secfrac = data
 632  end
 633
 634  def _to_s
 635    year = (@data.year > 0) ? @data.year : @data.year - 1
 636    s = format('%.4d-%02d-%02dT%02d:%02d:%02d',
 637      year, @data.mon, @data.mday, @data.hour, @data.min, @data.sec)
 638    if @data.sec_fraction.nonzero?
 639      if @secfrac
 640  	s << ".#{ @secfrac }"
 641      else
 642	s << sprintf("%.16f",
 643          (@data.sec_fraction * SecInDay).to_f).sub(/^0/, '').sub(/0*$/, '')
 644      end
 645    end
 646    add_tz(s)
 647  end
 648end
 649
 650class XSDTime < XSDAnySimpleType
 651  include XSDDateTimeImpl
 652  Type = QName.new(Namespace, TimeLiteral)
 653
 654  def initialize(value = nil)
 655    init(Type, value)
 656  end
 657
 658private
 659
 660  def screen_data_str(t)
 661    /^(\d\d):(\d\d):(\d\d(?:\.(\d*))?)(Z|(?:([+\-])(\d\d):(\d\d))?)?$/ =~ t.to_s.strip
 662    unless Regexp.last_match
 663      raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
 664    end
 665    hour = $1.to_i
 666    min = $2.to_i
 667    sec = $3.to_i
 668    secfrac = $4
 669    zonestr = $5
 670    data = DateTime.civil(1, 1, 1, hour, min, sec, tz2of(zonestr))
 671    if secfrac
 672      diffday = secfrac.to_i.to_r / (10 ** secfrac.size) / SecInDay
 673      data += diffday
 674    end
 675    [data, secfrac]
 676  end
 677
 678  def _set(data)
 679    if data.nil?
 680      @data = @secfrac = nil
 681      return
 682    end
 683    @data, @secfrac = data
 684  end
 685
 686  def _to_s
 687    s = format('%02d:%02d:%02d', @data.hour, @data.min, @data.sec)
 688    if @data.sec_fraction.nonzero?
 689      if @secfrac
 690  	s << ".#{ @secfrac }"
 691      else
 692	s << sprintf("%.16f",
 693          (@data.sec_fraction * SecInDay).to_f).sub(/^0/, '').sub(/0*$/, '')
 694      end
 695    end
 696    add_tz(s)
 697  end
 698end
 699
 700class XSDDate < XSDAnySimpleType
 701  include XSDDateTimeImpl
 702  Type = QName.new(Namespace, DateLiteral)
 703
 704  def initialize(value = nil)
 705    init(Type, value)
 706  end
 707
 708private
 709
 710  def screen_data_str(t)
 711    /^([+\-]?\d{4,})-(\d\d)-(\d\d)(Z|(?:([+\-])(\d\d):(\d\d))?)?$/ =~ t.to_s.strip
 712    unless Regexp.last_match
 713      raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
 714    end
 715    year = $1.to_i
 716    if year < 0
 717      year += 1
 718    end
 719    mon = $2.to_i
 720    mday = $3.to_i
 721    zonestr = $4
 722    DateTime.civil(year, mon, mday, 0, 0, 0, tz2of(zonestr))
 723  end
 724
 725  def _to_s
 726    year = (@data.year > 0) ? @data.year : @data.year - 1
 727    s = format('%.4d-%02d-%02d', year, @data.mon, @data.mday)
 728    add_tz(s)
 729  end
 730end
 731
 732class XSDGYearMonth < XSDAnySimpleType
 733  include XSDDateTimeImpl
 734  Type = QName.new(Namespace, GYearMonthLiteral)
 735
 736  def initialize(value = nil)
 737    init(Type, value)
 738  end
 739
 740private
 741
 742  def screen_data_str(t)
 743    /^([+\-]?\d{4,})-(\d\d)(Z|(?:([+\-])(\d\d):(\d\d))?)?$/ =~ t.to_s.strip
 744    unless Regexp.last_match
 745      raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
 746    end
 747    year = $1.to_i
 748    if year < 0
 749      year += 1
 750    end
 751    mon = $2.to_i
 752    zonestr = $3
 753    DateTime.civil(year, mon, 1, 0, 0, 0, tz2of(zonestr))
 754  end
 755
 756  def _to_s
 757    year = (@data.year > 0) ? @data.year : @data.year - 1
 758    s = format('%.4d-%02d', year, @data.mon)
 759    add_tz(s)
 760  end
 761end
 762
 763class XSDGYear < XSDAnySimpleType
 764  include XSDDateTimeImpl
 765  Type = QName.new(Namespace, GYearLiteral)
 766
 767  def initialize(value = nil)
 768    init(Type, value)
 769  end
 770
 771private
 772
 773  def screen_data_str(t)
 774    /^([+\-]?\d{4,})(Z|(?:([+\-])(\d\d):(\d\d))?)?$/ =~ t.to_s.strip
 775    unless Regexp.last_match
 776      raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
 777    end
 778    year = $1.to_i
 779    if year < 0
 780      year += 1
 781    end
 782    zonestr = $2
 783    DateTime.civil(year, 1, 1, 0, 0, 0, tz2of(zonestr))
 784  end
 785
 786  def _to_s
 787    year = (@data.year > 0) ? @data.year : @data.year - 1
 788    s = format('%.4d', year)
 789    add_tz(s)
 790  end
 791end
 792
 793class XSDGMonthDay < XSDAnySimpleType
 794  include XSDDateTimeImpl
 795  Type = QName.new(Namespace, GMonthDayLiteral)
 796
 797  def initialize(value = nil)
 798    init(Type, value)
 799  end
 800
 801private
 802
 803  def screen_data_str(t)
 804    /^(\d\d)-(\d\d)(Z|(?:[+\-]\d\d:\d\d)?)?$/ =~ t.to_s.strip
 805    unless Regexp.last_match
 806      raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
 807    end
 808    mon = $1.to_i
 809    mday = $2.to_i
 810    zonestr = $3
 811    DateTime.civil(1, mon, mday, 0, 0, 0, tz2of(zonestr))
 812  end
 813
 814  def _to_s
 815    s = format('%02d-%02d', @data.mon, @data.mday)
 816    add_tz(s)
 817  end
 818end
 819
 820class XSDGDay < XSDAnySimpleType
 821  include XSDDateTimeImpl
 822  Type = QName.new(Namespace, GDayLiteral)
 823
 824  def initialize(value = nil)
 825    init(Type, value)
 826  end
 827
 828private
 829
 830  def screen_data_str(t)
 831    /^(\d\d)(Z|(?:[+\-]\d\d:\d\d)?)?$/ =~ t.to_s.strip
 832    unless Regexp.last_match
 833      raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
 834    end
 835    mday = $1.to_i
 836    zonestr = $2
 837    DateTime.civil(1, 1, mday, 0, 0, 0, tz2of(zonestr))
 838  end
 839
 840  def _to_s
 841    s = format('%02d', @data.mday)
 842    add_tz(s)
 843  end
 844end
 845
 846class XSDGMonth < XSDAnySimpleType
 847  include XSDDateTimeImpl
 848  Type = QName.new(Namespace, GMonthLiteral)
 849
 850  def initialize(value = nil)
 851    init(Type, value)
 852  end
 853
 854private
 855
 856  def screen_data_str(t)
 857    /^(\d\d)(Z|(?:[+\-]\d\d:\d\d)?)?$/ =~ t.to_s.strip
 858    unless Regexp.last_match
 859      raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
 860    end
 861    mon = $1.to_i
 862    zonestr = $2
 863    DateTime.civil(1, mon, 1, 0, 0, 0, tz2of(zonestr))
 864  end
 865
 866  def _to_s
 867    s = format('%02d', @data.mon)
 868    add_tz(s)
 869  end
 870end
 871
 872class XSDHexBinary < XSDAnySimpleType
 873  Type = QName.new(Namespace, HexBinaryLiteral)
 874
 875  # String in Ruby could be a binary.
 876  def initialize(value = nil)
 877    init(Type, value)
 878  end
 879
 880  def set_encoded(value)
 881    if /^[0-9a-fA-F]*$/ !~ value
 882      raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
 883    end
 884    @data = String.new(value).strip
 885    @is_nil = false
 886  end
 887
 888  def string
 889    [@data].pack("H*")
 890  end
 891
 892private
 893
 894  def screen_data(value)
 895    value.unpack("H*")[0].tr('a-f', 'A-F')
 896  end
 897end
 898
 899class XSDBase64Binary < XSDAnySimpleType
 900  Type = QName.new(Namespace, Base64BinaryLiteral)
 901
 902  # String in Ruby could be a binary.
 903  def initialize(value = nil)
 904    init(Type, value)
 905  end
 906
 907  def set_encoded(value)
 908    if /^[A-Za-z0-9+\/=]*$/ !~ value
 909      raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
 910    end
 911    @data = String.new(value).strip
 912    @is_nil = false
 913  end
 914
 915  def string
 916    @data.unpack("m")[0]
 917  end
 918
 919private
 920
 921  def screen_data(value)
 922    [value].pack("m").strip
 923  end
 924end
 925
 926class XSDAnyURI < XSDAnySimpleType
 927  Type = QName.new(Namespace, AnyURILiteral)
 928
 929  def initialize(value = nil)
 930    init(Type, value)
 931  end
 932
 933private
 934
 935  def screen_data(value)
 936    begin
 937      URI.parse(value.to_s.strip)
 938    rescue URI::InvalidURIError
 939      raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
 940    end
 941  end
 942end
 943
 944class XSDQName < XSDAnySimpleType
 945  Type = QName.new(Namespace, QNameLiteral)
 946
 947  def initialize(value = nil)
 948    init(Type, value)
 949  end
 950
 951private
 952
 953  def screen_data(value)
 954    /^(?:([^:]+):)?([^:]+)$/ =~ value.to_s.strip
 955    unless Regexp.last_match
 956      raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
 957    end
 958    prefix = $1
 959    localpart = $2
 960    [prefix, localpart]
 961  end
 962
 963  def _set(data)
 964    if data.nil?
 965      @prefix = @localpart = @data = nil
 966      return
 967    end
 968    @prefix, @localpart = data
 969    @data = _to_s
 970    @data.freeze
 971  end
 972
 973  def _to_s
 974    if @prefix
 975      "#{ @prefix }:#{ @localpart }"
 976    else
 977      "#{ @localpart }"
 978    end
 979  end
 980end
 981
 982
 983###
 984## Derived types
 985#
 986class XSDNormalizedString < XSDString
 987  Type = QName.new(Namespace, NormalizedStringLiteral)
 988
 989  def initialize(value = nil)
 990    init(Type, value)
 991  end
 992
 993private
 994
 995  def screen_data(value)
 996    if /[\t\r\n]/ =~ value
 997      raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
 998    end
 999    super
1000  end
1001end
1002
1003class XSDInteger < XSDDecimal
1004  Type = QName.new(Namespace, IntegerLiteral)
1005
1006  def initialize(value = nil)
1007    init(Type, value)
1008  end
1009
1010private
1011
1012  def screen_data_str(str)
1013    begin
1014      data = Integer(str)
1015    rescue ArgumentError
1016      raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
1017    end
1018    unless validate(data)
1019      raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
1020    end
1021    data
1022  end
1023
1024  def _set(value)
1025    @data = value
1026  end
1027
1028  def _to_s()
1029    @data.to_s
1030  end
1031
1032  def validate(v)
1033    max = maxinclusive
1034    min = mininclusive
1035    (max.nil? or v <= max) and (min.nil? or v >= min)
1036  end
1037
1038  def maxinclusive
1039    nil
1040  end
1041
1042  def mininclusive
1043    nil
1044  end
1045
1046  PositiveMinInclusive = 1
1047  def positive(v)
1048    PositiveMinInclusive <= v
1049  end
1050end
1051
1052class XSDNonPositiveInteger < XSDInteger
1053  Type = QName.new(Namespace, NonPositiveIntegerLiteral)
1054
1055  def initialize(value = nil)
1056    init(Type, value)
1057  end
1058
1059private
1060
1061  def maxinclusive
1062    0
1063  end
1064
1065  def mininclusive
1066    nil
1067  end
1068end
1069
1070class XSDNegativeInteger < XSDNonPositiveInteger
1071  Type = QName.new(Namespace, NegativeIntegerLiteral)
1072
1073  def initialize(value = nil)
1074    init(Type, value)
1075  end
1076
1077private
1078
1079  def maxinclusive
1080    -1
1081  end
1082
1083  def mininclusive
1084    nil
1085  end
1086end
1087
1088class XSDLong < XSDInteger
1089  Type = QName.new(Namespace, LongLiteral)
1090
1091  def initialize(value = nil)
1092    init(Type, value)
1093  end
1094
1095private
1096
1097  def maxinclusive
1098    +9223372036854775807
1099  end
1100
1101  def mininclusive
1102    -9223372036854775808
1103  end
1104end
1105
1106class XSDInt < XSDLong
1107  Type = QName.new(Namespace, IntLiteral)
1108
1109  def initialize(value = nil)
1110    init(Type, value)
1111  end
1112
1113private
1114
1115  def maxinclusive
1116    +2147483647
1117  end
1118
1119  def mininclusive
1120    -2147483648
1121  end
1122end
1123
1124class XSDShort < XSDInt
1125  Type = QName.new(Namespace, ShortLiteral)
1126
1127  def initialize(value = nil)
1128    init(Type, value)
1129  end
1130
1131private
1132
1133  def maxinclusive
1134    +32767
1135  end
1136
1137  def mininclusive
1138    -32768
1139  end
1140end
1141
1142class XSDByte < XSDShort
1143  Type = QName.new(Namespace, ByteLiteral)
1144
1145  def initialize(value = nil)
1146    init(Type, value)
1147  end
1148
1149private
1150
1151  def maxinclusive
1152    +127
1153  end
1154
1155  def mininclusive
1156    -128
1157  end
1158end
1159
1160class XSDNonNegativeInteger < XSDInteger
1161  Type = QName.new(Namespace, NonNegativeIntegerLiteral)
1162
1163  def initialize(value = nil)
1164    init(Type, value)
1165  end
1166
1167private
1168
1169  def maxinclusive
1170    nil
1171  end
1172
1173  def mininclusive
1174    0
1175  end
1176end
1177
1178class XSDUnsignedLong < XSDNonNegativeInteger
1179  Type = QName.new(Namespace, UnsignedLongLiteral)
1180
1181  def initialize(value = nil)
1182    init(Type, value)
1183  end
1184
1185private
1186
1187  def maxinclusive
1188    +18446744073709551615
1189  end
1190
1191  def mininclusive
1192    0
1193  end
1194end
1195
1196class XSDUnsignedInt < XSDUnsignedLong
1197  Type = QName.new(Namespace, UnsignedIntLiteral)
1198
1199  def initialize(value = nil)
1200    init(Type, value)
1201  end
1202
1203private
1204
1205  def maxinclusive
1206    +4294967295
1207  end
1208
1209  def mininclusive
1210    0
1211  end
1212end
1213
1214class XSDUnsignedShort < XSDUnsignedInt
1215  Type = QName.new(Namespace, UnsignedShortLiteral)
1216
1217  def initialize(value = nil)
1218    init(Type, value)
1219  end
1220
1221private
1222
1223  def maxinclusive
1224    +65535
1225  end
1226
1227  def mininclusive
1228    0
1229  end
1230end
1231
1232class XSDUnsignedByte < XSDUnsignedShort
1233  Type = QName.new(Namespace, UnsignedByteLiteral)
1234
1235  def initialize(value = nil)
1236    init(Type, value)
1237  end
1238
1239private
1240
1241  def maxinclusive
1242    +255
1243  end
1244
1245  def mininclusive
1246    0
1247  end
1248end
1249
1250class XSDPositiveInteger < XSDNonNegativeInteger
1251  Type = QName.new(Namespace, PositiveIntegerLiteral)
1252
1253  def initialize(value = nil)
1254    init(Type, value)
1255  end
1256
1257private
1258
1259  def maxinclusive
1260    nil
1261  end
1262
1263  def mininclusive
1264    1
1265  end
1266end
1267
1268
1269end