View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.internal.impl.di;
20  
21  import java.util.Collection;
22  import java.util.HashMap;
23  import java.util.LinkedList;
24  import java.util.Map;
25  import java.util.function.Supplier;
26  
27  import org.apache.maven.api.annotations.Nonnull;
28  import org.apache.maven.di.Key;
29  import org.apache.maven.di.Scope;
30  import org.apache.maven.di.impl.DIException;
31  
32  /**
33   * MojoExecutionScope
34   */
35  public class MojoExecutionScope implements Scope {
36  
37      protected static final class ScopeState {
38          private final Map<Key<?>, Supplier<?>> seeded = new HashMap<>();
39  
40          private final Map<Key<?>, Object> provided = new HashMap<>();
41  
42          public <T> void seed(Class<T> clazz, Supplier<T> value) {
43              seeded.put(Key.of(clazz), value);
44          }
45  
46          public Collection<Object> provided() {
47              return provided.values();
48          }
49      }
50  
51      private final ThreadLocal<LinkedList<ScopeState>> values = new ThreadLocal<>();
52  
53      public MojoExecutionScope() {}
54  
55      public static <T> Supplier<T> seededKeySupplier(Class<? extends T> clazz) {
56          return () -> {
57              throw new IllegalStateException(
58                      "No instance of " + clazz.getName() + " is bound to the mojo execution scope.");
59          };
60      }
61  
62      public void enter() {
63          LinkedList<ScopeState> stack = values.get();
64          if (stack == null) {
65              stack = new LinkedList<>();
66              values.set(stack);
67          }
68          stack.addFirst(new ScopeState());
69      }
70  
71      protected ScopeState getScopeState() {
72          LinkedList<ScopeState> stack = values.get();
73          if (stack == null || stack.isEmpty()) {
74              throw new IllegalStateException();
75          }
76          return stack.getFirst();
77      }
78  
79      public void exit() {
80          final LinkedList<ScopeState> stack = values.get();
81          if (stack == null || stack.isEmpty()) {
82              throw new IllegalStateException();
83          }
84          stack.removeFirst();
85          if (stack.isEmpty()) {
86              values.remove();
87          }
88      }
89  
90      public <T> void seed(Class<T> clazz, Supplier<T> value) {
91          getScopeState().seed(clazz, value);
92      }
93  
94      public <T> void seed(Class<T> clazz, final T value) {
95          seed(clazz, (Supplier<T>) () -> value);
96      }
97  
98      @SuppressWarnings("unchecked")
99      @Nonnull
100     public <T> Supplier<T> scope(@Nonnull Key<T> key, @Nonnull Supplier<T> unscoped) {
101         return () -> {
102             LinkedList<ScopeState> stack = values.get();
103             if (stack == null || stack.isEmpty()) {
104                 throw new DIException("Cannot access " + key + " outside of a scoping block");
105             }
106 
107             ScopeState state = stack.getFirst();
108 
109             Supplier<?> seeded = state.seeded.get(key);
110 
111             if (seeded != null) {
112                 return (T) seeded.get();
113             }
114 
115             T provided = (T) state.provided.get(key);
116             if (provided == null && unscoped != null) {
117                 provided = unscoped.get();
118                 state.provided.put(key, provided);
119             }
120 
121             return provided;
122         };
123     }
124 }