com.google.template.soy.data.SoyMapData.java Source code

Java tutorial

Introduction

Here is the source code for com.google.template.soy.data.SoyMapData.java

Source

/*
 * Copyright 2008 Google Inc.
 *
 * Licensed 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 com.google.template.soy.data;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.template.soy.data.restricted.CollectionData;
import com.google.template.soy.data.restricted.StringData;

import java.io.IOException;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nonnull;

/**
 * A map data node in a Soy data tree.
 *
 * <p> Important: Even though this class is not marked 'final', do not extend this class.
 *
 */
public class SoyMapData extends CollectionData implements SoyDict {

    /** Underlying map. */
    private final Map<String, SoyData> map;

    public SoyMapData() {
        map = Maps.newLinkedHashMap();
    }

    /**
     * Constructor that initializes this SoyMapData from an existing map.
     * @param data The initial data in an existing map.
     */
    public SoyMapData(Map<String, ?> data) {
        map = new LinkedHashMap<>(data.size());

        for (Map.Entry<String, ?> entry : data.entrySet()) {
            String key;
            try {
                key = entry.getKey();
            } catch (ClassCastException cce) {
                throw new SoyDataException("Attempting to convert a map with non-string key to Soy data (key type "
                        + ((Map.Entry<?, ?>) entry).getKey().getClass().getName() + ").");
            }

            Object value = entry.getValue();

            try {
                map.put(key, SoyData.createFromExistingData(value));
            } catch (SoyDataException sde) {
                sde.prependKeyToDataPath(key);
                throw sde;
            }
        }
    }

    /**
     * Constructor that directly takes the keys/values as parameters.
     * @param data The initial data, with alternating keys/values.
     */
    public SoyMapData(Object... data) {
        this();
        put(data);
    }

    /**
     * Important: Please treat this method as superpackage-private. Do not call this method from
     * outside the 'tofu' and 'data' packages.
     *
     * Returns a view of this SoyMapData object as a Map.
     */
    public Map<String, SoyData> asMap() {
        return Collections.unmodifiableMap(map);
    }

    /**
     * Gets the keys in this map data.
     * @return A set containing the keys in this map data.
     */
    public Set<String> getKeys() {
        return Collections.unmodifiableSet(map.keySet());
    }

    /**
     * {@inheritDoc}
     *
     * <p> This method should only be used for debugging purposes.
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        try {
            render(sb);
        } catch (IOException e) {
            throw new RuntimeException(e); // impossible
        }
        return sb.toString();
    }

    @Override
    public void render(Appendable appendable) throws IOException {
        appendable.append('{');
        Iterator<Map.Entry<String, SoyData>> iterator = map.entrySet().iterator();
        if (iterator.hasNext()) {
            Map.Entry<String, SoyData> entry = iterator.next();
            appendable.append(entry.getKey()).append(": ");
            entry.getValue().render(appendable);
            while (iterator.hasNext()) {
                appendable.append(", ");
                entry = iterator.next();
                appendable.append(entry.getKey()).append(": ");
                entry.getValue().render(appendable);
            }
        }
        appendable.append('}');
    }

    /**
     * {@inheritDoc}
     *
     * <p> A map is always truthy.
     */
    @Deprecated
    @Override
    public boolean toBoolean() {
        return true;
    }

    @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
    @Override
    public boolean equals(Object other) {
        return this == other; // fall back to object equality
    }

    // -----------------------------------------------------------------------------------------------
    // Superpackage-private methods.

    /**
     * Important: Do not use outside of Soy code (treat as superpackage-private).
     *
     * Puts data into this data object at the specified key.
     * @param key An individual key.
     * @param value The data to put at the specified key.
     */
    @Override
    public void putSingle(String key, SoyData value) {
        map.put(key, value);
    }

    /**
     * Important: Do not use outside of Soy code (treat as superpackage-private).
     *
     * Removes the data at the specified key.
     * @param key An individual key.
     */
    @Override
    public void removeSingle(String key) {
        map.remove(key);
    }

    /**
     * Important: Do not use outside of Soy code (treat as superpackage-private).
     *
     * Gets the data at the specified key.
     * @param key An individual key.
     * @return The data at the specified key, or null if the key is not defined.
     */
    @Override
    public SoyData getSingle(String key) {
        return map.get(key);
    }

    // -----------------------------------------------------------------------------------------------
    // SoyDict.

    @Override
    @Nonnull
    public Map<String, ? extends SoyValueProvider> asJavaStringMap() {
        return asMap();
    }

    @Override
    @Nonnull
    public Map<String, ? extends SoyValue> asResolvedJavaStringMap() {
        return asMap();
    }

    // -----------------------------------------------------------------------------------------------
    // SoyRecord.

    @Override
    public boolean hasField(String name) {
        return getSingle(name) != null;
    }

    @Override
    public SoyValue getField(String name) {
        return getSingle(name);
    }

    @Override
    public SoyValueProvider getFieldProvider(String name) {
        return getSingle(name);
    }

    // -----------------------------------------------------------------------------------------------
    // SoyMap.

    @Override
    public int getItemCnt() {
        return getKeys().size();
    }

    @Override
    @Nonnull
    public Iterable<StringData> getItemKeys() {
        Set<String> internalKeys = getKeys();
        List<StringData> keys = Lists.newArrayListWithCapacity(internalKeys.size());
        for (String internalKey : internalKeys) {
            keys.add(StringData.forValue(internalKey));
        }
        return keys;
    }

    @Override
    public boolean hasItem(SoyValue key) {
        return getSingle(getStringKey(key)) != null;
    }

    @Override
    public SoyValue getItem(SoyValue key) {
        return getSingle(getStringKey(key));
    }

    @Override
    public SoyValueProvider getItemProvider(SoyValue key) {
        return getSingle(getStringKey(key));
    }

    /**
     * Gets the string key out of a SoyValue key, or throws SoyDataException if the key is not a
     * string.
     * @param key The SoyValue key.
     * @return The string key.
     */
    private String getStringKey(SoyValue key) {
        try {
            return key.stringValue();
        } catch (ClassCastException e) {
            throw new SoyDataException(
                    "SoyDict accessed with non-string key (got key type " + key.getClass().getName() + ").");
        }
    }

}