001 /* 002 * Copyright 2000-2004 The Apache Software Foundation 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 */ 017 018 package org.apache.tools.ant.types; 019 020 021 import java.util.Stack; 022 import org.apache.tools.ant.BuildException; 023 import org.apache.tools.ant.Project; 024 import org.apache.tools.ant.ProjectComponent; 025 026 /** 027 * Base class for those classes that can appear inside the build file 028 * as stand alone data types. 029 * 030 * <p>This class handles the common description attribute and provides 031 * a default implementation for reference handling and checking for 032 * circular references that is appropriate for types that can not be 033 * nested inside elements of the same type (i.e. <patternset> 034 * but not <path>).</p> 035 * 036 */ 037 public abstract class DataType extends ProjectComponent { 038 /** 039 * The description the user has set. 040 * 041 * @deprecated The user should not be directly referencing 042 * variable. Please use {@link #setDescription} or 043 * {@link #getDescription} instead. 044 */ 045 protected String description; 046 047 /** 048 * Value to the refid attribute. 049 * 050 * @deprecated The user should not be directly referencing 051 * variable. Please use {@link #getRefid} instead. 052 */ 053 protected Reference ref; 054 055 /** 056 * Are we sure we don't hold circular references? 057 * 058 * <p>Subclasses are responsible for setting this value to false 059 * if we'd need to investigate this condition (usually because a 060 * child element has been added that is a subclass of 061 * DataType).</p> 062 * 063 * @deprecated The user should not be directly referencing 064 * variable. Please use {@link #setChecked} or 065 * {@link #isChecked} instead. 066 */ 067 protected boolean checked = true; 068 069 /** 070 * Sets a description of the current data type. It will be useful 071 * in commenting what we are doing. 072 */ 073 public void setDescription(final String desc) { 074 description = desc; 075 } 076 077 /** 078 * Return the description for the current data type. 079 */ 080 public String getDescription() { 081 return description; 082 } 083 084 /** 085 * Has the refid attribute of this element been set? 086 */ 087 public boolean isReference() { 088 return ref != null; 089 } 090 091 /** 092 * Set the value of the refid attribute. 093 * 094 * <p>Subclasses may need to check whether any other attributes 095 * have been set as well or child elements have been created and 096 * thus override this method. if they do the must call 097 * <code>super.setRefid</code>.</p> 098 */ 099 public void setRefid(final Reference ref) { 100 this.ref = ref; 101 checked = false; 102 } 103 104 /** 105 * Check to see whether any DataType we hold references to is 106 * included in the Stack (which holds all DataType instances that 107 * directly or indirectly reference this instance, including this 108 * instance itself). 109 * 110 * <p>If one is included, throw a BuildException created by {@link 111 * #circularReference circularReference}.</p> 112 * 113 * <p>This implementation is appropriate only for a DataType that 114 * cannot hold other DataTypes as children.</p> 115 * 116 * <p>The general contract of this method is that it shouldn't do 117 * anything if {@link #checked <code>checked</code>} is true and 118 * set it to true on exit.</p> 119 */ 120 protected void dieOnCircularReference(final Stack stack, 121 final Project project) 122 throws BuildException { 123 124 if (checked || !isReference()) { 125 return; 126 } 127 Object o = ref.getReferencedObject(project); 128 129 if (o instanceof DataType) { Rate130 if (stack.contains(o)) { 131 throw circularReference(); 132 } else { 133 stack.push(o); 134 ((DataType) o).dieOnCircularReference(stack, project); 135 stack.pop(); 136 } 137 } 138 checked = true; 139 } 140 141 /** 142 * Performs the check for circular references and returns the 143 * referenced object. 144 */ 145 protected Object getCheckedRef(final Class requiredClass, 146 final String dataTypeName) { 147 if (!checked) { 148 Stack stk = new Stack(); 149 stk.push(this); 150 dieOnCircularReference(stk, getProject()); 151 } 152 153 Object o = ref.getReferencedObject(getProject()); 154 if (!(requiredClass.isAssignableFrom(o.getClass()))) { 155 String msg = ref.getRefId() + " doesn\'t denote a " + dataTypeName; 156 throw new BuildException(msg); 157 } else { 158 return o; 159 } 160 } 161 162 /** 163 * Creates an exception that indicates that refid has to be the 164 * only attribute if it is set. 165 */ 166 protected BuildException tooManyAttributes() { 167 return new BuildException("You must not specify more than one " 168 + "attribute when using refid"); 169 } 170 171 /** 172 * Creates an exception that indicates that this XML element must 173 * not have child elements if the refid attribute is set. 174 */ 175 protected BuildException noChildrenAllowed() { 176 return new BuildException("You must not specify nested elements " 177 + "when using refid"); 178 } 179 180 /** 181 * Creates an exception that indicates the user has generated a 182 * loop of data types referencing each other. 183 */ 184 protected BuildException circularReference() { 185 return new BuildException("This data type contains a circular " 186 + "reference."); 187 } 188 189 protected boolean isChecked() { 190 return checked; 191 } 192 193 protected void setChecked(final boolean checked) { 194 this.checked = checked; 195 } 196 197 /** 198 * get the reference set on this object 199 * @return the reference or null 200 */ 201 protected Reference getRefid() { 202 return ref; 203 } 204 205 /** 206 * check that it is ok to set attributes, i.e that no reference is defined 207 * @since Ant 1.6 208 * @throws BuildException if not allowed 209 */ 210 protected void checkAttributesAllowed() { 211 if (isReference()) { 212 throw tooManyAttributes(); 213 } 214 } 215 216 /** 217 * check that it is ok to add children, i.e that no reference is defined 218 * @since Ant 1.6 219 * @throws BuildException if not allowed 220 */ 221 protected void checkChildrenAllowed() { 222 if (isReference()) { 223 throw noChildrenAllowed(); 224 } 225 } 226 }