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 }