001/* 002// This software is subject to the terms of the Eclipse Public License v1.0 003// Agreement, available at the following URL: 004// http://www.eclipse.org/legal/epl-v10.html. 005// You must accept the terms of that agreement to use this software. 006// 007// Copyright (C) 2003-2005 Julian Hyde 008// Copyright (C) 2005-2013 Pentaho 009// All Rights Reserved. 010*/ 011package mondrian.rolap; 012 013import mondrian.calc.*; 014import mondrian.calc.impl.AbstractCalc; 015import mondrian.calc.impl.GenericCalc; 016import mondrian.olap.*; 017import mondrian.olap.type.StringType; 018import mondrian.rolap.sql.MemberChildrenConstraint; 019import mondrian.rolap.sql.TupleConstraint; 020 021import org.apache.log4j.Logger; 022 023import org.eigenbase.util.property.Property; 024 025import org.olap4j.mdx.IdentifierSegment; 026 027import java.util.*; 028import java.util.concurrent.ConcurrentHashMap; 029import javax.sql.DataSource; 030 031/** 032 * A <code>RolapSchemaReader</code> allows you to read schema objects while 033 * observing the access-control profile specified by a given role. 034 * 035 * @author jhyde 036 * @since Feb 24, 2003 037 */ 038public class RolapSchemaReader 039 implements SchemaReader, 040 RolapNativeSet.SchemaReaderWithMemberReaderAvailable, 041 NameResolver.Namespace 042{ 043 protected final Role role; 044 private final Map<Hierarchy, MemberReader> hierarchyReaders = 045 new ConcurrentHashMap<Hierarchy, MemberReader>(); 046 protected final RolapSchema schema; 047 private final SqlConstraintFactory sqlConstraintFactory = 048 SqlConstraintFactory.instance(); 049 private static final Logger LOGGER = 050 Logger.getLogger(RolapSchemaReader.class); 051 052 /** 053 * Creates a RolapSchemaReader. 054 * 055 * @param role Role for access control, must not be null 056 * @param schema Schema 057 */ 058 RolapSchemaReader(Role role, RolapSchema schema) { 059 assert role != null : "precondition: role != null"; 060 assert schema != null; 061 this.role = role; 062 this.schema = schema; 063 } 064 065 public Role getRole() { 066 return role; 067 } 068 069 public List<Member> getHierarchyRootMembers(Hierarchy hierarchy) { 070 final Role.HierarchyAccess hierarchyAccess = 071 role.getAccessDetails(hierarchy); 072 final Level[] levels = hierarchy.getLevels(); 073 final Level firstLevel; 074 if (hierarchyAccess == null) { 075 firstLevel = levels[0]; 076 } else { 077 firstLevel = levels[hierarchyAccess.getTopLevelDepth()]; 078 } 079 return getLevelMembers(firstLevel, true); 080 } 081 082 /** 083 * This method uses a double-checked locking idiom to avoid making the 084 * method fully synchronized, or potentially creating the same MemberReader 085 * more than once. Double-checked locking can cause issues if 086 * a second thread accesses the field without either a shared lock in 087 * place or the field being specified as volatile. 088 * In this case, hierarchyReaders is a ConcurrentHashMap, 089 * which internally uses volatile load semantics for read operations. 090 * This assures values written by one thread will be visible when read by 091 * others. 092 * http://en.wikipedia.org/wiki/Double-checked_locking 093 */ 094 public MemberReader getMemberReader(Hierarchy hierarchy) { 095 MemberReader memberReader = hierarchyReaders.get(hierarchy); 096 if (memberReader == null) { 097 synchronized (this) { 098 memberReader = hierarchyReaders.get(hierarchy); 099 if (memberReader == null) { 100 memberReader = 101 ((RolapHierarchy) hierarchy).createMemberReader(role); 102 hierarchyReaders.put(hierarchy, memberReader); 103 } 104 } 105 } 106 return memberReader; 107 } 108 109 110 public Member substitute(Member member) { 111 final MemberReader memberReader = 112 getMemberReader(member.getHierarchy()); 113 return memberReader.substitute((RolapMember) member); 114 } 115 116 public void getMemberRange( 117 Level level, Member startMember, Member endMember, List<Member> list) 118 { 119 getMemberReader(level.getHierarchy()).getMemberRange( 120 (RolapLevel) level, (RolapMember) startMember, 121 (RolapMember) endMember, Util.<RolapMember>cast(list)); 122 } 123 124 public int compareMembersHierarchically(Member m1, Member m2) { 125 RolapMember member1 = (RolapMember) m1; 126 RolapMember member2 = (RolapMember) m2; 127 final RolapHierarchy hierarchy = member1.getHierarchy(); 128 Util.assertPrecondition(hierarchy == m2.getHierarchy()); 129 return getMemberReader(hierarchy).compare(member1, member2, true); 130 } 131 132 public Member getMemberParent(Member member) { 133 return getMemberReader(member.getHierarchy()).getMemberParent( 134 (RolapMember) member); 135 } 136 137 public int getMemberDepth(Member member) { 138 final Role.HierarchyAccess hierarchyAccess = 139 role.getAccessDetails(member.getHierarchy()); 140 if (hierarchyAccess != null) { 141 final int memberDepth = member.getLevel().getDepth(); 142 final int topLevelDepth = hierarchyAccess.getTopLevelDepth(); 143 return memberDepth - topLevelDepth; 144 } else if (((RolapLevel) member.getLevel()).isParentChild()) { 145 // For members of parent-child hierarchy, members in the same level 146 // may have different depths. 147 int depth = 0; 148 for (Member m = member.getParentMember(); 149 m != null; 150 m = m.getParentMember()) 151 { 152 depth++; 153 } 154 return depth; 155 } else { 156 return member.getLevel().getDepth(); 157 } 158 } 159 160 161 public List<Member> getMemberChildren(Member member) { 162 return getMemberChildren(member, null); 163 } 164 165 public List<Member> getMemberChildren(Member member, Evaluator context) { 166 MemberChildrenConstraint constraint = 167 sqlConstraintFactory.getMemberChildrenConstraint(context); 168 List<RolapMember> memberList = 169 internalGetMemberChildren(member, constraint); 170 return Util.cast(memberList); 171 } 172 173 /** 174 * Helper for getMemberChildren. 175 * 176 * @param member Member 177 * @param constraint Constraint 178 * @return List of children 179 */ 180 private List<RolapMember> internalGetMemberChildren( 181 Member member, MemberChildrenConstraint constraint) 182 { 183 List<RolapMember> children = new ArrayList<RolapMember>(); 184 final Hierarchy hierarchy = member.getHierarchy(); 185 final MemberReader memberReader = getMemberReader(hierarchy); 186 memberReader.getMemberChildren( 187 (RolapMember) member, children, constraint); 188 return children; 189 } 190 191 public void getParentChildContributingChildren( 192 Member dataMember, 193 Hierarchy hierarchy, 194 List<Member> list) 195 { 196 final List<RolapMember> rolapMemberList = Util.cast(list); 197 list.add(dataMember); 198 ((RolapHierarchy) hierarchy).getMemberReader().getMemberChildren( 199 (RolapMember) dataMember, rolapMemberList); 200 } 201 202 public int getChildrenCountFromCache(Member member) { 203 final Hierarchy hierarchy = member.getHierarchy(); 204 final MemberReader memberReader = getMemberReader(hierarchy); 205 if (memberReader instanceof 206 RolapCubeHierarchy.RolapCubeHierarchyMemberReader) 207 { 208 List list = 209 ((RolapCubeHierarchy.RolapCubeHierarchyMemberReader) 210 memberReader) 211 .getRolapCubeMemberCacheHelper() 212 .getChildrenFromCache((RolapMember) member, null); 213 if (list == null) { 214 return -1; 215 } 216 return list.size(); 217 } 218 219 if (memberReader instanceof SmartMemberReader) { 220 List list = ((SmartMemberReader) memberReader).getMemberCache() 221 .getChildrenFromCache((RolapMember) member, null); 222 if (list == null) { 223 return -1; 224 } 225 return list.size(); 226 } 227 if (!(memberReader instanceof MemberCache)) { 228 return -1; 229 } 230 List list = ((MemberCache) memberReader) 231 .getChildrenFromCache((RolapMember) member, null); 232 if (list == null) { 233 return -1; 234 } 235 return list.size(); 236 } 237 238 /** 239 * Returns number of members in a level, 240 * if the information can be retrieved from cache. 241 * Otherwise {@link Integer#MIN_VALUE}. 242 * 243 * @param level Level 244 * @return number of members in level 245 */ 246 private int getLevelCardinalityFromCache(Level level) { 247 final Hierarchy hierarchy = level.getHierarchy(); 248 final MemberReader memberReader = getMemberReader(hierarchy); 249 if (memberReader instanceof 250 RolapCubeHierarchy.RolapCubeHierarchyMemberReader) 251 { 252 final MemberCacheHelper cache = 253 ((RolapCubeHierarchy.RolapCubeHierarchyMemberReader) 254 memberReader).getRolapCubeMemberCacheHelper(); 255 if (cache == null) { 256 return Integer.MIN_VALUE; 257 } 258 final List<RolapMember> list = 259 cache.getLevelMembersFromCache( 260 (RolapLevel) level, null); 261 if (list == null) { 262 return Integer.MIN_VALUE; 263 } 264 return list.size(); 265 } 266 267 if (memberReader instanceof SmartMemberReader) { 268 List<RolapMember> list = 269 ((SmartMemberReader) memberReader) 270 .getMemberCache() 271 .getLevelMembersFromCache( 272 (RolapLevel) level, null); 273 if (list == null) { 274 return Integer.MIN_VALUE; 275 } 276 return list.size(); 277 } 278 279 if (memberReader instanceof MemberCache) { 280 List<RolapMember> list = 281 ((MemberCache) memberReader) 282 .getLevelMembersFromCache( 283 (RolapLevel) level, null); 284 if (list == null) { 285 return Integer.MIN_VALUE; 286 } 287 return list.size(); 288 } 289 290 return Integer.MIN_VALUE; 291 } 292 293 public int getLevelCardinality( 294 Level level, 295 boolean approximate, 296 boolean materialize) 297 { 298 if (!this.role.canAccess(level)) { 299 return 1; 300 } 301 302 int rowCount = Integer.MIN_VALUE; 303 if (approximate) { 304 // See if the schema has an approximation. 305 rowCount = level.getApproxRowCount(); 306 } 307 308 if (rowCount == Integer.MIN_VALUE) { 309 // See if the precise row count is available in cache. 310 rowCount = getLevelCardinalityFromCache(level); 311 } 312 313 if (rowCount == Integer.MIN_VALUE) { 314 if (materialize) { 315 // Either the approximate row count hasn't been set, 316 // or they want the precise row count. 317 final MemberReader memberReader = 318 getMemberReader(level.getHierarchy()); 319 rowCount = 320 memberReader.getLevelMemberCount((RolapLevel) level); 321 // Cache it for future. 322 ((RolapLevel) level).setApproxRowCount(rowCount); 323 } 324 } 325 return rowCount; 326 } 327 328 public List<Member> getMemberChildren(List<Member> members) { 329 return getMemberChildren(members, null); 330 } 331 332 public List<Member> getMemberChildren( 333 List<Member> members, 334 Evaluator context) 335 { 336 if (members.size() == 0) { 337 return Collections.emptyList(); 338 } else { 339 MemberChildrenConstraint constraint = 340 sqlConstraintFactory.getMemberChildrenConstraint(context); 341 final Hierarchy hierarchy = members.get(0).getHierarchy(); 342 final MemberReader memberReader = getMemberReader(hierarchy); 343 final List<RolapMember> rolapMemberList = Util.cast(members); 344 final List<RolapMember> children = new ArrayList<RolapMember>(); 345 memberReader.getMemberChildren( 346 rolapMemberList, 347 children, 348 constraint); 349 return Util.cast(children); 350 } 351 } 352 353 public void getMemberAncestors(Member member, List<Member> ancestorList) { 354 Member parentMember = getMemberParent(member); 355 while (parentMember != null) { 356 ancestorList.add(parentMember); 357 parentMember = getMemberParent(parentMember); 358 } 359 } 360 361 public Cube getCube() { 362 throw new UnsupportedOperationException(); 363 } 364 365 public SchemaReader withoutAccessControl() { 366 assert this.getClass() == RolapSchemaReader.class 367 : "Subclass " + getClass() + " must override"; 368 if (role == schema.getDefaultRole()) { 369 return this; 370 } 371 return new RolapSchemaReader(schema.getDefaultRole(), schema); 372 } 373 374 public OlapElement getElementChild(OlapElement parent, Id.Segment name) { 375 return getElementChild(parent, name, MatchType.EXACT); 376 } 377 378 public OlapElement getElementChild( 379 OlapElement parent, Id.Segment name, MatchType matchType) 380 { 381 return parent.lookupChild(this, name, matchType); 382 } 383 384 public final Member getMemberByUniqueName( 385 List<Id.Segment> uniqueNameParts, 386 boolean failIfNotFound) 387 { 388 return getMemberByUniqueName( 389 uniqueNameParts, failIfNotFound, MatchType.EXACT); 390 } 391 392 public Member getMemberByUniqueName( 393 List<Id.Segment> uniqueNameParts, 394 boolean failIfNotFound, 395 MatchType matchType) 396 { 397 // In general, this schema reader doesn't have a cube, so we cannot 398 // start looking up members. 399 return null; 400 } 401 402 public OlapElement lookupCompound( 403 OlapElement parent, 404 List<Id.Segment> names, 405 boolean failIfNotFound, 406 int category) 407 { 408 return lookupCompound( 409 parent, names, failIfNotFound, category, MatchType.EXACT); 410 } 411 412 public final OlapElement lookupCompound( 413 OlapElement parent, 414 List<Id.Segment> names, 415 boolean failIfNotFound, 416 int category, 417 MatchType matchType) 418 { 419 if (MondrianProperties.instance().SsasCompatibleNaming.get()) { 420 return new NameResolver().resolve( 421 parent, 422 Util.toOlap4j(names), 423 failIfNotFound, 424 category, 425 matchType, 426 getNamespaces()); 427 } 428 return lookupCompoundInternal( 429 parent, 430 names, 431 failIfNotFound, 432 category, 433 matchType); 434 } 435 436 public final OlapElement lookupCompoundInternal( 437 OlapElement parent, 438 List<Id.Segment> names, 439 boolean failIfNotFound, 440 int category, 441 MatchType matchType) 442 { 443 return Util.lookupCompound( 444 this, parent, names, failIfNotFound, category, matchType); 445 } 446 447 public List<NameResolver.Namespace> getNamespaces() { 448 return Collections.<NameResolver.Namespace>singletonList(this); 449 } 450 451 public OlapElement lookupChild( 452 OlapElement parent, 453 IdentifierSegment segment) 454 { 455 return lookupChild(parent, segment, MatchType.EXACT); 456 } 457 458 public OlapElement lookupChild( 459 OlapElement parent, 460 IdentifierSegment segment, 461 MatchType matchType) 462 { 463 OlapElement element = getElementChild( 464 parent, 465 Util.convert(segment), 466 matchType); 467 if (element != null) { 468 return element; 469 } 470 if (parent instanceof Cube) { 471 // Named sets defined at the schema level do not, of course, belong 472 // to a cube. But if parent is a cube, this indicates that the name 473 // has not been qualified. 474 element = schema.getNamedSet(segment); 475 } 476 return element; 477 } 478 479 public Member lookupMemberChildByName( 480 Member parent, Id.Segment childName, MatchType matchType) 481 { 482 if (LOGGER.isDebugEnabled()) { 483 LOGGER.debug( 484 "looking for child \"" + childName + "\" of " + parent); 485 } 486 assert !(parent instanceof RolapHierarchy.LimitedRollupMember); 487 try { 488 MemberChildrenConstraint constraint; 489 if (childName instanceof Id.NameSegment 490 && matchType.isExact()) 491 { 492 constraint = sqlConstraintFactory.getChildByNameConstraint( 493 (RolapMember) parent, (Id.NameSegment) childName); 494 } else { 495 constraint = 496 sqlConstraintFactory.getMemberChildrenConstraint(null); 497 } 498 List<RolapMember> children = 499 internalGetMemberChildren(parent, constraint); 500 if (children.size() > 0) { 501 return 502 RolapUtil.findBestMemberMatch( 503 children, 504 (RolapMember) parent, 505 children.get(0).getLevel(), 506 childName, 507 matchType); 508 } 509 } catch (NumberFormatException e) { 510 // this was thrown in SqlQuery#quote(boolean numeric, Object 511 // value). This happens when Mondrian searches for unqualified Olap 512 // Elements like [Month], because it tries to look up a member with 513 // that name in all dimensions. Then it generates for example 514 // "select .. from time where year = Month" which will result in a 515 // NFE because "Month" can not be parsed as a number. The real bug 516 // is probably, that Mondrian looks at members at all. 517 // 518 // @see RolapCube#lookupChild() 519 LOGGER.debug( 520 "NumberFormatException in lookupMemberChildByName " 521 + "for parent = \"" + parent 522 + "\", childName=\"" + childName 523 + "\", exception: " + e.getMessage()); 524 } 525 return null; 526 } 527 528 public Member getCalculatedMember(List<Id.Segment> nameParts) { 529 // There are no calculated members defined against a schema. 530 return null; 531 } 532 533 public NamedSet getNamedSet(List<Id.Segment> nameParts) { 534 if (nameParts.size() != 1) { 535 return null; 536 } 537 if (!(nameParts.get(0) instanceof Id.NameSegment)) { 538 return null; 539 } 540 final String name = ((Id.NameSegment) nameParts.get(0)).name; 541 return schema.getNamedSet(name); 542 } 543 544 public Member getLeadMember(Member member, int n) { 545 final MemberReader memberReader = 546 getMemberReader(member.getHierarchy()); 547 return memberReader.getLeadMember((RolapMember) member, n); 548 } 549 550 public List<Member> getLevelMembers(Level level, boolean includeCalculated) 551 { 552 List<Member> members = getLevelMembers(level, null); 553 if (!includeCalculated) { 554 members = SqlConstraintUtils.removeCalculatedMembers(members); 555 } 556 return members; 557 } 558 559 public List<Member> getLevelMembers(Level level, Evaluator context) { 560 TupleConstraint constraint = 561 sqlConstraintFactory.getLevelMembersConstraint( 562 context, 563 new Level[] {level}); 564 final MemberReader memberReader = 565 getMemberReader(level.getHierarchy()); 566 List<RolapMember> membersInLevel = 567 memberReader.getMembersInLevel( 568 (RolapLevel) level, constraint); 569 return Util.cast(membersInLevel); 570 } 571 572 public List<Dimension> getCubeDimensions(Cube cube) { 573 assert cube != null; 574 final List<Dimension> dimensions = new ArrayList<Dimension>(); 575 for (Dimension dimension : cube.getDimensions()) { 576 switch (role.getAccess(dimension)) { 577 case NONE: 578 continue; 579 default: 580 dimensions.add(dimension); 581 break; 582 } 583 } 584 return dimensions; 585 } 586 587 public List<Hierarchy> getDimensionHierarchies(Dimension dimension) { 588 assert dimension != null; 589 final List<Hierarchy> hierarchies = new ArrayList<Hierarchy>(); 590 for (Hierarchy hierarchy : dimension.getHierarchies()) { 591 switch (role.getAccess(hierarchy)) { 592 case NONE: 593 continue; 594 default: 595 hierarchies.add(hierarchy); 596 break; 597 } 598 } 599 return hierarchies; 600 } 601 602 public List<Level> getHierarchyLevels(Hierarchy hierarchy) { 603 assert hierarchy != null; 604 final Role.HierarchyAccess hierarchyAccess = 605 role.getAccessDetails(hierarchy); 606 final Level[] levels = hierarchy.getLevels(); 607 if (hierarchyAccess == null) { 608 return Arrays.asList(levels); 609 } 610 Level topLevel = levels[hierarchyAccess.getTopLevelDepth()]; 611 Level bottomLevel = levels[hierarchyAccess.getBottomLevelDepth()]; 612 List<Level> restrictedLevels = 613 Arrays.asList(levels).subList( 614 topLevel.getDepth(), bottomLevel.getDepth() + 1); 615 assert restrictedLevels.size() >= 1 : "postcondition"; 616 return restrictedLevels; 617 } 618 619 public Member getHierarchyDefaultMember(Hierarchy hierarchy) { 620 assert hierarchy != null; 621 // If the whole hierarchy is inaccessible, return the intrinsic default 622 // member. This is important to construct a evaluator. 623 if (role.getAccess(hierarchy) == Access.NONE) { 624 return hierarchy.getDefaultMember(); 625 } 626 return getMemberReader(hierarchy).getDefaultMember(); 627 } 628 629 public boolean isDrillable(Member member) { 630 final RolapLevel level = (RolapLevel) member.getLevel(); 631 if (level.getParentExp() != null) { 632 // This is a parent-child level, so its children, if any, come from 633 // the same level. 634 // 635 // todo: More efficient implementation 636 return getMemberChildren(member).size() > 0; 637 } else { 638 // This is a regular level. It has children iff there is a lower 639 // level. 640 final Level childLevel = level.getChildLevel(); 641 return (childLevel != null) 642 && (role.getAccess(childLevel) != Access.NONE); 643 } 644 } 645 646 public boolean isVisible(Member member) { 647 return !member.isHidden() && role.canAccess(member); 648 } 649 650 public Cube[] getCubes() { 651 List<RolapCube> cubes = schema.getCubeList(); 652 List<Cube> visibleCubes = new ArrayList<Cube>(cubes.size()); 653 654 for (Cube cube : cubes) { 655 if (role.canAccess(cube)) { 656 visibleCubes.add(cube); 657 } 658 } 659 660 return visibleCubes.toArray(new Cube[visibleCubes.size()]); 661 } 662 663 public List<Member> getCalculatedMembers(Hierarchy hierarchy) { 664 return Collections.emptyList(); 665 } 666 667 public List<Member> getCalculatedMembers(Level level) { 668 return Collections.emptyList(); 669 } 670 671 public List<Member> getCalculatedMembers() { 672 return Collections.emptyList(); 673 } 674 675 public NativeEvaluator getNativeSetEvaluator( 676 FunDef fun, Exp[] args, Evaluator evaluator, Calc calc) 677 { 678 RolapEvaluator revaluator = (RolapEvaluator) 679 AbstractCalc.simplifyEvaluator(calc, evaluator); 680 if (evaluator.nativeEnabled()) { 681 return schema.getNativeRegistry().createEvaluator( 682 revaluator, fun, args); 683 } 684 return null; 685 } 686 687 public Parameter getParameter(String name) { 688 // Scan through schema parameters. 689 for (RolapSchemaParameter parameter : schema.parameterList) { 690 if (Util.equalName(parameter.getName(), name)) { 691 return parameter; 692 } 693 } 694 695 // Scan through mondrian and system properties. 696 List<Property> propertyList = 697 MondrianProperties.instance().getPropertyList(); 698 for (Property property : propertyList) { 699 if (property.getPath().equals(name)) { 700 return new SystemPropertyParameter(name, false); 701 } 702 } 703 if (System.getProperty(name) != null) { 704 return new SystemPropertyParameter(name, true); 705 } 706 707 return null; 708 } 709 710 public DataSource getDataSource() { 711 return schema.getInternalConnection().getDataSource(); 712 } 713 714 public RolapSchema getSchema() { 715 return schema; 716 } 717 718 public SchemaReader withLocus() { 719 return RolapUtil.locusSchemaReader( 720 schema.getInternalConnection(), 721 this); 722 } 723 724 public Map<? extends Member, Access> getMemberChildrenWithDetails( 725 Member member, 726 Evaluator evaluator) 727 { 728 MemberChildrenConstraint constraint = 729 sqlConstraintFactory.getMemberChildrenConstraint(evaluator); 730 final Hierarchy hierarchy = member.getHierarchy(); 731 final MemberReader memberReader = getMemberReader(hierarchy); 732 final ArrayList<RolapMember> memberChildren = 733 new ArrayList<RolapMember>(); 734 735 return memberReader.getMemberChildren( 736 (RolapMember) member, 737 memberChildren, 738 constraint); 739 } 740 741 /** 742 * Implementation of {@link Parameter} which is sourced from system 743 * propertes (see {@link System#getProperties()} or mondrian properties 744 * (see {@link MondrianProperties}. 745 * <p/> 746 * <p>The name of the property is the same as the key into the 747 * {@link java.util.Properties} object; for example "java.version" or 748 * "mondrian.trace.level". 749 */ 750 private static class SystemPropertyParameter 751 extends ParameterImpl 752 { 753 /** 754 * true if source is a system property; 755 * false if source is a mondrian property. 756 */ 757 private final boolean system; 758 /** 759 * Definition of mondrian property, or null if system property. 760 */ 761 private final Property propertyDefinition; 762 763 public SystemPropertyParameter(String name, boolean system) { 764 super( 765 name, 766 Literal.nullValue, 767 "System property '" + name + "'", 768 new StringType()); 769 this.system = system; 770 this.propertyDefinition = 771 system 772 ? null 773 : MondrianProperties.instance().getPropertyDefinition(name); 774 } 775 776 public Scope getScope() { 777 return Scope.System; 778 } 779 780 public boolean isModifiable() { 781 return false; 782 } 783 784 public Calc compile(ExpCompiler compiler) { 785 return new GenericCalc(new DummyExp(getType())) { 786 public Calc[] getCalcs() { 787 return new Calc[0]; 788 } 789 790 public Object evaluate(Evaluator evaluator) { 791 if (system) { 792 final String name = 793 SystemPropertyParameter.this.getName(); 794 return System.getProperty(name); 795 } else { 796 return propertyDefinition.stringValue(); 797 } 798 } 799 }; 800 } 801 } 802} 803 804// End RolapSchemaReader.java