Skip to content
This repository has been archived by the owner on Apr 13, 2019. It is now read-only.

Commit

Permalink
added field asm suppliers codegen
Browse files Browse the repository at this point in the history
  • Loading branch information
Konstantin Sobolev committed Nov 8, 2017
1 parent ddfe8df commit 8604a77
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,9 @@ object JavaGenNames {

def qnameArgs(fqn: Qn): Seq[String] = fqn.last() +: fqn.removeLastSegment().segments.toSeq

def javaTypeName(ln: String): String = if (ReservedTypeNames.contains(ln)) ln + '_' else ln
def javaTypeName(ln: String): String = if (ReservedTypeNames.contains(ln)) ln + '_' else JavaNames.javaName(ln)

def javaFieldName(fn: String): String = if (ReservedFieldNames.contains(fn)) fn + '_' else fn
def javaFieldName(fn: String): String = if (ReservedFieldNames.contains(fn)) fn + '_' else JavaNames.javaName(fn)

/** set of type names that conflict with our own generated java classes */
private val ReservedTypeNames: Set[String] = Set(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright 2017 Sumo Logic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package ws.epigraph.java.service.assemblers

import java.nio.file.Path

import ws.epigraph.compiler.CDatumType
import ws.epigraph.java._
import ws.epigraph.java.NewlineStringInterpolator.NewlineHelper
import ws.epigraph.java.JavaGenNames.{ln, lqn2}
import ws.epigraph.java.service.projections.req.output.ReqOutputRecordModelProjectionGen
import ws.epigraph.lang.Qn
import ws.epigraph.util.JavaNames

/**
* @author <a href="mailto:[email protected]">Konstantin Sobolev</a>
*/
class FieldAssemblersGen(rag: RecordAsmGen, val ctx: GenContext) extends JavaGen {
private val cType: CDatumType = JavaGenUtils.toCType(rag.g.op.`type`())

val namespace: Qn = rag.g.namespace

val shortClassName: String = ln(cType) + "FieldAssemblers"

val fullClassName: String = namespace.append(shortClassName).toString

override def relativeFilePath: Path = JavaGenUtils.fqnToPath(namespace).resolve(shortClassName + ".java")

private lazy val importManager: ImportManager = new ImportManager(namespace)

private lazy val parentOpt: Option[FieldAssemblersGen] = rag.g.parentClassGenOpt.map(
pg => pg.asInstanceOf[ReqOutputRecordModelProjectionGen].assemblerGen.fieldAssemblersGen
)

def methodName(fieldName: String): String = JavaNames.javaName(fieldName)

case class AsmSupplier(fieldName: String, overloaded: Boolean, pg: ReqOutputRecordModelProjectionGen, importManager: ImportManager) {
private val fieldPart = rag.fieldPart(fieldName).get

val projectionType: importManager.ImportedName = importManager.use(fieldPart.fieldGen.fullClassName)

val assemblerResultType: importManager.ImportedName = importManager.use(
lqn2(
fieldPart.fieldType,
namespace.toString
)
)

val resultTypeSuffix: String = if (fieldPart.isEntity) "" else ".Value"

def gen: String = /*@formatter:off*/sn"""\
/**
* Builds {@code $fieldName} field value
*
* @param dto data transfer object
* @param projection request projection
* @param ctx assembly context
*
* @return field value
*/
public ${Imports.notNull}$assemblerResultType$resultTypeSuffix ${methodName(fieldName)}(${Imports.notNull}D dto, ${Imports.notNull}$projectionType projection, ${Imports.notNull}${Imports.assemblerContext} context);
"""/*@formatter:on*/
}

val asmSuppliers: Seq[AsmSupplier] = rag.g.fieldProjections.toSeq.map { case (fieldName, (parentGenOpt, _)) =>
AsmSupplier(
fieldName,
parentGenOpt.isDefined,
parentGenOpt.getOrElse(rag.g).asInstanceOf[ReqOutputRecordModelProjectionGen],
importManager
)
}


override protected def generate: String = {
if (rag.g.invalidParentClassGenerator) {
throw new TryLaterException(s"Can't generate $fullClassName because parent projection wasn't created yet")
}

val parentImp = parentOpt.map(p => importManager.use(p.fullClassName))

closeImports()

val extendsClause: String = parentImp.map(ip => s"extends $ip<D> ").getOrElse("")

/*@formatter:off*/sn"""\
${JavaGenUtils.topLevelComment}
package $namespace;

${JavaGenUtils.generateImports(importManager.imports)}

/**
* Field assemblers for {@code ${ln(cType)}} type
*/
${JavaGenUtils.generatedAnnotation(this)}
public interface $shortClassName<D> $extendsClause{
${asmSuppliers.map(_.gen).mkString("\n")}\
}"""/*@formatter:on*/
}

protected object Imports {
val notNull: ImportManager.Imported =
if (ctx.java8Annotations) importManager.use("org.jetbrains.annotations.NotNull").prepend("@").append(" ") else ImportManager.empty
val nullable: ImportManager.Imported =
if (ctx.java8Annotations) importManager.use("org.jetbrains.annotations.Nullable").prepend("@").append(" ") else ImportManager.empty
val func: ImportManager.Imported = importManager.use("java.util.function.Function")
val assembler: ImportManager.Imported = importManager.use("ws.epigraph.assembly.Asm")
val assemblerContext: ImportManager.Imported = importManager.use("ws.epigraph.assembly.AsmContext")
val _type: ImportManager.Imported = importManager.use("ws.epigraph.types.Type")
val errValue: ImportManager.Imported = importManager.use("ws.epigraph.errors.ErrorValue")
}

protected def closeImports(): Unit = {
val _ = Imports.assembler // cause lazy eval
importManager.close()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,15 @@ import scala.collection.immutable.ListMap
* @author <a href="mailto:[email protected]">Konstantin Sobolev</a>
*/
class RecordAsmGen(
override protected val g: ReqOutputRecordModelProjectionGen,
override val g: ReqOutputRecordModelProjectionGen,
val ctx: GenContext) extends JavaGen with ModelAsmGen {

override protected type G = ReqOutputRecordModelProjectionGen

lazy val fieldAssemblersGen: FieldAssemblersGen = new FieldAssemblersGen(this, ctx)

override def children = Iterable(fieldAssemblersGen)

import Imports._

case class FieldParts(field: CField, fieldGen: ReqProjectionGen) extends Comparable[FieldParts] {
Expand All @@ -43,7 +47,7 @@ class RecordAsmGen(

def isEntity: Boolean = fieldType.kind == CTypeKind.ENTITY

val fieldGenType: importManager.ImportedName = importManager.use(fieldGen.fullClassName)
val fieldProjection: importManager.ImportedName = importManager.use(fieldGen.fullClassName)

val assemblerResultType: importManager.ImportedName = importManager.use(
lqn2(
Expand All @@ -52,7 +56,7 @@ class RecordAsmGen(
)
)

def fieldAsmType: String = s"$assembler<? super D, ? super $fieldGenType, ? extends $assemblerResultType${ if (isEntity) "" else ".Value" }>"
def fieldAsmType: String = s"$assembler<? super D, ? super $fieldProjection, ? extends $assemblerResultType${ if (isEntity) "" else ".Value" }>"

def fbf: String = field.name + "FieldAsm"

Expand All @@ -75,6 +79,8 @@ class RecordAsmGen(
FieldParts(f, fg.dataProjectionGen)
}.toSeq.sorted

def fieldPart(fieldName: String): Option[FieldParts] = fps.find(_.field.name == fieldName)

private val obj = importManager.use("java.lang.Object")

protected override lazy val defaultBuild: String = {
Expand All @@ -94,6 +100,8 @@ ${if (hasMeta) s" b.setMeta(metaAsm.assemble(dto, p.meta(), ctx));\n" else ""}\
}

override protected def generate: String = {
val fieldAssembersImp = importManager.use(fieldAssemblersGen.fullClassName)

closeImports()

/*@formatter:off*/sn"""\
Expand All @@ -106,33 +114,55 @@ ${JavaGenUtils.generateImports(importManager.imports)}
* Value assembler for {@code ${ln(cType)}} type, driven by request output projection
*/
${JavaGenUtils.generatedAnnotation(this)}
public class $shortClassName<D> implements $assembler<D, $notNull $projectionName, $notNull $t.Value> {
${if (hasTails) s" private final $notNull $func<? super D, ? extends Type> typeExtractor;\n" else "" }\
public class $shortClassName<D> implements $assembler<D, $notNull$projectionName, $notNull$t.Value> {
${if (hasTails) s" private final $notNull$func<? super D, ? extends Type> typeExtractor;\n" else "" }\
//field assemblers
${fps.map { fp => s" private final $notNull ${fp.fieldAsmType} ${fp.fbf};"}.mkString("\n") }\
${if (hasTails) tps.map { tp => s" private final $notNull ${tp.assemblerType} ${tp.assembler};"}.mkString("\n //tail assemblers\n","\n","") else "" }\
${if (hasMeta) s" //meta assembler\n private final $notNull $metaAsmType metaAsm;" else ""}
${fps.map { fp => s" private final $notNull${fp.fieldAsmType} ${fp.fbf};"}.mkString("\n") }\
${if (hasTails) tps.map { tp => s" private final $notNull${tp.assemblerType} ${tp.assembler};"}.mkString("\n //tail assemblers\n","\n","") else "" }\
${if (hasMeta) s" //meta assembler\n private final $notNull$metaAsmType metaAsm;" else ""}

/**
* Asm constructor
* Asm constructor from individual field assemblers
*
${if (hasTails) s" * @param typeExtractor data type extractor, used to determine DTO type\n" else ""}\
${fps.map { fp => s" * @param ${fp.javadoc}"}.mkString("\n") }\
${if (hasTails) tps.map { tp => s" * @param ${tp.javadoc}"}.mkString("\n","\n","") else "" }\
${if (hasMeta) s"\n * @param metaAsm metadata assembler" else ""}
*/
public $shortClassName(
${if (hasTails) s" $notNull $func<? super D, ? extends Type> typeExtractor,\n" else "" }\
${fps.map { fp => s" $notNull ${fp.fieldAsmType} ${fp.fbf}"}.mkString(",\n") }\
${if (hasTails) tps.map { tp => s" $notNull ${tp.assemblerType} ${tp.assembler}"}.mkString(",\n", ",\n", "") else ""}\
${if (hasMeta) s",\n $notNull $metaAsmType metaAsm" else ""}
${if (hasTails) s" $notNull$func<? super D, ? extends Type> typeExtractor,\n" else "" }\
${fps.map { fp => s" $notNull${fp.fieldAsmType} ${fp.fbf}"}.mkString(",\n") }\
${if (hasTails) tps.map { tp => s" $notNull${tp.assemblerType} ${tp.assembler}"}.mkString(",\n", ",\n", "") else ""}\
${if (hasMeta) s",\n $notNull$metaAsmType metaAsm" else ""}
) {
${if (hasTails) s" this.typeExtractor = typeExtractor;\n" else "" }\
${fps.map { fp => s" this.${fp.fbf} = ${fp.fbf};"}.mkString("\n") }\
${if (hasTails) tps.map { tp => s" this.${tp.assembler} = ${tp.assembler};"}.mkString("\n","\n","") else ""}\
${if (hasMeta) s"\n this.metaAsm = metaAsm;" else ""}
}

/**
* Asm factory using field assemblers supplier object
*
${if (hasTails) s" * @param typeExtractor data type extractor, used to determine DTO type\n" else ""}\
* @param fieldAssemblers field assemblers supplier object
${if (hasTails) tps.map { tp => s" * @param ${tp.javadoc}"}.mkString("","\n","\n") else "" }\
${if (hasMeta) s"\n * @param metaAsm metadata assembler\n" else ""}\
*/
public static <D> $shortClassName<D> fromFieldAssemblers(
${if (hasTails) s" $notNull$func<? super D, ? extends Type> typeExtractor,\n" else "" }\
$notNull$fieldAssembersImp<D> fieldAssemblers\
${if (hasTails) tps.map { tp => s"$notNull${tp.assemblerType} ${tp.assembler}"}.mkString(",\n ", ",\n ","") else ""}\
${if (hasMeta) s",\n $notNull$metaAsmType metaAsm" else ""}
) {
return new $shortClassName<D>(\
${if (hasTails) s"\n typeExtractor," else "" }\
${fps.map {fp => s"\n fieldAssemblers::${fieldAssemblersGen.methodName(fp.field.name)}"}.mkString("", ",", if(hasTails||hasMeta) "," else "")}\
${if (hasTails) tps.map { tp => s" ${tp.assembler}"}.mkString("\n", ",\n", if(hasMeta) "," else "") else ""}\
${if (hasMeta) "\n metaAsm" else ""}
);
}

/**
* Assembles {@code $t} value from DTO
*
Expand All @@ -143,7 +173,7 @@ ${if (hasMeta) s"\n this.metaAsm = metaAsm;" else ""}
* @return {@code $t} value object
*/
@Override
public $notNull $t.Value assemble(D dto, $notNull $projectionName p, $notNull $assemblerContext ctx) {
public $notNull$t.Value assemble(D dto, $notNull$projectionName p, $notNull$assemblerContext ctx) {
if (dto == null)
return $t.type.createValue($errValue.NULL);
else ${if (hasTails) tailsBuild else nonTailsBuild}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ trait ReqTypeProjectionGen extends ReqProjectionGen {
generate0
}

protected def invalidParentClassGenerator: Boolean =
def invalidParentClassGenerator: Boolean =
parentClassGenOpt match {
case Some(parentGen) => parentGen.invalidParentClassGenerator
case None => normalizedFromGenOpt.isDefined
Expand Down

0 comments on commit 8604a77

Please sign in to comment.