package sk.stuba.fiit.perconik.core.services.resources;
import java.util.Set;
import com.google.common.base.Optional;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.SetMultimap;
import sk.stuba.fiit.perconik.core.Listener;
import sk.stuba.fiit.perconik.core.Resource;
import sk.stuba.fiit.perconik.utilities.MoreSets;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Sets.newHashSet;
final class StandardResourceProvider extends AbstractResourceProvider {
private final BiMap<String, Resource<?>> nameToResource;
private final SetMultimap<Class<? extends Listener>, Resource<?>> typeToResources;
private final ResourceProvider parent;
StandardResourceProvider(final Builder builder) {
this.nameToResource = ImmutableBiMap.copyOf(builder.nameToResource);
this.typeToResources = ImmutableSetMultimap.copyOf(builder.typeToResources);
this.parent = builder.parent.or(ResourceProviders.superResourceProvider());
assert this.nameToResource.size() == newHashSet(this.typeToResources.values()).size();
}
public static final class Builder implements ResourceProvider.Builder {
final BiMap<String, Resource<?>> nameToResource;
final SetMultimap<Class<? extends Listener>, Resource<?>> typeToResources;
Optional<ResourceProvider> parent;
public Builder() {
this.nameToResource = HashBiMap.create();
this.typeToResources = HashMultimap.create();
this.parent = Optional.absent();
}
public <L extends Listener> Builder add(final Class<L> type, final Resource<? super L> resource) {
checkNotNull(type);
this.nameToResource.put(resource.getName(), resource);
this.typeToResources.put(type, resource);
return this;
}
public Builder parent(final ResourceProvider parent) {
checkState(!this.parent.isPresent());
this.parent = Optional.of(checkNotNull(parent));
return this;
}
public StandardResourceProvider build() {
return new StandardResourceProvider(this);
}
}
public static Builder builder() {
return new Builder();
}
public Resource<?> forName(final String name) {
Resource<?> resource = this.nameToResource.get(name);
if (resource != null) {
return resource;
}
return this.parentForName(name, null);
}
public <L extends Listener> Set<Resource<L>> forType(final Class<L> type) {
Set<Resource<L>> resources = newHashSet();
for (Resource<?> resource: this.typeToResources.get(type)) {
resources.add(Unsafe.cast(type, resource));
}
return MoreSets.newHashSet(resources, this.parentForType(type, null));
}
public Set<String> names() {
return MoreSets.newHashSet(this.nameToResource.keySet(), this.parent.names());
}
public Set<Class<? extends Listener>> types() {
return MoreSets.newHashSet(this.typeToResources.keySet(), this.parent.types());
}
public ResourceProvider parent() {
return this.parent;
}
}