rgen-xsd 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,110 @@
1
+ $:.unshift(File.dirname(__FILE__)+"/../../")
2
+
3
+ require "rgen/environment"
4
+ require "rgen/util/name_helper"
5
+ require 'mmgen/metamodel_generator'
6
+
7
+ require "rgen/xsd/xsd_instantiator"
8
+ require "rgen/xsd/metamodel_modification_helper"
9
+ require 'optparse'
10
+
11
+ include RGen::Util::NameHelper
12
+ include RGen::XSD::MetamodelModificationHelper
13
+
14
+ options = {}
15
+ optparse = OptionParser.new do|opts|
16
+ opts.banner = "Usage: metamodel_generator.rb [options] <schema file>+"
17
+
18
+ opts.on( '--mm VERSION', 'XML schema version: 1.0 or 1.1' ) do |v|
19
+ options[:mm] = v
20
+ end
21
+
22
+ opts.on('-o FILE', "Output metamodel file") do |f|
23
+ options[:outfile] = f
24
+ end
25
+ end
26
+ optparse.parse!
27
+ if !options[:mm]
28
+ puts "Schema version not specified"
29
+ exit
30
+ end
31
+ if !options[:outfile]
32
+ puts "Output file not specified"
33
+ exit
34
+ end
35
+
36
+ case options[:mm]
37
+ when "1.0"
38
+ require "rgen/xsd/1.0/metamodel"
39
+ when "1.1"
40
+ require "rgen/xsd/1.1/metamodel"
41
+ else
42
+ puts "Unknown schema version: #{options[:mm]}"
43
+ exit
44
+ end
45
+ XMLSchemaMetamodel = MM::W3Org2001XMLSchema
46
+
47
+ env = RGen::Environment.new
48
+ mm = XMLSchemaMetamodel
49
+ inst = RGen::XSD::XSDInstantiator.new(env, mm)
50
+
51
+ ARGV.each do |fn|
52
+ inst.instantiate(fn)
53
+ end
54
+
55
+ problems = []
56
+ inst.resolve(problems)
57
+
58
+ if problems.size > 0
59
+ problems.each do |p|
60
+ puts p
61
+ end
62
+ puts "#{problems.size} problems, stop."
63
+ exit
64
+ end
65
+
66
+ require "rgen/xsd/xml_schema_metamodel_ext"
67
+ require "rgen/xsd/xsd_to_ecore"
68
+
69
+ sch = env.find(:class => XMLSchemaMetamodel::Element, :name => "schema").first
70
+
71
+ env_ecore = RGen::Environment.new
72
+ trans = RGen::XSD::XSDToEcoreTransformer.new(env, env_ecore)
73
+ root = trans.transform
74
+
75
+ class_complexType = env_ecore.find(:class => RGen::ECore::EClass, :name => "ComplexType").first
76
+ class_simpleType = env_ecore.find(:class => RGen::ECore::EClass, :name => "SimpleType").first
77
+ class_element = env_ecore.find(:class => RGen::ECore::EClass, :name => "Element").first
78
+ class_group = env_ecore.find(:class => RGen::ECore::EClass, :name => "Group").first
79
+ class_attribute = env_ecore.find(:class => RGen::ECore::EClass, :name => "Attribute").first
80
+ class_attributeGroup = env_ecore.find(:class => RGen::ECore::EClass, :name => "AttributeGroup").first
81
+
82
+ # insert Type class as supertype of ComplexType and SimpleType
83
+ class_type = env_ecore.new(RGen::ECore::EClass, :name => "Type", :ePackage => class_complexType.ePackage,
84
+ :eSuperTypes => class_complexType.eSuperTypes)
85
+ class_complexType.eSuperTypes = [class_type]
86
+ class_simpleType.eSuperTypes = [class_type]
87
+
88
+ # explicit references
89
+ attribute_to_reference(env_ecore, "RestrictionType#base", class_type)
90
+ attribute_to_reference(env_ecore, "RestrictionTYPE#base", class_type)
91
+ attribute_to_reference(env_ecore, "ExtensionType#base", class_type)
92
+ attribute_to_reference(env_ecore, "Element#ref", class_element)
93
+ attribute_to_reference(env_ecore, "Element#type", class_type)
94
+ attribute_to_reference(env_ecore, "Group#ref", class_group)
95
+ attribute_to_reference(env_ecore, "AttributeGroup#ref", class_attributeGroup)
96
+ attribute_to_reference(env_ecore, "Attribute#ref", class_attribute)
97
+ attribute_to_reference(env_ecore, "Attribute#type", class_simpleType)
98
+ attribute_to_reference(env_ecore, "Element#substitutionGroup", class_element)
99
+ attribute_to_reference(env_ecore, "ListTYPE#itemType", class_simpleType)
100
+ attribute_to_reference(env_ecore, "UnionTYPE#memberTypes", class_simpleType)
101
+
102
+ # bidirectional references
103
+ create_opposite(env_ecore, "Element#substitutionGroup", "substitutes", -1)
104
+ create_opposite(env_ecore, "Element#complexType", "containingElement", 1)
105
+ create_opposite(env_ecore, "Element#simpleType", "containingElement", 1)
106
+ create_opposite(env_ecore, "Attribute#simpleType", "containingAttribute", 1)
107
+
108
+ include MMGen::MetamodelGenerator
109
+ generateMetamodel(root, options[:outfile])
110
+
@@ -0,0 +1,41 @@
1
+ module RGen
2
+
3
+ module XSD
4
+
5
+ module MetamodelModificationHelper
6
+
7
+ def find_feature(env, desc)
8
+ env.find(:class => RGen::ECore::EStructuralFeature, :name => desc.split("#").last).
9
+ find{|f| f.eContainingClass.name == desc.split("#").first}
10
+ end
11
+
12
+ def attribute_to_reference(env, desc, target)
13
+ a = find_feature(env, desc)
14
+ r = env.new(RGen::ECore::EReference, Hash[
15
+ RGen::ECore::EStructuralFeature.ecore.eAllStructuralFeatures.collect do |f|
16
+ next if f.derived
17
+ p = [f.name, a.getGeneric(f.name)]
18
+ if f.many
19
+ a.setGeneric(f.name, [])
20
+ else
21
+ a.setGeneric(f.name, nil)
22
+ end
23
+ p
24
+ end])
25
+ r.eType = target
26
+ r
27
+ end
28
+
29
+ def create_opposite(env, desc, name, upper_bound)
30
+ r = find_feature(env, desc)
31
+ r.eOpposite =
32
+ env.new(RGen::ECore::EReference, :name => name, :eType => r.eContainingClass,
33
+ :eContainingClass => r.eType, :upperBound => upper_bound, :eOpposite => r)
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+
@@ -0,0 +1,169 @@
1
+ module RGen
2
+ module XSD
3
+ module Particle
4
+
5
+ # helper structure representing a particle or particle tree
6
+ # particles of kind :choice, :sequence or :all can have child particles
7
+ Particle = Struct.new(:kind, :children, :minOccurs, :maxOccurs, :node)
8
+
9
+ # builds a flat list of the element particles contained in +node+
10
+ # to be used for ComplexType and complex content extensions
11
+ def element_particles(node)
12
+ trees = build_particles_trees(node)
13
+ if trees.size == 1
14
+ flatten_particle_tree(trees.first)
15
+ elsif trees.size > 1
16
+ puts "WARN: only one particle expected as a content model"
17
+ []
18
+ else
19
+ []
20
+ end
21
+ end
22
+
23
+ def add_substitution_particles(particles)
24
+ result = []
25
+ particles.each do |p|
26
+ result << p
27
+ if p.kind == :element
28
+ p.node.substitutes.each do |s|
29
+ result << Particle.new(:element, [], p.minOccurs, p.maxOccurs, s)
30
+ end
31
+ end
32
+ end
33
+ result
34
+ end
35
+
36
+ # flattens the particle tree with root +particle+ and calculates min/max occurrence
37
+ # returns a list of particles of kind "element" and "any"
38
+ # element names are unique throughout the list
39
+ # the order is the order of first occurrence
40
+ def flatten_particle_tree(particle)
41
+ if particle.kind == :element || particle.kind == :any
42
+ [particle]
43
+ else
44
+ eocc = {}
45
+ elist = []
46
+ particle.children.each do |c|
47
+ flatten_particle_tree(c).each do |e|
48
+ if e.kind == :element
49
+ name = e.node.name
50
+ else
51
+ name = "#any#"
52
+ end
53
+ if !eocc[name]
54
+ eocc[name] = []
55
+ elist << name
56
+ end
57
+ eocc[name] << e
58
+ end
59
+ end
60
+ is_unbounded = lambda do |particle, elements|
61
+ (particle.maxOccurs == "unbounded" &&
62
+ elements.any?{|e| e.maxOccurs == "unbounded" || e.maxOccurs.to_i > 0}) ||
63
+ (particle.maxOccurs.to_i > 0 &&
64
+ elements.any?{|e| e.maxOccurs == "unbounded"})
65
+ end
66
+ if particle.kind == :choice
67
+ elist.collect do |n|
68
+ if eocc[n].size < particle.children.size
69
+ # element is not in every choice
70
+ min = 0
71
+ else
72
+ min = eocc[n].collect{|e| e.minOccurs.to_i}.min * particle.minOccurs.to_i
73
+ end
74
+ if is_unbounded.call(particle, eocc[n])
75
+ max = "unbounded"
76
+ else
77
+ max = eocc[n].collect{|e| e.maxOccurs.to_i}.max * particle.maxOccurs.to_i
78
+ end
79
+ if n == "#any#"
80
+ Particle.new(:any, [], min, max, eocc[n].first.node)
81
+ else
82
+ Particle.new(:element, [], min, max, eocc[n].first.node)
83
+ end
84
+ end
85
+ else # sequence or all
86
+ elist.collect do |n|
87
+ min = eocc[n].inject(0){|m, e| m + e.minOccurs.to_i} * particle.minOccurs.to_i
88
+ if is_unbounded.call(particle, eocc[n])
89
+ max = "unbounded"
90
+ else
91
+ max = eocc[n].inject(0){|m, e| m + e.maxOccurs.to_i} * particle.maxOccurs.to_i
92
+ end
93
+ if n == "#any#"
94
+ Particle.new(:any, [], min, max, eocc[n].first.node)
95
+ else
96
+ Particle.new(:element, [], min, max, eocc[n].first.node)
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ # builds the particle trees for the particles contained in node
104
+ def build_particles_trees(node)
105
+ trees = []
106
+ # ComplexType and extension type have only one choice, sequence, all, group
107
+ choices = node.choice
108
+ choices = [choices].compact unless choices.is_a?(Array)
109
+ choices.each do |c|
110
+ trees << Particle.new(:choice, build_particles_trees(c), c.minOccurs || "1", c.maxOccurs || "1", c)
111
+ end
112
+ sequences = node.sequence
113
+ sequences = [sequences].compact unless sequences.is_a?(Array)
114
+ sequences.each do |s|
115
+ trees << Particle.new(:sequence, build_particles_trees(s), s.minOccurs || "1", s.maxOccurs || "1", s)
116
+ end
117
+ alls = node.all
118
+ alls = [alls].compact unless alls.is_a?(Array)
119
+ alls.each do |a|
120
+ trees << Particle.new(:all, build_particles_trees(s), a.minOccurs || "1", a.maxOccurs || "1", a)
121
+ end
122
+ # Group definitions don't have group reference particles
123
+ if node.respond_to?(:group)
124
+ groups = node.group
125
+ groups = [groups].compact unless groups.is_a?(Array)
126
+ groups.each do |g|
127
+ if g.ref
128
+ # the referenced group is a Model Group Definition, i.e. a node wrapping a Model Group
129
+ # it must contain at most one of Choice, Sequence, All
130
+ # it must not contain another "group" tag because this would be a particle
131
+ group_childs = g.ref.all + g.ref.choice + g.ref.sequence
132
+ if group_childs.size == 1
133
+ gtc = build_particles_trees(g.ref).first
134
+ gtc.minOccurs = g.minOccurs || "1"
135
+ gtc.maxOccurs = g.maxOccurs || "1"
136
+ trees << gtc
137
+ elsif group_childs.size > 1
138
+ puts "WARN: ignoring model group definition containing more than one model group"
139
+ else
140
+ # empty, ignore
141
+ end
142
+ else
143
+ puts "WARN: ignoring non-toplevel group without a ref"
144
+ end
145
+ end
146
+ end
147
+ # ComplexType and complex content extension don't have element particles
148
+ if node.respond_to?(:element)
149
+ node.element.each do |e|
150
+ if e.ref
151
+ trees << Particle.new(:element, [], e.minOccurs || "1", e.maxOccurs || "1", e.ref)
152
+ else
153
+ trees << Particle.new(:element, [], e.minOccurs || "1", e.maxOccurs || "1", e)
154
+ end
155
+ end
156
+ end
157
+ # ComplexType and complex content extension don't have 'any' particles
158
+ if node.respond_to?(:any)
159
+ node.any.each do |a|
160
+ trees << Particle.new(:any, [], a.minOccurs || "1", a.maxOccurs || "1", a)
161
+ end
162
+ end
163
+ trees
164
+ end
165
+
166
+ end
167
+ end
168
+ end
169
+
@@ -0,0 +1,87 @@
1
+ module RGen
2
+ module XSD
3
+ module SimpleType
4
+
5
+ SimpleType = Struct.new(:type, :isList, :minOccurs, :maxOccurs)
6
+
7
+ def build_type_desc(type)
8
+ builtin = builtin_type(type)
9
+ if builtin
10
+ builtin
11
+ elsif type.respond_to?(:list) && type.list
12
+ if type.list.itemType || type.list.simpleType
13
+ td = build_type_desc(type.list.itemType || type.list.simpleType)
14
+ td.isList = true
15
+ td.minOccurs = 0
16
+ td.maxOccurs = -1
17
+ td
18
+ else
19
+ puts "WARN: list type without an item type"
20
+ SimpleType.new(:string, false, 0, 1)
21
+ end
22
+ elsif type.respond_to?(:union) && type.union
23
+ # TODO
24
+ # (type.union.memberTypes + type.union.simpleType).each do |t|
25
+ # build_type_desc(t)
26
+ # end
27
+ # TODO: make this a string instead? otherwise there is a problem serializing with RText (could also be fixed in RText: o.to_s)
28
+ SimpleType.new(:object, false, 0, 1)
29
+ elsif type.respond_to?(:restriction) && type.restriction
30
+ if type.restriction.base
31
+ td = build_type_desc(type.restriction.base)
32
+ if td.type == :string && type.restriction.enumeration.size > 0
33
+ SimpleType.new(
34
+ type.restriction.enumeration.collect { |e| e.value },
35
+ false, 0, 1)
36
+ elsif td.isList
37
+ # assumption: restrictions are properly nested
38
+ # note: this doesn't work correctly in case of lists of lists
39
+ td.minOccurs = type.restriction.minLength.first.value.to_i if type.restriction.minLength.first
40
+ td.maxOccurs = type.restriction.maxLength.first.value.to_i if type.restriction.maxLength.first
41
+ td
42
+ else
43
+ # unhandled restriction, pass the original
44
+ td
45
+ end
46
+ else
47
+ puts "WARN: restriction type without a base type"
48
+ SimpleType.new(:string, false, 0, 1)
49
+ end
50
+ else
51
+ puts "WARN: unknown node type: #{type.class}"
52
+ SimpleType.new(:string, false, 0, 1)
53
+ end
54
+ end
55
+
56
+ def builtin_type(type)
57
+ if type.nil?
58
+ # assumen anySimpleType
59
+ return SimpleType.new(:string, false, 0, 1)
60
+ end
61
+ case type.name
62
+ when "anyType"
63
+ SimpleType.new(:object, false, 0, 1)
64
+ when "anySimpleType", "string", "normalizedString", "token", "language", "Name", "NCName",
65
+ "ID", "IDREF", "ENTITY", "NMTOKEN", "base64Binary", "hexBinary", "anyURI", "QName",
66
+ "NOTATION", "duration", "dateTime", "time", "date", "gYearMonth", "gYear", "gMonthDay",
67
+ "gDay", "gMonth"
68
+ SimpleType.new(:string, false, 0, 1)
69
+ when "IDREFS", "ENTITIES", "NMTOKENS"
70
+ SimpleType.new(:string, true, 0, -1)
71
+ when "float", "double"
72
+ SimpleType.new(:float, false, 0, 1)
73
+ when "decimal", "integer", "nonPositiveInteger", "negativeInteger", "long", "int", "short",
74
+ "byte", "nonNegativeInteger", "unsignedLong", "unsignedInt", "unsignedShort",
75
+ "unsignedByte", "positiveInteger"
76
+ SimpleType.new(:int, false, 0, 1)
77
+ when "boolean"
78
+ SimpleType.new(:boolean, false, 0, 1)
79
+ else
80
+ nil
81
+ end
82
+ end
83
+
84
+ end
85
+ end
86
+ end
87
+
@@ -0,0 +1,6 @@
1
+ This folder contains the following schema parts as published by the W3C
2
+
3
+ * xml.xsd (http://www.w3.org/2007/08/xml.xsd)
4
+
5
+ Copyright � 2007 World Wide Web Consortium, (Massachusetts Institute of Technology, European Research Consortium for Informatics and Mathematics, Keio University). All Rights Reserved. http://www.w3.org/Consortium/Legal/2002/copyright-documents-20021231
6
+
@@ -0,0 +1,145 @@
1
+ <?xml version='1.0'?>
2
+ <xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace" xmlns:xs="http://www.w3.org/2001/XMLSchema" xml:lang="en">
3
+
4
+ <xs:annotation>
5
+ <xs:documentation>
6
+ See http://www.w3.org/XML/1998/namespace.html and
7
+ http://www.w3.org/TR/REC-xml for information about this namespace.
8
+
9
+ This schema document describes the XML namespace, in a form
10
+ suitable for import by other schema documents.
11
+
12
+ Note that local names in this namespace are intended to be defined
13
+ only by the World Wide Web Consortium or its subgroups. The
14
+ following names are currently defined in this namespace and should
15
+ not be used with conflicting semantics by any Working Group,
16
+ specification, or document instance:
17
+
18
+ base (as an attribute name): denotes an attribute whose value
19
+ provides a URI to be used as the base for interpreting any
20
+ relative URIs in the scope of the element on which it
21
+ appears; its value is inherited. This name is reserved
22
+ by virtue of its definition in the XML Base specification.
23
+
24
+ id (as an attribute name): denotes an attribute whose value
25
+ should be interpreted as if declared to be of type ID.
26
+ This name is reserved by virtue of its definition in the
27
+ xml:id specification.
28
+
29
+ lang (as an attribute name): denotes an attribute whose value
30
+ is a language code for the natural language of the content of
31
+ any element; its value is inherited. This name is reserved
32
+ by virtue of its definition in the XML specification.
33
+
34
+ space (as an attribute name): denotes an attribute whose
35
+ value is a keyword indicating what whitespace processing
36
+ discipline is intended for the content of the element; its
37
+ value is inherited. This name is reserved by virtue of its
38
+ definition in the XML specification.
39
+
40
+ Father (in any context at all): denotes Jon Bosak, the chair of
41
+ the original XML Working Group. This name is reserved by
42
+ the following decision of the W3C XML Plenary and
43
+ XML Coordination groups:
44
+
45
+ In appreciation for his vision, leadership and dedication
46
+ the W3C XML Plenary on this 10th day of February, 2000
47
+ reserves for Jon Bosak in perpetuity the XML name
48
+ xml:Father
49
+ </xs:documentation>
50
+ </xs:annotation>
51
+
52
+ <xs:annotation>
53
+ <xs:documentation>This schema defines attributes and an attribute group
54
+ suitable for use by
55
+ schemas wishing to allow xml:base, xml:lang, xml:space or xml:id
56
+ attributes on elements they define.
57
+
58
+ To enable this, such a schema must import this schema
59
+ for the XML namespace, e.g. as follows:
60
+ &lt;schema . . .>
61
+ . . .
62
+ &lt;import namespace="http://www.w3.org/XML/1998/namespace"
63
+ schemaLocation="http://www.w3.org/2001/xml.xsd"/>
64
+
65
+ Subsequently, qualified reference to any of the attributes
66
+ or the group defined below will have the desired effect, e.g.
67
+
68
+ &lt;type . . .>
69
+ . . .
70
+ &lt;attributeGroup ref="xml:specialAttrs"/>
71
+
72
+ will define a type which will schema-validate an instance
73
+ element with any of those attributes</xs:documentation>
74
+ </xs:annotation>
75
+
76
+ <xs:annotation>
77
+ <xs:documentation>In keeping with the XML Schema WG's standard versioning
78
+ policy, this schema document will persist at
79
+ http://www.w3.org/2007/08/xml.xsd.
80
+ At the date of issue it can also be found at
81
+ http://www.w3.org/2001/xml.xsd.
82
+ The schema document at that URI may however change in the future,
83
+ in order to remain compatible with the latest version of XML Schema
84
+ itself, or with the XML namespace itself. In other words, if the XML
85
+ Schema or XML namespaces change, the version of this document at
86
+ http://www.w3.org/2001/xml.xsd will change
87
+ accordingly; the version at
88
+ http://www.w3.org/2007/08/xml.xsd will not change.
89
+ </xs:documentation>
90
+ </xs:annotation>
91
+
92
+ <xs:attribute name="lang">
93
+ <xs:annotation>
94
+ <xs:documentation>Attempting to install the relevant ISO 2- and 3-letter
95
+ codes as the enumerated possible values is probably never
96
+ going to be a realistic possibility. See
97
+ RFC 3066 at http://www.ietf.org/rfc/rfc3066.txt and the IANA registry
98
+ at http://www.iana.org/assignments/lang-tag-apps.htm for
99
+ further information.
100
+
101
+ The union allows for the 'un-declaration' of xml:lang with
102
+ the empty string.</xs:documentation>
103
+ </xs:annotation>
104
+ <xs:simpleType>
105
+ <xs:union memberTypes="xs:language">
106
+ <xs:simpleType>
107
+ <xs:restriction base="xs:string">
108
+ <xs:enumeration value=""/>
109
+ </xs:restriction>
110
+ </xs:simpleType>
111
+ </xs:union>
112
+ </xs:simpleType>
113
+ </xs:attribute>
114
+
115
+ <xs:attribute name="space">
116
+ <xs:simpleType>
117
+ <xs:restriction base="xs:NCName">
118
+ <xs:enumeration value="default"/>
119
+ <xs:enumeration value="preserve"/>
120
+ </xs:restriction>
121
+ </xs:simpleType>
122
+ </xs:attribute>
123
+
124
+ <xs:attribute name="base" type="xs:anyURI">
125
+ <xs:annotation>
126
+ <xs:documentation>See http://www.w3.org/TR/xmlbase/ for
127
+ information about this attribute.</xs:documentation>
128
+ </xs:annotation>
129
+ </xs:attribute>
130
+
131
+ <xs:attribute name="id" type="xs:ID">
132
+ <xs:annotation>
133
+ <xs:documentation>See http://www.w3.org/TR/xml-id/ for
134
+ information about this attribute.</xs:documentation>
135
+ </xs:annotation>
136
+ </xs:attribute>
137
+
138
+ <xs:attributeGroup name="specialAttrs">
139
+ <xs:attribute ref="xml:base"/>
140
+ <xs:attribute ref="xml:lang"/>
141
+ <xs:attribute ref="xml:space"/>
142
+ <xs:attribute ref="xml:id"/>
143
+ </xs:attributeGroup>
144
+
145
+ </xs:schema>
@@ -0,0 +1,54 @@
1
+ module XMLSchemaMetamodel
2
+
3
+ module Element::ClassModule
4
+ def effectiveElement
5
+ ref || self
6
+ end
7
+ def effectiveType
8
+ e = effectiveElement
9
+ if e.getType && e.complexType
10
+ puts "WARN: element has both, a type reference and a contained type"
11
+ end
12
+ e.getType || e.complexType || e.simpleType
13
+ end
14
+ end
15
+
16
+ module Attribute::ClassModule
17
+ def effectiveAttribute
18
+ ref || self
19
+ end
20
+ def effectiveType
21
+ e = effectiveAttribute
22
+ if e.getType && e.simpleType
23
+ puts "WARN: attribute has both, a type reference and a contained type"
24
+ end
25
+ e.getType || e.simpleType
26
+ end
27
+ end
28
+
29
+ module ComplexType::ClassModule
30
+ def allAttributes
31
+ attribute + attributeGroup.allAttributes +
32
+ (complexContent.andand.extension.andand.allAttributes || []) +
33
+ (simpleContent.andand.extension.andand.allAttributes || [])
34
+ end
35
+ end
36
+
37
+ module ExtensionType::ClassModule
38
+ def allAttributes
39
+ attribute + attributeGroup.allAttributes
40
+ end
41
+ end
42
+
43
+ module AttributeGroup::ClassModule
44
+ def effectiveAttributeGroup
45
+ ref || self
46
+ end
47
+ def allAttributes
48
+ effectiveAttributeGroup.attribute
49
+ end
50
+ end
51
+
52
+ end
53
+
54
+