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 }