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    }