/*
* Copyright 2012 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.devtools.j2objc.util;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.Table;
import java.util.HashSet;
import java.util.Set;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
/**
* Tracks classes, fields, and methods that are referenced in source code.
*
* @author Daniel Connelly
*/
public class CodeReferenceMap {
public static class Builder {
private final Set<String> deadClasses = new HashSet<String>();
private final Table<String, String, Set<String>> deadMethods = HashBasedTable.create();
private final ListMultimap<String, String> deadFields =
MultimapBuilder.hashKeys().arrayListValues().build();
public CodeReferenceMap build() {
ImmutableTable.Builder<String, String, ImmutableSet<String>> deadMethodsBuilder =
ImmutableTable.builder();
for (Table.Cell<String, String, Set<String>> cell : this.deadMethods.cellSet()) {
deadMethodsBuilder.put(
cell.getRowKey(),
cell.getColumnKey(),
ImmutableSet.copyOf(cell.getValue()));
}
return new CodeReferenceMap(
ImmutableSet.copyOf(deadClasses),
deadMethodsBuilder.build(),
ImmutableMultimap.copyOf(deadFields));
}
public Builder addClass(String clazz) {
deadClasses.add(clazz);
return this;
}
public Builder addMethod(String clazz, String name, String signature) {
if (!deadMethods.contains(clazz, name)) {
deadMethods.put(clazz, name, new HashSet<String>());
}
deadMethods.get(clazz, name).add(signature);
return this;
}
public Builder addField(String clazz, String field) {
deadFields.put(clazz, field);
return this;
}
}
public static Builder builder() {
return new Builder();
}
private final ImmutableSet<String> referencedClasses;
private final ImmutableTable<String, String, ImmutableSet<String>> referencedMethods;
private final ImmutableMultimap<String, String> referencedFields;
private final Set<String> hasConstructorRemovedClasses = new HashSet<>();
private CodeReferenceMap(
ImmutableSet<String> referencedClasses,
ImmutableTable<String, String, ImmutableSet<String>> referencedMethods,
ImmutableMultimap<String, String> referencedFields) {
this.referencedClasses = referencedClasses;
this.referencedMethods = referencedMethods;
this.referencedFields = referencedFields;
}
public ImmutableSet<String> getReferencedClasses() {
return referencedClasses;
}
public ImmutableTable<String, String, ImmutableSet<String>> getReferencedMethods() {
return referencedMethods;
}
public ImmutableMultimap<String, String> getReferencedFields() {
return referencedFields;
}
public boolean containsClass(String clazz) {
return referencedClasses.contains(clazz);
}
public boolean containsClass(TypeElement clazz, ElementUtil elementUtil) {
return containsClass(elementUtil.getBinaryName(clazz));
}
public boolean containsMethod(String clazz, String name, String signature) {
return referencedClasses.contains(clazz)
|| (referencedMethods.contains(clazz, name)
&& referencedMethods.get(clazz, name).contains(signature));
}
public boolean containsMethod(ExecutableElement method, TypeUtil typeUtil) {
String className = typeUtil.elementUtil().getBinaryName(ElementUtil.getDeclaringClass(method));
String methodName = typeUtil.getReferenceName(method);
String methodSig = typeUtil.getReferenceSignature(method);
return containsMethod(className, methodName, methodSig);
}
public boolean containsField(String clazz, String field) {
return referencedClasses.contains(clazz) || referencedFields.containsEntry(clazz, field);
}
public boolean isEmpty() {
return referencedClasses.isEmpty() && referencedMethods.isEmpty() && referencedFields.isEmpty();
}
public void addConstructorRemovedClass(String clazz) {
hasConstructorRemovedClasses.add(clazz);
}
public boolean classHasConstructorRemoved(String clazz) {
return hasConstructorRemovedClasses.contains(clazz);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(referencedClasses.asList().toString() + "\n");
builder.append(referencedFields.toString() + "\n");
builder.append(referencedMethods.toString());
return builder.toString();
}
}