/* * This file is part of the X10 project (http://x10-lang.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.opensource.org/licenses/eclipse-1.0.php * * This file was originally derived from the Polyglot extensible compiler framework. * * (C) Copyright 2000-2007 Polyglot project group, Cornell University * (C) Copyright IBM Corporation 2007-2012. */ package polyglot.types; import java.util.*; import polyglot.main.Reporter; import polyglot.util.*; import x10.util.CollectionFactory; /** * A <code>CachingResolver</code> memoizes another Resolver */ public class CachingResolver implements TopLevelResolver { protected TopLevelResolver inner; private Map<QName, List<Type>> cache; private Map<QName, Package> cachedPackages; private boolean cacheNotFound; protected Reporter reporter; /** * Create a caching resolver. * @param inner The resolver whose results this resolver caches. */ public CachingResolver(TopLevelResolver inner, boolean cacheNotFound, Reporter reporter) { this.inner = inner; this.cacheNotFound = cacheNotFound; this.cache = new LinkedHashMap<QName, List<Type>>(); this.cachedPackages = new LinkedHashMap<QName, Package>(); this.reporter = reporter; } public CachingResolver(TopLevelResolver inner, Reporter reporter) { this(inner, true, reporter); } protected boolean shouldReport(int level) { return (reporter.should_report(Reporter.sysresolver, level) && this instanceof SystemResolver) || reporter.should_report(TOPICS, level); } public boolean packageExists(QName name) { return inner.packageExists(name); } /* public Object copy() { try { CachingResolver r = (CachingResolver) super.clone(); // todo: the inner resolver is not deep cloned. so I removed this copy method. If it is needed, then Resolver should extend Copy and we should implement copy for all Resolvers. r.cache = CollectionFactory.newHashMap(this.cache); return r; } catch (CloneNotSupportedException e) { throw new InternalCompilerError("clone failed"); } } */ /** * The resolver whose results this resolver caches. */ public TopLevelResolver inner() { return this.inner; } public String toString() { return "(cache " + inner.toString() + ")"; } // FIXME: instead of copying, create an AggregateCollection that will use the actual values protected Collection<Type> cachedTypes() { ArrayList<Type> r = new ArrayList<Type>(); for (List<Type> tl : cache.values()) { r.addAll(tl); } return r; } protected static class ErrorType extends Type_c { private static final long serialVersionUID = -6530832412536840944L; public final SemanticException error; public ErrorType(SemanticException e) { this.error = e; } public String translate(Resolver c) { return error.toString(); } public String typeToString() { return error.toString(); } } protected static class ErrorPackage extends Package_c { private static final long serialVersionUID = 6490343022821637680L; public final SemanticException error; public ErrorPackage(SemanticException e) { this.error = e; } public String translate(Resolver c) { return error.toString(); } public String typeToString() { return error.toString(); } } /** * Find a type by name. * @param name The name to search for. * @return a list of types with that name */ public List<Type> find(QName name) throws SemanticException { if (shouldReport(2)) reporter.report(2, "CachingResolver: find: " + name); List<Type> q = cache.get(name); if (q == null) { if (shouldReport(3)) reporter.report(3, "CachingResolver: not cached: " + name); try { q = inner.find(name); } catch (NoClassException e) { if (shouldReport(3)) { reporter.report(3, "CachingResolver: " + e.getMessage()); reporter.report(3, "CachingResolver: installing " + name + "-> (not found) in resolver cache"); } if (cacheNotFound) { cache.put(name, CollectionUtil.<Type>list(new ErrorType(e))); } throw e; } for (Type t : q) { addNamed(name, t); addNamed(QName.make(t.fullName()), t); } cache.put(name, q); if (shouldReport(3)) reporter.report(3, "CachingResolver: loaded: " + name); } else { if (q.size() == 1 && q.get(0) instanceof ErrorType) { throw ((ErrorType) q.get(0)).error; } if (shouldReport(3)) reporter.report(3, "CachingResolver: cached: " + name); } return q; } /** * Find a single type by name. * @param name The name to search for. * @return a type with that name */ public Type findOne(QName name) throws SemanticException { List<Type> res = find(name); if (res.isEmpty()) { throw new SemanticException("Type with name "+name+" not found"); } if (res.size() != 1) { throw new SemanticException("Multiple types exist with name "+name); } Type t = res.get(0); if (t instanceof ErrorType) { throw ((ErrorType) t).error; } return t; } /** * Check if a type object is in the cache, returning null if not. * @param name The name to search for. */ public List<Type> check(QName name) { List<Type> q = cache.get(name); if (q != null && q.size() == 1 && q.get(0) instanceof ErrorType) return null; return (List<Type>) q; } /** * Find a package by name. For most code, this should be called * with the Java source name (p.A.B), not the class file name (p.A$B). The * exceptions are for resolving names in deserialized types and in types * loaded from raw class files. */ public Package findPackage(QName name) throws SemanticException { Package p = cachedPackages.get(name); if (p == null) { if (inner == null) { throw new SemanticException("Package with name "+name+" not found"); } return inner.findPackage(name); } return p; } /** * Install a qualifier in the cache. * @param name The name of the qualifier to insert. * @param q The qualifier to insert. */ public void install(QName name, Type q) { if (shouldReport(3)) reporter.report(3, "CachingResolver: installing type " + name + "->" + q + " in resolver cache"); if (shouldReport(5)) new Exception().printStackTrace(); List<Type> old = cache.get(name); List<Type> res; if (old == null) { res = CollectionUtil.list(q); } else { res = new ArrayList<Type>(old); if (!contains(res, q)) { res.add(q); } } cache.put(name, res); } private boolean contains(List<Type> l, Type t) { if (!(t instanceof ErrorType)) { for (Type u : l) { if (!(u instanceof ErrorType) && u.equals((Object)t)) return true; } } else { ErrorType et = (ErrorType) t; for (Type u : l) { if ((u instanceof ErrorType) && ((ErrorType) u).error.equals(et.error)) { return true; } } } return false; } /** * Install a type in the cache. * @param name The name of the type to insert. * @param q The type to insert. * @throws SemanticException */ public void addNamed(QName name, Type q) throws SemanticException { install(name, q); } /** * Install a package in the cache. * @param name The name of the package to insert. * @param q The package to insert. */ public void install(QName name, Package q) { if (shouldReport(3)) reporter.report(3, "CachingResolver: installing package " + name + "->" + q + " in resolver cache"); if (shouldReport(5)) new Exception().printStackTrace(); Package old = cachedPackages.get(name); cachedPackages.put(name, q); } /** * Install a package in the cache. * @param name The name of the package to insert. * @param q The package to insert. */ public void addPackage(QName name, Package q) { install(name, q); } public void dump() { reporter.report(1, "Dumping " + this); for (Map.Entry<QName, List<Type>> e : cache.entrySet()) { reporter.report(2, e.toString()); } } private static final Collection<String> TOPICS = CollectionUtil.list(Reporter.types, Reporter.resolver); }