mirror of
https://github.com/SpencerPark/IJava.git
synced 2025-04-19 21:06:08 +00:00
Compare commits
34 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a03ad7712a | ||
![]() |
554c9a0339 | ||
![]() |
3975e47f7f | ||
![]() |
d6695414d8 | ||
![]() |
3ea6536b82 | ||
![]() |
c594285afa | ||
![]() |
c99a809dc3 | ||
![]() |
a920ff3a27 | ||
![]() |
fe14b7313e | ||
![]() |
8e7b6b62b1 | ||
![]() |
4ceadf40df | ||
![]() |
8d030b040c | ||
![]() |
7e0da04cfe | ||
![]() |
ed1c8fe1df | ||
![]() |
b5945808c6 | ||
![]() |
3171f924d0 | ||
![]() |
ef44f8f900 | ||
![]() |
4d1ee69e39 | ||
![]() |
98386ad554 | ||
![]() |
853e101934 | ||
![]() |
47eaf7b2db | ||
![]() |
ee389feb5a | ||
![]() |
2f166ae7fb | ||
![]() |
2cb4fbbf51 | ||
![]() |
e4063112fe | ||
![]() |
b565c8806c | ||
![]() |
992ed21fe5 | ||
![]() |
2ad58cadc9 | ||
![]() |
99d916fa58 | ||
![]() |
d29537d3d3 | ||
![]() |
9de170663f | ||
![]() |
9187e781cb | ||
![]() |
110400f687 | ||
![]() |
32974b3f39 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
.gradle/
|
||||
.idea/
|
||||
build/
|
||||
build/
|
||||
out/
|
23
build.gradle
23
build.gradle
@ -2,7 +2,7 @@ plugins {
|
||||
id 'java'
|
||||
id 'maven-publish'
|
||||
id('com.github.hierynomus.license') version '0.14.0'
|
||||
id('io.github.spencerpark.jupyter-kernel-installer') version '1.1.8'
|
||||
id('io.github.spencerpark.jupyter-kernel-installer') version '2.1.0'
|
||||
id('com.github.jk1.dependency-license-report')
|
||||
}
|
||||
|
||||
@ -12,10 +12,11 @@ import com.github.jk1.license.filter.*
|
||||
import io.github.spencerpark.gradle.*
|
||||
|
||||
group = 'io.github.spencerpark'
|
||||
version = '1.1.1'
|
||||
version = '1.3.0'
|
||||
|
||||
wrapper {
|
||||
gradleVersion = '4.8.1'
|
||||
distributionType = Wrapper.DistributionType.ALL
|
||||
}
|
||||
|
||||
// Add the license header to source files
|
||||
@ -59,16 +60,19 @@ configurations {
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = 'https://oss.sonatype.org/content/repositories/snapshots/'
|
||||
}
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
shade group: 'io.github.spencerpark', name: 'jupyter-jvm-basekernel', version: '2.2.2-SNAPSHOT'
|
||||
shade group: 'org.jboss.shrinkwrap.resolver', name: 'shrinkwrap-resolver-impl-maven', version: '3.1.3'
|
||||
shade group: 'io.github.spencerpark', name: 'jupyter-jvm-basekernel', version: '2.3.0'
|
||||
|
||||
shade group: 'org.apache.ivy', name: 'ivy', version: '2.5.0-rc1'
|
||||
//shade group: 'org.apache.maven', name: 'maven-settings-builder', version: '3.6.0'
|
||||
shade group: 'org.apache.maven', name: 'maven-model-builder', version: '3.6.0'
|
||||
|
||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||
}
|
||||
@ -108,36 +112,35 @@ jupyter {
|
||||
|
||||
kernelParameters {
|
||||
list('classpath', 'IJAVA_CLASSPATH') {
|
||||
defaultValue = ''
|
||||
separator = PATH_SEPARATOR
|
||||
description = '''A file path separator delimited list of classpath entries that should be available to the user code. **Important:** no matter what OS, this should use forward slash "/" as the file separator. Also each path may actually be a simple glob.'''
|
||||
}
|
||||
|
||||
list('comp-opts', 'IJAVA_COMPILER_OPTS') {
|
||||
defaultValue = ''
|
||||
separator = ' '
|
||||
description = '''A space delimited list of command line options that would be passed to the `javac` command when compiling a project. For example `-parameters` to enable retaining parameter names for reflection.'''
|
||||
}
|
||||
|
||||
list('startup-scripts-path', 'IJAVA_STARTUP_SCRIPTS_PATH') {
|
||||
defaultValue = ''
|
||||
separator = PATH_SEPARATOR
|
||||
description = '''A file path seperator delimited list of `.jshell` scripts to run on startup. This includes ijava-jshell-init.jshell and ijava-display-init.jshell. **Important:** no matter what OS, this should use forward slash "/" as the file separator. Also each path may actually be a simple glob.'''
|
||||
}
|
||||
|
||||
string('startup-script', 'IJAVA_STARTUP_SCRIPT') {
|
||||
defaultValue = ''
|
||||
description = '''A block of java code to run when the kernel starts up. This may be something like `import my.utils;` to setup some default imports or even `void sleep(long time) { try {Thread.sleep(time); } catch (InterruptedException e) { throw new RuntimeException(e); }}` to declare a default utility method to use in the notebook.'''
|
||||
}
|
||||
|
||||
string('timeout', 'IJAVA_TIMEOUT') {
|
||||
aliases NO_TIMEOUT: '-1'
|
||||
defaultValue = '-1'
|
||||
description = '''A duration specifying a timeout (in milliseconds by default) for a _single top level statement_. If less than `1` then there is no timeout. If desired a time may be specified with a `TimeUnit` may be given following the duration number (ex `"30 SECONDS"`).'''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
installKernel {
|
||||
kernelInstallPath = commandLineSpecifiedPath(userInstallPath)
|
||||
}
|
||||
|
||||
zipKernel {
|
||||
installers {
|
||||
with 'python'
|
||||
|
@ -4,21 +4,21 @@ One of the many great things about the Jupyter front ends is the support for [`d
|
||||
|
||||
## Notebook functions
|
||||
|
||||
IJava injects 2 functions into the user space for displaying data: `display` and `render`. Most use cases should prefer the former but there is a necessary case for `render` that is outline below. Both are defined in [ijava-display-init.jshell](/src/main/resources/ijava-display-init.jshell).
|
||||
IJava injects 2 functions into the user space for displaying data: `display` and `render`. Most use cases should prefer the former but there is a necessary case for `render` that is outline below. In addition the `updateDisplay` function can be used to update a previously displayed object. All are defined in the runtime [Display](/src/main/java/io/github/spencerpark/ijava/runtime/Display.java) class.
|
||||
|
||||
All display/render functions include a `text/plain` representation in their output. By default this is the `String.valueOf(Object)` value but it can be overridden.
|
||||
|
||||
### `display(Object o)`
|
||||
### `String display(Object o)`
|
||||
|
||||
Display an object as it's **preferred** types. If you don't want a specific type it is best to let the object decide how it is best represented.
|
||||
|
||||
The object is rendered and published on the display stream.
|
||||
The object is rendered and published on the display stream. An id is returned which can be used to `updateDisplay` if desired.
|
||||
|
||||
### `display(Object o, String... as)`
|
||||
### `String display(Object o, String... as)`
|
||||
|
||||
Display an object as the **requested** types. In this case the object attempts to be rendered as the desired mime types given in `as`. No promises though, if a type is unsupported it will simply not appear in the output.
|
||||
|
||||
The object is rendered and published on the display stream.
|
||||
The object is rendered and published on the display stream. An id is returned which can be used to `updateDisplay` if desired.
|
||||
|
||||
This is useful when a type has many potential representations but not all are preferred. For example a `CharSequence` has many representations but only the `text/plain` is preferred. To display it as executable javascript we can use the following:
|
||||
|
||||
@ -26,13 +26,20 @@ This is useful when a type has many potential representations but not all are pr
|
||||
display("alert('Hello from IJava!');", "application/javascript");
|
||||
```
|
||||
|
||||
Since there is the potential that some front ends don't support a given format many can be given and the front end chooses the best.
|
||||
Since there is the potential that some front ends don't support a given format many can be given and the front end chooses the best. For example, to display as html and markdown:
|
||||
|
||||
### `render(Object o)`
|
||||
```java
|
||||
display("<b>Bold</b>", "text/html", "text/markdown");
|
||||
```
|
||||
|
||||
This will trigger a display message with values for `text/html`, `text/markdown`, and the implicit `text/plain`.
|
||||
|
||||
|
||||
### `DisplayData render(Object o)`
|
||||
|
||||
Renders an object as it's **preferred** types and returns it's rendered format. Similar to `display(Object o)` but without publishing the result.
|
||||
|
||||
### `render(Object o, String... as)`
|
||||
### `DisplayData render(Object o, String... as)`
|
||||
|
||||
Renders an object as the **requested** types and returns it's rendered format. Similar to `display(Object o, String... as)` but without publishing the result.
|
||||
|
||||
@ -46,4 +53,19 @@ render(md, "text/markdown")
|
||||
|
||||
This will result in the `Out[_]` result to be the pretty `text/markdown` representation rather than the boring `text/plain` representation.
|
||||
|
||||
### `void updateDisplay(String id, Object o)`
|
||||
|
||||
Renders an object as it's **preferred** types and updates an existing display with the given id to contain the new rendered object. Similar to `display(Object o)` but updates an existing displayed object instead of appending a new one.
|
||||
|
||||
### `void updateDisplay(String id, Object o, String... as)`
|
||||
|
||||
Renders an object as it's **requested** types and updates an existing display with the given id to contain the new rendered object. Similar to `display(Object o, String... as)` but updates an existing displayed object instead of appending a new one.
|
||||
|
||||
```java
|
||||
String id = display("<b>Countdown:</b> 3", "text/html");
|
||||
for (int i = 3; i >= 0; i--) {
|
||||
updateDisplay(id, "<b>Countdown:</b> " + i, "text/html");
|
||||
Thread.sleep(1000L);
|
||||
}
|
||||
render("<b>Liftoff!</b>", "text/html")
|
||||
```
|
22
docs/kernel.md
Normal file
22
docs/kernel.md
Normal file
@ -0,0 +1,22 @@
|
||||
# Kernel
|
||||
|
||||
All code running in IJava flows through the kernel. This makes it the place to register magics, add things to the classpath, and perform many jupyter related operations.
|
||||
|
||||
## Notebook functions
|
||||
|
||||
IJava injects a function for getting the active kernel instance and additional helpers for making use of the kernel at runtime. These are defined in the runtime [Kernel](/src/main/java/io/github/spencerpark/ijava/runtime/Kernel.java) class.
|
||||
|
||||
### `JavaKernel getKernelInstance()`
|
||||
|
||||
Get a reference to the current kernel. It may return null if called outside of a kernel context but should be considered `@NonNull` when inside a notebook or similar. The kernel api has lots of goodies, look at the [JavaKernel](/src/main/java/io/github/spencerpark/ijava/runtime/Kernel.java) class for more information. Specifically there is access to adding to the classpath, getting the magics registry and maven resolver, and access to eval.
|
||||
|
||||
### `Object eval(String expr) throws Exception`
|
||||
|
||||
The `eval` function provides full access to the code evaluation mechanism of the kernel. It evaluates the code in the _same_ scope as the kernel and **returns an object**. This object is an object that lives in the kernel!
|
||||
|
||||
The given expression can be anything you would write in a cell, including magics.
|
||||
|
||||
```java
|
||||
(int) eval("1 + 2") + 3
|
||||
```
|
||||
|
@ -44,7 +44,18 @@ Add jars to the notebook classpath.
|
||||
###### Line magic
|
||||
|
||||
* **arguments**:
|
||||
* _varargs_ list of simple glob paths to jars on the local file system
|
||||
* _varargs_ list of simple glob paths to jars on the local file system. If a glob matches a directory all files in that directory will be added.
|
||||
|
||||
|
||||
|
||||
### classpath
|
||||
|
||||
Add entries to the notebook classpath.
|
||||
|
||||
###### Line magic
|
||||
|
||||
* **arguments**:
|
||||
* _varargs_ list of simple glob paths to entries on the local file system. This includes directories or jars.
|
||||
|
||||
|
||||
|
||||
|
@ -46,9 +46,6 @@ public class IJava {
|
||||
public static final String STARTUP_SCRIPT_KEY = "IJAVA_STARTUP_SCRIPT";
|
||||
|
||||
public static final String DEFAULT_SHELL_INIT_RESOURCE_PATH = "ijava-jshell-init.jshell";
|
||||
public static final String MAGICS_INIT_RESOURCE_PATH = "ijava-magics-init.jshell";
|
||||
public static final String DISPLAY_INIT_RESOURCE_PATH = "ijava-display-init.jshell";
|
||||
public static final String EVAL_INIT_RESOURCE_PATH = "ijava-eval-init.jshell";
|
||||
|
||||
public static final String VERSION;
|
||||
|
||||
|
@ -24,30 +24,38 @@
|
||||
package io.github.spencerpark.ijava;
|
||||
|
||||
import io.github.spencerpark.ijava.execution.*;
|
||||
import io.github.spencerpark.ijava.magics.ClasspathMagics;
|
||||
import io.github.spencerpark.ijava.magics.MavenResolver;
|
||||
import io.github.spencerpark.jupyter.kernel.BaseKernel;
|
||||
import io.github.spencerpark.jupyter.kernel.LanguageInfo;
|
||||
import io.github.spencerpark.jupyter.kernel.ReplacementOptions;
|
||||
import io.github.spencerpark.jupyter.kernel.display.DisplayData;
|
||||
import io.github.spencerpark.jupyter.kernel.magic.*;
|
||||
import io.github.spencerpark.jupyter.kernel.magic.registry.Magics;
|
||||
import io.github.spencerpark.jupyter.kernel.magic.common.Load;
|
||||
import io.github.spencerpark.jupyter.kernel.util.CharPredicate;
|
||||
import io.github.spencerpark.jupyter.kernel.util.GlobFinder;
|
||||
import io.github.spencerpark.jupyter.kernel.util.StringStyler;
|
||||
import io.github.spencerpark.jupyter.kernel.util.TextColor;
|
||||
import io.github.spencerpark.jupyter.messages.Header;
|
||||
import jdk.jshell.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
public class JavaKernel extends BaseKernel {
|
||||
public static String completeCodeSignifier() {
|
||||
return BaseKernel.IS_COMPLETE_YES;
|
||||
}
|
||||
|
||||
public static String invalidCodeSignifier() {
|
||||
return BaseKernel.IS_COMPLETE_BAD;
|
||||
}
|
||||
|
||||
public static String maybeCompleteCodeSignifier() {
|
||||
return BaseKernel.IS_COMPLETE_MAYBE;
|
||||
}
|
||||
|
||||
private static final CharPredicate IDENTIFIER_CHAR = CharPredicate.builder()
|
||||
.inRange('a', 'z')
|
||||
.inRange('A', 'Z')
|
||||
@ -73,9 +81,6 @@ public class JavaKernel extends BaseKernel {
|
||||
.addClasspathFromString(System.getenv(IJava.CLASSPATH_KEY))
|
||||
.compilerOptsFromString(System.getenv(IJava.COMPILER_OPTS_KEY))
|
||||
.startupScript(IJava.resource(IJava.DEFAULT_SHELL_INIT_RESOURCE_PATH))
|
||||
.startupScript(IJava.resource(IJava.MAGICS_INIT_RESOURCE_PATH))
|
||||
.startupScript(IJava.resource(IJava.DISPLAY_INIT_RESOURCE_PATH))
|
||||
.startupScript(IJava.resource(IJava.EVAL_INIT_RESOURCE_PATH))
|
||||
.startupScriptFiles(System.getenv(IJava.STARTUP_SCRIPTS_KEY))
|
||||
.startupScript(System.getenv(IJava.STARTUP_SCRIPT_KEY))
|
||||
.timeoutFromString(System.getenv(IJava.TIMEOUT_DURATION_KEY))
|
||||
@ -88,26 +93,13 @@ public class JavaKernel extends BaseKernel {
|
||||
this.magicsTransformer = new MagicsSourceTransformer();
|
||||
this.magics = new Magics();
|
||||
this.magics.registerMagics(this.mavenResolver);
|
||||
this.magics.registerLineMagic("jars", args -> {
|
||||
List<String> jars = args.stream()
|
||||
.map(GlobFinder::new)
|
||||
.flatMap(g -> {
|
||||
try {
|
||||
return StreamSupport.stream(g.computeMatchingPaths().spliterator(), false);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Exception resolving jar glob", e);
|
||||
}
|
||||
})
|
||||
.map(p -> p.toAbsolutePath().toString())
|
||||
.collect(Collectors.toList());
|
||||
jars.forEach(this::addToClasspath);
|
||||
return jars;
|
||||
});
|
||||
this.magics.registerMagics(new ClasspathMagics(this::addToClasspath));
|
||||
this.magics.registerMagics(new Load(List.of(".jsh", ".jshell", ".java", ".ijava"), this::eval));
|
||||
|
||||
this.languageInfo = new LanguageInfo.Builder("Java")
|
||||
.version(Runtime.version().toString())
|
||||
.mimetype("text/x-java-source")
|
||||
.fileExtension(".java")
|
||||
.fileExtension(".jshell")
|
||||
.pygments("java")
|
||||
.codemirror("java")
|
||||
.build();
|
||||
@ -169,8 +161,12 @@ public class JavaKernel extends BaseKernel {
|
||||
return formatIncompleteSourceException((IncompleteSourceException) e);
|
||||
} else if (e instanceof EvalException) {
|
||||
return formatEvalException((EvalException) e);
|
||||
} else if (e instanceof UnresolvedReferenceException) {
|
||||
return formatUnresolvedReferenceException(((UnresolvedReferenceException) e));
|
||||
} else if (e instanceof EvaluationTimeoutException) {
|
||||
return formatEvaluationTimeoutException((EvaluationTimeoutException) e);
|
||||
} else if (e instanceof EvaluationInterruptedException) {
|
||||
return formatEvaluationInterruptedException((EvaluationInterruptedException) e);
|
||||
} else {
|
||||
fmt.addAll(super.formatError(e));
|
||||
}
|
||||
@ -184,8 +180,12 @@ public class JavaKernel extends BaseKernel {
|
||||
Snippet snippet = event.snippet();
|
||||
this.evaluator.getShell().diagnostics(snippet)
|
||||
.forEach(d -> {
|
||||
fmt.addAll(this.errorStyler.highlightSubstringLines(snippet.source(),
|
||||
(int) d.getStartPosition(), (int) d.getEndPosition()));
|
||||
// If has line information related, highlight that span
|
||||
if (d.getStartPosition() >= 0 && d.getEndPosition() >= 0)
|
||||
fmt.addAll(this.errorStyler.highlightSubstringLines(snippet.source(),
|
||||
(int) d.getStartPosition(), (int) d.getEndPosition()));
|
||||
else
|
||||
fmt.addAll(this.errorStyler.primaryLines(snippet.source()));
|
||||
|
||||
// Add the error message
|
||||
for (String line : StringStyler.splitLines(d.getMessage(null))) {
|
||||
@ -233,6 +233,23 @@ public class JavaKernel extends BaseKernel {
|
||||
return fmt;
|
||||
}
|
||||
|
||||
private List<String> formatUnresolvedReferenceException(UnresolvedReferenceException e) {
|
||||
List<String> fmt = new ArrayList<>();
|
||||
|
||||
DeclarationSnippet snippet = e.getSnippet();
|
||||
|
||||
List<String> unresolvedDependencies = this.evaluator.getShell().unresolvedDependencies(snippet)
|
||||
.collect(Collectors.toList());
|
||||
if (!unresolvedDependencies.isEmpty()) {
|
||||
fmt.addAll(this.errorStyler.primaryLines(snippet.source()));
|
||||
fmt.add(this.errorStyler.secondary("Unresolved dependencies:"));
|
||||
unresolvedDependencies.forEach(dep ->
|
||||
fmt.add(this.errorStyler.secondary(" - " + dep)));
|
||||
}
|
||||
|
||||
return fmt;
|
||||
}
|
||||
|
||||
private List<String> formatEvaluationTimeoutException(EvaluationTimeoutException e) {
|
||||
List<String> fmt = new ArrayList<>(this.errorStyler.primaryLines(e.getSource()));
|
||||
|
||||
@ -245,6 +262,14 @@ public class JavaKernel extends BaseKernel {
|
||||
return fmt;
|
||||
}
|
||||
|
||||
private List<String> formatEvaluationInterruptedException(EvaluationInterruptedException e) {
|
||||
List<String> fmt = new ArrayList<>(this.errorStyler.primaryLines(e.getSource()));
|
||||
|
||||
fmt.add(this.errorStyler.secondary("Evaluation interrupted."));
|
||||
|
||||
return fmt;
|
||||
}
|
||||
|
||||
public Object evalRaw(String expr) throws Exception {
|
||||
expr = this.magicsTransformer.transformMagics(expr);
|
||||
|
||||
@ -326,16 +351,21 @@ public class JavaKernel extends BaseKernel {
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new ReplacementOptions(options, replaceStart[0], at + 1);
|
||||
return new ReplacementOptions(options, replaceStart[0], at);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String isComplete(String code) {
|
||||
return super.isComplete(code);
|
||||
return this.evaluator.isComplete(code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShutdown(boolean isRestarting) {
|
||||
this.evaluator.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interrupt() {
|
||||
this.evaluator.interrupt();
|
||||
}
|
||||
}
|
||||
|
@ -1,270 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 Spencer Park
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package io.github.spencerpark.ijava;
|
||||
|
||||
import io.github.spencerpark.jupyter.kernel.magic.registry.CellMagic;
|
||||
import io.github.spencerpark.jupyter.kernel.magic.registry.LineMagic;
|
||||
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
|
||||
import org.jboss.shrinkwrap.resolver.api.maven.ConfigurableMavenResolverSystem;
|
||||
import org.jboss.shrinkwrap.resolver.api.maven.Maven;
|
||||
import org.jboss.shrinkwrap.resolver.api.maven.PomEquippedResolveStage;
|
||||
import org.jboss.shrinkwrap.resolver.api.maven.ScopeType;
|
||||
import org.jboss.shrinkwrap.resolver.api.maven.repository.MavenRemoteRepositories;
|
||||
import org.jboss.shrinkwrap.resolver.api.maven.repository.MavenRemoteRepository;
|
||||
import org.jboss.shrinkwrap.resolver.api.maven.strategy.TransitiveStrategy;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.*;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class MavenResolver {
|
||||
private final Consumer<String> addToClasspath;
|
||||
private final List<MavenRemoteRepository> repos;
|
||||
|
||||
public MavenResolver(Consumer<String> addToClasspath) {
|
||||
this.addToClasspath = addToClasspath;
|
||||
this.repos = new LinkedList<>();
|
||||
}
|
||||
|
||||
public void addRemoteRepo(String name, String url) {
|
||||
this.repos.add(MavenRemoteRepositories.createRemoteRepository(name, url, "default"));
|
||||
}
|
||||
|
||||
public void addRemoteRepo(String name, URL url) {
|
||||
this.repos.add(MavenRemoteRepositories.createRemoteRepository(name, url, "default"));
|
||||
}
|
||||
|
||||
public List<File> resolveMavenDependency(String canonical) {
|
||||
ConfigurableMavenResolverSystem resolver = Maven.configureResolver()
|
||||
.withClassPathResolution(true)
|
||||
.withMavenCentralRepo(true);
|
||||
|
||||
this.repos.forEach(resolver::withRemoteRepo);
|
||||
|
||||
return resolver.resolve(canonical)
|
||||
.using(TransitiveStrategy.INSTANCE)
|
||||
.asList(File.class);
|
||||
}
|
||||
|
||||
private String solidifyPartialPOM(String rawIn) throws ParserConfigurationException, IOException, SAXException, TransformerException {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setValidating(false);
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
|
||||
// Wrap in a dummy tag to allow fragments
|
||||
InputStream inStream = new SequenceInputStream(Collections.enumeration(Arrays.asList(
|
||||
new ByteArrayInputStream("<ijava>".getBytes(Charset.forName("utf-8"))),
|
||||
new ByteArrayInputStream(rawIn.getBytes(Charset.forName("utf-8"))),
|
||||
new ByteArrayInputStream("</ijava>".getBytes(Charset.forName("utf-8")))
|
||||
)));
|
||||
|
||||
Document doc = builder.parse(inStream);
|
||||
NodeList rootChildren = doc.getDocumentElement().getChildNodes();
|
||||
|
||||
// If input was a single "project" tag then we don't touch it. It is assumed
|
||||
// to be complete.
|
||||
if (rootChildren.getLength() == 1 && "project".equalsIgnoreCase(rootChildren.item(0).getNodeName()))
|
||||
return this.writeDOM(new DOMSource(rootChildren.item(0)));
|
||||
|
||||
// Put the pieces together and fill in the blanks.
|
||||
Document fixed = builder.newDocument();
|
||||
|
||||
Node project = fixed.appendChild(fixed.createElement("project"));
|
||||
|
||||
Node dependencies = project.appendChild(fixed.createElement("dependencies"));
|
||||
Node repositories = project.appendChild(fixed.createElement("repositories"));
|
||||
|
||||
boolean setModelVersion = false;
|
||||
boolean setGroupId = false;
|
||||
boolean setArtifactId = false;
|
||||
boolean setVersion = false;
|
||||
|
||||
for (int i = 0; i < rootChildren.getLength(); i++) {
|
||||
Node child = rootChildren.item(i);
|
||||
|
||||
switch (child.getNodeName()) {
|
||||
case "modelVersion":
|
||||
setModelVersion = true;
|
||||
this.appendChildInNewDoc(child, fixed, project);
|
||||
break;
|
||||
case "groupId":
|
||||
setGroupId = true;
|
||||
this.appendChildInNewDoc(child, fixed, project);
|
||||
break;
|
||||
case "artifactId":
|
||||
setArtifactId = true;
|
||||
this.appendChildInNewDoc(child, fixed, project);
|
||||
break;
|
||||
case "version":
|
||||
setVersion = true;
|
||||
this.appendChildInNewDoc(child, fixed, project);
|
||||
break;
|
||||
case "dependency":
|
||||
this.appendChildInNewDoc(child, fixed, dependencies);
|
||||
break;
|
||||
case "repository":
|
||||
this.appendChildInNewDoc(child, fixed, repositories);
|
||||
break;
|
||||
case "dependencies":
|
||||
// Add all dependencies to the collecting tag
|
||||
NodeList dependencyChildren = child.getChildNodes();
|
||||
for (int j = 0; j < dependencyChildren.getLength(); j++)
|
||||
this.appendChildInNewDoc(dependencyChildren.item(j), fixed, dependencies);
|
||||
break;
|
||||
case "repositories":
|
||||
// Add all repositories to the collecting tag
|
||||
NodeList repositoryChildren = child.getChildNodes();
|
||||
for (int j = 0; j < repositoryChildren.getLength(); j++)
|
||||
this.appendChildInNewDoc(repositoryChildren.item(j), fixed, repositories);
|
||||
break;
|
||||
default:
|
||||
this.appendChildInNewDoc(child, fixed, project);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!setModelVersion) {
|
||||
Node modelVersion = project.appendChild(fixed.createElement("modelVersion"));
|
||||
modelVersion.setTextContent("4.0.0");
|
||||
}
|
||||
|
||||
if (!setGroupId) {
|
||||
Node groupId = project.appendChild(fixed.createElement("groupId"));
|
||||
groupId.setTextContent("ijava.notebook");
|
||||
}
|
||||
|
||||
if (!setArtifactId) {
|
||||
Node artifactId = project.appendChild(fixed.createElement("artifactId"));
|
||||
artifactId.setTextContent("cell");
|
||||
}
|
||||
|
||||
if (!setVersion) {
|
||||
Node version = project.appendChild(fixed.createElement("version"));
|
||||
version.setTextContent("1");
|
||||
}
|
||||
|
||||
return this.writeDOM(new DOMSource(fixed));
|
||||
}
|
||||
|
||||
private void appendChildInNewDoc(Node oldNode, Document doc, Node newParent) {
|
||||
Node newNode = oldNode.cloneNode(true);
|
||||
doc.adoptNode(newNode);
|
||||
newParent.appendChild(newNode);
|
||||
}
|
||||
|
||||
private String writeDOM(Source src) throws TransformerException, UnsupportedEncodingException {
|
||||
Transformer idTransformer = TransformerFactory.newInstance().newTransformer();
|
||||
idTransformer.setOutputProperty(OutputKeys.INDENT, "yes");
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Result dest = new StreamResult(out);
|
||||
|
||||
idTransformer.transform(src, dest);
|
||||
|
||||
return out.toString("utf-8");
|
||||
}
|
||||
|
||||
public void addJarsToClasspath(Iterable<String> jars) {
|
||||
jars.forEach(this.addToClasspath);
|
||||
}
|
||||
|
||||
@LineMagic(aliases = { "addMavenDependency", "maven" })
|
||||
public void addMavenDependencies(List<String> args) {
|
||||
for (String arg : args) {
|
||||
this.addJarsToClasspath(
|
||||
this.resolveMavenDependency(arg).stream()
|
||||
.map(File::getAbsolutePath)
|
||||
::iterator
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@LineMagic(aliases = { "mavenRepo" })
|
||||
public void addMavenRepo(List<String> args) {
|
||||
if (args.size() != 2)
|
||||
throw new IllegalArgumentException("Expected 2 arguments: repository id and url. Got: " + args);
|
||||
|
||||
String id = args.get(0);
|
||||
String url = args.get(1);
|
||||
|
||||
this.addRemoteRepo(id, url);
|
||||
}
|
||||
|
||||
@CellMagic
|
||||
public void loadFromPOM(List<String> args, String body) throws Exception {
|
||||
try {
|
||||
Path tempPom = Files.createTempFile("ijava-maven-", ".pom");
|
||||
String rawPom = this.solidifyPartialPOM(body);
|
||||
Files.write(tempPom, rawPom.getBytes(Charset.forName("utf-8")));
|
||||
|
||||
List<String> loadArgs = new ArrayList<>(args.size() + 1);
|
||||
loadArgs.add(tempPom.toAbsolutePath().toString());
|
||||
loadArgs.addAll(args);
|
||||
|
||||
this.loadFromPOM(loadArgs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@LineMagic
|
||||
public void loadFromPOM(List<String> args) {
|
||||
if (args.isEmpty())
|
||||
throw new IllegalArgumentException("Loading from POM requires at least the path to the POM file");
|
||||
|
||||
String pomFile = args.get(0);
|
||||
List<String> scopes = args.subList(1, args.size());
|
||||
|
||||
PomEquippedResolveStage stage = Maven.resolver().loadPomFromFile(pomFile);
|
||||
|
||||
if (scopes.isEmpty())
|
||||
stage = stage.importCompileAndRuntimeDependencies();
|
||||
else
|
||||
stage = stage.importDependencies(scopes.stream().map(ScopeType::fromScopeType).toArray(ScopeType[]::new));
|
||||
|
||||
this.addJarsToClasspath(
|
||||
stage.resolve()
|
||||
.withTransitivity()
|
||||
.asList(File.class).stream()
|
||||
.map(File::getAbsolutePath)
|
||||
::iterator
|
||||
);
|
||||
}
|
||||
}
|
@ -23,11 +23,17 @@
|
||||
*/
|
||||
package io.github.spencerpark.ijava.execution;
|
||||
|
||||
import io.github.spencerpark.ijava.JavaKernel;
|
||||
import jdk.jshell.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class CodeEvaluator {
|
||||
private static final Pattern WHITESPACE_PREFIX = Pattern.compile("(?:^|\r?\n)(?<ws>\\s*).*$");
|
||||
private static final Pattern LAST_LINE = Pattern.compile("(?:^|\r?\n)(?<last>.*)$");
|
||||
|
||||
private static final String NO_MAGIC_RETURN = "\"__NO_MAGIC_RETURN\"";
|
||||
|
||||
private final JShell shell;
|
||||
@ -38,6 +44,8 @@ public class CodeEvaluator {
|
||||
private boolean isInitialized = false;
|
||||
private final List<String> startupScripts;
|
||||
|
||||
private final String indentation = " ";
|
||||
|
||||
public CodeEvaluator(JShell shell, IJavaExecutionControlProvider executionControlProvider, String executionControlID, List<String> startupScripts) {
|
||||
this.shell = shell;
|
||||
this.executionControlProvider = executionControlProvider;
|
||||
@ -75,8 +83,16 @@ public class CodeEvaluator {
|
||||
String key = event.value();
|
||||
if (key == null) continue;
|
||||
|
||||
Object value = executionControl.takeResult(key);
|
||||
switch (event.snippet().subKind()) {
|
||||
Snippet.SubKind subKind = event.snippet().subKind();
|
||||
|
||||
// Only executable snippets make their way through the machinery we have setup in the
|
||||
// IJavaExecutionControl. Declarations for example simply take their default value without
|
||||
// being executed.
|
||||
Object value = subKind.isExecutable()
|
||||
? executionControl.takeResult(key)
|
||||
: event.value();
|
||||
|
||||
switch (subKind) {
|
||||
case VAR_VALUE_SUBKIND:
|
||||
case OTHER_EXPRESSION_SUBKIND:
|
||||
case TEMP_VAR_EXPRESSION_SUBKIND:
|
||||
@ -93,8 +109,17 @@ public class CodeEvaluator {
|
||||
if (event.causeSnippet() == null) {
|
||||
JShellException e = event.exception();
|
||||
if (e != null) {
|
||||
if (e instanceof EvalException && IJavaExecutionControl.EXECUTION_TIMEOUT_NAME.equals(((EvalException) e).getExceptionClassName()))
|
||||
throw new EvaluationTimeoutException(executionControl.getTimeoutDuration(), executionControl.getTimeoutUnit(), code.trim());
|
||||
if (e instanceof EvalException) {
|
||||
switch (((EvalException) e).getExceptionClassName()) {
|
||||
case IJavaExecutionControl.EXECUTION_TIMEOUT_NAME:
|
||||
throw new EvaluationTimeoutException(executionControl.getTimeoutDuration(), executionControl.getTimeoutUnit(), code.trim());
|
||||
case IJavaExecutionControl.EXECUTION_INTERRUPTED_NAME:
|
||||
throw new EvaluationInterruptedException(code.trim());
|
||||
default:
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
|
||||
@ -127,8 +152,80 @@ public class CodeEvaluator {
|
||||
return lastEvalResult;
|
||||
}
|
||||
|
||||
private String computeIndentation(String partialStatement) {
|
||||
// Find the indentation of the last line
|
||||
Matcher m = WHITESPACE_PREFIX.matcher(partialStatement);
|
||||
String currentIndentation = m.find() ? m.group("ws") : "";
|
||||
|
||||
m = LAST_LINE.matcher(partialStatement);
|
||||
if (!m.find())
|
||||
throw new Error("Pattern broken. Every string should have a last line.");
|
||||
|
||||
// If a brace or paren was opened on the last line and not closed, indent some more.
|
||||
String lastLine = m.group("last");
|
||||
int newlyOpenedBraces = -1;
|
||||
int newlyOpenedParens = -1;
|
||||
for (int i = 0; i < lastLine.length(); i++) {
|
||||
switch (lastLine.charAt(i)) {
|
||||
case '}':
|
||||
// Ignore closing if one has not been opened on this line yet
|
||||
if (newlyOpenedBraces == -1) continue;
|
||||
// Otherwise close an opened one from this line
|
||||
newlyOpenedBraces--;
|
||||
break;
|
||||
case ')':
|
||||
// Same as for braces, but with the parens
|
||||
if (newlyOpenedParens == -1) continue;
|
||||
newlyOpenedParens--;
|
||||
break;
|
||||
case '{':
|
||||
// A brace was opened on this line!
|
||||
// If the first then get out og the -1 special case with an extra addition
|
||||
if (newlyOpenedBraces == -1) newlyOpenedBraces++;
|
||||
newlyOpenedBraces++;
|
||||
break;
|
||||
case '(':
|
||||
if (newlyOpenedParens == -1) newlyOpenedParens++;
|
||||
newlyOpenedParens++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return newlyOpenedBraces > 0 || newlyOpenedParens > 0
|
||||
? currentIndentation + this.indentation
|
||||
: currentIndentation;
|
||||
}
|
||||
|
||||
public String isComplete(String code) {
|
||||
SourceCodeAnalysis.CompletionInfo info = this.sourceAnalyzer.analyzeCompletion(code);
|
||||
while (info.completeness().isComplete())
|
||||
info = analyzeCompletion(info.remaining());
|
||||
|
||||
switch (info.completeness()) {
|
||||
case UNKNOWN:
|
||||
// Unknown means "bad code" and the only way to see if is complete is
|
||||
// to execute it.
|
||||
return JavaKernel.invalidCodeSignifier();
|
||||
case COMPLETE:
|
||||
case COMPLETE_WITH_SEMI:
|
||||
case EMPTY:
|
||||
return JavaKernel.completeCodeSignifier();
|
||||
case CONSIDERED_INCOMPLETE:
|
||||
case DEFINITELY_INCOMPLETE:
|
||||
// Compute the indent of the last line and match it
|
||||
return this.computeIndentation(info.remaining());
|
||||
default:
|
||||
// For completeness, return an "I don't know" if we somehow get down here
|
||||
return JavaKernel.maybeCompleteCodeSignifier();
|
||||
}
|
||||
}
|
||||
|
||||
public void interrupt() {
|
||||
this.shell.stop();
|
||||
IJavaExecutionControl executionControl =
|
||||
this.executionControlProvider.getRegisteredControlByID(this.executionControlID);
|
||||
|
||||
if (executionControl != null)
|
||||
executionControl.interrupt();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
|
@ -30,13 +30,13 @@ import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class CodeEvaluatorBuilder {
|
||||
private static final Pattern PATH_SPLITTER = Pattern.compile(File.pathSeparator, Pattern.LITERAL);
|
||||
private static final Pattern BLANK = Pattern.compile("^\\s*$");
|
||||
private static final int BUFFER_SIZE = 1024;
|
||||
|
||||
private static final OutputStream STDOUT = new LazyOutputStreamDelegate(() -> System.out);
|
||||
@ -59,7 +59,10 @@ public class CodeEvaluatorBuilder {
|
||||
|
||||
public CodeEvaluatorBuilder addClasspathFromString(String classpath) {
|
||||
if (classpath == null) return this;
|
||||
if (BLANK.matcher(classpath).matches()) return this;
|
||||
|
||||
Collections.addAll(this.classpath, PATH_SPLITTER.split(classpath));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -145,6 +148,7 @@ public class CodeEvaluatorBuilder {
|
||||
|
||||
public CodeEvaluatorBuilder startupScriptFiles(String paths) {
|
||||
if (paths == null) return this;
|
||||
if (BLANK.matcher(paths).matches()) return this;
|
||||
|
||||
for (String glob : PATH_SPLITTER.split(paths)) {
|
||||
GlobFinder resolver = new GlobFinder(glob);
|
||||
@ -199,6 +203,8 @@ public class CodeEvaluatorBuilder {
|
||||
.build();
|
||||
|
||||
for (String cp : this.classpath) {
|
||||
if (BLANK.matcher(cp).matches()) continue;
|
||||
|
||||
GlobFinder resolver = new GlobFinder(cp);
|
||||
try {
|
||||
for (Path entry : resolver.computeMatchingPaths())
|
||||
|
@ -0,0 +1,19 @@
|
||||
package io.github.spencerpark.ijava.execution;
|
||||
|
||||
public class EvaluationInterruptedException extends Exception {
|
||||
private final String source;
|
||||
|
||||
public EvaluationInterruptedException(String source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return String.format("Evaluator was interrupted while executing: '%s'",
|
||||
this.source);
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@ package io.github.spencerpark.ijava.execution;
|
||||
|
||||
import jdk.jshell.EvalException;
|
||||
import jdk.jshell.execution.DirectExecutionControl;
|
||||
import jdk.jshell.spi.SPIResolutionException;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
@ -41,10 +42,18 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
public class IJavaExecutionControl extends DirectExecutionControl {
|
||||
/**
|
||||
* A special "class name" for a {@link jdk.jshell.spi.ExecutionControl.UserException} such that it may be
|
||||
* identified after serialization into an {@link jdk.jshell.EvalException} via {@link EvalException#getExceptionClassName()}.
|
||||
* identified after serialization into an {@link jdk.jshell.EvalException} via {@link
|
||||
* EvalException#getExceptionClassName()}.
|
||||
*/
|
||||
public static final String EXECUTION_TIMEOUT_NAME = "Execution Timeout"; // Has spaces to not collide with a class name
|
||||
|
||||
/**
|
||||
* A special "class name" for a {@link jdk.jshell.spi.ExecutionControl.UserException} such that it may be
|
||||
* identified after serialization into an {@link jdk.jshell.EvalException} via {@link
|
||||
* EvalException#getExceptionClassName()}
|
||||
*/
|
||||
public static final String EXECUTION_INTERRUPTED_NAME = "Execution Interrupted";
|
||||
|
||||
private static final Object NULL = new Object();
|
||||
|
||||
private static final AtomicInteger EXECUTOR_THREAD_ID = new AtomicInteger(0);
|
||||
@ -54,6 +63,7 @@ public class IJavaExecutionControl extends DirectExecutionControl {
|
||||
private final long timeoutTime;
|
||||
private final TimeUnit timeoutUnit;
|
||||
|
||||
private final ConcurrentMap<String, Future<Object>> running = new ConcurrentHashMap<>();
|
||||
private final Map<String, Object> results = new ConcurrentHashMap<>();
|
||||
|
||||
public IJavaExecutionControl() {
|
||||
@ -81,17 +91,28 @@ public class IJavaExecutionControl extends DirectExecutionControl {
|
||||
return result == NULL ? null : result;
|
||||
}
|
||||
|
||||
private Object execute(Method doitMethod) throws TimeoutException, Exception {
|
||||
private Object execute(String key, Method doitMethod) throws TimeoutException, Exception {
|
||||
Future<Object> runningTask = this.executor.submit(() -> doitMethod.invoke(null));
|
||||
|
||||
this.running.put(key, runningTask);
|
||||
|
||||
try {
|
||||
if (this.timeoutTime > 0)
|
||||
return runningTask.get(this.timeoutTime, this.timeoutUnit);
|
||||
return runningTask.get();
|
||||
} catch (CancellationException e) {
|
||||
// If canceled this means that stop() was invoked in which case the protocol is to
|
||||
// throw an ExecutionControl.StoppedException.
|
||||
throw new StoppedException();
|
||||
// If canceled this means that stop() or interrupt() was invoked.
|
||||
if (this.executor.isShutdown())
|
||||
// If the executor is shutdown, the situation is the former in which
|
||||
// case the protocol is to throw an ExecutionControl.StoppedException.
|
||||
throw new StoppedException();
|
||||
else
|
||||
// The execution was purposely interrupted.
|
||||
throw new UserException(
|
||||
"Execution interrupted.",
|
||||
EXECUTION_INTERRUPTED_NAME,
|
||||
e.getStackTrace()
|
||||
);
|
||||
} catch (ExecutionException e) {
|
||||
// The execution threw an exception. The actual exception is the cause of the ExecutionException.
|
||||
Throwable cause = e.getCause();
|
||||
@ -101,15 +122,18 @@ public class IJavaExecutionControl extends DirectExecutionControl {
|
||||
}
|
||||
if (cause == null)
|
||||
throw new UserException("null", "Unknown Invocation Exception", e.getStackTrace());
|
||||
else if (cause instanceof SPIResolutionException)
|
||||
throw new ResolutionException(((SPIResolutionException) cause).id(), cause.getStackTrace());
|
||||
else
|
||||
throw new UserException(String.valueOf(cause.getMessage()), String.valueOf(cause.getClass().getName()), cause.getStackTrace());
|
||||
} catch (TimeoutException e) {
|
||||
this.stop();
|
||||
throw new UserException(
|
||||
String.format("Execution timed out after configured timeout of %d %s.", this.timeoutTime, this.timeoutUnit.toString().toLowerCase()),
|
||||
EXECUTION_TIMEOUT_NAME,
|
||||
new StackTraceElement[]{} // The trace is irrelevant because it is in the kernel space not the user space so leave it blank.
|
||||
e.getStackTrace()
|
||||
);
|
||||
} finally {
|
||||
this.running.remove(key, runningTask);
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,12 +149,17 @@ public class IJavaExecutionControl extends DirectExecutionControl {
|
||||
*/
|
||||
@Override
|
||||
protected String invoke(Method doitMethod) throws Exception {
|
||||
Object value = this.execute(doitMethod);
|
||||
String id = UUID.randomUUID().toString();
|
||||
Object value = this.execute(id, doitMethod);
|
||||
this.results.put(id, value);
|
||||
return id;
|
||||
}
|
||||
|
||||
public void interrupt() {
|
||||
this.running.forEach((id, future) ->
|
||||
future.cancel(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws EngineTerminationException, InternalException {
|
||||
this.executor.shutdownNow();
|
||||
|
@ -38,7 +38,7 @@ public class MagicsSourceTransformer {
|
||||
private final MagicParser parser;
|
||||
|
||||
public MagicsSourceTransformer() {
|
||||
this.parser = new MagicParser("%", "%%");
|
||||
this.parser = new MagicParser("(?<=(?:^|=))\\s*%", "%%");
|
||||
}
|
||||
|
||||
public String transformMagics(String source) {
|
||||
|
@ -0,0 +1,56 @@
|
||||
package io.github.spencerpark.ijava.magics;
|
||||
|
||||
import io.github.spencerpark.jupyter.kernel.magic.registry.LineMagic;
|
||||
import io.github.spencerpark.jupyter.kernel.util.GlobFinder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
public class ClasspathMagics {
|
||||
private final Consumer<String> addToClasspath;
|
||||
|
||||
public ClasspathMagics(Consumer<String> addToClasspath) {
|
||||
this.addToClasspath = addToClasspath;
|
||||
}
|
||||
|
||||
@LineMagic
|
||||
public List<String> jars(List<String> args) {
|
||||
List<String> jars = args.stream()
|
||||
.map(GlobFinder::new)
|
||||
.flatMap(g -> {
|
||||
try {
|
||||
return StreamSupport.stream(g.computeMatchingFiles().spliterator(), false);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Exception resolving jar glob", e);
|
||||
}
|
||||
})
|
||||
.map(p -> p.toAbsolutePath().toString())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
jars.forEach(this.addToClasspath);
|
||||
|
||||
return jars;
|
||||
}
|
||||
|
||||
@LineMagic
|
||||
public List<String> classpath(List<String> args) {
|
||||
List<String> paths = args.stream()
|
||||
.map(GlobFinder::new)
|
||||
.flatMap(g -> {
|
||||
try {
|
||||
return StreamSupport.stream(g.computeMatchingPaths().spliterator(), false);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Exception resolving jar glob", e);
|
||||
}
|
||||
})
|
||||
.map(p -> p.toAbsolutePath().toString())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
paths.forEach(this.addToClasspath);
|
||||
|
||||
return paths;
|
||||
}
|
||||
}
|
@ -0,0 +1,524 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 Spencer Park
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package io.github.spencerpark.ijava.magics;
|
||||
|
||||
import io.github.spencerpark.ijava.magics.dependencies.CommonRepositories;
|
||||
import io.github.spencerpark.ijava.magics.dependencies.Maven;
|
||||
import io.github.spencerpark.ijava.magics.dependencies.MavenToIvy;
|
||||
import io.github.spencerpark.jupyter.kernel.magic.registry.CellMagic;
|
||||
import io.github.spencerpark.jupyter.kernel.magic.registry.LineMagic;
|
||||
import io.github.spencerpark.jupyter.kernel.magic.registry.MagicsArgs;
|
||||
import org.apache.ivy.Ivy;
|
||||
import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
|
||||
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
|
||||
import org.apache.ivy.core.module.id.ModuleRevisionId;
|
||||
import org.apache.ivy.core.report.ArtifactDownloadReport;
|
||||
import org.apache.ivy.core.report.ResolveReport;
|
||||
import org.apache.ivy.core.resolve.ResolveOptions;
|
||||
import org.apache.ivy.core.settings.IvySettings;
|
||||
import org.apache.ivy.plugins.parser.m2.PomModuleDescriptorParser;
|
||||
import org.apache.ivy.plugins.repository.url.URLResource;
|
||||
import org.apache.ivy.plugins.resolver.ChainResolver;
|
||||
import org.apache.ivy.plugins.resolver.DependencyResolver;
|
||||
import org.apache.ivy.util.DefaultMessageLogger;
|
||||
import org.apache.ivy.util.Message;
|
||||
import org.apache.ivy.util.MessageLogger;
|
||||
import org.apache.maven.model.Model;
|
||||
import org.apache.maven.model.building.ModelBuildingException;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.*;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import java.io.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.text.ParseException;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MavenResolver {
|
||||
private static final String DEFAULT_RESOLVER_NAME = "default";
|
||||
|
||||
/**
|
||||
* "master" includes the artifact published by the module.
|
||||
* "runtime" includes the dependencies required for the module to run and
|
||||
* extends "compile" which is the dependencies required to compile the module.
|
||||
*/
|
||||
private static final String[] DEFAULT_RESOLVE_CONFS = { "master", "runtime" };
|
||||
|
||||
/**
|
||||
* The ivy artifact type corresponding to a binary artifact for a module.
|
||||
*/
|
||||
private static final String JAR_TYPE = "jar";
|
||||
|
||||
/**
|
||||
* The ivy artifact type corresponding to a source code artifact for a module. This
|
||||
* is still usually a ".jar" file but that corresponds to the "ext" not the "type".
|
||||
*/
|
||||
private static final String SOURCE_TYPE = "source";
|
||||
|
||||
/**
|
||||
* The ivy artifact type corresponding to a javadoc (HTML) artifact for a module. This
|
||||
* is still usually a ".jar" file but that corresponds to the "ext" not the "type".
|
||||
*/
|
||||
private static final String JAVADOC_TYPE = "javadoc";
|
||||
|
||||
private static final Pattern IVY_MRID_PATTERN = Pattern.compile(
|
||||
"^(?<organization>[-\\w/._+=]*)#(?<name>[-\\w/._+=]+)(?:#(?<branch>[-\\w/._+=]+))?;(?<revision>[-\\w/._+=,\\[\\]{}():@]+)$"
|
||||
);
|
||||
private static final Pattern MAVEN_MRID_PATTERN = Pattern.compile(
|
||||
"^(?<group>[^:\\s]+):(?<artifact>[^:\\s]+)(?::(?<packaging>[^:\\s]*)(?::(?<classifier>[^:\\s]+))?)?:(?<version>[^:\\s]+)$"
|
||||
);
|
||||
|
||||
private final Consumer<String> addToClasspath;
|
||||
private final List<DependencyResolver> repos;
|
||||
|
||||
public MavenResolver(Consumer<String> addToClasspath) {
|
||||
this.addToClasspath = addToClasspath;
|
||||
this.repos = new LinkedList<>();
|
||||
this.repos.add(CommonRepositories.mavenCentral());
|
||||
this.repos.add(CommonRepositories.mavenLocal());
|
||||
}
|
||||
|
||||
public void addRemoteRepo(String name, String url) {
|
||||
if (DEFAULT_RESOLVER_NAME.equals(name))
|
||||
throw new IllegalArgumentException("Illegal repository name, cannot use '" + DEFAULT_RESOLVER_NAME + "'.");
|
||||
|
||||
this.repos.add(CommonRepositories.maven(name, url));
|
||||
}
|
||||
|
||||
private ChainResolver searchAllReposResolver(Set<String> repos) {
|
||||
ChainResolver resolver = new ChainResolver();
|
||||
resolver.setName(DEFAULT_RESOLVER_NAME);
|
||||
|
||||
this.repos.stream()
|
||||
.filter(r -> repos == null || repos.contains(r.getName().toLowerCase()))
|
||||
.forEach(resolver::add);
|
||||
|
||||
if (repos != null) {
|
||||
Set<String> resolverNames = resolver.getResolvers().stream()
|
||||
.map(d -> d.getName().toLowerCase())
|
||||
.collect(Collectors.toSet());
|
||||
repos.removeAll(resolverNames);
|
||||
|
||||
repos.forEach(r -> {
|
||||
try {
|
||||
URL url = new URL(r);
|
||||
resolver.add(CommonRepositories.maven("from-" + url.getHost(), r));
|
||||
} catch (MalformedURLException e) {
|
||||
// Ignore as we will assume that a bad url was a name
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return resolver;
|
||||
}
|
||||
|
||||
private static ModuleRevisionId parseCanonicalArtifactName(String canonical) {
|
||||
Matcher m = IVY_MRID_PATTERN.matcher(canonical);
|
||||
if (m.matches()) {
|
||||
return ModuleRevisionId.newInstance(
|
||||
m.group("organization"),
|
||||
m.group("name"),
|
||||
m.group("branch"),
|
||||
m.group("revision")
|
||||
);
|
||||
}
|
||||
|
||||
m = MAVEN_MRID_PATTERN.matcher(canonical);
|
||||
if (m.matches()) {
|
||||
String packaging = m.group("packaging");
|
||||
String classifier = m.group("classifier");
|
||||
|
||||
return ModuleRevisionId.newInstance(
|
||||
m.group("group"),
|
||||
m.group("artifact"),
|
||||
m.group("version"),
|
||||
packaging == null
|
||||
? Collections.emptyMap()
|
||||
: classifier == null
|
||||
? Map.of("ext", packaging)
|
||||
: Map.of("ext", packaging, "m:classifier", classifier)
|
||||
);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Cannot resolve '" + canonical + "' as maven or ivy coordinates.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an ivy instance with the specified verbosity. The instance is relatively plain.
|
||||
*
|
||||
* @param verbosity the verbosity level.
|
||||
* <ol start="0">
|
||||
* <li>ERROR</li>
|
||||
* <li>WANRING</li>
|
||||
* <li>INFO</li>
|
||||
* <li>VERBOSE</li>
|
||||
* <li>DEBUG</li>
|
||||
* </ol>
|
||||
*
|
||||
* @return the fresh ivy instance.
|
||||
*/
|
||||
private Ivy createDefaultIvyInstance(int verbosity) {
|
||||
MessageLogger logger = new DefaultMessageLogger(verbosity);
|
||||
|
||||
// Set the default logger since not all things log to the ivy instance.
|
||||
Message.setDefaultLogger(logger);
|
||||
Ivy ivy = new Ivy();
|
||||
|
||||
ivy.getLoggerEngine().setDefaultLogger(logger);
|
||||
ivy.setSettings(new IvySettings());
|
||||
ivy.bind();
|
||||
|
||||
return ivy;
|
||||
}
|
||||
|
||||
// TODO support multiple at once. This is necessary for conflict resolution with multiple overlapping dependencies.
|
||||
// TODO support classpath resolution
|
||||
public List<File> resolveMavenDependency(String canonical, Set<String> repos, int verbosity) throws IOException, ParseException {
|
||||
ChainResolver rootResolver = this.searchAllReposResolver(repos);
|
||||
|
||||
Ivy ivy = this.createDefaultIvyInstance(verbosity);
|
||||
IvySettings settings = ivy.getSettings();
|
||||
|
||||
settings.addResolver(rootResolver);
|
||||
rootResolver.setCheckmodified(true);
|
||||
settings.setDefaultResolver(rootResolver.getName());
|
||||
|
||||
ivy.getLoggerEngine().info("Searching for dependencies in: " + rootResolver.getResolvers());
|
||||
|
||||
ResolveOptions resolveOptions = new ResolveOptions();
|
||||
resolveOptions.setTransitive(true);
|
||||
resolveOptions.setDownload(true);
|
||||
|
||||
ModuleRevisionId artifactIdentifier = MavenResolver.parseCanonicalArtifactName(canonical);
|
||||
DefaultModuleDescriptor containerModule = DefaultModuleDescriptor.newCallerInstance(
|
||||
artifactIdentifier,
|
||||
DEFAULT_RESOLVE_CONFS,
|
||||
true, // Transitive
|
||||
repos != null // Changing - the resolver will set this based on SNAPSHOT since they are all m2 compatible
|
||||
// but if `repos` is specified, we want to force a lookup.
|
||||
);
|
||||
|
||||
ResolveReport resolved = ivy.resolve(containerModule, resolveOptions);
|
||||
if (resolved.hasError()) {
|
||||
MessageLogger logger = ivy.getLoggerEngine();
|
||||
Arrays.stream(resolved.getAllArtifactsReports())
|
||||
.forEach(r -> {
|
||||
logger.error("download " + r.getDownloadStatus() + ": " + r.getArtifact() + " of " + r.getType());
|
||||
if (r.getArtifactOrigin() == null)
|
||||
logger.error("\tCouldn't find artifact.");
|
||||
else
|
||||
logger.error("\tfrom: " + r.getArtifactOrigin());
|
||||
});
|
||||
|
||||
// TODO better error...
|
||||
throw new RuntimeException("Error resolving '" + canonical + "'. " + resolved.getAllProblemMessages());
|
||||
}
|
||||
|
||||
return Arrays.stream(resolved.getAllArtifactsReports())
|
||||
.filter(a -> JAR_TYPE.equalsIgnoreCase(a.getType()))
|
||||
.map(ArtifactDownloadReport::getLocalFile)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private File convertPomToIvy(Ivy ivy, File pomFile) throws IOException, ParseException {
|
||||
PomModuleDescriptorParser parser = PomModuleDescriptorParser.getInstance();
|
||||
|
||||
URL pomUrl = pomFile.toURI().toURL();
|
||||
|
||||
ModuleDescriptor pomModule = parser.parseDescriptor(new IvySettings(), pomFile.toURI().toURL(), false);
|
||||
|
||||
File tempIvyFile = File.createTempFile("ijava-ivy-", ".xml").getAbsoluteFile();
|
||||
tempIvyFile.deleteOnExit();
|
||||
|
||||
parser.toIvyFile(pomUrl.openStream(), new URLResource(pomUrl), tempIvyFile, pomModule);
|
||||
|
||||
MessageLogger logger = ivy.getLoggerEngine();
|
||||
logger.info(new String(Files.readAllBytes(tempIvyFile.toPath()), Charset.forName("utf8")));
|
||||
|
||||
return tempIvyFile;
|
||||
}
|
||||
|
||||
private void addPomReposToIvySettings(IvySettings settings, File pomFile) throws ModelBuildingException {
|
||||
Model mavenModel = Maven.getInstance().readEffectiveModel(pomFile).getEffectiveModel();
|
||||
ChainResolver pomRepos = MavenToIvy.createChainForModelRepositories(mavenModel);
|
||||
pomRepos.setName(DEFAULT_RESOLVER_NAME);
|
||||
|
||||
settings.addResolver(pomRepos);
|
||||
settings.setDefaultResolver(DEFAULT_RESOLVER_NAME);
|
||||
}
|
||||
|
||||
private List<File> resolveFromIvyFile(Ivy ivy, File ivyFile, List<String> scopes) throws IOException, ParseException {
|
||||
ResolveOptions resolveOptions = new ResolveOptions();
|
||||
resolveOptions.setTransitive(true);
|
||||
resolveOptions.setDownload(true);
|
||||
resolveOptions.setConfs(!scopes.isEmpty()
|
||||
? scopes.toArray(new String[0])
|
||||
: DEFAULT_RESOLVE_CONFS
|
||||
);
|
||||
|
||||
ResolveReport resolved = ivy.resolve(ivyFile, resolveOptions);
|
||||
if (resolved.hasError())
|
||||
// TODO better error...
|
||||
throw new RuntimeException("Error resolving '" + ivyFile + "'. " + resolved.getAllProblemMessages());
|
||||
|
||||
return Arrays.stream(resolved.getAllArtifactsReports())
|
||||
.map(ArtifactDownloadReport::getLocalFile)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
private String solidifyPartialPOM(String rawIn) throws ParserConfigurationException, IOException, SAXException, TransformerException {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setValidating(false);
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
|
||||
// Wrap in a dummy tag to allow fragments
|
||||
InputStream inStream = new SequenceInputStream(Collections.enumeration(Arrays.asList(
|
||||
new ByteArrayInputStream("<ijava>".getBytes(Charset.forName("utf-8"))),
|
||||
new ByteArrayInputStream(rawIn.getBytes(Charset.forName("utf-8"))),
|
||||
new ByteArrayInputStream("</ijava>".getBytes(Charset.forName("utf-8")))
|
||||
)));
|
||||
|
||||
Document doc = builder.parse(inStream);
|
||||
NodeList rootChildren = doc.getDocumentElement().getChildNodes();
|
||||
|
||||
// If input was a single "project" tag then we don't touch it. It is assumed
|
||||
// to be complete.
|
||||
if (rootChildren.getLength() == 1 && "project".equalsIgnoreCase(rootChildren.item(0).getNodeName()))
|
||||
return this.writeDOM(new DOMSource(rootChildren.item(0)));
|
||||
|
||||
// Put the pieces together and fill in the blanks.
|
||||
Document fixed = builder.newDocument();
|
||||
|
||||
Node project = fixed.appendChild(fixed.createElement("project"));
|
||||
|
||||
Node dependencies = project.appendChild(fixed.createElement("dependencies"));
|
||||
Node repositories = project.appendChild(fixed.createElement("repositories"));
|
||||
|
||||
boolean setModelVersion = false;
|
||||
boolean setGroupId = false;
|
||||
boolean setArtifactId = false;
|
||||
boolean setVersion = false;
|
||||
|
||||
for (int i = 0; i < rootChildren.getLength(); i++) {
|
||||
Node child = rootChildren.item(i);
|
||||
|
||||
switch (child.getNodeName()) {
|
||||
case "modelVersion":
|
||||
setModelVersion = true;
|
||||
this.appendChildInNewDoc(child, fixed, project);
|
||||
break;
|
||||
case "groupId":
|
||||
setGroupId = true;
|
||||
this.appendChildInNewDoc(child, fixed, project);
|
||||
break;
|
||||
case "artifactId":
|
||||
setArtifactId = true;
|
||||
this.appendChildInNewDoc(child, fixed, project);
|
||||
break;
|
||||
case "version":
|
||||
setVersion = true;
|
||||
this.appendChildInNewDoc(child, fixed, project);
|
||||
break;
|
||||
case "dependency":
|
||||
this.appendChildInNewDoc(child, fixed, dependencies);
|
||||
break;
|
||||
case "repository":
|
||||
this.appendChildInNewDoc(child, fixed, repositories);
|
||||
break;
|
||||
case "dependencies":
|
||||
// Add all dependencies to the collecting tag
|
||||
NodeList dependencyChildren = child.getChildNodes();
|
||||
for (int j = 0; j < dependencyChildren.getLength(); j++)
|
||||
this.appendChildInNewDoc(dependencyChildren.item(j), fixed, dependencies);
|
||||
break;
|
||||
case "repositories":
|
||||
// Add all repositories to the collecting tag
|
||||
NodeList repositoryChildren = child.getChildNodes();
|
||||
for (int j = 0; j < repositoryChildren.getLength(); j++)
|
||||
this.appendChildInNewDoc(repositoryChildren.item(j), fixed, repositories);
|
||||
break;
|
||||
default:
|
||||
this.appendChildInNewDoc(child, fixed, project);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!setModelVersion) {
|
||||
Node modelVersion = project.appendChild(fixed.createElement("modelVersion"));
|
||||
modelVersion.setTextContent("4.0.0");
|
||||
}
|
||||
|
||||
if (!setGroupId) {
|
||||
Node groupId = project.appendChild(fixed.createElement("groupId"));
|
||||
groupId.setTextContent("ijava.notebook");
|
||||
}
|
||||
|
||||
if (!setArtifactId) {
|
||||
Node artifactId = project.appendChild(fixed.createElement("artifactId"));
|
||||
artifactId.setTextContent("cell");
|
||||
}
|
||||
|
||||
if (!setVersion) {
|
||||
Node version = project.appendChild(fixed.createElement("version"));
|
||||
version.setTextContent("1");
|
||||
}
|
||||
|
||||
return this.writeDOM(new DOMSource(fixed));
|
||||
}
|
||||
|
||||
private void appendChildInNewDoc(Node oldNode, Document doc, Node newParent) {
|
||||
Node newNode = oldNode.cloneNode(true);
|
||||
doc.adoptNode(newNode);
|
||||
newParent.appendChild(newNode);
|
||||
}
|
||||
|
||||
private String writeDOM(Source src) throws TransformerException, UnsupportedEncodingException {
|
||||
Transformer idTransformer = TransformerFactory.newInstance().newTransformer();
|
||||
idTransformer.setOutputProperty(OutputKeys.INDENT, "yes");
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Result dest = new StreamResult(out);
|
||||
|
||||
idTransformer.transform(src, dest);
|
||||
|
||||
return out.toString("utf-8");
|
||||
}
|
||||
|
||||
public void addJarsToClasspath(Iterable<String> jars) {
|
||||
jars.forEach(this.addToClasspath);
|
||||
}
|
||||
|
||||
@LineMagic(aliases = { "addMavenDependency", "maven" })
|
||||
public void addMavenDependencies(List<String> args) {
|
||||
MagicsArgs schema = MagicsArgs.builder()
|
||||
.varargs("deps")
|
||||
.keyword("from")
|
||||
.flag("verbose", 'v')
|
||||
.onlyKnownKeywords()
|
||||
.onlyKnownFlags()
|
||||
.build();
|
||||
|
||||
Map<String, List<String>> vals = schema.parse(args);
|
||||
|
||||
List<String> deps = vals.get("deps");
|
||||
List<String> from = vals.get("from");
|
||||
int verbosity = vals.get("verbose").size();
|
||||
|
||||
Set<String> repos = from.isEmpty() ? null : new LinkedHashSet<>(from);
|
||||
|
||||
|
||||
for (String dep : deps) {
|
||||
try {
|
||||
this.addJarsToClasspath(
|
||||
this.resolveMavenDependency(dep, repos, verbosity).stream()
|
||||
.map(File::getAbsolutePath)
|
||||
::iterator
|
||||
);
|
||||
} catch (IOException | ParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@LineMagic(aliases = { "mavenRepo" })
|
||||
public void addMavenRepo(List<String> args) {
|
||||
MagicsArgs schema = MagicsArgs.builder().required("id").required("url").build();
|
||||
Map<String, List<String>> vals = schema.parse(args);
|
||||
|
||||
String id = vals.get("id").get(0);
|
||||
String url = vals.get("url").get(0);
|
||||
|
||||
this.addRemoteRepo(id, url);
|
||||
}
|
||||
|
||||
@CellMagic
|
||||
public void loadFromPOM(List<String> args, String body) throws Exception {
|
||||
try {
|
||||
File tempPomPath = File.createTempFile("ijava-maven-", ".pom").getAbsoluteFile();
|
||||
tempPomPath.deleteOnExit();
|
||||
|
||||
String rawPom = this.solidifyPartialPOM(body);
|
||||
Files.write(tempPomPath.toPath(), rawPom.getBytes(Charset.forName("utf-8")));
|
||||
|
||||
List<String> loadArgs = new ArrayList<>(args.size() + 1);
|
||||
loadArgs.add(tempPomPath.getAbsolutePath());
|
||||
loadArgs.addAll(args);
|
||||
|
||||
this.loadFromPOM(loadArgs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@LineMagic
|
||||
public void loadFromPOM(List<String> args) {
|
||||
if (args.isEmpty())
|
||||
throw new IllegalArgumentException("Loading from POM requires at least the path to the POM file");
|
||||
|
||||
MagicsArgs schema = MagicsArgs.builder()
|
||||
.required("pomPath")
|
||||
.varargs("scopes")
|
||||
.flag("verbose", 'v')
|
||||
.onlyKnownKeywords().onlyKnownFlags().build();
|
||||
|
||||
Map<String, List<String>> vals = schema.parse(args);
|
||||
|
||||
String pomPath = vals.get("pomPath").get(0);
|
||||
List<String> scopes = vals.get("scopes");
|
||||
int verbosity = vals.get("verbose").size();
|
||||
|
||||
File pomFile = new File(pomPath);
|
||||
try {
|
||||
Ivy ivy = this.createDefaultIvyInstance(verbosity);
|
||||
IvySettings settings = ivy.getSettings();
|
||||
|
||||
File ivyFile = this.convertPomToIvy(ivy, pomFile);
|
||||
|
||||
this.addPomReposToIvySettings(settings, pomFile);
|
||||
|
||||
this.addJarsToClasspath(
|
||||
this.resolveFromIvyFile(ivy, ivyFile, scopes).stream()
|
||||
.map(File::getAbsolutePath)
|
||||
::iterator
|
||||
);
|
||||
} catch (IOException | ParseException | ModelBuildingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package io.github.spencerpark.ijava.magics.dependencies;
|
||||
|
||||
import org.apache.ivy.plugins.resolver.DependencyResolver;
|
||||
import org.apache.ivy.plugins.resolver.IBiblioResolver;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class CommonRepositories {
|
||||
protected static final String MAVEN_PATTERN_PREFIX = "[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier])";
|
||||
protected static final String MAVEN_ARTIFACT_PATTERN = MAVEN_PATTERN_PREFIX + ".[ext]";
|
||||
protected static final String MAVEN_POM_PATTERN = MAVEN_PATTERN_PREFIX + ".pom";
|
||||
|
||||
public static DependencyResolver maven(String name, String urlRaw) {
|
||||
IBiblioResolver resolver = new IBiblioResolver();
|
||||
resolver.setM2compatible(true);
|
||||
resolver.setUseMavenMetadata(true);
|
||||
resolver.setUsepoms(true);
|
||||
|
||||
resolver.setRoot(urlRaw);
|
||||
resolver.setName(name);
|
||||
|
||||
return resolver;
|
||||
}
|
||||
|
||||
public static DependencyResolver mavenCentral() {
|
||||
return CommonRepositories.maven("maven-central", "https://repo.maven.apache.org/maven2/");
|
||||
}
|
||||
|
||||
public static DependencyResolver jcenter() {
|
||||
return CommonRepositories.maven("jcenter", "https://jcenter.bintray.com/");
|
||||
}
|
||||
|
||||
public static DependencyResolver mavenLocal() {
|
||||
IBiblioResolver resolver = new IBiblioResolver();
|
||||
resolver.setM2compatible(true);
|
||||
resolver.setUseMavenMetadata(true);
|
||||
resolver.setUsepoms(true);
|
||||
|
||||
resolver.setName("maven-local");
|
||||
|
||||
Path localRepoPath;
|
||||
try {
|
||||
localRepoPath = Maven.getInstance().getConfiguredLocalRepositoryPath();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Error reading maven settings. " + e.getLocalizedMessage(), e);
|
||||
} catch (SAXException e) {
|
||||
throw new RuntimeException("Error parsing maven settings. " + e.getLocalizedMessage(), e);
|
||||
}
|
||||
|
||||
resolver.setRoot("file:///" + localRepoPath.toString());
|
||||
|
||||
return resolver;
|
||||
}
|
||||
}
|
@ -0,0 +1,214 @@
|
||||
package io.github.spencerpark.ijava.magics.dependencies;
|
||||
|
||||
import org.apache.maven.building.StringSource;
|
||||
import org.apache.maven.model.building.*;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class Maven {
|
||||
private static final Pattern MAVEN_VAR_PATTERN = Pattern.compile("\\$\\{(?<name>[^}*])}");
|
||||
|
||||
private static final Maven INSTANCE = new Maven(new Properties(), Collections.emptyMap());
|
||||
|
||||
public static Maven getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
// User provider environment overrides.
|
||||
private final Properties properties;
|
||||
private final Map<String, String> environment;
|
||||
|
||||
public Maven(Properties properties, Map<String, String> environment) {
|
||||
this.properties = properties;
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
private String getProperty(String name, String def) {
|
||||
String val = this.environment.get(name);
|
||||
if (val != null)
|
||||
return val;
|
||||
|
||||
val = System.getProperty(name);
|
||||
return val != null ? val : def;
|
||||
}
|
||||
|
||||
private String getProperty(String name) {
|
||||
return this.getProperty(name, null);
|
||||
}
|
||||
|
||||
private String getEnv(String name, String def) {
|
||||
String val = this.environment.get(name);
|
||||
if (val != null)
|
||||
return val;
|
||||
|
||||
val = System.getenv(name);
|
||||
return val != null ? val : def;
|
||||
}
|
||||
|
||||
private String getEnv(String name) {
|
||||
return this.getEnv(name, null);
|
||||
}
|
||||
|
||||
public Path getUserSystemHomePath() {
|
||||
String home = this.getProperty("user.home");
|
||||
return Paths.get(home).toAbsolutePath();
|
||||
}
|
||||
|
||||
private String replaceMavenVars(String raw) {
|
||||
StringBuilder replaced = new StringBuilder();
|
||||
|
||||
Matcher matcher = MAVEN_VAR_PATTERN.matcher(raw);
|
||||
while (matcher.find())
|
||||
matcher.appendReplacement(replaced,
|
||||
System.getProperty(matcher.group("name"), ""));
|
||||
|
||||
matcher.appendTail(replaced);
|
||||
|
||||
return replaced.toString();
|
||||
}
|
||||
|
||||
// Thanks gradle!
|
||||
|
||||
private Path getUserHomePath() {
|
||||
return this.getUserSystemHomePath().resolve(".m2");
|
||||
}
|
||||
|
||||
private Path getGlobalHomePath() {
|
||||
String envM2Home = this.getEnv("M2_HOME");
|
||||
return envM2Home != null
|
||||
? Paths.get(envM2Home).toAbsolutePath() : null;
|
||||
}
|
||||
|
||||
private Path getUserSettingsPath() {
|
||||
return this.getUserHomePath().resolve("settings.xml");
|
||||
}
|
||||
|
||||
private Path getGlobalSettingsPath() {
|
||||
Path sysHome = this.getGlobalHomePath();
|
||||
return sysHome != null ? sysHome.resolve("conf").resolve("settings.xml") : null;
|
||||
}
|
||||
|
||||
private Path getDefaultLocalRepoPath() {
|
||||
return this.getUserHomePath().resolve("repository");
|
||||
}
|
||||
|
||||
private Path readConfiguredLocalRepositoryPath(Path settingsXmlPath) throws IOException, SAXException {
|
||||
if (!Files.isRegularFile(settingsXmlPath))
|
||||
return null;
|
||||
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setValidating(false);
|
||||
|
||||
DocumentBuilder builder;
|
||||
try {
|
||||
builder = factory.newDocumentBuilder();
|
||||
} catch (ParserConfigurationException e) {
|
||||
// We are configuring the factory, the configuration will be fine...
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
try (InputStream in = Files.newInputStream(settingsXmlPath)) {
|
||||
Document settingsDoc = builder.parse(in);
|
||||
NodeList settings = settingsDoc.getElementsByTagName("settings");
|
||||
if (settings.getLength() == 0)
|
||||
return null;
|
||||
|
||||
for (int i = 0; i < settings.getLength(); i++) {
|
||||
Node setting = settings.item(i);
|
||||
switch (setting.getNodeName()) {
|
||||
case "localRepository":
|
||||
String localRepository = setting.getTextContent();
|
||||
localRepository = this.replaceMavenVars(localRepository);
|
||||
return Paths.get(localRepository);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO just use the effective settings
|
||||
public Path getConfiguredLocalRepositoryPath() throws IOException, SAXException {
|
||||
Path userSettingsXmlPath = this.getUserSettingsPath();
|
||||
Path path = this.readConfiguredLocalRepositoryPath(userSettingsXmlPath);
|
||||
|
||||
if (path == null) {
|
||||
Path globalSettingsXmlPath = this.getGlobalSettingsPath();
|
||||
if (globalSettingsXmlPath != null)
|
||||
path = this.readConfiguredLocalRepositoryPath(globalSettingsXmlPath);
|
||||
}
|
||||
|
||||
return path == null ? this.getDefaultLocalRepoPath() : path;
|
||||
}
|
||||
|
||||
/*public SettingsBuildingResult readEffectiveSettings() throws SettingsBuildingException {
|
||||
DefaultSettingsBuilder settingsBuilder = new DefaultSettingsBuilderFactory().newInstance();
|
||||
|
||||
SettingsBuildingRequest request = new DefaultSettingsBuildingRequest();
|
||||
|
||||
request.setSystemProperties(System.getProperties());
|
||||
request.setUserProperties(this.properties);
|
||||
|
||||
request.setUserSettingsFile(this.getUserSettingsPath().toFile());
|
||||
|
||||
Path globalSettingsPath = this.getGlobalSettingsPath();
|
||||
request.setGlobalSettingsFile(globalSettingsPath != null ? globalSettingsPath.toFile() : null);
|
||||
|
||||
return settingsBuilder.build(request);
|
||||
}*/
|
||||
|
||||
public ModelBuildingResult readEffectiveModel(CharSequence pom) throws ModelBuildingException {
|
||||
return this.readEffectiveModel(req ->
|
||||
req.setModelSource((ModelSource) new StringSource(pom))
|
||||
);
|
||||
}
|
||||
|
||||
public ModelBuildingResult readEffectiveModel(File pom) throws ModelBuildingException {
|
||||
return this.readEffectiveModel(req ->
|
||||
req.setPomFile(pom)
|
||||
);
|
||||
}
|
||||
|
||||
private ModelBuildingResult readEffectiveModel(Function<ModelBuildingRequest, ModelBuildingRequest> configuration) throws ModelBuildingException {
|
||||
DefaultModelBuilder modelBuilder = new DefaultModelBuilderFactory().newInstance();
|
||||
|
||||
ModelBuildingRequest request = new DefaultModelBuildingRequest();
|
||||
|
||||
request.setSystemProperties(System.getProperties());
|
||||
request.setUserProperties(this.properties);
|
||||
|
||||
// Allow force selection of active profile
|
||||
// request.setActiveProfileIds()
|
||||
// request.setInactiveProfileIds()
|
||||
|
||||
// Better error messages for bad poms
|
||||
request.setLocationTracking(true);
|
||||
|
||||
// Don't run plugins, in most cases this is what we want. I don't know of any
|
||||
// that would affect the POM.
|
||||
request.setProcessPlugins(false);
|
||||
|
||||
request = configuration.apply(request);
|
||||
|
||||
return modelBuilder.build(request);
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package io.github.spencerpark.ijava.magics.dependencies;
|
||||
|
||||
import org.apache.ivy.plugins.resolver.ChainResolver;
|
||||
import org.apache.ivy.plugins.resolver.DependencyResolver;
|
||||
import org.apache.maven.model.Model;
|
||||
import org.apache.maven.model.Repository;
|
||||
import org.apache.maven.model.building.ModelBuildingException;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MavenToIvy {
|
||||
public static List<DependencyResolver> getRepositoriesFromModel(CharSequence pom) throws ModelBuildingException {
|
||||
return MavenToIvy.getRepositoriesFromModel(Maven.getInstance().readEffectiveModel(pom).getEffectiveModel());
|
||||
}
|
||||
|
||||
public static List<DependencyResolver> getRepositoriesFromModel(File pom) throws ModelBuildingException {
|
||||
return MavenToIvy.getRepositoriesFromModel(Maven.getInstance().readEffectiveModel(pom).getEffectiveModel());
|
||||
}
|
||||
|
||||
public static List<DependencyResolver> getRepositoriesFromModel(Model model) {
|
||||
return model.getRepositories().stream()
|
||||
.map(MavenToIvy::convertRepository)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static DependencyResolver convertRepository(Repository repository) {
|
||||
return CommonRepositories.maven(repository.getId(), repository.getUrl());
|
||||
}
|
||||
|
||||
public static ChainResolver createChainForModelRepositories(Model model) {
|
||||
ChainResolver resolver = new ChainResolver();
|
||||
|
||||
// Maven central is always an implicit repository.
|
||||
resolver.add(CommonRepositories.mavenCentral());
|
||||
|
||||
MavenToIvy.getRepositoriesFromModel(model).forEach(resolver::add);
|
||||
|
||||
return resolver;
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package io.github.spencerpark.ijava.runtime;
|
||||
|
||||
import io.github.spencerpark.ijava.JavaKernel;
|
||||
import io.github.spencerpark.jupyter.kernel.display.DisplayData;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class Display {
|
||||
public static DisplayData render(Object o) {
|
||||
JavaKernel kernel = Kernel.getKernelInstance();
|
||||
|
||||
if (kernel != null) {
|
||||
return kernel.getRenderer().render(o);
|
||||
} else {
|
||||
throw new RuntimeException("No IJava kernel running");
|
||||
}
|
||||
}
|
||||
|
||||
public static DisplayData render(Object o, String... as) {
|
||||
JavaKernel kernel = Kernel.getKernelInstance();
|
||||
|
||||
if (kernel != null) {
|
||||
return kernel.getRenderer().renderAs(o, as);
|
||||
} else {
|
||||
throw new RuntimeException("No IJava kernel running");
|
||||
}
|
||||
}
|
||||
|
||||
public static String display(Object o) {
|
||||
JavaKernel kernel = Kernel.getKernelInstance();
|
||||
|
||||
if (kernel != null) {
|
||||
DisplayData data = kernel.getRenderer().render(o);
|
||||
|
||||
String id = data.getDisplayId();
|
||||
if (id == null) {
|
||||
id = UUID.randomUUID().toString();
|
||||
data.setDisplayId(id);
|
||||
}
|
||||
|
||||
kernel.display(data);
|
||||
|
||||
return id;
|
||||
} else {
|
||||
throw new RuntimeException("No IJava kernel running");
|
||||
}
|
||||
}
|
||||
|
||||
public static String display(Object o, String... as) {
|
||||
JavaKernel kernel = Kernel.getKernelInstance();
|
||||
|
||||
if (kernel != null) {
|
||||
DisplayData data = kernel.getRenderer().renderAs(o, as);
|
||||
|
||||
String id = data.getDisplayId();
|
||||
if (id == null) {
|
||||
id = UUID.randomUUID().toString();
|
||||
data.setDisplayId(id);
|
||||
}
|
||||
|
||||
kernel.display(data);
|
||||
|
||||
return id;
|
||||
} else {
|
||||
throw new RuntimeException("No IJava kernel running");
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateDisplay(String id, Object o) {
|
||||
JavaKernel kernel = Kernel.getKernelInstance();
|
||||
|
||||
if (kernel != null) {
|
||||
DisplayData data = kernel.getRenderer().render(o);
|
||||
kernel.getIO().display.updateDisplay(id, data);
|
||||
} else {
|
||||
throw new RuntimeException("No IJava kernel running");
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateDisplay(String id, Object o, String... as) {
|
||||
JavaKernel kernel = Kernel.getKernelInstance();
|
||||
|
||||
if (kernel != null) {
|
||||
DisplayData data = kernel.getRenderer().renderAs(o, as);
|
||||
kernel.getIO().display.updateDisplay(id, data);
|
||||
} else {
|
||||
throw new RuntimeException("No IJava kernel running");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package io.github.spencerpark.ijava.runtime;
|
||||
|
||||
import io.github.spencerpark.ijava.IJava;
|
||||
import io.github.spencerpark.ijava.JavaKernel;
|
||||
|
||||
public class Kernel {
|
||||
public static JavaKernel getKernelInstance() {
|
||||
return IJava.getKernelInstance();
|
||||
}
|
||||
|
||||
public static Object eval(String expr) throws Exception {
|
||||
JavaKernel kernel = getKernelInstance();
|
||||
|
||||
if (kernel != null) {
|
||||
return kernel.evalRaw(expr);
|
||||
} else {
|
||||
throw new RuntimeException("No IJava kernel running");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package io.github.spencerpark.ijava.runtime;
|
||||
|
||||
import io.github.spencerpark.ijava.IJava;
|
||||
import io.github.spencerpark.ijava.JavaKernel;
|
||||
import io.github.spencerpark.jupyter.kernel.magic.registry.UndefinedMagicException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Magics {
|
||||
public static <T> T lineMagic(String name, List<String> args) {
|
||||
JavaKernel kernel = IJava.getKernelInstance();
|
||||
|
||||
if (kernel != null) {
|
||||
try {
|
||||
return kernel.getMagics().applyLineMagic(name, args);
|
||||
} catch (UndefinedMagicException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(String.format("Exception occurred while running line magic '%s': %s", name, e.getMessage()), e);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("No IJava kernel running");
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T cellMagic(String name, List<String> args, String body) {
|
||||
JavaKernel kernel = IJava.getKernelInstance();
|
||||
|
||||
if (kernel != null) {
|
||||
try {
|
||||
return kernel.getMagics().applyCellMagic(name, args, body);
|
||||
} catch (UndefinedMagicException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(String.format("Exception occurred while running cell magic '%s': %s", name, e.getMessage()), e);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("No IJava kernel running");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
io.github.spencerpark.jupyter.kernel.display.DisplayData render(Object o) {
|
||||
io.github.spencerpark.ijava.JavaKernel kernel = io.github.spencerpark.ijava.IJava.getKernelInstance();
|
||||
|
||||
if (kernel != null) {
|
||||
return kernel.getRenderer().render(o);
|
||||
} else {
|
||||
throw new RuntimeException("No IJava kernel running");
|
||||
}
|
||||
}
|
||||
|
||||
io.github.spencerpark.jupyter.kernel.display.DisplayData render(Object o, String... as) {
|
||||
io.github.spencerpark.ijava.JavaKernel kernel = io.github.spencerpark.ijava.IJava.getKernelInstance();
|
||||
|
||||
if (kernel != null) {
|
||||
return kernel.getRenderer().renderAs(o, as);
|
||||
} else {
|
||||
throw new RuntimeException("No IJava kernel running");
|
||||
}
|
||||
}
|
||||
|
||||
void display(Object o) {
|
||||
io.github.spencerpark.ijava.JavaKernel kernel = io.github.spencerpark.ijava.IJava.getKernelInstance();
|
||||
|
||||
if (kernel != null) {
|
||||
io.github.spencerpark.jupyter.kernel.display.DisplayData data = kernel.getRenderer().render(o);
|
||||
kernel.display(data);
|
||||
} else {
|
||||
throw new RuntimeException("No IJava kernel running");
|
||||
}
|
||||
}
|
||||
|
||||
void display(Object o, String... as) {
|
||||
io.github.spencerpark.ijava.JavaKernel kernel = io.github.spencerpark.ijava.IJava.getKernelInstance();
|
||||
|
||||
if (kernel != null) {
|
||||
io.github.spencerpark.jupyter.kernel.display.DisplayData data = kernel.getRenderer().renderAs(o, as);
|
||||
kernel.display(data);
|
||||
} else {
|
||||
throw new RuntimeException("No IJava kernel running");
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
Object eval(String expr) throws Exception {
|
||||
io.github.spencerpark.ijava.JavaKernel kernel = io.github.spencerpark.ijava.IJava.getKernelInstance();
|
||||
|
||||
if (kernel != null) {
|
||||
return kernel.evalRaw(expr);
|
||||
} else {
|
||||
throw new RuntimeException("No IJava kernel running");
|
||||
}
|
||||
}
|
@ -6,6 +6,10 @@ import java.util.concurrent.*;
|
||||
import java.util.prefs.*;
|
||||
import java.util.regex.*;
|
||||
|
||||
import static io.github.spencerpark.ijava.runtime.Display.*;
|
||||
import static io.github.spencerpark.ijava.runtime.Kernel.*;
|
||||
import static io.github.spencerpark.ijava.runtime.Magics.*;
|
||||
|
||||
public void printf(String format, Object... args) {
|
||||
System.out.printf(format, args);
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
<T> T lineMagic(String name, java.util.List<String> args) {
|
||||
io.github.spencerpark.ijava.JavaKernel kernel = io.github.spencerpark.ijava.IJava.getKernelInstance();
|
||||
|
||||
if (kernel != null) {
|
||||
try {
|
||||
return kernel.getMagics().applyLineMagic(name, args);
|
||||
} catch (io.github.spencerpark.jupyter.kernel.magic.registry.UndefinedMagicException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(String.format("Exception occurred while running line magic '%s': %s", name, e.getMessage()), e);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("No IJava kernel running");
|
||||
}
|
||||
}
|
||||
|
||||
<T> T cellMagic(String name, java.util.List<String> args, String body) {
|
||||
io.github.spencerpark.ijava.JavaKernel kernel = io.github.spencerpark.ijava.IJava.getKernelInstance();
|
||||
|
||||
if (kernel != null) {
|
||||
try {
|
||||
return kernel.getMagics().applyCellMagic(name, args, body);
|
||||
} catch (io.github.spencerpark.jupyter.kernel.magic.registry.UndefinedMagicException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(String.format("Exception occurred while running cell magic '%s': %s", name, e.getMessage()), e);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("No IJava kernel running");
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user