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.rat.configuration.builders;
20  
21  import java.util.Map;
22  
23  import org.apache.rat.ConfigurationException;
24  import org.apache.rat.analysis.IHeaderMatcher;
25  import org.apache.rat.analysis.IHeaders;
26  import org.apache.rat.config.parameters.ComponentType;
27  import org.apache.rat.config.parameters.ConfigComponent;
28  import org.apache.rat.config.parameters.MatcherBuilder;
29  
30  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
31  
32  /**
33   * A reference matching Matcher builder.
34   * <p>
35   * This class stores a matcher id as a reference to the matcher. It also has a
36   * map of matcher ids to the matcher instances. When {@code build()} is called the matcher
37   * reference is looked up in the map. If it is found then its value is returned
38   * from the {@code build()} call. If the reference is not located then a
39   * IHeaderMatcherProxy is returned. the IHeaderMatcherProxy is resolved in a
40   * later configuration construction phase.
41   */
42  @MatcherBuilder(MatcherRefBuilder.IHeaderMatcherProxy.class)
43  public class MatcherRefBuilder extends AbstractBuilder {
44      /** The matcher id that this builder references */
45      private String referenceId;
46      /** The map of matcher id to matcher maintained by the system. Used for lookup. */
47      private Map<String, IHeaderMatcher> matchers;
48  
49      /**
50       * Constructs the MatcherReferenceBuilder using the provided reference id.
51       *
52       * @param refId the reverence to the matcher id.
53       * @return this builder for chaining.
54       */
55      public MatcherRefBuilder setRefId(final String refId) {
56          // this method is called by reflection
57          this.referenceId = refId;
58          return this;
59      }
60  
61      /**
62       * Set the Map of matcher ids to matcher instances.
63       *
64       * @param matchers the Map of ids to instances.
65       * @return this builder for chaining.
66       */
67      @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "Expected external update of matchers.")
68      public MatcherRefBuilder setMatcherMap(final Map<String, IHeaderMatcher> matchers) {
69          // this method is called by reflection
70          this.matchers = matchers;
71          return this;
72      }
73  
74      @Override
75      public IHeaderMatcher build() {
76          if (matchers == null) {
77              throw new ConfigurationException("'matchers' not set");
78          }
79          IHeaderMatcher result = matchers.get(referenceId);
80          return result != null ? result : new IHeaderMatcherProxy(referenceId, matchers);
81      }
82  
83      @Override
84      public String toString() {
85          return "MatcherRefBuilder: " + referenceId;
86      }
87  
88      /**
89       * A class that is a proxy to the actual matcher. It retrieves the actual
90       * matcher from the map of matcher ids to matcher instances on the first use of
91       * the matcher. This allows earlier read matchers to reference later constructed
92       * matchers as long as all the matchers are constructed before the earlier one
93       * is used.
94       */
95      @ConfigComponent(type = ComponentType.MATCHER, name = "matcherRef", desc = "A pointer to another Matcher")
96      public static class IHeaderMatcherProxy implements IHeaderMatcher {
97          /**
98           * The reference id (aka proxyId) for the reference.
99           */
100         @ConfigComponent(type = ComponentType.PARAMETER, name = "refId", desc = "Reference to an existing matcher", required = true)
101         private final String proxyId;
102         /** The header matcher that this proxy points to */
103         private IHeaderMatcher wrapped;
104         /** The map of reference IDs to matchers that is maintained in the build environment. Used for lookup */
105         @ConfigComponent(type = ComponentType.BUILD_PARAMETER, name = "matcherMap", desc = "Map of matcher names to matcher instances")
106         private Map<String, IHeaderMatcher> matchers;
107 
108         /**
109          * Constructor. The matchers map should be a reference to an object that will be
110          * updated by later processing of matcher definitions.
111          *
112          * @param proxyId the id of the matcher to find.
113          * @param matchers a mapping of matchers that have been found.
114          */
115         @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "Expected external update of matchers.")
116         public IHeaderMatcherProxy(final String proxyId, final Map<String, IHeaderMatcher> matchers) {
117             this.proxyId = proxyId;
118             this.matchers = matchers;
119         }
120 
121         private void checkProxy() {
122             if (wrapped == null) {
123                 wrapped = matchers.get(proxyId);
124                 if (wrapped == null) {
125                     throw new IllegalStateException(String.format("%s is not a valid matcher id", proxyId));
126                 }
127                 matchers = null;
128             }
129         }
130 
131         @Override
132         public String getId() {
133             checkProxy();
134             return wrapped.getId();
135         }
136 
137         @Override
138         public void reset() {
139             checkProxy();
140             wrapped.reset();
141         }
142 
143         @Override
144         public boolean matches(final IHeaders header) {
145             checkProxy();
146             return wrapped.matches(header);
147         }
148 
149         /**
150          * Gets the matcher ID that this proxy references.
151          * @return The matcher ID that this proxy references.
152          */
153         public String getRefId() {
154             // called by introspection
155             return proxyId;
156         }
157     }
158 }