GlobalRegistry.java

/*
 * Copyright 2016 Providence Authors
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 net.morimekta.providence.reflect;

import net.morimekta.providence.descriptor.PDeclaredDescriptor;
import net.morimekta.providence.descriptor.PService;
import net.morimekta.providence.types.TypeReference;
import net.morimekta.providence.types.TypeRegistry;
import net.morimekta.util.FileUtil;
import net.morimekta.util.collect.UnmodifiableList;

import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import static net.morimekta.providence.reflect.util.ReflectionUtils.programNameFromPath;

/**
 * A global type registry containing lots of individual program
 * registries for each program.
 *
 * The program registry is a wrapper around a set of type
 * registries for each of a set of different files. The individual
 * registries are identified by the <code>path</code> to the
 * thrift file that was parsed.
 */
public class GlobalRegistry extends TypeRegistry {
    private final Map<String, ProgramRegistry> pathToRegistry;

    /**
     * Create an empty program registry.
     */
    public GlobalRegistry() {
        pathToRegistry = new LinkedHashMap<>();
    }

    @Nonnull
    public ProgramRegistry registryForPath(String filePath) {
        try {
            Path canonicalPath = FileUtil.readCanonicalPath(Paths.get(filePath));
            return pathToRegistry.computeIfAbsent(
                    canonicalPath.toString(),
                    path -> new ProgramRegistry(programNameFromPath(canonicalPath)));
        } catch (IOException e) {
            throw new UncheckedIOException(e.getMessage(), e);
        }
    }

    public List<ProgramRegistry> getRegistries() {
        return UnmodifiableList.copyOf(pathToRegistry.values());
    }

    // TypeRegistry

    @Nonnull
    @Override
    public Optional<PDeclaredDescriptor<?>> getDeclaredType(@Nonnull TypeReference reference) {
        for (ProgramRegistry registry : pathToRegistry.values()) {
            if (registry.getProgramContext().equals(reference.programName)) {
                Optional<PDeclaredDescriptor<?>> out = registry.getDeclaredType(reference);
                if (out.isPresent()) {
                    return out;
                }
            }
        }
        return Optional.empty();
    }

    @Nonnull
    @Override
    public Optional<PService> getService(@Nonnull TypeReference reference) {
        for (ProgramRegistry registry : pathToRegistry.values()) {
            if (registry.getProgramContext().equals(reference.programName)) {
                Optional<PService> out = registry.getService(reference);
                if (out.isPresent()) {
                    return out;
                }
            }
        }
        return Optional.empty();
    }

    @Nonnull
    @Override
    public <T> Optional<T> getConstantValue(@Nonnull TypeReference reference) {
        for (ProgramRegistry registry : pathToRegistry.values()) {
            if (registry.getProgramContext().equals(reference.programName)) {
                Optional<T> out = registry.getConstantValue(reference);
                if (out.isPresent()) {
                    return out;
                }
            }
        }
        return Optional.empty();
    }

    @Nonnull
    @Override
    public Optional<TypeReference> getTypedef(@Nonnull TypeReference reference) {
        for (ProgramRegistry registry : pathToRegistry.values()) {
            if (registry.getProgramContext().equals(reference.programName)) {
                Optional<TypeReference> out = registry.getTypedef(reference);
                if (out.isPresent()) {
                    return out;
                }
            }
        }
        return Optional.empty();
    }

    @Override
    public List<PDeclaredDescriptor<?>> getDeclaredTypes() {
        Set<PDeclaredDescriptor<?>> combined = new HashSet<>();
        for (ProgramRegistry registry : pathToRegistry.values()) {
            combined.addAll(registry.getDeclaredTypes());
        }
        return UnmodifiableList.copyOf(combined);
    }

    @Override
    public boolean isKnownProgram(@Nonnull String program) {
        for (ProgramRegistry registry : pathToRegistry.values()) {
            if (registry.getProgramContext().equals(program)) {
                return true;
            }
        }
        return false;
    }
}