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