1 package com.github.javaparser.metamodel; 2 3 import com.github.javaparser.ast.Node; 4 5 import java.lang.reflect.Field; 6 import java.util.Optional; 7 8 import static com.github.javaparser.utils.CodeGenerationUtils.getterName; 9 import static com.github.javaparser.utils.CodeGenerationUtils.setterName; 10 11 /** 12 * Meta-data about a property of a node in the AST. 13 */ 14 public class PropertyMetaModel { 15 private final BaseNodeMetaModel containingNodeMetaModel; 16 private final String name; 17 private final Class<?> type; 18 private final Optional<BaseNodeMetaModel> nodeReference; 19 private final boolean isOptional; 20 private final boolean isNonEmpty; 21 private final boolean isNodeList; 22 private final boolean isEnumSet; 23 private final boolean hasWildcard; 24 25 public PropertyMetaModel(BaseNodeMetaModel containingNodeMetaModel, String name, Class<?> type, Optional<BaseNodeMetaModel> nodeReference, boolean isOptional, boolean isNonEmpty, boolean isNodeList, boolean isEnumSet, boolean hasWildcard) { 26 this.containingNodeMetaModel = containingNodeMetaModel; 27 this.name = name; 28 this.type = type; 29 this.nodeReference = nodeReference; 30 this.isOptional = isOptional; 31 this.isNonEmpty = isNonEmpty; 32 this.isNodeList = isNodeList; 33 this.isEnumSet = isEnumSet; 34 this.hasWildcard = hasWildcard; 35 } 36 37 /** 38 * @return is this the field fieldName on class c? 39 */ 40 public boolean is(Class<? extends Node> c, String fieldName) { 41 return containingNodeMetaModel.is(c) && name.equals(fieldName); 42 } 43 44 /** 45 * @return is this fields called fieldName? 46 */ 47 public boolean is(String fieldName) { 48 return name.equals(fieldName); 49 } 50 51 /** 52 * @return the name used in the AST for the setter 53 */ 54 public String getSetterMethodName() { 55 return setterName(name); 56 } 57 58 /** 59 * @return the name used in the AST for the getter 60 */ 61 public String getGetterMethodName() { 62 return getterName(type, name); 63 } 64 65 /** 66 * @return the NodeMetaModel that "has" this property. 67 */ 68 public BaseNodeMetaModel getContainingNodeMetaModel() { 69 return containingNodeMetaModel; 70 } 71 72 /** 73 * @return the name of the property. This is equal to the name of the field in the AST. 74 */ 75 public String getName() { 76 return name; 77 } 78 79 /** 80 * @return if this property is a String or a NodeList: whether it may be empty. 81 */ 82 public boolean isNonEmpty() { 83 return isNonEmpty; 84 } 85 86 /** 87 * @return the class of the field. 88 */ 89 public Class<?> getType() { 90 return type; 91 } 92 93 /** 94 * @return if this property is a Node, this will get the node meta model. 95 */ 96 public Optional<BaseNodeMetaModel> getNodeReference() { 97 return nodeReference; 98 } 99 100 /** 101 * @return whether this property is optional. 102 */ 103 public boolean isOptional() { 104 return isOptional; 105 } 106 107 /** 108 * @return whether this property is not optional. 109 */ 110 public boolean isRequired() { 111 return !isOptional; 112 } 113 114 /** 115 * @return whether this property is contained in a NodeList. 116 */ 117 public boolean isNodeList() { 118 return isNodeList; 119 } 120 121 /** 122 * @return whether this property is contained in an EnumSet. 123 */ 124 public boolean isEnumSet() { 125 return isEnumSet; 126 } 127 128 /** 129 * @return whether this property has a wildcard following it, like BodyDeclaration<?>. 130 */ 131 public boolean hasWildcard() { 132 return hasWildcard; 133 } 134 135 /** 136 * @return whether this property is not a list or set. 137 */ 138 public boolean isSingular() { 139 return !(isNodeList || isEnumSet); 140 } 141 142 @Override 143 public String toString() { 144 return "(" + getTypeName() + ")\t" + containingNodeMetaModel + "#" + name; 145 } 146 147 @Override 148 public boolean equals(Object o) { 149 if (this == o) return true; 150 if (o == null || getClass() != o.getClass()) return false; 151 152 PropertyMetaModel that = (PropertyMetaModel) o; 153 154 if (!name.equals(that.name)) return false; 155 if (!type.equals(that.type)) return false; 156 157 return true; 158 } 159 160 @Override 161 public int hashCode() { 162 int result = name.hashCode(); 163 result = 31 * result + type.hashCode(); 164 return result; 165 } 166 167 /** 168 * @return the type of a single element of this property, so no Optional or NodeList or EnumSet. 169 */ 170 public String getTypeNameGenerified() { 171 if (hasWildcard) { 172 return getTypeName() + "<?>"; 173 } 174 return getTypeName(); 175 } 176 177 /** 178 * @return the raw type of a single element of this property, so nothing but the name. 179 */ 180 public String getTypeName() { 181 return type.getSimpleName(); 182 } 183 184 /** 185 * @return the type that is returned from getters in the AST. 186 */ 187 public String getTypeNameForGetter() { 188 if (isOptional) { 189 return "Optional<" + getTypeNameForSetter() + ">"; 190 } 191 return getTypeNameForSetter(); 192 } 193 194 /** 195 * @return the type that is passed to setters in the AST. 196 */ 197 public String getTypeNameForSetter() { 198 if (isNodeList) { 199 return "NodeList<" + getTypeNameGenerified() + ">"; 200 } 201 if (isEnumSet) { 202 return "EnumSet<" + getTypeNameGenerified() + ">"; 203 } 204 return getTypeNameGenerified(); 205 } 206 207 /** 208 * @return is this property an AST Node? 209 */ 210 public boolean isNode() { 211 return getNodeReference().isPresent(); 212 } 213 214 /** 215 * The name of the field in the containing BaseNodeMetaModel for this property meta model. 216 */ 217 public String getMetaModelFieldName() { 218 return getName() + "PropertyMetaModel"; 219 } 220 221 /** 222 * @return is this property an attribute, meaning: not a node? 223 */ 224 public boolean isAttribute() { 225 return !isNode(); 226 } 227 228 /** 229 * Introspects the node to get the value from this field. 230 * Note that an optional empty field will return null here. 231 */ 232 public Object getValue(Node node) { 233 try { 234 for (Class<?> c = node.getClass(); c != null; c = c.getSuperclass()) { 235 Field[] fields = c.getDeclaredFields(); 236 for (Field classField : fields) { 237 if (classField.getName().equals(getName())) { 238 classField.setAccessible(true); 239 return classField.get(node); 240 } 241 } 242 } 243 throw new NoSuchFieldError(getName()); 244 } catch (IllegalAccessException e) { 245 throw new RuntimeException(e); 246 } 247 } 248 } 249