1/*
2 * Copyright 2016 Google Inc. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.turbine.binder.lookup;
18
19import static com.google.common.collect.Iterables.getLast;
20
21import com.google.common.base.Supplier;
22import com.google.common.base.Suppliers;
23import com.google.common.collect.ImmutableList;
24import com.google.turbine.binder.sym.ClassSymbol;
25import com.google.turbine.diag.SourceFile;
26import com.google.turbine.tree.Tree.ImportDecl;
27import java.util.Iterator;
28import java.util.LinkedHashMap;
29import java.util.Map;
30
31/** An index for statically imported members, in particular constant variables. */
32public class MemberImportIndex {
33
34  /** A cache of resolved static imports, keyed by the simple name of the member. */
35  private final Map<String, Supplier<ClassSymbol>> cache = new LinkedHashMap<>();
36
37  private final ImmutableList<Supplier<ClassSymbol>> classes;
38
39  public MemberImportIndex(
40      SourceFile source,
41      CanonicalSymbolResolver resolve,
42      TopLevelIndex tli,
43      ImmutableList<ImportDecl> imports) {
44    ImmutableList.Builder<Supplier<ClassSymbol>> packageScopes = ImmutableList.builder();
45    for (ImportDecl i : imports) {
46      if (!i.stat()) {
47        continue;
48      }
49      if (i.wild()) {
50        packageScopes.add(
51            Suppliers.memoize(
52                new Supplier<ClassSymbol>() {
53                  @Override
54                  public ClassSymbol get() {
55                    LookupResult result = tli.lookup(new LookupKey(i.type()));
56                    return result != null ? resolve.resolve(source, i.position(), result) : null;
57                  }
58                }));
59      } else {
60        cache.put(
61            getLast(i.type()),
62            Suppliers.memoize(
63                new Supplier<ClassSymbol>() {
64                  @Override
65                  public ClassSymbol get() {
66                    LookupResult result1 = tli.lookup(new LookupKey(i.type()));
67                    if (result1 == null) {
68                      return null;
69                    }
70                    ClassSymbol sym = (ClassSymbol) result1.sym();
71                    for (int i = 0; i < result1.remaining().size() - 1; i++) {
72                      sym = resolve.resolveOne(sym, result1.remaining().get(i));
73                    }
74                    return sym;
75                  }
76                }));
77      }
78    }
79    this.classes = packageScopes.build();
80  }
81
82  /** Resolves the owner of a single-member static import of the given simple name. */
83  public ClassSymbol singleMemberImport(String simpleName) {
84    Supplier<ClassSymbol> cachedResult = cache.get(simpleName);
85    return cachedResult != null ? cachedResult.get() : null;
86  }
87
88  /**
89   * Returns an iterator over all classes whose members are on-demand imported into the current
90   * compilation unit.
91   */
92  public Iterator<ClassSymbol> onDemandImports() {
93    return new WildcardSymbols(classes.iterator());
94  }
95
96  private static class WildcardSymbols implements Iterator<ClassSymbol> {
97    private final Iterator<Supplier<ClassSymbol>> it;
98
99    public WildcardSymbols(Iterator<Supplier<ClassSymbol>> it) {
100      this.it = it;
101    }
102
103    @Override
104    public boolean hasNext() {
105      return it.hasNext();
106    }
107
108    @Override
109    public ClassSymbol next() {
110      return it.next().get();
111    }
112
113    @Override
114    public void remove() {
115      throw new UnsupportedOperationException("remove");
116    }
117  }
118}
119