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

com.google.appengine.repackaged.com.google.common.flogger.context.ContextMetadata Maven / Gradle / Ivy

There is a newer version: 2.0.27
Show newest version
/*
 * Copyright (C) 2020 The Flogger 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.
 */

package com.google.common.flogger.context;

import static com.google.common.flogger.util.Checks.checkArgument;
import static com.google.common.flogger.util.Checks.checkNotNull;

import com.google.common.flogger.MetadataKey;
import com.google.common.flogger.backend.Metadata;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;

/**
 * Immutable {@link Metadata} implementation intended for use in nested contexts. Scope metadata can
 * be concatenated to inherit metadata from a parent context. This class is only expected to be
 * needed by implementations of {@link ScopedLoggingContext} and should not be considered a stable
 * API.
 */
public abstract class ContextMetadata extends Metadata {
  private static final class Entry {
    final MetadataKey key;
    final T value;

    Entry(MetadataKey key, T value) {
      this.key = checkNotNull(key, "key");
      this.value = checkNotNull(value, "value");
    }
  }

  /**
   * A builder to collect metadata key/values pairs in order. This class is only expected to be
   * needed by implementations of {@link ScopedLoggingContext} and should not be considered a stable
   * API.
   */
  public static final class Builder {
    private static final Entry[] EMPTY_ARRAY = new Entry[0];

    // Set an explicitly small initial capacity to avoid excessive allocations when we only ever
    // expect one or two keys to be added per context. We don't optimize for the case of zero keys,
    // since the scoped context builder shouldn't create a builder until the first key is added.
    private final List> entries = new ArrayList>(2);

    private Builder() {}

    /** Add a single metadata key/value pair to the builder. */
    @CanIgnoreReturnValue
    public  Builder add(MetadataKey key, T value) {
      // Entries are immutable and get moved into the metadata when it's built, so these get shared
      // and reduce the size of the metadata storage compared to storing adjacent key/value pairs.
      entries.add(new Entry(key, value));
      return this;
    }

    public ContextMetadata build() {
      // Analysis shows it's quicker to pass an empty array here and let the JVM optimize to avoid
      // creating an empty array just to overwrite all its elements.
      return new ImmutableScopeMetadata(entries.toArray(EMPTY_ARRAY));
    }
  }

  /** Returns a new {@code ScopeMetadata} builder. */
  public static Builder builder() {
    return new Builder();
  }

  /** Returns a space efficient {@code ScopeMetadata} containing a single value. */
  public static  ContextMetadata singleton(MetadataKey key, T value) {
    return new SingletonMetadata(key, value);
  }

  /** Returns the empty {@code ScopeMetadata}. */
  // We can't use empty() here as that's already taken by Metadata.
  public static ContextMetadata none() {
    return EmptyMetadata.INSTANCE;
  }

  private ContextMetadata() {}

  /**
   * Concatenates the given context metadata after this instance. Key value pairs are simply
   * concatenated (rather than being merged) which may result in multiple single valued keys
   * existing in the resulting sequence.
   *
   * 

Whether this is achieved via copying or chaining of instances is an implementation detail. * *

Use {@link com.google.common.flogger.backend.MetadataProcessor MetadataProcessor} to process * metadata consistently with respect to single valued and repeated keys, and use {@link * Metadata#findValue(MetadataKey)} to lookup the "most recent" value for a single valued key. */ public abstract ContextMetadata concatenate(ContextMetadata metadata); // Internal method to deal in entries directly during concatenation. abstract Entry get(int n); @Override public MetadataKey getKey(int n) { return get(n).key; } @Override public Object getValue(int n) { return get(n).value; } private static final class ImmutableScopeMetadata extends ContextMetadata { private final Entry[] entries; ImmutableScopeMetadata(Entry[] entries) { this.entries = entries; } @Override public int size() { return entries.length; } @Override Entry get(int n) { return entries[n]; } @Override @NullableDecl @SuppressWarnings("unchecked") public T findValue(MetadataKey key) { checkArgument(!key.canRepeat(), "metadata key must be single valued"); for (int n = entries.length - 1; n >= 0; n--) { Entry e = entries[n]; if (e.key.equals(key)) { return (T) e.value; } } return null; } @Override public ContextMetadata concatenate(ContextMetadata metadata) { int extraSize = metadata.size(); if (extraSize == 0) { return this; } if (entries.length == 0) { return metadata; } Entry[] merged = Arrays.copyOf(entries, entries.length + extraSize); for (int i = 0; i < extraSize; i++) { merged[i + entries.length] = metadata.get(i); } return new ImmutableScopeMetadata(merged); } } private static final class SingletonMetadata extends ContextMetadata { private final Entry entry; SingletonMetadata(MetadataKey key, T value) { this.entry = new Entry(key, value); } @Override public int size() { return 1; } @Override Entry get(int n) { if (n == 0) { return entry; } throw new IndexOutOfBoundsException(); } @Override @NullableDecl @SuppressWarnings("unchecked") public R findValue(MetadataKey key) { checkArgument(!key.canRepeat(), "metadata key must be single valued"); return entry.key.equals(key) ? (R) entry.value : null; } @Override public ContextMetadata concatenate(ContextMetadata metadata) { // No check for size() == 0 since this instance always has one value. int extraSize = metadata.size(); if (extraSize == 0) { return this; } Entry[] merged = new Entry[extraSize + 1]; merged[0] = entry; for (int i = 0; i < extraSize; i++) { merged[i + 1] = metadata.get(i); } return new ImmutableScopeMetadata(merged); } } // This is a static nested class as opposed to an anonymous class assigned to a constant field in // order to decouple its classloading when Metadata is loaded. Android users are particularly // careful about unnecessary class loading, and we've used similar mechanisms in Guava (see // CharMatchers). private static final class EmptyMetadata extends ContextMetadata { static final ContextMetadata INSTANCE = new EmptyMetadata(); @Override public int size() { return 0; } @Override Entry get(int n) { throw new IndexOutOfBoundsException(); } @Override @NullableDecl public T findValue(MetadataKey key) { // For consistency, do the same checks as for non-empty instances. checkArgument(!key.canRepeat(), "metadata key must be single valued"); return null; } @Override public ContextMetadata concatenate(ContextMetadata metadata) { return metadata; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy