1. /*
  2. * Copyright 2000-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package org.apache.tools.ant.types;
  18. import java.util.Stack;
  19. import org.apache.tools.ant.BuildException;
  20. import org.apache.tools.ant.Project;
  21. import org.apache.tools.ant.ProjectComponent;
  22. /**
  23. * Base class for those classes that can appear inside the build file
  24. * as stand alone data types.
  25. *
  26. * <p>This class handles the common description attribute and provides
  27. * a default implementation for reference handling and checking for
  28. * circular references that is appropriate for types that can not be
  29. * nested inside elements of the same type (i.e. <patternset>
  30. * but not <path>).</p>
  31. *
  32. */
  33. public abstract class DataType extends ProjectComponent {
  34. /**
  35. * The description the user has set.
  36. *
  37. * @deprecated The user should not be directly referencing
  38. * variable. Please use {@link #setDescription} or
  39. * {@link #getDescription} instead.
  40. */
  41. protected String description;
  42. /**
  43. * Value to the refid attribute.
  44. *
  45. * @deprecated The user should not be directly referencing
  46. * variable. Please use {@link #getRefid} instead.
  47. */
  48. protected Reference ref;
  49. /**
  50. * Are we sure we don't hold circular references?
  51. *
  52. * <p>Subclasses are responsible for setting this value to false
  53. * if we'd need to investigate this condition (usually because a
  54. * child element has been added that is a subclass of
  55. * DataType).</p>
  56. *
  57. * @deprecated The user should not be directly referencing
  58. * variable. Please use {@link #setChecked} or
  59. * {@link #isChecked} instead.
  60. */
  61. protected boolean checked = true;
  62. /**
  63. * Sets a description of the current data type. It will be useful
  64. * in commenting what we are doing.
  65. */
  66. public void setDescription(final String desc) {
  67. description = desc;
  68. }
  69. /**
  70. * Return the description for the current data type.
  71. */
  72. public String getDescription() {
  73. return description;
  74. }
  75. /**
  76. * Has the refid attribute of this element been set?
  77. */
  78. public boolean isReference() {
  79. return ref != null;
  80. }
  81. /**
  82. * Set the value of the refid attribute.
  83. *
  84. * <p>Subclasses may need to check whether any other attributes
  85. * have been set as well or child elements have been created and
  86. * thus override this method. if they do the must call
  87. * <code>super.setRefid</code>.</p>
  88. */
  89. public void setRefid(final Reference ref) {
  90. this.ref = ref;
  91. checked = false;
  92. }
  93. /**
  94. * Check to see whether any DataType we hold references to is
  95. * included in the Stack (which holds all DataType instances that
  96. * directly or indirectly reference this instance, including this
  97. * instance itself).
  98. *
  99. * <p>If one is included, throw a BuildException created by {@link
  100. * #circularReference circularReference}.</p>
  101. *
  102. * <p>This implementation is appropriate only for a DataType that
  103. * cannot hold other DataTypes as children.</p>
  104. *
  105. * <p>The general contract of this method is that it shouldn't do
  106. * anything if {@link #checked <code>checked</code>} is true and
  107. * set it to true on exit.</p>
  108. */
  109. protected void dieOnCircularReference(final Stack stack,
  110. final Project project)
  111. throws BuildException {
  112. if (checked || !isReference()) {
  113. return;
  114. }
  115. Object o = ref.getReferencedObject(project);
  116. if (o instanceof DataType) {
  117. if (stack.contains(o)) {
  118. throw circularReference();
  119. } else {
  120. stack.push(o);
  121. ((DataType) o).dieOnCircularReference(stack, project);
  122. stack.pop();
  123. }
  124. }
  125. checked = true;
  126. }
  127. /**
  128. * Performs the check for circular references and returns the
  129. * referenced object.
  130. */
  131. protected Object getCheckedRef(final Class requiredClass,
  132. final String dataTypeName) {
  133. if (!checked) {
  134. Stack stk = new Stack();
  135. stk.push(this);
  136. dieOnCircularReference(stk, getProject());
  137. }
  138. Object o = ref.getReferencedObject(getProject());
  139. if (!(requiredClass.isAssignableFrom(o.getClass()))) {
  140. String msg = ref.getRefId() + " doesn\'t denote a " + dataTypeName;
  141. throw new BuildException(msg);
  142. } else {
  143. return o;
  144. }
  145. }
  146. /**
  147. * Creates an exception that indicates that refid has to be the
  148. * only attribute if it is set.
  149. */
  150. protected BuildException tooManyAttributes() {
  151. return new BuildException("You must not specify more than one "
  152. + "attribute when using refid");
  153. }
  154. /**
  155. * Creates an exception that indicates that this XML element must
  156. * not have child elements if the refid attribute is set.
  157. */
  158. protected BuildException noChildrenAllowed() {
  159. return new BuildException("You must not specify nested elements "
  160. + "when using refid");
  161. }
  162. /**
  163. * Creates an exception that indicates the user has generated a
  164. * loop of data types referencing each other.
  165. */
  166. protected BuildException circularReference() {
  167. return new BuildException("This data type contains a circular "
  168. + "reference.");
  169. }
  170. protected boolean isChecked() {
  171. return checked;
  172. }
  173. protected void setChecked(final boolean checked) {
  174. this.checked = checked;
  175. }
  176. /**
  177. * get the reference set on this object
  178. * @return the reference or null
  179. */
  180. protected Reference getRefid() {
  181. return ref;
  182. }
  183. /**
  184. * check that it is ok to set attributes, i.e that no reference is defined
  185. * @since Ant 1.6
  186. * @throws BuildException if not allowed
  187. */
  188. protected void checkAttributesAllowed() {
  189. if (isReference()) {
  190. throw tooManyAttributes();
  191. }
  192. }
  193. /**
  194. * check that it is ok to add children, i.e that no reference is defined
  195. * @since Ant 1.6
  196. * @throws BuildException if not allowed
  197. */
  198. protected void checkChildrenAllowed() {
  199. if (isReference()) {
  200. throw noChildrenAllowed();
  201. }
  202. }
  203. }