ConfigContext.java

/*
 * Copyright 2017 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.config.parser;

import net.morimekta.providence.PMessage;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import static net.morimekta.providence.config.parser.ConfigUtil.RESERVED_WORDS;

/**
 * Context object related to a providence file being parsed. Takes care of
 * knowing about includes and registered references.
 */
class ConfigContext {
    private final Set<String>                  includeAliases;
    private final Map<String, Object>          references;
    private final Map<String, ConfigException> referenceExceptions;

    ConfigContext() {
        this.references = new HashMap<>();
        this.referenceExceptions = new HashMap<>();
        this.includeAliases = new HashSet<>();
    }

    boolean containsReference(String name) {
        return referenceExceptions.containsKey(name) ||
               references.containsKey(name);
    }

    void setInclude(String alias, PMessage include) {
        references.put(alias, include);
        includeAliases.add(alias);
    }

    String initReference(ConfigToken token) throws ConfigException {
        String reference = token.toString();
        if (RESERVED_WORDS.contains(reference)) {
            throw new ConfigException(token, "Trying to assign reference id '%s', which is reserved.", reference);
        }

        ConfigException ex = referenceExceptions.get(reference);
        if (ex != null) {
            if (references.containsKey(reference)) {
                throw new ConfigException(token,
                                          "Trying to reassign reference '%s', original at line %d",
                                          reference,
                                          ex.getLineNo())
                        .initCause(ex);
            }
            throw new ConfigException(token,
                                      "Trying to reassign reference '%s' while calculating it's value, original at line %d",
                                      reference,
                                      ex.getLineNo())
                    .initCause(ex);
        } else if (includeAliases.contains(reference)) {
            throw new ConfigException(token,
                                      "Trying to reassign include alias '%s' to reference.",
                                      reference);
        }

        referenceExceptions.put(reference, new ConfigException(token, "Original reference"));
        return reference;
    }

    Object setReference(String reference, Object value) {
        if (reference != null) {
            if (!referenceExceptions.containsKey(reference)) {
                throw new RuntimeException("Reference '" + reference + "' not initialised");
            }
            references.put(reference, value);
        }
        return value;
    }

    Object getReference(String reference, ConfigToken token) throws ConfigException {
        if (RESERVED_WORDS.contains(reference)) {
            return null;
        }
        if (references.containsKey(reference)) {
            return references.get(reference);
        }

        ConfigException ex = referenceExceptions.get(reference);
        if (ex != null) {
            throw new ConfigException(token, "Trying to reference '%s' while it's being defined, original at line %d",
                                      reference, ex.getLineNo());
        }
        throw new ConfigException(token, "No such reference '%s'", reference);
    }
}