/*
* #%~
* VDM Code Generator
* %%
* Copyright (C) 2008 - 2014 Overture
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #~%
*/
package org.overture.codegen.vdm2java;
import java.util.LinkedList;
import java.util.List;
import org.apache.log4j.Logger;
import org.overture.codegen.ir.SExpIR;
import org.overture.codegen.ir.SStmIR;
import org.overture.codegen.ir.STypeIR;
import org.overture.codegen.ir.analysis.AnalysisException;
import org.overture.codegen.ir.declarations.ADefaultClassDeclIR;
import org.overture.codegen.ir.declarations.AFieldDeclIR;
import org.overture.codegen.ir.declarations.AFormalParamLocalParamIR;
import org.overture.codegen.ir.declarations.AMethodDeclIR;
import org.overture.codegen.ir.declarations.ARecordDeclIR;
import org.overture.codegen.ir.expressions.AExternalExpIR;
import org.overture.codegen.ir.expressions.AIdentifierVarExpIR;
import org.overture.codegen.ir.expressions.ANewExpIR;
import org.overture.codegen.ir.expressions.ANotUnaryExpIR;
import org.overture.codegen.ir.expressions.ASeqConcatBinaryExpIR;
import org.overture.codegen.ir.expressions.AStringLiteralExpIR;
import org.overture.codegen.ir.expressions.ATernaryIfExpIR;
import org.overture.codegen.ir.name.ATypeNameIR;
import org.overture.codegen.ir.patterns.AIdentifierPatternIR;
import org.overture.codegen.ir.statements.AAssignToExpStmIR;
import org.overture.codegen.ir.statements.ABlockStmIR;
import org.overture.codegen.ir.statements.AIfStmIR;
import org.overture.codegen.ir.statements.AReturnStmIR;
import org.overture.codegen.ir.types.ABoolBasicTypeIR;
import org.overture.codegen.ir.types.AMethodTypeIR;
import org.overture.codegen.ir.types.ARecordTypeIR;
public class JavaRecordCreator extends JavaClassCreatorBase
{
private JavaFormat javaFormat;
private Logger log = Logger.getLogger(this.getClass().getName());
public JavaRecordCreator(JavaFormat javaFormat)
{
this.javaFormat = javaFormat;
}
public AMethodDeclIR genRecConstructor(ARecordDeclIR record)
throws AnalysisException
{
// Since Java does not have records but the OO AST does a record is generated as a Java class.
// To make sure that the record can be instantiated we must explicitly add a constructor.
AMethodDeclIR constructor = consDefaultCtorSignature(record.getName());
AMethodTypeIR methodType = new AMethodTypeIR();
methodType.setResult(consRecordType(record));
ABlockStmIR body = new ABlockStmIR();
constructor.setBody(body);
LinkedList<AFormalParamLocalParamIR> formalParams = constructor.getFormalParams();
LinkedList<SStmIR> bodyStms = body.getStatements();
LinkedList<AFieldDeclIR> fields = record.getFields();
for (AFieldDeclIR field : fields)
{
String name = field.getName();
STypeIR type = field.getType();
String paramName = "_" + name;
AIdentifierPatternIR idPattern = new AIdentifierPatternIR();
idPattern.setName(paramName);
methodType.getParams().add(type.clone());
// Construct formal parameter of the constructor
AFormalParamLocalParamIR formalParam = new AFormalParamLocalParamIR();
formalParam.setPattern(idPattern);
formalParam.setType(type.clone());
formalParams.add(formalParam);
// Construct the initialization of the record field using the
// corresponding formal parameter.
AAssignToExpStmIR assignment = new AAssignToExpStmIR();
AIdentifierVarExpIR id = new AIdentifierVarExpIR();
id.setName(name);
id.setType(type.clone());
id.setIsLambda(false);
id.setIsLocal(true);
AIdentifierVarExpIR varExp = new AIdentifierVarExpIR();
varExp.setType(type.clone());
varExp.setName(paramName);
varExp.setIsLocal(true);
assignment.setTarget(id);
if (!javaFormat.getIrInfo().getAssistantManager().getTypeAssistant().isBasicType(varExp.getType()))
{
// Example: b = (_b != null) ? _b.clone() : null;
ATernaryIfExpIR checkedAssignment = new ATernaryIfExpIR();
checkedAssignment.setType(new ABoolBasicTypeIR());
checkedAssignment.setCondition(javaFormat.getJavaFormatAssistant().consParamNotNullComp(varExp));
checkedAssignment.setTrueValue(varExp);
checkedAssignment.setFalseValue(javaFormat.getIrInfo().getExpAssistant().consNullExp());
assignment.setExp(checkedAssignment);
} else
{
assignment.setExp(varExp);
}
bodyStms.add(assignment);
}
constructor.setMethodType(methodType);
return constructor;
}
public AMethodDeclIR genCopyMethod(ARecordDeclIR record)
throws AnalysisException
{
ADefaultClassDeclIR defClass = record.getAncestor(ADefaultClassDeclIR.class);
ATypeNameIR typeName = new ATypeNameIR();
typeName.setDefiningClass(defClass.getName());
typeName.setName(record.getName());
ARecordTypeIR returnType = new ARecordTypeIR();
returnType.setName(typeName);
AMethodTypeIR methodType = new AMethodTypeIR();
methodType.setResult(returnType);
AMethodDeclIR method = consCopySignature(methodType);
ANewExpIR newExp = new ANewExpIR();
newExp.setType(returnType.clone());
newExp.setName(typeName.clone());
List<SExpIR> args = newExp.getArgs();
List<AFieldDeclIR> fields = record.getFields();
for (AFieldDeclIR field : fields)
{
String name = field.getName();
AIdentifierVarExpIR varExp = new AIdentifierVarExpIR();
varExp.setName(name);
varExp.setType(field.getType().clone());
varExp.setIsLocal(false);
args.add(varExp);
}
AReturnStmIR body = new AReturnStmIR();
body.setExp(newExp);
method.setBody(body);
return method;
}
public AMethodDeclIR genEqualsMethod(ARecordDeclIR record)
throws AnalysisException
{
String paramName = "obj";
// Construct equals method to be used for comparing records using
// "structural" equivalence
AMethodDeclIR equalsMethod = consEqualMethodSignature(paramName);
ABlockStmIR equalsMethodBody = new ABlockStmIR();
LinkedList<SStmIR> equalsStms = equalsMethodBody.getStatements();
AReturnStmIR returnTypeComp = new AReturnStmIR();
if (record.getFields().isEmpty())
{
// If the record has no fields equality is simply:
// return obj instanceof RecordType
returnTypeComp.setExp(javaFormat.getJavaFormatAssistant().consInstanceOf(record, paramName));
equalsStms.add(returnTypeComp);
} else
{
// Construct the initial check:
// if ((!obj instanceof RecordType))
// return false;
AIfStmIR ifStm = new AIfStmIR();
ANotUnaryExpIR negated = new ANotUnaryExpIR();
negated.setType(new ABoolBasicTypeIR());
negated.setExp(javaFormat.getJavaFormatAssistant().consInstanceOf(record, paramName));
ifStm.setIfExp(negated);
returnTypeComp.setExp(javaFormat.getIrInfo().getAssistantManager().getExpAssistant().consBoolLiteral(false));
ifStm.setThenStm(returnTypeComp);
// If the inital check is passed we can safely cast the formal parameter
// To the record type: RecordType other = ((RecordType) obj);
String localVarName = "other";
ABlockStmIR formalParamCasted = javaFormat.getJavaFormatAssistant().consVarFromCastedExp(record, paramName, localVarName);
// Next compare the fields of the instance with the fields of the formal parameter "obj":
// return (field1 == obj.field1) && (field2 == other.field2)...
LinkedList<AFieldDeclIR> fields = record.getFields();
SExpIR previousComparisons = javaFormat.getJavaFormatAssistant().consFieldComparison(record, fields.get(0), localVarName);
for (int i = 1; i < fields.size(); i++)
{
previousComparisons = javaFormat.getJavaFormatAssistant().extendAndExp(record, fields.get(i), previousComparisons, localVarName);
}
AReturnStmIR fieldsComparison = new AReturnStmIR();
fieldsComparison.setExp(previousComparisons);
equalsStms.add(ifStm);
equalsStms.add(formalParamCasted);
equalsStms.add(fieldsComparison);
}
equalsMethod.setBody(equalsMethodBody);
return equalsMethod;
}
public AMethodDeclIR genHashcodeMethod(ARecordDeclIR record)
throws AnalysisException
{
AMethodDeclIR hashcodeMethod = consHashcodeMethodSignature();
AReturnStmIR returnStm = new AReturnStmIR();
if (record.getFields().isEmpty())
{
AExternalExpIR zero = new AExternalExpIR();
zero.setType(hashcodeMethod.getMethodType().getResult().clone());
zero.setTargetLangExp("0");
returnStm.setExp(zero);
} else
{
returnStm.setExp(javaFormat.getJavaFormatAssistant().consUtilCallUsingRecFields(record, hashcodeMethod.getMethodType().getResult(), hashcodeMethod.getName()));
}
hashcodeMethod.setBody(returnStm);
return hashcodeMethod;
}
public AMethodDeclIR genToStringMethod(ARecordDeclIR record)
throws AnalysisException
{
AMethodDeclIR toStringMethod = consToStringSignature();
AReturnStmIR returnStm = new AReturnStmIR();
ADefaultClassDeclIR enclosingClass = record.getAncestor(ADefaultClassDeclIR.class);
String className = "";
if (enclosingClass != null)
{
className = enclosingClass.getName();
} else
{
log.error("Could not find enclosing class for record: "
+ record.getName());
}
String recToStrPrefix = String.format("mk_%s%s", className
+ "`", record.getName());
AStringLiteralExpIR emptyRecStr = new AStringLiteralExpIR();
emptyRecStr.setIsNull(false);
STypeIR strType = toStringMethod.getMethodType().getResult();
emptyRecStr.setType(strType.clone());
if (record.getFields().isEmpty())
{
emptyRecStr.setValue(recToStrPrefix + "()");
returnStm.setExp(emptyRecStr);
} else
{
ASeqConcatBinaryExpIR stringBuffer = new ASeqConcatBinaryExpIR();
stringBuffer.setType(strType.clone());
stringBuffer.setLeft(javaFormat.getIrInfo().getExpAssistant().consStringLiteral(recToStrPrefix, false));
stringBuffer.setRight(javaFormat.getJavaFormatAssistant().consUtilCallUsingRecFields(record, strType, "formatFields"));
returnStm.setExp(stringBuffer);
}
toStringMethod.setBody(returnStm);
return toStringMethod;
}
public ARecordTypeIR consRecordType(ARecordDeclIR record)
{
ADefaultClassDeclIR defClass = record.getAncestor(ADefaultClassDeclIR.class);
ATypeNameIR typeName = new ATypeNameIR();
typeName.setDefiningClass(defClass.getName());
typeName.setName(record.getName());
ARecordTypeIR returnType = new ARecordTypeIR();
returnType.setName(typeName);
return returnType;
}
}