From eb992cee8042a3afd2b7a43b370e99b000582bd7 Mon Sep 17 00:00:00 2001 From: Hosam Aly Date: Fri, 23 Mar 2018 16:32:56 +0000 Subject: [PATCH 1/9] Remove the reference to an unused library (scalactic) It wasn't included in the actual `libraryDependencies` so this change has no effect on the dependency structure. --- project/Dependencies.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index a4ed278..60206db 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -14,7 +14,6 @@ object Dependencies { val scalaTestVersion = "3.0.5" val scalaTest = "org.scalatest" %% "scalatest" % scalaTestVersion - val scalactic = "org.scalactic" %% "scalactic" % scalaTestVersion val xmlDiff = "com.github.andyglow" %% "scala-xml-diff" % "2.0.3" From aac39ebeed59a3277365eebc875ed6b7fd8f40e8 Mon Sep 17 00:00:00 2001 From: Hosam Aly Date: Sun, 25 Mar 2018 19:40:05 +0100 Subject: [PATCH 2/9] Implement an `AbstractBuilder` --- src/main/scala/Builders.scala | 19 +++++++++++++------ src/test/scala/BuildersSpec.scala | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/main/scala/Builders.scala b/src/main/scala/Builders.scala index 7ebfc6c..6797c1d 100644 --- a/src/main/scala/Builders.scala +++ b/src/main/scala/Builders.scala @@ -120,12 +120,6 @@ class BodyBuilder(var build: Body = Body()) extends Builder[Body] { class BodyHeadBuilder(var build: BodyHead = BodyHead()) extends Builder[BodyHead] { def withByline(x: String): this.type = withByline(Byline(Seq(dataRecord(x)))) def withHeadline(x: String): this.type = withHeadline(new HeadlineBuilder().withPrimaryHeadline(x)) - def withAbstract(x: NodeSeq): this.type = withAbstract(Abstract(Seq(dataRecord(x)))) - def withAbstract(x: String, markAsSummary: Boolean = true): this.type = { - var paragraphBuilder = new ParagraphBuilder().withText(x) - if (markAsSummary) paragraphBuilder = paragraphBuilder.asSummary - withAbstract(Abstract(Seq(dataRecord(paragraphBuilder.build)))) - } def withRights(x: Rights): this.type = { build = build.copy(rights = Option(x)); this } def withSeries(x: Series): this.type = { build = build.copy(series = Option(x)); this } @@ -141,6 +135,13 @@ class BodyHeadBuilder(var build: BodyHead = BodyHead()) extends Builder[BodyHead } } +class AbstractBuilder(var build: Abstract = Abstract()) extends Builder[Abstract] with BlockContentBuilder { + protected def withContent(x: DataRecord[_]): this.type = { + build = build.copy(abstractoption = build.abstractoption :+ x) + this + } +} + class HeadlineBuilder(var build: Hedline = Hedline(Hl1())) extends Builder[Hedline] { def withPrimaryHeadline(x: Hl1): this.type = { build = build.copy(hl1 = x); this } def withPrimaryHeadline(x: String): this.type = { withPrimaryHeadline(new PrimaryHeadlineBuilder(build.hl1).withText(x)) } @@ -166,7 +167,13 @@ trait BlockContentBuilder { def withHorizontalRule(x: Hr): this.type = withBlockContent(x) def withNitfTable(x: NitfTable): this.type = withBlockContent(x) def withSubordinateHeadline(x: Hl2): this.type = withBlockContent(x) + def withXml(x: NodeSeq): this.type = withContent(dataRecord(x)) + def withTextParagraph(x: String, markAsSummary: Boolean = true): this.type = { + var paragraphBuilder = new ParagraphBuilder().withText(x) + if (markAsSummary) paragraphBuilder = paragraphBuilder.asSummary + withParagraph(paragraphBuilder.build) + } protected def withBlockContent[T <: BlockContentOption : CanWriteXML](x: T): this.type = withContent(dataRecord(x)) protected def withContent(x: DataRecord[_]): this.type diff --git a/src/test/scala/BuildersSpec.scala b/src/test/scala/BuildersSpec.scala index 2e0045a..fbd7e7a 100644 --- a/src/test/scala/BuildersSpec.scala +++ b/src/test/scala/BuildersSpec.scala @@ -32,7 +32,7 @@ class BuildersSpec extends FunSpec { .withHead(new BodyHeadBuilder() .withHeadline("News Article") .withByline("It took a lot of work to get there") - .withAbstract(

It wasn't easy, but they never gave up!

) + .withAbstract(new AbstractBuilder().withXml(

It wasn't easy, but they never gave up!

)) ) .withContent(new BodyContentBuilder() .withParagraph(new ParagraphBuilder().withText("It was done, really!")) From a7ab1e4ee0610a2debbd052c88a0b47e04c1fe73 Mon Sep 17 00:00:00 2001 From: Hosam Aly Date: Sun, 25 Mar 2018 19:45:20 +0100 Subject: [PATCH 3/9] Move `BlockContentBuilder` to the top of the Builders file Grouping all builder traits together --- src/main/scala/Builders.scala | 54 +++++++++++++++++------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/main/scala/Builders.scala b/src/main/scala/Builders.scala index 6797c1d..2ca4f16 100644 --- a/src/main/scala/Builders.scala +++ b/src/main/scala/Builders.scala @@ -42,6 +42,32 @@ trait Builder[T] { override def toString: String = build.toString } +trait BlockContentBuilder { + def withNote(x: Note): this.type = withBlockContent(x) + def withFootnote(x: Fn): this.type = withBlockContent(x) + def withMedia(x: Media): this.type = withBlockContent(x) + def withParagraph(x: P): this.type = withBlockContent(x) + def withTable(x: Table): this.type = withBlockContent(x) + def withBlockQuote(x: Bq): this.type = withBlockContent(x) + def withOrderedList(x: Ol): this.type = withBlockContent(x) + def withPreformatted(x: Pre): this.type = withBlockContent(x) + def withUnorderedList(x: Ul): this.type = withBlockContent(x) + def withDefinitionList(x: Dl): this.type = withBlockContent(x) + def withHorizontalRule(x: Hr): this.type = withBlockContent(x) + def withNitfTable(x: NitfTable): this.type = withBlockContent(x) + def withSubordinateHeadline(x: Hl2): this.type = withBlockContent(x) + + def withXml(x: NodeSeq): this.type = withContent(dataRecord(x)) + def withTextParagraph(x: String, markAsSummary: Boolean = true): this.type = { + var paragraphBuilder = new ParagraphBuilder().withText(x) + if (markAsSummary) paragraphBuilder = paragraphBuilder.asSummary + withParagraph(paragraphBuilder.build) + } + + protected def withBlockContent[T <: BlockContentOption : CanWriteXML](x: T): this.type = withContent(dataRecord(x)) + protected def withContent(x: DataRecord[_]): this.type +} + trait EnrichedTextBuilder { def withAnchor(x: A): this.type = withEnrichedText(x) def withChron(x: Chron): this.type = withEnrichedText(x) @@ -70,8 +96,8 @@ trait EnrichedTextBuilder { } class NitfBuilder(var build: Nitf = Nitf(body = Body())) extends Builder[Nitf] { - def withHead(x: Head): this.type = { build = build.copy(head = Option(x)); this } def withBody(x: Body): this.type = { build = build.copy(body = x); this } + def withHead(x: Head): this.type = { build = build.copy(head = Option(x)); this } def withUno(x: String): this.type = { build = build.copy(uno = Option(x)); this } } @@ -153,32 +179,6 @@ class PrimaryHeadlineBuilder(var build: Hl1 = Hl1()) extends Builder[Hl1] with E protected def withContent(x: DataRecord[_]): this.type = { build = build.copy(mixed = build.mixed :+ x); this } } -trait BlockContentBuilder { - def withNote(x: Note): this.type = withBlockContent(x) - def withFootnote(x: Fn): this.type = withBlockContent(x) - def withMedia(x: Media): this.type = withBlockContent(x) - def withParagraph(x: P): this.type = withBlockContent(x) - def withTable(x: Table): this.type = withBlockContent(x) - def withBlockQuote(x: Bq): this.type = withBlockContent(x) - def withOrderedList(x: Ol): this.type = withBlockContent(x) - def withPreformatted(x: Pre): this.type = withBlockContent(x) - def withUnorderedList(x: Ul): this.type = withBlockContent(x) - def withDefinitionList(x: Dl): this.type = withBlockContent(x) - def withHorizontalRule(x: Hr): this.type = withBlockContent(x) - def withNitfTable(x: NitfTable): this.type = withBlockContent(x) - def withSubordinateHeadline(x: Hl2): this.type = withBlockContent(x) - - def withXml(x: NodeSeq): this.type = withContent(dataRecord(x)) - def withTextParagraph(x: String, markAsSummary: Boolean = true): this.type = { - var paragraphBuilder = new ParagraphBuilder().withText(x) - if (markAsSummary) paragraphBuilder = paragraphBuilder.asSummary - withParagraph(paragraphBuilder.build) - } - - protected def withBlockContent[T <: BlockContentOption : CanWriteXML](x: T): this.type = withContent(dataRecord(x)) - protected def withContent(x: DataRecord[_]): this.type -} - class BodyContentBuilder(var build: BodyContent = BodyContent()) extends Builder[BodyContent] with BlockContentBuilder { def withBlock(x: Block): this.type = withContent(dataRecord(x)) protected override def withContent(x: DataRecord[_]): this.type = { From 7d1e470a351d1a2b2c73cffbc5204cc2ecfb66ee Mon Sep 17 00:00:00 2001 From: Hosam Aly Date: Mon, 26 Mar 2018 13:21:58 +0100 Subject: [PATCH 4/9] Compile with more stringent checks --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 9766add..3de52b9 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -57,7 +57,7 @@ object BuildSettings { name := Metadata.projectName, crossScalaVersions := Dependencies.scalaVersions, scalaVersion := Dependencies.scalaVersions.min, - scalacOptions += "-target:jvm-1.8", + scalacOptions ++= Seq("-deprecation", "-feature", "-unchecked", "-target:jvm-1.8"), dependencyCheckFailBuildOnCVSS := 4 ) From 0883de910b8a97ce109b06d8605087843fc15d4d Mon Sep 17 00:00:00 2001 From: Hosam Aly Date: Mon, 26 Mar 2018 15:38:16 +0100 Subject: [PATCH 5/9] Accept an optional TitleType in HeadBuilder#withTitle --- src/main/scala/Builders.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/scala/Builders.scala b/src/main/scala/Builders.scala index 2ca4f16..b2c15dc 100644 --- a/src/main/scala/Builders.scala +++ b/src/main/scala/Builders.scala @@ -25,6 +25,7 @@ object `package` { type NoteType = TypeType2 type PublicationType = TypeType type TaglineType = TypeType3 + type TitleType = Type val BareNitfNamespace: NamespaceBinding = toScope(None -> defaultScope.uri) @@ -102,7 +103,10 @@ class NitfBuilder(var build: Nitf = Nitf(body = Body())) extends Builder[Nitf] { } class HeadBuilder(var build: Head = Head()) extends Builder[Head] { - def withTitle(x: String): this.type = { build = build.copy(title = Option(x).map(t => Title(Seq(dataRecord(t))))); this } + def withTitle(x: String, titleType: Option[TitleType] = None): this.type = { + build = build.copy(title = Option(x).map(t => Title(Seq(dataRecord(t)), typeValue = titleType))) + this + } def withDocData(x: Docdata): this.type = { build = build.copy(docdata = Option(x)); this } def withPublicationData(x: Pubdata): this.type = { build = build.copy(pubdata = build.pubdata :+ x); this } } From 5c0d2d6ef56e3b8bbc5f58db63d7d2fe7553abc0 Mon Sep 17 00:00:00 2001 From: Hosam Aly Date: Mon, 26 Mar 2018 16:13:42 +0100 Subject: [PATCH 6/9] Add `DocDataBuilder#withDocId(String)` and `BodyBuilder#withEnd(BodyEnd)` Additionally, make `DocDataBuilder#withDocDataOption` `protected` in line with other helper methods for easier extensibility. Finally, print the schema version in the `TwoWaySpec` test. --- src/main/scala/Builders.scala | 4 +++- src/test/scala/TwoWaySpec.scala | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/scala/Builders.scala b/src/main/scala/Builders.scala index b2c15dc..66af4cd 100644 --- a/src/main/scala/Builders.scala +++ b/src/main/scala/Builders.scala @@ -112,6 +112,7 @@ class HeadBuilder(var build: Head = Head()) extends Builder[Head] { } class DocDataBuilder(var build: Docdata = Docdata()) extends Builder[Docdata] { + def withDocId(x: String): this.type = withDocId(DocId(idString = Option(x))) def withDocId(x: DocId): this.type = withDocDataOption(x) def withCopyright(x: DocCopyright): this.type = withDocDataOption(x) def withIssueDate(x: LocalDate): this.type = withDocDataOption(DateIssue(norm = optionalString(x))) @@ -121,7 +122,7 @@ class DocDataBuilder(var build: Docdata = Docdata()) extends Builder[Docdata] { build = build.copy(managementStatus = optionalString(x)) this } - private def withDocDataOption[T <: DocdataOption : CanWriteXML](x: T): this.type = { + protected def withDocDataOption[T <: DocdataOption : CanWriteXML](x: T): this.type = { build = build.copy(docdataoption = build.docdataoption :+ dataRecord(x)) this } @@ -145,6 +146,7 @@ class PublicationDataBuilder(var build: Pubdata = Pubdata()) extends Builder[Pub class BodyBuilder(var build: Body = Body()) extends Builder[Body] { def withHead(x: BodyHead): this.type = { build = build.copy(bodyHead = Option(x)); this } def withContent(x: BodyContent): this.type = { build = build.copy(bodyContent = build.bodyContent :+ x); this } + def withEnd(x: BodyEnd): this.type = { build = build.copy(bodyEnd = Option(x)); this } } class BodyHeadBuilder(var build: BodyHead = BodyHead()) extends Builder[BodyHead] { diff --git a/src/test/scala/TwoWaySpec.scala b/src/test/scala/TwoWaySpec.scala index 2cd3f13..93e3785 100644 --- a/src/test/scala/TwoWaySpec.scala +++ b/src/test/scala/TwoWaySpec.scala @@ -34,7 +34,7 @@ class TwoWaySpec extends FunSpec { import TwoWaySpec._ describe("the parser") { - it("should parse and regenerate a sample file") { + it(s"should parse and regenerate a sample file (v$schemaVersion)") { val example = XML.load(resource(s"nitf-example-$schemaVersion.xml")) val schemaLocation = example.attribute(namespaces("xsi"), "schemaLocation").get.head.text From 10b5f41d9f1822a6cad25866ae80c27d2e4931df Mon Sep 17 00:00:00 2001 From: Hosam Aly Date: Mon, 26 Mar 2018 16:17:15 +0100 Subject: [PATCH 7/9] Move methods `withText` and `withXml` to their own traits Introduce `MixedContentBuilder` and `AnyContentBuilder` to encapsulate these methods. `MediaReferenceBuilder` now extends `MixedContentBuilder`. `AnyContentBuilder` is not used in this code base but an example in its docs shows how to use it. --- src/main/scala/Builders.scala | 51 +++++++++++++++++++++++++++---- src/test/scala/BuildersSpec.scala | 2 +- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/main/scala/Builders.scala b/src/main/scala/Builders.scala index 66af4cd..53c15d8 100644 --- a/src/main/scala/Builders.scala +++ b/src/main/scala/Builders.scala @@ -58,7 +58,6 @@ trait BlockContentBuilder { def withNitfTable(x: NitfTable): this.type = withBlockContent(x) def withSubordinateHeadline(x: Hl2): this.type = withBlockContent(x) - def withXml(x: NodeSeq): this.type = withContent(dataRecord(x)) def withTextParagraph(x: String, markAsSummary: Boolean = true): this.type = { var paragraphBuilder = new ParagraphBuilder().withText(x) if (markAsSummary) paragraphBuilder = paragraphBuilder.asSummary @@ -89,13 +88,46 @@ trait EnrichedTextBuilder { def withObjectTitle(x: ObjectTitle): this.type = withEnrichedText(x) def withPronunciation(x: Pronounce): this.type = withEnrichedText(x) def withVirtualLocation(x: Virtloc): this.type = withEnrichedText(x) - def withXml(x: NodeSeq): this.type = withContent(dataRecord(x)) - def withText(x: String): this.type = withContent(dataRecord(x)) protected def withEnrichedText[T <: EnrichedTextOption : CanWriteXML](x: T): this.type = withContent(dataRecord(x)) protected def withContent(x: DataRecord[_]): this.type } +trait MixedContentBuilder { + def withText(x: String): this.type = withContent(dataRecord(x)) + + /** Appends the given XML to the model object. + * Note that this method is _not_ type-safe! + * __No__ validation is performed to verify that the XML matches the expected schema.. + */ + def withXml(x: NodeSeq): this.type = withContent(dataRecord(x)) + + protected def withContent(x: DataRecord[_]): this.type +} + +/** An extension point to enable writing custom XML to NITF model objects that support extensions. + * This feature is supported by NITF 3.6 where the model objects contain a special field called ''any'', which acts as + * an extension point for provider-defined properties from other namespaces. + * + * This is an example of how it can be used (with versions 3.6.x only): + * {{{ + * import com.gu.nitf.model.builders._ + * import com.gu.nitf.scalaxb._ + * import scalaxb._ + * + * val builder = new NitfBuilder() with AnyExtensionsBuilder { + * protected override def withAny(x: DataRecord[_]) = { build = build.copy(any = build.any :+ x); this } + * } + * + * val nitf = builder.withXml().build + * val xml = scalaxb.toXML(nitf, None, None, toScope(Some("my") -> "http://www.example.com/my-extension")) + * }}} + */ +trait AnyContentBuilder { this: Builder[_ <: { def any: Seq[scalaxb.DataRecord[Any]] }] => + def withXml(x: NodeSeq): this.type = withAny(dataRecord(x)) + protected def withAny(x: DataRecord[_]): this.type +} + class NitfBuilder(var build: Nitf = Nitf(body = Body())) extends Builder[Nitf] { def withBody(x: Body): this.type = { build = build.copy(body = x); this } def withHead(x: Head): this.type = { build = build.copy(head = Option(x)); this } @@ -181,7 +213,7 @@ class HeadlineBuilder(var build: Hedline = Hedline(Hl1())) extends Builder[Hedli def withSubordinateHeadline(x: String): this.type = { withSubordinateHeadline(Hl2(Seq(dataRecord(x)))); this } } -class PrimaryHeadlineBuilder(var build: Hl1 = Hl1()) extends Builder[Hl1] with EnrichedTextBuilder { +class PrimaryHeadlineBuilder(var build: Hl1 = Hl1()) extends Builder[Hl1] with EnrichedTextBuilder with MixedContentBuilder { protected def withContent(x: DataRecord[_]): this.type = { build = build.copy(mixed = build.mixed :+ x); this } } @@ -221,6 +253,11 @@ class MediaBuilder(var build: Media) extends Builder[Media] { } } +class MediaCaptionBuilder(var build: MediaCaption = MediaCaption()) + extends Builder[MediaCaption] with BlockContentBuilder with EnrichedTextBuilder with MixedContentBuilder { + protected override def withContent(x: DataRecord[_]): this.type = { build = build.copy(build.mixed :+ x); this } +} + class MediaMetadataBuilder(var build: MediaMetadata) extends Builder[MediaMetadata] { def this(name: String) = this(MediaMetadata(name = name)) def this(name: String, value: String) = this(MediaMetadata(name = name, valueAttribute = Option(value))) @@ -229,7 +266,7 @@ class MediaMetadataBuilder(var build: MediaMetadata) extends Builder[MediaMetada def withValue(x: String): this.type = { build = build.copy(valueAttribute = Option(x)); this } } -class MediaReferenceBuilder(var build: MediaReference = MediaReference()) extends Builder[MediaReference] { +class MediaReferenceBuilder(var build: MediaReference = MediaReference()) extends Builder[MediaReference] with MixedContentBuilder { def asNoFlow: this.type = { build = build.copy(noflow = Some(NoflowValue)); this } def withSource(x: URI): this.type = withSource(x.toString) def withSource(x: String): this.type = { build = build.copy(source = Option(x)); this } @@ -245,9 +282,11 @@ class MediaReferenceBuilder(var build: MediaReference = MediaReference()) extend def withSourceCredit(x: String): this.type = { build = build.copy(sourceCredit = Option(x)); this } def withTimeUnitOfMeasure(x: String): this.type = { build = build.copy(timeUnitOfMeasure = Option(x)); this } def withTimeLength(x: Int): this.type = { build = build.copy(time = Option(x.toString)); this } + + protected override def withContent(x: DataRecord[_]): this.type = { build = build.copy(build.mixed :+ x); this } } -class ParagraphBuilder(var build: P = P()) extends Builder[P] with EnrichedTextBuilder { +class ParagraphBuilder(var build: P = P()) extends Builder[P] with EnrichedTextBuilder with MixedContentBuilder { def asLead: this.type = { build = build.copy(lede = Option("true")); this } def asSummary: this.type = { build = build.copy(summary = Option("true")); this } def asOptional: this.type = { build = build.copy(optionalText = Option("true")); this } diff --git a/src/test/scala/BuildersSpec.scala b/src/test/scala/BuildersSpec.scala index fbd7e7a..3a2f15f 100644 --- a/src/test/scala/BuildersSpec.scala +++ b/src/test/scala/BuildersSpec.scala @@ -32,7 +32,7 @@ class BuildersSpec extends FunSpec { .withHead(new BodyHeadBuilder() .withHeadline("News Article") .withByline("It took a lot of work to get there") - .withAbstract(new AbstractBuilder().withXml(

It wasn't easy, but they never gave up!

)) + .withAbstract(new AbstractBuilder().withTextParagraph("It wasn't easy, but they never gave up!")) ) .withContent(new BodyContentBuilder() .withParagraph(new ParagraphBuilder().withText("It was done, really!")) From 83865d98a6eabd0044659b00fb31ccebf123ca88 Mon Sep 17 00:00:00 2001 From: Hosam Aly Date: Mon, 26 Mar 2018 16:31:00 +0100 Subject: [PATCH 8/9] Test scaladoc syntax in Travis CI --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d8b54e5..31c4a08 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ env: script: - sbt "++$TRAVIS_SCALA_VERSION" "nitf${version}/test" + - sbt "++$TRAVIS_SCALA_VERSION" "nitf${version}/doc" # These directories are cached to S3 at the end of the build cache: @@ -24,6 +25,6 @@ cache: - $HOME/.sbt before_cache: - # Cleanup the cached directories to avoid unnecessary cache updates + # Clean up the cached directories to avoid unnecessary cache updates - find $HOME/.ivy2/cache -name "ivydata-*.properties" -print -delete - find $HOME/.sbt -name "*.lock" -print -delete From 74a60d0f2889b3a78fecb0b323655eaad3f984ce Mon Sep 17 00:00:00 2001 From: Hosam Aly Date: Mon, 26 Mar 2018 16:27:52 +0100 Subject: [PATCH 9/9] Deprecate `MixedContentBuilder#withXml` It's not safe. The caller can inject any XML that doesn't match the schema. In all builders of mixed-content objects, values are appended to the element. Combining these methods with `withText`, all forms of valid XML can be generated. --- src/main/scala/Builders.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/scala/Builders.scala b/src/main/scala/Builders.scala index 53c15d8..38d1074 100644 --- a/src/main/scala/Builders.scala +++ b/src/main/scala/Builders.scala @@ -99,7 +99,10 @@ trait MixedContentBuilder { /** Appends the given XML to the model object. * Note that this method is _not_ type-safe! * __No__ validation is performed to verify that the XML matches the expected schema.. + * + * @deprecated Use multiple invocations of other methods to construct the data for a type-safe approach */ + @deprecated("Use multiple invocations of other methods for a type-safe approach", since = "3.x.3") def withXml(x: NodeSeq): this.type = withContent(dataRecord(x)) protected def withContent(x: DataRecord[_]): this.type