mirror of
https://github.com/SpencerPark/IJava.git
synced 2025-04-20 05:16:10 +00:00
Compare commits
No commits in common. "master" and "v1.1.0" have entirely different histories.
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,4 +1,3 @@
|
||||
.gradle/
|
||||
.idea/
|
||||
build/
|
||||
out/
|
||||
build/
|
93
build.gradle
93
build.gradle
@ -1,25 +1,20 @@
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'maven-publish'
|
||||
id('com.github.hierynomus.license') version '0.14.0'
|
||||
id('io.github.spencerpark.jupyter-kernel-installer') version '2.1.0'
|
||||
id('com.github.jk1.dependency-license-report')
|
||||
id 'com.github.hierynomus.license' version '0.14.0'
|
||||
id 'io.github.spencerpark.jupyter-kernel-installer' version '1.1.5'
|
||||
}
|
||||
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
import com.github.jk1.license.render.*
|
||||
import com.github.jk1.license.filter.*
|
||||
import io.github.spencerpark.gradle.*
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
group = 'io.github.spencerpark'
|
||||
version = '1.3.0'
|
||||
version = '1.1.0-SNAPSHOT'
|
||||
|
||||
wrapper {
|
||||
gradleVersion = '4.8.1'
|
||||
distributionType = Wrapper.DistributionType.ALL
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '4.2.1'
|
||||
}
|
||||
|
||||
// Add the license header to source files
|
||||
license {
|
||||
header = file('LICENSE')
|
||||
exclude '**/*.json'
|
||||
@ -30,22 +25,6 @@ license {
|
||||
}
|
||||
build.dependsOn 'licenseFormat'
|
||||
|
||||
// Configures the license report generated for the dependencies.
|
||||
licenseReport {
|
||||
excludeGroups = []
|
||||
renderers = [
|
||||
// Generate a pretty HTML report that groups dependencies by their license.
|
||||
new NewInventoryHtmlReportRenderer('dependencies.html'),
|
||||
// TODO make sure ci verifies that all licenses are know to be allowed to redistribute before publishing
|
||||
new JsonReportRenderer('dependencies.json')
|
||||
]
|
||||
|
||||
// Group same licenses despite names being slightly different (ex. Apache 2.0 vs Apache version 2)
|
||||
filters = [new LicenseBundleNormalizer()]
|
||||
|
||||
configurations = ['compile']
|
||||
}
|
||||
|
||||
compileJava {
|
||||
sourceCompatibility = 1.9
|
||||
targetCompatibility = 1.9
|
||||
@ -60,19 +39,16 @@ 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.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'
|
||||
shade group: 'io.github.spencerpark', name: 'jupyter-jvm-basekernel', version: '2.2.1-SNAPSHOT'
|
||||
shade group: 'org.jboss.shrinkwrap.resolver', name: 'shrinkwrap-resolver-impl-maven', version: '3.1.3'
|
||||
|
||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||
}
|
||||
@ -80,7 +56,7 @@ dependencies {
|
||||
jar {
|
||||
//Include all shaded dependencies in the jar
|
||||
from configurations.shade
|
||||
.collect { it.isDirectory() ? it : zipTree(it) }
|
||||
.collect {it.isDirectory() ? it : zipTree(it)}
|
||||
|
||||
manifest {
|
||||
attributes('Main-class': 'io.github.spencerpark.ijava.IJava')
|
||||
@ -109,45 +85,4 @@ jupyter {
|
||||
kernelDisplayName = 'Java'
|
||||
kernelLanguage = 'java'
|
||||
kernelInterruptMode = 'message'
|
||||
|
||||
kernelParameters {
|
||||
list('classpath', 'IJAVA_CLASSPATH') {
|
||||
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') {
|
||||
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') {
|
||||
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') {
|
||||
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'
|
||||
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'
|
||||
}
|
||||
|
||||
from(generateLicenseReport.outputFolder) {
|
||||
into 'dependency-licenses'
|
||||
}
|
||||
}
|
||||
zipKernel.dependsOn 'generateLicenseReport'
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
plugins {
|
||||
id 'groovy'
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url = 'https://plugins.gradle.org/m2/'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile group: 'com.github.jk1.dependency-license-report', name: 'com.github.jk1.dependency-license-report.gradle.plugin', version: '1.1'
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package io.github.spencerpark.gradle
|
||||
|
||||
class DeclaredModuleInfo {
|
||||
String projectUrl
|
||||
String license
|
||||
String licenseUrl
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
package io.github.spencerpark.gradle
|
||||
|
||||
import com.github.jk1.license.*
|
||||
import groovy.json.JsonParserType
|
||||
import groovy.json.JsonSlurper
|
||||
|
||||
abstract class InventoryReportRenderer {
|
||||
/**
|
||||
* Collect declared from a JSON object with maven coordinates as keys and {@link DeclaredModuleInfo}
|
||||
* as values
|
||||
* @param declarations the stream to parse the json object from
|
||||
* @return the collected declarations
|
||||
*/
|
||||
static Map<String, DeclaredModuleInfo> parseDeclarations(InputStream declarations) {
|
||||
def rawDeclarations = new JsonSlurper()
|
||||
.setType(JsonParserType.LAX)
|
||||
.parse(declarations)
|
||||
|
||||
assert rawDeclarations instanceof Map: "Declaration spec must be a json object"
|
||||
|
||||
return rawDeclarations.collectEntries([:]) { coords, spec ->
|
||||
assert spec instanceof Map: "Declaration spec for $coords is not an object"
|
||||
|
||||
DeclaredModuleInfo info = new DeclaredModuleInfo()
|
||||
spec.forEach { String key, val ->
|
||||
assert val instanceof String: "Declaration spec for $coords::$key must be a string"
|
||||
assert info.hasProperty(key), "Declaration spec for $coords has unknown key $key"
|
||||
|
||||
info[key] = val
|
||||
}
|
||||
assert info.projectUrl: "Declaration missing required key: projectUrl"
|
||||
assert info.license: "Declaration missing required key: license"
|
||||
assert info.licenseUrl: "Declaration missing required key: licenseUrl"
|
||||
|
||||
return [(coords): info]
|
||||
}
|
||||
}
|
||||
|
||||
static Map<String, List<ModuleData>> collectModulesByLicenseName(ProjectData data, Map<String, DeclaredModuleInfo> declared) {
|
||||
Map<String, List<ModuleData>> modulesByLicense = [:]
|
||||
|
||||
def addModule = { String licenseName, ModuleData module ->
|
||||
String coords = module.with { "$group:$name:$version" }
|
||||
|
||||
if (licenseName == "Unknown" && declared.containsKey(coords))
|
||||
licenseName = declared[coords].license
|
||||
|
||||
modulesByLicense.compute(licenseName) { k, modules ->
|
||||
return (modules ?: []) << module
|
||||
}
|
||||
}
|
||||
|
||||
data.allDependencies.each { module ->
|
||||
if (module.poms.isEmpty()) {
|
||||
addModule(module.licenseFiles.isEmpty() ? "Unknown" : "Embedded", module)
|
||||
return
|
||||
}
|
||||
|
||||
PomData pom = module.poms.first()
|
||||
if (pom.licenses.isEmpty()) {
|
||||
addModule(module.licenseFiles.isEmpty() ? "Unknown" : "Embedded", module)
|
||||
} else {
|
||||
pom.licenses.each { License license ->
|
||||
addModule(license.name, module)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modulesByLicense
|
||||
}
|
||||
|
||||
// imported modules are things declared as dependencies but not actually included via gradle means. For
|
||||
// example a javascript dependency.
|
||||
static Map<String, Map<String, List<ImportedModuleData>>> collectModulesByLicenseFromImported(ProjectData data) {
|
||||
Map<String, Map<String, List<ImportedModuleData>>> externalByModulesByLicense = [:]
|
||||
|
||||
data.importedModules.each { ImportedModuleBundle module ->
|
||||
Map<String, List<ImportedModuleData>> modulesByLicense = [:]
|
||||
|
||||
module.modules.each { ImportedModuleData moduleData ->
|
||||
modulesByLicense.compute(moduleData.license) { k, modules ->
|
||||
return (modules ?: []) << moduleData
|
||||
}
|
||||
}
|
||||
|
||||
externalByModulesByLicense[module.name] = modulesByLicense
|
||||
}
|
||||
|
||||
return externalByModulesByLicense
|
||||
}
|
||||
}
|
@ -1,267 +0,0 @@
|
||||
package io.github.spencerpark.gradle
|
||||
|
||||
import com.github.jk1.license.*
|
||||
import com.github.jk1.license.render.ReportRenderer
|
||||
import groovy.text.SimpleTemplateEngine
|
||||
import groovy.text.StreamingTemplateEngine
|
||||
|
||||
class NewInventoryHtmlReportRenderer implements ReportRenderer {
|
||||
private final String name
|
||||
private final String fileName
|
||||
private final Map<String, DeclaredModuleInfo> declared
|
||||
private final Map<String, String> colors = [
|
||||
accent : '#F37726',
|
||||
primary : 'white',
|
||||
accentBg : '#616262',
|
||||
primaryBg: '#989798',
|
||||
darkText : '#4E4E4E',
|
||||
lightText: '#e8e5e5',
|
||||
]
|
||||
|
||||
private Writer output
|
||||
private int counter
|
||||
|
||||
NewInventoryHtmlReportRenderer(String fileName = 'index.html', String name = null, File declarationsFileName = null, Map<String, String> colors = [:]) {
|
||||
this.name = name
|
||||
this.fileName = fileName
|
||||
|
||||
if (declarationsFileName)
|
||||
declared = InventoryReportRenderer.parseDeclarations(declarationsFileName.newInputStream())
|
||||
else
|
||||
declared = [:]
|
||||
|
||||
this.colors.putAll(colors)
|
||||
}
|
||||
|
||||
@Override
|
||||
void render(ProjectData data) {
|
||||
this.counter = 0
|
||||
|
||||
def project = data.project
|
||||
def name = project.name
|
||||
LicenseReportExtension config = project.licenseReport
|
||||
def outFile = new File(config.outputDir, fileName)
|
||||
|
||||
def stylesheet = NewInventoryHtmlReportRenderer.class.getResourceAsStream("/license-report.template.css").withReader {
|
||||
new SimpleTemplateEngine()
|
||||
.createTemplate(it)
|
||||
.make(this.colors)
|
||||
.writeTo(new StringWriter())
|
||||
.toString()
|
||||
}
|
||||
def template = NewInventoryHtmlReportRenderer.class.getResourceAsStream("/license-report.template.html").withReader {
|
||||
new StreamingTemplateEngine().createTemplate(it)
|
||||
}
|
||||
|
||||
def binding = [
|
||||
stylesheet : stylesheet,
|
||||
name : name,
|
||||
project : project,
|
||||
inventory : InventoryReportRenderer.collectModulesByLicenseName(data, declared),
|
||||
externalInventories : InventoryReportRenderer.collectModulesByLicenseFromImported(data),
|
||||
serializeHref : { String... values ->
|
||||
values.findAll { it != null }.collect { it.replaceAll(/\s/, '_') }.join('_')
|
||||
},
|
||||
printDependency : this.&printDependency,
|
||||
printImportedDependency: this.&printImportedDependency,
|
||||
]
|
||||
|
||||
outFile.withWriter {
|
||||
template.make(binding).writeTo(it)
|
||||
}
|
||||
}
|
||||
|
||||
void tag(Map<String, String> attrs = [:], String name, def children) {
|
||||
output << "<$name ${attrs.collect { k, v -> "$k=\"$v\"" }.join(" ")}>\n"
|
||||
if (children.respondsTo("call"))
|
||||
children.call()
|
||||
else
|
||||
text(children)
|
||||
output << "</$name>\n"
|
||||
}
|
||||
|
||||
void div(Map<String, String> attrs = [:], def children) {
|
||||
tag(attrs, "div", children)
|
||||
}
|
||||
|
||||
void p(Map<String, String> attrs = [:], def children) {
|
||||
tag(attrs, "p", children)
|
||||
}
|
||||
|
||||
void a(Map<String, String> attrs = [:], def children) {
|
||||
tag(attrs, "a", children)
|
||||
}
|
||||
|
||||
void strong(Map<String, String> attrs = [:], def children) {
|
||||
tag(attrs, "strong", children)
|
||||
}
|
||||
|
||||
void ul(Map<String, String> attrs = [:], def children) {
|
||||
tag(attrs, "ul", children)
|
||||
}
|
||||
|
||||
void li(Map<String, String> attrs = [:], def children) {
|
||||
tag(attrs, "li", children)
|
||||
}
|
||||
|
||||
void text(def contents) {
|
||||
if (contents != null)
|
||||
output << String.valueOf(contents)
|
||||
}
|
||||
|
||||
void renderDependencyProperty(String label, def children) {
|
||||
div(class: "dependency-prop") {
|
||||
tag("label") { text(label) }
|
||||
div(class: "dependency-value", children)
|
||||
}
|
||||
}
|
||||
|
||||
void renderDependencyTitle(ModuleData data) {
|
||||
p(class: "title") {
|
||||
strong(class: "index", "${++counter}.")
|
||||
|
||||
if (data.group) {
|
||||
strong("Group: ")
|
||||
text(data.group)
|
||||
}
|
||||
|
||||
if (data.name) {
|
||||
strong("Name: ")
|
||||
text(data.name)
|
||||
}
|
||||
|
||||
if (data.version) {
|
||||
strong("Version: ")
|
||||
text(data.version)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void renderDependencyTitle(ImportedModuleData data) {
|
||||
p(class: "title") {
|
||||
strong(class: "index", ++counter)
|
||||
|
||||
if (data.name) {
|
||||
strong("Name: ")
|
||||
text(data.name)
|
||||
}
|
||||
|
||||
if (data.version) {
|
||||
strong("Version: ")
|
||||
text(data.version)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void renderDependencyProjectUrl(ModuleData data) {
|
||||
String coords = data.with { "$group:$name:$version" }
|
||||
|
||||
ManifestData manifest = data.manifests.isEmpty() ? null : data.manifests.first()
|
||||
PomData pomData = data.poms.isEmpty() ? null : data.poms.first()
|
||||
|
||||
if (manifest?.url && pomData?.projectUrl && manifest.url == pomData.projectUrl) {
|
||||
renderDependencyProperty("Project URL") {
|
||||
a(href: manifest.url, { text(manifest.url) })
|
||||
}
|
||||
} else if (manifest?.url || pomData?.projectUrl) {
|
||||
if (manifest?.url) {
|
||||
renderDependencyProperty("Manifest Project URL") {
|
||||
a(href: manifest.url, { text(manifest.url) })
|
||||
}
|
||||
}
|
||||
|
||||
if (pomData?.projectUrl) {
|
||||
renderDependencyProperty("POM Project URL") {
|
||||
a(href: pomData.projectUrl, { text(pomData.projectUrl) })
|
||||
}
|
||||
}
|
||||
} else if (declared.containsKey(coords)) {
|
||||
renderDependencyProperty("Project URL") {
|
||||
a(href: declared[coords].projectUrl, { text(declared[coords].projectUrl) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void renderReferencedLicenses(ModuleData data) {
|
||||
String coords = data.with { "$group:$name:$version" }
|
||||
|
||||
ManifestData manifest = data.manifests.isEmpty() ? null : data.manifests.first()
|
||||
PomData pomData = data.poms.isEmpty() ? null : data.poms.first()
|
||||
|
||||
if (manifest?.license || pomData?.licenses) {
|
||||
if (manifest?.license) {
|
||||
if (manifest.license.startsWith("http")) {
|
||||
renderDependencyProperty("Manifest license URL") {
|
||||
a(href: manifest.license, { text(manifest.license) })
|
||||
}
|
||||
} else if (manifest.hasPackagedLicense) {
|
||||
renderDependencyProperty("Packaged License File") {
|
||||
a(href: manifest.license, { text(manifest.url) })
|
||||
}
|
||||
} else {
|
||||
renderDependencyProperty("Manifest License") {
|
||||
text("${manifest.license} (Not Packaged)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pomData?.licenses) {
|
||||
pomData.licenses.each { License license ->
|
||||
if (license.url) {
|
||||
renderDependencyProperty("POM License") {
|
||||
text("${license.name} - ")
|
||||
if (license.url.startsWith("http"))
|
||||
a(href: license.url, { text(license.url) })
|
||||
else
|
||||
text(license.url)
|
||||
}
|
||||
} else {
|
||||
renderDependencyProperty("POM License", { text(license.name) })
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (declared.containsKey(coords)) {
|
||||
renderDependencyProperty("License URL") {
|
||||
a(href: declared[coords].licenseUrl, { text(declared[coords].license) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void renderIncludedLicenses(ModuleData data) {
|
||||
if (!data.licenseFiles.isEmpty() && !data.licenseFiles.first().fileDetails.isEmpty()) {
|
||||
renderDependencyProperty("Embedded license files") {
|
||||
ul {
|
||||
data.licenseFiles.first().fileDetails.each {
|
||||
def file = it.file
|
||||
li {
|
||||
a(href: file, { text(file) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void printDependency(Writer out, ModuleData data) {
|
||||
this.output = out
|
||||
div(class: "dependency") {
|
||||
renderDependencyTitle(data)
|
||||
renderDependencyProjectUrl(data)
|
||||
renderReferencedLicenses(data)
|
||||
renderIncludedLicenses(data)
|
||||
}
|
||||
}
|
||||
|
||||
private printImportedDependency(Writer out, ImportedModuleData data) {
|
||||
this.output = out
|
||||
div(class: "dependency") {
|
||||
renderDependencyTitle(data)
|
||||
renderDependencyProperty("Project URL") {
|
||||
a(href: data.projectUrl, { text(data.projectUrl) })
|
||||
}
|
||||
renderDependencyProperty("License URL") {
|
||||
a(href: data.licenseUrl, { text(data.license) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,151 +0,0 @@
|
||||
@media print {
|
||||
.inventory {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: static !important;
|
||||
}
|
||||
}
|
||||
|
||||
html, body, section {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
line-height: 125%;
|
||||
margin: 0;
|
||||
background: $primaryBg;
|
||||
}
|
||||
|
||||
.header {
|
||||
/* #22aa44 */
|
||||
background: $accent;
|
||||
color: $darkText;
|
||||
padding: 2em 1em 1em 1em;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 16pt;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
.header h2 {
|
||||
font-size: 10pt;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
}
|
||||
|
||||
.inventory {
|
||||
background: $accentBg;
|
||||
color: $lightText;
|
||||
padding: 0;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 25%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.inventory ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.inventory li {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.inventory li a {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
color: $lightText;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 0.938em 0.750em;
|
||||
}
|
||||
|
||||
.inventory li a:hover {
|
||||
background: rgba(50, 50, 50, 0.5);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.inventory .section-heading {
|
||||
background: rgba(50, 50, 50, 0.25);
|
||||
padding-left: 0.5em;
|
||||
margin: 0;
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
.license .license-name {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.license .badge {
|
||||
background: $accent;
|
||||
padding: 0.625em 0.938em;
|
||||
border-radius: 1.250em;
|
||||
color: $darkText;
|
||||
display: inline-table;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 0 1rem;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 25%;
|
||||
width: 75%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.content h1 {
|
||||
color: $darkText;
|
||||
background-color: $accent;
|
||||
padding: 0.67em;
|
||||
margin: 0 -1rem;
|
||||
}
|
||||
|
||||
.dependency {
|
||||
background: white;
|
||||
padding: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.dependency:hover {
|
||||
box-shadow: dimgrey 0.2em 0.2em 0.2em 0em;
|
||||
}
|
||||
|
||||
.dependency .index {
|
||||
font-size: larger;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
.dependency-prop {
|
||||
padding: 0.3em;
|
||||
}
|
||||
|
||||
.dependency-prop:hover {
|
||||
background: rgba(50, 50, 50, 0.25);
|
||||
}
|
||||
|
||||
.dependency-prop label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dependency-value {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.dependency-value ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Dependency License Report for $name</title>
|
||||
<style>
|
||||
<% out.println stylesheet %>
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
||||
<!-- INVENTORY -->
|
||||
<div class="inventory">
|
||||
<div class="header">
|
||||
<h1>${project.name} ${!"unspecified".equals(project.version) ? project.version : ""}</h1>
|
||||
<h2>Dependency License Report</h2>
|
||||
<h2 class="timestamp">
|
||||
<em>${new Date().format("yyyy-MM-dd HH:mm:ss z")}</em>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<h3 class="section-heading">${name}</h3>
|
||||
<ul>
|
||||
<% inventory.keySet().sort().each { license -> %>
|
||||
<li>
|
||||
<a class="license" href="#${serializeHref.call(name, license)}">
|
||||
<span class="license-name">${license}</span>
|
||||
<span class="badge">${inventory[license].size()}</span>
|
||||
</a>
|
||||
</li>
|
||||
<% } %>
|
||||
</ul>
|
||||
|
||||
<% externalInventories.each { name, modules -> %>
|
||||
<h3 class="section-heading">${name}</h3>
|
||||
<ul>
|
||||
<% modules.each { license, dependencies -> %>
|
||||
<li>
|
||||
<a class="license" href="#${serializeHref.call(name, license)}">
|
||||
<span class="license-name">${license}</span>
|
||||
<span class="badge">${dependencies.size()}</span>
|
||||
</a>
|
||||
</li>
|
||||
<% } %>
|
||||
</ul>
|
||||
<% } %>
|
||||
</div>
|
||||
|
||||
<!-- DETAILS -->
|
||||
<div class="content">
|
||||
<h1>${name}</h1>
|
||||
<% inventory.keySet().sort().each { license -> %>
|
||||
<a id="${serializeHref.call(name, license)}"></a>
|
||||
<h2>${license}</h2>
|
||||
<% inventory[license].sort({ a, b -> a.group <=> b.group }).each { data ->
|
||||
printDependency.call(out, data)
|
||||
} %>
|
||||
<% } %>
|
||||
|
||||
<% externalInventories.keySet().sort().each { String name -> %>
|
||||
<h1>${name}</h1>
|
||||
<% externalInventories[name].each { String license, dependencies -> %>
|
||||
<a id="${serializeHref.call(name, license)}"></a>
|
||||
<% dependencies.each { importedData ->
|
||||
printImportedDependency.call(out, importedData)
|
||||
} %>
|
||||
<% } %>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -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. 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.
|
||||
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).
|
||||
|
||||
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.
|
||||
|
||||
### `String display(Object o)`
|
||||
### `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. An id is returned which can be used to `updateDisplay` if desired.
|
||||
The object is rendered and published on the display stream.
|
||||
|
||||
### `String display(Object o, String... as)`
|
||||
### `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. An id is returned which can be used to `updateDisplay` if desired.
|
||||
The object is rendered and published on the display stream.
|
||||
|
||||
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,20 +26,13 @@ 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. For example, to display as html and markdown:
|
||||
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.
|
||||
|
||||
```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)`
|
||||
### `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.
|
||||
|
||||
### `DisplayData render(Object o, String... as)`
|
||||
### `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.
|
||||
|
||||
@ -53,19 +46,4 @@ 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")
|
||||
```
|
@ -1,22 +0,0 @@
|
||||
# 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,18 +44,7 @@ Add jars to the notebook classpath.
|
||||
###### Line magic
|
||||
|
||||
* **arguments**:
|
||||
* _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.
|
||||
* _varargs_ list of simple glob paths to jars on the local file system
|
||||
|
||||
|
||||
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,4 +1,4 @@
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.2.1-bin.zip
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
|
@ -46,6 +46,9 @@ 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,38 +24,30 @@
|
||||
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')
|
||||
@ -81,6 +73,9 @@ 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))
|
||||
@ -93,13 +88,26 @@ public class JavaKernel extends BaseKernel {
|
||||
this.magicsTransformer = new MagicsSourceTransformer();
|
||||
this.magics = new Magics();
|
||||
this.magics.registerMagics(this.mavenResolver);
|
||||
this.magics.registerMagics(new ClasspathMagics(this::addToClasspath));
|
||||
this.magics.registerMagics(new Load(List.of(".jsh", ".jshell", ".java", ".ijava"), this::eval));
|
||||
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.languageInfo = new LanguageInfo.Builder("Java")
|
||||
.version(Runtime.version().toString())
|
||||
.mimetype("text/x-java-source")
|
||||
.fileExtension(".jshell")
|
||||
.fileExtension(".java")
|
||||
.pygments("java")
|
||||
.codemirror("java")
|
||||
.build();
|
||||
@ -161,12 +169,8 @@ 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));
|
||||
}
|
||||
@ -180,12 +184,8 @@ public class JavaKernel extends BaseKernel {
|
||||
Snippet snippet = event.snippet();
|
||||
this.evaluator.getShell().diagnostics(snippet)
|
||||
.forEach(d -> {
|
||||
// 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()));
|
||||
fmt.addAll(this.errorStyler.highlightSubstringLines(snippet.source(),
|
||||
(int) d.getStartPosition(), (int) d.getEndPosition()));
|
||||
|
||||
// Add the error message
|
||||
for (String line : StringStyler.splitLines(d.getMessage(null))) {
|
||||
@ -233,23 +233,6 @@ 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()));
|
||||
|
||||
@ -262,14 +245,6 @@ 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);
|
||||
|
||||
@ -351,21 +326,16 @@ public class JavaKernel extends BaseKernel {
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new ReplacementOptions(options, replaceStart[0], at);
|
||||
return new ReplacementOptions(options, replaceStart[0], at + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String isComplete(String code) {
|
||||
return this.evaluator.isComplete(code);
|
||||
return super.isComplete(code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShutdown(boolean isRestarting) {
|
||||
this.evaluator.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interrupt() {
|
||||
this.evaluator.interrupt();
|
||||
}
|
||||
}
|
||||
|
247
src/main/java/io/github/spencerpark/ijava/MavenResolver.java
Normal file
247
src/main/java/io/github/spencerpark/ijava/MavenResolver.java
Normal file
@ -0,0 +1,247 @@
|
||||
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,17 +23,11 @@
|
||||
*/
|
||||
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;
|
||||
@ -44,8 +38,6 @@ 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;
|
||||
@ -83,16 +75,8 @@ public class CodeEvaluator {
|
||||
String key = event.value();
|
||||
if (key == null) continue;
|
||||
|
||||
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) {
|
||||
Object value = executionControl.takeResult(key);
|
||||
switch (event.snippet().subKind()) {
|
||||
case VAR_VALUE_SUBKIND:
|
||||
case OTHER_EXPRESSION_SUBKIND:
|
||||
case TEMP_VAR_EXPRESSION_SUBKIND:
|
||||
@ -109,17 +93,8 @@ public class CodeEvaluator {
|
||||
if (event.causeSnippet() == null) {
|
||||
JShellException e = event.exception();
|
||||
if (e != null) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (e instanceof EvalException && IJavaExecutionControl.EXECUTION_TIMEOUT_NAME.equals(((EvalException) e).getExceptionClassName()))
|
||||
throw new EvaluationTimeoutException(executionControl.getTimeoutDuration(), executionControl.getTimeoutUnit(), code.trim());
|
||||
throw e;
|
||||
}
|
||||
|
||||
@ -152,80 +127,8 @@ 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() {
|
||||
IJavaExecutionControl executionControl =
|
||||
this.executionControlProvider.getRegisteredControlByID(this.executionControlID);
|
||||
|
||||
if (executionControl != null)
|
||||
executionControl.interrupt();
|
||||
this.shell.stop();
|
||||
}
|
||||
|
||||
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,10 +59,7 @@ 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;
|
||||
}
|
||||
|
||||
@ -148,7 +145,6 @@ 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);
|
||||
@ -203,8 +199,6 @@ 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())
|
||||
|
@ -1,19 +0,0 @@
|
||||
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,7 +25,6 @@ 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;
|
||||
@ -42,18 +41,10 @@ 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);
|
||||
@ -63,7 +54,6 @@ 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() {
|
||||
@ -91,28 +81,17 @@ public class IJavaExecutionControl extends DirectExecutionControl {
|
||||
return result == NULL ? null : result;
|
||||
}
|
||||
|
||||
private Object execute(String key, Method doitMethod) throws TimeoutException, Exception {
|
||||
private Object execute(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() 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()
|
||||
);
|
||||
// If canceled this means that stop() was invoked in which case the protocol is to
|
||||
// throw an ExecutionControl.StoppedException.
|
||||
throw new StoppedException();
|
||||
} catch (ExecutionException e) {
|
||||
// The execution threw an exception. The actual exception is the cause of the ExecutionException.
|
||||
Throwable cause = e.getCause();
|
||||
@ -122,18 +101,15 @@ 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,
|
||||
e.getStackTrace()
|
||||
new StackTraceElement[]{} // The trace is irrelevant because it is in the kernel space not the user space so leave it blank.
|
||||
);
|
||||
} finally {
|
||||
this.running.remove(key, runningTask);
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,17 +125,12 @@ 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();
|
||||
|
@ -1,26 +1,3 @@
|
||||
/*
|
||||
* 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.execution;
|
||||
|
||||
import io.github.spencerpark.jupyter.kernel.magic.CellMagicParseContext;
|
||||
@ -38,7 +15,7 @@ public class MagicsSourceTransformer {
|
||||
private final MagicParser parser;
|
||||
|
||||
public MagicsSourceTransformer() {
|
||||
this.parser = new MagicParser("(?<=(?:^|=))\\s*%", "%%");
|
||||
this.parser = new MagicParser("%", "%%");
|
||||
}
|
||||
|
||||
public String transformMagics(String source) {
|
||||
|
@ -1,56 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -1,524 +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.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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -1,214 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
41
src/main/resources/ijava-display-init.jshell
Normal file
41
src/main/resources/ijava-display-init.jshell
Normal file
@ -0,0 +1,41 @@
|
||||
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");
|
||||
}
|
||||
}
|
9
src/main/resources/ijava-eval-init.jshell
Normal file
9
src/main/resources/ijava-eval-init.jshell
Normal file
@ -0,0 +1,9 @@
|
||||
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,10 +6,6 @@ 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);
|
||||
}
|
31
src/main/resources/ijava-magics-init.jshell
Normal file
31
src/main/resources/ijava-magics-init.jshell
Normal file
@ -0,0 +1,31 @@
|
||||
<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