Skip to content

Commit 3f1e411

Browse files
committed
support for null results (fixes albertlatacz#29)
1 parent 4842952 commit 3f1e411

12 files changed

+98
-57
lines changed

src/javarepl/EvaluationTemplate.java

+9-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package javarepl;
22

3+
import com.googlecode.totallylazy.Option;
4+
35
import static com.googlecode.totallylazy.Predicates.equalTo;
46
import static com.googlecode.totallylazy.Predicates.where;
57
import static javarepl.Result.functions.key;
6-
import static javarepl.Result.functions.value;
78

89
@SuppressWarnings("unused")
910
public abstract class EvaluationTemplate {
@@ -15,10 +16,12 @@ public EvaluationTemplate(EvaluationContext context) {
1516

1617
@SuppressWarnings("unchecked")
1718
public final <T> T valueOf(final String key) {
18-
return (T) context.results()
19-
.filter(where(key(), equalTo(key)))
20-
.map(value())
21-
.headOption()
22-
.getOrThrow(new IllegalArgumentException("Result '" + key + "' not found"));
19+
Option<Result> result = context.results()
20+
.find(where(key(), equalTo(key)));
21+
22+
if (result.isEmpty())
23+
throw new IllegalArgumentException("Result '" + key + "' not found");
24+
25+
return (T) result.get().value();
2326
}
2427
}

src/javarepl/Evaluator.java

+35-20
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import com.googlecode.totallylazy.*;
44
import com.googlecode.totallylazy.annotations.multimethod;
55
import javarepl.expressions.*;
6+
import javarepl.expressions.Method;
7+
import javarepl.expressions.Type;
68
import javarepl.expressions.Value;
79

810
import javax.tools.DiagnosticCollector;
@@ -11,8 +13,7 @@
1113
import javax.tools.StandardJavaFileManager;
1214
import java.io.File;
1315
import java.io.FileNotFoundException;
14-
import java.lang.reflect.Constructor;
15-
import java.lang.reflect.Field;
16+
import java.lang.reflect.*;
1617
import java.net.URL;
1718
import java.util.regex.MatchResult;
1819

@@ -97,8 +98,13 @@ private Import createImport(String expression) {
9798
}
9899

99100
private AssignmentWithType createAssignmentWithType(String expression) {
100-
MatchResult match = Patterns.assignmentWithTypeNamePattern.match(expression);
101-
return new AssignmentWithType(expression, match.group(1), match.group(2), match.group(3));
101+
try {
102+
MatchResult match = Patterns.assignmentWithTypeNamePattern.match(expression);
103+
java.lang.reflect.Method declaredMethod = detectMethod(match.group(1) + " " + randomIdentifier("method") + "(){}");
104+
return new AssignmentWithType(expression, declaredMethod.getReturnType(), match.group(2), match.group(3));
105+
} catch (Exception e) {
106+
throw new RuntimeException(e);
107+
}
102108
}
103109

104110
private Assignment createAssignmentExpression(String expression) {
@@ -154,7 +160,7 @@ public final Option<Class> typeOfExpression(String expression) {
154160
if (evaluation.isRight()) {
155161
Option<Result> result = evaluation.right().result();
156162
if (!result.isEmpty()) {
157-
expressionType = some((Class) result.get().value().getClass());
163+
expressionType = some((Class) result.get().type());
158164
}
159165
}
160166

@@ -214,23 +220,25 @@ private Either<? extends Throwable, Evaluation> evaluate(Type expression) {
214220

215221
private Method createMethodExpression(String expression) {
216222
try {
217-
final String className = randomIdentifier("Method");
218-
final File outputJavaFile = file(outputDirectory, className + ".java");
219-
220-
final String sources = renderMethodSignatureDetection(context, className, expression);
221-
Files.write(sources.getBytes(), outputJavaFile);
223+
java.lang.reflect.Method declaredMethod = detectMethod(expression);
224+
return new Method(expression, declaredMethod.getReturnType(), declaredMethod.getName(), sequence(declaredMethod.getParameterTypes()));
225+
} catch (Exception e) {
226+
throw new RuntimeException(e);
227+
}
228+
}
222229

223-
compile(outputJavaFile);
230+
private java.lang.reflect.Method detectMethod(String expression) throws Exception {
231+
final String className = randomIdentifier("Method");
232+
final File outputJavaFile = file(outputDirectory, className + ".java");
224233

225-
Class<?> expressionClass = classLoader.loadClass(className);
234+
final String sources = renderMethodSignatureDetection(context, className, expression);
235+
Files.write(sources.getBytes(), outputJavaFile);
226236

227-
java.lang.reflect.Method declaredMethod = expressionClass.getDeclaredMethods()[0];
237+
compile(outputJavaFile);
228238

229-
return new Method(expression, declaredMethod.getReturnType(), declaredMethod.getName(), sequence(declaredMethod.getParameterTypes()));
239+
Class<?> expressionClass = classLoader.loadClass(className);
230240

231-
} catch (Exception e) {
232-
throw new RuntimeException(e);
233-
}
241+
return expressionClass.getDeclaredMethods()[0];
234242
}
235243

236244
private Either<? extends Throwable, Evaluation> evaluateExpression(final Expression expression) {
@@ -252,12 +260,13 @@ private Either<? extends Throwable, Evaluation> evaluateExpression(final Express
252260

253261
final Object expressionInstance = constructor.newInstance(newContext);
254262

255-
Object resultObject = expressionClass.getMethod("evaluate").invoke(expressionInstance);
263+
java.lang.reflect.Method method = expressionClass.getMethod("evaluate");
264+
Object resultObject = method.invoke(expressionInstance);
256265

257266
Sequence<Result> modifiedResults = modifiedResults(expressionInstance);
258267

259-
if (resultObject != null) {
260-
Result result = Result.result(nextResultKeyFor(expression), resultObject);
268+
if (resultObject != null || !method.getReturnType().equals(void.class)) {
269+
Result result = Result.result(nextResultKeyFor(expression), resultObject, typeFor(expression));
261270
context = newContext.addExpression(expression).addResults(modifiedResults.add(result)).lastSource(sources);
262271
return right(evaluation(expression, some(result)));
263272
} else {
@@ -294,6 +303,12 @@ private String nextResultKeyFor(Expression expression) {
294303
: expression.key();
295304
}
296305

306+
private Option<Class<?>> typeFor(Expression expression) {
307+
return (expression instanceof AssignmentWithType)
308+
? Option.<Class<?>>some(((AssignmentWithType) expression).type())
309+
: Option.<Class<?>>none();
310+
}
311+
297312
private void compile(File file) throws Exception {
298313
String classpath = sequence(System.getProperty("java.class.path")).join(sequence(classLoader.getURLs()).map(toString)).toString(pathSeparator);
299314
JavaCompiler compiler = getSystemJavaCompiler();

src/javarepl/Result.java

+22-6
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,19 @@
1010
public class Result {
1111
private final String key;
1212
private final Object value;
13+
private final Option<Class<?>> type;
1314

14-
private Result(String key, Object value) {
15+
private Result(String key, Object value, Option<Class<?>> type) {
1516
this.key = key;
1617
this.value = value;
18+
this.type = type;
1719
}
1820

21+
public static Result result(String key, Object value, Option<Class<?>> type) {
22+
return new Result(key, value, type);
23+
}
1924
public static Result result(String key, Object value) {
20-
return new Result(key, value);
25+
return result(key, value, Option.<Class<?>>none());
2126
}
2227

2328
public String key() {
@@ -28,13 +33,16 @@ public Object value() {
2833
return value;
2934
}
3035

36+
public Class<?> type() {
37+
return type.getOrElse(value != null ? value.getClass() : Object.class);
38+
}
39+
3140
public static Option<Result> noResult() {
3241
return none();
3342
}
3443

3544
public String toString(boolean canonical) {
36-
Class<?> type = extractType(value.getClass());
37-
return (canonical ? type.getCanonicalName() : type.getSimpleName()) + " " + key + " = " + renderValue(value);
45+
return (canonical ? type().getCanonicalName() : type().getSimpleName()) + " " + key + " = " + renderValue(value);
3846
}
3947

4048
@Override
@@ -59,15 +67,23 @@ public static final class functions {
5967
public static Mapper<Result, String> key() {
6068
return new Mapper<Result, String>() {
6169
public String call(Result result) throws Exception {
62-
return result.key;
70+
return result.key();
6371
}
6472
};
6573
}
6674

6775
public static Mapper<Result, Object> value() {
6876
return new Mapper<Result, Object>() {
6977
public Object call(Result result) throws Exception {
70-
return result.value;
78+
return result.value();
79+
}
80+
};
81+
}
82+
83+
public static Mapper<Result, Class<?>> type() {
84+
return new Mapper<Result, Class<?>>() {
85+
public Class<?> call(Result result) throws Exception {
86+
return result.type();
7187
}
7288
};
7389
}

src/javarepl/Utils.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ public static String randomIdentifier(String prefix) {
3434
return prefix + "$" + takeFromValues(characters("abcdefghijklmnopqrstuvwxyz1234567890")).take(20).toString("");
3535
}
3636

37+
public static Class<?> extractType(Object object) {
38+
if (object == null) {
39+
return Object.class;
40+
} else {
41+
return extractType(object.getClass());
42+
}
43+
}
44+
3745
public static Class<?> extractType(Class<?> clazz) {
3846
if (clazz.isAnonymousClass()) {
3947
if (clazz.getSuperclass().equals(Object.class)) {
@@ -84,7 +92,6 @@ public static String applicationVersion() {
8492
JarInputStream jarStream = new JarInputStream(new FileInputStream(path));
8593
Manifest manifest = jarStream.getManifest();
8694
return manifest.getMainAttributes().getValue("Implementation-Version");
87-
8895
}
8996
} catch (Exception e) {
9097
// ignore

src/javarepl/console/commands/EvaluateFile.java

-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ public void execute(String expression) {
2626
for (String line : Strings.lines(resolveURL(path).openStream())) {
2727
EvaluateExpression.evaluate(evaluator, logger, line);
2828
}
29-
;
3029

3130
logger.success(format("Finished evaluating %s", path));
3231
} catch (Exception e) {

src/javarepl/expressions/AssignmentWithType.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package javarepl.expressions;
22

33
public final class AssignmentWithType extends Expression {
4-
private final String type;
4+
private final Class<?> type;
55
private final String key;
66
private final String value;
77

8-
public AssignmentWithType(String source, String type, String key, String value) {
8+
public AssignmentWithType(String source, Class<?> type, String key, String value) {
99
super(source);
1010

1111
this.type = type;
@@ -17,7 +17,7 @@ public String key() {
1717
return key;
1818
}
1919

20-
public String type() {
20+
public Class<?> type() {
2121
return type;
2222
}
2323

src/javarepl/rendering/EvaluationClassRenderer.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ private static String render(EvaluationContext context, String className, Expres
140140
private static String renderPreviousEvaluations(EvaluationContext context) {
141141
return context.results().map(new Mapper<Result, String>() {
142142
public String call(Result result) throws Exception {
143-
return format(" public %s %s = valueOf(\"%s\");", extractType(result.value().getClass()).getCanonicalName(), result.key(), result.key());
143+
return format(" public %s %s = valueOf(\"%s\");", result.type().getCanonicalName(), result.key(), result.key());
144144
}
145145
}).toString("\n");
146146
}

src/javarepl/rendering/ExpressionTokenRenderer.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ private static String renderExpressionToken(Import expression) {
4848
}
4949

5050
private static String expressionWithValue(String value) {
51-
return expressionWithValue(value, "Object");
51+
return expressionWithValue(value, Object.class);
5252
}
5353

54-
private static String expressionWithValue(String value, String returnType) {
55-
return format(" %s %s =\n\n %s;\n\n return %s;", returnType, EXPRESSION_VALUE, EXPRESSION_TOKEN, EXPRESSION_VALUE);
54+
private static String expressionWithValue(String value, Class<?> returnType) {
55+
return format(" %s %s =\n\n %s;\n\n return %s;", returnType.getCanonicalName(), EXPRESSION_VALUE, EXPRESSION_TOKEN, EXPRESSION_VALUE);
5656
}
5757
}

src/javarepl/rendering/MethodNameRenderer.java

+7-7
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,22 @@ public static String renderMethodName(Expression expression) {
1515

1616
@multimethod
1717
private static String renderMethodName(Statement expression) {
18-
return methodNameWithType("void");
18+
return methodNameWithType(void.class);
1919
}
2020

2121
@multimethod
2222
private static String renderMethodName(Import expression) {
23-
return methodNameWithType("void");
23+
return methodNameWithType(void.class);
2424
}
2525

2626
@multimethod
2727
private static String renderMethodName(Method expression) {
28-
return methodNameWithType("void");
28+
return methodNameWithType(void.class);
2929
}
3030

3131
@multimethod
3232
private static String renderMethodName(Assignment expression) {
33-
return methodNameWithType("Object");
33+
return methodNameWithType(Object.class);
3434
}
3535

3636
@multimethod
@@ -40,10 +40,10 @@ private static String renderMethodName(AssignmentWithType expression) {
4040

4141
@multimethod
4242
private static String renderMethodName(Value expression) {
43-
return methodNameWithType("Object");
43+
return methodNameWithType(Object.class);
4444
}
4545

46-
private static String methodNameWithType(String returnType) {
47-
return format(" public %s evaluate() throws Exception {", returnType);
46+
private static String methodNameWithType(Class<?> returnType) {
47+
return format(" public %s evaluate() throws Exception {", returnType.getCanonicalName());
4848
}
4949
}

test/javarepl/EvaluatorTest.java

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public void shouldEvaluateExpressions() {
4040
assertThat(evaluating("class NewClass {public int field=20;}"), hasNoResult());
4141
assertThat(evaluating("class NewClass {public int field=20;}", "new NewClass().field"), hasResult(20));
4242
assertThat(evaluating("int max(int a, int b) { return a > b ? a : b; }", "max(20, 30)"), hasResult(30));
43+
assertThat(evaluating("String res = null", "res"), hasResult(null));
4344
}
4445

4546
@Test

test/javarepl/console/SimpleConsoleTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public void providesFeedbackAfterEvaluation() {
2727
assertThat(executing("import java.net.*;"), hasLogged(info("Imported java.net.*")));
2828
assertThat(executing("int method(Integer i){return i;}"), hasLogged(info("Created method int method(java.lang.Integer)")));
2929
assertThat(executing("class SomeClass{}"), hasLogged(info("Created type SomeClass")));
30-
assertThat(executing("int i = 42"), hasLogged(info("Integer i = 42")));
30+
assertThat(executing("int i = 42"), hasLogged(info("int i = 42")));
3131
}
3232

3333
@Test
@@ -179,7 +179,7 @@ public void supportsEvaluatingFileWithExpressions() {
179179
hasLogged(
180180
info("Created method void method()"),
181181
info("Hello world"),
182-
info("Integer num = 42"),
182+
info("int num = 42"),
183183
info("Finished evaluating " + path)));
184184
}
185185

test/javarepl/rendering/EvaluationClassRendererTest.java

+7-7
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ public void rendersTemplateForValue() {
4343
.append(" public AClass(javarepl.EvaluationContext context) { super(context); }\n")
4444
.append("\n")
4545
.append("\n")
46-
.append(" public Object evaluate() throws Exception {\n")
47-
.append(" Object $JAVAREPL_EXPRESSION_VALUE$ =\n")
46+
.append(" public java.lang.Object evaluate() throws Exception {\n")
47+
.append(" java.lang.Object $JAVAREPL_EXPRESSION_VALUE$ =\n")
4848
.append("\n")
4949
.append(" $JAVAREPL_EXPRESSION_TOKEN$;\n")
5050
.append("\n")
@@ -65,8 +65,8 @@ public void rendersTemplateForAssignment() {
6565
.append(" public AClass(javarepl.EvaluationContext context) { super(context); }\n")
6666
.append("\n")
6767
.append("\n")
68-
.append(" public Object evaluate() throws Exception {\n")
69-
.append(" Object $JAVAREPL_EXPRESSION_VALUE$ =\n")
68+
.append(" public java.lang.Object evaluate() throws Exception {\n")
69+
.append(" java.lang.Object $JAVAREPL_EXPRESSION_VALUE$ =\n")
7070
.append("\n")
7171
.append(" $JAVAREPL_EXPRESSION_TOKEN$;\n")
7272
.append("\n")
@@ -78,7 +78,7 @@ public void rendersTemplateForAssignment() {
7878

7979
@Test
8080
public void rendersTemplateForAssignmentWithType() {
81-
assertThat(renderExpressionClass(evaluationContext(), "AClass", new AssignmentWithType("someAssignmentWithType", "SomeType", "someKey", "someValue")), is(new StringBuilder()
81+
assertThat(renderExpressionClass(evaluationContext(), "AClass", new AssignmentWithType("someAssignmentWithType", Object.class, "someKey", "someValue")), is(new StringBuilder()
8282
.append("import java.lang.*;\n")
8383
.append("import java.util.*;\n")
8484
.append("import java.math.*;\n")
@@ -87,8 +87,8 @@ public void rendersTemplateForAssignmentWithType() {
8787
.append(" public AClass(javarepl.EvaluationContext context) { super(context); }\n")
8888
.append("\n")
8989
.append("\n")
90-
.append(" public SomeType evaluate() throws Exception {\n")
91-
.append(" SomeType $JAVAREPL_EXPRESSION_VALUE$ =\n")
90+
.append(" public java.lang.Object evaluate() throws Exception {\n")
91+
.append(" java.lang.Object $JAVAREPL_EXPRESSION_VALUE$ =\n")
9292
.append("\n")
9393
.append(" $JAVAREPL_EXPRESSION_TOKEN$;\n")
9494
.append("\n")

0 commit comments

Comments
 (0)