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