rtext 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,112 @@
1
+ module RText
2
+
3
+ # ContextElementBuilder build a partial model from a set of context lines.
4
+ #
5
+ # Context lines are lines from an RText file which contain a (context) command and all
6
+ # the parent commands wrapped around it. Any sibling commands can be omitted as well as
7
+ # any lines containing closing braces and brackets.
8
+ #
9
+ # The resulting partial model contains a (context) model element and all its parent
10
+ # elements. Further references are not resolved.
11
+ #
12
+ module ContextElementBuilder
13
+
14
+ class << self
15
+
16
+ # Instantiates the context element based on a set of +content_lines+. Content lines
17
+ # are the RText lines containing the nested command headers in the original order.
18
+ # The last line of +context_lines+ is the one which will create the context element.
19
+ # +position_in_line+ is the cursor column position within the last line
20
+ def build_context_element(language, context_lines, position_in_line)
21
+ context_info = fix_context(context_lines)
22
+ return nil unless context_info
23
+ element = instantiate_context_element(language, context_info)
24
+ unless element
25
+ fix_current_line(context_info, position_in_line)
26
+ element = instantiate_context_element(language, context_info)
27
+ end
28
+ element
29
+ end
30
+
31
+ private
32
+
33
+ def instantiate_context_element(language, context_info)
34
+ root_elements = []
35
+ problems = []
36
+ Instantiator.new(language).instantiate(context_info.lines.join("\n"),
37
+ :root_elements => root_elements, :problems => problems)
38
+ if root_elements.size > 0
39
+ find_leaf_child(root_elements.first, context_info.num_elements-1)
40
+ else
41
+ nil
42
+ end
43
+ end
44
+
45
+ def find_leaf_child(element, num_required_children)
46
+ childs = element.class.ecore.eAllReferences.select{|r| r.containment}.collect{|r|
47
+ element.getGenericAsArray(r.name)}.flatten
48
+ if childs.size > 0
49
+ find_leaf_child(childs.first, num_required_children-1)
50
+ elsif num_required_children == 0
51
+ element
52
+ else
53
+ nil
54
+ end
55
+ end
56
+
57
+ ContextInfo = Struct.new(:lines, :num_elements, :pos_leaf_element)
58
+
59
+ # extend +context_lines+ into a set of lines which can be processed by the RText
60
+ # instantiator: cut of curly brace from current line if present and add missing
61
+ # closing curly braces and square brackets
62
+ # returns a ContextInfo containing the new set of lines, the number of model elements
63
+ # contained in this model snipped and the number of the line containing the leaf element
64
+ def fix_context(context_lines)
65
+ context_lines = context_lines.dup
66
+ line = context_lines.last
67
+ return nil if line.nil? || is_non_element_line(line)
68
+ context_lines << strip_curly_brace(context_lines.pop)
69
+ pos_leaf_element = context_lines.size-1
70
+ num_elements = 1
71
+ context_lines.reverse.each do |l|
72
+ if l =~ /\{\s*$/
73
+ context_lines << "}"
74
+ num_elements += 1
75
+ elsif l =~ /\[\s*$/
76
+ context_lines << "]"
77
+ end
78
+ end
79
+ ContextInfo.new(context_lines, num_elements, pos_leaf_element)
80
+ end
81
+
82
+ def is_non_element_line(line)
83
+ line = line.strip
84
+ line == "" || line == "}" || line == "]" || line =~ /^#/ || line =~ /^\w+:$/
85
+ end
86
+
87
+ def strip_curly_brace(line)
88
+ line.sub(/\{\s*$/,'')
89
+ end
90
+
91
+ def fix_current_line(context_info, pos_in_line)
92
+ context_info.lines[context_info.pos_leaf_element] =
93
+ cut_current_argument(context_info.lines[context_info.pos_leaf_element], pos_in_line)
94
+ end
95
+
96
+ def cut_current_argument(line, pos_in_line)
97
+ left_comma_pos = line.rindex(",", pos_in_line-1)
98
+ if left_comma_pos
99
+ line[0..left_comma_pos-1]
100
+ elsif line =~ /^\s*\w+/
101
+ $&
102
+ else
103
+ ""
104
+ end
105
+ end
106
+
107
+ end
108
+
109
+ end
110
+
111
+ end
112
+
@@ -0,0 +1,137 @@
1
+ require 'rgen/environment'
2
+ require 'rgen/util/file_change_detector'
3
+ require 'rgen/fragment/model_fragment'
4
+ require 'rtext/instantiator'
5
+
6
+ module RText
7
+
8
+ # Loads RText files into a FragmentedModel.
9
+ #
10
+ # A glob pattern or file provider specifies the files which should be loaded. The load method
11
+ # can be called to load and to reload the model. Only changed files will be reloaded.
12
+ #
13
+ # Optionally, the loader can use a fragment cache to speed up loading.
14
+ #
15
+ class DefaultLoader
16
+
17
+ # Create a default loader for +language+, loading into +fragmented_model+.
18
+ # It will find files either by evaluating the glob pattern given with +:pattern+
19
+ # (see Dir.glob) or by means of a +file_provider+. Options:
20
+ #
21
+ # :pattern
22
+ # a glob pattern or an array of glob patterns
23
+ # alternatively, a +:file_provider+ may be specified
24
+ #
25
+ # :file_provider
26
+ # a proc which is called without any arguments and should return the files to load
27
+ # this is an alternative to providing +:pattern+
28
+ #
29
+ # :cache
30
+ # a fragment cache to be used for loading
31
+ #
32
+ def initialize(language, fragmented_model, options={})
33
+ @lang = language
34
+ @model = fragmented_model
35
+ @change_detector = RGen::Util::FileChangeDetector.new(
36
+ :file_added => method(:file_added),
37
+ :file_removed => method(:file_removed),
38
+ :file_changed => method(:file_changed))
39
+ @cache = options[:cache]
40
+ @fragment_by_file = {}
41
+ pattern = options[:pattern]
42
+ @file_provider = options[:file_provider] || proc { Dir.glob(pattern) }
43
+ end
44
+
45
+ # Loads or reloads model fragments from files using the file patterns or file provider
46
+ # specified in the constructor. Options:
47
+ #
48
+ # :before_load
49
+ # a proc which is called before a file is actually loaded, receives the fragment to load
50
+ # into and a symbol indicating the kind of loading: :load, :load_cached, :load_update_cache
51
+ # default: no before load proc
52
+ #
53
+ def load(options={})
54
+ @before_load_proc = options[:before_load]
55
+ files = @file_provider.call
56
+ @change_detector.check_files(files)
57
+ @model.resolve(:fragment_provider => method(:fragment_provider),
58
+ :use_target_type => @lang.per_type_identifier)
59
+ end
60
+
61
+ private
62
+
63
+ def file_added(file)
64
+ fragment = RGen::Fragment::ModelFragment.new(file,
65
+ :identifier_provider => @lang.identifier_provider)
66
+ load_fragment_cached(fragment)
67
+ @model.add_fragment(fragment)
68
+ @fragment_by_file[file] = fragment
69
+ end
70
+
71
+ def file_removed(file)
72
+ @model.remove_fragment(@fragment_by_file[file])
73
+ @fragment_by_file.delete(file)
74
+ end
75
+
76
+ def file_changed(file)
77
+ file_removed(file)
78
+ file_added(file)
79
+ end
80
+
81
+ def fragment_provider(element)
82
+ fr = @lang.fragment_ref(element)
83
+ fr && fr.fragment
84
+ end
85
+
86
+ def load_fragment_cached(fragment)
87
+ if @cache
88
+ begin
89
+ result = @cache.load(fragment)
90
+ rescue ArgumentError => e
91
+ # Marshal#load raises an ArgumentError if required classes are not present
92
+ if e.message =~ /undefined class\/module/
93
+ result = :invalid
94
+ else
95
+ raise
96
+ end
97
+ end
98
+ if result == :invalid
99
+ @before_load_proc && @before_load_proc.call(fragment, :load_update_cache)
100
+ load_fragment(fragment)
101
+ @cache.store(fragment)
102
+ else
103
+ @before_load_proc && @before_load_proc.call(fragment, :load_cached)
104
+ end
105
+ else
106
+ @before_load_proc && @before_load_proc.call(fragment, :load)
107
+ load_fragment(fragment)
108
+ end
109
+ end
110
+
111
+ def load_fragment(fragment)
112
+ env = RGen::Environment.new
113
+ urefs = []
114
+ problems = []
115
+ root_elements = []
116
+ inst = RText::Instantiator.new(@lang)
117
+ File.open(fragment.location) do |f|
118
+ inst.instantiate(f.read,
119
+ :env => env,
120
+ :unresolved_refs => urefs,
121
+ :problems => problems,
122
+ :root_elements => root_elements,
123
+ :fragment_ref => fragment.fragment_ref,
124
+ :file_name => fragment.location)
125
+ end
126
+ fragment.data = {:problems => problems}
127
+ fragment.set_root_elements(root_elements,
128
+ :unresolved_refs => urefs,
129
+ :elements => env.elements)
130
+ fragment.build_index
131
+ fragment.resolve_local(:use_target_type => @lang.per_type_identifier)
132
+ end
133
+
134
+ end
135
+
136
+ end
137
+
@@ -0,0 +1,153 @@
1
+ module RText
2
+
3
+ class DefaultServiceProvider
4
+
5
+ def initialize(language, fragmented_model, model_loader)
6
+ @lang = language
7
+ @model = fragmented_model
8
+ @loader = model_loader
9
+ @element_name_index = nil
10
+ @model.add_fragment_change_listener(proc {|fragment, kind|
11
+ @element_name_index = nil
12
+ })
13
+ end
14
+
15
+ def load_model
16
+ @loader.load
17
+ end
18
+
19
+ ReferenceCompletionOption = Struct.new(:identifier, :type)
20
+ def get_reference_completion_options(reference, context)
21
+ if @model.environment
22
+ targets = @model.environment.find(:class => reference.eType.instanceClass)
23
+ else
24
+ clazz = reference.eType.instanceClass
25
+ targets = @model.index.values.flatten.select{|e| e.is_a?(clazz)}
26
+ end
27
+ targets.collect{|t|
28
+ ident = @lang.identifier_provider.call(t, context)
29
+ if ident
30
+ ReferenceCompletionOption.new(ident, t.class.ecore.name)
31
+ else
32
+ nil
33
+ end
34
+ }.compact.sort{|a,b| a.identifier <=> b.identifier}
35
+ end
36
+
37
+ ReferenceTarget = Struct.new(:file, :line, :display_name)
38
+ def get_reference_targets(identifier, context, lines, linepos)
39
+ result = []
40
+ identifier = @lang.qualify_reference(identifier, context)
41
+ targets = @model.index[identifier]
42
+ if targets && @lang.per_type_identifier
43
+ current_line = lines.last
44
+ linestart = current_line[0..linepos-1]
45
+ if linestart =~ /\s*(\w+)\s+(?:[^,]+,)*\s*(\w+):\s*(\S*)$/
46
+ command, fn, prefix = $1, $2, $3
47
+ clazz = @lang.class_by_command(command)
48
+ feature = clazz && @lang.non_containments(clazz.ecore).find{|f| f.name == fn}
49
+ if feature
50
+ targets = targets.select{|t| t.is_a?(feature.eType.instanceClass)}
51
+ end
52
+ end
53
+ end
54
+ targets && targets.each do |t|
55
+ if @lang.fragment_ref(t)
56
+ path = File.expand_path(@lang.fragment_ref(t).fragment.location)
57
+ result << ReferenceTarget.new(path, @lang.line_number(t), "#{identifier} [#{t.class.ecore.name}]")
58
+ end
59
+ end
60
+ result
61
+ end
62
+
63
+ def get_referencing_elements(identifier, context)
64
+ result = []
65
+ targets = @model.index[@lang.identifier_provider.call(context, nil)]
66
+ if targets && @lang.per_type_identifier
67
+ targets = targets.select{|t| t.class == context.class}
68
+ end
69
+ if targets && targets.size == 1
70
+ target = targets.first
71
+ elements = target.class.ecore.eAllReferences.select{|r|
72
+ r.eOpposite && !r.containment && !r.eOpposite.containment}.collect{|r|
73
+ target.getGenericAsArray(r.name)}.flatten
74
+ elements.each do |e|
75
+ if @lang.fragment_ref(e)
76
+ path = File.expand_path(@lang.fragment_ref(e).fragment.location)
77
+ display_name = ""
78
+ ident = @lang.identifier_provider.call(e, nil)
79
+ display_name += "#{ident} " if ident
80
+ display_name += "[#{e.class.ecore.name}]"
81
+ result << ReferenceTarget.new(path, @lang.line_number(e), display_name)
82
+ end
83
+ end
84
+ end
85
+ result
86
+ end
87
+
88
+ FileProblems = Struct.new(:file, :problems)
89
+ Problem = Struct.new(:severity, :line, :message)
90
+ def get_problems
91
+ load_model
92
+ result = []
93
+ @model.fragments.sort{|a,b| a.location <=> b.location}.each do |fragment|
94
+ problems = []
95
+ if fragment.data && fragment.data[:problems]
96
+ fragment.data[:problems].each do |p|
97
+ problems << Problem.new("Error", p.line, p.message)
98
+ end
99
+ end
100
+ fragment.unresolved_refs.each do |ur|
101
+ # TODO: where do these proxies come from?
102
+ next unless ur.proxy.targetIdentifier
103
+ problems << Problem.new("Error", @lang.line_number(ur.element), "unresolved reference #{ur.proxy.targetIdentifier}")
104
+ end
105
+ if problems.size > 0
106
+ result << FileProblems.new(File.expand_path(fragment.location), problems)
107
+ end
108
+ end
109
+ result
110
+ end
111
+
112
+ OpenElementChoice = Struct.new(:display_name, :file, :line)
113
+ def get_open_element_choices(pattern)
114
+ result = []
115
+ return result unless pattern
116
+ sub_index = element_name_index[pattern[0..0].downcase]
117
+ sub_index && sub_index.each_pair do |ident, elements|
118
+ if ident.split(/\W/).last.downcase.index(pattern.downcase) == 0
119
+ elements.each do |e|
120
+ if @lang.fragment_ref(e)
121
+ non_word_index = ident.rindex(/\W/)
122
+ if non_word_index
123
+ name = ident[non_word_index+1..-1]
124
+ scope = ident[0..non_word_index-1]
125
+ else
126
+ name = ident
127
+ scope = ""
128
+ end
129
+ display_name = "#{name} [#{e.class.ecore.name}]"
130
+ display_name += " - #{scope}" if scope.size > 0
131
+ path = File.expand_path(@lang.fragment_ref(e).fragment.location)
132
+ result << OpenElementChoice.new(display_name, path, @lang.line_number(e))
133
+ end
134
+ end
135
+ end
136
+ end
137
+ result.sort{|a,b| a.display_name <=> b.display_name}
138
+ end
139
+
140
+ def element_name_index
141
+ return @element_name_index if @element_name_index
142
+ @element_name_index = {}
143
+ @model.index.each_pair do |ident, elements|
144
+ key = ident.split(/\W/).last[0..0].downcase
145
+ @element_name_index[key] ||= {}
146
+ @element_name_index[key][ident] = elements
147
+ end
148
+ @element_name_index
149
+ end
150
+
151
+ end
152
+
153
+ end
@@ -0,0 +1,284 @@
1
+ require 'rgen/ecore/ecore_ext'
2
+ require 'rgen/instantiator/reference_resolver'
3
+ require 'rtext/parser'
4
+
5
+ module RText
6
+
7
+ class Instantiator
8
+
9
+ # A problem found during instantiation
10
+ # if the file is not known, it will be nil
11
+ InstantiatorProblem = Struct.new(:message, :file, :line)
12
+
13
+ # Creates an instantiator for RText::Language +language+
14
+ #
15
+ def initialize(language)
16
+ @lang = language
17
+ end
18
+
19
+ # instantiate +str+, +options+ include:
20
+ #
21
+ # :env
22
+ # environment to which model elements will be added
23
+ #
24
+ # :problems
25
+ # an array to which problems will be appended
26
+ #
27
+ # :unresolved_refs
28
+ # an array to which unresolved references will be appended
29
+ #
30
+ # :root_elements
31
+ # an array which will hold the root elements
32
+ #
33
+ # :file_name
34
+ # name of the file being instantiated, will be set on model elements
35
+ #
36
+ # :fragment_ref
37
+ # object which references the fragment being instantiated, will be set on model elements
38
+ #
39
+ def instantiate(str, options={})
40
+ @line_numbers = {}
41
+ @env = options[:env]
42
+ @problems = options[:problems] || []
43
+ @unresolved_refs = options[:unresolved_refs]
44
+ @root_elements = options[:root_elements] || []
45
+ @file_name = options[:file_name]
46
+ @fragment_ref = options[:fragment_ref]
47
+ parser = Parser.new(@lang.reference_regexp)
48
+ begin
49
+ @root_elements.clear
50
+ parser.parse(str) do |*args|
51
+ if args[0]
52
+ create_element(*args)
53
+ else
54
+ unassociated_comments(args[3])
55
+ end
56
+ end
57
+ rescue Parser::Error => e
58
+ problem(e.message, e.line)
59
+ @unresolved_refs.clear if @unresolved_refs
60
+ @root_elements.clear
61
+ end
62
+ if @unresolved_refs
63
+ @unresolved_refs.each do |ur|
64
+ ur.proxy.targetIdentifier = @lang.qualify_reference(ur.proxy.targetIdentifier, ur.element)
65
+ end
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ def unassociated_comments(comments)
72
+ comments.each do |c|
73
+ handle_comment(c, nil)
74
+ end
75
+ end
76
+
77
+ def create_element(command, arg_list, element_list, comments, is_root)
78
+ clazz = @lang.class_by_command(command.value)
79
+ if !clazz
80
+ problem("Unknown command '#{command.value}'", command.line)
81
+ return
82
+ end
83
+ if clazz.ecore.abstract
84
+ problem("Unknown command '#{command.value}' (metaclass is abstract)", command.line)
85
+ return
86
+ end
87
+ element = clazz.new
88
+ @env << element if @env
89
+ @root_elements << element if is_root
90
+ unlabled_args = @lang.unlabled_arguments(clazz.ecore).name
91
+ di_index = 0
92
+ defined_args = {}
93
+ arg_list.each do |a|
94
+ if is_labeled(a)
95
+ set_argument(element, a[0].value, a[1], defined_args, command.line)
96
+ else
97
+ if di_index < unlabled_args.size
98
+ set_argument(element, unlabled_args[di_index], a, defined_args, command.line)
99
+ di_index += 1
100
+ else
101
+ problem("Unexpected unlabled argument, #{unlabled_args.size} unlabled arguments expected", command.line)
102
+ end
103
+ end
104
+ end
105
+ element_list.each do |e|
106
+ if is_labeled(e)
107
+ add_children(element, e[1], e[0].value, e[0].line)
108
+ else
109
+ add_children(element, e, nil, nil)
110
+ end
111
+ end
112
+ set_line_number(element, command.line)
113
+ set_file_name(element)
114
+ set_fragment_ref(element)
115
+ comments.each do |c|
116
+ handle_comment(c, element)
117
+ end
118
+ element
119
+ end
120
+
121
+ def add_children(element, children, role, role_line)
122
+ if role
123
+ feature = @lang.feature_by_name(element.class.ecore, role)
124
+ if !feature
125
+ problem("Unknown child role '#{role}'", role_line)
126
+ return
127
+ end
128
+ if !feature.is_a?(RGen::ECore::EReference) || !feature.containment
129
+ problem("Role '#{role}' can not take child elements", role_line)
130
+ return
131
+ end
132
+ children = [children] unless children.is_a?(Array)
133
+ children.compact!
134
+ if children.size == 0
135
+ return
136
+ end
137
+ if !feature.many &&
138
+ (element.getGenericAsArray(role).size > 0 || children.size > 1)
139
+ problem("Only one child allowed in role '#{role}'", line_number(children[0]))
140
+ return
141
+ end
142
+ expected_type = @lang.concrete_types(feature.eType)
143
+ children.each do |c|
144
+ if !expected_type.include?(c.class.ecore)
145
+ problem("Role '#{role}' can not take a #{c.class.ecore.name}, expected #{expected_type.name.join(", ")}", line_number(c))
146
+ else
147
+ element.setOrAddGeneric(feature.name, c)
148
+ end
149
+ end
150
+ else
151
+ raise "if there is no role, children must not be an Array" if children.is_a?(Array)
152
+ child = children
153
+ return if child.nil?
154
+ feature = @lang.containments_by_target_type(element.class.ecore, child.class.ecore)
155
+ if feature.size == 0
156
+ problem("This kind of element can not be contained here", line_number(child))
157
+ return
158
+ end
159
+ if feature.size > 1
160
+ problem("Role of element is ambiguous, use a role label", line_number(child))
161
+ return
162
+ end
163
+ feature = feature[0]
164
+ if element.getGenericAsArray(feature.name).size > 0 && !feature.many
165
+ problem("Only one child allowed in role '#{feature.name}'", line_number(child))
166
+ return
167
+ end
168
+ element.setOrAddGeneric(feature.name, child)
169
+ end
170
+ end
171
+
172
+ def set_argument(element, name, value, defined_args, line)
173
+ feature = @lang.feature_by_name(element.class.ecore, name)
174
+ if !feature
175
+ problem("Unknown argument '#{name}'", line)
176
+ return
177
+ end
178
+ if feature.is_a?(RGen::ECore::EReference) && feature.containment
179
+ problem("Argument '#{name}' can only take child elements", line)
180
+ return
181
+ end
182
+ if defined_args[name]
183
+ problem("Argument '#{name}' already defined", line)
184
+ return
185
+ end
186
+ value = [value] unless value.is_a?(Array)
187
+ if value.size > 1 && !feature.many
188
+ problem("Argument '#{name}' can take only one value", line)
189
+ return
190
+ end
191
+ expected_kind = expected_token_kind(feature)
192
+ value.each do |v|
193
+ if !expected_kind.include?(v.kind)
194
+ problem("Argument '#{name}' can not take a #{v.kind}, expected #{expected_kind.join(", ")}", line)
195
+ elsif feature.eType.is_a?(RGen::ECore::EEnum)
196
+ if !feature.eType.eLiterals.name.include?(v.value)
197
+ problem("Argument '#{name}' can not take value #{v.value}, expected #{feature.eType.eLiterals.name.join(", ")}", line)
198
+ else
199
+ element.setOrAddGeneric(feature.name, v.value.to_sym)
200
+ end
201
+ elsif feature.is_a?(RGen::ECore::EReference)
202
+ proxy = RGen::MetamodelBuilder::MMProxy.new(v.value)
203
+ if @unresolved_refs
204
+ @unresolved_refs <<
205
+ RGen::Instantiator::ReferenceResolver::UnresolvedReference.new(element, feature.name, proxy)
206
+ end
207
+ element.setOrAddGeneric(feature.name, proxy)
208
+ else
209
+ element.setOrAddGeneric(feature.name, v.value)
210
+ end
211
+ end
212
+ defined_args[name] = true
213
+ end
214
+
215
+ def handle_comment(comment_desc, element)
216
+ if @lang.comment_handler
217
+ kind = comment_desc[1]
218
+ if kind == :eol
219
+ comment = comment_desc[0].value
220
+ else
221
+ comment = comment_desc[0].collect{|c| c.value}.join("\n")
222
+ end
223
+ success = @lang.comment_handler.call(comment, kind, element, @env)
224
+ if !success
225
+ if element.nil?
226
+ problem("Unassociated comment not allowed", comment_desc[0][0].line)
227
+ else
228
+ problem("This kind of element can not take this comment", line_number(element))
229
+ end
230
+ end
231
+ end
232
+ end
233
+
234
+ def is_labeled(a)
235
+ a.is_a?(Array) && a[0].respond_to?(:kind) && a[0].kind == :label
236
+ end
237
+
238
+ def expected_token_kind(feature)
239
+ if feature.is_a?(RGen::ECore::EReference)
240
+ [:reference, :identifier]
241
+ elsif feature.eType.is_a?(RGen::ECore::EEnum)
242
+ [:identifier, :string]
243
+ else
244
+ { String => [:string, :identifier],
245
+ Integer => [:integer],
246
+ Float => [:float],
247
+ RGen::MetamodelBuilder::DataTypes::Boolean => [:boolean]
248
+ }[feature.eType.instanceClass]
249
+ end
250
+ end
251
+
252
+ def set_line_number(element, line)
253
+ @line_numbers[element] = line
254
+ if @lang.line_number_attribute && element.respond_to?("#{@lang.line_number_attribute}=")
255
+ element.send("#{@lang.line_number_attribute}=", line)
256
+ end
257
+ end
258
+
259
+ def set_file_name(element)
260
+ if @file_name &&
261
+ @lang.file_name_attribute && element.respond_to?("#{@lang.file_name_attribute}=")
262
+ element.send("#{@lang.file_name_attribute}=", @file_name)
263
+ end
264
+ end
265
+
266
+ def set_fragment_ref(element)
267
+ if @fragment_ref &&
268
+ @lang.fragment_ref_attribute && element.respond_to?("#{@lang.fragment_ref_attribute}=")
269
+ element.send("#{@lang.fragment_ref_attribute}=", @fragment_ref)
270
+ end
271
+ end
272
+
273
+ def line_number(e)
274
+ @line_numbers[e]
275
+ end
276
+
277
+ def problem(msg, line)
278
+ @problems << InstantiatorProblem.new(msg, @file_name, line)
279
+ end
280
+
281
+ end
282
+
283
+ end
284
+