All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.baidu.bjf.remoting.protobuf.MapField Maven / Gradle / Ivy

/*
 * Copyright (c) Baidu Inc. All rights reserved.
 * 
 * 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.
 */
// Copyright 2008 Google Inc.  All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package com.baidu.bjf.remoting.protobuf;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.protobuf.Message;

/**
 * Internal representation of map fields in generated messages.
 * 
 * This class supports accessing the map field as a {@link Map} to be used in generated API and also supports accessing
 * the field as a {@link List} to be used in reflection API. It keeps track of where the data is currently stored and do
 * necessary conversions between map and list.
 * 
 * This class is a protobuf implementation detail. Users shouldn't use this class directly.
 * 
 * THREAD-SAFETY NOTE: Read-only access is thread-safe. Users can call getMap() and getList() concurrently in multiple
 * threads. If write-access is needed, all access must be synchronized.
 *
 * @param  the key type
 * @param  the value type
 */
public class MapField {
    /**
     * Indicates where the data of this map field is currently stored.
     * 
     * MAP: Data is stored in mapData. LIST: Data is stored in listData. BOTH: mapData and listData have the same data.
     *
     * When the map field is accessed (through generated API or reflection API), it will shift between these 3 modes:
     * 
     * getMap() getList() getMutableMap() getMutableList() MAP MAP BOTH MAP LIST LIST BOTH LIST MAP LIST BOTH BOTH BOTH
     * MAP LIST
     * 
     * As the map field changes its mode, the list/map reference returned in a previous method call may be invalidated.
     */
    private enum StorageMode {

        /** The map. */
        MAP,
        /** The list. */
        LIST,
        /** The both. */
        BOTH
    }

    /** The mode. */
    private volatile StorageMode mode;

    /** The map data. */
    private Map mapData;

    /** The list data. */
    private List listData;

    /**
     * The Interface Converter.
     *
     * @param  the key type
     * @param  the value type
     */
    // Convert between a map entry Message and a key-value pair.
    private static interface Converter {

        /**
         * Convert key and value to message.
         *
         * @param key the key
         * @param value the value
         * @return the message
         */
        Message convertKeyAndValueToMessage(K key, V value);

        /**
         * Convert message to key and value.
         *
         * @param message the message
         * @param map the map
         */
        void convertMessageToKeyAndValue(Message message, Map map);

        /**
         * Gets the message default instance.
         *
         * @return the message default instance
         */
        Message getMessageDefaultInstance();
    }

    /**
     * The Class ImmutableMessageConverter.
     *
     * @param  the key type
     * @param  the value type
     */
    private static class ImmutableMessageConverter implements Converter {

        /** The default entry. */
        private final MapEntry defaultEntry;

        /**
         * Instantiates a new immutable message converter.
         *
         * @param defaultEntry the default entry
         */
        public ImmutableMessageConverter(MapEntry defaultEntry) {
            this.defaultEntry = defaultEntry;
        }

        /*
         * (non-Javadoc)
         * 
         * @see com.baidu.bjf.remoting.protobuf.MapField.Converter#convertKeyAndValueToMessage(java.lang.Object,
         * java.lang.Object)
         */
        public Message convertKeyAndValueToMessage(K key, V value) {
            return defaultEntry.newBuilderForType().setKey(key).setValue(value).buildPartial();
        }

        /*
         * (non-Javadoc)
         * 
         * @see
         * com.baidu.bjf.remoting.protobuf.MapField.Converter#convertMessageToKeyAndValue(com.google.protobuf.Message,
         * java.util.Map)
         */
        public void convertMessageToKeyAndValue(Message message, Map map) {
            MapEntry entry = (MapEntry) message;
            map.put(entry.getKey(), entry.getValue());
        }

        /*
         * (non-Javadoc)
         * 
         * @see com.baidu.bjf.remoting.protobuf.MapField.Converter#getMessageDefaultInstance()
         */
        public Message getMessageDefaultInstance() {
            return defaultEntry;
        }
    }

    /** The converter. */
    private final Converter converter;

    /**
     * Instantiates a new map field.
     *
     * @param converter the converter
     * @param mode the mode
     * @param mapData the map data
     * @param listData the list data
     */
    private MapField(Converter converter, StorageMode mode, Map mapData, List listData) {
        this.converter = converter;
        this.mode = mode;
        this.mapData = mapData;
        this.listData = listData;
    }

    /**
     * Instantiates a new map field.
     *
     * @param defaultEntry the default entry
     * @param mode the mode
     * @param mapData the map data
     * @param listData the list data
     */
    private MapField(MapEntry defaultEntry, StorageMode mode, Map mapData, List listData) {
        this(new ImmutableMessageConverter(defaultEntry), mode, mapData, listData);
    }

    /**
     * Returns an immutable empty MapField.
     *
     * @param  the key type
     * @param  the value type
     * @param defaultEntry the default entry
     * @return the map field
     */
    public static  MapField emptyMapField(MapEntry defaultEntry) {
        return new MapField(defaultEntry, StorageMode.MAP, Collections. emptyMap(), null);
    }

    /**
     * Creates a new mutable empty MapField.
     *
     * @param  the key type
     * @param  the value type
     * @param defaultEntry the default entry
     * @return the map field
     */
    public static  MapField newMapField(MapEntry defaultEntry) {
        return new MapField(defaultEntry, StorageMode.MAP, new HashMap(), null);
    }

    /**
     * Convert key and value to message.
     *
     * @param key the key
     * @param value the value
     * @return the message
     */
    private Message convertKeyAndValueToMessage(K key, V value) {
        return converter.convertKeyAndValueToMessage(key, value);
    }

    /**
     * Convert message to key and value.
     *
     * @param message the message
     * @param map the map
     */
    @SuppressWarnings("unchecked")
    private void convertMessageToKeyAndValue(Message message, Map map) {
        converter.convertMessageToKeyAndValue(message, map);
    }

    /**
     * Convert map to list.
     *
     * @param mapData the map data
     * @return the list
     */
    private List convertMapToList(Map mapData) {
        List listData = new ArrayList();
        for (Map.Entry entry : mapData.entrySet()) {
            listData.add(convertKeyAndValueToMessage(entry.getKey(), entry.getValue()));
        }
        return listData;
    }

    /**
     * Convert list to map.
     *
     * @param listData the list data
     * @return the map
     */
    private Map convertListToMap(List listData) {
        Map mapData = new HashMap();
        for (Message item : listData) {
            convertMessageToKeyAndValue(item, mapData);
        }
        return mapData;
    }

    /**
     * Gets the map.
     *
     * @return the map
     */
    public Map getMap() {
        if (mode == StorageMode.LIST) {
            synchronized (this) {
                if (mode == StorageMode.LIST) {
                    mapData = convertListToMap(listData);
                    mode = StorageMode.BOTH;
                }
            }
        }
        return Collections.unmodifiableMap(mapData);
    }

    /**
     * Gets the mutable map.
     *
     * @return the mutable map
     */
    public Map getMutableMap() {
        if (mode != StorageMode.MAP) {
            if (mode == StorageMode.LIST) {
                mapData = convertListToMap(listData);
            }
            listData = null;
            mode = StorageMode.MAP;
        }
        return mapData;
    }

    /**
     * Merge from.
     *
     * @param other the other
     */
    public void mergeFrom(MapField other) {
        getMutableMap().putAll(MapFieldLite.copy(other.getMap()));
    }

    /**
     * Clear.
     */
    public void clear() {
        mapData = new HashMap();
        mode = StorageMode.MAP;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @SuppressWarnings("unchecked")
    @Override
    public boolean equals(Object object) {
        if (!(object instanceof MapField)) {
            return false;
        }
        MapField other = (MapField) object;
        return MapFieldLite. equals(getMap(), other.getMap());
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        return MapFieldLite. calculateHashCodeForMap(getMap());
    }

    /**
     * Returns a deep copy of this MapField.
     *
     * @return the map field
     */
    public MapField copy() {
        return new MapField(converter, StorageMode.MAP, MapFieldLite.copy(getMap()), null);
    }

    /**
     * Gets the list.
     *
     * @return the list
     */
    List getList() {
        if (mode == StorageMode.MAP) {
            synchronized (this) {
                if (mode == StorageMode.MAP) {
                    listData = convertMapToList(mapData);
                    mode = StorageMode.BOTH;
                }
            }
        }
        return Collections.unmodifiableList(listData);
    }

    /**
     * Gets the mutable list.
     *
     * @return the mutable list
     */
    List getMutableList() {
        if (mode != StorageMode.LIST) {
            if (mode == StorageMode.MAP) {
                listData = convertMapToList(mapData);
            }
            mapData = null;
            mode = StorageMode.LIST;
        }
        return listData;
    }

    /**
     * Gets the map entry message default instance.
     *
     * @return the map entry message default instance
     */
    Message getMapEntryMessageDefaultInstance() {
        return converter.getMessageDefaultInstance();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy