Module: SDL4R

Defined in:
lib/sdl4r.rb,
lib/sdl4r/tag.rb,
lib/sdl4r/sdl4r.rb,
lib/sdl4r/token.rb,
lib/sdl4r/reader.rb,
lib/sdl4r/element.rb,
lib/sdl4r/tokenizer.rb,
lib/sdl4r/sdl_binary.rb,
lib/sdl4r/serializer.rb,
lib/sdl4r/sdl4r_version.rb,
lib/sdl4r/sdl_time_span.rb,
lib/sdl4r/sdl_parse_error.rb,
lib/sdl4r/constant_timezone.rb,
lib/sdl4r/relative_timezone.rb,
lib/sdl4r/tz_abbreviation_db.rb,
lib/sdl4r/abbreviation_timezone_proxy.rb

Overview

To change this template, choose Tools | Templates and open the template in the editor.

Defined Under Namespace

Classes: AbbreviationTimezoneProxy, ConstantTimezone, Element, Reader, RelativeTimezone, SdlBinary, SdlParseError, SdlTimeSpan, Serializer, TZAbbreviationDB, Tag, Token, Tokenizer

Constant Summary collapse

MAX_INTEGER_32 =
2**31 - 1
MIN_INTEGER_32 =
-(2**31)
MAX_INTEGER_64 =
2**63 - 1
MIN_INTEGER_64 =
-(2**63)
BASE64_WRAP_LINE_LENGTH =
72
ANONYMOUS_TAG_NAME =
"content"
ROOT_TAG_NAME =
"root"
IDENTIFIER_START_CLASS =
@@UNICODE_REGEXP_SUPPORTED ? '[\\p{Alpha}_]' : '[a-zA-Z_]'
IDENTIFIER_START_REGEXP =

Matches the first character of a valid SDL identifier.

@@UNICODE_REGEXP_SUPPORTED ? /\A#{IDENTIFIER_START_CLASS}/u : /\A#{IDENTIFIER_START_CLASS}/
IDENTIFIER_PART_CLASS =
@@UNICODE_REGEXP_SUPPORTED ? '[\\p{Alnum}_\\-\\.$]' : '[\\w\\-\\.$]'
IDENTIFIER_PART_REGEXP =

Matches characters of a valid SDL identifier after the first one. Works with one character long strings.

@@UNICODE_REGEXP_SUPPORTED ?
/\A#{IDENTIFIER_PART_CLASS}\Z/u :
/\A#{IDENTIFIER_PART_CLASS}\Z/
IDENTIFIER_REGEXP =

Matches a valid SDL identifier (start to end).

@@UNICODE_REGEXP_SUPPORTED ?
/\A#{IDENTIFIER_START_CLASS}#{IDENTIFIER_PART_CLASS}*\Z/u :
/\A#{IDENTIFIER_START_CLASS}#{IDENTIFIER_PART_CLASS}*\Z/
VERSION =

Version of the SDL library.

"0.9.11"
@@use_datetime =
true

Class Method Summary collapse

Class Method Details

.coerce_or_fail(o) ⇒ Object

Coerce the type to a standard SDL type or raises an ArgumentError.

Returns o if of the following classes: NilClass, String, Numeric, Float, TrueClass, FalseClass, Date, DateTime, Time, SdlTimeSpan, SdlBinary,

Rationals are turned into Floats using Rational#to_f. Symbols are turned into Strings using Symbol#to_s.

Raises:

  • (ArgumentError)


187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/sdl4r/sdl4r.rb', line 187

def self.coerce_or_fail(o)
  case o

  when Rational
    return o.to_f

  when Symbol
    return o.to_s

  when NilClass,
      String,
      Numeric,
      Float,
      TrueClass,
      FalseClass,
      Date,
      DateTime,
      Time,
      SdlTimeSpan,
      SdlBinary
    return o

  end

  raise ArgumentError, "#{o.class.name} is not coercible to an SDL type"
end

.disable_tzinfoObject



69
70
71
72
73
74
# File 'lib/sdl4r/sdl4r_tzinfo.rb', line 69

def disable_tzinfo
  class << self
    undef_method :new_time
    define_method(:new_time, instance_method(:tzinfo_orig_new_time))
  end
end

.dump(o, output = nil) ⇒ Object

Dumps the specified object to a given output or returns the corresponding SDL string if output is nil.

o

the root object (equivalent to a root SDL tag, therefore it’s values and attributes are

NOT dumped. See below for an example.)
output

an output as accepted by Tag#write or nil in order to convert to a SDL string.

example:

food = OpenStruct.new(:name => 'french fries', 'comment' => 'eat with bier')
food.fan = OpenStruct.new(:firstname => 'Homer')

puts SDL4R::dump(:food => food)

gives us

food comment="eat with bier" name="french fries" {
  fan firstname="Homer"
}


420
421
422
423
424
425
426
427
428
429
# File 'lib/sdl4r/sdl4r.rb', line 420

def self.dump(o, output = nil)
  tag = Serializer.new.serialize(o)

  if output.nil?
    tag.children_to_string
  else
    tag.write(output)
    output
  end
end

.enable_tzinfoObject



62
63
64
65
66
67
# File 'lib/sdl4r/sdl4r_tzinfo.rb', line 62

def enable_tzinfo
  class << self
    undef_method :new_time if respond_to? :new_time
    define_method(:new_time, instance_method(:tzinfo_replac_new_time))
  end
end

.format(o, add_quotes = true, line_prefix = "", indent = "\t") ⇒ Object

Creates an SDL string representation for a given object and returns it.

o

the object to format

add_quotes

indicates whether quotes will be added to Strings and characters (true by default)

line_prefix

the line prefix to use (“” by default)

indent

the indent string to use (“t” by default)



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/sdl4r/sdl4r.rb', line 52

def self.format(o, add_quotes = true, line_prefix = "", indent = "\t")
  case o
  when String
    return format_string(o, add_quotes)
    
  when Symbol
    return format_string(o.to_s, add_quotes)
    
  when Bignum
    return o.to_s + "BD"
    
  when Integer
    if MIN_INTEGER_32 <= o and o <= MAX_INTEGER_32
      return o.to_s
    elsif MIN_INTEGER_64 <= o and o <= MAX_INTEGER_64
      return o.to_s + "L"
    else
      return o.to_s + "BD"
    end
    
  when Float
    return (o.to_s + "F")
    
  when Rational
    return o.to_f.to_s + "F"

  when BigDecimal
    s = o.to_s('F')
    s.sub!(/\.0$/, "")
    return "#{s}BD"

  when NilClass
    return "null"

  when SdlBinary
    encoded_o = Base64.encode64(o.bytes)
    encoded_o.gsub!(/[\r\n]/m, "") # Remove the EOL inserted every 60 chars

    if add_quotes
      if encoded_o.length > BASE64_WRAP_LINE_LENGTH
        # FIXME: we should a constant or some parameter instead of hardcoded spaces
        wrap_lines_in_ascii(encoded_o, BASE64_WRAP_LINE_LENGTH, "#{line_prefix}#{indent}")
        encoded_o.insert(0, "[#{$/}")
        encoded_o << "#{$/}#{line_prefix}]"
      else
        encoded_o.insert(0, "[")
        encoded_o << "]"
      end
    end

    return encoded_o
    
  # Below, we use "#{o.year}" instead of "%Y" because "%Y" always emit 4 chars at least even if
  # the date is before 1000.
  when DateTime, Time
    return format_time(o)
    
  when Date
    return "#{o.strftime("#{o.year}/%m/%d")}"
    
  else
    return o.to_s
  end
end

.format_time(time) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/sdl4r/sdl4r.rb', line 117

def self.format_time(time)
    s = "" # important as strftime() tends to return a US-ASCII string
    s << time.strftime("#{time.year}/%m/%d %H:%M:%S") # %Y tends to return "88" for 1988 in many implementations

    milliseconds = get_datetime_milliseconds(time)
    s << sprintf(".%03d", milliseconds) if milliseconds != 0

    zone_part = time.strftime("%z") # >> "+0130"  --  "%:z" is not supported by every interpreter
    unless zone_part.nil? or zone_part.empty? or zone_part == "+0000" or not zone_part =~ /[+-]\d+/
      zone_part.insert(3, ":")
      s << "-GMT" << zone_part
    end

    return s
end

.is_coercible?(o) ⇒ Boolean

Indicates whether ‘o’ is coercible to a SDL literal type. See #coerce_or_fail

Returns:

  • (Boolean)


217
218
219
220
221
222
223
224
225
# File 'lib/sdl4r/sdl4r.rb', line 217

def self.is_coercible?(o)
  begin
    coerce_or_fail(o)
    true
    
  rescue ArgumentError
    false
  end
end

.load(input) ⇒ Object

Loads the specified ‘input’ and deserializes into the returned object.

input

an input as accepted by SDL4R#read or a Tag.

example:

top = SDL4R::load(<<EOS
food name="chili con carne" {
  ingredient "beans"
  ingredient "chili"
  ingredient "cheese"
  note 8.9
}
EOS
)

top.food.name # => "chili con carne"
top.food.ingredient # => ["beans", "chili", "cheese"]
top.food.note # => 8.9


390
391
392
393
394
395
396
397
398
# File 'lib/sdl4r/sdl4r.rb', line 390

def self.load(input)
  if input.is_a? Tag
    tag = input
  else
    tag = read(input)
  end

  return Serializer.new.deserialize(tag)
end

.new_time(year, month, day, hour, min, sec, msec, timezone_code) ⇒ Object Also known as: tzinfo_orig_new_time

Creates and returns the object representing a time (DateTime by default). This method is called by the Parser class.

See #use_datetime=



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/sdl4r/sdl4r.rb', line 153

def self.new_time(year, month, day, hour, min, sec, msec, timezone_code)
  if @@use_datetime
    timezone_code ||= Time.now.zone
    sec_msec = (msec == 0)? sec : Rational(sec * 1000 + msec, 1000)
    return DateTime.civil(year, month, day, hour, min, sec_msec, timezone_code)

  else
    if timezone_code =~ /\A(?:GMT|UTC)([+-]\d+:\d+)\Z/
      timezone_code = $1
    end
    timezone_offset = Time.zone_offset(timezone_code, year) if timezone_code
    if timezone_offset
      timezone_offset_hour = timezone_offset.abs / 3600
      timezone_offset_min = (timezone_offset.abs % 3600) / 60
      return Time.xmlschema(
        sprintf(
          "%d-%02d-%02dT%02d:%02d:%02d.%03d#{timezone_offset >= 0 ? '+' : '-'}%02d:%02d",
          year, month, day, hour, min, sec, msec, timezone_offset_hour, timezone_offset_min))

    else
      return Time.local(year, month, day, hour, min, sec, msec * 1000)
    end
  end
end

.read(input) ⇒ Object

Creates and returns a tag named “root” and add all the tags specified in the given input.

input

String, IO, Pathname or URI.

root = SDL4R::read(<<EOS
planets {
  earth area_km2=510900000
  mars
}
EOS
)

root = SDL4R::read(Pathname.new("my_dir/my_file.sdl"))

IO.open("my_dir/my_file.sdl", "r") { |io|
  root = SDL4R::read(io)
}

root = SDL4R::read(URI.new("http://my_site/my_file.sdl"))


327
328
329
# File 'lib/sdl4r/sdl4r.rb', line 327

def self.read(input)
  Tag.new(ROOT_TAG_NAME).read(input)
end

.SdlBinary(o) ⇒ Object

Try to coerce ‘o’ into a SdlBinary. Raise an ArgumentError if it fails.



72
73
74
75
76
77
78
79
80
# File 'lib/sdl4r/sdl_binary.rb', line 72

def self.SdlBinary(o)
  if o.kind_of? SdlBinary
    return o
  elsif o.kind_of? String
    return SdlBinary.new(o)
  else
    raise ArgumentError, "can't coerce argument"
  end
end

.supports_unicode_identifiers?Boolean

Returns:

  • (Boolean)


238
239
240
# File 'lib/sdl4r/sdl4r.rb', line 238

def self.supports_unicode_identifiers?
  @@UNICODE_REGEXP_SUPPORTED
end

.to_attribute_hash(s) ⇒ Object

Parse a string representing the attributes portion of an SDL tag and return the results as a map.

Example

hash = SDL4R.to_attribute_hash("value=1 debugging=on time=12:24:01");

# { "value" => 1, "debugging" => true, "time" => SdlTimeSpan.new(12, 24, 01) }

Raises:

  • (ArgumentError)


365
366
367
368
# File 'lib/sdl4r/sdl4r.rb', line 365

def self.to_attribute_hash(s)
  raise ArgumentError, "'s' cannot be null" if s.nil?
  return read("atts " + s).child.attributes
end

.to_value(s) ⇒ Object

Parses and returns the value corresponding with the specified SDL literal.

SDL4R.to_value("\"abcd\"") # => "abcd"
SDL4R.to_value("1") # => 1
SDL4R.to_value("null") # => nil

Raises:

  • (ArgumentError)


337
338
339
340
# File 'lib/sdl4r/sdl4r.rb', line 337

def self.to_value(s)
  raise ArgumentError, "'s' cannot be null" if s.nil?
  return read(s).child.value
end

.to_value_array(s) ⇒ Object

Example

array = SDL4R.to_value_array("1 true 12:24:01")

Will return an int, a boolean, and a time span.

Raises:

  • (ArgumentError)


351
352
353
354
# File 'lib/sdl4r/sdl4r.rb', line 351

def self.to_value_array(s)
  raise ArgumentError, "'s' cannot be null" if s.nil?
  return read(s).child.values
end

.tzinfo_replac_new_time(year, month, day, hour, min, sec, msec, timezone_code) ⇒ Object

Creates and returns the object representing a time (DateTime by default). This method is called by the Parser class.

This implementation uses TZInfo in order to parse timezones (wider support of timezones and better support of DST and such issues).

See #use_datetime=



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/sdl4r/sdl4r_tzinfo.rb', line 40

def tzinfo_replac_new_time(year, month, day, hour, min, sec, msec, timezone_code)
  if timezone_code
    timezone = SDL4R::RelativeTimezone::get(timezone_code)
    tz_ref_time = Time.utc(year, month, day, hour, min, sec, msec * 1000)
    # Arbitrary decision: in ambiguous cases, we choose the not-DST offset.
    timezone_period = timezone.period_for_local(tz_ref_time, false)
  end

  if use_datetime?
    sec_msec = (msec == 0)? sec : Rational(sec * 1000 + msec, 1000)
    timezone_offset = timezone_code ?
      timezone_period.utc_total_offset_rational :
      Rational(Time.now.utc_offset, SECONDS_IN_DAY)
    return DateTime.civil(year, month, day, hour, min, sec_msec, timezone_offset)

  else
    return timezone_code ?
      timezone_period.to_utc(Time.utc(year, month, day, hour, min, sec, msec * 1000)) :
      Time.local(year, month, day, hour, min, sec, msec * 1000)
  end
end

.use_datetime=(bool) ⇒ Object

Sets whether DateTime should be used for representing times at parsing. If set to false, Time will be used instead (true by default).



144
145
146
# File 'lib/sdl4r/sdl4r.rb', line 144

def self.use_datetime=(bool)
  @@use_datetime = bool
end

.use_datetime?Boolean

Indicates whether DateTime is used to represent times. If false, Time is used instead. True by default.

Returns:

  • (Boolean)


137
138
139
# File 'lib/sdl4r/sdl4r.rb', line 137

def self.use_datetime?
  @@use_datetime
end

.valid_identifier?(identifier) ⇒ Boolean

Returns whether the specified SDL identifier is valid. See SDL4R#validate_identifier.

Returns:

  • (Boolean)


303
304
305
# File 'lib/sdl4r/sdl4r.rb', line 303

def self.valid_identifier?(identifier)
  !IDENTIFIER_REGEXP.match(identifier).nil?
end

.validate_identifier(identifier) ⇒ Object

Validates an SDL identifier String. SDL Identifiers must start with a Unicode letter or underscore (_) and contain only unicode letters, digits, underscores (_), dashes(-), periods (.) and dollar signs ($).

Raises

ArgumentError if the identifier is not legal

TODO: support UTF-8 identifiers



272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/sdl4r/sdl4r.rb', line 272

def self.validate_identifier(identifier)
  if identifier.nil? or identifier.empty?
    raise ArgumentError, "SDL identifiers cannot be null or empty."
  end

  # in Java, was if(!Character.isJavaIdentifierStart(identifier.charAt(0)))
  unless identifier =~ IDENTIFIER_START_REGEXP
    raise ArgumentError,
      "'" + identifier[/^./] +
      "' is not a legal first character for an SDL identifier. " +
      "SDL Identifiers must start with a unicode letter or " +
      "an underscore (_). (identifier=<#{identifier}>)"
  end

  unless identifier.length == 1 or identifier =~ IDENTIFIER_REGEXP
    for i in 1..identifier.length
      unless identifier[i..i] =~ IDENTIFIER_PART_REGEXP
        raise ArgumentError,
          "'" + identifier[i..i] + 
          "' is not a legal character for an SDL identifier. " +
          "SDL Identifiers must start with a unicode letter or " +
          "underscore (_) followed by 0 or more unicode " +
          "letters, digits, underscores (_), dashes (-), periodss (.) and dollar signs ($)"
      end
    end
  end
end