Skip to content

Commit

Permalink
Improve javadoc in IdentifierNode.
Browse files Browse the repository at this point in the history
Add optimized implementation of read-only lists backed by arrays,
and use it in IdentifierNode and XmlaOlap4jConnection.MetadataRequest.


git-svn-id: https://olap4j.svn.sourceforge.net/svnroot/olap4j/trunk@231 c6a108a4-781c-0410-a6c6-c2d559e19af0
  • Loading branch information
julianhyde committed May 8, 2009
1 parent 9c7ceab commit 8d210c4
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 60 deletions.
9 changes: 6 additions & 3 deletions src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -1743,6 +1743,11 @@ enum MetadataRequest {
final List<MetadataColumn> columns;
final Map<String, MetadataColumn> columnsByName;

/**
* Creates a MetadataRequest.
*
* @param columns Columns
*/
MetadataRequest(MetadataColumn... columns) {
if (name().equals("DBSCHEMA_CATALOGS")) {
// DatabaseMetaData.getCatalogs() is defined by JDBC not XMLA,
Expand All @@ -1758,9 +1763,7 @@ enum MetadataRequest {
new MetadataColumn("CATALOG_NAME", "TABLE_CAT")
};
}
this.columns =
Collections.unmodifiableList(
Arrays.asList(columns));
this.columns = UnmodifiableArrayList.asCopyOf(columns);
final Map<String, MetadataColumn> map =
new HashMap<String, MetadataColumn>();
for (MetadataColumn column : columns) {
Expand Down
87 changes: 87 additions & 0 deletions src/org/olap4j/impl/UnmodifiableArrayList.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
// $Id: AbstractNamedList.java 229 2009-05-08 19:11:29Z jhyde $
// This software is subject to the terms of the Eclipse Public License v1.0
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// Copyright (C) 2009-2009 Julian Hyde
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
*/
package org.olap4j.impl;

import java.util.*;

/**
* Unmodifiable list backed by an array.
*
* <p>The traditional solution to this problem is to call
* {@link java.util.Arrays#asList(Object[])} followed by
* {@link java.util.Collections#unmodifiableList(java.util.List)}, but this
* class has one fewer wrapper object, saving space and indirection effort.
*
* @author jhyde
* @version $Id: AbstractNamedList.java 229 2009-05-08 19:11:29Z jhyde $
* @since May 7, 2009
*/
public class UnmodifiableArrayList<T>
extends AbstractList<T>
implements List<T>
{
private final T[] elements;

/**
* Creates an UnmodifiableArrayList.
*
* <p>Does not create a copy of the array. Future changes to the array will
* be reflected in the contents of the list.
*
* @param elements Array
*/
public UnmodifiableArrayList(T... elements) {
this.elements = elements;
}

public T get(int index) {
return elements[index];
}

public int size() {
return elements.length;
}

/**
* Creates an unmodifable list as a shallow copy of an array.
*
* <p>Future changes to the array will not be reflected in the contents
* of the list.
*
* @param elements Elements of list
* @param <T> Type of elements
* @return Unmodifiable list with same contents that the array had at call
* time
*/
public static <T> UnmodifiableArrayList<T> asCopyOf(T... elements) {
return new UnmodifiableArrayList<T>(elements.clone());
}

/**
* Creates an unmodifable list as a shallow copy of a collection.
*
* <p>Future changes to the collection will not be reflected in the contents
* of the list.
*
* @param collection Elements of list
* @param <T> Type of elements
* @return Unmodifiable list with same contents that the collection had at
* call time
*/
public static <T> UnmodifiableArrayList<T> of(
Collection<? extends T> collection)
{
//noinspection unchecked
return new UnmodifiableArrayList<T>(
(T[]) collection.toArray());
}
}

// End UnmodifiableArrayList.java
144 changes: 87 additions & 57 deletions src/org/olap4j/mdx/IdentifierNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
package org.olap4j.mdx;

import org.olap4j.impl.Olap4jUtil;
import org.olap4j.impl.UnmodifiableArrayList;
import org.olap4j.type.Type;

import java.util.*;
Expand All @@ -21,10 +22,10 @@
*
* <p>An identifer consists of one or more {@link Segment}s. A segment is
* either:<ul>
* <li>An unquoted value such as 'CA',
* <li>A value quoted in brackets, such as '[San Francisco]', or
* <li>An unquoted value such as '{@code CA}',
* <li>A value quoted in brackets, such as '{@code [San Francisco]}', or
* <li>A key of one or more parts, each of which is prefixed with '&amp;',
* such as '&[Key 1]&Key2&[5]'.
* such as '{@code &amp;[Key 1]&amp;Key2&amp;[5]}'.
* </ul>
*
* <p>Segment types are indicated by the {@link Quoting} enumeration.
Expand All @@ -45,8 +46,8 @@
* </ul>
*
* <p>A more complex example illustrates a compound key. The identifier
* {@code [Customers].[City].&[San Francisco]&CA&USA.&[cust1234]} contains four
* segments as follows:
* {@code [Customers].[City].&amp;[San Francisco]&amp;CA&amp;USA.&amp;[cust1234]}
* contains four segments as follows:
* <ul>
* <li>Segment #0 is QUOTED, name "Customers"</li>
* <li>Segment #1 is QUOTED, name "City"</li>
Expand Down Expand Up @@ -80,7 +81,10 @@ public class IdentifierNode
* style
*/
public IdentifierNode(IdentifierNode.Segment... segments) {
this(Arrays.asList(segments));
if (segments.length < 1) {
throw new IllegalArgumentException();
}
this.segments = UnmodifiableArrayList.asCopyOf(segments);
}

/**
Expand All @@ -93,8 +97,9 @@ public IdentifierNode(List<IdentifierNode.Segment> segments) {
throw new IllegalArgumentException();
}
this.segments =
Collections.unmodifiableList(
new ArrayList<Segment>(segments));
new UnmodifiableArrayList<Segment>(
segments.toArray(
new NameSegment[segments.size()]));
}

public Type getType() {
Expand Down Expand Up @@ -161,15 +166,7 @@ public void unparse(ParseTreeWriter writer) {
}

public String toString() {
final StringBuilder buf = new StringBuilder();
int k = 0;
for (IdentifierNode.Segment s : segments) {
if (k++ > 0) {
buf.append('.');
}
s.toString(buf);
}
return buf.toString();
return unparseIdentifierList(segments);
}

public IdentifierNode deepCopy() {
Expand All @@ -184,7 +181,7 @@ public IdentifierNode deepCopy() {
* was {@link Quoting quoted}. For example,
*
* <blockquote><code>
* parseIdentifier("[Customers].USA.[South Dakota].[Sioux Falls].&[1245]")
* parseIdentifier("[Customers].USA.[South Dakota].[Sioux Falls].&amp;[1245]")
* </code></blockquote>
*
* returns
Expand Down Expand Up @@ -311,24 +308,54 @@ static void quoteMdxIdentifier(String id, StringBuilder buf) {
* <p>For example, {"Store", "USA",
* "California"} becomes "[Store].[USA].[California]".
*
* @param ids List of segments
* @param segments List of segments
* @return Segments as quoted string
*/
static String unparseIdentifierList(List<? extends Segment> ids) {
StringBuilder sb = new StringBuilder(64);
for (int i = 0; i < ids.size(); i++) {
static String unparseIdentifierList(List<? extends Segment> segments) {
final StringBuilder buf = new StringBuilder(64);
for (int i = 0; i < segments.size(); i++) {
Segment segment = segments.get(i);
if (i > 0) {
sb.append('.');
buf.append('.');
}
sb.append(ids.get(i).toString());
segment.toString(buf);
}
return sb.toString();
return buf.toString();
}

/**
* Component in a compound identifier. It is described by its name and how
* the name is quoted.
*
* <p>For example, the identifier
* <code>[Store].USA.[New Mexico].&amp;[45]</code> has four segments:<ul>
* <li>"Store", {@link IdentifierNode.Quoting#QUOTED}</li>
* <li>"USA", {@link IdentifierNode.Quoting#UNQUOTED}</li>
* <li>"New Mexico", {@link IdentifierNode.Quoting#QUOTED}</li>
* <li>"45", {@link IdentifierNode.Quoting#KEY}</li>
* </ul>
*
* <p>QUOTED and UNQUOTED segments are represented using a
* {@link org.olap4j.mdx.IdentifierNode.NameSegment NameSegment};
* KEY segments are represented using a
* {@link org.olap4j.mdx.IdentifierNode.KeySegment KeySegment}.
*
* <p>To parse an identifier into a list of segments, use the method
* {@link IdentifierNode#parseIdentifier(String)}.</p>
*/
public interface Segment {

/**
* Appends this segment to a StringBuffer
* Returns a string representation of this Segment.
*
* <p>For example, "[Foo]", "&amp;[123]", "Abc".
*
* @return String representation of this Segment
*/
String toString();

/**
* Appends a string representation of this Segment to a StringBuffer.
*
* @param buf StringBuffer
*/
Expand Down Expand Up @@ -370,19 +397,10 @@ public interface Segment {
}

/**
* Component in a compound identifier. It is described by its name and how
* the name is quoted.
* Component in a compound identifier that describes the name of an object.
* Optionally, the name is quoted in brackets.
*
* <p>For example, the identifier
* <code>[Store].USA.[New Mexico].&[45]</code> has four segments:<ul>
* <li>"Store", {@link IdentifierNode.Quoting#QUOTED}</li>
* <li>"USA", {@link IdentifierNode.Quoting#UNQUOTED}</li>
* <li>"New Mexico", {@link IdentifierNode.Quoting#QUOTED}</li>
* <li>"45", {@link IdentifierNode.Quoting#KEY}</li>
* </ul>
*
* To parse an identifier into a list of segments, use the method
* {@link IdentifierNode#parseIdentifier(String)}.</p>
* @see org.olap4j.mdx.IdentifierNode.KeySegment
*/
public static class NameSegment implements Segment {
final String name;
Expand Down Expand Up @@ -415,21 +433,12 @@ public NameSegment(String name) {
this(null, name, Quoting.QUOTED);
}

/**
* Returns a string representation of this Segment.
*
* <p>For example, "[Foo]", "&[123]", "Abc".
*
* @return String representation of this Segment
*/
public String toString() {
switch (quoting) {
case UNQUOTED:
return name;
case QUOTED:
return quoteMdxIdentifier(name);
case KEY:
return '&' + quoteMdxIdentifier(name);
default:
throw Olap4jUtil.unexpected(quoting);
}
Expand All @@ -443,10 +452,6 @@ public void toString(StringBuilder buf) {
case QUOTED:
quoteMdxIdentifier(name, buf);
return;
case KEY:
buf.append('&');
quoteMdxIdentifier(name, buf);
return;
default:
throw Olap4jUtil.unexpected(quoting);
}
Expand All @@ -473,20 +478,45 @@ public List<NameSegment> getKeyParts() {
*
* <p>Such a segment appears in an identifier with each component prefixed
* with '&amp;'. For example, in the identifier
* '[Customer].[State].&[WA]&[USA]', the third segment is a compound key
* whose parts are "WA" and "USA".
* '{@code [Customer].[State].&amp;[WA]&amp;[USA]}', the third segment is a
* compound key whose parts are "@{code WA}" and "{@code USA}".
*
* @see org.olap4j.mdx.IdentifierNode.NameSegment
*/
public static class KeySegment implements Segment {
private final List<NameSegment> subSegmentList;

/**
* Creates a KeySegment.
* Creates a KeySegment with one or more sub-segments.
*
* @param subSegmentList List if sub-segments
* @param subSegments Array of sub-segments
*/
public KeySegment(NameSegment... subSegments) {
if (subSegments.length < 1) {
throw new IllegalArgumentException();
}
this.subSegmentList = UnmodifiableArrayList.asCopyOf(subSegments);
}

/**
* Creates a KeySegment a list of sub-segments.
*
* @param subSegmentList List of sub-segments
*/
public KeySegment(List<NameSegment> subSegmentList) {
this.subSegmentList = new ArrayList<NameSegment>(subSegmentList);
assert subSegmentList.size() >= 1;
if (subSegmentList.size() < 1) {
throw new IllegalArgumentException();
}
this.subSegmentList =
new UnmodifiableArrayList<NameSegment>(
subSegmentList.toArray(
new NameSegment[subSegmentList.size()]));
}

public String toString() {
final StringBuilder buf = new StringBuilder();
toString(buf);
return buf.toString();
}

public void toString(StringBuilder buf) {
Expand Down
Loading

0 comments on commit 8d210c4

Please sign in to comment.