mondrian-olap 0.4.0-java
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +2 -0
- data/Changelog.md +60 -0
- data/Gemfile +21 -0
- data/LICENSE-Mondrian.html +259 -0
- data/LICENSE.txt +22 -0
- data/README.md +302 -0
- data/RUNNING_TESTS.rdoc +66 -0
- data/Rakefile +48 -0
- data/VERSION +1 -0
- data/lib/mondrian-olap.rb +1 -0
- data/lib/mondrian/jars/commons-collections-3.1.jar +0 -0
- data/lib/mondrian/jars/commons-dbcp-1.2.1.jar +0 -0
- data/lib/mondrian/jars/commons-logging-1.0.4.jar +0 -0
- data/lib/mondrian/jars/commons-math-1.0.jar +0 -0
- data/lib/mondrian/jars/commons-pool-1.2.jar +0 -0
- data/lib/mondrian/jars/commons-vfs-1.0.jar +0 -0
- data/lib/mondrian/jars/eigenbase-properties.jar +0 -0
- data/lib/mondrian/jars/eigenbase-resgen.jar +0 -0
- data/lib/mondrian/jars/eigenbase-xom.jar +0 -0
- data/lib/mondrian/jars/javacup.jar +0 -0
- data/lib/mondrian/jars/log4j-1.2.8.jar +0 -0
- data/lib/mondrian/jars/log4j.properties +5 -0
- data/lib/mondrian/jars/mondrian.jar +0 -0
- data/lib/mondrian/jars/olap4j.jar +0 -0
- data/lib/mondrian/olap.rb +17 -0
- data/lib/mondrian/olap/connection.rb +201 -0
- data/lib/mondrian/olap/cube.rb +297 -0
- data/lib/mondrian/olap/error.rb +57 -0
- data/lib/mondrian/olap/query.rb +342 -0
- data/lib/mondrian/olap/result.rb +264 -0
- data/lib/mondrian/olap/schema.rb +378 -0
- data/lib/mondrian/olap/schema_element.rb +153 -0
- data/lib/mondrian/olap/schema_udf.rb +282 -0
- data/mondrian-olap.gemspec +128 -0
- data/spec/connection_role_spec.rb +130 -0
- data/spec/connection_spec.rb +72 -0
- data/spec/cube_spec.rb +318 -0
- data/spec/fixtures/MondrianTest.xml +134 -0
- data/spec/fixtures/MondrianTestOracle.xml +134 -0
- data/spec/mondrian_spec.rb +53 -0
- data/spec/query_spec.rb +807 -0
- data/spec/rake_tasks.rb +260 -0
- data/spec/schema_definition_spec.rb +1249 -0
- data/spec/spec_helper.rb +134 -0
- data/spec/support/matchers/be_like.rb +24 -0
- metadata +278 -0
data/RUNNING_TESTS.rdoc
ADDED
@@ -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.
|
data/Rakefile
ADDED
@@ -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'
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -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
|