Skip to content

Commit 1fd1389

Browse files
authored
Upgrade to scala 2.13 (#7327)
* WIP: Upgrade to scala 2.13 * compiler options * adapt code to api changes * get rid of iteratees in datastore * fix compiler errors in datastore * fix compiler issues in wk module * update slick * fix zipio * WIP: add play-swagger inside of repo * compatible versions for swagger+jackson * fix api prefixes in swagger schema * bump wk-wrap version * deal with some deprecation warnings * no more symbols in jspaths * four more warnings * deal with scala deprecation warnings * add license for swagger module * adapt tests * changelog * refresh snapshots (default map ordering has changed) * remove commented out line * remove Symbol wrapper in scriptPublicReads * add comment on unsafeWrapArray
1 parent fa55e89 commit 1fd1389

File tree

97 files changed

+2258
-660
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+2258
-660
lines changed

CHANGELOG.unreleased.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
1515
- Added social media link previews for links to datasets and annotations (only if they are public or if the links contain sharing tokens). [#7331](https://github.com/scalableminds/webknossos/pull/7331)
1616

1717
### Changed
18+
- Updated backend code to Scala 2.13, with upgraded Dependencies for optimized performance. [#7327](https://github.com/scalableminds/webknossos/pull/7327)
1819

1920
### Fixed
2021
- Fixed that segment statistics were requested in the wrong resolution and without properly considering the dataset scale. [#7355](https://github.com/scalableminds/webknossos/pull/7355)

app/controllers/AnnotationIOController.scala

+6-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import akka.actor.ActorSystem
66
import akka.stream.Materializer
77
import com.mohiva.play.silhouette.api.Silhouette
88
import com.scalableminds.util.accesscontext.{DBAccessContext, GlobalAccessContext}
9-
import com.scalableminds.util.io.{NamedEnumeratorStream, ZipIO}
9+
import com.scalableminds.util.io.ZipIO
1010
import com.scalableminds.util.tools.{Fox, FoxImplicits, TextUtils}
1111
import com.scalableminds.webknossos.datastore.SkeletonTracing.{SkeletonTracing, SkeletonTracingOpt, SkeletonTracings}
1212
import com.scalableminds.webknossos.datastore.VolumeTracing.{VolumeTracing, VolumeTracingOpt, VolumeTracings}
@@ -383,7 +383,8 @@ Expects:
383383
tracingStoreClient.getSkeletonTracing(_, skeletonVersion))
384384
user <- userService.findOneCached(annotation._user)(GlobalAccessContext)
385385
taskOpt <- Fox.runOptional(annotation._task)(taskDAO.findOne)
386-
nmlStream = nmlWriter.toNmlStream(fetchedAnnotationLayers,
386+
nmlStream = nmlWriter.toNmlStream("temp",
387+
fetchedAnnotationLayers,
387388
Some(annotation),
388389
dataset.scale,
389390
None,
@@ -394,7 +395,7 @@ Expects:
394395
taskOpt)
395396
nmlTemporaryFile = temporaryFileCreator.create()
396397
temporaryFileStream = new BufferedOutputStream(new FileOutputStream(nmlTemporaryFile))
397-
_ <- NamedEnumeratorStream("", nmlStream).writeTo(temporaryFileStream)
398+
_ <- nmlStream.writeTo(temporaryFileStream)
398399
_ = temporaryFileStream.close()
399400
} yield nmlTemporaryFile
400401

@@ -415,6 +416,7 @@ Expects:
415416
user <- userService.findOneCached(annotation._user)(GlobalAccessContext) ?~> "annotation.download.findUser.failed"
416417
taskOpt <- Fox.runOptional(annotation._task)(taskDAO.findOne)
417418
nmlStream = nmlWriter.toNmlStream(
419+
name,
418420
fetchedSkeletonLayers ::: fetchedVolumeLayers,
419421
Some(annotation),
420422
dataset.scale,
@@ -428,7 +430,7 @@ Expects:
428430
)
429431
temporaryFile = temporaryFileCreator.create()
430432
zipper = ZipIO.startZip(new BufferedOutputStream(new FileOutputStream(new File(temporaryFile.path.toString))))
431-
_ <- zipper.addFileFromEnumerator(name + ".nml", nmlStream) ?~> "annotation.download.zipNml.failed"
433+
_ <- zipper.addFileFromNamedStream(nmlStream, suffix = ".nml") ?~> "annotation.download.zipNml.failed"
432434
_ = fetchedVolumeLayers.zipWithIndex.map {
433435
case (volumeLayer, index) =>
434436
volumeLayer.volumeDataOpt.foreach { volumeData =>

app/controllers/Application.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ class Application @Inject()(multiUserDAO: MultiUserDAO,
4747
addRemoteOriginHeaders(
4848
Ok(
4949
Json.obj(
50-
"webknossos" -> webknossos.BuildInfo.toMap.mapValues(_.toString),
51-
"webknossos-wrap" -> webknossoswrap.BuildInfo.toMap.mapValues(_.toString),
50+
"webknossos" -> Json.toJson(webknossos.BuildInfo.toMap.view.mapValues(_.toString).toMap),
51+
"webknossos-wrap" -> Json.toJson(webknossoswrap.BuildInfo.toMap.view.mapValues(_.toString).toMap),
5252
"schemaVersion" -> schemaVersion.toOption,
5353
"localDataStoreEnabled" -> storeModules.localDataStoreEnabled,
5454
"localTracingStoreEnabled" -> storeModules.localTracingStoreEnabled

app/controllers/AuthenticationController.scala

+184-170
Large diffs are not rendered by default.

app/controllers/DataStoreController.scala

+11-11
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,19 @@ class DataStoreController @Inject()(dataStoreDAO: DataStoreDAO,
2525
with FoxImplicits {
2626

2727
private val dataStoreReads: Reads[DataStore] =
28-
((__ \ 'name).read[String] and
29-
(__ \ 'url).read[String] and
30-
(__ \ 'publicUrl).read[String] and
31-
(__ \ 'key).read[String] and
32-
(__ \ 'isScratch).readNullable[Boolean] and
33-
(__ \ 'allowsUpload).readNullable[Boolean])(DataStore.fromForm _)
28+
((__ \ "name").read[String] and
29+
(__ \ "url").read[String] and
30+
(__ \ "publicUrl").read[String] and
31+
(__ \ "key").read[String] and
32+
(__ \ "isScratch").readNullable[Boolean] and
33+
(__ \ "allowsUpload").readNullable[Boolean])(DataStore.fromForm _)
3434

3535
private val dataStorePublicReads: Reads[DataStore] =
36-
((__ \ 'name).read[String] and
37-
(__ \ 'url).read[String] and
38-
(__ \ 'publicUrl).read[String] and
39-
(__ \ 'isScratch).readNullable[Boolean] and
40-
(__ \ 'allowsUpload).readNullable[Boolean])(DataStore.fromUpdateForm _)
36+
((__ \ "name").read[String] and
37+
(__ \ "url").read[String] and
38+
(__ \ "publicUrl").read[String] and
39+
(__ \ "isScratch").readNullable[Boolean] and
40+
(__ \ "allowsUpload").readNullable[Boolean])(DataStore.fromUpdateForm _)
4141
@ApiOperation(value = "List all available datastores", nickname = "datastoreList")
4242
@ApiResponses(
4343
Array(new ApiResponse(code = 200, message = "JSON list of objects containing datastore information"),

app/controllers/DatasetController.scala

+6-6
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,12 @@ class DatasetController @Inject()(userService: UserService,
7878
extends Controller {
7979

8080
private val datasetPublicReads =
81-
((__ \ 'description).readNullable[String] and
82-
(__ \ 'displayName).readNullable[String] and
83-
(__ \ 'sortingKey).readNullable[Instant] and
84-
(__ \ 'isPublic).read[Boolean] and
85-
(__ \ 'tags).read[List[String]] and
86-
(__ \ 'folderId).readNullable[ObjectId]).tupled
81+
((__ \ "description").readNullable[String] and
82+
(__ \ "displayName").readNullable[String] and
83+
(__ \ "sortingKey").readNullable[Instant] and
84+
(__ \ "isPublic").read[Boolean] and
85+
(__ \ "tags").read[List[String]] and
86+
(__ \ "folderId").readNullable[ObjectId]).tupled
8787

8888
@ApiOperation(hidden = true, value = "")
8989
def removeFromThumbnailCache(organizationName: String, dataSetName: String): Action[AnyContent] =

app/controllers/JobsController.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ class JobsController @Inject()(jobDAO: JobDAO,
291291
}
292292
}
293293

294-
def export(jobId: String): Action[AnyContent] =
294+
def redirectToExport(jobId: String): Action[AnyContent] =
295295
sil.SecuredAction.async { implicit request =>
296296
for {
297297
jobIdValidated <- ObjectId.fromString(jobId)

app/controllers/OrganizationController.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,8 @@ class OrganizationController @Inject()(
189189
}
190190

191191
private val organizationUpdateReads =
192-
((__ \ 'displayName).read[String] and
193-
(__ \ 'newUserMailingList).read[String]).tupled
192+
((__ \ "displayName").read[String] and
193+
(__ \ "newUserMailingList").read[String]).tupled
194194

195195
def sendExtendPricingPlanEmail(): Action[AnyContent] = sil.SecuredAction.async { implicit request =>
196196
for {

app/controllers/ScriptController.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ class ScriptController @Inject()(scriptDAO: ScriptDAO,
2323
with FoxImplicits {
2424

2525
private val scriptPublicReads =
26-
((__ \ 'name).read[String](minLength[String](2) or maxLength[String](50)) and
27-
(__ \ 'gist).read[String] and
28-
(__ \ 'owner).read[ObjectId])(Script.fromForm _)
26+
((__ \ "name").read[String](minLength[String](2) or maxLength[String](50)) and
27+
(__ \ "gist").read[String] and
28+
(__ \ "owner").read[ObjectId])(Script.fromForm _)
2929

3030
def create: Action[JsValue] = sil.SecuredAction.async(parse.json) { implicit request =>
3131
withJsonBodyUsing(scriptPublicReads) { script =>
+12-10
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
11
package controllers
22

3-
import akka.stream.scaladsl.Source
43
import com.google.inject.Inject
54
import com.mohiva.play.silhouette.api.Silhouette
65
import oxalis.security.WkEnv
7-
import play.api.libs.iteratee.streams.IterateeStreams
86
import play.api.mvc.{Action, AnyContent}
97
import utils.SitemapWriter
108

11-
class SitemapController @Inject()(sitemapWriter: SitemapWriter, sil: Silhouette[WkEnv]) extends Controller {
9+
import scala.concurrent.ExecutionContext
1210

13-
// Only called explicitly via RequestHandler
14-
def getSitemap(prefix: String): Action[AnyContent] = sil.UserAwareAction {
15-
val downloadStream = sitemapWriter.toSitemapStream(prefix)
11+
class SitemapController @Inject()(sitemapWriter: SitemapWriter, sil: Silhouette[WkEnv])(implicit ec: ExecutionContext)
12+
extends Controller {
1613

17-
Ok.chunked(Source.fromPublisher(IterateeStreams.enumeratorToPublisher(downloadStream)))
18-
.as(xmlMimeType)
19-
.withHeaders(CONTENT_DISPOSITION ->
20-
"""sitemap.xml""")
14+
// Only called explicitly via RequestHandler
15+
def getSitemap(prefix: String): Action[AnyContent] = sil.UserAwareAction.async { implicit request =>
16+
for {
17+
sitemap <- sitemapWriter.getSitemap(prefix)
18+
} yield
19+
Ok(sitemap)
20+
.as(xmlMimeType)
21+
.withHeaders(CONTENT_DISPOSITION ->
22+
"""sitemap.xml""")
2123
}
2224

2325
}

app/controllers/TaskController.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class TaskController @Inject()(taskCreationService: TaskCreationService,
6969
taskParameters)
7070
volumeBaseOpts: List[Option[(VolumeTracing, Option[File])]] <- taskCreationService
7171
.createTaskVolumeTracingBases(taskParameters, request.identity._organization)
72-
paramsWithTracings = (taskParameters, skeletonBaseOpts, volumeBaseOpts).zipped.map {
72+
paramsWithTracings = taskParameters.lazyZip(skeletonBaseOpts).lazyZip(volumeBaseOpts).map {
7373
case (params, skeletonOpt, volumeOpt) => Full((params, skeletonOpt, volumeOpt))
7474
}
7575
result <- taskCreationService.createTasks(paramsWithTracings, request.identity)

app/controllers/TaskTypeController.scala

+6-6
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ class TaskTypeController @Inject()(taskTypeDAO: TaskTypeDAO,
2727
with FoxImplicits {
2828

2929
private val taskTypePublicReads =
30-
((__ \ 'summary).read[String](minLength[String](2) or maxLength[String](50)) and
31-
(__ \ 'description).read[String] and
32-
(__ \ 'teamId).read[ObjectId] and
33-
(__ \ 'settings).read[AnnotationSettings] and
34-
(__ \ 'recommendedConfiguration).readNullable[JsValue] and
35-
(__ \ 'tracingType).read[TracingType.Value])(taskTypeService.fromForm _)
30+
((__ \ "summary").read[String](minLength[String](2) or maxLength[String](50)) and
31+
(__ \ "description").read[String] and
32+
(__ \ "teamId").read[ObjectId] and
33+
(__ \ "settings").read[AnnotationSettings] and
34+
(__ \ "recommendedConfiguration").readNullable[JsValue] and
35+
(__ \ "tracingType").read[TracingType.Value])(taskTypeService.fromForm _)
3636

3737
def create: Action[JsValue] = sil.SecuredAction.async(parse.json) { implicit request =>
3838
withJsonBodyUsing(taskTypePublicReads) { taskType =>

app/controllers/TracingStoreController.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ class TracingStoreController @Inject()(tracingStoreService: TracingStoreService,
1717
extends Controller
1818
with FoxImplicits {
1919
private val tracingStorePublicReads: Reads[TracingStore] =
20-
((__ \ 'name).read[String] and
21-
(__ \ 'url).read[String] and
22-
(__ \ 'publicUrl).read[String])(TracingStore.fromUpdateForm _)
20+
((__ \ "name").read[String] and
21+
(__ \ "url").read[String] and
22+
(__ \ "publicUrl").read[String])(TracingStore.fromUpdateForm _)
2323

2424
def listOne: Action[AnyContent] = sil.UserAwareAction.async { implicit request =>
2525
for {

app/models/annotation/AnnotationIdentifier.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import scala.concurrent.ExecutionContext
99
case class AnnotationIdentifier(annotationType: AnnotationType, identifier: ObjectId) {
1010

1111
def toUniqueString: String =
12-
annotationType + "__" + identifier
12+
f"${annotationType}__$identifier"
1313

1414
}
1515

app/models/annotation/AnnotationPrivateLink.scala

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import com.scalableminds.util.tools.Fox
66
import com.scalableminds.webknossos.schema.Tables._
77
import oxalis.security.RandomIDGenerator
88
import play.api.libs.json.{JsValue, Json, OFormat}
9-
import slick.jdbc.PostgresProfile.api._
109
import slick.lifted.Rep
1110
import utils.ObjectId
1211
import utils.sql.{SQLDAO, SqlClient, SqlToken}

app/models/annotation/AnnotationService.scala

+24-26
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import akka.actor.ActorSystem
44
import akka.stream.Materializer
55
import com.scalableminds.util.accesscontext.{AuthorizedAccessContext, DBAccessContext, GlobalAccessContext}
66
import com.scalableminds.util.geometry.{BoundingBox, Vec3Double, Vec3Int}
7-
import com.scalableminds.util.io.ZipIO
7+
import com.scalableminds.util.io.{NamedStream, ZipIO}
88
import com.scalableminds.util.mvc.Formatter
99
import com.scalableminds.util.time.Instant
1010
import com.scalableminds.util.tools.{BoxImplicits, Fox, FoxImplicits, TextUtils}
@@ -43,7 +43,6 @@ import models.annotation.AnnotationType.AnnotationType
4343
import models.annotation.handler.SavedTracingInformationHandler
4444
import models.annotation.nml.NmlWriter
4545
import models.binary._
46-
import models.mesh.{MeshDAO, MeshService}
4746
import models.organization.OrganizationDAO
4847
import models.project.ProjectDAO
4948
import models.task.{Task, TaskDAO, TaskService, TaskTypeDAO}
@@ -52,7 +51,6 @@ import models.user.{User, UserDAO, UserService}
5251
import net.liftweb.common.{Box, Full}
5352
import play.api.i18n.{Messages, MessagesProvider}
5453
import play.api.libs.Files.{TemporaryFile, TemporaryFileCreator}
55-
import play.api.libs.iteratee.Enumerator
5654
import play.api.libs.json.{JsNull, JsObject, JsValue, Json}
5755
import utils.{ObjectId, WkConf}
5856

@@ -106,8 +104,6 @@ class AnnotationService @Inject()(
106104
annotationRestrictionDefults: AnnotationRestrictionDefaults,
107105
nmlWriter: NmlWriter,
108106
temporaryFileCreator: TemporaryFileCreator,
109-
meshDAO: MeshDAO,
110-
meshService: MeshService,
111107
conf: WkConf,
112108
)(implicit ec: ExecutionContext, val materializer: Materializer)
113109
extends BoxImplicits
@@ -643,7 +639,7 @@ class AnnotationService @Inject()(
643639
ctx: DBAccessContext): Fox[TemporaryFile] =
644640
for {
645641
downloadAnnotations <- getTracingsScalesAndNamesFor(annotations, skipVolumeData)
646-
nmlsAndNames <- Fox.serialCombined(downloadAnnotations.flatten) {
642+
nmlsAndVolumes <- Fox.serialCombined(downloadAnnotations.flatten) {
647643
case DownloadAnnotation(skeletonTracingIdOpt,
648644
volumeTracingIdOpt,
649645
skeletonTracingOpt,
@@ -661,18 +657,21 @@ class AnnotationService @Inject()(
661657
volumeTracingIdOpt,
662658
skeletonTracingOpt,
663659
volumeTracingOpt)
664-
nml = nmlWriter.toNmlStream(fetchedAnnotationLayersForAnnotation,
665-
Some(annotation),
666-
scaleOpt,
667-
Some(name + "_data.zip"),
668-
organizationName,
669-
conf.Http.uri,
670-
datasetName,
671-
Some(user),
672-
taskOpt)
673-
} yield (nml, name, volumeDataOpt)
660+
nml = nmlWriter.toNmlStream(
661+
name,
662+
fetchedAnnotationLayersForAnnotation,
663+
Some(annotation),
664+
scaleOpt,
665+
Some(name + "_data.zip"),
666+
organizationName,
667+
conf.Http.uri,
668+
datasetName,
669+
Some(user),
670+
taskOpt
671+
)
672+
} yield (nml, volumeDataOpt)
674673
}
675-
zip <- createZip(nmlsAndNames, zipFileName)
674+
zip <- createZip(nmlsAndVolumes, zipFileName)
676675
} yield zip
677676

678677
private def getTracingsScalesAndNamesFor(annotations: List[Annotation], skipVolumeData: Boolean)(
@@ -768,27 +767,26 @@ class AnnotationService @Inject()(
768767
Fox.combined(tracingsGrouped.toList)
769768
}
770769

771-
private def createZip(nmls: List[(Enumerator[Array[Byte]], String, Option[Array[Byte]])],
772-
zipFileName: String): Future[TemporaryFile] = {
770+
private def createZip(nmls: List[(NamedStream, Option[Array[Byte]])], zipFileName: String): Fox[TemporaryFile] = {
773771
val zipped = temporaryFileCreator.create(TextUtils.normalize(zipFileName), ".zip")
774772
val zipper = ZipIO.startZip(new BufferedOutputStream(new FileOutputStream(new File(zipped.path.toString))))
775773

776-
def addToZip(nmls: List[(Enumerator[Array[Byte]], String, Option[Array[Byte]])]): Future[Boolean] =
774+
def addToZip(nmls: List[(NamedStream, Option[Array[Byte]])]): Fox[Boolean] =
777775
nmls match {
778-
case (nml, name, volumeDataOpt) :: tail =>
776+
case (nml, volumeDataOpt) :: tail =>
779777
if (volumeDataOpt.isDefined) {
780-
val subZip = temporaryFileCreator.create(TextUtils.normalize(name), ".zip")
778+
val subZip = temporaryFileCreator.create(TextUtils.normalize(nml.name), ".zip")
781779
val subZipper =
782780
ZipIO.startZip(new BufferedOutputStream(new FileOutputStream(new File(subZip.path.toString))))
783-
volumeDataOpt.foreach(volumeData => subZipper.addFileFromBytes(name + "_data.zip", volumeData))
781+
volumeDataOpt.foreach(volumeData => subZipper.addFileFromBytes(nml.name + "_data.zip", volumeData))
784782
for {
785-
_ <- subZipper.addFileFromEnumerator(name + ".nml", nml)
783+
_ <- subZipper.addFileFromNamedStream(nml, suffix = ".nml")
786784
_ = subZipper.close()
787-
_ = zipper.addFileFromTemporaryFile(name + ".zip", subZip)
785+
_ = zipper.addFileFromTemporaryFile(nml.name + ".zip", subZip)
788786
res <- addToZip(tail)
789787
} yield res
790788
} else {
791-
zipper.addFileFromEnumerator(name + ".nml", nml).flatMap(_ => addToZip(tail))
789+
zipper.addFileFromNamedStream(nml, suffix = ".nml").flatMap(_ => addToZip(tail))
792790
}
793791
case _ =>
794792
Future.successful(true)

app/models/annotation/nml/NmlParser.scala

+9-12
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,8 @@ object NmlParser extends LazyLogging with ProtoGeometryImplicits with ColorGener
262262
depth <- getSingleAttribute(node, "depth").toIntOpt
263263
} yield BoundingBox(Vec3Int(topLeftX, topLeftY, topLeftZ), width, height, depth)
264264

265-
private def parseAdditionalAxes(nodes: NodeSeq)(implicit m: MessagesProvider) = {
266-
val additionalAxes = nodes.headOption.map(
265+
private def parseAdditionalAxes(nodes: NodeSeq)(implicit m: MessagesProvider): Box[Seq[AdditionalAxisProto]] = {
266+
val additionalAxes: Option[collection.Seq[AdditionalAxisProto]] = nodes.headOption.map(
267267
_.child.flatMap(
268268
additionalAxisNode => {
269269
for {
@@ -281,7 +281,7 @@ object NmlParser extends LazyLogging with ProtoGeometryImplicits with ColorGener
281281
additionalAxes match {
282282
case Some(axes) =>
283283
if (axes.map(_.name).distinct.size == axes.size) {
284-
Full(axes)
284+
Full(axes.toSeq)
285285
} else {
286286
Failure(Messages("nml.additionalCoordinates.notUnique"))
287287
}
@@ -519,17 +519,14 @@ object NmlParser extends LazyLogging with ProtoGeometryImplicits with ColorGener
519519
}
520520

521521
private def parseAdditionalCoordinateValues(node: XMLNode): Seq[AdditionalCoordinateProto] = {
522-
val regex = "additionalCoordinate-(\\w)".r("name")
522+
val regex = "^additionalCoordinate-(\\w)".r
523523
node.attributes.flatMap {
524-
case attribute: Attribute => {
525-
if (attribute.key.startsWith("additionalCoordinate")) {
526-
Some(
527-
new AdditionalCoordinateProto(regex.findAllIn(attribute.key).group("name"),
528-
attribute.value.toString().toInt))
529-
} else {
530-
None
524+
case attribute: Attribute =>
525+
attribute.key match {
526+
case regex(axisName) =>
527+
Some(new AdditionalCoordinateProto(axisName, attribute.value.toString().toInt))
528+
case _ => None
531529
}
532-
}
533530
case _ => None
534531
}.toSeq
535532
}

0 commit comments

Comments
 (0)