/*
* #%~
* The VDM Type Checker
* %%
* 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.typechecker.utilities.type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import org.overture.ast.analysis.AnalysisException;
import org.overture.ast.factory.AstFactory;
import org.overture.ast.lex.LexNameToken;
import org.overture.ast.lex.LexQuoteToken;
import org.overture.ast.types.AFieldField;
import org.overture.ast.types.ANamedInvariantType;
import org.overture.ast.types.ARecordInvariantType;
import org.overture.ast.types.AUnionType;
import org.overture.ast.types.AUnknownType;
import org.overture.ast.types.PType;
import org.overture.ast.types.SInvariantType;
import org.overture.ast.util.PTypeSet;
import org.overture.typechecker.assistant.ITypeCheckerAssistantFactory;
/**
* Used to get a Record type from a type
*
* @author kel
*/
public class RecordTypeFinder extends TypeUnwrapper<ARecordInvariantType>
{
protected ITypeCheckerAssistantFactory af;
public RecordTypeFinder(ITypeCheckerAssistantFactory af)
{
this.af = af;
}
@Override
public ARecordInvariantType defaultSInvariantType(SInvariantType type)
throws AnalysisException
{
if (type instanceof ANamedInvariantType)
{
return ((ANamedInvariantType) type).getType().apply(THIS);
} else if (type instanceof ARecordInvariantType)
{
return (ARecordInvariantType) type;
} else
{
return null;
}
}
@Override
public ARecordInvariantType caseAUnionType(AUnionType type)
throws AnalysisException
{
if (!type.getRecDone())
{
type.setRecDone(true); // Mark early to avoid recursion.
af.createPTypeAssistant();
// type.setRecType(PTypeAssistantTC.getRecord(AstFactory.newAUnknownType(type.getLocation())));
type.setRecType(af.createPTypeAssistant().getRecord(AstFactory.newAUnknownType(type.getLocation())));
// Build a record type with the common fields of the contained
// record types, making the field types the union of the original
// fields' types...
Map<String, Vector<PType>> common = new HashMap<String, Vector<PType>>();
int recordCount = 0;
for (PType t : type.getTypes())
{
af.createPTypeAssistant();
if (af.createPTypeAssistant().isRecord(t))
{
recordCount++;
for (AFieldField f : t.apply(THIS).getFields())
{
List<PType> current = common.get(f.getTag());
if (current == null)
{
Vector<PType> list = new Vector<PType>();
list.add(f.getType());
common.put(f.getTag(), list);
}
else
{
current.add(f.getType());
}
}
}
}
// If all fields were present in all records, the TypeLists will be the
// same size. But if not, the shorter ones have to have UnknownTypes added,
// because some of the records do not have that field.
Map<String, PTypeSet> typesets = new HashMap<String, PTypeSet>();
for (String field: common.keySet())
{
List<PType> list = common.get(field);
if (list.size() != recordCount)
{
// Both unknown and undefined types do not trigger isSubType, so we use
// an illegal quote type, <?>.
list.add(AstFactory.newAQuoteType(new LexQuoteToken("?", type.getLocation())));
}
PTypeSet set = new PTypeSet(af);
set.addAll(list);
typesets.put(field, set);
}
List<AFieldField> fields = new Vector<AFieldField>();
for (String tag : typesets.keySet())
{
LexNameToken tagname = new LexNameToken("?", tag, type.getLocation());
fields.add(AstFactory.newAFieldField(tagname, tag, typesets.get(tag).getType(type.getLocation()), false));
}
type.setRecType(fields.isEmpty() ? null
: AstFactory.newARecordInvariantType(type.getLocation(), fields));
}
return type.getRecType();
}
@Override
public ARecordInvariantType caseAUnknownType(AUnknownType type)
throws AnalysisException
{
return AstFactory.newARecordInvariantType(type.getLocation(), new Vector<AFieldField>());
}
@Override
public ARecordInvariantType defaultPType(PType type)
throws AnalysisException
{
assert false : "Can't getRecord of a non-record";
return null;
}
}