packages feed

hydra-0.5.0: src/main/haskell/Hydra/Langs/Java/Utils.hs

module Hydra.Langs.Java.Utils where

import Hydra.Kernel
import Hydra.Langs.Java.Language
import Hydra.Langs.Java.Names
import qualified Hydra.Langs.Java.Syntax as Java
import qualified Hydra.Lib.Strings as Strings

import qualified Data.List as L
import qualified Data.Map as M
import qualified Data.Set as S
import qualified Data.Maybe as Y


type PackageMap = M.Map Namespace Java.PackageName

data Aliases = Aliases {
  aliasesCurrentNamespace :: Namespace,
  aliasesPackages :: PackageMap,
  aliasesRecursiveVars :: S.Set Name } deriving Show

addExpressions :: [Java.MultiplicativeExpression] -> Java.AdditiveExpression
addExpressions exprs = L.foldl add (Java.AdditiveExpressionUnary $ L.head exprs) $ L.tail exprs
  where
    add ae me = Java.AdditiveExpressionPlus $ Java.AdditiveExpression_Binary ae me

addJavaTypeParameter :: Java.ReferenceType -> Java.Type -> Flow (Graph) Java.Type
addJavaTypeParameter rt t = case t of
  Java.TypeReference rt1 -> case rt1 of
    Java.ReferenceTypeClassOrInterface cit -> case cit of
      Java.ClassOrInterfaceTypeClass (Java.ClassType anns qual id args) -> pure $
        Java.TypeReference $ Java.ReferenceTypeClassOrInterface $
          Java.ClassOrInterfaceTypeClass $ Java.ClassType anns qual id (args ++ [Java.TypeArgumentReference rt])
      _ -> fail $ "expected a Java class type. Found: " ++ show cit
    Java.ReferenceTypeVariable tv -> pure $ javaTypeVariableToType tv
    _ -> fail $ "expected a Java class or interface type, or a variable. Found: " ++ show rt
  _ -> fail $ "expected a reference type. Found: " ++ show t

fieldExpression :: Java.Identifier -> Java.Identifier -> Java.ExpressionName
fieldExpression varId fieldId = Java.ExpressionName (Just $ Java.AmbiguousName [varId]) fieldId

fieldNameToJavaExpression :: Name -> Java.Expression
fieldNameToJavaExpression fname = javaPostfixExpressionToJavaExpression $
  Java.PostfixExpressionName $ Java.ExpressionName Nothing (fieldNameToJavaIdentifier fname)

fieldNameToJavaIdentifier :: Name -> Java.Identifier
fieldNameToJavaIdentifier (Name name) = javaIdentifier name

fieldNameToJavaVariableDeclarator :: Name -> Java.VariableDeclarator
fieldNameToJavaVariableDeclarator (Name n) = javaVariableDeclarator (javaIdentifier n) Nothing

fieldNameToJavaVariableDeclaratorId :: Name -> Java.VariableDeclaratorId
fieldNameToJavaVariableDeclaratorId (Name n) = javaVariableDeclaratorId $ javaIdentifier n

importAliasesForModule :: Module -> Aliases
importAliasesForModule mod = Aliases (moduleNamespace mod) M.empty S.empty

interfaceMethodDeclaration :: [Java.InterfaceMethodModifier] -> [Java.TypeParameter] -> String -> [Java.FormalParameter]
   -> Java.Result -> Maybe [Java.BlockStatement] -> Java.InterfaceMemberDeclaration
interfaceMethodDeclaration mods tparams methodName params result stmts = Java.InterfaceMemberDeclarationInterfaceMethod $
    Java.InterfaceMethodDeclaration mods header body
  where
    header = javaMethodHeader tparams methodName params result
    body = javaMethodBody stmts

isEscaped :: String -> Bool
isEscaped s = L.head s == '$'

javaAdditiveExpressionToJavaExpression :: Java.AdditiveExpression -> Java.Expression
javaAdditiveExpressionToJavaExpression = javaRelationalExpressionToJavaExpression .
  Java.RelationalExpressionSimple . Java.ShiftExpressionUnary

javaAssignmentStatement :: Java.LeftHandSide -> Java.Expression -> Java.Statement
javaAssignmentStatement lhs rhs = Java.StatementWithoutTrailing $ Java.StatementWithoutTrailingSubstatementExpression $
    Java.ExpressionStatement $ Java.StatementExpressionAssignment ass
  where
    ass = Java.Assignment lhs Java.AssignmentOperatorSimple rhs

javaBoolean :: Bool -> Java.Literal
javaBoolean = Java.LiteralBoolean

javaBooleanExpression :: Bool -> Java.Expression
javaBooleanExpression = javaPrimaryToJavaExpression . javaLiteralToJavaPrimary . javaBoolean

javaBooleanType :: Java.Type
javaBooleanType = javaPrimitiveTypeToJavaType Java.PrimitiveTypeBoolean

javaCastExpression :: Java.ReferenceType -> Java.UnaryExpression -> Java.CastExpression
javaCastExpression rt expr = Java.CastExpressionNotPlusMinus $ Java.CastExpression_NotPlusMinus rb expr
  where
    rb = Java.CastExpression_RefAndBounds rt []

javaCastExpressionToJavaExpression :: Java.CastExpression -> Java.Expression
javaCastExpressionToJavaExpression = javaUnaryExpressionToJavaExpression . Java.UnaryExpressionOther .
  Java.UnaryExpressionNotPlusMinusCast

javaClassDeclaration :: Aliases -> [Java.TypeParameter] -> Name -> [Java.ClassModifier]
   -> Maybe Name -> [Java.InterfaceType] -> [Java.ClassBodyDeclarationWithComments] -> Java.ClassDeclaration
javaClassDeclaration aliases tparams elName mods supname impls bodyDecls = Java.ClassDeclarationNormal $ Java.NormalClassDeclaration {
  Java.normalClassDeclarationModifiers = mods,
  Java.normalClassDeclarationIdentifier = javaDeclName elName,
  Java.normalClassDeclarationParameters = tparams,
  Java.normalClassDeclarationExtends = fmap (\n -> nameToJavaClassType aliases True [] n Nothing) supname,
  Java.normalClassDeclarationImplements = impls,
  Java.normalClassDeclarationBody = Java.ClassBody bodyDecls}

javaClassType :: [Java.ReferenceType] -> Maybe Java.PackageName -> String -> Java.ClassType
javaClassType args pkg id = Java.ClassType [] qual (javaTypeIdentifier id) targs
  where
    qual = maybe Java.ClassTypeQualifierNone Java.ClassTypeQualifierPackage pkg
    targs = Java.TypeArgumentReference <$> args

javaClassTypeToJavaType :: Java.ClassType -> Java.Type
javaClassTypeToJavaType = Java.TypeReference . Java.ReferenceTypeClassOrInterface . Java.ClassOrInterfaceTypeClass

javaConditionalAndExpressionToJavaExpression :: Java.ConditionalAndExpression -> Java.Expression
javaConditionalAndExpressionToJavaExpression condAndEx = Java.ExpressionAssignment $
  Java.AssignmentExpressionConditional $ Java.ConditionalExpressionSimple $ Java.ConditionalOrExpression [condAndEx]

javaConstructorCall :: Java.ClassOrInterfaceTypeToInstantiate -> [Java.Expression] -> Maybe Java.ClassBody -> Java.Expression
javaConstructorCall ci args mbody = javaPrimaryToJavaExpression $
  Java.PrimaryNoNewArray $
  Java.PrimaryNoNewArrayClassInstance $
  Java.ClassInstanceCreationExpression Nothing $
  Java.UnqualifiedClassInstanceCreationExpression [] ci args mbody

javaConstructorName :: Java.Identifier -> Maybe Java.TypeArgumentsOrDiamond -> Java.ClassOrInterfaceTypeToInstantiate
javaConstructorName id targs = Java.ClassOrInterfaceTypeToInstantiate [Java.AnnotatedIdentifier [] id] targs

javaDeclName :: Name -> Java.TypeIdentifier
javaDeclName = Java.TypeIdentifier . javaVariableName

javaEmptyStatement :: Java.Statement
javaEmptyStatement = Java.StatementWithoutTrailing $ Java.StatementWithoutTrailingSubstatementEmpty Java.EmptyStatement

javaEqualityExpressionToJavaExpression :: Java.EqualityExpression -> Java.Expression
javaEqualityExpressionToJavaExpression eqEx = javaConditionalAndExpressionToJavaExpression $
    Java.ConditionalAndExpression [javaEqualityExpressionToJavaInclusiveOrExpression eqEx]

javaEqualityExpressionToJavaInclusiveOrExpression :: Java.EqualityExpression -> Java.InclusiveOrExpression
javaEqualityExpressionToJavaInclusiveOrExpression eq = Java.InclusiveOrExpression [
  Java.ExclusiveOrExpression [Java.AndExpression [eq]]]

javaEquals :: Java.EqualityExpression -> Java.RelationalExpression -> Java.EqualityExpression
javaEquals lhs rhs = Java.EqualityExpressionEqual $ Java.EqualityExpression_Binary lhs rhs

javaEqualsNull :: Java.EqualityExpression -> Java.EqualityExpression
javaEqualsNull lhs = javaEquals lhs $ javaLiteralToJavaRelationalExpression Java.LiteralNull

javaExpressionNameToJavaExpression :: Java.ExpressionName -> Java.Expression
javaExpressionNameToJavaExpression = javaPostfixExpressionToJavaExpression . Java.PostfixExpressionName

javaExpressionToJavaPrimary :: Java.Expression -> Java.Primary
javaExpressionToJavaPrimary = Java.PrimaryNoNewArray . Java.PrimaryNoNewArrayParens

javaExpressionToJavaUnaryExpression :: Java.Expression -> Java.UnaryExpression
javaExpressionToJavaUnaryExpression = javaPrimaryToJavaUnaryExpression . javaExpressionToJavaPrimary

javaFieldAccessToJavaExpression :: Java.FieldAccess -> Java.Expression
javaFieldAccessToJavaExpression = javaPrimaryToJavaExpression . Java.PrimaryNoNewArray . Java.PrimaryNoNewArrayFieldAccess

javaIdentifier :: String -> Java.Identifier
javaIdentifier = Java.Identifier . sanitizeJavaName

javaIdentifierToJavaExpressionName :: Java.Identifier -> Java.ExpressionName
javaIdentifierToJavaExpressionName id = Java.ExpressionName Nothing id

javaIdentifierToJavaExpression :: Java.Identifier -> Java.Expression
javaIdentifierToJavaExpression = javaUnaryExpressionToJavaExpression . javaIdentifierToJavaUnaryExpression

javaIdentifierToJavaRelationalExpression :: Java.Identifier -> Java.RelationalExpression
javaIdentifierToJavaRelationalExpression id = javaPostfixExpressionToJavaRelationalExpression $
  Java.PostfixExpressionName $ Java.ExpressionName Nothing id

javaIdentifierToJavaUnaryExpression :: Java.Identifier -> Java.UnaryExpression
javaIdentifierToJavaUnaryExpression = javaRelationalExpressionToJavaUnaryExpression . javaIdentifierToJavaRelationalExpression

javaInstanceOf :: Java.RelationalExpression -> Java.ReferenceType -> Java.RelationalExpression
javaInstanceOf lhs rhs = Java.RelationalExpressionInstanceof $ Java.RelationalExpression_InstanceOf lhs rhs

javaInt :: Integral a => a -> Java.Literal
javaInt i = Java.LiteralInteger $ Java.IntegerLiteral $ fromIntegral i

javaIntExpression :: Integer -> Java.Expression
javaIntExpression = javaPrimaryToJavaExpression . javaLiteralToJavaPrimary . javaInt

javaIntType :: Java.Type
javaIntType = javaPrimitiveTypeToJavaType $ Java.PrimitiveTypeNumeric $ Java.NumericTypeIntegral Java.IntegralTypeInt

javaInterfaceDeclarationToJavaClassBodyDeclaration :: Java.NormalInterfaceDeclaration -> Java.ClassBodyDeclaration
javaInterfaceDeclarationToJavaClassBodyDeclaration = Java.ClassBodyDeclarationClassMember .
  Java.ClassMemberDeclarationInterface . Java.InterfaceDeclarationNormalInterface

javaLambda :: Name -> Java.Expression -> Java.Expression
javaLambda var jbody = Java.ExpressionLambda $ Java.LambdaExpression params (Java.LambdaBodyExpression jbody)
  where
    params = Java.LambdaParametersSingle $ variableToJavaIdentifier var

javaLambdaFromBlock :: Name -> Java.Block -> Java.Expression
javaLambdaFromBlock var block = Java.ExpressionLambda $ Java.LambdaExpression params (Java.LambdaBodyBlock block)
  where
    params = Java.LambdaParametersSingle $ variableToJavaIdentifier var

javaLiteralToJavaRelationalExpression :: Java.Literal -> Java.RelationalExpression
javaLiteralToJavaRelationalExpression = javaMultiplicativeExpressionToJavaRelationalExpression .
  javaLiteralToJavaMultiplicativeExpression

javaLiteralToJavaExpression = javaRelationalExpressionToJavaExpression .
  javaMultiplicativeExpressionToJavaRelationalExpression .
  javaLiteralToJavaMultiplicativeExpression

javaLiteralToJavaMultiplicativeExpression = Java.MultiplicativeExpressionUnary . javaPrimaryToJavaUnaryExpression .
  javaLiteralToJavaPrimary

javaLiteralToJavaPrimary :: Java.Literal -> Java.Primary
javaLiteralToJavaPrimary = Java.PrimaryNoNewArray . Java.PrimaryNoNewArrayLiteral

javaMemberField :: [Java.FieldModifier] -> Java.Type -> Java.VariableDeclarator -> Java.ClassBodyDeclaration
javaMemberField mods jt var = Java.ClassBodyDeclarationClassMember $ Java.ClassMemberDeclarationField $
  Java.FieldDeclaration mods (Java.UnannType jt) [var]

javaMethodBody :: Maybe [Java.BlockStatement] -> Java.MethodBody
javaMethodBody stmts = Y.maybe Java.MethodBodyNone (Java.MethodBodyBlock . Java.Block) stmts

javaMethodDeclarationToJavaClassBodyDeclaration :: Java.MethodDeclaration -> Java.ClassBodyDeclaration
javaMethodDeclarationToJavaClassBodyDeclaration = Java.ClassBodyDeclarationClassMember . Java.ClassMemberDeclarationMethod

javaMethodHeader :: [Java.TypeParameter] -> String -> [Java.FormalParameter] -> Java.Result -> Java.MethodHeader
javaMethodHeader tparams methodName params result = Java.MethodHeader tparams result decl mthrows
  where
    decl = Java.MethodDeclarator (Java.Identifier methodName) Nothing params
    mthrows = Nothing

javaMethodInvocationToJavaExpression :: Java.MethodInvocation -> Java.Expression
javaMethodInvocationToJavaExpression = javaPrimaryToJavaExpression . javaMethodInvocationToJavaPrimary

javaMethodInvocationToJavaStatement :: Java.MethodInvocation -> Java.Statement
javaMethodInvocationToJavaStatement = Java.StatementWithoutTrailing . Java.StatementWithoutTrailingSubstatementExpression .
  Java.ExpressionStatement . Java.StatementExpressionMethodInvocation

javaMethodInvocationToJavaPostfixExpression :: Java.MethodInvocation -> Java.PostfixExpression
javaMethodInvocationToJavaPostfixExpression = Java.PostfixExpressionPrimary . Java.PrimaryNoNewArray .
  Java.PrimaryNoNewArrayMethodInvocation

javaMethodInvocationToJavaPrimary :: Java.MethodInvocation -> Java.Primary
javaMethodInvocationToJavaPrimary = Java.PrimaryNoNewArray .
  Java.PrimaryNoNewArrayMethodInvocation

javaMultiplicativeExpressionToJavaRelationalExpression :: Java.MultiplicativeExpression -> Java.RelationalExpression
javaMultiplicativeExpressionToJavaRelationalExpression = Java.RelationalExpressionSimple .
  Java.ShiftExpressionUnary . Java.AdditiveExpressionUnary

javaPackageDeclaration :: Namespace -> Java.PackageDeclaration
javaPackageDeclaration (Namespace name) = Java.PackageDeclaration [] (Java.Identifier <$> Strings.splitOn "/" name)

javaPostfixExpressionToJavaEqualityExpression :: Java.PostfixExpression -> Java.EqualityExpression
javaPostfixExpressionToJavaEqualityExpression = Java.EqualityExpressionUnary .
  javaUnaryExpressionToJavaRelationalExpression . Java.UnaryExpressionOther . Java.UnaryExpressionNotPlusMinusPostfix

javaPostfixExpressionToJavaExpression :: Java.PostfixExpression -> Java.Expression
javaPostfixExpressionToJavaExpression = javaRelationalExpressionToJavaExpression .
  javaPostfixExpressionToJavaRelationalExpression

javaPostfixExpressionToJavaInclusiveOrExpression :: Java.PostfixExpression -> Java.InclusiveOrExpression
javaPostfixExpressionToJavaInclusiveOrExpression = javaEqualityExpressionToJavaInclusiveOrExpression .
  javaPostfixExpressionToJavaEqualityExpression

javaPostfixExpressionToJavaRelationalExpression :: Java.PostfixExpression -> Java.RelationalExpression
javaPostfixExpressionToJavaRelationalExpression =
  javaUnaryExpressionToJavaRelationalExpression . javaPostfixExpressionToJavaUnaryExpression

javaPostfixExpressionToJavaUnaryExpression :: Java.PostfixExpression -> Java.UnaryExpression
javaPostfixExpressionToJavaUnaryExpression = Java.UnaryExpressionOther . Java.UnaryExpressionNotPlusMinusPostfix

javaPrimaryToJavaExpression :: Java.Primary -> Java.Expression
javaPrimaryToJavaExpression = javaPostfixExpressionToJavaExpression . Java.PostfixExpressionPrimary

javaPrimaryToJavaUnaryExpression :: Java.Primary -> Java.UnaryExpression
javaPrimaryToJavaUnaryExpression = Java.UnaryExpressionOther .
  Java.UnaryExpressionNotPlusMinusPostfix .
  Java.PostfixExpressionPrimary

javaPrimitiveTypeToJavaType :: Java.PrimitiveType -> Java.Type
javaPrimitiveTypeToJavaType pt = Java.TypePrimitive $ Java.PrimitiveTypeWithAnnotations pt []

javaRefType :: [Java.ReferenceType] -> Maybe Java.PackageName -> String -> Java.Type
javaRefType args pkg id = Java.TypeReference $ Java.ReferenceTypeClassOrInterface $ Java.ClassOrInterfaceTypeClass $
  javaClassType args pkg id

javaRelationalExpressionToJavaEqualityExpression :: Java.RelationalExpression -> Java.EqualityExpression
javaRelationalExpressionToJavaEqualityExpression = Java.EqualityExpressionUnary

javaRelationalExpressionToJavaExpression :: Java.RelationalExpression -> Java.Expression
javaRelationalExpressionToJavaExpression = javaEqualityExpressionToJavaExpression . javaRelationalExpressionToJavaEqualityExpression

javaRelationalExpressionToJavaUnaryExpression :: Java.RelationalExpression -> Java.UnaryExpression
javaRelationalExpressionToJavaUnaryExpression = javaPrimaryToJavaUnaryExpression .
  Java.PrimaryNoNewArray .
  Java.PrimaryNoNewArrayParens .
  javaRelationalExpressionToJavaExpression

javaReturnStatement :: Y.Maybe Java.Expression -> Java.Statement
javaReturnStatement = Java.StatementWithoutTrailing . Java.StatementWithoutTrailingSubstatementReturn .
  Java.ReturnStatement

javaStatementsToBlock :: [Java.Statement] -> Java.Block
javaStatementsToBlock stmts = Java.Block (Java.BlockStatementStatement <$> stmts)

javaString :: String -> Java.Literal
javaString = Java.LiteralString . Java.StringLiteral

javaStringMultiplicativeExpression :: String -> Java.MultiplicativeExpression
javaStringMultiplicativeExpression = javaLiteralToJavaMultiplicativeExpression . javaString

javaThrowIllegalArgumentException :: [Java.Expression] -> Java.Statement
javaThrowIllegalArgumentException args = javaThrowStatement $
  javaConstructorCall (javaConstructorName (Java.Identifier "IllegalArgumentException") Nothing) args Nothing

javaThrowIllegalStateException :: [Java.Expression] -> Java.Statement
javaThrowIllegalStateException args = javaThrowStatement $
  javaConstructorCall (javaConstructorName (Java.Identifier "IllegalStateException") Nothing) args Nothing

javaThrowStatement :: Java.Expression -> Java.Statement
javaThrowStatement = Java.StatementWithoutTrailing .
  Java.StatementWithoutTrailingSubstatementThrow . Java.ThrowStatement

javaThis :: Java.Expression
javaThis = javaPrimaryToJavaExpression $ Java.PrimaryNoNewArray Java.PrimaryNoNewArrayThis

javaTypeFromTypeName :: Aliases -> Name -> Java.Type
javaTypeFromTypeName aliases elName = javaTypeVariableToType $ Java.TypeVariable [] $
  nameToJavaTypeIdentifier aliases False elName

javaTypeIdentifier :: String -> Java.TypeIdentifier
javaTypeIdentifier = Java.TypeIdentifier . Java.Identifier

javaTypeIdentifierToJavaTypeArgument :: Java.TypeIdentifier -> Java.TypeArgument
javaTypeIdentifierToJavaTypeArgument id = Java.TypeArgumentReference $ Java.ReferenceTypeVariable $ Java.TypeVariable [] id

javaTypeName :: Java.Identifier -> Java.TypeName
javaTypeName id = Java.TypeName (Java.TypeIdentifier id) Nothing

javaTypeParameter :: String -> Java.TypeParameter
javaTypeParameter v = Java.TypeParameter [] (javaTypeIdentifier v) Nothing

javaTypeToJavaFormalParameter :: Java.Type -> Name -> Java.FormalParameter
javaTypeToJavaFormalParameter jt fname = Java.FormalParameterSimple $ Java.FormalParameter_Simple [] argType argId
  where
    argType = Java.UnannType jt
    argId = fieldNameToJavaVariableDeclaratorId fname

javaTypeToJavaReferenceType :: Java.Type -> Flow (Graph) Java.ReferenceType
javaTypeToJavaReferenceType t = case t of
  Java.TypeReference rt -> pure rt
  _ -> fail $ "expected a Java reference type. Found: " ++ show t

javaTypeToJavaResult :: Java.Type -> Java.Result
javaTypeToJavaResult = Java.ResultType . Java.UnannType

javaTypeToJavaTypeArgument :: Java.Type -> Java.TypeArgument
javaTypeToJavaTypeArgument t = case t of
  Java.TypeReference rt -> Java.TypeArgumentReference rt
  _ -> Java.TypeArgumentWildcard $ Java.Wildcard [] Nothing -- TODO

javaTypeVariable :: String -> Java.ReferenceType
javaTypeVariable v = Java.ReferenceTypeVariable $ Java.TypeVariable [] $ javaTypeIdentifier $ capitalize v

javaTypeVariableToType :: Java.TypeVariable -> Java.Type
javaTypeVariableToType = Java.TypeReference . Java.ReferenceTypeVariable

javaUnaryExpressionToJavaExpression :: Java.UnaryExpression -> Java.Expression
javaUnaryExpressionToJavaExpression = javaRelationalExpressionToJavaExpression .
  javaUnaryExpressionToJavaRelationalExpression

javaUnaryExpressionToJavaRelationalExpression :: Java.UnaryExpression -> Java.RelationalExpression
javaUnaryExpressionToJavaRelationalExpression = javaMultiplicativeExpressionToJavaRelationalExpression .
  Java.MultiplicativeExpressionUnary

javaVariableDeclarator :: Java.Identifier -> Y.Maybe Java.VariableInitializer -> Java.VariableDeclarator
javaVariableDeclarator id = Java.VariableDeclarator (javaVariableDeclaratorId id)

javaVariableDeclaratorId :: Java.Identifier -> Java.VariableDeclaratorId
javaVariableDeclaratorId id = Java.VariableDeclaratorId id Nothing

javaVariableName :: Name -> Java.Identifier
javaVariableName = javaIdentifier . localNameOfEager

makeConstructor :: Aliases -> Name -> Bool -> [Java.FormalParameter]
  -> [Java.BlockStatement] -> Java.ClassBodyDeclaration
makeConstructor aliases elName private params stmts = Java.ClassBodyDeclarationConstructorDeclaration $
    Java.ConstructorDeclaration mods cons Nothing body
  where
    nm = Java.SimpleTypeName $ nameToJavaTypeIdentifier aliases False elName
    cons = Java.ConstructorDeclarator [] nm Nothing params
    mods = [if private then Java.ConstructorModifierPrivate else Java.ConstructorModifierPublic]
    body = Java.ConstructorBody Nothing stmts

methodDeclaration :: [Java.MethodModifier] -> [Java.TypeParameter] -> [Java.Annotation] -> String
  -> [Java.FormalParameter] -> Java.Result -> Maybe [Java.BlockStatement] -> Java.ClassBodyDeclaration
methodDeclaration mods tparams anns methodName params result stmts =
    javaMethodDeclarationToJavaClassBodyDeclaration $
    Java.MethodDeclaration anns mods header body
  where
    header = javaMethodHeader tparams methodName params result
    body = javaMethodBody stmts

methodInvocation :: Y.Maybe (Either Java.ExpressionName Java.Primary) -> Java.Identifier -> [Java.Expression] -> Java.MethodInvocation
methodInvocation lhs methodName = Java.MethodInvocation header
  where
    header = case lhs of
      Nothing -> Java.MethodInvocation_HeaderSimple $ Java.MethodName methodName
      Just either -> Java.MethodInvocation_HeaderComplex $ Java.MethodInvocation_Complex variant targs methodName
        where
          targs = []
          variant = case either of
            Left name -> Java.MethodInvocation_VariantExpression name
            Right prim -> Java.MethodInvocation_VariantPrimary prim

methodInvocationStatic :: Java.Identifier -> Java.Identifier -> [Java.Expression] -> Java.MethodInvocation
methodInvocationStatic self methodName = methodInvocation (Just $ Left name) methodName
  where
    name = Java.ExpressionName Nothing self

nameToJavaClassType :: Aliases -> Bool -> [Java.TypeArgument] -> Name -> Maybe String -> Java.ClassType
nameToJavaClassType aliases qualify args name mlocal = Java.ClassType [] pkg id args
  where
    (id, pkg) = nameToQualifiedJavaName aliases qualify name mlocal

nameToQualifiedJavaName :: Aliases -> Bool -> Name -> Maybe String -> (Java.TypeIdentifier, Java.ClassTypeQualifier)
nameToQualifiedJavaName aliases qualify name mlocal = (jid, pkg)
  where
    QualifiedName ns local = qualifyNameEager name
    alias = case ns of
      Nothing -> Nothing
      Just n -> case M.lookup n (aliasesPackages aliases) of
          Nothing -> Just $ javaPackageName $ Strings.splitOn "/" $ unNamespace n
          Just id -> Just id
    pkg = if qualify
      then Y.maybe none Java.ClassTypeQualifierPackage alias
      else none
    none = Java.ClassTypeQualifierNone
    jid = javaTypeIdentifier $ case mlocal of
      Nothing -> sanitizeJavaName local
      Just l -> sanitizeJavaName local ++ "." ++ sanitizeJavaName l

nameToJavaName :: Aliases -> Name -> Java.Identifier
nameToJavaName aliases name = Java.Identifier $ if isEscaped (unName name)
    then sanitizeJavaName local
    else case ns of
      Nothing -> local
      Just gname -> case M.lookup gname (aliasesPackages aliases) of
        Nothing -> fromParts $ Strings.splitOn "/" $ unNamespace gname
        Just (Java.PackageName parts) -> fromParts (Java.unIdentifier <$> parts)
  where
    QualifiedName ns local = qualifyNameEager name
    fromParts parts = L.intercalate "." $ parts ++ [sanitizeJavaName local]

nameToJavaReferenceType :: Aliases -> Bool -> [Java.TypeArgument] -> Name -> Maybe String -> Java.ReferenceType
nameToJavaReferenceType aliases qualify args name mlocal = Java.ReferenceTypeClassOrInterface $ Java.ClassOrInterfaceTypeClass $
  nameToJavaClassType aliases qualify args name mlocal

nameToJavaTypeIdentifier :: Aliases -> Bool -> Name -> Java.TypeIdentifier
nameToJavaTypeIdentifier aliases qualify name = fst $ nameToQualifiedJavaName aliases qualify name Nothing

overrideAnnotation :: Java.Annotation
overrideAnnotation = Java.AnnotationMarker $ Java.MarkerAnnotation $ javaTypeName $ Java.Identifier "Override"

referenceTypeToResult :: Java.ReferenceType -> Java.Result
referenceTypeToResult = javaTypeToJavaResult . Java.TypeReference

sanitizeJavaName :: String -> String
sanitizeJavaName name = if isEscaped name
  -- The '$' prefix allows names to be excluded from sanitization
  then unescape name
  else sanitizeWithUnderscores reservedWords name

toAcceptMethod :: Bool -> [Java.TypeParameter] -> Java.ClassBodyDeclaration
toAcceptMethod abstract vtparams = methodDeclaration mods tparams anns acceptMethodName [param] result body
  where
    mods = [Java.MethodModifierPublic] ++ if abstract then [Java.MethodModifierAbstract] else []
    tparams = [javaTypeParameter visitorReturnParameter]
    anns = if abstract
      then []
      else [overrideAnnotation]
    param = javaTypeToJavaFormalParameter ref (Name varName)
      where
        ref = javaClassTypeToJavaType $
          Java.ClassType
            []
            Java.ClassTypeQualifierNone
            (javaTypeIdentifier visitorName)
            (typeArgs ++ [Java.TypeArgumentReference visitorTypeVariable])
    typeArgs = (Java.TypeArgumentReference . typeParameterToReferenceType) <$> vtparams
    result = javaTypeToJavaResult $ Java.TypeReference visitorTypeVariable
    varName = "visitor"
    body = if abstract
      then Nothing
      else Just [Java.BlockStatementStatement $ javaReturnStatement $ Just returnExpr]
    returnExpr = javaMethodInvocationToJavaExpression $
        methodInvocationStatic (Java.Identifier varName) (Java.Identifier visitMethodName) [javaThis]

toAssignStmt :: Name -> Java.Statement
toAssignStmt fname = javaAssignmentStatement lhs rhs
  where
    lhs = Java.LeftHandSideFieldAccess $ thisField id
      where
        id = fieldNameToJavaIdentifier fname
    rhs = fieldNameToJavaExpression fname
    thisField = Java.FieldAccess $ Java.FieldAccess_QualifierPrimary $ Java.PrimaryNoNewArray Java.PrimaryNoNewArrayThis

toJavaArrayType :: Java.Type -> Flow (Graph) Java.Type
toJavaArrayType t = Java.TypeReference . Java.ReferenceTypeArray <$> case t of
  Java.TypeReference rt -> case rt of
    Java.ReferenceTypeClassOrInterface cit -> pure $
      Java.ArrayType (Java.Dims [[]]) $ Java.ArrayType_VariantClassOrInterface cit
    Java.ReferenceTypeArray (Java.ArrayType (Java.Dims d) v) -> pure $
      Java.ArrayType (Java.Dims $ d ++ [[]]) v
    _ -> fail $ "don't know how to make Java reference type into array type: " ++ show rt
  _ -> fail $ "don't know how to make Java type into array type: " ++ show t

typeParameterToTypeArgument :: Java.TypeParameter -> Java.TypeArgument
typeParameterToTypeArgument (Java.TypeParameter _ id _) = javaTypeIdentifierToJavaTypeArgument id

typeParameterToReferenceType :: Java.TypeParameter -> Java.ReferenceType
typeParameterToReferenceType = javaTypeVariable . unTypeParameter

unTypeParameter :: Java.TypeParameter -> String
unTypeParameter (Java.TypeParameter [] (Java.TypeIdentifier (Java.Identifier v)) Nothing) = v

unescape :: String -> String
unescape = L.tail

variableDeclarationStatement :: Aliases -> Java.Type -> Java.Identifier -> Java.Expression -> Java.BlockStatement
variableDeclarationStatement aliases jtype id rhs = Java.BlockStatementLocalVariableDeclaration $
    Java.LocalVariableDeclarationStatement $ Java.LocalVariableDeclaration [] (Java.LocalVariableTypeType $ Java.UnannType jtype) [vdec]
  where
    vdec = javaVariableDeclarator id (Just init)
      where
        init = Java.VariableInitializerExpression rhs

variableToJavaIdentifier :: Name -> Java.Identifier
variableToJavaIdentifier (Name var) = Java.Identifier $ if var == ignoredVariable
  then "ignored"
  else var -- TODO: escape

variantClassName :: Bool -> Name -> Name -> Name
variantClassName qualify elName (Name fname) = unqualifyName (QualifiedName ns local1)
  where
    QualifiedName ns local = qualifyNameEager elName
    flocal = capitalize fname
    local1 = if qualify
      then local ++ "." ++ flocal
      else if flocal == local then flocal ++ "_" else flocal

visitorTypeVariable :: Java.ReferenceType
visitorTypeVariable = javaTypeVariable "r"