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.resolver.internal.ant.tasks;
20  
21  import org.apache.tools.ant.BuildException;
22  import org.apache.tools.ant.ComponentHelper;
23  import org.apache.tools.ant.Project;
24  import org.apache.tools.ant.Task;
25  import org.apache.tools.ant.types.Reference;
26  
27  /**
28   * An abstract base class for Ant tasks that support referencing another task instance via {@code refid}.
29   * <p>
30   * This allows build scripts to reuse task definitions by referencing a previously defined instance.
31   * Subclasses that extend {@code RefTask} gain support for reference checking, attribute and child validation,
32   * and safe type casting of the referenced object.
33   * </p>
34   *
35   * <p>
36   * Tasks inheriting from {@code RefTask} should use {@link #checkAttributesAllowed()} and {@link #checkChildrenAllowed()}
37   * to enforce correct usage when a reference is set.
38   * </p>
39   *
40   */
41  public abstract class RefTask extends Task {
42  
43      private Reference ref;
44  
45      /**
46       * Default constructor for {@code RefTask}.
47       */
48      public RefTask() {
49          // Default constructor
50      }
51  
52      /**
53       * Checks whether this task is configured as a reference to another task.
54       *
55       * @return {@code true} if a {@code refid} was set, {@code false} otherwise
56       */
57      public boolean isReference() {
58          return ref != null;
59      }
60  
61      /**
62       * Sets a reference to another task instance defined elsewhere in the build file.
63       * <p>
64       * Once set, no other attributes or nested elements should be specified on this task.
65       * </p>
66       *
67       * @param ref the reference to another task
68       */
69      public void setRefid(final Reference ref) {
70          this.ref = ref;
71      }
72  
73      /**
74       * Verifies that attributes may still be set on this task.
75       * <p>
76       * If this task is configured as a reference, this method throws an exception.
77       * </p>
78       *
79       * @throws BuildException if {@code refid} is set
80       */
81      protected void checkAttributesAllowed() {
82          if (isReference()) {
83              throw tooManyAttributes();
84          }
85      }
86  
87      /**
88       * Verifies that nested child elements may be added to this task.
89       * <p>
90       * If this task is configured as a reference, this method throws an exception.
91       * </p>
92       *
93       * @throws BuildException if {@code refid} is set
94       */
95      protected void checkChildrenAllowed() {
96          if (isReference()) {
97              throw noChildrenAllowed();
98          }
99      }
100 
101     /**
102      * Constructs a {@link BuildException} to indicate that additional attributes are not allowed
103      * when {@code refid} is used.
104      *
105      * @return the build exception
106      */
107     protected BuildException tooManyAttributes() {
108         return new BuildException("You must not specify more than one " + "attribute when using refid");
109     }
110 
111     /**
112      * Constructs a {@link BuildException} to indicate that nested elements are not allowed
113      * when {@code refid} is used.
114      *
115      * @return the build exception
116      */
117     protected BuildException noChildrenAllowed() {
118         return new BuildException("You must not specify nested elements " + "when using refid");
119     }
120 
121     /**
122      * Returns the Ant data type or task name associated with this task.
123      * This is primarily used for error reporting and validation messages.
124      *
125      * @return the element name of this task
126      */
127     protected String getDataTypeName() {
128         return ComponentHelper.getElementName(getProject(), this, true);
129     }
130 
131     /**
132      * Resolves the reference set on this task and verifies it is of the same type as this class.
133      *
134      * @return the referenced object
135      * @throws BuildException if the reference is not set or is of an unexpected type
136      */
137     protected Object getCheckedRef() {
138         return getCheckedRef(getClass(), getDataTypeName(), getProject());
139     }
140 
141     /**
142      * Resolves and validates the reference set on this task.
143      * Ensures the referenced object is assignable to the specified class.
144      *
145      * @param requiredClass the expected class of the referenced object
146      * @param dataTypeName the Ant data type name used for error messages
147      * @param project the current Ant project
148      * @return the referenced object
149      *
150      * @throws BuildException if the reference is missing, the project is {@code null},
151      *         or the referenced object is of an incompatible type
152      */
153     protected Object getCheckedRef(final Class<?> requiredClass, final String dataTypeName, final Project project) {
154         if (project == null) {
155             throw new BuildException("No Project specified");
156         }
157         Object o = ref.getReferencedObject(project);
158         if (!(requiredClass.isAssignableFrom(o.getClass()))) {
159             log("Class " + o.getClass() + " is not a subclass of " + requiredClass, Project.MSG_VERBOSE);
160             String msg = ref.getRefId() + " doesn't denote a " + dataTypeName;
161             throw new BuildException(msg);
162         }
163         return o;
164     }
165 }