Skip to content

Commit 73ac164

Browse files
committed
Ensure that all models compile in headless mode
1 parent 99d6482 commit 73ac164

File tree

9 files changed

+73
-43
lines changed

9 files changed

+73
-43
lines changed

src/main/BackingModelManager.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import org.nlogo.ls.gui.{ GUILevelSpaceMenu, LevelSpaceMenu, ModelCodeTab, Model
44

55
import org.nlogo.app.App
66
import org.nlogo.app.codetab.CodeTab
7-
import org.nlogo.workspace.AbstractWorkspaceScala
7+
import org.nlogo.workspace.AbstractWorkspace
88

99
import scala.collection.Map
1010
import scala.collection.parallel.mutable.ParHashMap
@@ -52,7 +52,7 @@ class BackingModelManager extends LSModelManager {
5252
}
5353

5454
def registerTab(filePath: String, model: ChildModel)
55-
(f: AbstractWorkspaceScala => ModelCodeTab): Option[ModelCodeTab] = {
55+
(f: AbstractWorkspace => ModelCodeTab): Option[ModelCodeTab] = {
5656
if (backingModels.get(filePath).isDefined) {
5757
None
5858
} else {
@@ -63,7 +63,7 @@ class BackingModelManager extends LSModelManager {
6363
}
6464

6565
def registerTab(filePath: String)
66-
(f: AbstractWorkspaceScala => ModelCodeTab): Option[ModelCodeTab] = {
66+
(f: AbstractWorkspace => ModelCodeTab): Option[ModelCodeTab] = {
6767
if (backingModels.get(filePath).isDefined) {
6868
None
6969
} else {
@@ -93,5 +93,5 @@ class HeadlessBackingModelManager extends LSModelManager {
9393
def removeTab(tab: ModelCodeTab): Unit = {}
9494
def existingTab(filePath: String): Option[ModelCodeTab] = None
9595
def registerTab(filePath: String)
96-
(f: AbstractWorkspaceScala => ModelCodeTab): Option[ModelCodeTab] = None
96+
(f: AbstractWorkspace => ModelCodeTab): Option[ModelCodeTab] = None
9797
}

src/main/ChildModel.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ package org.nlogo.ls
33
import javax.swing.{ JFrame, SwingUtilities }
44

55
import org.nlogo.api.{ CommandRunnable, ExtensionException, Version, Workspace }
6-
import org.nlogo.workspace.AbstractWorkspaceScala
6+
import org.nlogo.workspace.AbstractWorkspace
77

88
import scala.concurrent.ExecutionContext.Implicits.global
99
import scala.concurrent.Future
1010
import scala.jdk.CollectionConverters.IterableHasAsScala
1111

1212
abstract class ChildModel(val parentWorkspace: Workspace, val modelID: Int) {
13-
lazy val evaluator = new Evaluator(modelID, name, workspace, parentWorkspace.asInstanceOf[AbstractWorkspaceScala])
13+
lazy val evaluator = new Evaluator(modelID, name, workspace, parentWorkspace.asInstanceOf[AbstractWorkspace])
1414

1515
private var _name: Option[String] = None
1616
def name_= (newName: String): Unit = {
@@ -46,7 +46,7 @@ abstract class ChildModel(val parentWorkspace: Workspace, val modelID: Int) {
4646
def frame: Option[JFrame]
4747

4848
def setSpeed(d: Double): Unit
49-
def workspace: AbstractWorkspaceScala
49+
def workspace: AbstractWorkspace
5050

5151
// can't change once model is loaded
5252
lazy val usesLevelSpace: Boolean =

src/main/Evaluator.scala

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,24 @@ import org.nlogo.api.{ Context, MersenneTwisterFast, SimpleJobOwner, Workspace }
66
import org.nlogo.core.{ AgentKind, LogoList, Nobody }
77
import org.nlogo.nvm.{ ExclusiveJob, Procedure, Reporter }
88
import org.nlogo.prim.{ _constboolean, _constdouble, _constlist, _conststring, _nobody }
9-
import org.nlogo.workspace.{ AbstractWorkspaceScala, Plotting }
9+
import org.nlogo.workspace.{ AbstractWorkspace, Plotting }
1010

1111
object RNG {
12-
def apply(ctx: Context): RNG = ctx.workspace match {
13-
case p: Plotting if p.plotRNG eq ctx.getRNG => PlotRNG
14-
case w: Workspace if w.mainRNG eq ctx.getRNG => MainRNG
15-
case w: Workspace if w.auxRNG eq ctx.getRNG => MainRNG
16-
case _ => LocalRNG
12+
def apply(ctx: Context): RNG = {
13+
if (ctx.workspace.workspaceContext.workspaceGUI) {
14+
ctx.workspace match {
15+
case p: Plotting if p.plotRNG eq ctx.getRNG => PlotRNG
16+
case w: Workspace if w.mainRNG eq ctx.getRNG => MainRNG
17+
case w: Workspace if w.auxRNG eq ctx.getRNG => MainRNG
18+
case _ => LocalRNG
19+
}
20+
} else {
21+
ctx.workspace match {
22+
case w: Workspace if w.mainRNG eq ctx.getRNG => MainRNG
23+
case w: Workspace if w.auxRNG eq ctx.getRNG => MainRNG
24+
case _ => LocalRNG
25+
}
26+
}
1727
}
1828
}
1929

@@ -27,16 +37,22 @@ case object AuxRNG extends RNG {
2737
override def apply(ws: Workspace): MersenneTwisterFast = ws.auxRNG
2838
}
2939
case object PlotRNG extends RNG {
30-
override def apply(ws: Workspace): MersenneTwisterFast = ws match {
31-
case p: Plotting => p.plotRNG
32-
case _ => ws.auxRNG
40+
override def apply(ws: Workspace): MersenneTwisterFast = {
41+
if (ws.workspaceContext.workspaceGUI) {
42+
ws match {
43+
case p: Plotting => p.plotRNG
44+
case _ => ws.auxRNG
45+
}
46+
} else {
47+
ws.auxRNG
48+
}
3349
}
3450
}
3551
case object LocalRNG extends RNG {
3652
override def apply(ws: Workspace): MersenneTwisterFast = ws.mainRNG.clone
3753
}
3854

39-
class Evaluator(modelID: Int, name: String, ws: AbstractWorkspaceScala, parentWS: AbstractWorkspaceScala) {
55+
class Evaluator(modelID: Int, name: String, ws: AbstractWorkspace, parentWS: AbstractWorkspace) {
4056

4157
val mainOwner = new SimpleJobOwner(name, MainRNG(ws), AgentKind.Observer)
4258
val auxOwner = new SimpleJobOwner(name, AuxRNG(ws), AgentKind.Observer)

src/main/GUIPanel.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import org.nlogo.app.interfacetab.CommandCenter
88
import org.nlogo.swing.{ ScrollPane, SplitPane, Transparent }
99
import org.nlogo.theme.InterfaceColors
1010
import org.nlogo.window.{ Events, GUIWorkspace, TickCounterLabel }
11-
import org.nlogo.workspace.AbstractWorkspaceScala
11+
import org.nlogo.workspace.AbstractWorkspace
1212

13-
abstract class ModelPanel(ws: AbstractWorkspaceScala, panel: JPanel, verticalScroll: Int, resizeWeight: Int)
13+
abstract class ModelPanel(ws: AbstractWorkspace, panel: JPanel, verticalScroll: Int, resizeWeight: Int)
1414
extends JPanel with Events.OutputEvent.Handler {
1515
setLayout(new BorderLayout)
1616

@@ -91,5 +91,5 @@ extends ModelPanel(ws, panel, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, 1)
9191
}
9292
}
9393

94-
class HeadlessPanel(ws: AbstractWorkspaceScala, panel: JPanel)
94+
class HeadlessPanel(ws: AbstractWorkspace, panel: JPanel)
9595
extends ModelPanel(ws, panel, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, 0)

src/main/HeadlessChildModel.scala

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,13 @@ class HeadlessChildModel (parentWorkspace: AbstractWorkspace, path: String, mode
2424
null) {
2525

2626
override def sendOutput(oo: OutputObject, toOutputArea: Boolean): Unit = {
27-
frame.foreach { f => onEDT {
28-
new org.nlogo.window.Events.OutputEvent(false, oo, false, !toOutputArea, System.currentTimeMillis).raise(f)
29-
}}
27+
if (LevelSpace.isHeadless) {
28+
workspace.sendOutput(oo, toOutputArea)
29+
} else {
30+
frame.foreach { f => onEDT {
31+
new org.nlogo.window.Events.OutputEvent(false, oo, false, !toOutputArea, System.currentTimeMillis).raise(f)
32+
}}
33+
}
3034
}
3135

3236
override def runtimeError(owner: JobOwner, context: Context, instruction: Instruction, ex: Exception): Unit = {
@@ -54,12 +58,14 @@ class HeadlessChildModel (parentWorkspace: AbstractWorkspace, path: String, mode
5458
scheduledRepaint.setRepeats(false)
5559
// Since we never block on painting child models, we don't care if we have a world lock or not.
5660
override def updateDisplay(ignored: Boolean): Unit = {
57-
frame.foreach { f =>
58-
if (f.isVisible && !scheduledRepaint.isRunning) {
59-
// Not that if we don't max(0) here, the conversion to int can underflow
60-
val nextRepaint = (minTimeBetweenRepaints - timeSinceLastRepaint).max(0).toInt
61-
scheduledRepaint.setDelay(nextRepaint)
62-
scheduledRepaint.start()
61+
if (!LevelSpace.isHeadless) {
62+
frame.foreach { f =>
63+
if (f.isVisible && !scheduledRepaint.isRunning) {
64+
// Not that if we don't max(0) here, the conversion to int can underflow
65+
val nextRepaint = (minTimeBetweenRepaints - timeSinceLastRepaint).max(0).toInt
66+
scheduledRepaint.setDelay(nextRepaint)
67+
scheduledRepaint.start()
68+
}
6369
}
6470
}
6571
}
@@ -76,7 +82,8 @@ class HeadlessChildModel (parentWorkspace: AbstractWorkspace, path: String, mode
7682
workspace.requestDisplayUpdate(false)
7783
}
7884

79-
def isVisible: Boolean = frame.exists(_.isVisible)
85+
def isVisible: Boolean =
86+
!LevelSpace.isHeadless && frame.exists(_.isVisible)
8087

8188
def setSpeed(d: Double): Unit = {}
8289

src/main/LevelSpace.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,10 @@ class LevelSpace extends DefaultClassManager { // This can be accessed by both t
165165
}
166166

167167
def updateModelMenu(): Unit = {
168-
EventQueue.invokeLater { () =>
169-
modelManager.updateChildModels(models)
168+
if (!LevelSpace.isHeadless) {
169+
EventQueue.invokeLater { () =>
170+
modelManager.updateChildModels(models)
171+
}
170172
}
171173
}
172174

src/main/LevelSpaceMenu.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ import org.nlogo.app.codetab.CodeTab
1313
import org.nlogo.awt.UserCancelException
1414
import org.nlogo.fileformat.FileFormat
1515
import org.nlogo.swing.{ FileDialog, Menu, MenuItem }
16-
import org.nlogo.workspace.{ AbstractWorkspaceScala, ModelsLibrary, ModelTracker, SaveModel }
16+
import org.nlogo.workspace.{ AbstractWorkspace, ModelsLibrary, ModelTracker, SaveModel }
1717

1818
trait ModelManager {
1919
def removeTab(tab: ModelCodeTab): Unit
2020
def existingTab(filePath: String): Option[CodeTab]
2121
def registerTab(filePath: String)
22-
(f: AbstractWorkspaceScala => ModelCodeTab): Option[ModelCodeTab]
22+
(f: AbstractWorkspace => ModelCodeTab): Option[ModelCodeTab]
2323
}
2424

2525
trait LevelSpaceMenu extends JMenu

src/main/ModelCodeTab.scala

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ import org.nlogo.core.{ I18N, Model }
1111
import org.nlogo.fileformat.{ FailedConversionResult, FileFormat }
1212
import org.nlogo.swing.OptionPane
1313
import org.nlogo.window.Events.ModelSavedEvent
14-
import org.nlogo.workspace.{ AbstractWorkspaceScala, OpenModel, OpenModelFromURI, SaveModel, ModelsLibrary }
14+
import org.nlogo.workspace.{ AbstractWorkspace, ModelsLibrary, ModelTracker, OpenModel, OpenModelFromURI, SaveModel }
1515

1616
import java.nio.file.Paths
1717

18-
class ModelCodeTab(workspace: AbstractWorkspaceScala, tabManager: TabManager, modelManager: ModelManager)
18+
class ModelCodeTab(workspace: AbstractWorkspace, tabManager: TabManager, modelManager: ModelManager)
1919
extends CodeTab(workspace, tabManager)
2020
with ModelSavedEvent.Handler {
2121
val tabName = workspace.getModelFileName
@@ -105,11 +105,16 @@ with ModelSavedEvent.Handler {
105105
}
106106
currentModel = currentModel.map(_.copy(code = innerSource)) orElse Some(Model(code = innerSource))
107107
currentModel.foreach { model =>
108-
SaveModel(model, loader, controller, workspace, Version).foreach {
109-
_.apply().foreach { _ =>
110-
changedSourceWarning()
111-
dirty = false
112-
}
108+
workspace match {
109+
case tracker: ModelTracker =>
110+
SaveModel(model, loader, controller, workspace.asInstanceOf[ModelTracker], Version).foreach {
111+
_.apply().foreach { _ =>
112+
changedSourceWarning()
113+
dirty = false
114+
}
115+
}
116+
117+
case _ =>
113118
}
114119
}
115120
}

src/main/NotifyingJob.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ package org.nlogo.ls
33
import org.nlogo.agent.AgentSet
44
import org.nlogo.api.JobOwner
55
import org.nlogo.nvm.{ConcurrentJob, HaltException, Job, Procedure}
6-
import org.nlogo.workspace.AbstractWorkspaceScala
6+
import org.nlogo.workspace.AbstractWorkspace
77

88
class NotifyingJob(override val notifyObject: AnyRef,
9-
workspace: AbstractWorkspaceScala,
9+
workspace: AbstractWorkspace,
1010
owner: JobOwner,
1111
agentSet: AgentSet,
1212
procedure: Procedure)

0 commit comments

Comments
 (0)