/** * Copyright (c) 2009-2011, The HATS Consortium. All rights reserved. * This file is licensed under the terms of the Modified BSD License. */ package org.absmodels.abs.plugin.internal; import static org.absmodels.abs.plugin.util.Constants.ABSFRONTEND_PLUGIN_ID; import java.io.File; import java.io.IOException; import java.util.Iterator; import org.absmodels.abs.plugin.Activator; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.SubProgressMonitor; import abs.common.WrongProgramArgumentException; import abs.frontend.analyser.ErrorMessage; import abs.frontend.analyser.SemanticError; import abs.frontend.analyser.SemanticConditionList; import abs.frontend.ast.ASTNode; import abs.frontend.ast.CompilationUnit; import abs.frontend.ast.List; import abs.frontend.ast.Model; import abs.frontend.ast.ProductDecl; import abs.frontend.delta.DeltaModellingException; import abs.frontend.delta.DeltaModellingWithNodeException; import abs.frontend.parser.Main; import abs.frontend.typechecker.TypeCheckerException; import abs.frontend.typechecker.locationtypes.LocationType; import abs.frontend.typechecker.locationtypes.infer.LocationTypeInferrerExtension; import abs.frontend.typechecker.locationtypes.infer.LocationTypeInferrerExtension.LocationTypingPrecision; public class IncrementalModelBuilder { private Model model = null; private LocationTypeInferrerExtension ltie; public LocationTypeInferrerExtension getLocationTypeInferrerExtension() { return ltie; } /** * This code was introduced for SQL. Iterating over the * transformed nodes will cause errors in Eclipse. #85 * TODO: No longer needed since SQL-sunset? */ public static void flushAll(ASTNode<?> node){ // for(int i=0; i<node.getNumChildNoTransform(); i++){ // flushAll(node.getChildNoTransform(i)); // } node.flushCache(); return; } public synchronized void addCompilationUnits(Iterable<CompilationUnit> units) throws IOException, NoModelException { // Initialize the model. if(model == null){ model = new Model(); model.addCompilationUnit(getStdLibCompilationUnit()); } for (CompilationUnit u : units) { addCompilationUnit(u); } Assert.isNotNull(model); Main.exceptionHack(model); } /** * Creates an empty model with only the stdlib when you pass null. */ public synchronized void addCompilationUnit(CompilationUnit cu) { if(model == null){ model = new Model(); model.addCompilationUnit(getStdLibCompilationUnit()); if (cu != null) // just give us the stdlib model.addCompilationUnit(cu); return; } if (cu == null) return; String filename = cu.getFileName(); assert filename != null; CompilationUnit cuold = null; try { cuold = getCompilationUnit(filename); } catch (NoModelException e) { // we're pretty sure there's a model. assert false; } List<CompilationUnit> culist = model.getCompilationUnitList(); int cindex = culist.getIndexOfChild(cuold); if(cindex>0){ model.setCompilationUnit(cu, cindex); } else { model.addCompilationUnit(cu); } // model.flushCache(); flushAll(model); } private CompilationUnit getStdLibCompilationUnit() { CompilationUnit stdLib; File bundle; try { stdLib = new Main().getStdLib(); bundle = FileLocator.getBundleFile(Platform.getBundle(ABSFRONTEND_PLUGIN_ID)); } catch (IOException e) { Activator.logException(e); // Your plugin is probably busted. return null; } File src = new File(bundle, stdLib.getFileName()); if (!src.exists()) { src = new File(bundle, "src/"+stdLib.getFileName()); } stdLib.setName(src.getAbsolutePath()); return stdLib; } public synchronized void removeCompilationUnit(CompilationUnit cu) throws NoModelException{ if(model == null) throw new NoModelException(); String filename = cu.getFileName(); assert filename != null; CompilationUnit cuold = getCompilationUnit(filename); List<CompilationUnit> culist = model.getCompilationUnitList(); int cindex = culist.getIndexOfChild(cuold); if(cindex>0){ culist.removeChild(cindex); } // model.flushCache(); flushAll(model); } public synchronized CompilationUnit getCompilationUnit(String fileName) throws NoModelException{ if(model == null) throw new NoModelException(); Iterator<CompilationUnit> iter = model.getCompilationUnits().iterator(); // normalize representation: fileName = normalizePath(fileName); while(iter.hasNext()){ CompilationUnit cu = iter.next(); String cuFileName = cu.getFileName(); // normalize representation: cuFileName = normalizePath(cuFileName); if(fileName.equals(cuFileName)){ return cu; } } return null; } private String normalizePath(String fileName) { fileName = fileName.replace('\\', '/'); if (fileName.matches("^/[a-zA-Z]+:/.*$")) { // a path like /C:/.... // remove the slash from the beginning fileName = fileName.substring(1); } return fileName; } public synchronized SemanticConditionList typeCheckModel(IProgressMonitor monitor, boolean locationTypeChecking, String defaultloctype, String locationTypePrecision, boolean checkProducts) throws NoModelException, TypecheckInternalException{ if(model == null) throw new NoModelException(); Main.exceptionHack(model); if(model.hasParserErrors()) return new SemanticConditionList(); // don't typecheck if the model has parsererrors // throw new TypecheckInternalException(new Exception("Model has parser errors!")); // model.flushCache(); flushAll(model); model.getTypeExt().clearTypeSystemExtensions(); if (locationTypeChecking) { LocationType defaultLocType = LocationType.createFromName(defaultloctype); LocationTypeInferrerExtension ltie = new LocationTypeInferrerExtension(model); this.ltie = ltie; ltie.setDefaultType(defaultLocType); ltie.setLocationTypingPrecision(LocationTypingPrecision.valueOf(locationTypePrecision)); model.registerTypeSystemExtension(ltie); } try { SemanticConditionList semerrors = model.getErrors(); /* Don't typecheck with semerrors, it might trip up. */ if (!semerrors.containsErrors()) semerrors = model.typeCheck(); /* Check products for errors. * Only the first error is reported (if any), on the product AST-node. * TODO: May be time-consuming for large projects, hence the checkProducts-switch. * Also could use a timer to switch off if it becomes excessive. * TODO: Use Eclipse's nested markers to show ALL contained errors? * TODO: The outline could indicate the broken product as well. */ if (!semerrors.containsErrors() && checkProducts) { monitor = new SubProgressMonitor(monitor, 10); // arbitrary value monitor.beginTask("Checking products", model.getProductDecls().size()); for (ProductDecl p : model.getProductDecls()) { monitor.subTask("Checking "+p.getName()); Model m2 = model.parseTreeCopy(); Main.exceptionHack(m2); try { m2.flattenForProduct(p); SemanticConditionList p_errs = m2.typeCheck(); if (p_errs.containsErrors()) { // Only show first error, on product semerrors.add(new SemanticError(p, ErrorMessage.ERROR_IN_PRODUCT, p.getName(), p_errs.getFirstError().getMessage())); } } catch (WrongProgramArgumentException e) { semerrors.add(new SemanticError(p, ErrorMessage.ERROR_IN_PRODUCT, p.getName(), e.getMessage())); } catch (DeltaModellingException e) { /* We we have a better location for error reporting? */ final ASTNode<?> loc; if (e instanceof DeltaModellingWithNodeException) loc = ((DeltaModellingWithNodeException) e).getNode(); else loc = p; if (e.getDelta() == null) semerrors.add(new SemanticError(loc, ErrorMessage.ERROR_IN_PRODUCT, p.getName(), e.getMessage())); else semerrors.add(new SemanticError(loc, ErrorMessage.ERROR_IN_PRODUCT_WITH_DELTA, p.getName(), e.getDelta().getName(), e.getMessage())); } } monitor.done(); } return semerrors; } catch (TypeCheckerException e) { return new SemanticConditionList(e); } catch (RuntimeException e) { throw new TypecheckInternalException(e); } } public synchronized Model getCompleteModel(){ if (model != null) Main.exceptionHack(model); return model; } public synchronized void cleanModel(){ model = null; } }