package nodebox.function; import com.google.common.base.Joiner; import com.google.common.base.Objects; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import nodebox.node.Node; import nodebox.node.Port; import java.util.*; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; /** * Manages a collection of function libraries. */ public class FunctionRepository { public static FunctionRepository of(FunctionLibrary... libraries) { ImmutableSet.Builder<FunctionLibrary> librarySet = ImmutableSet.builder(); librarySet.addAll(ImmutableSet.copyOf(libraries)); // The core library is always included. librarySet.add(CoreFunctions.LIBRARY); ImmutableMap.Builder<String, FunctionLibrary> builder = ImmutableMap.builder(); for (FunctionLibrary library : librarySet.build()) { builder.put(library.getNamespace(), library); } return new FunctionRepository(builder.build()); } public static FunctionRepository combine(FunctionRepository... repositories) { ImmutableSet.Builder<FunctionLibrary> librarySet = ImmutableSet.builder(); // The core library is always included. librarySet.add(CoreFunctions.LIBRARY); for (FunctionRepository repository : repositories) { librarySet.addAll(repository.getLibraries()); } ImmutableMap.Builder<String, FunctionLibrary> builder = ImmutableMap.builder(); for (FunctionLibrary library : librarySet.build()) { builder.put(library.getNamespace(), library); } return new FunctionRepository(builder.build()); } private final ImmutableMap<String, FunctionLibrary> libraryMap; private final transient Map<String, Function> functionCache = new HashMap<String, Function>(); private FunctionRepository(ImmutableMap<String, FunctionLibrary> libraryMap) { this.libraryMap = libraryMap; } public void reload() { invalidateFunctionCache(); for (FunctionLibrary library : getLibraries()) { if (library == CoreFunctions.LIBRARY) continue; library.reload(); } } public void invalidateFunctionCache() { functionCache.clear(); } public Function getFunction(String identifier) { if (functionCache.containsKey(identifier)) { return functionCache.get(identifier); } else { String[] functionParts = identifier.split("/"); checkArgument(functionParts.length == 2, "The function identifier should be in the form 'namespace/function'."); String namespace = functionParts[0]; String functionName = functionParts[1]; FunctionLibrary library = libraryMap.get(namespace); checkArgument(library != null, "Could not find function %s: unknown namespace.", identifier); assert library != null; // To avoid a compiler warning. checkArgument(library.hasFunction(functionName), "Could not find function %s: unknown function.", identifier); Function function = library.getFunction(functionName); functionCache.put(identifier, function); return function; } } public boolean hasFunction(String identifier) { try { return getFunction(identifier) != null; } catch (Exception e) { return false; } } public Node nodeForFunction(String identifier) { Function function = getFunction(identifier); Node n = Node.ROOT.withFunction(identifier); for (Function.Argument arg : function.getArguments()) { n = n.withInputAdded(Port.portForType(arg.getName(), arg.getType())); } return n; } public Collection<FunctionLibrary> getLibraries() { return libraryMap.values(); } public boolean hasLibrary(String namespace) { return libraryMap.containsKey(namespace); } public FunctionLibrary getLibrary(String namespace) { checkNotNull(namespace); checkArgument(libraryMap.containsKey(namespace), "Could not find library %s: unknown namespace.", namespace); return libraryMap.get(namespace); } public FunctionRepository withLibraryAdded(FunctionLibrary newLibrary) { List<FunctionLibrary> newLibraries = new ArrayList<FunctionLibrary>(); Collection<FunctionLibrary> libraries = getLibraries(); if (libraries.contains(newLibrary)) { for (FunctionLibrary library : libraries) { if (library.equals(newLibrary)) newLibraries.add(newLibrary); else newLibraries.add(library); } } else { newLibraries.addAll(libraries); newLibraries.add(newLibrary); } FunctionLibrary[] fl = newLibraries.toArray(new FunctionLibrary[newLibraries.size()]); return FunctionRepository.of(fl); } public FunctionRepository withLibraryRemoved(FunctionLibrary library) { checkNotNull(library); checkArgument(hasLibrary(library.getNamespace()), "Could not find library %s: unknown namespace.", library.getNamespace()); List<FunctionLibrary> newLibraries = new ArrayList<FunctionLibrary>(); newLibraries.addAll(getLibraries()); newLibraries.remove(library); FunctionLibrary[] fl = newLibraries.toArray(new FunctionLibrary[newLibraries.size()]); return FunctionRepository.of(fl); } //// Object overrides //// @Override public int hashCode() { return Objects.hashCode(libraryMap); } @Override public boolean equals(Object o) { if (!(o instanceof FunctionRepository)) return false; final FunctionRepository other = (FunctionRepository) o; return Objects.equal(libraryMap, other.libraryMap); } @Override public String toString() { return String.format("<FunctionRepository %s>", Joiner.on(", ").join(libraryMap.values())); } }