001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2024–2025 microBean™.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
006 * the License. You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
011 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
012 * specific language governing permissions and limitations under the License.
013 */
014package org.microbean.bean;
015
016import java.lang.System.Logger;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.Comparator;
021import java.util.List;
022import java.util.Set;
023
024import static java.lang.System.getLogger;
025import static java.util.Collections.sort;
026import static java.util.HashSet.newHashSet;
027
028/**
029 * Utility methods for working with {@link Bean}s.
030 *
031 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
032 *
033 * @see #normalize(Collection)
034 *
035 * @see Bean
036 */
037public final class Beans {
038
039
040  /*
041   * Static fields.
042   */
043
044
045  private static final Logger LOGGER = getLogger(Beans.class.getName());
046
047  private static final Comparator<Bean<?>> byRankComparator = Comparator
048    .<Bean<?>>comparingInt(Ranked::rank)
049    .reversed();
050
051  private static final Comparator<Bean<?>> byAlternateThenByRankComparator = Comparator
052    .<Bean<?>, Boolean>comparing(Ranked::alternate)
053    .reversed()
054    .thenComparing(byRankComparator);
055
056
057  /*
058   * Constructors.
059   */
060
061
062  private Beans() {
063    super();
064  }
065
066
067  /*
068   * Static methods.
069   */
070
071
072  /**
073   * Returns an immutable {@link List} containing a subset of {@linkplain Bean#equals(Object) distinct elements}
074   * contained in the supplied {@link Collection}, sorted in a deliberately unspecified fashion.
075   *
076   * @param beans a {@link Collection} of {@link Bean}s; must not be {@code null}
077   *
078   * @return a non-{@code null} immutable {@link List}
079   *
080   * @exception NullPointerException if {@code beans} is {@code null}
081   *
082   * @see Bean#equals(Object)
083   */
084  public static List<Bean<?>> normalize(final Collection<? extends Bean<?>> beans) {
085    if (beans.isEmpty()) {
086      return List.of();
087    } else if (beans.size() == 1) {
088      // no duplicate weeding or sorting needed
089      return List.copyOf(beans);
090    }
091    final ArrayList<Bean<?>> newBeans = new ArrayList<>(beans.size());
092    if (beans instanceof Set) {
093      newBeans.addAll(beans);
094    } else {
095      final Set<Bean<?>> newBeansSet = newHashSet(beans.size());
096      for (final Bean<?> bean : beans) {
097        if (newBeansSet.add(bean)) {
098          newBeans.add(bean);
099        }
100      }
101      if (newBeans.isEmpty()) {
102        return List.of();
103      }
104    }
105    assert !newBeans.isEmpty();
106    if (newBeans.size() > 1) {
107      // Sort
108      sort(newBeans, byAlternateThenByRankComparator);
109    }
110    return List.copyOf(newBeans);
111  }
112
113}