From c1c0fce5b6b7a925fb82a502d5be7a444bade1b6 Mon Sep 17 00:00:00 2001 From: MichelleChan <6752211+michelchan@users.noreply.github.com> Date: Thu, 3 Oct 2024 18:37:23 -0400 Subject: [PATCH] Moving everything to top level --- .../src/org/finos/morphir/ir/gen1/Names.scala | 13 - .../morphir/ir/gen1/NamingOptionsModule.scala | 11 +- .../src/org/finos/morphir/ir/gen1/Type.scala | 2 +- .../ir/gen1/TypeMapReferenceName.scala | 2 +- .../org/finos/morphir/ir/gen1/fqname.scala | 163 ++++++----- .../finos/morphir/ir/gen1/moduleName.scala | 104 ++++--- .../src/org/finos/morphir/ir/gen1/name.scala | 241 ++++++++-------- .../org/finos/morphir/ir/gen1/namespace.scala | 88 +++--- .../org/finos/morphir/ir/gen1/naming.scala | 2 +- .../org/finos/morphir/ir/gen1/nodeID.scala | 257 +++++++++--------- .../finos/morphir/ir/gen1/packageName.scala | 67 +++-- .../src/org/finos/morphir/ir/gen1/path.scala | 189 +++++++------ .../src/org/finos/morphir/ir/gen1/qname.scala | 75 +++-- .../morphir/ir/gen1/qualifiedModuleName.scala | 91 +++---- 14 files changed, 627 insertions(+), 678 deletions(-) delete mode 100644 morphir/src/org/finos/morphir/ir/gen1/Names.scala diff --git a/morphir/src/org/finos/morphir/ir/gen1/Names.scala b/morphir/src/org/finos/morphir/ir/gen1/Names.scala deleted file mode 100644 index c7caf4410..000000000 --- a/morphir/src/org/finos/morphir/ir/gen1/Names.scala +++ /dev/null @@ -1,13 +0,0 @@ -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 diff --git a/morphir/src/org/finos/morphir/ir/gen1/NamingOptionsModule.scala b/morphir/src/org/finos/morphir/ir/gen1/NamingOptionsModule.scala index 6441d3aa0..e443b3665 100644 --- a/morphir/src/org/finos/morphir/ir/gen1/NamingOptionsModule.scala +++ b/morphir/src/org/finos/morphir/ir/gen1/NamingOptionsModule.scala @@ -1,11 +1,8 @@ package org.finos.morphir.ir.gen1 -trait NamingOptionsModule { self: PackageNameModule with ModuleNameModule => +sealed case class FQNamingOptions(defaultPackage: PackageName, defaultModule: ModuleName, defaultSeparator: String) - sealed case class FQNamingOptions(defaultPackage: PackageName, defaultModule: ModuleName, defaultSeparator: String) - - object FQNamingOptions { - implicit val default: FQNamingOptions = - FQNamingOptions(PackageName.empty, ModuleName.empty, ":") - } +object FQNamingOptions { + implicit val default: FQNamingOptions = + FQNamingOptions(PackageName.empty, ModuleName.empty, ":") } diff --git a/morphir/src/org/finos/morphir/ir/gen1/Type.scala b/morphir/src/org/finos/morphir/ir/gen1/Type.scala index d5473dca8..0bd451a8e 100644 --- a/morphir/src/org/finos/morphir/ir/gen1/Type.scala +++ b/morphir/src/org/finos/morphir/ir/gen1/Type.scala @@ -1,6 +1,6 @@ package org.finos.morphir.ir.gen1 -import org.finos.morphir.ir.gen1.naming.* +import org.finos.morphir.ir.gen1.* import org.finos.morphir.ir.gen1.Type.{Unit as UnitType, *} import scala.annotation.{tailrec, unused} diff --git a/morphir/src/org/finos/morphir/ir/gen1/TypeMapReferenceName.scala b/morphir/src/org/finos/morphir/ir/gen1/TypeMapReferenceName.scala index baee808cf..fc3b96eea 100644 --- a/morphir/src/org/finos/morphir/ir/gen1/TypeMapReferenceName.scala +++ b/morphir/src/org/finos/morphir/ir/gen1/TypeMapReferenceName.scala @@ -1,6 +1,6 @@ package org.finos.morphir.ir.gen1 -import org.finos.morphir.ir.gen1.naming.* +import org.finos.morphir.ir.gen1.FQName import org.finos.morphir.ir.gen1.Type.{Unit as UnitType, *} final case class TypeMapReferenceName[Attrib](f: FQName => FQName) extends TypeRewritingFolder[Any, Attrib] { diff --git a/morphir/src/org/finos/morphir/ir/gen1/fqname.scala b/morphir/src/org/finos/morphir/ir/gen1/fqname.scala index 224742c55..4f38cf07b 100644 --- a/morphir/src/org/finos/morphir/ir/gen1/fqname.scala +++ b/morphir/src/org/finos/morphir/ir/gen1/fqname.scala @@ -1,106 +1,99 @@ 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(":") - } +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) +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) + 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(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 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)) + 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 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 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 + /** 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 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 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 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)) + /// 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 fromLocalName(localName: Name)(implicit qualifiedModuleName: QualifiedModuleName): FQName = + FQName(qualifiedModuleName.packageName, qualifiedModuleName.modulePath, localName) - def toString(fqName: FQName): String = fqName.toString + 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) - } + /** 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) + def fromString(fqNameString: String)(implicit options: FQNamingOptions): FQName = + fromString(fqNameString, options.defaultSeparator) - object ReferenceName { - def unapply(fqName: FQName): Some[String] = Some(fqName.toReferenceName) - } + 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") } + +sealed case class FQNameParsingError(invalidName: String) + extends Exception(s"Unable to parse: [$invalidName] into a valid FQName") diff --git a/morphir/src/org/finos/morphir/ir/gen1/moduleName.scala b/morphir/src/org/finos/morphir/ir/gen1/moduleName.scala index 11af0ee6f..3d62858ef 100644 --- a/morphir/src/org/finos/morphir/ir/gen1/moduleName.scala +++ b/morphir/src/org/finos/morphir/ir/gen1/moduleName.scala @@ -1,61 +1,57 @@ package org.finos.morphir.ir.gen1 -trait ModuleNameModule { self: NameModule with PathModule => - - /** A module name is a unique identifier for a module within a package. It is represented by a `Path`, which is a - * "list" of names. - */ - sealed case class ModuleName(path: Path) { self => - - /// Construct a new module name by concatting the given module name to this one. - def ++(other: ModuleName): ModuleName = ModuleName(path ++ other.path) - - /// Construct a new module name by concatting the given module path to this one. - def ++(other: Path): ModuleName = ModuleName(path ++ other) - - /// Construct a new module name by concatting the given local name to this module name. - def /(name: Name): ModuleName = ModuleName(path / name) - /// Construct a new module name by concatting the given local name to this module name. - def /(name: String): ModuleName = ModuleName(path / Name(name)) - - // Need a non-symbolic version of this for QualifiedModuleName, strange compilation errors - // happen in QualifiedModuleName./(str: String) implementation when using `moduleName / str` otherwise - def addPart(name: String): ModuleName = ModuleName(path / Name(name)) - - /// Check if the module name is empty. - @inline def isEmpty: Boolean = path.isEmpty - - /// Get the name of this module. - /// For example if the module name is `Morphir.SDK.Basics` then the name is `Basics`. - def name: Name = - self match { - case ModuleName(Path(Vector())) => Name.empty - case ModuleName(Path(segments)) => segments.last - } - - // Get the name of this module if a name is present. - def nameOption: Option[Name] = - self match { - case ModuleName(Path(Vector())) => None - case ModuleName(Path(segments)) => Some(segments.last) - } - - /// Convert this module name to a `Path`. - @inline def toPath: Path = path - override def toString: String = path.toString - } - - object ModuleName { - /// Create an empty module name. - val empty: ModuleName = ModuleName(Path.empty) +/** A module name is a unique identifier for a module within a package. It is represented by a `Path`, which is a "list" + * of names. + */ +sealed case class ModuleName(path: Path) { self => + + /// Construct a new module name by concatting the given module name to this one. + def ++(other: ModuleName): ModuleName = ModuleName(path ++ other.path) + + /// Construct a new module name by concatting the given module path to this one. + def ++(other: Path): ModuleName = ModuleName(path ++ other) + + /// Construct a new module name by concatting the given local name to this module name. + def /(name: Name): ModuleName = ModuleName(path / name) + /// Construct a new module name by concatting the given local name to this module name. + def /(name: String): ModuleName = ModuleName(path / Name(name)) + + // Need a non-symbolic version of this for QualifiedModuleName, strange compilation errors + // happen in QualifiedModuleName./(str: String) implementation when using `moduleName / str` otherwise + def addPart(name: String): ModuleName = ModuleName(path / Name(name)) + + /// Check if the module name is empty. + @inline def isEmpty: Boolean = path.isEmpty + + /// Get the name of this module. + /// For example if the module name is `Morphir.SDK.Basics` then the name is `Basics`. + def name: Name = + self match { + case ModuleName(Path(Vector())) => Name.empty + case ModuleName(Path(segments)) => segments.last + } + + // Get the name of this module if a name is present. + def nameOption: Option[Name] = + self match { + case ModuleName(Path(Vector())) => None + case ModuleName(Path(segments)) => Some(segments.last) + } + + /// Convert this module name to a `Path`. + @inline def toPath: Path = path + override def toString: String = path.toString +} - def apply(input: String): ModuleName = fromString(input) +object ModuleName { + /// Create an empty module name. + val empty: ModuleName = ModuleName(Path.empty) - def fromString(input: String): ModuleName = ModuleName(Path.fromString(input)) + def apply(input: String): ModuleName = fromString(input) - def apply(parts: Name*): ModuleName = ModuleName(Path.fromIterable(parts)) - def fromIterable(segments: Iterable[Name]): ModuleName = ModuleName(Path.fromIterable(segments)) - def fromStrings(parts: String*): ModuleName = ModuleName(Path.fromStrings(parts: _*)) - } + def fromString(input: String): ModuleName = ModuleName(Path.fromString(input)) + def apply(parts: Name*): ModuleName = ModuleName(Path.fromIterable(parts)) + def fromIterable(segments: Iterable[Name]): ModuleName = ModuleName(Path.fromIterable(segments)) + def fromStrings(parts: String*): ModuleName = ModuleName(Path.fromStrings(parts: _*)) } diff --git a/morphir/src/org/finos/morphir/ir/gen1/name.scala b/morphir/src/org/finos/morphir/ir/gen1/name.scala index 9ef448a78..4e57d8343 100644 --- a/morphir/src/org/finos/morphir/ir/gen1/name.scala +++ b/morphir/src/org/finos/morphir/ir/gen1/name.scala @@ -2,165 +2,162 @@ package org.finos.morphir.ir.gen1 import scala.annotation.tailrec -trait NameModule { - - /** `Name` is an abstraction of human-readable identifiers made up of words. This abstraction allows us to use the - * same identifiers across various naming conventions used by the different frontend and backend languages Morphir - * integrates with. - */ - sealed case class Name private (toList: List[String]) { - self => - // def :+(that: String): Name = Name(self.toList :+ that) - - // def +:(that: String): Name = Name(that +: self.toList) - - def ++(that: Name): Name = Name(self.toList ++ that.toList) - - def +(that: String): Name = self ++ Name.fromString(that) - - // def /(that: Name): Path = Path(IndexedSeq(self, that)) - - def humanize: List[String] = { - val words = toList - val join: List[String] => String = abbrev => abbrev.map(_.toUpperCase()).mkString("") - - @tailrec - def loop( - prefix: List[String], - abbrev: List[String], - suffix: List[String] - ): List[String] = - suffix match { - case Nil => +/** `Name` is an abstraction of human-readable identifiers made up of words. This abstraction allows us to use the same + * identifiers across various naming conventions used by the different frontend and backend languages Morphir + * integrates with. + */ +sealed case class Name private (toList: List[String]) { + self => + // def :+(that: String): Name = Name(self.toList :+ that) + + // def +:(that: String): Name = Name(that +: self.toList) + + def ++(that: Name): Name = Name(self.toList ++ that.toList) + + def +(that: String): Name = self ++ Name.fromString(that) + + // def /(that: Name): Path = Path(IndexedSeq(self, that)) + + def humanize: List[String] = { + val words = toList + val join: List[String] => String = abbrev => abbrev.map(_.toUpperCase()).mkString("") + + @tailrec + def loop( + prefix: List[String], + abbrev: List[String], + suffix: List[String] + ): List[String] = + suffix match { + case Nil => + abbrev match { + case Nil => prefix + case _ => prefix ++ List(join(abbrev)) + } + case first :: rest => + if (first.length() == 1) + loop(prefix, abbrev ++ List(first), rest) + else abbrev match { - case Nil => prefix - case _ => prefix ++ List(join(abbrev)) + case Nil => loop(prefix ++ List(first), List.empty, rest) + case _ => + loop(prefix ++ List(join(abbrev), first), List.empty, rest) } - case first :: rest => - if (first.length() == 1) - loop(prefix, abbrev ++ List(first), rest) - else - abbrev match { - case Nil => loop(prefix ++ List(first), List.empty, rest) - case _ => - loop(prefix ++ List(join(abbrev), first), List.empty, rest) - } - } - - loop(List.empty, List.empty, words.toList) - } + } - /** Maps segments of the `Name`. - */ - def mapParts(f: String => String): Name = Name(self.toList.map(f)) + loop(List.empty, List.empty, words.toList) + } - def mkString(f: String => String)(sep: String): String = - toList.map(f).mkString(sep) + /** Maps segments of the `Name`. + */ + def mapParts(f: String => String): Name = Name(self.toList.map(f)) - def render(implicit renderer: NameRenderer): String = renderer.render(self) + def mkString(f: String => String)(sep: String): String = + toList.map(f).mkString(sep) - def toUpperCase: String = mkString(part => part.toUpperCase)("") + def render(implicit renderer: NameRenderer): String = renderer.render(self) - // def toLocalName(implicit renderer: Name.Renderer): LocalName = { - // val localNameStr = render - // LocalName(localNameStr) - // } + def toUpperCase: String = mkString(part => part.toUpperCase)("") - def toLowerCase: String = - mkString(part => part.toLowerCase)("") + // def toLocalName(implicit renderer: Name.Renderer): LocalName = { + // val localNameStr = render + // LocalName(localNameStr) + // } - def toCamelCase: String = - toList match { - case Nil => "" - case head :: tail => - (head :: tail.map(_.capitalize)).mkString("") - } + def toLowerCase: String = + mkString(part => part.toLowerCase)("") - def toKebabCase: String = - humanize.mkString("-") + def toCamelCase: String = + toList match { + case Nil => "" + case head :: tail => + (head :: tail.map(_.capitalize)).mkString("") + } - def toSnakeCase: String = - humanize.mkString("_") + def toKebabCase: String = + humanize.mkString("-") - def toTitleCase: String = - toList - .map(_.capitalize) - .mkString("") + def toSnakeCase: String = + humanize.mkString("_") - override def toString: String = toList.mkString("[", ",", "]") - } + def toTitleCase: String = + toList + .map(_.capitalize) + .mkString("") - object Name { + override def toString: String = toList.mkString("[", ",", "]") +} - val empty: Name = Name(Nil) +object Name { - private[morphir] def wrap(value: List[String]): Name = Name(value) + val empty: Name = Name(Nil) - private[morphir] def wrap(value: Array[String]): Name = Name(value.toList) + private[morphir] def wrap(value: List[String]): Name = Name(value) - def apply(first: String, rest: String*): Name = - fromIterable(first +: rest) + private[morphir] def wrap(value: Array[String]): Name = Name(value.toList) - private val pattern = """([a-zA-Z][a-z]*|[0-9]+)""".r + def apply(first: String, rest: String*): Name = + fromIterable(first +: rest) - /** Converts a list of strings into a name. NOTE: When this function is used, the strings are used as is to - * construct the name, and don't go through any processing. This behavior is desired here as it is consistent with - * Morphir's semantics for this function as defined in the `morphir-elm` project. - */ - def fromList(list: List[String]): Name = Name(list) + private val pattern = """([a-zA-Z][a-z]*|[0-9]+)""".r - /** Converts a list of strings into a name. NOTE: When this function is used, the strings are used as is to - * construct the name, and don't go through any processing. This behavior is desired here as it is consistent with - * Morphir's semantics for this function as defined in the `morphir-elm` project. - */ - def fromList(list: String*): Name = fromList(list.toList) + /** Converts a list of strings into a name. NOTE: When this function is used, the strings are used as is to construct + * the name, and don't go through any processing. This behavior is desired here as it is consistent with Morphir's + * semantics for this function as defined in the `morphir-elm` project. + */ + def fromList(list: List[String]): Name = Name(list) - def fromIterable(iterable: Iterable[String]): Name = - wrap(iterable.flatMap(str => pattern.findAllIn(str)).map(_.toLowerCase).toList) + /** Converts a list of strings into a name. NOTE: When this function is used, the strings are used as is to construct + * the name, and don't go through any processing. This behavior is desired here as it is consistent with Morphir's + * semantics for this function as defined in the `morphir-elm` project. + */ + def fromList(list: String*): Name = fromList(list.toList) - def fromString(str: String): Name = - Name(pattern.findAllIn(str).toList.map(_.toLowerCase())) + def fromIterable(iterable: Iterable[String]): Name = + wrap(iterable.flatMap(str => pattern.findAllIn(str)).map(_.toLowerCase).toList) - def toList(name: Name): List[String] = name.toList + def fromString(str: String): Name = + Name(pattern.findAllIn(str).toList.map(_.toLowerCase())) - @inline def toTitleCase(name: Name): String = name.toTitleCase + def toList(name: Name): List[String] = name.toList - @inline def toCamelCase(name: Name): String = name.toCamelCase + @inline def toTitleCase(name: Name): String = name.toTitleCase - @inline def toSnakeCase(name: Name): String = name.toSnakeCase + @inline def toCamelCase(name: Name): String = name.toCamelCase - @inline def toKebabCase(name: Name): String = name.toKebabCase + @inline def toSnakeCase(name: Name): String = name.toSnakeCase - @inline def toHumanWords(name: Name): List[String] = name.humanize + @inline def toKebabCase(name: Name): String = name.toKebabCase - object VariableName { - def unapply(name: Name): Option[String] = - Some(name.toCamelCase) - } + @inline def toHumanWords(name: Name): List[String] = name.humanize + object VariableName { + def unapply(name: Name): Option[String] = + Some(name.toCamelCase) } - trait NameRenderer extends (Name => String) { - final def render(name: Name): String = apply(name) - } +} - object NameRenderer { - object CamelCase extends NameRenderer { - def apply(name: Name): String = name.toCamelCase - } +trait NameRenderer extends (Name => String) { + final def render(name: Name): String = apply(name) +} - object KebabCase extends NameRenderer { - def apply(name: Name): String = name.toKebabCase - } +object NameRenderer { + object CamelCase extends NameRenderer { + def apply(name: Name): String = name.toCamelCase + } - object SnakeCase extends NameRenderer { - def apply(name: Name): String = name.toSnakeCase - } + object KebabCase extends NameRenderer { + def apply(name: Name): String = name.toKebabCase + } - object TitleCase extends NameRenderer { - def apply(name: Name): String = name.toTitleCase - } + object SnakeCase extends NameRenderer { + def apply(name: Name): String = name.toSnakeCase + } - implicit val default: NameRenderer = TitleCase + object TitleCase extends NameRenderer { + def apply(name: Name): String = name.toTitleCase } + + implicit val default: NameRenderer = TitleCase } diff --git a/morphir/src/org/finos/morphir/ir/gen1/namespace.scala b/morphir/src/org/finos/morphir/ir/gen1/namespace.scala index b23678e76..06a5d6385 100644 --- a/morphir/src/org/finos/morphir/ir/gen1/namespace.scala +++ b/morphir/src/org/finos/morphir/ir/gen1/namespace.scala @@ -1,48 +1,46 @@ package org.finos.morphir.ir.gen1 -trait NamespaceModule { self: NameModule with PathModule with ModuleNameModule => - sealed case class Namespace(path: Path) { self => - def ++(name: Namespace): Namespace = Namespace(path ++ path) - def /(segment: String): Namespace = Namespace(path ++ Path.fromString(segment)) - def /(names: String*): Namespace = Namespace(path ++ Path.fromIterable(names.map(Name.fromString(_)))) - def /(names: Iterable[String]): Namespace = Namespace(path ++ Path.fromIterable(names.map(Name.fromString(_)))) - - @inline def toPath: Path = path - def parts(implicit renderer: NamespaceRenderer): IndexedSeq[String] = path.parts(renderer) - - def render(implicit renderer: NamespaceRenderer): String = renderer(path) - /// An alias for `render` - def show(implicit renderer: NamespaceRenderer): String = render - def toModuleName: ModuleName = ModuleName(path) - - override def toString(): String = render - } - - object Namespace { - val ns: Namespace = Namespace(Path.empty) - - def apply(parts: Name*): Namespace = Namespace(Path.fromIterable(parts)) - def fromIterable(segments: Iterable[Name]): Namespace = Namespace(Path.fromIterable(segments)) - def fromModuleName(moduleName: ModuleName): Namespace = Namespace(moduleName.path) - def fromStrings(parts: String*): Namespace = Namespace(Path.fromStrings(parts: _*)) - - def fromPath(path: Path): Namespace = Namespace(path) - - } - - sealed case class NamespaceRenderer(separator: String, nameRenderer: NameRenderer) extends (Path => String) { - def apply(path: Path): String = path.toString(nameRenderer, separator) - final def render(path: Path): String = apply(path) - } - - object NamespaceRenderer { - val CamelCase: NamespaceRenderer = NamespaceRenderer(".", NameRenderer.CamelCase) - val KebabCase: NamespaceRenderer = NamespaceRenderer(".", NameRenderer.KebabCase) - val SnakeCase: NamespaceRenderer = NamespaceRenderer(".", NameRenderer.SnakeCase) - val TitleCase: NamespaceRenderer = NamespaceRenderer(".", NameRenderer.TitleCase) - - implicit val default: NamespaceRenderer = TitleCase - implicit def toPathRenderer(renderer: NamespaceRenderer): PathRenderer = - PathRenderer(renderer.separator, renderer.nameRenderer) - } +sealed case class Namespace(path: Path) { self => + def ++(name: Namespace): Namespace = Namespace(path ++ path) + def /(segment: String): Namespace = Namespace(path ++ Path.fromString(segment)) + def /(names: String*): Namespace = Namespace(path ++ Path.fromIterable(names.map(Name.fromString(_)))) + def /(names: Iterable[String]): Namespace = Namespace(path ++ Path.fromIterable(names.map(Name.fromString(_)))) + + @inline def toPath: Path = path + def parts(implicit renderer: NamespaceRenderer): IndexedSeq[String] = path.parts(renderer) + + def render(implicit renderer: NamespaceRenderer): String = renderer(path) + /// An alias for `render` + def show(implicit renderer: NamespaceRenderer): String = render + def toModuleName: ModuleName = ModuleName(path) + + override def toString(): String = render +} + +object Namespace { + val ns: Namespace = Namespace(Path.empty) + + def apply(parts: Name*): Namespace = Namespace(Path.fromIterable(parts)) + def fromIterable(segments: Iterable[Name]): Namespace = Namespace(Path.fromIterable(segments)) + def fromModuleName(moduleName: ModuleName): Namespace = Namespace(moduleName.path) + def fromStrings(parts: String*): Namespace = Namespace(Path.fromStrings(parts: _*)) + + def fromPath(path: Path): Namespace = Namespace(path) + +} + +sealed case class NamespaceRenderer(separator: String, nameRenderer: NameRenderer) extends (Path => String) { + def apply(path: Path): String = path.toString(nameRenderer, separator) + final def render(path: Path): String = apply(path) +} + +object NamespaceRenderer { + val CamelCase: NamespaceRenderer = NamespaceRenderer(".", NameRenderer.CamelCase) + val KebabCase: NamespaceRenderer = NamespaceRenderer(".", NameRenderer.KebabCase) + val SnakeCase: NamespaceRenderer = NamespaceRenderer(".", NameRenderer.SnakeCase) + val TitleCase: NamespaceRenderer = NamespaceRenderer(".", NameRenderer.TitleCase) + + implicit val default: NamespaceRenderer = TitleCase + implicit def toPathRenderer(renderer: NamespaceRenderer): PathRenderer = + PathRenderer(renderer.separator, renderer.nameRenderer) } diff --git a/morphir/src/org/finos/morphir/ir/gen1/naming.scala b/morphir/src/org/finos/morphir/ir/gen1/naming.scala index aa3e62aa0..603bf62db 100644 --- a/morphir/src/org/finos/morphir/ir/gen1/naming.scala +++ b/morphir/src/org/finos/morphir/ir/gen1/naming.scala @@ -1,6 +1,6 @@ package org.finos.morphir.ir.gen1 -object naming extends Names { +object naming { final implicit class PackageNameSyntax(val self: PackageName) extends AnyVal { def /(moduleName: ModuleName): QualifiedModuleName = QualifiedModuleName(self, moduleName) diff --git a/morphir/src/org/finos/morphir/ir/gen1/nodeID.scala b/morphir/src/org/finos/morphir/ir/gen1/nodeID.scala index 355ac93fe..a03860c47 100644 --- a/morphir/src/org/finos/morphir/ir/gen1/nodeID.scala +++ b/morphir/src/org/finos/morphir/ir/gen1/nodeID.scala @@ -1,158 +1,153 @@ 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::"` - * - When refering to definitions: `"type\value::#"`, 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" - } +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::"` + * - When refering to definitions: `"type\value::#"`, 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) - } + 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 { +} +type NodeIDCompanion = NodeID.type +object NodeID { - def fromQualifiedName(qualifiedModuleName: QualifiedModuleName): NodeID = - ModuleID.fromQualifiedName(qualifiedModuleName) + 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))) - } + 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)) - } + 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)) + 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) - } + 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") - } + 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) - } + object InvalidNodeId { + def apply(input: String): InvalidNodeId = new InvalidNodeId(input) } } +} - sealed case class NodePath(steps: Vector[NodePathStep]) { self => - import NodePathStep.* +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)) + def /(step: NodePathStep): NodePath = NodePath(steps :+ step) + def /(name: String): NodePath = self / ChildByName(Name.fromString(name)) - @inline def isEmpty: Boolean = steps.isEmpty + @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("#", ":", "") - } + 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) +object NodePath { + import NodePathStep.* + val empty: NodePath = NodePath(Vector.empty) - @inline def fromIterable(iterable: Iterable[NodePathStep]): NodePath = NodePath(iterable.toVector) + @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) - } - }) - } + 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 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 - } + sealed case class ChildByName(name: Name) extends NodePathStep + sealed case class ChildByIndex(index: Int) extends NodePathStep +} - trait HasId { - def id: NodeID - } +trait HasId { + def id: NodeID } diff --git a/morphir/src/org/finos/morphir/ir/gen1/packageName.scala b/morphir/src/org/finos/morphir/ir/gen1/packageName.scala index af76b271c..85b254d14 100644 --- a/morphir/src/org/finos/morphir/ir/gen1/packageName.scala +++ b/morphir/src/org/finos/morphir/ir/gen1/packageName.scala @@ -1,44 +1,41 @@ 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 - } +/** 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 +val root = PackageName.root - object PackageName { - val empty: PackageName = PackageName(Path.empty) - val root: PackageName = PackageName(Path.empty) +object PackageName { + val empty: PackageName = PackageName(Path.empty) + val root: PackageName = PackageName(Path.empty) - // val morphirSdk:PackageName = PackageName.fromString("Morphir.SDK") + // val morphirSdk:PackageName = PackageName.fromString("Morphir.SDK") - def fromPath(path: Path): PackageName = PackageName(path) + 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)) + 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)) - } } diff --git a/morphir/src/org/finos/morphir/ir/gen1/path.scala b/morphir/src/org/finos/morphir/ir/gen1/path.scala index c439f89ec..72fabdb8b 100644 --- a/morphir/src/org/finos/morphir/ir/gen1/path.scala +++ b/morphir/src/org/finos/morphir/ir/gen1/path.scala @@ -2,119 +2,116 @@ package org.finos.morphir.ir.gen1 import scala.annotation.tailrec -trait PathModule { self: NameModule => +sealed case class Path(segments: Vector[Name]) { + self => - sealed case class Path(segments: Vector[Name]) { - self => - - def ++(that: Path): Path = Path(segments ++ that.segments) + def ++(that: Path): Path = Path(segments ++ that.segments) // def ::(name: Name): QName = QName(self.toPath, name) - /** Indicates whether this path is empty. */ - def isEmpty: Boolean = toList.isEmpty + /** Indicates whether this path is empty. */ + def isEmpty: Boolean = toList.isEmpty - def toList: List[Name] = segments.toList + def toList: List[Name] = segments.toList - /** Constructs a new path by combining this path with the given name. */ - def /(name: Name): Path = Path(segments ++ List(name)) + /** Constructs a new path by combining this path with the given name. */ + def /(name: Name): Path = Path(segments ++ List(name)) - /** Constructs a new path by combining this path with the given path. */ - def /(that: Path): Path = Path(segments ++ that.toList) - // def %(other: Path): PackageAndModulePath = - // PackageAndModulePath(PackageName(self), ModulePath(other)) + /** Constructs a new path by combining this path with the given path. */ + def /(that: Path): Path = Path(segments ++ that.toList) + // def %(other: Path): PackageAndModulePath = + // PackageAndModulePath(PackageName(self), ModulePath(other)) - def zip(other: Path): (Path, Path) = (self, other) + def zip(other: Path): (Path, Path) = (self, other) - def toString(f: Name => String, separator: String): String = - toList.map(f).mkString(separator) + def toString(f: Name => String, separator: String): String = + toList.map(f).mkString(separator) - /** Checks if this path is a prefix of provided path */ - def isPrefixOf(path: Path): Boolean = Path.isPrefixOf(self, path) + /** Checks if this path is a prefix of provided path */ + def isPrefixOf(path: Path): Boolean = Path.isPrefixOf(self, path) - def parts(implicit renderer: PathRenderer): IndexedSeq[String] = segments.map(_.render(renderer.nameRenderer)) + def parts(implicit renderer: PathRenderer): IndexedSeq[String] = segments.map(_.render(renderer.nameRenderer)) - def render(implicit renderer: PathRenderer): String = renderer(self) - def render(separator: String)(implicit nameRenderer: NameRenderer): String = - render(PathRenderer(separator, nameRenderer)) + def render(implicit renderer: PathRenderer): String = renderer(self) + def render(separator: String)(implicit nameRenderer: NameRenderer): String = + render(PathRenderer(separator, nameRenderer)) - // def toPackageName(implicit renderer: Name.Renderer = Name.Renderer.TitleCase): PackageName = { - // val nsSegments = PackageName.segments(segments.map(_.render)) - // PackageName.fromIterable(nsSegments) - // } + // def toPackageName(implicit renderer: Name.Renderer = Name.Renderer.TitleCase): PackageName = { + // val nsSegments = PackageName.segments(segments.map(_.render)) + // PackageName.fromIterable(nsSegments) + // } - // def toNamespace(implicit renderer: Name.Renderer = Name.Renderer.TitleCase): Namespace = { - // val nsSegments = Namespace.segments(segments.map(_.render)) - // Namespace.fromIterable(nsSegments) - // } + // def toNamespace(implicit renderer: Name.Renderer = Name.Renderer.TitleCase): Namespace = { + // val nsSegments = Namespace.segments(segments.map(_.render)) + // Namespace.fromIterable(nsSegments) + // } - override def toString(): String = render - } + override def toString(): String = render +} - object Path { - val separatorRegex = """[^\w\s]+""".r - val empty: Path = Path(Vector.empty) - val root: Path = Path(Vector.empty) - - def apply(first: String, rest: String*): Path = - if (rest.isEmpty) Path(Vector(Name.fromString(first))) - else Path.fromIterable(Name.fromString(first) +: rest.map(Name.fromString(_))) - - def apply(first: Name, rest: Name*): Path = - if (rest.isEmpty) Path(Vector(first)) - else Path(first +: rest.toVector) - - /** Translates a string into a path by splitting it into names along special characters. The algorithm will treat - * any non-word characters that are not spaces as a path separator. - */ - def fromString(str: String): Path = - fromArray(separatorRegex.split(str).map(Name.fromString)) - - def toString(f: Name => String, separator: String, path: Path): String = - path.toString(f, separator) - - /// Converts an array of names into a path. - @inline def fromArray(names: Array[Name]): Path = Path(names.toVector) - /// Converts names into a path. - @inline def fromIterable(names: Iterable[Name]): Path = Path(names.toVector) - /// Converts a list of names into a path. - @inline def fromList(names: List[Name]): Path = Path(names.toVector) - /// Converts a list of names into a path. - @inline def fromList(names: Name*): Path = Path(names.toVector) - - def fromStrings(names: String*): Path = Path(names.map(Name.fromString).toVector) - /// Converts names into a path. - @inline def fromVector(names: Vector[Name]): Path = Path(names) - - @inline def toList(path: Path): List[Name] = path.toList.toList - - /** Checks if the first provided path is a prefix of the second path */ - @tailrec - def isPrefixOf(prefix: Path, path: Path): Boolean = (prefix.toList, path.toList) match { - case (Nil, _) => true - case (_, Nil) => false - case (prefixHead :: prefixTail, pathHead :: pathTail) => - if (prefixHead == pathHead) - isPrefixOf( - Path.fromList(prefixTail), - Path.fromList(pathTail) - ) - else false - } - - private[morphir] def unsafeMake(parts: Name*): Path = Path(parts.toVector) +object Path { + val separatorRegex = """[^\w\s]+""".r + val empty: Path = Path(Vector.empty) + val root: Path = Path(Vector.empty) + + def apply(first: String, rest: String*): Path = + if (rest.isEmpty) Path(Vector(Name.fromString(first))) + else Path.fromIterable(Name.fromString(first) +: rest.map(Name.fromString(_))) + + def apply(first: Name, rest: Name*): Path = + if (rest.isEmpty) Path(Vector(first)) + else Path(first +: rest.toVector) + + /** Translates a string into a path by splitting it into names along special characters. The algorithm will treat any + * non-word characters that are not spaces as a path separator. + */ + def fromString(str: String): Path = + fromArray(separatorRegex.split(str).map(Name.fromString)) + + def toString(f: Name => String, separator: String, path: Path): String = + path.toString(f, separator) + + /// Converts an array of names into a path. + @inline def fromArray(names: Array[Name]): Path = Path(names.toVector) + /// Converts names into a path. + @inline def fromIterable(names: Iterable[Name]): Path = Path(names.toVector) + /// Converts a list of names into a path. + @inline def fromList(names: List[Name]): Path = Path(names.toVector) + /// Converts a list of names into a path. + @inline def fromList(names: Name*): Path = Path(names.toVector) + + def fromStrings(names: String*): Path = Path(names.map(Name.fromString).toVector) + /// Converts names into a path. + @inline def fromVector(names: Vector[Name]): Path = Path(names) + + @inline def toList(path: Path): List[Name] = path.toList.toList + + /** Checks if the first provided path is a prefix of the second path */ + @tailrec + def isPrefixOf(prefix: Path, path: Path): Boolean = (prefix.toList, path.toList) match { + case (Nil, _) => true + case (_, Nil) => false + case (prefixHead :: prefixTail, pathHead :: pathTail) => + if (prefixHead == pathHead) + isPrefixOf( + Path.fromList(prefixTail), + Path.fromList(pathTail) + ) + else false } - sealed case class PathRenderer(separator: String, nameRenderer: NameRenderer) extends (Path => String) { - def apply(path: Path): String = path.toString(nameRenderer, separator) - final def render(path: Path): String = apply(path) - } + private[morphir] def unsafeMake(parts: Name*): Path = Path(parts.toVector) +} + +sealed case class PathRenderer(separator: String, nameRenderer: NameRenderer) extends (Path => String) { + def apply(path: Path): String = path.toString(nameRenderer, separator) + final def render(path: Path): String = apply(path) +} - object PathRenderer { - val CamelCase: PathRenderer = PathRenderer(".", NameRenderer.CamelCase) - val KebabCase: PathRenderer = PathRenderer(".", NameRenderer.KebabCase) - val SnakeCase: PathRenderer = PathRenderer(".", NameRenderer.SnakeCase) - val TitleCase: PathRenderer = PathRenderer(".", NameRenderer.TitleCase) +object PathRenderer { + val CamelCase: PathRenderer = PathRenderer(".", NameRenderer.CamelCase) + val KebabCase: PathRenderer = PathRenderer(".", NameRenderer.KebabCase) + val SnakeCase: PathRenderer = PathRenderer(".", NameRenderer.SnakeCase) + val TitleCase: PathRenderer = PathRenderer(".", NameRenderer.TitleCase) - implicit val default: PathRenderer = TitleCase - } + implicit val default: PathRenderer = TitleCase } diff --git a/morphir/src/org/finos/morphir/ir/gen1/qname.scala b/morphir/src/org/finos/morphir/ir/gen1/qname.scala index d3b21bb31..6a3e86691 100644 --- a/morphir/src/org/finos/morphir/ir/gen1/qname.scala +++ b/morphir/src/org/finos/morphir/ir/gen1/qname.scala @@ -1,55 +1,52 @@ package org.finos.morphir.ir.gen1 -trait QNameModule { self: ModuleNameModule with NameModule with PathModule => +/// A qualified name (`QName`) is a combination of a module path and a local name. +sealed case class QName(moduleName: ModuleName, localName: Name) { + def modulePath: Path = moduleName.path - /// A qualified name (`QName`) is a combination of a module path and a local name. - sealed case class QName(moduleName: ModuleName, localName: Name) { - def modulePath: Path = moduleName.path + /// Turn a qualified name into a tuple of a module path and a local name. + @inline def toTuple: (Path, Name) = (modulePath, localName) - /// Turn a qualified name into a tuple of a module path and a local name. - @inline def toTuple: (Path, Name) = (modulePath, localName) + override def toString: String = + modulePath.toString(Name.toTitleCase, ".") + ":" + localName.toCamelCase - override def toString: String = - modulePath.toString(Name.toTitleCase, ".") + ":" + localName.toCamelCase - - } +} - object QName { - val empty: QName = QName(ModuleName.empty, Name.empty) - def apply(modulePath: Path, localName: Name): QName = - QName(ModuleName(modulePath), localName) +object QName { + val empty: QName = QName(ModuleName.empty, Name.empty) + def apply(modulePath: Path, localName: Name): QName = + QName(ModuleName(modulePath), localName) - def apply(moduleName: String, localName: String): QName = - QName(ModuleName.fromString(moduleName), Name.fromString(localName)) + def apply(moduleName: String, localName: String): QName = + QName(ModuleName.fromString(moduleName), Name.fromString(localName)) - /// Turn a qualified name into a tuple of a module path and a local name. - def toTuple(qName: QName): (Path, Name) = qName.toTuple + /// Turn a qualified name into a tuple of a module path and a local name. + def toTuple(qName: QName): (Path, Name) = qName.toTuple - /// Turn a tuple of a module path and a local name into a qualified name (`QName`). - def fromTuple(tuple: (Path, Name)): QName = QName(tuple._1, tuple._2) + /// Turn a tuple of a module path and a local name into a qualified name (`QName`). + def fromTuple(tuple: (Path, Name)): QName = QName(tuple._1, tuple._2) - /// Creates a qualified name from a module path and a local name. - def fromName(modulePath: Path, localName: Name): QName = QName(modulePath, localName) + /// Creates a qualified name from a module path and a local name. + def fromName(modulePath: Path, localName: Name): QName = QName(modulePath, localName) - /// Creates a qualified name from strings representing a module path and a local name. - def fromName(modulePath: String, localName: String): QName = - QName(Path.fromString(modulePath), Name.fromString(localName)) + /// Creates a qualified name from strings representing a module path and a local name. + def fromName(modulePath: String, localName: String): QName = + QName(Path.fromString(modulePath), Name.fromString(localName)) - /// Get the local name part of a qualified name. - def getLocalName(qname: QName): Name = qname.localName + /// Get the local name part of a qualified name. + def getLocalName(qname: QName): Name = qname.localName - /// Get the module path part of a qualified name. - def getModulePath(qname: QName): Path = qname.modulePath + /// Get the module path part of a qualified name. + def getModulePath(qname: QName): Path = qname.modulePath - /// Turn a `QName` into a string using `:` as the separator between the module path and the local name. - def toString(qName: QName): String = qName.toString + /// Turn a `QName` into a string using `:` as the separator between the module path and the local name. + def toString(qName: QName): String = qName.toString - /// Parse a string into a qualified name using `:` as the separator between the module path and the local name. - def fromString(str: String): Option[QName] = - str.split(":") match { - case Array(packageNameString, localNameString) => - Some(QName(Path.fromString(packageNameString), Name.fromString(localNameString))) - case _ => None - } - } + /// Parse a string into a qualified name using `:` as the separator between the module path and the local name. + def fromString(str: String): Option[QName] = + str.split(":") match { + case Array(packageNameString, localNameString) => + Some(QName(Path.fromString(packageNameString), Name.fromString(localNameString))) + case _ => None + } } diff --git a/morphir/src/org/finos/morphir/ir/gen1/qualifiedModuleName.scala b/morphir/src/org/finos/morphir/ir/gen1/qualifiedModuleName.scala index 1357c06aa..438ba3105 100644 --- a/morphir/src/org/finos/morphir/ir/gen1/qualifiedModuleName.scala +++ b/morphir/src/org/finos/morphir/ir/gen1/qualifiedModuleName.scala @@ -1,60 +1,55 @@ package org.finos.morphir.ir.gen1 -trait QualifiedModuleNameModule { - self: NameModule with PackageNameModule with PathModule with ModuleNameModule with NamespaceModule - with NamingOptionsModule => - - /// A qualified module name is a globally unique identifier for a module. It is represented by the combination of a package name and the module name. - sealed case class QualifiedModuleName(packageName: PackageName, modulePath: ModuleName) { self => - def /(moduleName: ModuleName): QualifiedModuleName = - QualifiedModuleName(self.packageName, self.modulePath ++ moduleName) - def /(namespaceAddition: String): QualifiedModuleName = - QualifiedModuleName(self.packageName, modulePath.addPart(namespaceAddition)) - - def toTuple: (Path, Path) = (packageName.toPath, modulePath.toPath) - - override def toString: String = Array( - Path.toString(Name.toTitleCase, ".", packageName.toPath), - Path.toString(Name.toTitleCase, ".", modulePath.toPath) - ).mkString(":") - } +/// A qualified module name is a globally unique identifier for a module. It is represented by the combination of a package name and the module name. +sealed case class QualifiedModuleName(packageName: PackageName, modulePath: ModuleName) { self => + def /(moduleName: ModuleName): QualifiedModuleName = + QualifiedModuleName(self.packageName, self.modulePath ++ moduleName) + def /(namespaceAddition: String): QualifiedModuleName = + QualifiedModuleName(self.packageName, modulePath.addPart(namespaceAddition)) + + def toTuple: (Path, Path) = (packageName.toPath, modulePath.toPath) + + override def toString: String = Array( + Path.toString(Name.toTitleCase, ".", packageName.toPath), + Path.toString(Name.toTitleCase, ".", modulePath.toPath) + ).mkString(":") +} - object QualifiedModuleName { - val empty: QualifiedModuleName = QualifiedModuleName(PackageName.empty, ModuleName.empty) +object QualifiedModuleName { + val empty: QualifiedModuleName = QualifiedModuleName(PackageName.empty, ModuleName.empty) - def apply(packageName: Path, modulePath: Path): QualifiedModuleName = - QualifiedModuleName(PackageName.fromPath(packageName), ModuleName(modulePath)) + def apply(packageName: Path, modulePath: Path): QualifiedModuleName = + QualifiedModuleName(PackageName.fromPath(packageName), ModuleName(modulePath)) - def apply(modulePath: String)(implicit packageName: PackageName): QualifiedModuleName = - QualifiedModuleName(packageName, ModuleName.fromString(modulePath)) + def apply(modulePath: String)(implicit packageName: PackageName): QualifiedModuleName = + QualifiedModuleName(packageName, ModuleName.fromString(modulePath)) - /** Parse a string into a QualifedModuleName using splitter as the separator between package, and module */ - def fromString(nameString: String, splitter: String)(implicit options: FQNamingOptions): QualifiedModuleName = - nameString.split(splitter) match { - case Array(moduleNameString, localNameString) => - qmn(moduleNameString, localNameString) - case Array(localNameString) => - qmn(localNameString) - case _ => throw QualifiedModuleNameParsingError(nameString) - } + /** Parse a string into a QualifedModuleName using splitter as the separator between package, and module */ + def fromString(nameString: String, splitter: String)(implicit options: FQNamingOptions): QualifiedModuleName = + nameString.split(splitter) match { + case Array(moduleNameString, localNameString) => + qmn(moduleNameString, localNameString) + case Array(localNameString) => + qmn(localNameString) + case _ => throw QualifiedModuleNameParsingError(nameString) + } - def fromString(fqNameString: String)(implicit options: FQNamingOptions): QualifiedModuleName = - fromString(fqNameString, options.defaultSeparator) + def fromString(fqNameString: String)(implicit options: FQNamingOptions): QualifiedModuleName = + fromString(fqNameString, options.defaultSeparator) - /** Convenience function to create a fully-qualified name from 2 strings with default package name */ - def qmn(packageName: String, moduleName: String): QualifiedModuleName = - QualifiedModuleName(PackageName.fromString(packageName), ModuleName(Path.fromString(moduleName))) + /** Convenience function to create a fully-qualified name from 2 strings with default package name */ + def qmn(packageName: String, moduleName: String): QualifiedModuleName = + QualifiedModuleName(PackageName.fromString(packageName), ModuleName(Path.fromString(moduleName))) - /** Convenience function to create a fully-qualified name from 1 string with defaults for package and module */ - def qmn(moduleName: String)(implicit options: FQNamingOptions): QualifiedModuleName = - QualifiedModuleName(options.defaultPackage, ModuleName(Path.fromString(moduleName))) + /** Convenience function to create a fully-qualified name from 1 string with defaults for package and module */ + def qmn(moduleName: String)(implicit options: FQNamingOptions): QualifiedModuleName = + QualifiedModuleName(options.defaultPackage, ModuleName(Path.fromString(moduleName))) - object AsTuple { - def unapply(name: QualifiedModuleName): Option[(Path, Path)] = - Some(name.toTuple) - } + object AsTuple { + def unapply(name: QualifiedModuleName): Option[(Path, Path)] = + Some(name.toTuple) } - - sealed case class QualifiedModuleNameParsingError(invalidName: String) - extends Exception(s"Unable to parse: [$invalidName] into a valid QualifiedModuleName") } + +sealed case class QualifiedModuleNameParsingError(invalidName: String) + extends Exception(s"Unable to parse: [$invalidName] into a valid QualifiedModuleName")