-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #302 from apex-dev-tools/301-improve-schema-type-l…
…ookup-performance 301 improve schema type lookup performance
- Loading branch information
Showing
7 changed files
with
196 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
74 changes: 74 additions & 0 deletions
74
jvm/src/main/scala/com/nawforce/apexlink/org/TypeDeclarationCache.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/* | ||
* Copyright (c) 2024 Certinia Inc. All rights reserved. | ||
*/ | ||
|
||
package com.nawforce.apexlink.org | ||
|
||
import com.nawforce.apexlink.types.core.TypeDeclaration | ||
import com.nawforce.pkgforce.names.TypeName | ||
import com.nawforce.pkgforce.names.TypeNameFuncs.TypeNameFuncs | ||
|
||
import scala.collection.mutable | ||
|
||
/** Cache of TypeDeclarations against their TypeName. Provided to speed up Schema namespace | ||
* searched by avoiding the need to construct a new TypeName for each search. | ||
*/ | ||
class TypeDeclarationCache { | ||
private val allTypes = mutable.Map[TypeName, TypeDeclaration]() | ||
private val schemaTypes = mutable.Map[TypeName, TypeDeclaration]() | ||
|
||
def size: Int = { | ||
allTypes.size | ||
} | ||
|
||
def put(td: TypeDeclaration, altTypeName: Option[TypeName] = None): Unit = { | ||
put(altTypeName.getOrElse(td.typeName), td) | ||
} | ||
|
||
/** Upsert an entry. Beware, assumes the TypeName is fully qualified. */ | ||
def put(typeName: TypeName, td: TypeDeclaration): Unit = { | ||
allTypes.put(typeName, td) | ||
val stripped = typeName.replaceTail(TypeName.Schema, None) | ||
if (stripped ne typeName) | ||
schemaTypes.put(stripped, td) | ||
} | ||
|
||
def get(typeName: TypeName): Option[TypeDeclaration] = { | ||
allTypes.get(typeName) | ||
} | ||
|
||
def getUnsafe(typeName: TypeName): TypeDeclaration = { | ||
allTypes(typeName) | ||
} | ||
|
||
def getWithSchema(typeName: TypeName): Option[TypeDeclaration] = { | ||
schemaTypes.get(typeName) | ||
} | ||
|
||
def contains(typeName: TypeName): Boolean = { | ||
allTypes.contains(typeName) | ||
} | ||
|
||
def values(): Iterable[TypeDeclaration] = { | ||
allTypes.values | ||
} | ||
|
||
def filter( | ||
pred: ((TypeName, TypeDeclaration)) => Boolean | ||
): mutable.Map[TypeName, TypeDeclaration] = { | ||
allTypes.filter(pred) | ||
} | ||
|
||
def collect[T](pf: PartialFunction[(TypeName, TypeDeclaration), T]): mutable.Iterable[T] = { | ||
allTypes.collect(pf) | ||
} | ||
|
||
def remove(typeName: TypeName): Option[TypeDeclaration] = { | ||
val result = allTypes.remove(typeName) | ||
val stripped = typeName.replaceTail(TypeName.Schema, None) | ||
if (stripped != typeName) { | ||
schemaTypes.remove(stripped) | ||
} | ||
result | ||
} | ||
} |
72 changes: 72 additions & 0 deletions
72
jvm/src/test/scala/com/nawforce/apexlink/org/TypeDeclarationCacheTest.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/* | ||
* Copyright (c) 2024 Certinia Inc. All rights reserved. | ||
*/ | ||
package com.nawforce.apexlink.org | ||
|
||
import com.nawforce.apexlink.TestHelper | ||
import com.nawforce.apexlink.names.TypeNames | ||
import com.nawforce.pkgforce.names.TypeNameFuncs.TypeNameFuncs | ||
import com.nawforce.pkgforce.names.{Name, TypeName} | ||
import org.scalatest.Inspectors.forAll | ||
import org.scalatest.funsuite.AnyFunSuite | ||
|
||
class TypeDeclarationCacheTest extends AnyFunSuite with TestHelper { | ||
|
||
private val simpleTypeName = TypeName(Name("Simple")) | ||
private val scopedTypeName = TypeName(Name("Scoped"), Nil, Some(TypeName(Name("Outer")))) | ||
private val systemTypeName = TypeName(Name("String"), Nil, Some(TypeName(Name("System")))) | ||
private val genericTypeName = | ||
TypeName(Name("List"), systemTypeName :: Nil, Some(TypeName(Name("System")))) | ||
private val schemaTypeName = | ||
TypeName(Name("Account"), Nil, Some(TypeNames.Schema)) | ||
private val nestedSchemaTypeName = | ||
TypeName(Name("Fake"), Nil, Some(TypeName(Name("Account"), Nil, Some(TypeNames.Schema)))) | ||
|
||
test("Empty cache behaviour") { | ||
val cache = new TypeDeclarationCache() | ||
assert(cache.size == 0) | ||
assert(cache.values().toList == Nil) | ||
assert(cache.filter(_ => true).isEmpty) | ||
assert(cache.collect { case _ => }.isEmpty) | ||
} | ||
|
||
forAll( | ||
List( | ||
simpleTypeName, | ||
scopedTypeName, | ||
systemTypeName, | ||
genericTypeName, | ||
schemaTypeName, | ||
nestedSchemaTypeName | ||
) | ||
) { testTypeName => | ||
test(s"$testTypeName name can be added and removed") { | ||
val cache = new TypeDeclarationCache() | ||
cache.put(testTypeName, null) | ||
|
||
assert(cache.size == 1) | ||
assert(cache.contains(testTypeName)) | ||
assert(cache.get(testTypeName).contains(null)) | ||
assert(cache.getUnsafe(testTypeName) == null) | ||
assert(cache.getWithSchema(testTypeName).isEmpty) | ||
val stripped = testTypeName.replaceTail(TypeName.Schema, None) | ||
if (stripped != testTypeName) | ||
assert(cache.getWithSchema(stripped).nonEmpty) | ||
assert(cache.values().toList == List(null)) | ||
assert(cache.filter(_._1 == testTypeName).nonEmpty) | ||
assert(cache.collect { case (t: TypeName, null) if t == testTypeName => }.nonEmpty) | ||
|
||
assert(cache.remove(testTypeName).contains(null)) | ||
assert(cache.size == 0) | ||
assert(!cache.contains(testTypeName)) | ||
assert(cache.get(testTypeName).isEmpty) | ||
assert(cache.getWithSchema(testTypeName).isEmpty) | ||
if (stripped != testTypeName) | ||
assert(cache.getWithSchema(stripped).isEmpty) | ||
assert(cache.values().toList == Nil) | ||
assert(cache.filter(_._1 == testTypeName).isEmpty) | ||
assert(cache.collect { case (t: TypeName, null) if t == testTypeName => }.isEmpty) | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters