-
Notifications
You must be signed in to change notification settings - Fork 56
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 #301 from michelchan/morefiles
Adding More IR Files
- Loading branch information
Showing
11 changed files
with
888 additions
and
607 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package org.finos.morphir.ir.gen1 | ||
|
||
trait Names | ||
extends FQNameModule | ||
with ModuleNameModule | ||
with NameModule | ||
with NamespaceModule | ||
with NodeIDModule | ||
with PathModule | ||
with PackageNameModule | ||
with QualifiedModuleNameModule | ||
with QNameModule | ||
with NamingOptionsModule |
11 changes: 11 additions & 0 deletions
11
morphir/src/org/finos/morphir/ir/gen1/NamingOptionsModule.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,11 @@ | ||
package org.finos.morphir.ir.gen1 | ||
|
||
trait NamingOptionsModule { self: PackageNameModule with ModuleNameModule => | ||
|
||
sealed case class FQNamingOptions(defaultPackage: PackageName, defaultModule: ModuleName, defaultSeparator: String) | ||
|
||
object FQNamingOptions { | ||
implicit val default: FQNamingOptions = | ||
FQNamingOptions(PackageName.empty, ModuleName.empty, ":") | ||
} | ||
} |
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,106 @@ | ||
package org.finos.morphir.ir.gen1 | ||
|
||
trait FQNameModule { | ||
self: NameModule with ModuleNameModule with NamespaceModule with PackageNameModule with PathModule | ||
with QualifiedModuleNameModule | ||
with QNameModule | ||
with NamingOptionsModule => | ||
|
||
sealed case class FQName(packagePath: PackageName, modulePath: ModuleName, localName: Name) { self => | ||
def getPackagePath: Path = packagePath.toPath | ||
|
||
def getModulePath: Path = modulePath.toPath | ||
def getModuleName: ModuleName = modulePath | ||
|
||
/// An alias for `packagePath` | ||
def pack: PackageName = packagePath | ||
|
||
def toReferenceName: String = Seq( | ||
Path.toString(Name.toTitleCase, ".", packagePath.toPath), | ||
Path.toString(Name.toTitleCase, ".", modulePath.toPath), | ||
localName.toTitleCase | ||
).mkString(".") | ||
|
||
override def toString: String = Array( | ||
Path.toString(Name.toTitleCase, ".", packagePath.toPath), | ||
Path.toString(Name.toTitleCase, ".", modulePath.toPath), | ||
Name.toCamelCase(localName) | ||
).mkString(":") | ||
|
||
def toStringTitleCase: String = Array( | ||
Path.toString(Name.toTitleCase, ".", packagePath.toPath), | ||
Path.toString(Name.toTitleCase, ".", modulePath.toPath), | ||
Name.toTitleCase(this.localName) | ||
).mkString(":") | ||
} | ||
|
||
object FQName { | ||
val empty: FQName = FQName(PackageName.empty, ModuleName.empty, Name.empty) | ||
// def apply(packagePath: Path, modulePath: Path, localName: Name): FQName = | ||
// FQName(PackageName(packagePath), ModulePath(modulePath), localName) | ||
|
||
val fqName: Path => Path => Name => FQName = packagePath => | ||
modulePath => localName => FQName(PackageName.fromPath(packagePath), ModuleName(modulePath), localName) | ||
|
||
def fromQName(packagePath: Path, qName: QName): FQName = | ||
FQName(PackageName.fromPath(packagePath), ModuleName(qName.modulePath), qName.localName) | ||
|
||
def fromQName(qName: QName)(implicit packageName: PackageName): FQName = | ||
FQName(packageName, ModuleName(qName.modulePath), qName.localName) | ||
|
||
def fromQName(qName: QName)(implicit options: FQNamingOptions): FQName = | ||
FQName(options.defaultPackage, ModuleName(QName.getModulePath(qName)), QName.getLocalName(qName)) | ||
|
||
/** Get the package path part of a fully-qualified name. */ | ||
def getPackagePath(fqName: FQName): Path = fqName.getPackagePath | ||
|
||
/** Get the module path part of a fully-qualified name */ | ||
def getModulePath(fqName: FQName): Path = fqName.getModulePath | ||
|
||
/** Get the local name part of a fully-qualified name */ | ||
def getLocalName(fqName: FQName): Name = fqName.localName | ||
|
||
/** Convenience function to create a fully-qualified name from 3 strings */ | ||
def fqn(packageName: String, moduleName: String, localName: String): FQName = | ||
FQName(PackageName.fromString(packageName), ModuleName.fromString(moduleName), Name.fromString(localName)) | ||
|
||
/** Convenience function to create a fully-qualified name from 2 strings with default package name */ | ||
def fqn(moduleName: String, localName: String)(implicit options: FQNamingOptions): FQName = | ||
FQName(options.defaultPackage, ModuleName(Path.fromString(moduleName)), Name.fromString(localName)) | ||
|
||
/** Convenience function to create a fully-qualified name from 1 string with defaults for package and module */ | ||
def fqn(localName: String)(implicit options: FQNamingOptions): FQName = | ||
FQName(options.defaultPackage, options.defaultModule, Name.fromString(localName)) | ||
|
||
/// Convenience function to create a fully-qualified name from a local name and an implicitly provided `QualifiedModuleName`. | ||
def fromLocalName(localName: String)(implicit qualifiedModuleName: QualifiedModuleName): FQName = | ||
FQName(qualifiedModuleName.packageName, qualifiedModuleName.modulePath, Name.fromString(localName)) | ||
|
||
def fromLocalName(localName: Name)(implicit qualifiedModuleName: QualifiedModuleName): FQName = | ||
FQName(qualifiedModuleName.packageName, qualifiedModuleName.modulePath, localName) | ||
|
||
def toString(fqName: FQName): String = fqName.toString | ||
|
||
/** Parse a string into a FQName using splitter as the separator between package, module, and local names */ | ||
def fromString(fqNameString: String, splitter: String)(implicit options: FQNamingOptions): FQName = | ||
fqNameString.split(splitter) match { | ||
case Array(packageNameString, moduleNameString, localNameString) => | ||
fqn(packageNameString, moduleNameString, localNameString) | ||
case Array(moduleNameString, localNameString) => | ||
fqn(moduleNameString, localNameString) | ||
case Array(localNameString) => | ||
fqn(localNameString) | ||
case _ => throw FQNameParsingError(fqNameString) | ||
} | ||
|
||
def fromString(fqNameString: String)(implicit options: FQNamingOptions): FQName = | ||
fromString(fqNameString, options.defaultSeparator) | ||
|
||
object ReferenceName { | ||
def unapply(fqName: FQName): Some[String] = Some(fqName.toReferenceName) | ||
} | ||
} | ||
|
||
sealed case class FQNameParsingError(invalidName: String) | ||
extends Exception(s"Unable to parse: [$invalidName] into a valid FQName") | ||
} |
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,55 @@ | ||
package org.finos.morphir.ir.gen1 | ||
|
||
object naming extends Names { | ||
|
||
final implicit class PackageNameSyntax(val self: PackageName) extends AnyVal { | ||
def /(moduleName: ModuleName): QualifiedModuleName = QualifiedModuleName(self, moduleName) | ||
} | ||
|
||
final implicit class QualifiedModuleNameSyntax(val self: QualifiedModuleName) extends AnyVal { | ||
def %(localName: String): FQName = FQName(self.packageName, self.modulePath, Name.fromString(localName)) | ||
def %(name: Name): FQName = FQName(self.packageName, self.modulePath, name) | ||
} | ||
|
||
final implicit class NamingHelper(val sc: StringContext) extends AnyVal { | ||
|
||
def fqn(args: Any*): FQName = { | ||
val interlaced = interlace(sc.parts, args.map(_.toString)) | ||
FQName.fromString(interlaced.mkString) | ||
} | ||
|
||
def mod(args: Any*): ModuleName = { | ||
val interlaced = interlace(sc.parts, args.map(_.toString)) | ||
ModuleName.fromString(interlaced.mkString) | ||
} | ||
|
||
def n(args: Any*): Name = { | ||
val interlaced = interlace(sc.parts, args.map(_.toString)) | ||
Name.fromString(interlaced.mkString) | ||
} | ||
|
||
def qmn(args: Any*): QualifiedModuleName = { | ||
val interlaced = interlace(sc.parts, args.map(_.toString)) | ||
QualifiedModuleName.fromString(interlaced.mkString) | ||
} | ||
|
||
def name(args: Any*): Name = { | ||
val interlaced = interlace(sc.parts, args.map(_.toString)) | ||
Name.fromString(interlaced.mkString) | ||
} | ||
|
||
def pkg(args: Any*): PackageName = { | ||
val interlaced = interlace(sc.parts, args.map(_.toString)) | ||
PackageName.fromString(interlaced.mkString) | ||
} | ||
|
||
def path(args: Any*): Path = { | ||
val interlaced = interlace(sc.parts, args.map(_.toString)) | ||
Path.fromString(interlaced.mkString) | ||
} | ||
} | ||
|
||
private[morphir] def interlace[T](a: Iterable[T], b: Iterable[T]): List[T] = | ||
if (a.isEmpty) b.toList | ||
else a.head +: interlace(b, a.tail) | ||
} |
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,158 @@ | ||
package org.finos.morphir.ir.gen1 | ||
|
||
trait NodeIDModule { | ||
self: FQNameModule with NamingOptionsModule with NameModule with PathModule with PackageNameModule | ||
with ModuleNameModule | ||
with QualifiedModuleNameModule => | ||
import NodePath.* | ||
|
||
/** Represents a path in the IR. | ||
* ==Overview== | ||
* A NodeID can have two slightly different structures depending on if we are refering to modules or definitions | ||
* (types/values). | ||
* | ||
* - When refefering to modules: `"module:<Package>:<Module>"` | ||
* - When refering to definitions: `"type\value:<Package>:<Module><localName>#<nodePath>"`, where nodePath is | ||
* optional | ||
* | ||
* Examples of valid NodeIDs: | ||
* - "module:Morphir.Reference.Model:BooksAndRecords" | ||
* - "type:Morphir.Reference.Model:BooksAndRecords:deal" | ||
* - "value:Morphir.Reference.Model:BooksAndRecords:deal#1" | ||
* | ||
* ==Referring to modules== | ||
* We can refer to modules by their Qualified Name, with the module: prefix | ||
* | ||
* For example: `"module:Morphir.Reference.Model:BooksAndRecords"` refers to the `Books and Records` module inside | ||
* the `Morphir.Reference.Model` package. | ||
*/ | ||
sealed trait NodeID extends Product with Serializable { self => | ||
import NodeID.* | ||
override def toString(): String = { | ||
implicit val renderer: PathRenderer = PathRenderer.TitleCase | ||
def mapToTypeOrValue( | ||
packageName: Path, | ||
moduleName: Path, | ||
localName: Name, | ||
suffix: String, | ||
nodePath: NodePath | ||
): String = { | ||
val nodeIdString = s"${packageName.render}:${moduleName.render}:${localName.toCamelCase}$suffix" | ||
nodePath match { | ||
case NodePath(Vector()) => nodeIdString | ||
case _ => s"$nodeIdString$nodePath" | ||
} | ||
} | ||
|
||
self match { | ||
case ModuleID(packagePath, modulePath) => | ||
s"${packagePath.path.render}:${modulePath.path.render}" | ||
case TypeID(FQName(packageName, moduleName, localName), path) => | ||
mapToTypeOrValue(packageName.path, moduleName.path, localName, ".type", path) | ||
case ValueID(FQName(packageName, moduleName, localName), path) => | ||
mapToTypeOrValue(packageName.path, moduleName.path, localName, ".value", path) | ||
} | ||
} | ||
} | ||
type NodeIDCompanion = NodeID.type | ||
object NodeID { | ||
|
||
def fromQualifiedName(qualifiedModuleName: QualifiedModuleName): NodeID = | ||
ModuleID.fromQualifiedName(qualifiedModuleName) | ||
|
||
def fromString(input: String): Either[Error, NodeID] = { | ||
def mapToTypeOrValue(packageName: String, moduleName: String, defNameWithSuffix: String, nodePath: String) = { | ||
def defName(suffix: String) = defNameWithSuffix.dropRight(suffix.length()) | ||
if (defNameWithSuffix.endsWith(".value")) | ||
Right(ValueID(FQName.fqn(packageName, moduleName, defName(".value")), NodePath.fromString(nodePath))) | ||
else | ||
Right(TypeID(FQName.fqn(packageName, moduleName, defName(".type")), NodePath.fromString(nodePath))) | ||
} | ||
|
||
input.split(":") match { | ||
case Array(packageName, moduleName) => | ||
Right(ModuleID(Path(packageName), Path(moduleName))) | ||
case Array(packageName, moduleName, localName) => | ||
if (localName.contains("#")) | ||
localName.split("#") match { | ||
case Array(defName, path) => mapToTypeOrValue(packageName, moduleName, defName, path) | ||
case _ => Left(Error.InvalidNodeId(input)) | ||
} | ||
else | ||
mapToTypeOrValue(packageName, moduleName, localName, "") | ||
case _ => | ||
Left(Error.InvalidNodeId(input)) | ||
} | ||
} | ||
|
||
sealed case class TypeID(name: FQName, memberPath: NodePath) extends NodeID | ||
sealed case class ValueID(name: FQName, memberPath: NodePath) extends NodeID | ||
sealed case class ModuleID(packageName: PackageName, moduleName: ModuleName) extends NodeID | ||
object ModuleID { | ||
def apply(packagePath: Path, modulePath: Path): ModuleID = | ||
ModuleID(PackageName(packagePath), ModuleName(modulePath)) | ||
|
||
def fromQualifiedName(qualifiedModuleName: QualifiedModuleName): ModuleID = | ||
ModuleID(qualifiedModuleName.packageName, qualifiedModuleName.modulePath) | ||
} | ||
|
||
sealed abstract class Error(errorMessage: String) extends Exception(errorMessage) | ||
object Error { | ||
sealed case class InvalidPath(input: String, errorMessage: String) extends Error(errorMessage) | ||
sealed case class InvalidNodeId(input: String, errorMessage: String) extends Error(errorMessage) { | ||
def this(input: String) = this(input, s"Invalid NodeId: $input") | ||
} | ||
|
||
object InvalidNodeId { | ||
def apply(input: String): InvalidNodeId = new InvalidNodeId(input) | ||
} | ||
} | ||
} | ||
|
||
sealed case class NodePath(steps: Vector[NodePathStep]) { self => | ||
import NodePathStep.* | ||
|
||
def /(step: NodePathStep): NodePath = NodePath(steps :+ step) | ||
def /(name: String): NodePath = self / ChildByName(Name.fromString(name)) | ||
|
||
@inline def isEmpty: Boolean = steps.isEmpty | ||
|
||
override def toString(): String = | ||
if (self.isEmpty) "" | ||
else | ||
steps.map { | ||
case ChildByName(name) => name.toCamelCase | ||
case ChildByIndex(index) => index.toString() | ||
}.mkString("#", ":", "") | ||
} | ||
|
||
object NodePath { | ||
import NodePathStep.* | ||
val empty: NodePath = NodePath(Vector.empty) | ||
|
||
@inline def fromIterable(iterable: Iterable[NodePathStep]): NodePath = NodePath(iterable.toVector) | ||
|
||
def fromString(input: String): NodePath = | ||
if (input.isEmpty()) empty | ||
else | ||
fromIterable(input.split(":").map { stepString => | ||
stepString.toIntOption match { | ||
case Some(index) => NodePathStep.childByIndex(index) | ||
case None => NodePathStep.childByName(stepString) | ||
} | ||
}) | ||
} | ||
|
||
sealed trait NodePathStep | ||
object NodePathStep { | ||
def childByName(input: String): NodePathStep = ChildByName(Name.fromString(input)) | ||
def childByIndex(index: Int): NodePathStep = ChildByIndex(index) | ||
|
||
sealed case class ChildByName(name: Name) extends NodePathStep | ||
sealed case class ChildByIndex(index: Int) extends NodePathStep | ||
} | ||
|
||
trait HasId { | ||
def id: NodeID | ||
} | ||
} |
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,44 @@ | ||
package org.finos.morphir.ir.gen1 | ||
|
||
trait PackageNameModule { self: Names => | ||
|
||
/** A package name is a globally unique identifier for a package. It is represented by a `Path` which is a list of | ||
* names. | ||
*/ | ||
sealed case class PackageName(path: Path) { self => | ||
def ++(that: PackageName): PackageName = PackageName(path ++ that.path) | ||
def ++(that: Path): PackageName = PackageName(path ++ that) | ||
def /(pathString: String): PackageName = PackageName(path ++ Path.fromString(pathString)) | ||
|
||
@deprecated("Use `%(moduleName: ModuleName)` instead", "0.4.0-M3") | ||
def /(moduleName: ModuleName): QualifiedModuleName = QualifiedModuleName(self, moduleName) | ||
def %(modulePath: String): QualifiedModuleName = QualifiedModuleName(self, ModuleName.fromString(modulePath)) | ||
def %(moduleName: ModuleName): QualifiedModuleName = QualifiedModuleName(self, moduleName) | ||
|
||
@inline def isEmpty: Boolean = path.isEmpty | ||
@inline def toPath: Path = path | ||
|
||
def render(implicit renderer: PathRenderer): String = renderer(path) | ||
/// An alias for `render` | ||
def show(implicit renderer: PathRenderer): String = render | ||
override def toString(): String = render | ||
} | ||
|
||
val root = PackageName.root | ||
|
||
object PackageName { | ||
val empty: PackageName = PackageName(Path.empty) | ||
val root: PackageName = PackageName(Path.empty) | ||
|
||
// val morphirSdk:PackageName = PackageName.fromString("Morphir.SDK") | ||
|
||
def fromPath(path: Path): PackageName = PackageName(path) | ||
|
||
def fromString(str: String): PackageName = PackageName(Path.fromString(str)) | ||
def fromIterable(segments: Iterable[Name]): PackageName = | ||
PackageName(Path.fromIterable(segments)) | ||
def fromList(segments: List[Name]): PackageName = | ||
PackageName(Path.fromList(segments)) | ||
|
||
} | ||
} |
Oops, something went wrong.