001    /*
002      Copyright (C) 2001-2003 Renaud Pawlak <renaud@aopsys.com>, 
003                              Laurent Martelli <laurent@aopsys.com>
004      
005      This program is free software; you can redistribute it and/or modify
006      it under the terms of the GNU Lesser General Public License as
007      published by the Free Software Foundation; either version 2 of the
008      License, or (at your option) any later version.
009    
010      This program is distributed in the hope that it will be useful,
011      but WITHOUT ANY WARRANTY; without even the implied warranty of
012      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013      GNU Lesser General Public License for more details.
014    
015      You should have received a copy of the GNU Lesser General Public License
016      along with this program; if not, write to the Free Software
017      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
018    
019    package org.objectweb.jac.core.rtti;
020    
021    import java.lang.reflect.*;
022    import java.util.Arrays;
023    import java.util.Vector;
024    
025    /**
026     * This class defines a meta item that corresponds to the
027     * <code>java.lang.reflect.Method</code> and to the
028     * <code>java.lang.reflect.Constructor</code> meta elements.
029     *
030     * <p>It appears that methods and contructors can be seen as
031     * semantically similar (a constructor is nothing else that a special
032     * static method that returns an object that is of the owner class
033     * type). Thus, we decide to wrap those two meta item into a unique
034     * one (on contrary to the Java choice)<p>
035     *
036     * @author Laurent Martelli
037     * @author Renaud Pawlak
038     */
039    
040    public abstract class AbstractMethodItem extends MemberItem {
041       
042        /**
043         * Default contructor to create a new abstract method item.<p>
044         *
045         * @param delegate the <code>java.lang.reflect</code> actual
046         * meta item */
047    
048        public AbstractMethodItem(Object delegate, ClassItem parent)
049            throws InvalidDelegateException 
050        {
051            super(delegate,parent);
052            isStatic = Modifier.isStatic(((Member)delegate).getModifiers());
053        }
054    
055        /**
056         * If the field does not have a value for the request attribute,
057         * tries on the superclass.
058         */
059        public final Object getAttribute(String name) {
060            Object value = super.getAttribute(name);
061            if (value==null) {
062                ClassItem parent = ((ClassItem)getParent()).getSuperclass();
063                if (parent!=null) {
064                    try {
065                        AbstractMethodItem parentMethod = parent.getAbstractMethod(getFullName());
066                        value = parentMethod.getAttribute(name);
067                    } catch (NoSuchMethodException e) {
068                        ClassItem[] interfaces = parent.getInterfaceItems();
069                        for (int i=0; i<interfaces.length && value==null;i++) {
070                            if (parent.hasMethod(getFullName())) {
071                                AbstractMethodItem parentMethod = parent.getAbstractMethod(getFullName());
072                                value = parentMethod.getAttribute(name);
073                            }      
074                        }
075                    }
076                }
077            }
078            return value;
079        }
080    
081        /**
082         * Gets the parameter types of this abstract method item.<p>
083         *
084         * @return the actual method parameter types
085         */
086        public abstract Class[] getParameterTypes();
087        
088        public void setParameter(Object[] params, int i, Object value) {
089            params[i] = value;
090        }
091    
092        public Object getParameter(Object[] params, int i) {
093            return params[i];
094        }
095    
096        /**
097         * Get the ClassItem of the type of a parameter of the method.
098         * @param n the number of the parameter
099         * @return the ClassItem of the type of the parameter
100         */
101        public ClassItem getParameterTypeItem(int n) {
102            return ClassRepository.get().getClass(getParameterTypes()[n]);
103        }
104    
105        /**
106         * Gets the number of parameters
107         *
108         * @return the number of parameters.
109         */
110        public int getParameterCount() {
111            return getParameterTypes().length;
112        }
113    
114        boolean isStatic = false;
115    
116        public final boolean isStatic() {
117            return isStatic;
118        }
119       
120        private String fullName;
121        /**
122         * Return the full method name, ie with parameter types.
123         *
124         * @return The full method name. For instance myMethod(java.lang.String,int)
125         * @see #getCompactFullName()
126         */
127        public String getFullName() {
128            if (fullName==null) {
129                fullName = getFullName(getName(),getParameterTypes());
130            }
131            return fullName;
132        }
133    
134        String realFullName;
135        /**
136         * Return the full method name, ie with parameter types.
137         *
138         * @return The full method name. For instance myMethod(java.lang.String,int)
139         * @see #getCompactFullName()
140         */
141        public String getRealFullName() {
142            if (realFullName==null) {
143                realFullName = getFullName(getName(),((Method)delegate).getParameterTypes());
144            }
145            return realFullName;
146        }
147    
148        public static String getFullName(String name, Class[] pts) {
149            StringBuffer ret = new StringBuffer(name.length()+2+pts.length*15);
150            ret.append(name);
151            ret.append('(');
152            for (int i=0; i<pts.length; i++) {
153                ret.append(NamingConventions.getStandardClassName(pts[i]));
154                if (i < pts.length-1) 
155                    ret.append(',');
156            }
157            ret.append(')');
158            return ret.toString();
159        }
160    
161        /**
162         * Return the full method name, ie with short parameter types.
163         *
164         * @return The full method name. For instance "myMethod(String,int)"
165         * @see #getFullName()
166         */
167        public String getCompactFullName() {
168            //.substring(0, getName().lastIndexOf('.'))
169            Class[] pts = getParameterTypes();
170            StringBuffer ret = new StringBuffer(getName().length()+2+pts.length*15);
171            ret.append(getName());
172            ret.append('(');
173            for (int i=0; i<pts.length; i++) {
174                ret.append(NamingConventions.getShortClassName(pts[i]));
175                if (i < pts.length-1) 
176                    ret.append(',');
177            }
178            ret.append(')');
179            return ret.toString();
180        }
181    
182        private String longName;
183        public String getLongName() {
184            if (longName==null)
185                longName = parent.getName()+"."+getFullName();
186            return longName;
187        }
188    
189        public Object invoke(Object object, Object[] parameters) 
190        {
191            if (true) {
192                throw new RuntimeException(
193                    "wrong invocation on an abstract method "+this);
194            }
195            return null;
196        }
197    
198        AbstractMethodItem concreteMethod;
199    
200        /**
201         * Returns the method item who really holds the byte code. 
202         */
203        public AbstractMethodItem getConcreteMethod() {
204            if (concreteMethod==null) {
205                Member m = (Member)delegate;
206                if (parent.getDelegate() == m.getDeclaringClass()) {
207                    return this;
208                }
209                concreteMethod = ClassRepository.get().getClass(m.getDeclaringClass()).
210                    getAbstractMethod(getRealFullName());
211            }
212            return concreteMethod;
213        }
214    
215        /**
216         * Returns the owning class of this method.
217         */
218        public ClassItem getOwningClass() {
219            return (ClassItem)parent;
220        }
221    
222        public abstract boolean isAdder();
223        public abstract CollectionItem[] getAddedCollections();
224        /**
225         * Get the value of the collection that is added by this
226         * method (the method is the unique adder of the collection).<p>
227         *
228         * @return value of addedCollection.
229         */
230        public final CollectionItem getAddedCollection() {
231            CollectionItem[] colls = getAddedCollections();
232            return ((colls != null) ? colls[0] : null);
233        }
234    
235        public abstract boolean isRemover();
236        public abstract CollectionItem[] getRemovedCollections();
237    
238        public abstract boolean isAccessor();
239        public abstract boolean isWriter();
240        public abstract boolean isGetter();
241        public abstract boolean isSetter();
242    
243        public abstract boolean isCollectionGetter();
244        public abstract boolean isCollectionAccessor();
245        public abstract boolean isCollectionSetter();
246    
247        public abstract boolean isFieldGetter();
248        public abstract boolean isFieldSetter();
249    
250        public abstract boolean isReferenceGetter();
251        public abstract boolean isReferenceSetter();
252        public abstract boolean isReferenceAccessor();
253    
254        public abstract FieldItem getSetField();
255    
256        /** Stores the fields that are written by this method.<p> */
257        FieldItem[] writtenFields = null;
258       
259        /**
260         * Gets the value of the fields that are written by this method.<p>
261         *
262         * @return value of writtenFields.  */
263    
264        public final FieldItem[] getWrittenFields() {
265            ((ClassItem)parent).buildFieldInfo();
266            return writtenFields;
267        }
268       
269        public final boolean hasWrittenFields() {
270            ((ClassItem)parent).buildFieldInfo();
271            return writtenFields!=null && writtenFields.length>0;
272        }
273    
274        /**
275         * Sets the value of the fields that are written by this method.<p>
276         *
277         * @param writtenFields value to assign to writtenFields
278         * @see #addWrittenField(FieldItem)
279         */
280    
281        public final void setWrittenFields(FieldItem[] writtenFields) {
282            this.writtenFields = writtenFields;
283        }
284    
285        /**
286         * Adds a new written field for this method.<p>
287         *
288         * @param writtenField the field to add
289         * @see #setWrittenFields(FieldItem[])
290         * @see #removeWrittenField(FieldItem)
291         */
292        public final void addWrittenField(FieldItem writtenField) {
293            if (writtenFields == null) {
294                writtenFields = new FieldItem[] { writtenField };
295            } else {
296                FieldItem[] tmp = new FieldItem[writtenFields.length + 1];
297                System.arraycopy(writtenFields, 0, tmp, 0, writtenFields.length);
298                tmp[writtenFields.length] = writtenField;
299                writtenFields = tmp;
300            }
301        }
302    
303        /**
304         * Removes a new written field for this method.<p>
305         *
306         * @param field the field to remove
307         * @see #addWrittenField(FieldItem)
308         */
309        public final void removeWrittenField(FieldItem field) {
310            if (writtenFields != null) {
311                Vector v = new Vector(Arrays.asList(writtenFields));
312                v.remove(field);
313                writtenFields = new FieldItem[v.size()];
314                System.arraycopy(v.toArray(),0,writtenFields,0,v.size());
315            }
316        }
317    
318        /** Stores the number of references read by this method */
319        int numAccessedReferences = 0;
320    
321        /** Stores the number of collections read by this method */
322        int numAccessedCollections = 0;
323    
324        /** Stores the collections that are modified by this method.<p> */
325        CollectionItem[] modifiedCollections = null;
326       
327        /**
328         * Gets the value of the collections that are modified by this
329         * method.<p>
330         *
331         * @return value of removedCollections.  */
332    
333        public final CollectionItem[] getModifiedCollections() {
334            ((ClassItem)parent).buildFieldInfo();
335            return modifiedCollections;
336        }
337       
338        /**
339         * Returns true if the method has at least one modified collection
340         */
341    
342        public final boolean hasModifiedCollections() {
343            ((ClassItem)parent).buildFieldInfo();
344            return modifiedCollections!=null && modifiedCollections.length>0;
345        }
346    
347        /**
348         * Adds a new modified collection for this method.<p>
349         *
350         * @param modifiedCollection the collection to add
351         */
352    
353        public final void addModifiedCollection(CollectionItem modifiedCollection) {
354            if ( modifiedCollections == null ) {
355                modifiedCollections = new CollectionItem[] { modifiedCollection };
356            } else {
357                CollectionItem[] tmp = new CollectionItem[modifiedCollections.length + 1];
358                System.arraycopy( modifiedCollections, 0, tmp, 0, modifiedCollections.length );
359                tmp[modifiedCollections.length] = modifiedCollection;
360                modifiedCollections = tmp;
361            }
362        }
363    
364        /** Stores the fields that are read by this method.<p> */
365        FieldItem[] accessedFields = null;
366    
367        /**
368         * Gets the value of the fields that are written by this method.<p>
369         *
370         * @return value of accessedFields.  */
371    
372        public final FieldItem[] getAccessedFields() {
373            ((ClassItem)parent).buildFieldInfo();
374            return accessedFields;
375        }
376    
377        /**
378         * Sets the value of the fields that are read by this method.<p>
379         *
380         * @param accessedFields value to assign to accessedFields
381         * @see #addAccessedField(FieldItem)
382         */
383    
384        public void setAccessedFields(FieldItem[]  accessedFields) {
385            this.accessedFields = accessedFields;
386            numAccessedReferences = 0;
387            numAccessedCollections = 0;
388            if (accessedFields!=null) {
389                for(int i=0; i<accessedFields.length; i++) {
390                    if (accessedFields[i].isReference())
391                        numAccessedReferences++;
392                    else if (accessedFields[i] instanceof CollectionItem)
393                        numAccessedCollections++;
394                }
395            }
396        }
397    
398        /**
399         * Adds a new accessed field for this method.<p>
400         *
401         * @param accessedField the field to add
402         * @see #setAccessedFields(FieldItem[])
403         * @see #removeAccessedField(FieldItem)
404         */
405    
406        public void addAccessedField(FieldItem accessedField) {
407            if (accessedField.isReference())
408                numAccessedReferences++;
409            else if (accessedField instanceof CollectionItem)
410                numAccessedCollections++;
411            if (accessedFields == null) {
412                accessedFields = new FieldItem[] { accessedField };
413            } else {
414                FieldItem[] tmp = new FieldItem[accessedFields.length + 1];
415                System.arraycopy( accessedFields, 0, tmp, 0, accessedFields.length );
416                tmp[accessedFields.length] = accessedField;
417                accessedFields = tmp;
418            }
419        }
420    
421        /**
422         * Removes an accessed field for this method.<p>
423         *
424         * @param field the field to remove
425         * @see #addAccessedField(FieldItem)
426         */
427        public void removeAccessedField(FieldItem field) {
428            if (accessedFields != null) {
429                Vector v = new Vector(Arrays.asList(accessedFields));
430                if (v.remove(field)) {
431                    if (field.isReference())
432                        numAccessedReferences--;
433                    else if (field instanceof CollectionItem) 
434                        numAccessedCollections--;
435                    accessedFields = new FieldItem[v.size()];
436                    System.arraycopy(v.toArray(),0,accessedFields,0,v.size());
437                }
438            }
439        }
440    
441        /**
442         * Gets the value of the fields that are written by this method.<p>
443         *
444         * @return value of accessedFields.
445         */
446        public final FieldItem[] getAccessedReferences() {
447            ((ClassItem)parent).buildFieldInfo();
448            FieldItem[] refs = new FieldItem[numAccessedReferences];
449            int j=0;
450            for (int i=0; i<accessedFields.length; i++) {
451                if (accessedFields[i].isReference())
452                    refs[j++] = accessedFields[i];
453            }
454            return refs;
455        }
456    
457        public final CollectionItem[] getAccessedCollections() {
458            ((ClassItem)parent).buildFieldInfo();
459            CollectionItem[] colls = new CollectionItem[numAccessedCollections];
460            int j=0;
461            for (int i=0; i<accessedFields.length; i++) {
462                if (accessedFields[i] instanceof CollectionItem)
463                    colls[j++] = (CollectionItem)accessedFields[i];
464            }
465            return colls;
466        }
467    
468        /** Tells wether the method modifies the state of the object */
469        public final boolean isModifier() {
470            ((ClassItem)parent).buildFieldInfo();
471            return (writtenFields!=null && writtenFields.length>0) 
472                || (modifiedCollections!=null && modifiedCollections.length>0) 
473                || isAdder() || isRemover();
474        }
475    
476       public String toString() {
477          return getLongName();
478       }
479    }