SimpleTypeRegistry.java
/*
* Copyright 2015-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.types;
import net.morimekta.providence.descriptor.PContainer;
import net.morimekta.providence.descriptor.PDeclaredDescriptor;
import net.morimekta.providence.descriptor.PDescriptor;
import net.morimekta.providence.descriptor.PEnumDescriptor;
import net.morimekta.providence.descriptor.PField;
import net.morimekta.providence.descriptor.PMap;
import net.morimekta.providence.descriptor.PMessageDescriptor;
import net.morimekta.providence.descriptor.PService;
import net.morimekta.providence.descriptor.PServiceMethod;
import net.morimekta.providence.descriptor.PUnionDescriptor;
import net.morimekta.providence.descriptor.PValueProvider;
import net.morimekta.util.collect.UnmodifiableList;
import javax.annotation.Nonnull;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
/**
* Registry for declared types referenced in a flat space program context.
* The registry itself does not have a context per se, as these may
* reference each other recursively. Note that this registry does not
* handle situations where two programs with the same name exist with
* a type each with the same name.
*/
public class SimpleTypeRegistry extends WritableTypeRegistry {
private final Set<String> knownPrograms;
private final Map<TypeReference, PDeclaredDescriptor<?>> declaredTypes;
private final Map<TypeReference, PService> services;
private final Map<TypeReference, TypeReference> typedefs;
private final Map<TypeReference, PValueProvider> constants;
public SimpleTypeRegistry() {
this.knownPrograms = new HashSet<>();
this.declaredTypes = new LinkedHashMap<>();
this.services = new LinkedHashMap<>();
this.typedefs = new LinkedHashMap<>();
this.constants = new LinkedHashMap<>();
}
// TypeRegistry
@Override
public boolean isKnownProgram(@Nonnull String programName) {
return knownPrograms.contains(programName);
}
@Override
@Nonnull
public Optional<TypeReference> getTypedef(@Nonnull TypeReference reference) {
return Optional.ofNullable(typedefs.get(reference));
}
@Override
public List<PDeclaredDescriptor<?>> getDeclaredTypes() {
return UnmodifiableList.copyOf(declaredTypes.values());
}
@Nonnull
@Override
public Optional<PDeclaredDescriptor<?>> getDeclaredType(@Nonnull TypeReference reference) {
return Optional.ofNullable(declaredTypes.get(reference));
}
@Nonnull
@Override
public Optional<PService> getService(@Nonnull TypeReference reference) {
return Optional.ofNullable(services.get(reference));
}
@Nonnull
@Override
@SuppressWarnings("unchecked")
public <T> Optional<T> getConstantValue(@Nonnull TypeReference reference) {
return Optional.ofNullable((PValueProvider<T>) constants.get(reference))
.map(PValueProvider::get);
}
// WritableTypeRegistry
@Override
public void registerTypedef(@Nonnull TypeReference reference,
@Nonnull TypeReference target) {
// Ignore if the type already exists.
if (typedefs.containsKey(reference)) return;
if (reference.isNativeType()) {
throw new IllegalArgumentException("Registering typedef with native type name '" + reference + "'");
}
// Fail if we typedef back to self. Circular typedefs would be problematic if stored.
if (reference.equals(finalTypeReference(target))) {
throw new IllegalArgumentException("Typedef defining back to itself: " + reference);
}
knownPrograms.add(reference.programName);
typedefs.put(reference, target);
}
@Override
public void registerConstant(@Nonnull TypeReference reference, @Nonnull PValueProvider value) {
if (constants.containsKey(reference)) return;
if (reference.isNativeType()) {
throw new IllegalArgumentException("Registering const with native type name '" + reference + "'");
}
knownPrograms.add(reference.programName);
constants.put(reference, value);
}
@Override
@SuppressWarnings("unchecked")
public void registerService(@Nonnull PService service) {
TypeReference reference = TypeReference.ref(service.getProgramName(),
service.getName());
if (services.containsKey(reference)) {
return;
}
knownPrograms.add(service.getProgramName());
services.put(reference, service);
for (PServiceMethod method : service.getMethods()) {
PUnionDescriptor returnType = method.getResponseType();
if (returnType != null) {
registerType(returnType);
}
PMessageDescriptor requestType = method.getRequestType();
registerType(requestType);
}
}
@Override
@SuppressWarnings("unchecked")
public <T> void registerType(PDeclaredDescriptor<T> declaredType) {
TypeReference reference = TypeReference.ref(declaredType.getProgramName(),
declaredType.getName());
if (declaredTypes.containsKey(reference)) {
return;
}
knownPrograms.add(reference.programName);
declaredTypes.put(reference, declaredType);
if (declaredType instanceof PMessageDescriptor) {
PMessageDescriptor descriptor = (PMessageDescriptor) declaredType;
for (PField field : descriptor.getFields()) {
registerUntyped(field.getDescriptor());
}
}
}
@SuppressWarnings("unchecked")
private void registerUntyped(PDescriptor descriptor) {
if (descriptor instanceof PEnumDescriptor ||
descriptor instanceof PMessageDescriptor) {
registerType((PDeclaredDescriptor) descriptor);
} else if (descriptor instanceof PContainer) {
registerUntyped(((PContainer) descriptor).itemDescriptor());
if (descriptor instanceof PMap) {
registerUntyped(((PMap) descriptor).keyDescriptor());
}
}
}
}