    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    }