001    // Copyright 2011 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    // http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry5.internal.plastic;
016    
017    import java.util.Set;
018    
019    /**
020     * Used to track which methods are implemented by a base class, which is often needed when transforming
021     * a subclass.
022     */
023    public class InheritanceData
024    {
025        private final InheritanceData parent;
026    
027        private final Set<String> methodNames = PlasticInternalUtils.newSet();
028        private final Set<String> methods = PlasticInternalUtils.newSet();
029        private final Set<String> interfaceNames = PlasticInternalUtils.newSet();
030    
031        public InheritanceData()
032        {
033            this(null);
034        }
035    
036        private InheritanceData(InheritanceData parent)
037        {
038            this.parent = parent;
039        }
040    
041        /**
042         * Is this bundle for a transformed class, or for a base class (typically Object)?
043         *
044         * @return true if this bundle is for transformed class, false otherwise
045         */
046        public boolean isTransformed()
047        {
048            return parent != null;
049        }
050    
051        /**
052         * Returns a new MethodBundle that represents the methods of a child class
053         * of this bundle. The returned bundle will always be {@linkplain #isTransformed() transformed}.
054         *
055         * @param childClassName name of subclass
056         * @return new method bundle
057         */
058        public InheritanceData createChild(String childClassName)
059        {
060            return new InheritanceData(this);
061        }
062    
063        /**
064         * Adds a new instance method. Only non-private, non-abstract methods should be added (that is, methods which might
065         * be overridden in subclasses). This can later be queried to see if any base class implements the method.
066         *
067         * @param name name of method
068         * @param desc method descriptor
069         */
070        public void addMethod(String name, String desc)
071        {
072            String value = toValue(name, desc);
073    
074            methods.add(value);
075            methodNames.add(name);
076        }
077    
078        /**
079         * Returns true if a transformed parent class contains the indicated method.
080         *
081         * @param name method name
082         * @param desc method descriptor
083         * @return the <em>internal name</em> of the implementing base class for this method,
084         *         or null if no base class implements the method
085         */
086        public boolean isImplemented(String name, String desc)
087        {
088            return checkForMethod(toValue(name, desc));
089        }
090    
091    
092        private boolean checkForMethod(String value)
093        {
094            if (methods.contains(value))
095                return true;
096    
097            return parent == null ? false : parent.checkForMethod(value);
098        }
099    
100        /**
101         * Returns true if the class represented by this data, or any parent data, implements
102         * the named interface.
103         */
104        public boolean isInterfaceImplemented(String name)
105        {
106            InheritanceData cursor = this;
107    
108            while (cursor != null)
109            {
110                if (cursor.interfaceNames.contains(name))
111                {
112                    return true;
113                }
114    
115                cursor = cursor.parent;
116            }
117    
118            return false;
119        }
120    
121        public void addInterface(String name) {
122            interfaceNames.add(name);
123        }
124    
125        /**
126         * Combines a method name and its desc (which describes parameter types and return value) to form
127         * a value, which is how methods are tracked.
128         */
129        private String toValue(String name, String desc)
130        {
131            return name + ":" + desc;
132        }
133    
134        /**
135         * Returns the names of any methods in this bundle, or from any parent bundles.
136         */
137        public Set<String> methodNames()
138        {
139            Set<String> result = PlasticInternalUtils.newSet();
140    
141            InheritanceData cursor = this;
142    
143            while (cursor != null)
144            {
145                result.addAll(cursor.methodNames);
146                cursor = cursor.parent;
147            }
148    
149            return result;
150        }
151    }