001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  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 implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.bean.rfc7807;
018
019import static org.apache.juneau.commons.utils.CollectionUtils.*;
020import static org.apache.juneau.commons.utils.Shorts.*;
021
022import java.util.*;
023
024/**
025 * Aggregator bean for registering multiple {@link ProblemMapper}s with a single bean-store entry.
026 *
027 * <p>
028 * The problem-details processor resolves only the <em>unnamed</em> {@link ProblemMapper} from the bean store, so a
029 * second {@code @Bean public ProblemMapper foo()} factory method (even a differently named one) would register a
030 * distinct bean that the processor never consults. To register more than one mapper, declare a single
031 * {@code @Bean public ProblemMapperList mappers()} factory and let the list carry the dispatch order:
032 *
033 * <p class='bjava'>
034 *    <ja>@Rest</ja>(problemDetails=<js>"true"</js>)
035 *    <jk>public class</jk> MyResource {
036 *
037 *       <ja>@Bean</ja> <jk>public</jk> ProblemMapperList problemMappers() {
038 *          <jk>return</jk> ProblemMapperList.<jsm>of</jsm>(<jk>new</jk> InsufficientCreditMapper(), <jk>new</jk> NotFoundMapper());
039 *       }
040 *    }
041 * </p>
042 *
043 * <p>
044 * For the single-mapper case the conventional pattern still works &mdash;
045 * {@code @Bean public ProblemMapper<MyException> mapper()} registers under {@code ProblemMapper.class}
046 * and the server-side processor picks it up via {@code BeanStore.getBean(ProblemMapper.class)} without
047 * needing a list wrapper.
048 *
049 * <h5 class='section'>See Also:</h5><ul>
050 *    <li class='jc'>{@link ProblemMapper}
051 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerProblemDetails">RFC 7807 / 9457 Problem Details</a>
052 * </ul>
053 */
054public class ProblemMapperList {
055
056   private final List<ProblemMapper<?>> mappers;
057
058   /**
059    * Creates an empty list. Mappers can be added via {@link #append(ProblemMapper)} /
060    * {@link #append(Collection)}.
061    */
062   public ProblemMapperList() {
063      mappers = l();
064   }
065
066   /**
067    * Creates a list populated with the supplied mappers in argument order.
068    *
069    * @param mappers The mappers to add. Can be <jk>null</jk>, and <jk>null</jk> entries are skipped; duplicates are allowed and preserved.
070    * @return A new {@link ProblemMapperList} containing the supplied mappers.
071    */
072   public static ProblemMapperList of(ProblemMapper<?>... mappers) {
073      var l = new ProblemMapperList();
074      addAllNn(l.mappers, mappers);
075      return l;
076   }
077
078   /**
079    * Appends a mapper to the end of the list.
080    *
081    * @param mapper The mapper to add. Can be <jk>null</jk> (no-op).
082    * @return This object for fluent chaining.
083    */
084   public ProblemMapperList append(ProblemMapper<?> mapper) {
085      if (mapper != null)
086         mappers.add(mapper);
087      return this;
088   }
089
090   /**
091    * Appends a collection of mappers to the end of the list, in iteration order.
092    *
093    * @param values The mappers to add. Can be <jk>null</jk>, and <jk>null</jk> entries are skipped.
094    * @return This object for fluent chaining.
095    */
096   public ProblemMapperList append(Collection<? extends ProblemMapper<?>> values) {
097      addAllNn(mappers, values);
098      return this;
099   }
100
101   /**
102    * Returns the registered mappers in their declared order.
103    *
104    * @return An unmodifiable view of the underlying list. Never {@code null}.
105    */
106   public List<ProblemMapper<?>> asList() {
107      return u(mappers);
108   }
109
110   /**
111    * Returns {@code true} when this list contains no mappers.
112    *
113    * @return {@code true} when {@link #asList()} is empty.
114    */
115   public boolean isEmpty() {
116      return mappers.isEmpty();
117   }
118}