001 package org.mesopotamia.lang.java; 002 003 import java.util.ArrayList; 004 import java.util.Iterator; 005 import java.util.List; 006 import java.util.logging.Logger; 007 008 import org.mesopotamia.LanguageElement; 009 import org.mesopotamia.MesopotamiaException; 010 import org.mesopotamia.NodeData; 011 import org.mesopotamia.RepositoryLanguage; 012 import org.mesopotamia.Scan; 013 import org.mesopotamia.SimpleLanguageElement; 014 import org.mesopotamia.SourceUnit; 015 import org.mesopotamia.lang.java.ref.MethodInfo; 016 import org.mesopotamia.lang.java.ref.Scope; 017 import org.mesopotamia.lang.java.ref.SoftCachingTypeInfo; 018 import org.mesopotamia.lang.java.ref.TypeInfo; 019 import org.mesopotamia.lang.java.ref.TypeSpecInfo; 020 import org.mesopotamia.lang.java.ref.VariableInfo; 021 import org.w3c.dom.Element; 022 023 import biz.hammurapi.util.CollectionVisitable; 024 import biz.hammurapi.util.Visitor; 025 026 public class TypeDefinition extends Field implements TypeInfo, TypeDefinitionOrAnonymous { 027 private static final Logger logger = Logger.getLogger(TypeDefinition.class.getName()); 028 029 public TypeDefinition( 030 NodeData xData, 031 Class<?> context, 032 Scan scan, 033 RepositoryLanguage language, 034 Object environment) throws MesopotamiaException { 035 036 super(xData, context, scan, language, environment); 037 038 // Select attributes 039 Name = selectSingleElementText(Identifier.class, "IDENT|DOT"); 040 Fields = select(Field.class, "OBJBLOCK/*"); 041 042 } 043 044 public void toDom(Element holder) { 045 super.toDom(holder); 046 047 // Serialize attributes 048 setAttribute(holder, "Name", Name); 049 setElement(holder, "Fields", Fields); 050 } 051 052 // Attributes 053 private String Name; 054 055 private List<Field> Fields; 056 057 // Accessors 058 public String getName() { 059 return Name; 060 } 061 062 public List<Field> getFields() { 063 return Fields; 064 } 065 066 protected void acceptChildren(Visitor visitor) { 067 super.acceptChildren(visitor); 068 // Visiting non-text attributes 069 new CollectionVisitable(Fields, false).accept(visitor); 070 } 071 072 @SuppressWarnings("unchecked") 073 public String getFcn() { 074 if (!fcnCalculated) { 075 LanguageElement parent = getParent(); 076 if (parent instanceof SimpleLanguageElement && "OBJBLOCK".equals(parent.getTokenName())) { 077 parent = parent.getParent(); 078 } 079 if (parent instanceof TypeDefinition) { 080 fcn = ((TypeDefinition) parent).getFcn()+"."+getName(); 081 } else if (parent instanceof JavaLanguageElement) { 082 fcn = ((JavaLanguageElement) parent).getFcn()+"."+getName(); 083 SourceUnit su = getSourceUnit(); 084 if (su instanceof JavaSourceUnit) { 085 ((JavaSourceUnit) su).registerLocalOrAnonymous(fcn, this); 086 } 087 } else if (parent==null) { 088 String packageName = ((JavaSourceUnit) getSourceUnit()).getPackageName(); 089 if (packageName.length()==0) { 090 fcn = getName(); 091 } else { 092 fcn = packageName+"."+getName(); 093 } 094 } else { 095 throw new IllegalStateException("Logical problem"); 096 } 097 098 fcnCalculated = true; 099 } 100 return fcn; 101 } 102 103 public MethodInfo findMethod(String name, String[] parameterTypes) { 104 MethodInfo candidate = findTypeMethod(name, parameterTypes); 105 106 if (candidate!=null) { 107 return candidate; // Does Java look for more specific methods in enclosed scope? 108 } 109 110 Scope ec = getEnclosingScope(); 111 return ec==null ? null : ec.findMethod(name, parameterTypes); 112 } 113 114 public static enum ParameterMatch { 115 NO_MATCH, 116 MATCH, 117 FULL_MATCH; 118 } 119 120 /** 121 * Matches formal parameter types with arguments for compatibility. 122 * @param parameters 123 * @param arguments 124 * @return Match result. Full match means that arguments are exactly of parmeter types, 125 * match means that some of them are subtypes of parameter types. 126 */ 127 public static ParameterMatch match(List<TypeSpecInfo> parameters, List<TypeSpecInfo> arguments) { 128 if (parameters.size()!=arguments.size()) { 129 return ParameterMatch.NO_MATCH; 130 } 131 132 boolean fullMatch = true; 133 Iterator<TypeSpecInfo> ppit = parameters.iterator(); 134 Iterator<TypeSpecInfo> apit = arguments.iterator(); 135 while (apit.hasNext() && ppit.hasNext()) { 136 TypeSpecInfo argument = apit.next(); 137 if (argument == null) { 138 return ParameterMatch.NO_MATCH; 139 } 140 TypeSpecInfo parameter = ppit.next(); 141 if (parameter == null) { 142 return ParameterMatch.NO_MATCH; 143 } 144 if (argument!=Null.NULL_TYPE_SPEC_INFO && argument.getDimensions()!=parameter.getDimensions()) { 145 if (parameter.getDimensions()==0 146 && parameter.getTypeInfo()!=null 147 && JAVA_LANG_OBJECT.equals(parameter.getTypeInfo().getFcn())) { 148 fullMatch = false; 149 continue; 150 } 151 return ParameterMatch.NO_MATCH; 152 } 153 154 TypeInfo ati = argument.getTypeInfo(); 155 if (ati==null) { 156 return ParameterMatch.NO_MATCH; 157 } 158 TypeInfo pti = parameter.getTypeInfo(); 159 if (pti == null) { 160 return ParameterMatch.NO_MATCH; 161 } 162 if (!ati.isKindOf(pti.getFcn())) { 163 return ParameterMatch.NO_MATCH; 164 } 165 if (!pti.getFcn().equals(ati.getFcn())) { 166 fullMatch = false; 167 } 168 } 169 return fullMatch ? ParameterMatch.FULL_MATCH : ParameterMatch.MATCH; 170 } 171 172 /** 173 * @param m0 174 * @param m1 175 * @return m0 or m1 if m1 is more specific than m0. This method doesn't handle ambiguity. 176 */ 177 public static MethodInfo selectMoreSpecific(MethodInfo m0, MethodInfo m1) { 178 if (m0==null) { 179 return m1; 180 } 181 if (m1==null) { 182 return m0; 183 } 184 185 ParameterMatch cmr = match(m0.getParameterTypes(), m1.getParameterTypes()); 186 return cmr==ParameterMatch.MATCH ? m1 : m0; 187 } 188 189 protected Scope getEnclosingScope() { 190 if (getParent()==null) { 191 SourceUnit su = getSourceUnit(); 192 return su instanceof JavaSourceUnit ? (JavaSourceUnit) su : null; 193 } 194 195 return findParent(Scope.class); 196 } 197 198 public TypeInfo findType(String name) { 199 TypeInfo ret = findNestedType(name); 200 if (ret!=null) { 201 return ret; 202 } 203 204 Scope ec = getEnclosingScope(); 205 return ec==null ? null : ec.findType(name); 206 } 207 208 public VariableInfo findVariable(String name) { 209 VariableInfo ret = findTypeVariable(name); 210 if (ret!=null) { 211 return ret; 212 } 213 214 Scope ec = getEnclosingScope(); 215 return ec==null ? null : ec.findVariable(name); 216 } 217 218 protected boolean isKindOfInternal(String superFcn) { 219 return JAVA_LANG_OBJECT.equals(superFcn) || superFcn.equals(getFcn()); 220 } 221 222 protected TypeInfo findNestedTypeInternal(String name) { 223 for (Field field: getFields()) { 224 if (field instanceof TypeDefinition) { 225 final TypeDefinition td = (TypeDefinition) field; 226 if (td.getName().equals(name) || td.getFcn().equals(name)) { 227 return td; 228 } 229 } 230 } 231 232 return null; // Subclasses traverse supers 233 } 234 235 protected MethodInfo findTypeMethodInternal(String name, String[] argumentTypes) { 236 List<TypeSpecInfo> aTypes=new ArrayList<TypeSpecInfo>(); 237 boolean pTypesResolved = true; 238 for (int i=0; argumentTypes!=null && i<argumentTypes.length; ++i) { 239 if (argumentTypes[i]==null) { 240 StringBuffer sig = new StringBuffer(); 241 for (String pt: argumentTypes) { 242 sig.append(sig.length()==0 ? "(" : ", "); 243 sig.append(pt); 244 } 245 sig.append(")"); 246 logger.warning("Could not resolve method "+name+sig.toString()+" argument "+i+" type ["+getFcn()+", "+getLocation()+"]"); 247 aTypes.add(null); 248 pTypesResolved = false; 249 } else { 250 int dimensions=0; 251 String typeName = argumentTypes[i]; 252 while (typeName.endsWith(TypeSpecInfo.ARRAY_BRACKETS)) { 253 ++dimensions; 254 typeName=typeName.substring(0, typeName.length()-TypeSpecInfo.ARRAY_BRACKETS.length()); 255 } 256 final TypeInfo ptInfo = findType(typeName); 257 if (ptInfo==null) { 258 logger.warning("Type "+ptInfo+" not found"); 259 return null; 260 } 261 final int d = dimensions; 262 aTypes.add(new TypeSpecInfo() { 263 264 public int getDimensions() { 265 return d; 266 } 267 268 public TypeInfo getTypeInfo() { 269 return ptInfo; 270 } 271 272 }); 273 } 274 } 275 276 MethodInfo pnumMatch = null; 277 MethodInfo candidate = null; 278 for (Field field: getFields()) { 279 if (field instanceof MethodDefinition) { 280 MethodDefinition method = (MethodDefinition) field; 281 if (!method.getName().equals(name)) { 282 continue; 283 } 284 285 List<TypeSpecInfo> mTypes = method.getParameterTypes(); 286 ParameterMatch matchResult = match(mTypes, aTypes); 287 if (matchResult == ParameterMatch.FULL_MATCH) { 288 return method; 289 } else if (matchResult == ParameterMatch.MATCH) { 290 candidate = selectMoreSpecific(candidate, method); 291 } else if (!pTypesResolved && pnumMatch==null) { 292 pnumMatch = method; 293 } 294 } 295 } 296 297 return candidate==null ? pnumMatch : candidate; 298 } 299 300 protected VariableInfo findTypeVariableInternal(String name) { 301 for (Field field: getFields()) { 302 if (field instanceof VariableDefinition) { 303 final VariableDefinition vd = (VariableDefinition) field; 304 if (vd.getName().equals(name)) { 305 return vd; 306 } 307 } 308 } 309 310 return null; // Subclasses traverse supers 311 } 312 313 public TypeInfo getDeclaringType() { 314 LanguageElement parent = getParent(); 315 return parent instanceof TypeDefinition ? (TypeDefinition) parent : null; 316 } 317 318 private TypeInfo cachingTypeInfo = new SoftCachingTypeInfo() { 319 320 @Override 321 protected MethodInfo findConstructorInternal(String[] argumentTypes) { 322 boolean pTypesResolved = true; 323 List<TypeSpecInfo> aTypes=new ArrayList<TypeSpecInfo>(); 324 for (int i=0; argumentTypes!=null && i<argumentTypes.length; ++i) { 325 if (argumentTypes[i]==null) { 326 StringBuffer sig = new StringBuffer(); 327 for (String pt: argumentTypes) { 328 sig.append(sig.length()==0 ? "(" : ", "); 329 sig.append(pt); 330 } 331 sig.append(")"); 332 logger.warning("Could not resolve constructor "+getName()+sig.toString()+" argument "+i+" type ["+getFcn()+", "+getLocation()+"]"); 333 aTypes.add(null); 334 pTypesResolved = false; 335 } else { 336 int dimensions=0; 337 String typeName = argumentTypes[i]; 338 while (typeName.endsWith(TypeSpecInfo.ARRAY_BRACKETS)) { 339 ++dimensions; 340 typeName=typeName.substring(0, typeName.length()-TypeSpecInfo.ARRAY_BRACKETS.length()); 341 } 342 final TypeInfo ptInfo = findType(typeName); 343 if (ptInfo==null) { 344 logger.warning("Type "+ptInfo+" not found"); 345 return null; 346 } 347 final int d = dimensions; 348 aTypes.add(new TypeSpecInfo() { 349 350 public int getDimensions() { 351 return d; 352 } 353 354 public TypeInfo getTypeInfo() { 355 return ptInfo; 356 } 357 358 }); 359 } 360 } 361 362 MethodInfo pnumMatch = null; 363 MethodInfo candidate = null; 364 for (Field field: getFields()) { 365 if (field instanceof ConstructorDefinition) { 366 ConstructorDefinition constructor = (ConstructorDefinition) field; 367 368 List<TypeSpecInfo> mTypes = constructor.getParameterTypes(); 369 ParameterMatch matchResult = match(mTypes, aTypes); 370 if (matchResult == ParameterMatch.FULL_MATCH) { 371 return constructor; 372 } else if (matchResult == ParameterMatch.MATCH) { 373 candidate = selectMoreSpecific(candidate, constructor); 374 } else if (!pTypesResolved && pnumMatch==null) { 375 pnumMatch = constructor; 376 } 377 } 378 } 379 380 return candidate == null ? pnumMatch : candidate; 381 } 382 383 @Override 384 protected MethodInfo findMethodInternal(String name, String[] parameterTypes) { 385 return TypeDefinition.this.findTypeMethodInternal(name, parameterTypes); 386 } 387 388 @Override 389 protected TypeInfo findNestedTypeInternal(String name) { 390 return TypeDefinition.this.findNestedTypeInternal(name); 391 } 392 393 @Override 394 protected VariableInfo findTypeVariableInternal(String name) { 395 return TypeDefinition.this.findTypeVariableInternal(name); 396 } 397 398 @Override 399 protected TypeInfo getDeclaringTypeInternal() { 400 throw new UnsupportedOperationException(); 401 } 402 403 @Override 404 protected String getFcnInternal() { 405 throw new UnsupportedOperationException(); 406 } 407 408 @Override 409 protected List<String> getModifiersInternal() { 410 throw new UnsupportedOperationException(); 411 } 412 413 @Override 414 protected boolean isKindOfInternal(String superFcn) { 415 return TypeDefinition.this.isKindOfInternal(superFcn); 416 } 417 418 public String getName() { 419 return TypeDefinition.this.getName(); 420 } 421 422 }; 423 424 public MethodInfo findConstructor(String[] argumentTypes) { 425 return cachingTypeInfo.findConstructor(argumentTypes); 426 } 427 428 public TypeInfo findNestedType(String name) { 429 return cachingTypeInfo.findNestedType(name); 430 } 431 432 public MethodInfo findTypeMethod(String name, String[] argumentTypes) { 433 return cachingTypeInfo.findTypeMethod(name, argumentTypes); 434 } 435 436 public VariableInfo findTypeVariable(String name) { 437 return cachingTypeInfo.findTypeVariable(name); 438 } 439 440 public boolean isKindOf(String superFcn) { 441 return cachingTypeInfo.isKindOf(superFcn); 442 } 443 444 }