View Javadoc
1 ////////////////////////////////////////////////////////////////////////////// 2 // Clirr: compares two versions of a java library for binary compatibility 3 // Copyright (C) 2003 - 2004 Lars Kühne 4 // 5 // This library is free software; you can redistribute it and/or 6 // modify it under the terms of the GNU Lesser General Public 7 // License as published by the Free Software Foundation; either 8 // version 2.1 of the License, or (at your option) any later version. 9 // 10 // This library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 // Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public 16 // License along with this library; if not, write to the Free Software 17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 ////////////////////////////////////////////////////////////////////////////// 19 20 package net.sf.clirr.checks; 21 22 import java.util.Arrays; 23 import java.util.Comparator; 24 25 import net.sf.clirr.framework.ClassChangeCheck; 26 import net.sf.clirr.framework.AbstractDiffReporter; 27 import net.sf.clirr.framework.ApiDiffDispatcher; 28 import net.sf.clirr.event.ApiDifference; 29 import net.sf.clirr.event.Severity; 30 import org.apache.bcel.classfile.JavaClass; 31 import org.apache.bcel.classfile.Field; 32 import org.apache.bcel.classfile.ConstantValue; 33 34 /*** 35 * Checks the fields of a class. 36 * 37 * @author lkuehne 38 */ 39 public class FieldSetCheck 40 extends AbstractDiffReporter 41 implements ClassChangeCheck 42 { 43 private static final class FieldNameComparator implements Comparator 44 { 45 public int compare(Object o1, Object o2) 46 { 47 Field f1 = (Field) o1; 48 Field f2 = (Field) o2; 49 50 final String name1 = f1.getName(); 51 final String name2 = f2.getName(); 52 53 return name1.compareTo(name2); 54 } 55 } 56 57 private Comparator comparator = new FieldNameComparator(); 58 59 public FieldSetCheck(ApiDiffDispatcher dispatcher) 60 { 61 super(dispatcher); 62 } 63 64 public final void check(JavaClass compatBaseline, JavaClass currentVersion) 65 { 66 final Field[] baselineFields = compatBaseline.getFields(); 67 final Field[] currentFields = currentVersion.getFields(); 68 69 // Sigh... BCEL 5.1 hands out it's internal datastructure, 70 // so we have to make a copy here to make sure we don't mess up BCEL by sorting 71 72 final Field[] bFields = createSortedCopy(baselineFields); 73 final Field[] cFields = createSortedCopy(currentFields); 74 75 checkForChanges(bFields, cFields, compatBaseline, currentVersion); 76 } 77 78 private void checkForChanges( 79 Field[] bFields, Field[] cFields, JavaClass baseLineClass, JavaClass currentClass) 80 { 81 boolean[] newInCurrent = new boolean[cFields.length]; 82 Arrays.fill(newInCurrent, true); 83 84 for (int i = 0; i < bFields.length; i++) 85 { 86 Field bField = bFields[i]; 87 if (!bField.isPublic() && !bField.isProtected()) 88 { 89 continue; 90 } 91 int cIdx = Arrays.binarySearch(cFields, bField, comparator); 92 if (cIdx < 0) 93 { 94 final String name = bField.getName(); 95 fireDiff("Field " + name + " has been removed", Severity.ERROR, baseLineClass, bField); 96 } 97 else 98 { 99 Field cField = cFields[cIdx]; 100 newInCurrent[cIdx] = false; 101 checkForModifierChange(bField, cField, currentClass); 102 checkForVisibilityChange(bField, cField, currentClass); 103 checkForReturnTypeChange(bField, cField, currentClass); 104 checkForConstantValueChange(bField, cField, currentClass); 105 } 106 } 107 108 for (int i = 0; i < newInCurrent.length; i++) 109 { 110 Field field = cFields[i]; 111 if (newInCurrent[i] && (field.isPublic() || field.isProtected())) 112 { 113 String scope = field.isPublic() ? "public" : "protected"; 114 final String fieldName = field.getName(); 115 fireDiff("Added " + scope + " field " + fieldName, Severity.INFO, currentClass, field); 116 } 117 } 118 } 119 120 private void checkForConstantValueChange(Field bField, Field cField, JavaClass currentClass) 121 { 122 if (!(bField.isStatic() && bField.isFinal() && cField.isStatic() && cField.isFinal())) 123 { 124 return; 125 } 126 127 final ConstantValue bVal = bField.getConstantValue(); 128 129 if (bVal != null) 130 { 131 final String bValRep = bVal.toString(); 132 final ConstantValue cVal = cField.getConstantValue(); 133 if (cVal == null) 134 { 135 fireDiff("Value of " + bField.getName() 136 + " is no longer a compile time constant", 137 Severity.WARNING, currentClass, cField); 138 return; 139 } 140 141 final String cValRep = String.valueOf(cVal); 142 if (!bValRep.equals(cValRep)) 143 { 144 // TODO: print out old and new value 145 // How can that be done with BCEL, esp. for boolean values? 146 fireDiff("Value of compile time constant " + bField.getName() 147 + " has been changed", 148 Severity.WARNING, currentClass, cField); 149 } 150 } 151 } 152 153 private void checkForReturnTypeChange(Field bField, Field cField, JavaClass currentClass) 154 { 155 final String bSig = bField.getType().toString(); 156 final String cSig = cField.getType().toString(); 157 if (!bSig.equals(cSig)) 158 { 159 fireDiff("Changed type of field " + bField.getName() + " from " + bSig + " to " + cSig, 160 Severity.ERROR, currentClass, bField); 161 } 162 } 163 164 private void checkForModifierChange(Field bField, Field cField, JavaClass clazz) 165 { 166 if (bField.isFinal() && !cField.isFinal()) 167 { 168 fireDiff("Field " + bField.getName() + " is now non-final", Severity.INFO, clazz, cField); 169 } 170 171 if (!bField.isFinal() && cField.isFinal()) 172 { 173 fireDiff("Field " + bField.getName() + " is now final", Severity.ERROR, clazz, cField); 174 } 175 176 if (bField.isStatic() && !cField.isStatic()) 177 { 178 fireDiff("Field " + bField.getName() + " is now non-static", Severity.ERROR, clazz, cField); 179 } 180 181 if (!bField.isStatic() && cField.isStatic()) 182 { 183 fireDiff("Field " + bField.getName() + " is now static", Severity.ERROR, clazz, cField); 184 } 185 186 // JLS, 13.4.10: Adding or deleting a transient modifier of a field 187 // does not break compatibility with pre-existing binaries 188 189 // TODO: What about volatile? 190 } 191 192 private void checkForVisibilityChange(Field bField, Field cField, JavaClass clazz) 193 { 194 if (bField.isProtected() && cField.isPublic()) 195 { 196 fireDiff("Field " + bField.getName() + " is now public", Severity.INFO, clazz, cField); 197 } 198 else if (bField.isProtected() && !(cField.isProtected() || cField.isPublic()) 199 || bField.isPublic() && !cField.isPublic()) 200 { 201 fireDiff("Accessibility of field " + bField.getName() + " has been weakened", 202 Severity.ERROR, clazz, cField); 203 } 204 } 205 206 private void fireDiff(String report, Severity severity, JavaClass clazz, Field field) 207 { 208 final String className = clazz.getClassName(); 209 final ApiDifference diff = 210 new ApiDifference(report + " in " + className, 211 severity, className, null, field.getName()); 212 getApiDiffDispatcher().fireDiff(diff); 213 214 } 215 216 private Field[] createSortedCopy(final Field[] orig) 217 { 218 final Field[] fields = new Field[orig.length]; 219 System.arraycopy(orig, 0, fields, 0, orig.length); 220 Arrays.sort(fields, comparator); 221 return fields; 222 } 223 }

This page was automatically generated by Maven