/* GENERATED SOURCE. DO NOT MODIFY. */
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * 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.android.org.conscrypt;

import java.security.Principal;
import java.security.cert.Certificate;
import java.util.HashMap;
import java.util.List;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionBindingEvent;
import javax.net.ssl.SSLSessionBindingListener;
import javax.net.ssl.SSLSessionContext;

/**
 * An externalized view of the underlying {@link SSLSession} used within a
 * socket/engine. This class provides the caller with a consistent session
 * handle which will continue to be usable regardless of internal changes
 * to the connection.  It does this by delegating calls to the <b>current</b>
 * internal session, which is provided by the session {@code Provider}
 * (i.e. the socket or engine that owns the session).  This allows the provider
 * to switch implementations (for instance, using a JNI implementation to
 * access live values while the connection is open and a set of final values
 * when the connection is closed), even if the caller stores a reference to
 * the session object.
 *
 * <p>This class implements the {@link SSLSession} value API itself, rather
 * than delegating to the provided session, to ensure the caller has a consistent
 * value map, regardless of which internal session is currently being used by the
 * socket/engine.  This class will never call the value API methods on the
 * underlying sessions, so they need not be implemented.
 */
final class ExternalSession implements ConscryptSession {
    // Use an initial capacity of 2 to keep it small in the average case.
    private final HashMap<String, Object> values = new HashMap<>(2);
    private final Provider provider;

    public ExternalSession(Provider provider) {
        this.provider = provider;
    }

    @Override
    public String getRequestedServerName() {
        return provider.provideSession().getRequestedServerName();
    }

    @Override
    public List<byte[]> getStatusResponses() {
        return provider.provideSession().getStatusResponses();
    }

    @Override
    public byte[] getPeerSignedCertificateTimestamp() {
        return provider.provideSession().getPeerSignedCertificateTimestamp();
    }

    @Override
    public byte[] getId() {
        return provider.provideSession().getId();
    }

    @Override
    public SSLSessionContext getSessionContext() {
        return provider.provideSession().getSessionContext();
    }

    @Override
    public long getCreationTime() {
        return provider.provideSession().getCreationTime();
    }

    @Override
    public long getLastAccessedTime() {
        return provider.provideSession().getLastAccessedTime();
    }

    @Override
    public void invalidate() {
        provider.provideSession().invalidate();
    }

    @Override
    public boolean isValid() {
        return provider.provideSession().isValid();
    }

    @Override
    public java.security.cert.X509Certificate[] getPeerCertificates()
            throws SSLPeerUnverifiedException {
        return provider.provideSession().getPeerCertificates();
    }

    @Override
    public Certificate[] getLocalCertificates() {
        return provider.provideSession().getLocalCertificates();
    }

    @Override
    @SuppressWarnings("deprecation") // Public API
    public javax.security.cert.X509Certificate[] getPeerCertificateChain()
            throws SSLPeerUnverifiedException {
        return provider.provideSession().getPeerCertificateChain();
    }

    @Override
    public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
        return provider.provideSession().getPeerPrincipal();
    }

    @Override
    public Principal getLocalPrincipal() {
        return provider.provideSession().getLocalPrincipal();
    }

    @Override
    public String getCipherSuite() {
        return provider.provideSession().getCipherSuite();
    }

    @Override
    public String getProtocol() {
        return provider.provideSession().getProtocol();
    }

    @Override
    public String getPeerHost() {
        return provider.provideSession().getPeerHost();
    }

    @Override
    public int getPeerPort() {
        return provider.provideSession().getPeerPort();
    }

    @Override
    public int getPacketBufferSize() {
        return provider.provideSession().getPacketBufferSize();
    }

    @Override
    public int getApplicationBufferSize() {
        return provider.provideSession().getApplicationBufferSize();
    }

    @Override
    public String getApplicationProtocol() {
        return provider.provideSession().getApplicationProtocol();
    }

    @Override
    public Object getValue(String name) {
        if (name == null) {
            throw new IllegalArgumentException("name == null");
        }
        return values.get(name);
    }

    @Override
    public String[] getValueNames() {
        return values.keySet().toArray(new String[0]);
    }

    @Override
    public void putValue(String name, Object value) {
        putValue(this, name, value);
    }

    void putValue(SSLSession session, String name, Object value) {
        if (name == null || value == null) {
            throw new IllegalArgumentException("name == null || value == null");
        }
        Object old = values.put(name, value);
        if (value instanceof SSLSessionBindingListener) {
            ((SSLSessionBindingListener) value)
                    .valueBound(new SSLSessionBindingEvent(session, name));
        }
        if (old instanceof SSLSessionBindingListener) {
            ((SSLSessionBindingListener) old)
                    .valueUnbound(new SSLSessionBindingEvent(session, name));
        }
    }

    @Override
    public void removeValue(String name) {
        removeValue(this, name);
    }

    void removeValue(SSLSession session, String name) {
        if (name == null) {
            throw new IllegalArgumentException("name == null");
        }
        Object old = values.remove(name);
        if (old instanceof SSLSessionBindingListener) {
            SSLSessionBindingListener listener = (SSLSessionBindingListener) old;
            listener.valueUnbound(new SSLSessionBindingEvent(session, name));
        }
    }

    /**
     * The provider of the current delegate session.
     */
    interface Provider {
        ConscryptSession provideSession();
    }
}
