# Source code for opentelemetry.sdk.metrics._internal.view

    # Copyright The OpenTelemetry Authors
    #
    # 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.

    from fnmatch import fnmatch
    from logging import getLogger
    from typing import Callable, Optional, Set, Type

    from opentelemetry.metrics import Instrument
    from opentelemetry.sdk.metrics._internal.aggregation import (
        Aggregation,
        DefaultAggregation,
        _Aggregation,
        _ExplicitBucketHistogramAggregation,
        _ExponentialBucketHistogramAggregation,
    )
    from opentelemetry.sdk.metrics._internal.exemplar import (
        AlignedHistogramBucketExemplarReservoir,
        ExemplarReservoirBuilder,
        SimpleFixedSizeExemplarReservoir,
    )

    _logger = getLogger(__name__)

    def _default_reservoir_factory(
        aggregation_type: Type[_Aggregation],
    ) -> ExemplarReservoirBuilder:
        """Default reservoir factory per aggregation."""
        if issubclass(aggregation_type, _ExplicitBucketHistogramAggregation):
            return AlignedHistogramBucketExemplarReservoir
        if issubclass(aggregation_type, _ExponentialBucketHistogramAggregation):
            return SimpleFixedSizeExemplarReservoir
        return SimpleFixedSizeExemplarReservoir

    class View:
        """
        A `View` configuration parameters can be used for the following
        purposes:

        1. Match instruments: When an instrument matches a view, measurements
           received by that instrument will be processed.
        2. Customize metric streams: A metric stream is identified by a match
           between a view and an instrument and a set of attributes. The metric
           stream can be customized by certain attributes of the corresponding view.

        The attributes documented next serve one of the previous two purposes.

        Args:
            instrument_type: This is an instrument matching attribute: the class the
                instrument must be to match the view.

            instrument_name: This is an instrument matching attribute: the name the
                instrument must have to match the view. Wild card characters are supported. Wild
                card characters should not be used with this attribute if the view has also a
                ``name`` defined.

            meter_name: This is an instrument matching attribute: the name the
                instrument meter must have to match the view.

            meter_version: This is an instrument matching attribute: the version
                the instrument meter must have to match the view.

            meter_schema_url: This is an instrument matching attribute: the schema
                URL the instrument meter must have to match the view.

            name: This is a metric stream customizing attribute: the name of the
                metric stream. If `None`, the name of the instrument will be used.

            description: This is a metric stream customizing attribute: the
                description of the metric stream. If `None`, the description of the instrument will
                be used.

            attribute_keys: This is a metric stream customizing attribute: this is
                a set of attribute keys. If not `None` then only the measurement attributes that
                are in ``attribute_keys`` will be used to identify the metric stream.

            aggregation: This is a metric stream customizing attribute: the
                aggregation instance to use when data is aggregated for the
                corresponding metrics stream. If `None` an instance of
                `DefaultAggregation` will be used.

            exemplar_reservoir_factory: This is a metric stream customizing attribute:
                the exemplar reservoir factory

            instrument_unit: This is an instrument matching attribute: the unit the
                instrument must have to match the view.

        This class is not intended to be subclassed by the user.
        """

        _default_aggregation = DefaultAggregation()

        def __init__(
            self,
            instrument_type: Optional[Type[Instrument]] = None,
            instrument_name: Optional[str] = None,
            meter_name: Optional[str] = None,
            meter_version: Optional[str] = None,
            meter_schema_url: Optional[str] = None,
            name: Optional[str] = None,
            description: Optional[str] = None,
            attribute_keys: Optional[Set[str]] = None,
            aggregation: Optional[Aggregation] = None,
            exemplar_reservoir_factory: Optional[
                Callable[[Type[_Aggregation]], ExemplarReservoirBuilder]
            ] = None,
            instrument_unit: Optional[str] = None,
        ):
            if (
                instrument_type
                is instrument_name
                is instrument_unit
                is meter_name
                is meter_version
                is meter_schema_url
                is None
            ):
                # pylint: disable=broad-exception-raised
                raise Exception(
                    "Some instrument selection "
                    f"criteria must be provided for View {name}"
                )

            if (
                name is not None
                and instrument_name is not None
                and ("*" in instrument_name or "?" in instrument_name)
            ):
                # pylint: disable=broad-exception-raised
                raise Exception(
                    f"View {name} declared with wildcard "
                    "characters in instrument_name"
                )

            # _name, _description, _aggregation, _exemplar_reservoir_factory and
            # _attribute_keys will be accessed when instantiating a _ViewInstrumentMatch.
            self._name = name
            self._instrument_type = instrument_type
            self._instrument_name = instrument_name
            self._instrument_unit = instrument_unit
            self._meter_name = meter_name
            self._meter_version = meter_version
            self._meter_schema_url = meter_schema_url

            self._description = description
            self._attribute_keys = attribute_keys
            self._aggregation = aggregation or self._default_aggregation
            self._exemplar_reservoir_factory = (
                exemplar_reservoir_factory or _default_reservoir_factory
            )

        # pylint: disable=too-many-return-statements
        # pylint: disable=too-many-branches
        def _match(self, instrument: Instrument) -> bool:
            if self._instrument_type is not None:
                if not isinstance(instrument, self._instrument_type):
                    return False

            if self._instrument_name is not None:
                if not fnmatch(instrument.name, self._instrument_name):
                    return False

            if self._instrument_unit is not None:
                if not fnmatch(instrument.unit, self._instrument_unit):
                    return False

            if self._meter_name is not None:
                if instrument.instrumentation_scope.name != self._meter_name:
                    return False

            if self._meter_version is not None:
                if instrument.instrumentation_scope.version != self._meter_version:
                    return False

            if self._meter_schema_url is not None:
                if (
                    instrument.instrumentation_scope.schema_url
                    != self._meter_schema_url
                ):
                    return False

            return True