jfreeze-ruby-gdsii 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. data/CHANGELOG.txt +6 -0
  2. data/LICENSE.txt +20 -0
  3. data/README.txt +113 -0
  4. data/Rakefile +30 -0
  5. data/bin/rgds-debug +43 -0
  6. data/bin/rgds-dump +38 -0
  7. data/bin/rgds-join +98 -0
  8. data/bin/rgds-layers +53 -0
  9. data/bin/rgds-sremove +136 -0
  10. data/bin/rgds-ssplit +113 -0
  11. data/bin/rgds-stats +134 -0
  12. data/bin/rgds-structs +41 -0
  13. data/bin/rgds-tree +167 -0
  14. data/bin/rgds2rb +99 -0
  15. data/lib/gdsii.rb +137 -0
  16. data/lib/gdsii/aref.rb +243 -0
  17. data/lib/gdsii/bnf.rb +309 -0
  18. data/lib/gdsii/boundary.rb +53 -0
  19. data/lib/gdsii/box.rb +65 -0
  20. data/lib/gdsii/byte_order.rb +36 -0
  21. data/lib/gdsii/element.rb +172 -0
  22. data/lib/gdsii/group.rb +98 -0
  23. data/lib/gdsii/library.rb +518 -0
  24. data/lib/gdsii/mixins.rb +378 -0
  25. data/lib/gdsii/node.rb +65 -0
  26. data/lib/gdsii/path.rb +169 -0
  27. data/lib/gdsii/property.rb +108 -0
  28. data/lib/gdsii/record.rb +606 -0
  29. data/lib/gdsii/record/consts.rb +384 -0
  30. data/lib/gdsii/record/datatypes/ascii.rb +145 -0
  31. data/lib/gdsii/record/datatypes/bitarray.rb +101 -0
  32. data/lib/gdsii/record/datatypes/data.rb +111 -0
  33. data/lib/gdsii/record/datatypes/int2.rb +67 -0
  34. data/lib/gdsii/record/datatypes/int4.rb +65 -0
  35. data/lib/gdsii/record/datatypes/nodata.rb +60 -0
  36. data/lib/gdsii/record/datatypes/real4.rb +51 -0
  37. data/lib/gdsii/record/datatypes/real8.rb +120 -0
  38. data/lib/gdsii/sref.rb +61 -0
  39. data/lib/gdsii/strans.rb +133 -0
  40. data/lib/gdsii/structure.rb +352 -0
  41. data/lib/gdsii/text.rb +203 -0
  42. data/pkg/ruby-gdsii.gem +46 -0
  43. data/samples/hello.gds +0 -0
  44. data/samples/hello.out.rb +84 -0
  45. data/samples/hello.rb +94 -0
  46. data/test/baseline/dcp1.gds +0 -0
  47. data/test/baseline/h_write.gds +0 -0
  48. data/test/h_pthru.rb +22 -0
  49. data/test/h_write.rb +117 -0
  50. data/test/hs_pthru.rb +31 -0
  51. data/test/l_pthru.rb +23 -0
  52. data/test/test_gds_group.rb +379 -0
  53. data/test/test_gds_record.rb +99 -0
  54. metadata +117 -0
@@ -0,0 +1,120 @@
1
+ require 'gdsii/record/datatypes/data.rb'
2
+
3
+ module Gdsii
4
+
5
+ module RecData
6
+
7
+ #
8
+ # Class for REAL8 data type
9
+ #
10
+ class Real8 < Data
11
+
12
+ # Value is an array of floating point numbers
13
+ attr_reader :value
14
+
15
+ # Construct an Gdsii::RecData::Real8 data object. The value is an array
16
+ # of floating point numbers (Float).
17
+ def initialize(value)
18
+ super(GDT_REAL8, value)
19
+ end
20
+
21
+ # Set the value for this object; verify that the value items are of
22
+ # type Float (or at least can be coerced using "to_f").
23
+ def value=(value)
24
+ @value = Data.coerce_value(value, Float, :to_f)
25
+ end
26
+
27
+ # Returns the size of the record *data* in bytes. Each array element
28
+ # consumes 8 bytes (hence REAL8).
29
+ def byte_size()
30
+ @value.length * 8
31
+ end
32
+
33
+ # Reads a REAL8 record from the given file and for the length of bytes
34
+ # given and returns a new Gdsii::RecData::Real8 object.
35
+ def Real8.read(file, byte_count)
36
+ data = []
37
+ while (byte_count > 0)
38
+
39
+ # read the first byte and get sign and exponent values from it
40
+ raw = file.read(1)
41
+ sign_val = raw.unpack('B')[0].to_i
42
+ exponent = raw.unpack('C')[0]
43
+ exponent -= (sign_val == 0) ? 64 : 192 # exponent is in Excess 64 fmt
44
+
45
+ # read the rest of the real number - save as binary
46
+ raw = file.read(7)
47
+ mant_binary = raw.unpack('b*')[0]
48
+
49
+ # convert mantissa from binary to decimal
50
+ mantissa = 0.0
51
+ (1...8).each do |i|
52
+ str = mant_binary[(i-1)*8,8]
53
+ ub = [("0"*32+str.reverse.to_s)[-32..-1]].pack("B32").unpack("N")[0]
54
+ mantissa += ub / (256.0 ** i)
55
+ end
56
+ real = mantissa * (16**exponent)
57
+ real = -real if (sign_val != 0)
58
+ data.push real
59
+
60
+ byte_count -= 8
61
+
62
+ end
63
+
64
+ Real8.new(data)
65
+ end
66
+
67
+ # Writes the integer values in this Gdsii::RecData::Real8 object to the
68
+ # given file as a GDSII REAL8 record.
69
+ def write(file)
70
+
71
+ self.value.each do |item|
72
+
73
+ if item == 0 then
74
+ file.write "\x00" * 8
75
+ else
76
+ # Note: process differently for big endian?
77
+ bit_str = [item].pack('G').unpack('B*')[0]
78
+
79
+ # get the sign and expt (IEEE)
80
+ sign = bit_str[0,1]
81
+ expt = ['00000'+bit_str[1,11]].pack('B16').unpack('n')[0] - 1023
82
+
83
+ # Divide by 4 because 16**x == (2**4)**x == 2**(4*x)
84
+ b16_expt = (expt / 4).floor
85
+ b16_rem = expt % 4
86
+
87
+ # Shift the mantissa 4 bits to the right and increment the expt
88
+ b16_expt += 1
89
+ mant = ['0', '0', '0', '1', bit_str[12..63].split('')].flatten
90
+
91
+ # Shift the mantissa to left to take care of the expt remainder
92
+ (0...b16_rem).each do |i|
93
+ mant.shift
94
+ mant.push '0'
95
+ end
96
+
97
+ # Bias the base-16 expoent
98
+ b16_expt += 64
99
+
100
+ # Convert the expt to a 7-bit binary string
101
+ b16_expt_str = [b16_expt].pack('C').unpack('B*')[0][1..7]
102
+
103
+ # Now assemble the sign, expt and mantissa
104
+ real8_fmt = sign + b16_expt_str + mant.join('')
105
+ file.write [real8_fmt].pack('B*')
106
+ end
107
+
108
+ end
109
+ end
110
+
111
+ # Converts the array of floating point values to a string (values are
112
+ # joined by spaces).
113
+ def to_s()
114
+ value.map {|v| v.to_s}.join(' ')
115
+ end
116
+
117
+ end
118
+ end
119
+ end
120
+
data/lib/gdsii/sref.rb ADDED
@@ -0,0 +1,61 @@
1
+ require 'gdsii/group'
2
+ require 'gdsii/element'
3
+ require 'gdsii/strans'
4
+
5
+ module Gdsii
6
+
7
+ #
8
+ # Represents a GDSII structure reference (SRef) element. Most
9
+ # methods are from Element or from the various included Access module
10
+ # methods.
11
+ #
12
+ class SRef < Element
13
+
14
+ # Include various record accessors
15
+ include Access::XY
16
+ include Access::ELFlags
17
+ include Access::Plex
18
+ include Access::StransGroup
19
+ include Access::Sname
20
+
21
+ #
22
+ # SRef BNF description:
23
+ #
24
+ # <sref> ::= SREF [ELFLAGS] [PLEX] SNAME [<strans>] XY
25
+ #
26
+ self.bnf_spec = BnfSpec.new(
27
+ BnfItem.new(GRT_SREF),
28
+ BnfItem.new(GRT_ELFLAGS, true),
29
+ BnfItem.new(GRT_PLEX, true),
30
+ BnfItem.new(GRT_SNAME),
31
+ BnfItem.new(Strans, true),
32
+ BnfItem.new(GRT_XY),
33
+ BnfItem.new(Properties, true),
34
+ BnfItem.new(GRT_ENDEL)
35
+ )
36
+
37
+ #
38
+ # Create a structure reference (SREF) within a Structure object (also
39
+ # known as a structure "instantiation").
40
+ #
41
+ # struct1 = Gdsii::Structure.new('top')
42
+ # struct2 = Gdsii::Structure.new('sub')
43
+ # struct1.add SRef.new('sub')
44
+ #
45
+ # Alternatively, any object with a #to_s method can be passed and the
46
+ # #to_s method will be used to coerce the object into a string. For
47
+ # example, a structure object itself can be used (instead of the structure
48
+ # name) through Structure#to_s:
49
+ #
50
+ # struct1.add SRef.new(struct2)
51
+ #
52
+ def initialize(sname=nil, xy=nil)
53
+ super()
54
+ @records[GRT_SREF] = Record.new(GRT_SREF)
55
+ self.sname = sname.to_s unless sname.nil?
56
+ self.xy = xy unless xy.nil?
57
+ yield self if block_given?
58
+ end
59
+
60
+ end
61
+ end
@@ -0,0 +1,133 @@
1
+ require 'gdsii/group'
2
+
3
+ module Gdsii
4
+
5
+ #
6
+ # Represents a GDSII structure translation object (Strans).
7
+ #
8
+ class Strans < Group
9
+
10
+ #
11
+ # Strans BNF description:
12
+ #
13
+ # <strans> ::= STRANS [MAG] [ANGLE]
14
+ #
15
+ self.bnf_spec = BnfSpec.new(
16
+ BnfItem.new(GRT_STRANS),
17
+ BnfItem.new(GRT_MAG, true),
18
+ BnfItem.new(GRT_ANGLE, true)
19
+ )
20
+
21
+ # Constructor
22
+ def initialize(mag=nil, angle=nil, reflect_x=false, abs_mag=false, abs_angle=false)
23
+ super()
24
+ @records[GRT_STRANS] = Record.new(GRT_STRANS, 0)
25
+ self.reflect_x = true if reflect_x
26
+ self.abs_mag = true if abs_mag
27
+ self.abs_angle = true if abs_angle
28
+ self.mag = mag unless mag.nil?
29
+ self.angle = angle unless angle.nil?
30
+ yield self if block_given?
31
+ end
32
+
33
+ #
34
+ # Get the strans bitarray (returns Record)
35
+ #
36
+ def record() @records.get(GRT_STRANS); end
37
+
38
+ #
39
+ # Get the strans bitarray data (returns Fixnum). The recommendation is to
40
+ # not access this directly but rather use the various bitwise query
41
+ # methods instead: #reflect_x?, #abs_angle?, #abs_mag?.
42
+ #
43
+ def value() @records.get_data(GRT_STRANS); end
44
+
45
+ #
46
+ # Set the strans bitarray record. The recommendation is to not access
47
+ # this directly but rather use the various bitwise manipulation methods
48
+ # instead: #reflect_x=, #abs_angle=, #abs_mag=.
49
+ #
50
+ # * 15 = reflect_x
51
+ # * 2 = abs_mag
52
+ # * 1 = abs_angle
53
+ # * All others reserved for future use.
54
+ #
55
+ def value=(val)
56
+ @records.set(GRT_STRANS,val);
57
+ end
58
+
59
+ #
60
+ # Get the strans angle (returns Record)
61
+ #
62
+ def angle_record() @records.get_data(GRT_ANGLE); end
63
+
64
+ #
65
+ # Get the strans angle value (returns Fixnum)
66
+ #
67
+ def angle() @records.get_data(GRT_ANGLE); end
68
+
69
+ #
70
+ # Set the strans angle record
71
+ #
72
+ def angle=(val) @records.set(GRT_ANGLE,val); end
73
+
74
+ #
75
+ # Get the strans magnification (returns Record)
76
+ #
77
+ def mag_record() @records.get_data(GRT_MAG); end
78
+
79
+ #
80
+ # Get the strans magnification value (returns Fixnum)
81
+ #
82
+ def mag() @records.get_data(GRT_MAG); end
83
+
84
+ #
85
+ # Set the strans magnification record
86
+ #
87
+ def mag=(val) @records.set(GRT_MAG,val); end
88
+
89
+ #
90
+ # Return true if the translation bitarray indicates that a reflection
91
+ # about the x-axis is set.
92
+ #
93
+ def reflect_x?()
94
+ (value & 0x8000) == 0x8000
95
+ end
96
+
97
+ #
98
+ # Set or clear the strans x-reflect bit (true = set; false = clear)
99
+ #
100
+ def reflect_x=(flag)
101
+ self.value = flag ? value | 0x8000 : value & 0x7fff
102
+ end
103
+
104
+ #
105
+ # Return true if an absolute magnification is set; false if not
106
+ #
107
+ def abs_mag?()
108
+ (value & 0x0004) == 0x0004
109
+ end
110
+
111
+ #
112
+ # Set or clear the absolute magnification bit (true = set; false = clear)
113
+ #
114
+ def abs_mag=(flag)
115
+ self.value = flag ? value | 0x0004 : value & 0xfffb
116
+ end
117
+
118
+ #
119
+ # Return true if the absolute angle bit is set; false if not
120
+ #
121
+ def abs_angle?()
122
+ (value & 0x0002) == 0x0002
123
+ end
124
+
125
+ #
126
+ # Set the strans absAngle bit
127
+ #
128
+ def abs_angle=(flag)
129
+ self.value = flag ? value | 0x0002 : value & 0xfffd
130
+ end
131
+
132
+ end
133
+ end
@@ -0,0 +1,352 @@
1
+ require 'time'
2
+ require 'gdsii/mixins'
3
+ require 'gdsii/element'
4
+ require 'gdsii/boundary'
5
+ require 'gdsii/path'
6
+ require 'gdsii/text'
7
+ require 'gdsii/node'
8
+ require 'gdsii/box'
9
+ require 'gdsii/sref'
10
+ require 'gdsii/aref'
11
+
12
+ module Gdsii
13
+
14
+ #
15
+ # Represents a GDSII Structure.
16
+ #
17
+ class Structure < Group
18
+
19
+ include Access::GdsiiTime
20
+
21
+ #
22
+ # Structure BNF description:
23
+ #
24
+ # <structure> ::= BGNSTR STRNAME [STRCLASS] {<element>}* ENDSTR
25
+ # <element> ::= {<boundary> | <path> | <sref> | <aref> |
26
+ # <text> | <node> | <box>} {<property>}* ENDEL
27
+ #
28
+ self.bnf_spec = BnfSpec.new(
29
+ BnfItem.new(GRT_BGNSTR),
30
+ BnfItem.new(GRT_STRNAME),
31
+ BnfItem.new(GRT_STRCLASS, true),
32
+ BnfItem.new(Elements, true),
33
+ BnfItem.new(GRT_ENDSTR)
34
+ )
35
+
36
+ #
37
+ # Creates a Structure object. Various GDSII Elements are added to a
38
+ # structure such as a Boundary, Path, SRef, ARef, Text, Node, and Box.
39
+ #
40
+ # str_sub = Structure.new('sub')
41
+ # str_top = Structure.new('top')
42
+ # str_top.add SRef.new(str_sub)
43
+ # str_top.add Boundary.new(1, 0, [0,0, 0,10, 10,10, 10,0, 0,0])
44
+ #
45
+ def initialize(name=nil)
46
+ # Create the record grouping
47
+ super()
48
+ @records[Elements] = Elements.new
49
+ @records[GRT_ENDSTR] = Record.new(GRT_ENDSTR)
50
+
51
+ # set the name
52
+ self.name = name unless name.nil?
53
+
54
+ # Set create/modify time to the current time
55
+ now = Time.now
56
+ self.create_time = now
57
+ self.modify_time = now
58
+
59
+ yield self if block_given?
60
+ end
61
+
62
+ #
63
+ # Access to the Elements object. See Elements for a listing of methods.
64
+ #
65
+ def elements(); @records.get(Elements); end
66
+
67
+ #
68
+ # Shortcut for Elements#add. For example, instead of:
69
+ #
70
+ # struct = Structure.new('test')
71
+ # struct.elements.add Text(1, 0, [0,0], 'hello')
72
+ #
73
+ # It could be simply:
74
+ #
75
+ # struct.add Text(1, 0, [0,0], 'hello')
76
+ #
77
+ def add(*args); elements.add(*args); end
78
+
79
+ #
80
+ # Shortcut for Elements#remove. For example, instead of:
81
+ #
82
+ # struct.elements.remove {|e| e.class == Gdsii::Text}
83
+ #
84
+ # It could be simply:
85
+ #
86
+ # struct.remove {|e| e.class == Gdsii::Text}
87
+ #
88
+ def remove(*args); elements.remove(*args); end
89
+
90
+ #
91
+ # Get the Structure STRNAME record (returns Record).
92
+ #
93
+ def name_record() @records.get(GRT_STRNAME); end
94
+
95
+ #
96
+ # Get the Structure name (returns String).
97
+ #
98
+ def name() @records.get_data(GRT_STRNAME); end
99
+
100
+ #
101
+ # Set the Structure name.
102
+ #
103
+ def name=(val) @records.set(GRT_STRNAME, val); end
104
+
105
+ #
106
+ # Get the strclass record (returns Record).
107
+ #
108
+ def strclass_record() @records.get(GRT_STRCLASS); end
109
+
110
+ #
111
+ # Get the strclass bitarray number (returns Fixnum).
112
+ #
113
+ def strclass() @records.get_data(GRT_STRCLASS); end
114
+
115
+ #
116
+ # Set the strclass bitarray number. According to the GDSII specification,
117
+ # this is only to be used by Calma - otherwise it should be omitted or
118
+ # set to 0. It is probably a good idea to not touch this property.
119
+ #
120
+ def strclass=(val) @records.set(GRT_STRCLASS, val); end
121
+
122
+ #
123
+ # Get the bgnstr record (returns Record).
124
+ #
125
+ def bgnstr_record() @records.get(GRT_BGNSTR); end
126
+
127
+ #
128
+ # Get the bgnstr number (returns Fixnum). This holds the create/modify
129
+ # time stamp for the structure. It is probably easier to not access this
130
+ # directly but to use #create_time and #modify_time instead.
131
+ #
132
+ def bgnstr() @records.get_data(GRT_BGNSTR); end
133
+
134
+ #
135
+ # Set the bgnstr number. The value is a Fixnum representation of the
136
+ # create/modify time stamp for the structure. It is probably easier to
137
+ # not access this directly but to use #create_time= and #modify_time=
138
+ # instead.
139
+ #
140
+ def bgnstr=(val) @records.set(GRT_BGNSTR, val); end
141
+
142
+ #
143
+ # Accepts a Time object and sets the create time for the structure.
144
+ #
145
+ # struct.create_time = Time.now
146
+ #
147
+ def create_time=(time)
148
+ @create_time = time
149
+ update_times
150
+ end
151
+
152
+ #
153
+ # Returns the create time for this structure (returns Time)
154
+ #
155
+ def create_time(); @create_time; end
156
+
157
+ #
158
+ # Accepts a Time object and sets the modify time for the structure.
159
+ #
160
+ # struct.modify_time = Time.now
161
+ #
162
+ def modify_time=(time)
163
+ @modify_time = time
164
+ update_times
165
+ end
166
+
167
+ #
168
+ # Returns the modify time for this structure (returns Time)
169
+ #
170
+ def modify_time(); @modify_time; end
171
+
172
+ #
173
+ # Reads records related to a Structure header from the given file handle.
174
+ # It is assumed that the file position is already at BGNSTR (likely after
175
+ # Library#read_header). The structure is yielded (if desired) and
176
+ # returned. The iterative version of this method is likely preferable
177
+ # in most cases (Structure#read_each_header).
178
+ #
179
+ def Structure.read_header(file)
180
+ Structure.read(file, nil, nil, :before_group) {|struct|
181
+ yield struct if block_given?
182
+ break struct
183
+ }
184
+ end
185
+
186
+ #
187
+ # Reads each Structure and its Elements from the given file handle. Each
188
+ # Structure is yielded after the entire Structure is read from the file.
189
+ # Compare this with Structure#read_each_header which might be more
190
+ # efficient, more streamlined, and consume less memory.
191
+ #
192
+ # The Library#read_header method must be called as a prerequisite (the file
193
+ # handle must be at a BGNSTR record).
194
+ #
195
+ # File.open(file_name, 'rb') do |file|
196
+ # Library.read_header(file) do |lib|
197
+ # Structure.read_each(file) do |struct|
198
+ # puts "#{struct.name} has #{struct.elements.length} elements"
199
+ # end
200
+ # end
201
+ # end
202
+ #
203
+ def Structure.read_each(file)
204
+ while (Record.peek(file).type == GRT_BGNSTR) do
205
+ yield Structure.read(file)
206
+ end
207
+ end
208
+
209
+ #
210
+ # Reads the Structure header records from a file handle but without reading
211
+ # any Element records. The resulting Structure element is yielded. This
212
+ # is useful for using the high-level GDSII access methods as a stream file
213
+ # is being read in.
214
+ #
215
+ # Prior to using this method, the file position must be at the first
216
+ # structure definition (i.e. after the Library header). The best method
217
+ # to do this is to call Library#read_header first.
218
+ #
219
+ # Also, you _MUST_ advance the file position to the next structure record
220
+ # header (BGNSTR) either with Structure#seek_next or
221
+ # Structure#read_each_element within the code block. Otherwise the file
222
+ # pointer will not be advanced properly and only the first Structure will
223
+ # be yielded.
224
+ #
225
+ # File.open(file_name, 'rb') do |file|
226
+ # Library.read_header(file) do |lib|
227
+ # Structure.read_each_header(file) do |struct|
228
+ # if struct.name == 'TOP'
229
+ # # Show all elements in structure "TOP"
230
+ # puts "Processing structure #{struct.name}"
231
+ # Element.read_each(file) do |element|
232
+ # puts "--> Element: #{element.class}"
233
+ # end
234
+ # else
235
+ # # Skip past elements in other blocks
236
+ # puts "Ignoring structure #{struct.name}"
237
+ # Structure.seek_next(file)
238
+ # end
239
+ # end
240
+ # end
241
+ # end
242
+ #
243
+ def Structure.read_each_header(file)
244
+ while (Record.peek(file).type == GRT_BGNSTR) do
245
+ yield Structure.read_header(file)
246
+ end
247
+ end
248
+
249
+ #
250
+ # Reads from the given file handle until a ENDSTR record is met (presumably
251
+ # to a BGNSTR or ENDLIB record. This effectively "skips" past all elements
252
+ # within a structure and prepares the file handle to read the next
253
+ # structure in the file (or ENDLIB if at the end of the GDSII library).
254
+ # See Structure#read_each_header for an example. The new file position is
255
+ # returned.
256
+ #
257
+ # Compare this with Element#read_each which accomplishes the same thing
258
+ # but instead yields each element as it is read from the file.
259
+ #
260
+ def Structure.seek_next(file)
261
+ Record.read_each(file) do |record|
262
+ break file.pos if record.is_endstr?
263
+ end
264
+ nil
265
+ end
266
+
267
+ #
268
+ # Writes only the header portion of the Structure to a file (no elements).
269
+ # This is useful when streamlined writing is desired (for better
270
+ # performance or when writing GDSII as another GDSII is being read). Be
271
+ # sure to either:
272
+ #
273
+ # 1. Call #write_footer after writing the header and any Element
274
+ # objects. Also be sure to wrap this around Library#write_header and
275
+ # Library#write_footer; Or
276
+ # 2. Pass a block whereupon the footer will automatically be added after
277
+ # the block is processed.
278
+ #
279
+ # See Library#write_header for an example.
280
+ #
281
+ def write_header(file)
282
+ # alter the BNF to exclude Elements and ENDSTR; then write to file
283
+ # according to the modified BNF
284
+ alt_bnf = BnfSpec.new(*Structure.bnf_spec.bnf_items[0..-3])
285
+ self.write(file, alt_bnf)
286
+
287
+ # if block is given, then yield to that block and then write the
288
+ # footer afterwards
289
+ if block_given?
290
+ yield
291
+ self.write_footer(file)
292
+ end
293
+ end
294
+
295
+ #
296
+ # Writes only the Structure footer (just ENDSTR record) to file. To be
297
+ # used with #write_header.
298
+ #
299
+ def write_footer(file)
300
+ Record.new(GRT_ENDSTR).write(file)
301
+ end
302
+
303
+ #####################
304
+ ## PRIVATE METHODS ##
305
+ #####################
306
+
307
+ private
308
+
309
+ # Used by #create_time and #modify_time
310
+ def update_times()
311
+ if create_time and modify_time
312
+ self.bgnstr = build_time(create_time) + build_time(modify_time)
313
+ else
314
+ self.bgnstr = nil
315
+ end
316
+ end
317
+
318
+ end
319
+
320
+ ############################################################################
321
+
322
+ #
323
+ # Class to hold a collection of Structure objects. This is used in the
324
+ # Library class BNF.
325
+ #
326
+ class Structures < Group
327
+
328
+ include Access::EnumerableGroup
329
+
330
+ #
331
+ # Structures BNF description:
332
+ #
333
+ # <structures> ::= {<structure>}*
334
+ # <structure> ::= {<boundary> | <path> | <sref> | <aref> |
335
+ # <text> | <node> | <box>} {<property>}* ENDEL
336
+ #
337
+ self.bnf_spec = BnfSpec.new(
338
+ BnfItem.new(Structure, true, true)
339
+ )
340
+
341
+ #
342
+ # Create an Structures object.
343
+ #
344
+ def initialize(structures=[])
345
+ super()
346
+ @records[Structure] = @list = structures
347
+ end
348
+
349
+ end
350
+
351
+
352
+ end