mondrian-olap 0.4.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.rspec +2 -0
  2. data/Changelog.md +60 -0
  3. data/Gemfile +21 -0
  4. data/LICENSE-Mondrian.html +259 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +302 -0
  7. data/RUNNING_TESTS.rdoc +66 -0
  8. data/Rakefile +48 -0
  9. data/VERSION +1 -0
  10. data/lib/mondrian-olap.rb +1 -0
  11. data/lib/mondrian/jars/commons-collections-3.1.jar +0 -0
  12. data/lib/mondrian/jars/commons-dbcp-1.2.1.jar +0 -0
  13. data/lib/mondrian/jars/commons-logging-1.0.4.jar +0 -0
  14. data/lib/mondrian/jars/commons-math-1.0.jar +0 -0
  15. data/lib/mondrian/jars/commons-pool-1.2.jar +0 -0
  16. data/lib/mondrian/jars/commons-vfs-1.0.jar +0 -0
  17. data/lib/mondrian/jars/eigenbase-properties.jar +0 -0
  18. data/lib/mondrian/jars/eigenbase-resgen.jar +0 -0
  19. data/lib/mondrian/jars/eigenbase-xom.jar +0 -0
  20. data/lib/mondrian/jars/javacup.jar +0 -0
  21. data/lib/mondrian/jars/log4j-1.2.8.jar +0 -0
  22. data/lib/mondrian/jars/log4j.properties +5 -0
  23. data/lib/mondrian/jars/mondrian.jar +0 -0
  24. data/lib/mondrian/jars/olap4j.jar +0 -0
  25. data/lib/mondrian/olap.rb +17 -0
  26. data/lib/mondrian/olap/connection.rb +201 -0
  27. data/lib/mondrian/olap/cube.rb +297 -0
  28. data/lib/mondrian/olap/error.rb +57 -0
  29. data/lib/mondrian/olap/query.rb +342 -0
  30. data/lib/mondrian/olap/result.rb +264 -0
  31. data/lib/mondrian/olap/schema.rb +378 -0
  32. data/lib/mondrian/olap/schema_element.rb +153 -0
  33. data/lib/mondrian/olap/schema_udf.rb +282 -0
  34. data/mondrian-olap.gemspec +128 -0
  35. data/spec/connection_role_spec.rb +130 -0
  36. data/spec/connection_spec.rb +72 -0
  37. data/spec/cube_spec.rb +318 -0
  38. data/spec/fixtures/MondrianTest.xml +134 -0
  39. data/spec/fixtures/MondrianTestOracle.xml +134 -0
  40. data/spec/mondrian_spec.rb +53 -0
  41. data/spec/query_spec.rb +807 -0
  42. data/spec/rake_tasks.rb +260 -0
  43. data/spec/schema_definition_spec.rb +1249 -0
  44. data/spec/spec_helper.rb +134 -0
  45. data/spec/support/matchers/be_like.rb +24 -0
  46. metadata +278 -0
@@ -0,0 +1,66 @@
1
+ == Creating test database
2
+
3
+ By default unit tests use MySQL database but PostgreSQL, Oracle and SQL Server databases are supported as well. Set MONDRIAN_DRIVER environment variable to "mysql" (default), "postgresql", "oracle", "luciddb", "mssql" (jTDS) or "sqlserver" (Microsoft JDBC) to specify database driver that should be used.
4
+
5
+ If using MySQL, PostgreSQL or SQL Server database then create database user mondrian_test with password mondrian_test, create database mondrian_test and grant full access to this database for mondrian_test user. By default it is assumed that database is located on localhost (can be overridden with DATABASE_HOST environment variable).
6
+
7
+ If using Oracle database then create database user mondrian_test with password mondrian_test. By default it is assumed that database orcl is located on localhost (can be overridden with DATABASE_NAME and DATABASE_HOST environment variables).
8
+
9
+ If using LucidDB database then create schema MONDRIAN_TEST and create user MONDRIAN_TEST with password mondrian_test and with default schema MONDRIAN_TEST. By default it is assumed that database is located on localhost (can be overridden with DATABASE_HOST environment variable).
10
+
11
+ See spec/spec_helper.rb for details of default connection parameters and how to override them.
12
+
13
+ == Creating test data
14
+
15
+ Install necessary gems with
16
+
17
+ bundle install
18
+
19
+ Create tables with test data using
20
+
21
+ rake db:create_data
22
+
23
+ or specify which database driver to use
24
+
25
+ rake db:create_data MONDRIAN_DRIVER=mysql
26
+ rake db:create_data MONDRIAN_DRIVER=postgresql
27
+ rake db:create_data MONDRIAN_DRIVER=oracle
28
+ rake db:create_data MONDRIAN_DRIVER=mssql
29
+ rake db:create_data MONDRIAN_DRIVER=sqlserver
30
+
31
+ In case of LucidDB data are not generated and inserted directly into database but are imported from MySQL mondrian_test database (because inserting individual records into LucidDB is very inefficient). Therefore at first generate test data with mysql (using default database settings) and then run data creation task for LucidDB.
32
+
33
+ rake db:create_data MONDRIAN_DRIVER=mysql
34
+ rake db:create_data MONDRIAN_DRIVER=luciddb
35
+
36
+ == Running tests
37
+
38
+ Run tests with
39
+
40
+ rake spec
41
+
42
+ or specify which database driver to use
43
+
44
+ rake spec MONDRIAN_DRIVER=mysql
45
+ rake spec MONDRIAN_DRIVER=postgresql
46
+ rake spec MONDRIAN_DRIVER=oracle
47
+ rake spec MONDRIAN_DRIVER=luciddb
48
+ rake spec MONDRIAN_DRIVER=mssql
49
+ rake spec MONDRIAN_DRIVER=sqlserver
50
+
51
+ or also alternatively with
52
+
53
+ rake spec:mysql
54
+ rake spec:postgresql
55
+ rake spec:oracle
56
+ rake spec:luciddb
57
+ rake spec:mssql
58
+ rake spec:sqlserver
59
+
60
+ You can also run all tests on all databases with
61
+
62
+ rake spec:all
63
+
64
+ == JRuby versions
65
+
66
+ It is recommended to use RVM (http://rvm.beginrescueend.com) to run tests with different JRuby implementations. mondrian-olap is being tested with latest versions of JRuby 1.6 and 1.7 on Java 6 and 7.
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+
11
+ require 'rake'
12
+
13
+ require 'jeweler'
14
+ Jeweler::Tasks.new do |gem|
15
+ gem.name = "mondrian-olap"
16
+ gem.summary = "JRuby API for Mondrian OLAP Java library"
17
+ gem.description = <<-EOS
18
+ JRuby gem for performing multidimensional queries of relational database data using Mondrian OLAP Java library
19
+ EOS
20
+ gem.email = "raimonds.simanovskis@gmail.com"
21
+ gem.homepage = "http://github.com/rsim/mondrian-olap"
22
+ gem.authors = ["Raimonds Simanovskis"]
23
+ gem.platform = "java"
24
+ gem.extra_rdoc_files = ['README.md']
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core/rake_task'
29
+ RSpec::Core::RakeTask.new(:spec)
30
+
31
+ RSpec::Core::RakeTask.new(:rcov) do |t|
32
+ t.rcov = true
33
+ t.rcov_opts = ['--exclude', '/Library,spec/']
34
+ end
35
+
36
+ task :default => :spec
37
+
38
+ require 'rdoc/task'
39
+ RDoc::Task.new do |rdoc|
40
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
41
+
42
+ rdoc.rdoc_dir = 'doc'
43
+ rdoc.title = "mondrian-olap #{version}"
44
+ rdoc.rdoc_files.include('README*')
45
+ rdoc.rdoc_files.include('lib/**/*.rb')
46
+ end
47
+
48
+ require 'spec/rake_tasks'
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.0
@@ -0,0 +1 @@
1
+ require 'mondrian/olap'
@@ -0,0 +1,5 @@
1
+ # Logs errors on the console
2
+ #
3
+ log4j.rootLogger = ERROR, A1
4
+ log4j.appender.A1 = org.apache.log4j.ConsoleAppender
5
+ log4j.appender.A1.layout=org.apache.log4j.PatternLayout
@@ -0,0 +1,17 @@
1
+ require 'java'
2
+
3
+ directory = File.expand_path("../jars", __FILE__)
4
+ Dir["#{directory}/*.jar"].each do |file|
5
+ require file
6
+ end
7
+
8
+ unless java.lang.System.getProperty("log4j.configuration")
9
+ file_uri = java.io.File.new("#{directory}/log4j.properties").toURI.to_s
10
+ java.lang.System.setProperty("log4j.configuration", file_uri)
11
+ end
12
+ # register Mondrian olap4j driver
13
+ Java::mondrian.olap4j.MondrianOlap4jDriver
14
+
15
+ %w(error connection query result schema schema_udf cube).each do |file|
16
+ require "mondrian/olap/#{file}"
17
+ end
@@ -0,0 +1,201 @@
1
+ module Mondrian
2
+ module OLAP
3
+ class Connection
4
+ def self.create(params)
5
+ connection = new(params)
6
+ connection.connect
7
+ connection
8
+ end
9
+
10
+ attr_reader :raw_connection, :raw_catalog, :raw_schema
11
+
12
+ def initialize(params={})
13
+ @params = params
14
+ @driver = params[:driver]
15
+ @connected = false
16
+ @raw_connection = nil
17
+ end
18
+
19
+ def connect
20
+ Error.wrap_native_exception do
21
+ # hack to call private constructor of MondrianOlap4jDriver
22
+ # to avoid using DriverManager which fails to load JDBC drivers
23
+ # because of not seeing JRuby required jar files
24
+ cons = Java::MondrianOlap4j::MondrianOlap4jDriver.java_class.declared_constructor
25
+ cons.accessible = true
26
+ driver = cons.new_instance.to_java
27
+
28
+ props = java.util.Properties.new
29
+ props.setProperty('JdbcUser', @params[:username]) if @params[:username]
30
+ props.setProperty('JdbcPassword', @params[:password]) if @params[:password]
31
+
32
+ conn_string = connection_string
33
+
34
+ # TODO: removed workaround for Mondrian ServiceDiscovery
35
+ # need to check if database dialects are always loaded by ServiceDiscovery detected class loader
36
+ @raw_jdbc_connection = driver.connect(conn_string, props)
37
+
38
+ @raw_connection = @raw_jdbc_connection.unwrap(Java::OrgOlap4j::OlapConnection.java_class)
39
+ @raw_catalog = @raw_connection.getOlapCatalog
40
+ # currently it is assumed that there is just one schema per connection catalog
41
+ @raw_schema = @raw_catalog.getSchemas.first
42
+ @connected = true
43
+ true
44
+ end
45
+ end
46
+
47
+ def connected?
48
+ @connected
49
+ end
50
+
51
+ def close
52
+ @raw_connection.close
53
+ @connected = false
54
+ @raw_connection = @raw_jdbc_connection = nil
55
+ true
56
+ end
57
+
58
+ def execute(query_string)
59
+ Error.wrap_native_exception do
60
+ statement = @raw_connection.prepareOlapStatement(query_string)
61
+ Result.new(self, statement.executeQuery())
62
+ end
63
+ end
64
+
65
+ def from(cube_name)
66
+ Query.from(self, cube_name)
67
+ end
68
+
69
+ def cube_names
70
+ @raw_schema.getCubes.map{|c| c.getName}
71
+ end
72
+
73
+ def cube(name)
74
+ Cube.get(self, name)
75
+ end
76
+
77
+ # Will affect only the next created connection. If it is necessary to clear all schema cache then
78
+ # flush_schema_cache should be called, then close and then new connection should be created.
79
+ def flush_schema_cache
80
+ unwrapped_connection = @raw_connection.unwrap(Java::MondrianOlap::Connection.java_class)
81
+ raw_cache_control = unwrapped_connection.getCacheControl(nil)
82
+ raw_cache_control.flushSchemaCache
83
+ end
84
+
85
+ def available_role_names
86
+ @raw_connection.getAvailableRoleNames.to_a
87
+ end
88
+
89
+ def role_name
90
+ @raw_connection.getRoleName
91
+ end
92
+
93
+ def role_names
94
+ @raw_connection.getRoleNames.to_a
95
+ end
96
+
97
+ def role_name=(name)
98
+ Error.wrap_native_exception do
99
+ @raw_connection.setRoleName(name)
100
+ end
101
+ end
102
+
103
+ def role_names=(names)
104
+ Error.wrap_native_exception do
105
+ @raw_connection.setRoleNames(Array(names))
106
+ end
107
+ end
108
+
109
+ private
110
+
111
+ def connection_string
112
+ string = "jdbc:mondrian:Jdbc=#{quote_string(jdbc_uri)};JdbcDrivers=#{jdbc_driver};"
113
+ # by default use content checksum to reload schema when catalog has changed
114
+ string << "UseContentChecksum=true;" unless @params[:use_content_checksum] == false
115
+ if role = @params[:role] || @params[:roles]
116
+ roles = Array(role).map{|r| r && r.to_s.gsub(',', ',,')}.compact
117
+ string << "Role=#{quote_string(roles.join(','))};" unless roles.empty?
118
+ end
119
+ string << (@params[:catalog] ? "Catalog=#{catalog_uri}" : "CatalogContent=#{quote_string(catalog_content)}")
120
+ end
121
+
122
+ def jdbc_uri
123
+ case @driver
124
+ when 'mysql', 'postgresql'
125
+ uri = "jdbc:#{@driver}://#{@params[:host]}#{@params[:port] && ":#{@params[:port]}"}/#{@params[:database]}"
126
+ uri << "?useUnicode=yes&characterEncoding=UTF-8" if @driver == 'mysql'
127
+ uri
128
+ when 'oracle'
129
+ # connection using TNS alias
130
+ if @params[:database] && !@params[:host] && !@params[:url] && ENV['TNS_ADMIN']
131
+ "jdbc:oracle:thin:@#{@params[:database]}"
132
+ else
133
+ @params[:url] ||
134
+ "jdbc:oracle:thin:@#{@params[:host] || 'localhost'}:#{@params[:port] || 1521}:#{@params[:database]}"
135
+ end
136
+ when 'luciddb'
137
+ uri = "jdbc:luciddb:http://#{@params[:host]}#{@params[:port] && ":#{@params[:port]}"}"
138
+ uri << ";schema=#{@params[:database_schema]}" if @params[:database_schema]
139
+ uri
140
+ when 'mssql'
141
+ uri = "jdbc:jtds:sqlserver://#{@params[:host]}#{@params[:port] && ":#{@params[:port]}"}/#{@params[:database]}"
142
+ uri << ";instance=#{@params[:instance]}" if @params[:instance]
143
+ uri << ";domain=#{@params[:domain]}" if @params[:domain]
144
+ uri << ";appname=#{@params[:appname]}" if @params[:appname]
145
+ uri
146
+ when 'sqlserver'
147
+ uri = "jdbc:sqlserver://#{@params[:host]}#{@params[:port] && ":#{@params[:port]}"}"
148
+ uri << ";databaseName=#{@params[:database]}" if @params[:database]
149
+ uri << ";integratedSecurity=#{@params[:integrated_security]}" if @params[:integrated_security]
150
+ uri << ";applicationName=#{@params[:application_name]}" if @params[:application_name]
151
+ uri << ";instanceName=#{@params[:instance_name]}" if @params[:instance_name]
152
+ uri
153
+ else
154
+ raise ArgumentError, 'unknown JDBC driver'
155
+ end
156
+ end
157
+
158
+ def jdbc_driver
159
+ case @driver
160
+ when 'mysql'
161
+ 'com.mysql.jdbc.Driver'
162
+ when 'postgresql'
163
+ 'org.postgresql.Driver'
164
+ when 'oracle'
165
+ 'oracle.jdbc.OracleDriver'
166
+ when 'luciddb'
167
+ 'org.luciddb.jdbc.LucidDbClientDriver'
168
+ when 'mssql'
169
+ 'net.sourceforge.jtds.jdbc.Driver'
170
+ when 'sqlserver'
171
+ 'com.microsoft.sqlserver.jdbc.SQLServerDriver'
172
+ else
173
+ raise ArgumentError, 'unknown JDBC driver'
174
+ end
175
+ end
176
+
177
+ def catalog_uri
178
+ if @params[:catalog]
179
+ "file://#{File.expand_path(@params[:catalog])}"
180
+ else
181
+ raise ArgumentError, 'missing catalog source'
182
+ end
183
+ end
184
+
185
+ def catalog_content
186
+ if @params[:catalog_content]
187
+ @params[:catalog_content]
188
+ elsif @params[:schema]
189
+ @params[:schema].to_xml(:driver => @driver)
190
+ else
191
+ raise ArgumentError, "Specify catalog with :catalog, :catalog_content or :schema option"
192
+ end
193
+ end
194
+
195
+ def quote_string(string)
196
+ "'#{string.gsub("'","''")}'"
197
+ end
198
+
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,297 @@
1
+ module Mondrian
2
+ module OLAP
3
+ class Cube
4
+ def self.get(connection, name)
5
+ if raw_cube = connection.raw_schema.getCubes.get(name)
6
+ Cube.new(connection, raw_cube)
7
+ end
8
+ end
9
+
10
+ def initialize(connection, raw_cube)
11
+ @connection = connection
12
+ @raw_cube = raw_cube
13
+ end
14
+
15
+ def name
16
+ @name ||= @raw_cube.getName
17
+ end
18
+
19
+ def description
20
+ @description ||= @raw_cube.getDescription
21
+ end
22
+
23
+ def dimensions
24
+ @dimenstions ||= @raw_cube.getDimensions.map{|d| Dimension.new(self, d)}
25
+ end
26
+
27
+ def dimension_names
28
+ dimensions.map{|d| d.name}
29
+ end
30
+
31
+ def dimension(name)
32
+ dimensions.detect{|d| d.name == name}
33
+ end
34
+
35
+ def query
36
+ Query.from(@connection, name)
37
+ end
38
+
39
+ def member(full_name)
40
+ segment_list = Java::OrgOlap4jMdx::IdentifierNode.parseIdentifier(full_name).getSegmentList
41
+ raw_member = @raw_cube.lookupMember(segment_list)
42
+ raw_member && Member.new(raw_member)
43
+ end
44
+
45
+ def member_by_segments(*segment_names)
46
+ segment_list = Java::OrgOlap4jMdx::IdentifierNode.ofNames(*segment_names).getSegmentList
47
+ raw_member = @raw_cube.lookupMember(segment_list)
48
+ raw_member && Member.new(raw_member)
49
+ end
50
+ end
51
+
52
+ class Dimension
53
+ def initialize(cube, raw_dimension)
54
+ @cube = cube
55
+ @raw_dimension = raw_dimension
56
+ end
57
+
58
+ attr_reader :cube
59
+
60
+ def name
61
+ @name ||= @raw_dimension.getName
62
+ end
63
+
64
+ def description
65
+ @description ||= @raw_dimension.getDescription
66
+ end
67
+
68
+ def full_name
69
+ @full_name ||= @raw_dimension.getUniqueName
70
+ end
71
+
72
+ def hierarchies
73
+ @hierarchies ||= @raw_dimension.getHierarchies.map{|h| Hierarchy.new(self, h)}
74
+ end
75
+
76
+ def hierarchy_names
77
+ hierarchies.map{|h| h.name}
78
+ end
79
+
80
+ def hierarchy(name = nil)
81
+ name ||= self.name
82
+ hierarchies.detect{|h| h.name == name}
83
+ end
84
+
85
+ def measures?
86
+ @raw_dimension.getDimensionType == Java::OrgOlap4jMetadata::Dimension::Type::MEASURE
87
+ end
88
+
89
+ def dimension_type
90
+ case @raw_dimension.getDimensionType
91
+ when Java::OrgOlap4jMetadata::Dimension::Type::TIME
92
+ :time
93
+ when Java::OrgOlap4jMetadata::Dimension::Type::MEASURE
94
+ :measures
95
+ else
96
+ :standard
97
+ end
98
+ end
99
+ end
100
+
101
+ class Hierarchy
102
+ def initialize(dimension, raw_hierarchy)
103
+ @dimension = dimension
104
+ @raw_hierarchy = raw_hierarchy
105
+ end
106
+
107
+ def name
108
+ @name ||= @raw_hierarchy.getName
109
+ end
110
+
111
+ def description
112
+ @description ||= @raw_hierarchy.getDescription
113
+ end
114
+
115
+ def levels
116
+ @levels = @raw_hierarchy.getLevels.map{|l| Level.new(self, l)}
117
+ end
118
+
119
+ def level(name)
120
+ levels.detect{|l| l.name == name}
121
+ end
122
+
123
+ def level_names
124
+ levels.map{|l| l.name}
125
+ end
126
+
127
+ def has_all?
128
+ @raw_hierarchy.hasAll
129
+ end
130
+
131
+ def all_member_name
132
+ has_all? ? @raw_hierarchy.getRootMembers.first.getName : nil
133
+ end
134
+
135
+ def all_member
136
+ has_all? ? Member.new(@raw_hierarchy.getRootMembers.first) : nil
137
+ end
138
+
139
+ def root_members
140
+ @raw_hierarchy.getRootMembers.map{|m| Member.new(m)}
141
+ end
142
+
143
+ def root_member_names
144
+ @raw_hierarchy.getRootMembers.map{|m| m.getName}
145
+ end
146
+
147
+ def root_member_full_names
148
+ @raw_hierarchy.getRootMembers.map{|m| m.getUniqueName}
149
+ end
150
+
151
+ def child_names(*parent_member_segment_names)
152
+ Error.wrap_native_exception do
153
+ parent_member = if parent_member_segment_names.empty?
154
+ return root_member_names unless has_all?
155
+ all_member
156
+ else
157
+ @dimension.cube.member_by_segments(*parent_member_segment_names)
158
+ end
159
+ parent_member && parent_member.children.map{|m| m.name}
160
+ end
161
+ end
162
+ end
163
+
164
+ class Level
165
+ def initialize(hierarchy, raw_level)
166
+ @hierarchy = hierarchy
167
+ @raw_level = raw_level
168
+ end
169
+
170
+ def name
171
+ @name ||= @raw_level.getName
172
+ end
173
+
174
+ def description
175
+ @description ||= @raw_level.getDescription
176
+ end
177
+
178
+ def depth
179
+ @raw_level.getDepth
180
+ end
181
+
182
+ def cardinality
183
+ @cardinality = @raw_level.getCardinality
184
+ end
185
+
186
+ def members_count
187
+ @members_count ||= begin
188
+ if cardinality >= 0
189
+ cardinality
190
+ else
191
+ Error.wrap_native_exception do
192
+ @raw_level.getMembers.size
193
+ end
194
+ end
195
+ end
196
+ end
197
+
198
+ def members
199
+ Error.wrap_native_exception do
200
+ @raw_level.getMembers.map{|m| Member.new(m)}
201
+ end
202
+ end
203
+ end
204
+
205
+ class Member
206
+ def initialize(raw_member)
207
+ @raw_member = raw_member
208
+ end
209
+
210
+ def name
211
+ @raw_member.getName
212
+ end
213
+
214
+ def full_name
215
+ @raw_member.getUniqueName
216
+ end
217
+
218
+ def caption
219
+ @raw_member.getCaption
220
+ end
221
+
222
+ def calculated?
223
+ @raw_member.isCalculated
224
+ end
225
+
226
+ def visible?
227
+ @raw_member.isVisible
228
+ end
229
+
230
+ def all_member?
231
+ @raw_member.isAll
232
+ end
233
+
234
+ def drillable?
235
+ return false if calculated?
236
+ # @raw_member.getChildMemberCount > 0
237
+ # This hopefully is faster than counting actual child members
238
+ raw_level = @raw_member.getLevel
239
+ raw_levels = raw_level.getHierarchy.getLevels
240
+ raw_levels.indexOf(raw_level) < raw_levels.size - 1
241
+ end
242
+
243
+ def depth
244
+ @raw_member.getDepth
245
+ end
246
+
247
+ def dimension_type
248
+ case @raw_member.getDimension.getDimensionType
249
+ when Java::OrgOlap4jMetadata::Dimension::Type::TIME
250
+ :time
251
+ when Java::OrgOlap4jMetadata::Dimension::Type::MEASURE
252
+ :measures
253
+ else
254
+ :standard
255
+ end
256
+ end
257
+
258
+ def children
259
+ Error.wrap_native_exception do
260
+ @raw_member.getChildMembers.map{|m| Member.new(m)}
261
+ end
262
+ end
263
+
264
+ def descendants_at_level(level)
265
+ Error.wrap_native_exception do
266
+ raw_level = @raw_member.getLevel
267
+ raw_levels = raw_level.getHierarchy.getLevels
268
+ current_level_index = raw_levels.indexOf(raw_level)
269
+ descendants_level_index = raw_levels.indexOfName(level)
270
+
271
+ return nil unless descendants_level_index > current_level_index
272
+
273
+ members = [self]
274
+ (descendants_level_index - current_level_index).times do
275
+ members = members.map do |member|
276
+ member.children
277
+ end.flatten
278
+ end
279
+ members
280
+ end
281
+ end
282
+
283
+ def property_value(name)
284
+ if property = @raw_member.getProperties.get(name)
285
+ @raw_member.getPropertyValue(property)
286
+ end
287
+ end
288
+
289
+ def property_formatted_value(name)
290
+ if property = @raw_member.getProperties.get(name)
291
+ @raw_member.getPropertyFormattedValue(property)
292
+ end
293
+ end
294
+
295
+ end
296
+ end
297
+ end