001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2017–2019 microBean.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
014 * implied.  See the License for the specific language governing
015 * permissions and limitations under the License.
016 */
017package org.microbean.configuration.spi;
018
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.Collections; // for javadoc only
022import java.util.List;
023import java.util.Map;
024
025import org.microbean.configuration.api.ConfigurationValue;
026
027/**
028 * A {@link ComparatorBasedArbiter} that compares {@link
029 * ConfigurationValue}s by their {@linkplain
030 * ConfigurationValue#getSource() sources}.
031 *
032 * @author <a href="https://about.me/lairdnelson"
033 * target="_parent">Laird Nelson</a>
034 *
035 * @see ConfigurationValue
036 *
037 * @see ComparatorBasedArbiter
038 */
039public class ConfigurationValueSourceComparingArbiter extends ComparatorBasedArbiter<ConfigurationValue> {
040
041
042  /*
043   * Constructors.
044   */
045
046
047  /**
048   * Creates a new {@link ConfigurationValueSourceComparingArbiter}.
049   *
050   * @see #ConfigurationValueSourceComparingArbiter(List)
051   */
052  public ConfigurationValueSourceComparingArbiter() {
053    this(null);
054  }
055
056  /**
057   * Creates a new {@link ConfigurationValueSourceComparingArbiter}.
058   *
059   * @param configurationInstances a {@link List} of {@link Class}
060   * instances describing configuration value {@linkplain
061   * ConfigurationValue#getSource() sources}; may be (trivially)
062   * {@code null}
063   *
064   * @see RankedComparator#RankedComparator(List)
065   */
066  public ConfigurationValueSourceComparingArbiter(final List<? extends Configuration> configurationInstances) {
067    super(new RankedComparator<ConfigurationValue>(configurationInstances) {
068        private static final long serialVersionUID = 1L;
069        @Override
070        protected final Object getComparisonObject(final ConfigurationValue realObject) {
071          final Object returnValue;
072          if (realObject == null) {
073            returnValue = null;
074          } else {
075            returnValue = realObject.getSource();
076          }
077          return returnValue;
078        }
079      });
080  }
081
082
083  /*
084   * Instance methods.
085   */
086
087
088  /**
089   * Resolves the configuration value ambiguity represented by the
090   * supplied {@code ambiguousValues} parameter value by selecting the
091   * {@link ConfigurationValue} whose {@linkplain
092   * ConfigurationValue#getSource() associated source} is {@linkplain
093   * #ConfigurationValueSourceComparingArbiter(List) ranked} closer to
094   * {@code 0}.
095   *
096   * @param requestedCoordinates the ({@linkplain
097   * Collections#unmodifiableMap(Map) immutable}) configuration
098   * coordinates in effect for the request; may be {@code null}
099   *
100   * @param name the name of the configuration value; must not be
101   * {@code null}
102   *
103   * @param ambiguousValues an {@linkplain
104   * Collections#unmodifiableCollection(Collection) immutable} {@link
105   * Collection} of definitionally ambiguous {@link
106   * ConfigurationValue}s that resulted from the request; may be
107   * {@code null}
108   *
109   * @return the result of arbitration, or {@code null} if the dispute
110   * cannot be arbitrated by this {@link Arbiter}
111   *
112   * @exception NullPointerException if {@code name} is {@code null}
113   */
114  @Override
115  public ConfigurationValue arbitrate(final Map<? extends String, ? extends String> requestedCoordinates,
116                                      final String name,
117                                      final Collection<? extends ConfigurationValue> ambiguousValues) {
118    final ConfigurationValue returnValue;
119    if (ambiguousValues == null || ambiguousValues.isEmpty() || !this.canArbitrate(ambiguousValues)) {
120      returnValue = null;
121    } else {
122      final int size = ambiguousValues.size();
123      assert size > 0;
124      switch (size) {
125      case 1: // pathological case
126        returnValue = ambiguousValues.iterator().next();
127        break;
128      default:
129        final List<? extends ConfigurationValue> list = new ArrayList<>(ambiguousValues);
130        list.sort(this.getComparator());
131        returnValue = list.get(0);
132        break;
133      }
134    }
135    return returnValue;
136  }
137
138  /**
139   * Returns the {@link RankedComparator} used by this {@link
140   * ConfigurationValueSourceComparingArbiter} implementation.
141   *
142   * <p>This method never returns {@code null}.</p>
143   *
144   * <p>Overrides of this method must never return {@code null}.</p>
145   *
146   * @return a non-{@code null} {@link RankedComparator}
147   */
148  @Override
149  protected RankedComparator<ConfigurationValue> getComparator() {
150    return (RankedComparator<ConfigurationValue>)super.getComparator();
151  }
152
153  /**
154   * Returns {@code true} if this {@link
155   * ConfigurationValueSourceComparingArbiter} can effectively
156   * arbitrate the conflict represented by the supplied {@link
157   * Collection} of ambiguous {@link ConfigurationValue}s.
158   *
159   * @param ambiguousValues a {@link Collection} of {@link
160   * ConfigurationValue}s; may be {@code null}; must not be modified
161   * by overrides of this method
162   *
163   * @return {@code true} if this {@link
164   * ConfigurationValueSourceComparingArbiter} can effectively
165   * arbitrate the conflict represented by the supplied {@link
166   * Collection} of ambiguous {@link ConfigurationValue}s; {@code
167   * false} otherwise
168   */
169  protected boolean canArbitrate(final Collection<? extends ConfigurationValue> ambiguousValues) {
170    final boolean returnValue;
171    if (ambiguousValues == null || ambiguousValues.isEmpty()) {
172      returnValue = false;
173    } else {
174      boolean canArbitrate = true;
175      final RankedComparator<ConfigurationValue> comparator = this.getComparator();
176      assert comparator != null;
177      for (final ConfigurationValue value : ambiguousValues) {
178        if (value == null || !comparator.ranks(value)) {
179          canArbitrate = false;
180          break;
181        }
182      }
183      returnValue = canArbitrate;
184    }
185    return returnValue;
186  }
187
188}