/* * 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.types.CachingResolver.ErrorType; import polyglot.util.*; import x10.util.CollectionFactory; /** * A <code>CachingResolver</code> memoizes another Resolver */ public class AnotherCachingResolver implements Resolver { protected Resolver inner; private Map<Object, List<Type>> cache; private boolean cacheNotFound; protected Reporter reporter; /** * Create a caching resolver. * @param inner The resolver whose results this resolver caches. */ public AnotherCachingResolver(Resolver inner, boolean cacheNotFound, Reporter reporter) { this.inner = inner; this.cacheNotFound = cacheNotFound; this.cache = new LinkedHashMap<Object, List<Type>>(); this.reporter = reporter; } public AnotherCachingResolver(Resolver inner, Reporter reporter) { this(inner, true, reporter); } protected boolean shouldReport(int level) { return reporter.should_report(TOPICS, level); } /* public Object copy() { try { AnotherCachingResolver r = (AnotherCachingResolver) 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 Resolver 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; } /** * Find a type by name. * @param name The name to search for. */ public List<Type> find(Matcher<Type> matcher) throws SemanticException { if (shouldReport(2)) reporter.report(2, "CachingResolver: find: " + matcher.signature()); List<Type> q = null; if (matcher.key() != null) q = cache.get(matcher.key()); if (q == null) { if (shouldReport(3)) reporter.report(3, "CachingResolver: not cached: " + matcher.signature()); try { q = inner.find(matcher); } catch (NoClassException e) { if (shouldReport(3)) { reporter.report(3, "CachingResolver: " + e.getMessage()); reporter.report(3, "CachingResolver: installing " + matcher.signature() + "-> (not found) in resolver cache"); } if (cacheNotFound) { if (matcher.key() != null) cache.put(matcher.key(), CollectionUtil.<Type>list(new ErrorType(e))); } throw e; } if (matcher.key() != null) cache.put(matcher.key(), q); if (shouldReport(3)) reporter.report(3, "CachingResolver: loaded: " + matcher.signature()); } else { if (q.size() == 1 && q.get(0) instanceof ErrorType) { throw ((ErrorType) q.get(0)).error; } if (shouldReport(3)) reporter.report(3, "CachingResolver: cached: " + matcher.signature()); } return q; } /** * Check if a type object is in the cache, returning null if not. * @param name The name to search for. */ public List<Type> check(Matcher<Type> matcher) { if (matcher.key() == null) return null; List<Type> q = cache.get(matcher.key()); if (q != null && q.size() == 1 && q.get(0) instanceof ErrorType) return null; return (List<Type>) q; } public void dump() { reporter.report(1, "Dumping " + this); for (Map.Entry<Object, List<Type>> e : cache.entrySet()) { reporter.report(2, e.toString()); } } private static final Collection<String> TOPICS = CollectionUtil.list(Reporter.types, Reporter.resolver); }