/* * #%~ * 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.definitions.APublicAccess; import org.overture.ast.definitions.PDefinition; import org.overture.ast.factory.AstFactory; import org.overture.ast.intf.lex.ILexNameToken; import org.overture.ast.lex.LexLocation; import org.overture.ast.lex.LexNameList; import org.overture.ast.lex.LexNameToken; import org.overture.ast.typechecker.NameScope; import org.overture.ast.types.AAccessSpecifierAccessSpecifier; import org.overture.ast.types.AClassType; import org.overture.ast.types.AFunctionType; import org.overture.ast.types.ANamedInvariantType; import org.overture.ast.types.AOperationType; 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.Environment; import org.overture.typechecker.assistant.ITypeCheckerAssistantFactory; import org.overture.typechecker.assistant.type.PTypeAssistantTC; import org.overture.typechecker.util.LexNameTokenMap; /** * Used to get a class type from a type * * @author kel */ public class ClassTypeFinder extends TypeUnwrapper<AClassType> { public static final String UNION_CLASS_PREFIX = "*union"; protected ITypeCheckerAssistantFactory af; protected Environment env; public ClassTypeFinder(ITypeCheckerAssistantFactory af, Environment env) { this.af = af; this.env = env; } @Override public AClassType caseAClassType(AClassType type) throws AnalysisException { return type; } @Override public AClassType defaultSInvariantType(SInvariantType type) throws AnalysisException { if (type instanceof ANamedInvariantType) { return ((ANamedInvariantType) type).getType().apply(THIS); } else { return null; } } @Override public AClassType caseAUnionType(AUnionType type) throws AnalysisException { if (!type.getClassDone()) { type.setClassDone(true); // Mark early to avoid recursion. // type.setClassType(PTypeAssistantTC.getClassType(AstFactory.newAUnknownType(type.getLocation()))); // Rewritten in non static way. type.setClassType(af.createPTypeAssistant().getClassType(AstFactory.newAUnknownType(type.getLocation()), env)); // Build a class type with the common fields of the contained // class types, making the field types the union of the original // fields' types... Map<ILexNameToken, PTypeSet> common = new HashMap<ILexNameToken, PTypeSet>(); Map<ILexNameToken, AAccessSpecifierAccessSpecifier> access = new LexNameTokenMap<AAccessSpecifierAccessSpecifier>(); // Derive the pseudoclass name for the combined union String classString = UNION_CLASS_PREFIX; // NB, illegal class name int count = 0; AClassType found = null; for (PType t: type.getTypes()) { if (af.createPTypeAssistant().isClass(t, env)) { found = t.apply(THIS); classString = classString + "_" + found.getName().getName(); // eg. "*union_A_B" count++; } } if (count == 1) // Only one class in union, so just return this one { type.setClassType(found); return found; } else if (count == 0) { type.setClassType(null); return null; } ILexNameToken classname = new LexNameToken("CLASS", classString, new LexLocation()); for (PType t : type.getTypes()) { if (af.createPTypeAssistant().isClass(t, env)) { AClassType ct = t.apply(THIS);// PTypeAssistantTC.getClassType(t); for (PDefinition f : af.createPDefinitionAssistant().getDefinitions(ct.getClassdef())) { if (env != null && !af.createSClassDefinitionAssistant().isAccessible(env, f, false)) { // Omit inaccessible definitions continue; } // TypeSet current = common.get(f.name); ILexNameToken synthname = f.getName().getModifiedName(classname.getName()); PTypeSet current = null; for (ILexNameToken n : common.keySet()) { if (n.getName().equals(synthname.getName())) { current = common.get(n); break; } } PType ftype = af.createPDefinitionAssistant().getType(f); if (current == null) { common.put(synthname, new PTypeSet(ftype, af)); } else { current.add(ftype); } AAccessSpecifierAccessSpecifier curracc = access.get(synthname); if (curracc == null) { AAccessSpecifierAccessSpecifier acc = f.getAccess().clone(); acc.setAccess(new APublicAccess()); // Guaranteed to be accessible access.put(synthname, acc); } else if (!curracc.getPure() && f.getAccess().getPure()) { AAccessSpecifierAccessSpecifier purified = AstFactory.newAAccessSpecifierAccessSpecifier( new APublicAccess(), f.getAccess().getStatic() != null, f.getAccess().getAsync() != null, curracc.getPure() || f.getAccess().getPure()); access.put(synthname, purified); } } } } List<PDefinition> newdefs = new Vector<PDefinition>(); PTypeAssistantTC assistant = af.createPTypeAssistant(); for (ILexNameToken synthname : common.keySet()) { PType ptype = common.get(synthname).getType(type.getLocation()); ILexNameToken newname = null; if (assistant.isOperation(ptype)) { AOperationType optype = assistant.getOperation(ptype); optype.setPure(access.get(synthname).getPure()); ptype = optype; newname = synthname.getModifiedName(optype.getParameters()); } else if (assistant.isFunction(ptype)) { AFunctionType ftype = assistant.getFunction(ptype); newname = synthname.getModifiedName(ftype.getParameters()); } PDefinition def = AstFactory.newALocalDefinition(synthname.getLocation(), (newname == null ? synthname : newname), NameScope.GLOBAL, ptype); def.setAccess(access.get(synthname).clone()); newdefs.add(def); } type.setClassType(classname == null ? null : AstFactory.newAClassType(type.getLocation(), AstFactory.newAClassClassDefinition(classname.clone(), new LexNameList(), newdefs))); } return type.getClassType(); } @Override public AClassType caseAUnknownType(AUnknownType type) throws AnalysisException { return AstFactory.newAClassType(type.getLocation(), AstFactory.newAClassClassDefinition()); } @Override public AClassType defaultPType(PType tyoe) throws AnalysisException { assert false : "Can't getClass of a non-class"; return null; } }