PInterfaceDescriptor.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.descriptor;

import net.morimekta.providence.PMessage;
import net.morimekta.providence.PMessageBuilder;
import net.morimekta.providence.PMessageVariant;
import net.morimekta.util.collect.UnmodifiableList;
import net.morimekta.util.collect.UnmodifiableMap;

import javax.annotation.Nonnull;
import javax.annotation.concurrent.Immutable;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * The definition of a providence interface. This is a complete class,
 * in order to minimize the need for generated code for interfaces.
 */
@Immutable
public class PInterfaceDescriptor<T extends PMessage<T>> extends PMessageDescriptor<T> {
    private final PField<T>[]             fields;
    private final Map<Integer, PField<T>> fieldIdMap;
    private final Map<String, PField<T>>  fieldNameMap;
    private final Map<String, PField<T>>  fieldPojoNameMap;

    private final List<PMessageDescriptorProvider<?>>          possibleTypeProviders;
    private final AtomicReference<List<PMessageDescriptor<?>>> possibleTypes;

    public PInterfaceDescriptor(String programName,
                                String name,
                                PField<T>[] fields,
                                PMessageDescriptorProvider<?>... possibleTypes) {
        super(programName, name, null, false);
        this.fields = fields;
        this.possibleTypeProviders = UnmodifiableList.copyOf(possibleTypes);
        this.possibleTypes = new AtomicReference<>();
        // TODO: Make better detection of auto interfaces. Currently there is none.

        UnmodifiableMap.Builder<Integer, PField<T>> fieldIdMap   = UnmodifiableMap.builder(this.fields.length);
        UnmodifiableMap.Builder<String, PField<T>>  fieldNameMap = UnmodifiableMap.builder(this.fields.length);
        UnmodifiableMap.Builder<String, PField<T>>  fieldPojoNameMap = UnmodifiableMap.builder(this.fields.length);
        for (PField<T> field : fields) {
            fieldIdMap.put(field.getId(), field);
            fieldNameMap.put(field.getName(), field);
            fieldPojoNameMap.put(field.getPojoName(), field);
        }
        this.fieldIdMap = fieldIdMap.build();
        this.fieldNameMap = fieldNameMap.build();
        this.fieldPojoNameMap = fieldPojoNameMap.build();
    }

    @Override
    public boolean isInnerType() {
        return false;
    }

    @Override
    public boolean isAutoType() {
        return false;
    }

    /**
     * @return Get a list of know possible types implementing this interface.
     */
    public List<PMessageDescriptor<?>> getPossibleTypes() {
        return possibleTypes.updateAndGet(list -> {
            if (list == null) {
                list = possibleTypeProviders.stream().map(PMessageDescriptorProvider::descriptor).collect(Collectors.toList());
            }
            return list;
        });
    }

    @Nonnull
    @Override
    public PMessageVariant getVariant() {
        return PMessageVariant.INTERFACE;
    }

    @Nonnull
    @Override
    public PField<T>[] getFields() {
        return Arrays.copyOf(fields, fields.length);
    }

    @Override
    public PField<T> findFieldByName(String name) {
        return fieldNameMap.get(name);
    }

    @Override
    public PField<T> findFieldByPojoName(String pojoName) {
        return fieldPojoNameMap.get(pojoName);
    }

    @Override
    public PField<T> findFieldById(int id) {
        return fieldIdMap.get(id);
    }

    @Nonnull
    @Override
    public PMessageBuilder<T> builder() {
        throw new UnsupportedOperationException("Interfaces cannot be built");
    }
}