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}