Skip to content

Commit

Permalink
Redesign lambdas and projections handling
Browse files Browse the repository at this point in the history
Fixes #5: validate lambda expression upon construction
  • Loading branch information
maciejlach committed Oct 1, 2014
1 parent f6d5a00 commit 115cae4
Show file tree
Hide file tree
Showing 12 changed files with 375 additions and 99 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
------------------------------------------------------------------------------
qJava 2.1.0 [TBA]
qJava 2.1.0 [2014.10.01]
------------------------------------------------------------------------------

- Redesigned lambdas and projections handling
- Temporal classes are now Serializable

------------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!-- img src="http://www.devnet.de/fileadmin/images/DEVnet_Logo2014.png" width="150px" height="150px"/-->

qJava 2.0
qJava 2.1
=========

The q/kdb+ interface is implemented as a set of Java classes and provides:
Expand Down
44 changes: 38 additions & 6 deletions doc/Type-conversion.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,25 @@ The `QType` class defines mapping between the q and corresponding Java types.
| TIME | -19 | time | QTime |
| TIME_LIST | 19 | time list | QTime[] |
| GENERAL_LIST | 0 | general list | Object[] |
| LAMBDA | 100, 104 | function body | QLambda |
| TABLE | 98 | table | QTable |
| KEYED_TABLE | 99 | keyed table | QKeyedTable |
| DICTIONARY | 99 | dictionary | QDictionary |
| LAMBDA | 100 | function body | QLambda |
| PROJECTION | 104 | function projection | QProjection |
| UNARY_PRIMITIVE_FUNC | 101 | function | QFunction |
| BINARY_PRIMITIVE_FUNC | 102 | function | QFunction |
| TERNARY_OPERATOR_FUNC | 103 | function | QFunction |
| COMPOSITION_FUNC | 105 | function | QFunction |
| ADVERB_FUNC | 106-111 | function | QFunction |
```

Note that q list are represented as arrays of primitive type by the qJava library.
It is possible to send to q arrays of primitive type (e.g. `int[]`) as well as of boxed type (e.g. `Integer[]`).
Note that q list are represented as arrays of primitive type by the qJava
library. It is possible to send to q arrays of primitive type (e.g. `int[]`) as
well as of boxed type (e.g. `Integer[]`).

### Temporal types
q language provides multiple types for operating on temporal data. The qJava library provides a corresponding temporal class for each q temporal type.
q language provides multiple types for operating on temporal data. The qJava
library provides a corresponding temporal class for each q temporal type.

Instance of each class can be created:
* from the the underlying base type (e.g. `Long` in case of `QTimespan` and `QTimestamp`, `Double` in case of `QDateTime`),
Expand All @@ -66,10 +74,34 @@ public Object getValue() // Returns internal q representation of the temp
public DateTime toDateTime() // Represents q date/time with the instance of java.util.Date.
```

### Functions, lambdas and projections

IPC protocol type codes 100+ are used to represent functions, lambdas and
projections. These types are represented as instances of base class
`QFunction` or descendent classes:

* `QLambda` - represents q lambda expression, note that expression is required
to be either:
* q expression enclosed in {}, e.g.: `{x + y}`
* k expression, e.g.: `k){x + y}`

* `QProjection` - represents function projection with parameters, e.g.:
```java
// { x + y}[3]
new QProjection(new Object[] {new QLambda("{x+y}"), 3L });
```

Note that only `QLambda` and `QProjection` are serializable. qJava doesn't
provide means to serialize other function types.

### Null values
The `QType` enumeration exposes a utility static method `getQNull(QType type)` that returns corresponding q null value of given type. Keep in mind that null values are only defined and available for primitive q types.
The `QType` enumeration exposes a utility static method `getQNull(QType type)`
that returns corresponding q null value of given type. Keep in mind that null
values are only defined and available for primitive q types.

As null values in q are represented as arbitrary values, it is also possible to produce null value without explicitly calling the `getQNull` method. Q null values are mapped to Java according to the following table:
As null values in q are represented as arbitrary values, it is also possible to
produce null value without explicitly calling the `getQNull` method. Q null
values are mapped to Java according to the following table:

```
| q type | Java null |
Expand Down
50 changes: 50 additions & 0 deletions src/main/java/com/exxeleron/qjava/QFunction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* Copyright (c) 2011-2014 Exxeleron GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.exxeleron.qjava;

/**
* Represents q function.
*
* Note that the {@link QFunction} instances cannot be serialized to IPC protocol.
*/
public class QFunction {

private final byte qTypeCode;

/**
* Creates representation of q function with given q type code.
*
* @param qTypeCode
* q type code
*/
protected QFunction(final byte qTypeCode) {
this.qTypeCode = qTypeCode;
}

/**
* Retrieve q type code connected with function.
*
* @return type code for function
*/
public byte getTypeCode() {
return qTypeCode;
}

@Override
public String toString() {
return "QFunction#" + qTypeCode + "h";
}
}
69 changes: 31 additions & 38 deletions src/main/java/com/exxeleron/qjava/QLambda.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/**
* Copyright (c) 2011-2014 Exxeleron GmbH
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Expand All @@ -15,73 +15,66 @@
*/
package com.exxeleron.qjava;

import java.util.regex.Pattern;

/**
* Represents a q lambda expression.
*/
public final class QLambda {
public final class QLambda extends QFunction {
private static final Pattern LAMBDA_REGEX = Pattern.compile("\\s*(k\\))?\\s*\\{.*\\}");

private final String expression;
private final Object[] parameters;

/**
* Gets body of a q lambda expression.
*
*
* @return body of a q lambda expression
*/
public String getExpression() {
return expression;
}

/**
* Gets parameters of a q lambda expression.
*
* @return array containing lambda expression parameters
*/
public Object[] getParameters() {
return parameters;
}

/**
* Creates new {@link QLambda} instance with given body and parameters.
*
* Creates new {@link QLambda} instance with given body. Note that expression is trimmed and required to be enclosed
* in { and } brackets.
*
* @param expression
* body of a q lambda expression
* @param parameters
* array containing lambda expression parameters
*
* @throws IllegalArgumentException
*/
public QLambda(final String expression, final Object[] parameters) {
if ( expression == null || expression.length() == 0 ) {
public QLambda(final String expression) {
super(QType.LAMBDA.getTypeCode());

if ( expression == null ) {
throw new IllegalArgumentException("Lambda expression cannot be null or empty");
}

this.expression = expression;
this.parameters = parameters;
}
this.expression = expression.trim();
if ( this.expression.length() == 0 ) {
throw new IllegalArgumentException("Lambda expression cannot be null or empty");
}

/**
* Creates new {@link QLambda} instance with given body and no parameters.
*
* @param expression
* body of a q lambda expression
*/
public QLambda(final String expression) {
this(expression, null);
if ( !LAMBDA_REGEX.matcher(expression).matches() ) {
throw new IllegalArgumentException("Invalid lambda expression: " + expression);
}
}

/**
* Returns a String that represents the current {@link QLambda}.
*
*
* @return a String representation of the {@link QLambda}
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "QLambda: " + expression + (parameters == null ? "" : Utils.arrayToString(parameters));
return "QLambda: " + expression;
}

/**
* Indicates whether some other object is "equal to" this lambda expression. {@link QLambda} objects are considered
* equal if the expression and parameters list are equal for both instances.
*
* equal if the expression is equal for both instances.
*
* @return <code>true</code> if this object is the same as the obj argument, <code>false</code> otherwise.
* @see java.lang.Object#equals(java.lang.Object)
*/
Expand All @@ -96,12 +89,12 @@ public boolean equals( final Object obj ) {
}

final QLambda l = (QLambda) obj;
return expression.equals(l.expression) && Utils.deepArraysEquals(parameters, l.parameters);
return expression.equals(l.expression);
}

/**
* Returns a hash code value for this {@link QLambda}.
*
*
* @return a hash code value for this object
* @see java.lang.Object#hashCode()
*/
Expand Down
85 changes: 85 additions & 0 deletions src/main/java/com/exxeleron/qjava/QProjection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* Copyright (c) 2011-2014 Exxeleron GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.exxeleron.qjava;

import java.util.Arrays;

/**
* Represents a q projection.
*/
public final class QProjection extends QFunction{
private final Object[] parameters;

/**
* Gets parameters of a q projection.
*
* @return array containing projection parameters
*/
public Object[] getParameters() {
return parameters;
}

/**
* Creates new {@link QProjection} instance with given parameters.
*
* @param parameters
* array containing projection parameters
*
* @throws IllegalArgumentException
*/
public QProjection(final Object[] parameters) {
super(QType.PROJECTION.getTypeCode());
this.parameters = parameters;
}

/**
* Returns a String that represents the current {@link QProjection}.
*
* @return a String representation of the {@link QProjection}
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "QProjection: " + (parameters == null ? "<null>" : Utils.arrayToString(parameters));
}

/**
* Indicates whether some other object is "equal to" this projection. {@link QProjection} objects are considered
* equal if the parameters list are equal for both instances.
*
* @return <code>true</code> if this object is the same as the obj argument, <code>false</code> otherwise.
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals( final Object obj ) {
if ( this == obj ) {
return true;
}

if ( !(obj instanceof QProjection) ) {
return false;
}

final QProjection p = (QProjection) obj;
return Utils.deepArraysEquals(parameters, p.parameters);
}

@Override
public int hashCode() {
return Arrays.hashCode(parameters);
}

}
Loading

0 comments on commit 115cae4

Please sign in to comment.