Module: Jinx::Introspector

Includes:
Propertied
Included in:
Metadata
Defined in:
lib/jinx/metadata/introspector.rb

Overview

Meta-data mix-in to infer attribute meta-data from Java properties.

Instance Attribute Summary

Attributes included from Propertied

#attributes, #defaults

Instance Method Summary collapse

Methods included from Propertied

#add_alternate_key_attribute, #add_attribute, #add_attribute_aliases, #add_attribute_default, #add_attribute_defaults, #add_mandatory_attribute, #add_mandatory_attributes, #add_primary_key_attribute, #add_property, #add_restriction, #add_secondary_key_attribute, #alias_attribute, #alias_standard_attribute_hash, #all_key_attributes, #alternate_key_attributes, #append_ancestor_enum, #attribute_filter, #collect_mandatory_attributes, #collection_attribute?, #create_nonjava_property, #default_mandatory_local_attributes, #dependent_attributes, #detect_attribute_with_type, #domain_attribute?, #domain_attributes, #each_property, #independent_attributes, #init_property_classifiers, #java_attributes, #mandatory_attributes, #mandatory_owner_attribute, #most_specific_domain_attribute, #nondomain_attribute?, #nondomain_attributes, #nondomain_java_attributes, #nonowner_attributes, #offset_attribute, #primary_key_attributes, #property, #property_defined?, #property_hash, #property_path, #qualify_attribute, #register_property_alias, #remove_attribute, #secondary_key_attributes, #secondary_key_non_owner_domain_attributes, #set_alternate_key_attributes, #set_attribute_type, #set_primary_key_attributes, #set_secondary_key_attributes, #standard_attribute, #unidirectional_dependent_attributes

Instance Method Details

#add_attribute_value_initializerObject

Adds an optional attribute=>value constructor parameter to this class.



18
19
20
21
22
23
24
25
26
27
# File 'lib/jinx/metadata/introspector.rb', line 18

def add_attribute_value_initializer
  class << self
    def new(params=nil)
      obj = super()
      obj.merge_attributes(params) if params
      obj
    end
  end
  logger.debug { "#{self} is extended with an optional {attribute=>value} constructor parameter." }
end

#add_java_property(pd) ⇒ Property (private)

Makes a standard attribute for the given property descriptor. Adds a camelized Java-like alias to the standard attribute.

Parameters:

  • the (Java::PropertyDescriptor)

    introspected property descriptor

Returns:



153
154
155
156
157
158
159
160
161
162
163
# File 'lib/jinx/metadata/introspector.rb', line 153

def add_java_property(pd)
  # make the attribute metadata
  prop = create_java_property(pd)
  add_property(prop)
  # the property name is an alias for the standard attribute
  pa = prop.attribute
  # the Java property name as an attribute symbol
  ja = pd.name.to_sym
  delegate_to_property(ja, prop) unless prop.reader == ja
  prop
end

#alias_property_accessors(property) ⇒ Object (private)

Aliases the given Ruby property reader and writer to its underlying Java property reader and writer, resp.

Parameters:

  • property (Property)

    the property to alias



140
141
142
143
144
145
146
# File 'lib/jinx/metadata/introspector.rb', line 140

def alias_property_accessors(property)
  # the Java reader and writer accessor method symbols
  jra, jwa = property.java_accessors
  # strip the Java reader and writer is/get/set prefix and make a symbol
  alias_method(property.reader, jra)
  alias_method(property.writer, jwa)
end

#create_java_property(pd) ⇒ Property (private)

Returns the new property.

Parameters:

  • the (Java::PropertyDescriptor)

    introspected property descriptor

Returns:



167
168
169
# File 'lib/jinx/metadata/introspector.rb', line 167

def create_java_property(pd)
  JavaProperty.new(pd, self)
end

#define_java_property(pd) ⇒ Object (private)

Defines the Java property attribute and standard attribute methods, e.g. study_protocol and studyProtocol. A boolean attribute is provisioned with an additional reader alias, e.g. available? for is_available.

A standard attribute which differs from the property attribute delegates to the property attribute, e.g. study_protocol delegates to studyProtocol rather than aliasing setStudyProtocol. Redefining these methods results in a call to the redefined method. This contrasts with a Ruby alias, where each attribute alias is bound to the respective attribute reader or writer.

Parameters:

  • the (Java::PropertyDescriptor)

    introspected property descriptor



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/jinx/metadata/introspector.rb', line 75

def define_java_property(pd)
  if transient?(pd) then
    logger.debug { "Ignoring #{name.demodulize} transient attribute #{pd.name}." }
    return
  end
  # the standard underscore lower-case attributes
  prop = add_java_property(pd)
  # delegate the standard attribute accessors to the attribute accessors
  alias_property_accessors(prop)
  # add special wrappers
  wrap_java_property(prop)
  # create Ruby alias for boolean, e.g. alias :empty? for :empty
  if pd.property_type.name[/\w+$/].downcase == 'boolean' then
    # Strip the leading is_, if any, before appending a question mark.
    aliaz = prop.to_s[/^(is_)?(\w+)/, 2] << '?'
    delegate_to_property(aliaz, prop)
  end
end

#delegate_to_property(aliaz, property) ⇒ Object (private)

Defines methods aliaz and aliaz= which calls the standard attribute and attribute= accessor methods, resp. Calling rather than aliasing the attribute accessor allows the aliaz accessor to reflect a change to the attribute accessor.



175
176
177
178
179
180
181
# File 'lib/jinx/metadata/introspector.rb', line 175

def delegate_to_property(aliaz, property)
  ra, wa = property.accessors
  if aliaz == ra then raise MetadataError.new("Cannot delegate #{self} #{aliaz} to itself.") end
  define_method(aliaz) { send(ra) }
  define_method("#{aliaz}=".to_sym) { |value| send(wa, value) }
  register_property_alias(aliaz, property.attribute)
end

#introspectObject

Defines the Java attribute access methods, e.g. study_protocol and studyProtocol. A boolean attribute is provisioned with an additional reader alias, e.g. available? for is_available.

Each Java property attribute delegates to the Java attribute getter and setter. Each standard attribute delegates to the Java property attribute. Redefining these methods results in a call to the redefined method. This contrasts with a Ruby alias, where the alias remains bound to the original method body.



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/jinx/metadata/introspector.rb', line 38

def introspect
  # Set up the attribute data structures; delegates to Propertied.
  init_property_classifiers
  logger.debug { "Introspecting #{qp} metadata..." }
  # check for method conflicts
  conflicts = instance_methods(false) & Resource.instance_methods(false)
  unless conflicts.empty? then
    logger.warn("#{self} methods conflict with #{Resource} methods: #{conflicts.qp}")
  end
  # If this is a Java class rather than interface, then define the Java property
  # attributes.
  if Class === self then
    # the Java attributes defined by this class with both a read and a write method
    pds = property_descriptors(false)
    # Define the standard Java attribute methods.
    pds.each { |pd| define_java_property(pd) }
  end
  # Mark this class as introspected.
  @introspected = true
  logger.debug { "Introspection of #{qp} metadata complete." }
  self
end

#introspected?Boolean

Returns whether this class has been introspected.

Returns:

  • (Boolean)

    whether this class has been introspected



13
14
15
# File 'lib/jinx/metadata/introspector.rb', line 13

def introspected?
  !!@introspected
end

#wrap_java_date_property(property) ⇒ Object (private)

Adds a Java-Ruby Date filter to the given Date property Ruby access methods. The reader returns a Ruby date. The writer sets a Java date.



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/jinx/metadata/introspector.rb', line 118

def wrap_java_date_property(property)
  ra, wa = property.accessors
  jra, jwa = property.java_accessors
  
  # filter the attribute reader
  define_method(ra) do
    value = send(jra)
    Java::JavaUtil::Date === value ? value.to_ruby_date : value
  end
  
  # filter the attribute writer
  define_method(wa) do |value|
    value = Java::JavaUtil::Date.from_ruby_date(value) if ::Date === value
    send(jwa, value)
  end

  logger.debug { "Filtered #{qp} #{ra} and #{wa} methods with Java Date <-> Ruby Date converter." }
end

#wrap_java_property(property) ⇒ Object (private)

Adds a filter to the given property access methods if it is a String or Date.



95
96
97
98
99
100
101
102
# File 'lib/jinx/metadata/introspector.rb', line 95

def wrap_java_property(property)
  pd = property.property_descriptor
  if pd.property_type == Java::JavaLang::String.java_class then
    wrap_java_string_property(property)
  elsif pd.property_type == Java::JavaUtil::Date.java_class then
    wrap_java_date_property(property)
  end
end

#wrap_java_string_property(property) ⇒ Object (private)

Adds a number -> string filter to the given String property Ruby access methods.



105
106
107
108
109
110
111
112
113
114
# File 'lib/jinx/metadata/introspector.rb', line 105

def wrap_java_string_property(property)
  ra, wa = property.accessors
  jra, jwa = property.java_accessors
  # filter the attribute writer
  define_method(wa) do |value|
    stdval = Math.numeric?(value) ? value.to_s : value
    send(jwa, stdval)
  end
  logger.debug { "Filtered #{qp} #{wa} method with non-String -> String converter." }
end