All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mysema.scalagen.Constructors.scala Maven / Gradle / Ivy

There is a newer version: 1.0.2
Show newest version
/*
 * Copyright (C) 2011, Mysema Ltd
 *
 * 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 com.mysema.scalagen

import japa.parser.ast.body.ModifierSet
import java.util.ArrayList
import UnitTransformer._

object Constructors extends Constructors

/**
 * Constructors reorders and normalizes constructors
 */
class Constructors extends UnitTransformerBase {

  def transform(cu: CompilationUnit): CompilationUnit = {
    cu.accept(this, cu).asInstanceOf[CompilationUnit]
  }

  override def visit(n: ClassOrInterfaceDecl, cu: CompilationUnit):  ClassOrInterfaceDecl = {
    val t = super.visit(n, cu).asInstanceOf[ClassOrInterfaceDecl]
    // make members list mutable
    t.setMembers(new ArrayList[BodyDecl](t.getMembers))

    // get all constructors
    val constr = t.getMembers.collect { case c: Constructor => c }

    if (constr.isEmpty) {
      return t
    }

    // get first without delegating
    val first = constr.find( c =>
      c.getBlock.isEmpty || !isThisConstructor(c.getBlock()(0)))

    // move in front of others
    first.filter(_ != constr(0)).foreach { c =>
      t.getMembers.remove(c)
      t.getMembers.add(t.getMembers.indexOf(constr(0)), c)
    }

    // copy initializer, if constructor block has non-constructor statements
    val c = first.getOrElse(constr(0))

    // add empty constructor invocation for all other constructors without
    // constructor invocations
    constr.filter(_ != c).foreach { c =>
      if (c.getBlock.isEmpty) {// || !c.getBlock()(0).isInstanceOf[ConstructorInvocation]) {
        c.getBlock.add(new ConstructorInvocation(true, null, null))
      }
    }

    if (!c.getBlock.isEmpty &&
        !c.getBlock.getStmts.filter(!_.isInstanceOf[ConstructorInvocation]).isEmpty) {

      processStatements(cu, t, c)

      if (!c.getBlock.isEmpty &&
          !(c.getBlock.size == 1 && c.getBlock()(0).isInstanceOf[ConstructorInvocation] &&
          !c.getBlock()(0).asInstanceOf[ConstructorInvocation].isThis())) {
        val initializer = new Initializer(false, c.getBlock)
        t.getMembers.add(t.getMembers.indexOf(c), initializer)
      }

    }

    // add missing delegations
    t.getMembers.collect { case c: Constructor => c }.filter(_ != c)
      .foreach { c =>
        if (!c.getBlock.isEmpty && !c.getBlock()(0).isInstanceOf[ConstructorInvocation]) {
          //c.getBlock.getStmts.add(0, new ConstructorInvocation(true, null, null))
          c.getBlock.setStmts(new ConstructorInvocation(true, null, null) :: c.getBlock.getStmts)
        }
      }
    t
  }

  private def processStatements(cu: CompilationUnit, t: TypeDecl, c: Constructor) {
    val fields = t.getMembers.collect { case f: Field => f }
    val variables = fields.flatMap(_.getVariables).map(v => (v.getId.getName, v)).toMap
    val variableToField = fields.flatMap(f => f.getVariables.map(v => (v.getId.getName,f)) ).toMap

    var replacements = Map[String, String]()
    
    // go through statements and map assignments to variable initializers
    c.getBlock.getStmts.collect { case s: ExpressionStmt => s }
      .filter(isAssignment(_))
      .foreach { s =>
      val assign = s.getExpression.asInstanceOf[Assign]
      if (assign.getTarget.isInstanceOf[FieldAccess]) {
        val fieldAccess = assign.getTarget.asInstanceOf[FieldAccess]
        processFieldAssign(s, assign, fieldAccess, c, variables, variableToField)
      } else if (assign.getTarget.isInstanceOf[Name]) {
        val namedTarget = assign.getTarget.asInstanceOf[Name]
        if (variables.contains(namedTarget.getName)) {
          if (assign.getValue.isInstanceOf[Name]) { // field = parameter
            val namedValue = assign.getValue.asInstanceOf[Name]
            c.getParameters.find(_.getId.getName == namedValue.getName).foreach { param =>
              val field = variableToField(namedTarget.getName)
              // rename parameter to field name
              param.setId(namedTarget.getName)
              replacements = replacements.+((param.getId.getName, namedTarget.getName))
              copyAnnotationsAndModifiers(field, param)
              // remove field
              field.setVariables(field.getVariables.filterNot(_ == variables(namedTarget.getName)))
            }
          } else { // field = ?!?
            variables(namedTarget.getName).setInit(assign.getValue)
          }
          c.getBlock.remove(s)
        }
      }
    }

    // remove empty field declarations
    fields.filter(_.getVariables.isEmpty).foreach { t.getMembers.remove(_) }
    
    // modify variables in other statements
    val renameTransformer = new RenameTransformer(replacements)
    c.getBlock.setStmts(c.getBlock.getStmts.map(stmt => {
      if (!stmt.isInstanceOf[ExpressionStmt]) {
        stmt.accept(renameTransformer, cu).asInstanceOf[Statement]
      } else {
        stmt
      }
    }))

  }

  private def processFieldAssign(s: ExpressionStmt, assign: Assign, fieldAccess: FieldAccess,
      c: Constructor, variables: Map[String, Variable], variableToField: Map[String, Field] ) {
    if (fieldAccess.getScope.isInstanceOf[This] &&
        variables.contains(fieldAccess.getField)) {
      if (fieldAccess.getField == assign.getValue.toString) {
        val field = variableToField(fieldAccess.getField)
        c.getParameters.find(_.getId.getName == fieldAccess.getField)
          .foreach(copyAnnotationsAndModifiers(field,_))
        // remove field, as constructor parameter can be used
        //field.getVariables.remove(variables(fieldAccess.getField))
        field.setVariables(field.getVariables.filterNot(_ == variables(fieldAccess.getField)))

      } else {
        // remove statement, put init to field
        variables(fieldAccess.getField).setInit(assign.getValue)
      }
      c.getBlock.remove(s)
    }
  }

  private def copyAnnotationsAndModifiers(f: Field, p: Parameter) {
    if (f.getAnnotations != null) {
      p.setAnnotations(p.getAnnotations.union(f.getAnnotations))
    }

    val modifiers = f.getModifiers.addModifier(PROPERTY)
    p.setModifiers(modifiers)
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy